aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/checkpatch.yml32
-rw-r--r--.github/workflows/linux-build.yml61
-rw-r--r--.github/workflows/snapshot.yml23
-rw-r--r--.github/workflows/spike-openocd-tests.yml123
-rw-r--r--.travis.yml124
-rw-r--r--HACKING26
-rw-r--r--Makefile.am15
-rw-r--r--configure.ac3
-rwxr-xr-xcontrib/cross-build.sh2
-rw-r--r--contrib/loaders/flash/gd32v/gd32vf103.inc8
-rw-r--r--doc/openocd.texi231
-rwxr-xr-xgit-hooks/commit-msg191
-rwxr-xr-xgit-hooks/pre-push3
m---------jimtcl0
-rw-r--r--src/flash/nor/drivers.c2
-rw-r--r--src/flash/nor/stm32f1x.c52
-rw-r--r--src/flash/nor/stm32lx.c5
-rw-r--r--src/flash/nor/tcl.c3
-rw-r--r--src/helper/Makefile.am4
-rw-r--r--src/helper/base64.c157
-rw-r--r--src/helper/base64.h17
-rw-r--r--src/jtag/adapter.c4
-rw-r--r--src/jtag/drivers/ftdi.c448
-rw-r--r--src/jtag/drivers/xds110.c2
-rw-r--r--src/rtos/FreeRTOS.c762
-rw-r--r--src/rtos/hwthread.c52
-rw-r--r--src/rtos/rtos.c198
-rw-r--r--src/rtos/rtos.h51
-rw-r--r--src/rtos/rtos_standard_stackings.c213
-rw-r--r--src/rtos/rtos_standard_stackings.h5
-rw-r--r--src/server/gdb_server.c121
-rw-r--r--src/server/server.c1
-rw-r--r--src/target/breakpoints.c27
-rw-r--r--src/target/breakpoints.h2
-rw-r--r--src/target/dsp563xx.c3
-rw-r--r--src/target/riscv/batch.c17
-rw-r--r--src/target/riscv/batch.h3
-rw-r--r--src/target/riscv/encoding.h255
-rw-r--r--src/target/riscv/gdb_regs.h8
-rw-r--r--src/target/riscv/opcodes.h13
-rw-r--r--src/target/riscv/program.c25
-rw-r--r--src/target/riscv/program.h2
-rw-r--r--src/target/riscv/riscv-011.c38
-rw-r--r--src/target/riscv/riscv-013.c1814
-rw-r--r--src/target/riscv/riscv.c3076
-rw-r--r--src/target/riscv/riscv.h135
-rw-r--r--src/target/riscv/riscv_semihosting.c50
-rw-r--r--src/target/target.c96
-rw-r--r--src/target/target.h12
-rw-r--r--tcl/board/esp32c2-ftdi.cfg21
-rw-r--r--tcl/board/esp32c3-ftdi.cfg21
-rw-r--r--tcl/board/sifive-e31arty-cjtag.cfg23
-rw-r--r--tcl/board/sifive-e31arty-onboard-ftdi.cfg25
-rw-r--r--tcl/board/sifive-hifive1-revb.cfg3
-rw-r--r--tcl/interface/ftdi/arty-onboard-ftdi.cfg7
-rw-r--r--tcl/interface/ftdi/digilent-hs2-cjtag.cfg17
-rw-r--r--tcl/interface/ftdi/olimex-arm-jtag-cjtag.cfg27
-rw-r--r--tcl/interface/ftdi/olimex-arm-usb-ocd-h-cjtag.cfg22
-rw-r--r--tcl/interface/ftdi/olimex-arm-usb-tiny-h-cjtag.cfg27
-rw-r--r--tcl/target/1986Be1T.cfg (renamed from tcl/target/1986ве1т.cfg)0
-rw-r--r--tcl/target/K1879x61R.cfg (renamed from tcl/target/к1879xб1я.cfg)0
-rw-r--r--tcl/target/esp32c2.cfg111
-rw-r--r--tcl/target/esp32c3.cfg113
-rw-r--r--tcl/target/esp_common.cfg6
-rw-r--r--tcl/target/gd32vf103.cfg105
-rwxr-xr-xtools/filter_openocd_log.py120
-rwxr-xr-xtools/scripts/checkpatch.pl21
67 files changed, 7010 insertions, 2174 deletions
diff --git a/.github/workflows/checkpatch.yml b/.github/workflows/checkpatch.yml
new file mode 100644
index 0000000..a9ce549
--- /dev/null
+++ b/.github/workflows/checkpatch.yml
@@ -0,0 +1,32 @@
+on: pull_request
+
+name: Check Code Style (checkpatch)
+
+jobs:
+ check:
+ runs-on: ubuntu-latest
+ env:
+ DL_DIR: ../downloads
+ BUILD_DIR: ../build
+ steps:
+ - name: Checkout Code
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+ - name: Checkout Base
+ run: |
+ git fetch origin ${{ github.event.pull_request.base.ref }}
+ echo "The current base for checkpatch is: $(git show FETCH_HEAD --oneline --raw)"
+ - name: Install required packages (apt-get)
+ run: |
+ sudo apt-get update
+ sudo apt-get install patchutils python3-ply python3-git
+ - name: Run checkpatch
+ run: |
+ git diff --patch FETCH_HEAD \
+ | filterdiff \
+ -x "a/src/jtag/drivers/libjaylink/*" \
+ -x "a/tools/git2cl/*" \
+ -x "a/.github/*" \
+ -x "a/HACKING" \
+ | ./tools/scripts/checkpatch.pl --no-signoff -
diff --git a/.github/workflows/linux-build.yml b/.github/workflows/linux-build.yml
new file mode 100644
index 0000000..01e230c
--- /dev/null
+++ b/.github/workflows/linux-build.yml
@@ -0,0 +1,61 @@
+on: pull_request
+
+name: Linux Build
+
+jobs:
+ # 32-bit, clang
+ build32:
+ runs-on: ubuntu-20.04
+ env:
+ CFLAGS: -m32
+ CC: clang
+ steps:
+ - name: Checkout Code
+ uses: actions/checkout@v2
+ - name: Install required packages (apt-get)
+ run: |
+ sudo apt-get update
+ sudo apt-get install clang gcc-multilib
+ - run: ./bootstrap
+ - run: ./configure --enable-remote-bitbang --enable-jtag_vpi --disable-target64
+ - run: make -j`nproc`
+ - run: file src/openocd | grep 32-bit
+ - run: src/openocd --version
+
+ # 64-bit, gcc
+ build64:
+ runs-on: ubuntu-latest
+ env:
+ CFLAGS: -m64
+ CC: gcc
+ steps:
+ - name: Checkout Code
+ uses: actions/checkout@v3
+ - name: Configure environment
+ run: |
+ TAG=$(git rev-parse --short HEAD)
+ echo "TAG=$TAG" >> $GITHUB_OUTPUT
+ echo "NAME=openocd64-$TAG" >> $GITHUB_ENV
+ - name: Install required packages (apt-get)
+ run: |
+ sudo apt-get update
+ sudo apt-get install libusb-1.0-0 libusb-1.0-0-dev
+ - run: ./bootstrap
+ - run: ./configure --enable-remote-bitbang --enable-jtag_vpi --enable-ftdi-cjtag --prefix /tmp/${{ env.NAME }}
+ - run: make -j`nproc`
+ - name: Check that we built something
+ run: |
+ file src/openocd | grep 64-bit
+ src/openocd --version
+ - name: Package
+ # Package into tgz so that github stores a compressed artifact, even
+ # though it zips that artifact again before it sends it back to be
+ # downloaded.
+ run: |
+ make install
+ tar zcvf ${{ env.NAME }}.tgz -C /tmp ${{ env.NAME }}
+ - name: Upload Artifact
+ uses: actions/upload-artifact@v3
+ with:
+ name: ${{ env.NAME }}
+ path: ${{ env.NAME }}.tgz
diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml
index e5997a0..6cbce86 100644
--- a/.github/workflows/snapshot.yml
+++ b/.github/workflows/snapshot.yml
@@ -2,13 +2,13 @@
# Copyright (C) 2020 by Tarek BOUCHKATI <tarek.bouchkati@gmail.com>
-on: push
+on: pull_request
name: OpenOCD Snapshot
jobs:
package:
- runs-on: [ubuntu-18.04]
+ runs-on: [ubuntu-20.04]
env:
DL_DIR: ../downloads
BUILD_DIR: ../build
@@ -64,6 +64,7 @@ jobs:
HIDAPI_CONFIG: --enable-shared --disable-static --disable-testgui
LIBFTDI_CONFIG: -DSTATICLIBS=OFF -DEXAMPLES=OFF -DFTDI_EEPROM=OFF
CAPSTONE_CONFIG: "CAPSTONE_BUILD_CORE_ONLY=yes CAPSTONE_STATIC=yes CAPSTONE_SHARED=no"
+ CAPSTONE_CFLAGS: -I$(CAPSTONE_SRC)/include/capstone
run: |
# check if there is tag pointing at HEAD, otherwise take the HEAD SHA-1 as OPENOCD_TAG
OPENOCD_TAG="`git tag --points-at HEAD`"
@@ -89,23 +90,9 @@ jobs:
echo "RELEASE_NAME=$RELEASE_NAME" >> $GITHUB_ENV
echo "IS_PRE_RELEASE=$IS_PRE_RELEASE" >> $GITHUB_ENV
echo "ARTIFACT_PATH=$PWD/$ARTIFACT" >> $GITHUB_ENV
+ echo "ARTIFACT_NAME=openocd-windows-${OPENOCD_TAG}" >> $GITHUB_ENV
- name: Publish OpenOCD packaged for windows
uses: actions/upload-artifact@v2
with:
+ name: ${{ env.ARTIFACT_NAME }}
path: ${{ env.ARTIFACT_PATH }}
- - name: Delete 'latest' Release
- uses: dev-drprasad/delete-tag-and-release@v0.2.0
- with:
- delete_release: true
- tag_name: ${{ env.RELEASE_NAME }}
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- - name: Create Release
- uses: ncipollo/release-action@v1
- with:
- tag: ${{ env.RELEASE_NAME }}
- commit: ${{ github.sha }}
- draft: false
- artifacts: ${{ env.ARTIFACT_PATH }}
- prerelease: ${{ env.IS_PRE_RELEASE }}
- token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/spike-openocd-tests.yml b/.github/workflows/spike-openocd-tests.yml
new file mode 100644
index 0000000..72cc6ea
--- /dev/null
+++ b/.github/workflows/spike-openocd-tests.yml
@@ -0,0 +1,123 @@
+# Build Spike and run a couple of debug tests.
+
+name: Test OpenOCD against 2 spike configurations
+
+env:
+ SPIKE_REPO: https://github.com/riscv-software-src/riscv-isa-sim.git
+ SPIKE_REV: master
+ RISCV_TESTS_REPO: https://github.com/riscv-software-src/riscv-tests.git
+ RISCV_TESTS_REV: master
+ TOOLCHAIN_URL: https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v12.2.0-1/xpack-riscv-none-elf-gcc-12.2.0-1-linux-x64.tar.gz
+
+on: pull_request
+
+# There is some commented out code below that would be useful in adding this
+# workflow to other repos. Ideally we can come up with something that would
+# leave this file almost identical between repos, so they can all easily run
+# this test suite.
+
+jobs:
+ test:
+ name: Test debug (Ubuntu)
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Install packages
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y device-tree-compiler build-essential
+
+ - name: Get revisions of dependencies
+ run: |
+ SPIKE_COMMIT=$( git ls-remote "$SPIKE_REPO" master | awk '{ print $1; }' )
+ RISC_V_TESTS_COMMIT=$( git ls-remote "$RISCV_TESTS_REPO" master | awk '{ print $1; }' )
+ echo "Revison of Spike: $SPIKE_COMMIT"
+ echo "Revision of RISC-V tests: $RISC_V_TESTS_COMMIT"
+ # Save for later use
+ echo "SPIKE_COMMIT=$SPIKE_COMMIT" >> $GITHUB_ENV
+ echo "RISC_V_TESTS_COMMIT=$RISC_V_TESTS_COMMIT" >> $GITHUB_ENV
+
+ - name: Get the toolchain from cache (if available)
+ id: cache-toolchain
+ uses: actions/cache@v3
+ with:
+ path: /opt/riscv/toolchain
+ key: "toolchain-${{env.TOOLCHAIN_URL}}"
+
+ - name: Get spike from cache (if available)
+ id: cache-spike
+ uses: actions/cache@v3
+ with:
+ path: /opt/riscv/spike
+ key: "spike-${{env.SPIKE_COMMIT}}"
+
+ - if: ${{ steps.cache-toolchain.outputs.cache-hit != 'true' }}
+ name: Download Toolchain (if not cached)
+ run: |
+ mkdir -p /opt/riscv/toolchain
+ wget --progress=dot:giga $TOOLCHAIN_URL -O /tmp/toolchain.tar.gz
+
+ - if: ${{ steps.cache-toolchain.outputs.cache-hit != 'true' }}
+ name: Install Toolchain (if not cached)
+ run: tar zxf /tmp/toolchain.tar.gz --strip-components=1 -C /opt/riscv/toolchain
+
+ - if: ${{ steps.cache-spike.outputs.cache-hit != 'true' }}
+ name: Download Spike source (if not cached)
+ run: |
+ git clone "$SPIKE_REPO"
+ cd riscv-isa-sim
+ git checkout "$SPIKE_COMMIT"
+ git submodule update --init --recursive
+
+ - if: ${{ steps.cache-spike.outputs.cache-hit != 'true' }}
+ name: Build Spike (if not cached)
+ run: |
+ cd riscv-isa-sim
+ mkdir build && cd build
+ ../configure --prefix=/opt/riscv/spike
+ make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)"
+ make install
+
+ - name: Build OpenOCD
+ run: |
+ #cd riscv-openocd
+ ./bootstrap
+ ./configure --prefix=/opt/riscv
+ make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)"
+ ls -l src/openocd
+
+# - name: Download OpenOCD
+# run: |
+# git clone --recurse-submodules https://github.com/riscv/riscv-openocd.git
+# cd riscv-openocd
+# git checkout 43ea20dfbb6c815004a51106a3b2009d7f6c4940
+
+ - name: Download Tests
+ run: |
+ git clone "$RISCV_TESTS_REPO"
+ cd riscv-tests
+ git checkout "$RISCV_TESTS_REV"
+ git submodule update --init --recursive
+
+ - name: Run Tests
+ run: |
+ cd riscv-tests/debug
+ ./gdbserver.py targets/RISC-V/spike32.py --print-failures \
+ --gcc /opt/riscv/toolchain/bin/riscv-none-elf-gcc \
+ --gdb /opt/riscv/toolchain/bin/riscv-none-elf-gdb \
+ --sim_cmd /opt/riscv/spike/bin/spike \
+ --server_cmd $GITHUB_WORKSPACE/src/openocd
+ ./gdbserver.py targets/RISC-V/spike64-2.py --print-failures \
+ --gcc /opt/riscv/toolchain/bin/riscv-none-elf-gcc \
+ --gdb /opt/riscv/toolchain/bin/riscv-none-elf-gdb \
+ --sim_cmd /opt/riscv/spike/bin/spike \
+ --server_cmd $GITHUB_WORKSPACE/src/openocd
+
+ - name: Archive test logs
+ # Proceed even if there was a failed test
+ if: ${{ success() || failure() }}
+ uses: actions/upload-artifact@v3
+ with:
+ name: test-logs
+ path: riscv-tests/debug/logs
diff --git a/.travis.yml b/.travis.yml
index 28d5502..e5ebe4d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -28,64 +28,78 @@ env:
- CC=clang-9
language: c
+dist: trusty
-git:
- depth: 1
- autocrlf: input
+matrix:
+ include:
+ - os: linux
+ env:
+ - BUILD=x86_64-linux-gnu
+ - EXECUTABLE=openocd
+ addons:
+ apt:
+ packages:
+ - patchutils
+ compiler: gcc
-script:
- - $mingw64 ${CC} --version
- - $mingw64 env
- - $mingw64 ./bootstrap
- - $mingw64 ./configure
- - $mingw64 make
+ - os: linux
+ env:
+ - BUILD=i686-linux-gnu
+ - CFLAGS=-m32
+ - EXECUTABLE=openocd
+ addons:
+ apt:
+ packages:
+ - gcc-multilib patchutils
+ compiler: gcc
-before_install:
- - |-
- case $TRAVIS_OS_NAME in
- linux)
- sudo apt install ${CC} libusb-1.0-0-dev
- ;;
- osx)
- brew install libtool automake libusb libusb-compat hidapi libftdi
- ;;
- windows)
- [[ ! -f C:/tools/msys64/msys2_shell.cmd ]] && rm -rf C:/tools/msys64
- choco uninstall -y mingw
- choco upgrade --no-progress -y msys2
- export msys2='cmd //C RefreshEnv.cmd '
- export msys2+='& set MSYS=winsymlinks:nativestrict '
- export msys2+='& C:\\tools\\msys64\\msys2_shell.cmd -defterm -no-start'
- export mingw64="$msys2 -mingw64 -full-path -here -c \$\* --"
- export msys2+=" -msys2 -c \$\* --"
- $msys2 pacman --sync --noconfirm --needed mingw-w64-x86_64-toolchain autoconf autoconf-archive automake automake-wrapper binutils gcc gettext git libtool m4 make pkg-config tcl texinfo mingw-w64-x86_64-libusb mingw-w64-x86_64-libusb-compat-git mingw-w64-x86_64-libjaylink-git mingw-w64-x86_64-libftdi mingw-w64-x86_64-hidapi mingw-w64-x86_64-clang
- ## FIXME: Also build for i686?
- ## Install more MSYS2 packages from https://packages.msys2.org/base here
- taskkill //IM gpg-agent.exe //F # https://travis-ci.community/t/4967
- export PATH=/C/tools/msys64/mingw64/bin:$PATH
- export MAKE=mingw32-make # so that Autotools can find it
- ;;
- esac
+ - os: linux
+ env:
+ - BUILD=x86_64-linux-gnu
+ - EXECUTABLE=openocd
+ addons:
+ apt:
+ packages:
+ - patchutils
+ compiler: clang
-before_cache:
-- |-
- case $TRAVIS_OS_NAME in
- windows)
- # https://unix.stackexchange.com/a/137322/107554
- $msys2 pacman --sync --clean --noconfirm
- ;;
- esac
+ - os: linux
+ env:
+ - BUILD=i686-linux-gnu
+ - CFLAGS=-m32
+ - CONFIGURE_ARGS="--disable-target64"
+ - EXECUTABLE=openocd
+ compiler: clang
+ addons:
+ apt:
+ packages:
+ - gcc-multilib patchutils
-cache:
- directories:
- - $HOME/AppData/Local/Temp/chocolatey
- - /C/tools/msys64
+ - os: linux
+ env:
+ - BUILD=i686-w64-mingw
+ - CONFIGURE_ARGS="--build=i686-unknown-linux-gnu --host=i686-w64-mingw32"
+ - EXECUTABLE=openocd.exe
+ compiler: i686-w64-mingw32-gcc
+ addons:
+ apt:
+ packages:
+ - binutils-mingw-w64-i686 gcc-mingw-w64-i686 g++-mingw-w64-i686 patchutils
-matrix:
- include:
- - os: osx
- env:
- - CC=clang
- - os: windows
- env:
- - CC=gcc
+script:
+ # This is here for the signoff check.
+ # Disabled because when travis does the integration build the last change
+ # is an automated change, which won't have the Signed-off-by line.
+ #- ./tools/checkpatch.sh
+
+ # Ideally we'd diff back to where we either branched off OpenOCD or master,
+ # or riscv. But that's tricky, and the default git clone only gets the last
+ # 50 changes any case. Most merges won't consist of more than 40 changes,
+ # so this should work fine most of the time, and be a lot better than not
+ # checking at all.
+ - git diff -U20 HEAD~40 |
+ filterdiff -x "a/src/jtag/drivers/libjaylink/*" -x "a/tools/git2cl/*"
+ -x "b/src/gnulib/*" |
+ ./tools/scripts/checkpatch.pl --no-signoff -
+ - ./bootstrap && ./configure --enable-remote-bitbang --enable-jtag_vpi $CONFIGURE_ARGS && make
+ - file src/$EXECUTABLE
diff --git a/HACKING b/HACKING
index be56999..a2d4dcc 100644
--- a/HACKING
+++ b/HACKING
@@ -77,6 +77,32 @@ patch:
src/openocd -s ../tcl -f /path/to/openocd.cfg
@endcode
+- Runtime coverage testing
+
+ Apply the following patch to prevent OpenOCD from killing itself:
+ @code
+--- a/src/openocd.c
++++ b/src/openocd.c
+@@ -372,8 +372,6 @@ int openocd_main(int argc, char *argv[])
+
+ if (ERROR_FAIL == ret)
+ return EXIT_FAILURE;
+- else if (ERROR_OK != ret)
+- exit_on_signal(ret);
+
+ return ret;
+ }
+ @endcode
+
+ Configure your OpenOCD binary with coverage support as follows:
+ @code
+ LDFLAGS="-fprofile-arcs -ftest-coverage"
+ CFLAGS="-fprofile-arcs -ftest-coverage" ./configure
+ @endcode
+
+ Now every time OpenOCD is run, coverage info in your build directory is
+ updated. Running `gcov src/path/file.c` will generate a report.
+
Please consider performing these additional checks where appropriate
(especially Clang Static Analyzer for big portions of new code) and
mention the results (e.g. "Valgrind-clean, no new Clang analyzer
diff --git a/Makefile.am b/Makefile.am
index 41daf95..bc7ab21 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -33,13 +33,15 @@ DIST_SUBDIRS += jimtcl
endif
# common flags used in openocd build
-AM_CFLAGS = $(GCC_WARNINGS)
+AM_CFLAGS = $(GCC_WARNINGS)\
+ -DFD_SETSIZE=128
AM_CPPFLAGS = $(HOST_CPPFLAGS)\
-I$(top_srcdir)/src \
-I$(top_builddir)/src \
-DPKGDATADIR=\"$(pkgdatadir)\" \
- -DBINDIR=\"$(bindir)\"
+ -DBINDIR=\"$(bindir)\"\
+ -DFD_SETSIZE=128
if INTERNAL_JIMTCL
AM_CPPFLAGS += -I$(top_srcdir)/jimtcl \
@@ -131,6 +133,15 @@ distclean-local:
rm -rf Doxyfile doxygen
rm -f $(srcdir)/jimtcl/configure.gnu
+# We want every change to have Signed-off-by. This is tricky to enforce in
+# Travis, because it automatically makes temporary commits when merging. So
+# instead we have a hook that enforces this in each workspace. To make sure
+# that users actually use those hooks, we point git at them here.
+# If git fails for some reason, that's OK. It's probably because somebody is
+# building the source completely outside a git repo.
+all-local:
+ cd $(srcdir) && git config core.hooksPath ./git-hooks || true
+
DISTCLEANFILES = doxygen.log
METASOURCES = AUTO
diff --git a/configure.ac b/configure.ac
index cc7139c..a32fe89 100644
--- a/configure.ac
+++ b/configure.ac
@@ -115,6 +115,7 @@ m4_define([ADAPTER_OPT], [m4_translit(ADAPTER_ARG($1), [_], [-])])
m4_define([USB1_ADAPTERS],
[[[ftdi], [MPSSE mode of FTDI based devices], [FTDI]],
+ [[ftdi_cjtag], [cJTAG (OScan1, JScan3) tunneled thru MPSSE], [FTDI_CJTAG]],
[[stlink], [ST-Link Programmer], [HLADAPTER_STLINK]],
[[ti_icdi], [TI ICDI JTAG Programmer], [HLADAPTER_ICDI]],
[[ulink], [Keil ULINK JTAG Programmer], [ULINK]],
@@ -374,7 +375,7 @@ AC_ARG_ENABLE([internal-libjaylink],
AC_ARG_ENABLE([remote-bitbang],
AS_HELP_STRING([--enable-remote-bitbang], [Enable building support for the Remote Bitbang jtag driver]),
- [build_remote_bitbang=$enableval], [build_remote_bitbang=no])
+ [build_remote_bitbang=$enableval], [build_remote_bitbang=yes])
AS_CASE(["${host_cpu}"],
[i?86|x86*], [],
diff --git a/contrib/cross-build.sh b/contrib/cross-build.sh
index b199bf7..ded1691 100755
--- a/contrib/cross-build.sh
+++ b/contrib/cross-build.sh
@@ -24,7 +24,7 @@
# export HIDAPI_SRC=/path/to/hidapi
# export OPENOCD_CONFIG="--enable-..."
# cd /work/dir
-# /path/to/openocd/contrib/cross-build.sh <host-triplet>
+# .../path/to/openocd/contrib/cross-build.sh <host-triplet>
#
# For static linking, a workaround is to
# export LIBUSB1_CONFIG="--enable-static --disable-shared"
diff --git a/contrib/loaders/flash/gd32v/gd32vf103.inc b/contrib/loaders/flash/gd32v/gd32vf103.inc
new file mode 100644
index 0000000..d68b173
--- /dev/null
+++ b/contrib/loaders/flash/gd32v/gd32vf103.inc
@@ -0,0 +1,8 @@
+/* Autogenerated with ../../../../src/helper/bin2char.sh */
+0x6f,0x00,0x80,0x00,0x73,0x00,0x10,0x00,0x03,0x2b,0x06,0x00,0x63,0x0c,0x0b,0x04,
+0x83,0x2a,0x46,0x00,0xb3,0x87,0x6a,0x41,0xe3,0x88,0x07,0xfe,0x03,0xdb,0x0a,0x00,
+0x23,0x10,0x67,0x01,0x93,0x8a,0x2a,0x00,0x13,0x07,0x27,0x00,0x83,0x2b,0xc5,0x00,
+0x93,0xf7,0x1b,0x00,0xe3,0x9c,0x07,0xfe,0x93,0xf7,0x4b,0x01,0x63,0x90,0x07,0x02,
+0x63,0xe6,0xda,0x00,0x93,0x0a,0x06,0x00,0x93,0x8a,0x8a,0x00,0x23,0x22,0x56,0x01,
+0x93,0x85,0xf5,0xff,0x63,0x88,0x05,0x00,0x6f,0xf0,0x1f,0xfb,0x13,0x05,0x00,0x00,
+0x23,0x22,0xa6,0x00,0x13,0x85,0x0b,0x00,0x6f,0xf0,0xdf,0xf9,
diff --git a/doc/openocd.texi b/doc/openocd.texi
index aa1707b..d1edc91 100644
--- a/doc/openocd.texi
+++ b/doc/openocd.texi
@@ -2644,6 +2644,35 @@ minimal impact on the target system. Avoid floating inputs, conflicting outputs
and initially asserted reset signals.
@end deffn
+@deffn {Command} {ftdi oscan1_mode} on|off
+Enable or disable OScan1 mode. This mode is intended for use with an adapter,
+such as the ARM-JTAG-SWD by Olimex, that sits in between the FTDI chip and the
+target. The cJTAG prococol is composed of two wires: TCKC (clock) and TMSC (data).
+TMSC is a bidirectional signal which is time-multiplexed alternating TDI, TMS and
+TDO. The multiplexing is achieved by a tri-state buffer which puts TMSC in Hi-Z
+when the device is supposed to take the control of the line (TDO phase).
+
+The ARM-JTAG-SWD adapter uses standard TRST and TMS signals to control TMSC
+direction. TRST is used by the adapter as selector for the multiplexers which set
+the JTAG probe in 2-wire mode. Whatever signal is used for this purpose, it must
+be defined with the name JTAG_SEL using @command{ftdi layout_signal}. JTAG_SEL is
+set to 0 during OScan1 initialization.
+
+Some JTAG probes like the Digilent JTAG-HS2, support cJTAG by using a
+separate pin to control when TMS is driven onto TMSC. You can use such
+probes by defining the signal TMSC_EN using
+@command{ftdi layout_signal TMSC_EN -data <mask>}.
+@end deffn
+
+@deffn {Command} {ftdi jscan3_mode} on|off
+Enable or disable JScan3 mode. This mode uses the classic 4-wire JTAG protocol
+in chips whose JTAG port is only compliant with the cJTAG standard (IEEE 1149.7).
+
+Since cJTAG needs a 2-wire escape sequence to select the operating mode,
+a cJTAG adapter like ARM-JTAG-SWD by Olimex is still required. This means
+that a cJTAG probe configuration script must be used too.
+@end deffn
+
@deffn {Command} {ftdi layout_signal} name [@option{-data}|@option{-ndata} data_mask] [@option{-input}|@option{-ninput} input_mask] [@option{-oe}|@option{-noe} oe_mask] [@option{-alias}|@option{-nalias} name]
Creates a signal with the specified @var{name}, controlled by one or more FTDI
GPIO pins via a range of possible buffer connections. The masks are FTDI GPIO
@@ -4809,6 +4838,8 @@ compact Thumb2 instruction set. Supports also ARMv6-M and ARMv8-M cores
@item @code{esirisc} -- this is an EnSilica eSi-RISC core.
The current implementation supports eSi-32xx cores.
@item @code{esp32} -- this is an Espressif SoC with dual Xtensa cores.
+@item @code{esp32c2} -- this is an Espressif SoC with single RISC-V core.
+@item @code{esp32c3} -- this is an Espressif SoC with single RISC-V core.
@item @code{esp32s2} -- this is an Espressif SoC with single Xtensa core.
@item @code{esp32s3} -- this is an Espressif SoC with dual Xtensa cores.
@item @code{fa526} -- resembles arm920 (w/o Thumb).
@@ -10616,6 +10647,13 @@ follows:
@subsection RISC-V Debug Configuration Commands
+@deffn {Command} {riscv dump_sample_buf} [base64]
+Dump and clear the contents of the sample buffer. Which samples are collected
+is configured with @code{riscv memory_sample}. If the optional base64
+argument is passed, the raw buffer is dumped in base64 format, so that
+external tools can gather the data efficiently.
+@end deffn
+
@deffn {Config Command} {riscv expose_csrs} n[-m|=name] [...]
Configure which CSRs to expose in addition to the standard ones. The CSRs to expose
can be specified as individual register numbers or register ranges (inclusive). For the
@@ -10630,13 +10668,13 @@ CSRs.
@example
# Expose a single RISC-V CSR number 128 under the name "csr128":
-$_TARGETNAME expose_csrs 128
+riscv expose_csrs 128
# Expose multiple RISC-V CSRs 128..132 under names "csr128" through "csr132":
-$_TARGETNAME expose_csrs 128-132
+riscv expose_csrs 128-132
# Expose a single RISC-V CSR number 1996 under custom name "csr_myregister":
-$_TARGETNAME expose_csrs 1996=myregister
+riscv expose_csrs 1996=myregister
@end example
@end deffn
@@ -10664,6 +10702,39 @@ $_TARGETNAME expose_custom 32=myregister
@end example
@end deffn
+@deffn {Config Command} {riscv hide_csrs} n[-m] [,n1[-m1]] [...]
+The RISC-V Specification defines many CSRs, and we may want to avoid showing
+each CSR to the user, as they may not be relevant to the task at hand. For
+example, we may choose not to show trigger or PMU registers for simple
+debugging scenarios. This command allows to mark individual registers or
+register ranges (inclusive) as "hidden". Such hidden registers won't be
+displayed in GDB or @code{reg} command output.
+
+@example
+
+# Hide range of RISC-V CSRs
+# CSR_TSELECT - 1952 and CSR_TDATA1 - 1953
+$_TARGETNAME riscv hide_csrs 1952-1953
+
+@end example
+@end deffn
+
+@deffn {Command} {riscv memory_sample} bucket address|clear [size=4]
+Configure OpenOCD to frequently read size bytes at the given addresses.
+Execute the command with no arguments to see the current configuration. Use
+clear to stop using a given bucket.
+
+OpenOCD will allocate a 1MB sample buffer, and when it fills up no more
+samples will be collected until it is emptied with @code{riscv
+dump_sample_buf}.
+@end deffn
+
+@deffn {Command} {riscv repeat_read} count address [size=4]
+Quickly read count words of the given size from address. This can be useful
+to read out a buffer that's memory-mapped to be accessed through a single
+address, or to sample a changing value in a memory-mapped device.
+@end deffn
+
@deffn {Command} {riscv info}
Displays some information OpenOCD detected about the target.
@end deffn
@@ -10684,6 +10755,11 @@ Set the maximum time to wait for a hart to come out of reset after reset is
deasserted.
@end deffn
+@deffn {Command} {riscv set_scratch_ram} none|[address]
+Set the address of 16 bytes of scratch RAM the debugger can use, or 'none'.
+This is used to access 64-bit floating point registers on 32-bit targets.
+@end deffn
+
@deffn {Command} {riscv set_mem_access} method1 [method2] [method3]
Specify which RISC-V memory access method(s) shall be used, and in which order
of priority. At least one method must be specified.
@@ -10736,9 +10812,48 @@ When utilizing version 0.11 of the RISC-V Debug Specification,
and DBUS registers, respectively.
@end deffn
+@deffn {Command} {riscv smp} [on|off]
+Display, enable or disable SMP handling mode. This command is needed only if
+user wants to temporary @b{disable} SMP handling for an existing SMP group
+(see @code{aarch64 smp} for additional information). To define an SMP
+group the command @code{target smp} should be used.
+@end deffn
+
+@deffn {Command} {riscv smp_gdb} [core_id]
+Display/set the current core displayed in GDB. This is needed only if
+@code{riscv smp} was used.
+@end deffn
+
@deffn {Command} {riscv use_bscan_tunnel} value
-Enable or disable use of a BSCAN tunnel to reach DM. Supply the width of
-the DM transport TAP's instruction register to enable. Supply a value of 0 to disable.
+Enable or disable use of a BSCAN tunnel to reach the Debug Module. Supply the
+width of the DM transport TAP's instruction register to enable. Supply a
+value of 0 to disable.
+
+This BSCAN tunnel interface is specific to SiFive IP. Anybody may implement
+it, but currently there is no good documentation on it. In a nutshell, this
+feature scans USER4 into a Xilinx TAP to select the tunnel device (assuming
+hardware is present and it is hooked up to the Xilinx USER4 IR) and
+encapsulates a tunneled scan directive into a DR scan into the Xilinx TAP. A
+tunneled DR scan consists of:
+@enumerate
+@item 1 bit that selects IR when 0, or DR when 1
+@item 7 bits that encode the width of the desired tunneled scan
+@item A width+1 stream of bits for the tunneled TDI. The plus one is because there is a one-clock skew between TDI of Xilinx chain and TDO from tunneled chain.
+@item 3 bits of zero that the tunnel uses to go back to idle state.
+@end enumerate
+
+@end deffn
+
+@deffn {Command} {riscv set_bscan_tunnel_ir} value
+Allows the use_bscan_tunnel feature to target non Xilinx device by
+specifying the JTAG TAP IR used to access the bscan tunnel.
+@end deffn
+
+@deffn {Command} {riscv set_maskisr} [@option{off}|@option{steponly}]
+Selects whether interrupts will be disabled when single stepping. The default configuration is @option{off}.
+This feature is only useful on hardware that always steps into interrupts and doesn't support dcsr.stepie=0.
+Keep in mind, disabling the option does not guarantee that single stepping will go into interrupt handlers.
+To make that happen, dcsr.stepie would have to be written to 1 as well.
@end deffn
@deffn {Command} {riscv set_ebreakm} on|off
@@ -10766,12 +10881,14 @@ set challenge [riscv authdata_read]
riscv authdata_write [expr @{$challenge + 1@}]
@end example
-@deffn {Command} {riscv authdata_read}
-Return the 32-bit value read from authdata.
+@deffn {Command} {riscv authdata_read} [index=0]
+Return the 32-bit value read from authdata or authdata0 (index=0), or
+authdata1 (index=1).
@end deffn
-@deffn {Command} {riscv authdata_write} value
-Write the 32-bit value to authdata.
+@deffn {Command} {riscv authdata_write} [index=0] value
+Write the 32-bit value to authdata or authdata0 (index=0), or authdata1
+(index=1).
@end deffn
@subsection RISC-V DMI Commands
@@ -10787,6 +10904,94 @@ Perform a 32-bit DMI read at address, returning the value.
Perform a 32-bit DMI write of value at address.
@end deffn
+@subsection RISC-V Trigger Commands
+
+The RISC-V Debug Specification defines several optional trigger types that don't
+map cleanly onto OpenOCD's notion of hardware breakpoints. For the types that
+the target supports, these commands let you
+set those triggers directly. (It's also possible to do so by writing the
+appropriate CSRs.)
+
+@deffn {Command} {riscv etrigger set} [@option{m}] [@option{s}] [@option{u}] [@option{vs}] [@option{vu}] exception_codes
+Set an exception trigger (type 5) on the current target, which halts the target when it
+fires. @option{m}, @option{s}, @option{u}, @option{vs}, and @option{vu} control
+which execution modes the trigger fires in. @var{exception_codes} is a bit
+field, where each bit corresponds to an exception code in mcause (defined in the
+RISC-V Privileged Spec). The etrigger will fire on the exceptions whose bits are
+set in @var{exception_codes}.
+
+For details on this trigger type, see the RISC-V Debug Specification.
+@end deffn
+
+@deffn {Command} {riscv etrigger clear}
+Clear the type 5 trigger that was set using @command{riscv etrigger set}.
+@end deffn
+
+@deffn {Command} {riscv icount set} [@option{m}] [@option{s}] [@option{u}] [@option{vs}] [@option{vu}] [@option{pending}] count
+Set an instruction count
+trigger (type 3) on the current target, which halts the target when it fires.
+@option{m}, @option{s}, @option{u}, @option{vs}, and @option{vu} control which
+execution modes the trigger fires in. If [@option{pending}] is passed then the
+pending bit is set, which is unlikely to be useful unless you're debugging the
+hardware implementation of this trigger.
+@var{count} sets the number of instructions to execute before the trigger is
+taken.
+
+For details on this trigger type, see the RISC-V Debug Specification.
+@end deffn
+
+@deffn {Command} {riscv icount clear}
+Clear the type 3 trigger that was set using @command{riscv icount set}.
+@end deffn
+
+@deffn {Command} {riscv itrigger set} [@option{m}] [@option{s}] [@option{u}] [@option{vs}] [@option{vu}] [@option{nmi}] mie_bits
+Set an interrupt trigger (type 4) on the current target, which halts the target when it
+fires. @option{m}, @option{s}, @option{u}, @option{vs}, and @option{vu} control
+which execution modes the trigger fires in. If [@option{nmi}] is passed then
+the trigger will fire on non-maskable interrupts in those modes. @var{mie_bits}
+controls which interrupts the trigger fires on, using the same bit assignments
+as in the mie CSR (defined in the RISC-V Privileged Spec).
+
+For details on this trigger type, see the RISC-V Debug Specification.
+@end deffn
+
+@deffn {Command} {riscv itrigger clear}
+Clear the type 4 trigger that was set using @command{riscv itrigger set}.
+@end deffn
+
+@subsection RISC-V Program Buffer Commands
+
+Program Buffer is an optional feature of RISC-V targets - it is a mechanism that debuggers
+can use to execute sequences of arbitrary instructions (small programs) on the target.
+For details on the Program Buffer, please refer to the RISC-V Debug Specification.
+
+@deffn {Command} {riscv exec_progbuf} inst1 [inst2 [... inst16]]
+Execute the given sequence of instructions on the target using the Program Buffer.
+The command can only be used on halted targets.
+
+The instructions @var{inst1} .. @var{inst16} shall be specified in their binary form
+(as 32-bit integers). In case a pair of compressed (16-bit) instructions is used,
+the first instruction should be placed to the lower 16-bits of the 32-bit value.
+The terminating @var{ebreak} instruction needs not be specified - it is added
+automatically if needed.
+@end deffn
+
+Examples:
+
+@example
+# Execute 32-bit instructions "fence rw,rw" (0x0330000f)
+# and "fence.i" (0x0000100f) using the Program Buffer,
+# in this order:
+
+riscv exec_progbuf 0x0330000f 0x0000100f
+
+# Execute 16-bit instructions "c.addi s0,s0,1" (0x0405)
+# and "c.add s1,s1,s0" (0x94a2) using the Program Buffer,
+# in this order:
+
+riscv exec_progbuf 0x94a20405
+@end example
+
@section ARC Architecture
@cindex ARC
@@ -11926,6 +12131,14 @@ contrib/rtos-helpers/FreeRTOS-openocd.c
contrib/rtos-helpers/uCOS-III-openocd.c
@end table
+@section RTOS Commands
+@cindex RTOS Commands
+
+@deffn {Config Command} {freertos_ticktype_size} (2|4|8)
+Pass the size (in bytes) of FreeRTOS TickType_t to OpenOCD. To make sure the
+calculation of offsets and sizes is correct. Defaults to 4.
+@end deffn
+
@anchor{usingopenocdsmpwithgdb}
@section Using OpenOCD SMP with GDB
@cindex SMP
diff --git a/git-hooks/commit-msg b/git-hooks/commit-msg
new file mode 100755
index 0000000..99d96cd
--- /dev/null
+++ b/git-hooks/commit-msg
@@ -0,0 +1,191 @@
+#!/bin/sh
+# From Gerrit Code Review 2.13.5
+#
+# Part of Gerrit Code Review (https://www.gerritcodereview.com/)
+#
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+unset GREP_OPTIONS
+
+CHANGE_ID_AFTER="Bug|Issue|Test|Feature|Fixes|Fixed"
+MSG="$1"
+
+# Check for, and add if missing, a unique Change-Id
+#
+add_ChangeId() {
+ clean_message=`sed -e '
+ /^diff --git .*/{
+ s///
+ q
+ }
+ /^Signed-off-by:/d
+ /^#/d
+ ' "$MSG" | git stripspace`
+ if test -z "$clean_message"
+ then
+ return
+ fi
+
+ # Do not add Change-Id to temp commits
+ if echo "$clean_message" | head -1 | grep -q '^\(fixup\|squash\)!'
+ then
+ return
+ fi
+
+ if test "false" = "`git config --bool --get gerrit.createChangeId`"
+ then
+ return
+ fi
+
+ # Does Change-Id: already exist? if so, exit (no change).
+ if grep -i '^Change-Id:' "$MSG" >/dev/null
+ then
+ return
+ fi
+
+ id=`_gen_ChangeId`
+ T="$MSG.tmp.$$"
+ AWK=awk
+ if [ -x /usr/xpg4/bin/awk ]; then
+ # Solaris AWK is just too broken
+ AWK=/usr/xpg4/bin/awk
+ fi
+
+ # Get core.commentChar from git config or use default symbol
+ commentChar=`git config --get core.commentChar`
+ commentChar=${commentChar:-#}
+
+ # How this works:
+ # - parse the commit message as (textLine+ blankLine*)*
+ # - assume textLine+ to be a footer until proven otherwise
+ # - exception: the first block is not footer (as it is the title)
+ # - read textLine+ into a variable
+ # - then count blankLines
+ # - once the next textLine appears, print textLine+ blankLine* as these
+ # aren't footer
+ # - in END, the last textLine+ block is available for footer parsing
+ $AWK '
+ BEGIN {
+ # while we start with the assumption that textLine+
+ # is a footer, the first block is not.
+ isFooter = 0
+ footerComment = 0
+ blankLines = 0
+ }
+
+ # Skip lines starting with commentChar without any spaces before it.
+ /^'"$commentChar"'/ { next }
+
+ # Skip the line starting with the diff command and everything after it,
+ # up to the end of the file, assuming it is only patch data.
+ # If more than one line before the diff was empty, strip all but one.
+ /^diff --git / {
+ blankLines = 0
+ while (getline) { }
+ next
+ }
+
+ # Count blank lines outside footer comments
+ /^$/ && (footerComment == 0) {
+ blankLines++
+ next
+ }
+
+ # Catch footer comment
+ /^\[[a-zA-Z0-9-]+:/ && (isFooter == 1) {
+ footerComment = 1
+ }
+
+ /]$/ && (footerComment == 1) {
+ footerComment = 2
+ }
+
+ # We have a non-blank line after blank lines. Handle this.
+ (blankLines > 0) {
+ print lines
+ for (i = 0; i < blankLines; i++) {
+ print ""
+ }
+
+ lines = ""
+ blankLines = 0
+ isFooter = 1
+ footerComment = 0
+ }
+
+ # Detect that the current block is not the footer
+ (footerComment == 0) && (!/^\[?[a-zA-Z0-9-]+:/ || /^[a-zA-Z0-9-]+:\/\//) {
+ isFooter = 0
+ }
+
+ {
+ # We need this information about the current last comment line
+ if (footerComment == 2) {
+ footerComment = 0
+ }
+ if (lines != "") {
+ lines = lines "\n";
+ }
+ lines = lines $0
+ }
+
+ # Footer handling:
+ # If the last block is considered a footer, splice in the Change-Id at the
+ # right place.
+ # Look for the right place to inject Change-Id by considering
+ # CHANGE_ID_AFTER. Keys listed in it (case insensitive) come first,
+ # then Change-Id, then everything else (eg. Signed-off-by:).
+ #
+ # Otherwise just print the last block, a new line and the Change-Id as a
+ # block of its own.
+ END {
+ unprinted = 1
+ if (isFooter == 0) {
+ print lines "\n"
+ lines = ""
+ }
+ changeIdAfter = "^(" tolower("'"$CHANGE_ID_AFTER"'") "):"
+ numlines = split(lines, footer, "\n")
+ for (line = 1; line <= numlines; line++) {
+ if (unprinted && match(tolower(footer[line]), changeIdAfter) != 1) {
+ unprinted = 0
+ print "Change-Id: I'"$id"'"
+ }
+ print footer[line]
+ }
+ if (unprinted) {
+ print "Change-Id: I'"$id"'"
+ }
+ }' "$MSG" > "$T" && mv "$T" "$MSG" || rm -f "$T"
+}
+_gen_ChangeIdInput() {
+ echo "tree `git write-tree`"
+ if parent=`git rev-parse "HEAD^0" 2>/dev/null`
+ then
+ echo "parent $parent"
+ fi
+ echo "author `git var GIT_AUTHOR_IDENT`"
+ echo "committer `git var GIT_COMMITTER_IDENT`"
+ echo
+ printf '%s' "$clean_message"
+}
+_gen_ChangeId() {
+ _gen_ChangeIdInput |
+ git hash-object -t commit --stdin
+}
+
+
+add_ChangeId
diff --git a/git-hooks/pre-push b/git-hooks/pre-push
new file mode 100755
index 0000000..a7e1266
--- /dev/null
+++ b/git-hooks/pre-push
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+./tools/checkpatch.sh
diff --git a/jimtcl b/jimtcl
-Subproject a77ef1a6218fad4c928ddbdc03c1aedc41007e7
+Subproject 70b007b63669a709b0e8aef34a22658047815cc
diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c
index b9353d8..bd3363b 100644
--- a/src/flash/nor/drivers.c
+++ b/src/flash/nor/drivers.c
@@ -33,6 +33,7 @@ extern const struct flash_driver faux_flash;
extern const struct flash_driver fm3_flash;
extern const struct flash_driver fm4_flash;
extern const struct flash_driver fespi_flash;
+extern const struct flash_driver gd32vf103_flash;
extern const struct flash_driver jtagspi_flash;
extern const struct flash_driver kinetis_flash;
extern const struct flash_driver kinetis_ke_flash;
@@ -109,6 +110,7 @@ static const struct flash_driver * const flash_drivers[] = {
&fm3_flash,
&fm4_flash,
&fespi_flash,
+ &gd32vf103_flash,
&jtagspi_flash,
&kinetis_flash,
&kinetis_ke_flash,
diff --git a/src/flash/nor/stm32f1x.c b/src/flash/nor/stm32f1x.c
index ab1ef2a..04f4da0 100644
--- a/src/flash/nor/stm32f1x.c
+++ b/src/flash/nor/stm32f1x.c
@@ -1740,3 +1740,55 @@ const struct flash_driver stm32f1x_flash = {
.info = get_stm32x_info,
.free_driver_priv = default_flash_free_driver_priv,
};
+
+/* flash bank gd32vf103 <base> <size> 0 0 <target#>
+ */
+FLASH_BANK_COMMAND_HANDLER(gd32vf103_flash_bank_command)
+{
+ struct stm32x_flash_bank *stm32x_info;
+
+ LOG_WARNING("DEPRECATED: The gd32vf103 flash target will be removed in June of 2023, please use stm32f1x instead.");
+ /* The reset code are just copy from stm32x_flash_bank_command function */
+ if (CMD_ARGC < 6)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ stm32x_info = malloc(sizeof(struct stm32x_flash_bank));
+
+ bank->driver_priv = stm32x_info;
+ stm32x_info->probed = false;
+ stm32x_info->has_dual_banks = false;
+ stm32x_info->can_load_options = false;
+ stm32x_info->register_base = FLASH_REG_BASE_B0;
+ stm32x_info->user_bank_size = bank->size;
+
+ /* The flash write must be aligned to a halfword boundary */
+ bank->write_start_alignment = bank->write_end_alignment = 2;
+ return ERROR_OK;
+}
+
+static const struct command_registration gd32vf103_command_handlers[] = {
+ {
+ .name = "gd32vf103",
+ .mode = COMMAND_ANY,
+ .help = "gd32vf103 flash command group (identical to flash commands from stm32f1x)",
+ .usage = "",
+ .chain = stm32f1x_exec_command_handlers,
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+const struct flash_driver gd32vf103_flash = {
+ .name = "gd32vf103",
+ .commands = gd32vf103_command_handlers,
+ .flash_bank_command = gd32vf103_flash_bank_command,
+ .erase = stm32x_erase,
+ .protect = stm32x_protect,
+ .write = stm32x_write,
+ .read = default_flash_read,
+ .probe = stm32x_probe,
+ .auto_probe = stm32x_auto_probe,
+ .erase_check = default_flash_blank_check,
+ .protect_check = stm32x_protect_check,
+ .info = get_stm32x_info,
+ .free_driver_priv = default_flash_free_driver_priv,
+};
diff --git a/src/flash/nor/stm32lx.c b/src/flash/nor/stm32lx.c
index 1459e94..0c76ed7 100644
--- a/src/flash/nor/stm32lx.c
+++ b/src/flash/nor/stm32lx.c
@@ -890,11 +890,10 @@ static int stm32lx_get_info(struct flash_bank *bank, struct command_invocation *
if (rev_id == info->revs[i].rev)
rev_str = info->revs[i].str;
- if (rev_str) {
+ if (rev_str)
command_print_sameline(cmd, "%s - Rev: %s", info->device_str, rev_str);
- } else {
+ else
command_print_sameline(cmd, "%s - Rev: unknown (0x%04x)", info->device_str, rev_id);
- }
return ERROR_OK;
}
diff --git a/src/flash/nor/tcl.c b/src/flash/nor/tcl.c
index 4ff6d98..0562906 100644
--- a/src/flash/nor/tcl.c
+++ b/src/flash/nor/tcl.c
@@ -720,7 +720,8 @@ COMMAND_HANDLER(handle_flash_md_command)
retval = flash_driver_read(bank, buffer, offset, sizebytes);
if (retval == ERROR_OK)
- target_handle_md_output(CMD, target, address, wordsize, count, buffer);
+ target_handle_md_output(CMD, target, address, wordsize, count,
+ buffer, true);
free(buffer);
diff --git a/src/helper/Makefile.am b/src/helper/Makefile.am
index 7354f54..0268694 100644
--- a/src/helper/Makefile.am
+++ b/src/helper/Makefile.am
@@ -30,7 +30,9 @@ noinst_LTLIBRARIES += %D%/libhelper.la
%D%/system.h \
%D%/jep106.h \
%D%/jep106.inc \
- %D%/jim-nvp.h
+ %D%/jim-nvp.h \
+ %D%/base64.c \
+ %D%/base64.h
STARTUP_TCL_SRCS += %D%/startup.tcl
EXTRA_DIST += \
diff --git a/src/helper/base64.c b/src/helper/base64.c
new file mode 100644
index 0000000..effa8ac
--- /dev/null
+++ b/src/helper/base64.c
@@ -0,0 +1,157 @@
+/*
+ * Base64 encoding/decoding (RFC1341)
+ * Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "base64.h"
+
+static const unsigned char base64_table[65] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/**
+ * base64_encode - Base64 encode
+ * @src: Data to be encoded
+ * @len: Length of the data to be encoded
+ * @out_len: Pointer to output length variable, or %NULL if not used
+ * Returns: Allocated buffer of out_len bytes of encoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer. Returned buffer is
+ * nul terminated to make it easier to use as a C string. The nul terminator is
+ * not included in out_len.
+ */
+unsigned char *base64_encode(const unsigned char *src, size_t len,
+ size_t *out_len)
+{
+ unsigned char *out, *pos;
+ const unsigned char *end, *in;
+ size_t olen;
+ int line_len;
+
+ olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
+ olen += olen / 72; /* line feeds */
+ olen++; /* nul termination */
+ if (olen < len)
+ return NULL; /* integer overflow */
+ out = malloc(olen);
+ if (out == NULL)
+ return NULL;
+
+ end = src + len;
+ in = src;
+ pos = out;
+ line_len = 0;
+ while (end - in >= 3) {
+ *pos++ = base64_table[in[0] >> 2];
+ *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
+ *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
+ *pos++ = base64_table[in[2] & 0x3f];
+ in += 3;
+ line_len += 4;
+ if (line_len >= 72) {
+ *pos++ = '\n';
+ line_len = 0;
+ }
+ }
+
+ if (end - in) {
+ *pos++ = base64_table[in[0] >> 2];
+ if (end - in == 1) {
+ *pos++ = base64_table[(in[0] & 0x03) << 4];
+ *pos++ = '=';
+ } else {
+ *pos++ = base64_table[((in[0] & 0x03) << 4) |
+ (in[1] >> 4)];
+ *pos++ = base64_table[(in[1] & 0x0f) << 2];
+ }
+ *pos++ = '=';
+ line_len += 4;
+ }
+
+ if (line_len)
+ *pos++ = '\n';
+
+ *pos = '\0';
+ if (out_len)
+ *out_len = pos - out;
+ return out;
+}
+
+
+/**
+ * base64_decode - Base64 decode
+ * @src: Data to be decoded
+ * @len: Length of the data to be decoded
+ * @out_len: Pointer to output length variable
+ * Returns: Allocated buffer of out_len bytes of decoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer.
+ */
+unsigned char *base64_decode(const unsigned char *src, size_t len,
+ size_t *out_len)
+{
+ unsigned char dtable[256], *out, *pos, block[4], tmp;
+ size_t i, count, olen;
+ int pad = 0;
+
+ memset(dtable, 0x80, 256);
+ for (i = 0; i < sizeof(base64_table) - 1; i++)
+ dtable[base64_table[i]] = (unsigned char) i;
+ dtable['='] = 0;
+
+ count = 0;
+ for (i = 0; i < len; i++) {
+ if (dtable[src[i]] != 0x80)
+ count++;
+ }
+
+ if (count == 0 || count % 4)
+ return NULL;
+
+ olen = count / 4 * 3;
+ pos = out = malloc(olen);
+ if (out == NULL)
+ return NULL;
+
+ count = 0;
+ for (i = 0; i < len; i++) {
+ tmp = dtable[src[i]];
+ if (tmp == 0x80)
+ continue;
+
+ if (src[i] == '=')
+ pad++;
+ block[count] = tmp;
+ count++;
+ if (count == 4) {
+ *pos++ = (block[0] << 2) | (block[1] >> 4);
+ *pos++ = (block[1] << 4) | (block[2] >> 2);
+ *pos++ = (block[2] << 6) | block[3];
+ count = 0;
+ if (pad) {
+ if (pad == 1)
+ pos--;
+ else if (pad == 2)
+ pos -= 2;
+ else {
+ /* Invalid padding */
+ free(out);
+ return NULL;
+ }
+ break;
+ }
+ }
+ }
+
+ *out_len = pos - out;
+ return out;
+}
diff --git a/src/helper/base64.h b/src/helper/base64.h
new file mode 100644
index 0000000..dbabb60
--- /dev/null
+++ b/src/helper/base64.h
@@ -0,0 +1,17 @@
+/*
+ * Base64 encoding/decoding (RFC1341)
+ * Copyright (c) 2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BASE64_H
+#define BASE64_H
+
+unsigned char *base64_encode(const unsigned char *src, size_t len,
+ size_t *out_len);
+unsigned char *base64_decode(const unsigned char *src, size_t len,
+ size_t *out_len);
+
+#endif /* BASE64_H */
diff --git a/src/jtag/adapter.c b/src/jtag/adapter.c
index eb73fcb..430af6c 100644
--- a/src/jtag/adapter.c
+++ b/src/jtag/adapter.c
@@ -18,10 +18,6 @@
#include "interfaces.h"
#include <transport/transport.h>
-#ifdef HAVE_STRINGS_H
-#include <strings.h>
-#endif
-
/**
* @file
* Holds support for configuring debug adapters from TCl scripts.
diff --git a/src/jtag/drivers/ftdi.c b/src/jtag/drivers/ftdi.c
index 3097d41..d2dd893 100644
--- a/src/jtag/drivers/ftdi.c
+++ b/src/jtag/drivers/ftdi.c
@@ -74,6 +74,16 @@
/* FTDI access library includes */
#include "mpsse.h"
+#if BUILD_FTDI_CJTAG == 1
+#define DO_CLOCK_DATA clock_data
+#define DO_CLOCK_TMS_CS clock_tms_cs
+#define DO_CLOCK_TMS_CS_OUT clock_tms_cs_out
+#else
+#define DO_CLOCK_DATA mpsse_clock_data
+#define DO_CLOCK_TMS_CS mpsse_clock_tms_cs
+#define DO_CLOCK_TMS_CS_OUT mpsse_clock_tms_cs_out
+#endif
+
#define JTAG_MODE (LSB_FIRST | POS_EDGE_IN | NEG_EDGE_OUT)
#define JTAG_MODE_ALT (LSB_FIRST | NEG_EDGE_IN | NEG_EDGE_OUT)
#define SWD_MODE (LSB_FIRST | POS_EDGE_IN | NEG_EDGE_OUT)
@@ -84,6 +94,39 @@ static uint8_t ftdi_jtag_mode = JTAG_MODE;
static bool swd_mode;
+#if BUILD_FTDI_CJTAG == 1
+#define ESCAPE_SEQ_OAC_BIT2 28
+
+static void cjtag_reset_online_activate(void);
+
+/*
+ The cJTAG 2-wire OScan1 protocol, in lieu of 4-wire JTAG, is a configuration option
+ for some SoCs. An FTDI-based adapter that can be configured to appropriately drive
+ the bidirectional pin TMSC is able to drive OScan1 protocol. For example, an Olimex
+ ARM-USB-TINY-H with the ARM-JTAG-SWD adapter, connected to a cJTAG-enabled
+ target board is such a topology. A TCK cycle with TMS=1/TDI=N translates to a TMSC
+ output of N, and a TCK cycle with TMS=0 translates to a TMSC input from the target back
+ to the adapter/probe. The OScan1 protocol uses 3 TCK cycles to generate the data flow
+ that is equivalent to that of a single TCK cycle in 4-wire JTAG. The OScan1-related
+ code in this module translates IR/DR scan commanads and JTAG state traversal commands
+ to the two-wire clocking and signaling of OScan1 protocol, if placed into OScan1 mode
+ during initialization.
+*/
+static void oscan1_mpsse_clock_data(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, uint8_t *in,
+ unsigned in_offset, unsigned length, uint8_t mode);
+static void oscan1_mpsse_clock_tms_cs(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, uint8_t *in,
+ unsigned in_offset, unsigned length, bool tdi, uint8_t mode);
+static void oscan1_mpsse_clock_tms_cs_out(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset,
+ unsigned length, bool tdi, uint8_t mode);
+
+static bool oscan1_mode;
+
+/*
+ The cJTAG 4-wire JScan3 allows to use standard JTAG protocol with cJTAG hardware
+*/
+static bool jscan3_mode;
+#endif
+
#define MAX_USB_IDS 8
/* vid = pid = 0 marks the end of the list */
static uint16_t ftdi_vid[MAX_USB_IDS + 1] = { 0 };
@@ -229,6 +272,35 @@ static int ftdi_get_signal(const struct signal *s, uint16_t *value_out)
return ERROR_OK;
}
+#if BUILD_FTDI_CJTAG == 1
+static void clock_data(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, uint8_t *in,
+ unsigned in_offset, unsigned length, uint8_t mode)
+{
+ if (oscan1_mode)
+ oscan1_mpsse_clock_data(ctx, out, out_offset, in, in_offset, length, mode);
+ else
+ mpsse_clock_data(ctx, out, out_offset, in, in_offset, length, mode);
+}
+
+static void clock_tms_cs(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, uint8_t *in,
+ unsigned in_offset, unsigned length, bool tdi, uint8_t mode)
+{
+ if (oscan1_mode)
+ oscan1_mpsse_clock_tms_cs(ctx, out, out_offset, in, in_offset, length, tdi, mode);
+ else
+ mpsse_clock_tms_cs(ctx, out, out_offset, in, in_offset, length, tdi, mode);
+}
+
+static void clock_tms_cs_out(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset,
+ unsigned length, bool tdi, uint8_t mode)
+{
+ if (oscan1_mode)
+ oscan1_mpsse_clock_tms_cs_out(ctx, out, out_offset, length, tdi, mode);
+ else
+ mpsse_clock_tms_cs_out(ctx, out, out_offset, length, tdi, mode);
+}
+#endif
+
/**
* Function move_to_state
* moves the TAP controller from the current state to a
@@ -257,7 +329,7 @@ static void move_to_state(tap_state_t goal_state)
for (int i = 0; i < tms_count; i++)
tap_set_state(tap_state_transition(tap_get_state(), (tms_bits >> i) & 1));
- mpsse_clock_tms_cs_out(mpsse_ctx,
+ DO_CLOCK_TMS_CS_OUT(mpsse_ctx,
&tms_bits,
0,
tms_count,
@@ -311,7 +383,7 @@ static void ftdi_end_state(tap_state_t state)
static void ftdi_execute_runtest(struct jtag_command *cmd)
{
int i;
- uint8_t zero = 0;
+ static const uint8_t zero;
LOG_DEBUG_IO("runtest %i cycles, end in %s",
cmd->cmd.runtest->num_cycles,
@@ -325,7 +397,7 @@ static void ftdi_execute_runtest(struct jtag_command *cmd)
while (i > 0) {
/* there are no state transitions in this code, so omit state tracking */
unsigned this_len = i > 7 ? 7 : i;
- mpsse_clock_tms_cs_out(mpsse_ctx, &zero, 0, this_len, false, ftdi_jtag_mode);
+ DO_CLOCK_TMS_CS_OUT(mpsse_ctx, &zero, 0, this_len, false, ftdi_jtag_mode);
i -= this_len;
}
@@ -360,7 +432,7 @@ static void ftdi_execute_tms(struct jtag_command *cmd)
LOG_DEBUG_IO("TMS: %d bits", cmd->cmd.tms->num_bits);
/* TODO: Missing tap state tracking, also missing from ft2232.c! */
- mpsse_clock_tms_cs_out(mpsse_ctx,
+ DO_CLOCK_TMS_CS_OUT(mpsse_ctx,
cmd->cmd.tms->bits,
0,
cmd->cmd.tms->num_bits,
@@ -407,7 +479,7 @@ static void ftdi_execute_pathmove(struct jtag_command *cmd)
state_count++;
if (bit_count == 7 || num_states == 0) {
- mpsse_clock_tms_cs_out(mpsse_ctx,
+ DO_CLOCK_TMS_CS_OUT(mpsse_ctx,
&tms_byte,
0,
bit_count,
@@ -461,7 +533,7 @@ static void ftdi_execute_scan(struct jtag_command *cmd)
if (i == cmd->cmd.scan->num_fields - 1 && tap_get_state() != tap_get_end_state()) {
/* Last field, and we're leaving IRSHIFT/DRSHIFT. Clock last bit during tap
* movement. This last field can't have length zero, it was checked above. */
- mpsse_clock_data(mpsse_ctx,
+ DO_CLOCK_DATA(mpsse_ctx,
field->out_value,
0,
field->in_value,
@@ -471,12 +543,8 @@ static void ftdi_execute_scan(struct jtag_command *cmd)
uint8_t last_bit = 0;
if (field->out_value)
bit_copy(&last_bit, 0, field->out_value, field->num_bits - 1, 1);
-
- /* If endstate is TAP_IDLE, clock out 1-1-0 (->EXIT1 ->UPDATE ->IDLE)
- * Otherwise, clock out 1-0 (->EXIT1 ->PAUSE)
- */
uint8_t tms_bits = 0x03;
- mpsse_clock_tms_cs(mpsse_ctx,
+ DO_CLOCK_TMS_CS(mpsse_ctx,
&tms_bits,
0,
field->in_value,
@@ -486,7 +554,7 @@ static void ftdi_execute_scan(struct jtag_command *cmd)
ftdi_jtag_mode);
tap_set_state(tap_state_transition(tap_get_state(), 1));
if (tap_get_end_state() == TAP_IDLE) {
- mpsse_clock_tms_cs_out(mpsse_ctx,
+ DO_CLOCK_TMS_CS_OUT(mpsse_ctx,
&tms_bits,
1,
2,
@@ -495,7 +563,7 @@ static void ftdi_execute_scan(struct jtag_command *cmd)
tap_set_state(tap_state_transition(tap_get_state(), 1));
tap_set_state(tap_state_transition(tap_get_state(), 0));
} else {
- mpsse_clock_tms_cs_out(mpsse_ctx,
+ DO_CLOCK_TMS_CS_OUT(mpsse_ctx,
&tms_bits,
2,
1,
@@ -504,7 +572,7 @@ static void ftdi_execute_scan(struct jtag_command *cmd)
tap_set_state(tap_state_transition(tap_get_state(), 0));
}
} else
- mpsse_clock_data(mpsse_ctx,
+ DO_CLOCK_DATA(mpsse_ctx,
field->out_value,
0,
field->in_value,
@@ -585,7 +653,7 @@ static void ftdi_execute_stableclocks(struct jtag_command *cmd)
while (num_cycles > 0) {
/* there are no state transitions in this code, so omit state tracking */
unsigned this_len = num_cycles > 7 ? 7 : num_cycles;
- mpsse_clock_tms_cs_out(mpsse_ctx, &tms, 0, this_len, false, ftdi_jtag_mode);
+ DO_CLOCK_TMS_CS_OUT(mpsse_ctx, &tms, 0, this_len, false, ftdi_jtag_mode);
num_cycles -= this_len;
}
@@ -597,10 +665,19 @@ static void ftdi_execute_stableclocks(struct jtag_command *cmd)
static void ftdi_execute_command(struct jtag_command *cmd)
{
switch (cmd->type) {
+#if BUILD_FTDI_CJTAG == 1
+ case JTAG_RESET:
+ if (cmd->cmd.reset->trst)
+ cjtag_reset_online_activate(); /* put the target (back) into selected cJTAG mode */
+ break;
+#endif
case JTAG_RUNTEST:
ftdi_execute_runtest(cmd);
break;
case JTAG_TLR_RESET:
+#if BUILD_FTDI_CJTAG == 1
+ cjtag_reset_online_activate(); /* put the target (back) into selected cJTAG mode */
+#endif
ftdi_execute_statemove(cmd);
break;
case JTAG_PATHMOVE:
@@ -680,6 +757,21 @@ static int ftdi_initialize(void)
/* A dummy SWD_EN would have zero mask */
if (sig->data_mask)
ftdi_set_signal(sig, '1');
+#if BUILD_FTDI_CJTAG == 1
+ } else if (oscan1_mode || jscan3_mode) {
+ struct signal *sig = find_signal_by_name("JTAG_SEL");
+ if (!sig) {
+ LOG_ERROR("A cJTAG mode is active but JTAG_SEL signal is not defined");
+ return ERROR_JTAG_INIT_FAILED;
+ }
+ /* A dummy JTAG_SEL would have zero mask */
+ if (sig->data_mask)
+ ftdi_set_signal(sig, '0');
+ else if (jscan3_mode) {
+ LOG_ERROR("In JScan3 mode JTAG_SEL signal cannot be dummy, data mask needed");
+ return ERROR_JTAG_INIT_FAILED;
+ }
+#endif
}
mpsse_set_data_bits_low_byte(mpsse_ctx, output & 0xff, direction & 0xff);
@@ -711,6 +803,290 @@ static int ftdi_quit(void)
return ERROR_OK;
}
+#if BUILD_FTDI_CJTAG == 1
+static void oscan1_mpsse_clock_data(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, uint8_t *in,
+ unsigned in_offset, unsigned length, uint8_t mode)
+{
+ static const uint8_t zero;
+ static const uint8_t one = 1;
+
+ struct signal *tmsc_en = find_signal_by_name("TMSC_EN");
+
+ LOG_DEBUG_IO("oscan1_mpsse_clock_data: %sout %d bits", in ? "in" : "", length);
+
+ for (unsigned i = 0; i < length; i++) {
+ int bitnum;
+ uint8_t bit;
+
+ /* OScan1 uses 3 separate clocks */
+
+ /* drive TMSC to the *negation* of the desired TDI value */
+ bitnum = out_offset + i;
+ bit = out ? ((out[bitnum/8] >> (bitnum%8)) & 0x1) : 0;
+
+ /* Try optimized case first: if desired TDI bit is 1, then we
+ can fuse what would otherwise be the first two MPSSE commands */
+ if (bit) {
+ const uint8_t tmsbits = 0x3; /* 1, 1 */
+ mpsse_clock_tms_cs_out(mpsse_ctx, &tmsbits, 0, 2, false, mode);
+ } else {
+ /* Can't fuse because TDI varies; less efficient */
+ mpsse_clock_tms_cs_out(mpsse_ctx, &one, 0, 1, bit ? 0 : 1, mode);
+
+ /* drive TMSC to desired TMS value (always zero in this context) */
+ mpsse_clock_tms_cs_out(mpsse_ctx, &one, 0, 1, false, mode);
+ }
+
+ if (tmsc_en)
+ ftdi_set_signal(tmsc_en, '0'); /* put TMSC in high impedance */
+
+ /* drive another TCK without driving TMSC (TDO cycle) */
+ mpsse_clock_tms_cs(mpsse_ctx, &zero, 0, in, in_offset+i, 1, false, mode);
+
+ if (tmsc_en)
+ ftdi_set_signal(tmsc_en, '1'); /* drive again TMSC */
+ }
+}
+
+
+static void oscan1_mpsse_clock_tms_cs(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, uint8_t *in,
+ unsigned in_offset, unsigned length, bool tdi, uint8_t mode)
+{
+ static const uint8_t zero;
+ static const uint8_t one = 1;
+
+ struct signal *tmsc_en = find_signal_by_name("TMSC_EN");
+
+ LOG_DEBUG_IO("oscan1_mpsse_clock_tms_cs: %sout %d bits, tdi=%d", in ? "in" : "", length, tdi);
+
+ for (unsigned i = 0; i < length; i++) {
+ int bitnum;
+ uint8_t tmsbit;
+ uint8_t tdibit;
+
+ /* OScan1 uses 3 separate clocks */
+
+ /* drive TMSC to the *negation* of the desired TDI value */
+ tdibit = tdi ? 0 : 1;
+
+ /* drive TMSC to desired TMS value */
+ bitnum = out_offset + i;
+ tmsbit = ((out[bitnum/8] >> (bitnum%8)) & 0x1);
+
+ if (tdibit == tmsbit) {
+ /* Can squash into a single MPSSE command */
+ const uint8_t tmsbits = 0x3;
+ mpsse_clock_tms_cs_out(mpsse_ctx, &tmsbits, 0, 2, tdibit, mode);
+ } else {
+ /* Unoptimized case, can't formulate with a single command */
+ mpsse_clock_tms_cs_out(mpsse_ctx, &one, 0, 1, tdibit, mode);
+ mpsse_clock_tms_cs_out(mpsse_ctx, &one, 0, 1, (tmsbit != 0), mode);
+ }
+
+ if (tmsc_en)
+ ftdi_set_signal(tmsc_en, '0'); /* put TMSC in high impedance */
+
+ /* drive another TCK without driving TMSC (TDO cycle) */
+ mpsse_clock_tms_cs(mpsse_ctx, &zero, 0, in, in_offset+i, 1, false, mode);
+
+ if (tmsc_en)
+ ftdi_set_signal(tmsc_en, '1'); /* drive again TMSC */
+ }
+}
+
+
+static void oscan1_mpsse_clock_tms_cs_out(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset,
+ unsigned length, bool tdi, uint8_t mode)
+{
+ oscan1_mpsse_clock_tms_cs(ctx, out, out_offset, 0, 0, length, tdi, mode);
+}
+
+
+static void cjtag_set_tck_tms_tdi(struct signal *tck, char tckvalue, struct signal *tms,
+ char tmsvalue, struct signal *tdi, char tdivalue)
+{
+ ftdi_set_signal(tms, tmsvalue);
+ ftdi_set_signal(tdi, tdivalue);
+ ftdi_set_signal(tck, tckvalue);
+}
+
+static void cjtag_reset_online_activate(void)
+{
+ /* After TAP reset, the cJTAG-to-JTAG adapter is in offline and
+ non-activated state. Escape sequences are needed to bring the
+ TAP online and activated into the desired working mode. */
+
+ struct signal *tck = find_signal_by_name("TCK");
+ struct signal *tdi = find_signal_by_name("TDI");
+ struct signal *tms = find_signal_by_name("TMS");
+ struct signal *tdo = find_signal_by_name("TDO");
+ struct signal *tmsc_en = find_signal_by_name("TMSC_EN");
+ uint16_t tdovalue;
+
+ static struct {
+ int8_t tck;
+ int8_t tms;
+ int8_t tdi;
+ } sequence[] = {
+ /* TCK=0, TMS=1, TDI=0 (drive TMSC to 0 baseline) */
+ {'0', '1', '0'},
+
+ /* Drive cJTAG escape sequence for TAP reset - 8 TMSC edges */
+ /* TCK=1, TMS=1, TDI=0 (rising edge of TCK with TMSC still 0) */
+ {'1', '1', '0'},
+ /* TCK=1, TMS=1, TDI=1 (drive rising TMSC edge) */
+ {'1', '1', '1'},
+ /* TCK=1, TMS=1, TDI=0 (drive falling TMSC edge) */
+ {'1', '1', '0'},
+ /* TCK=1, TMS=1, TDI=1 (drive rising TMSC edge) */
+ {'1', '1', '1'},
+ /* TCK=1, TMS=1, TDI=0 (drive falling TMSC edge) */
+ {'1', '1', '0'},
+ /* TCK=1, TMS=1, TDI=1 (drive rising TMSC edge) */
+ {'1', '1', '1'},
+ /* TCK=1, TMS=1, TDI=0 (drive falling TMSC edge) */
+ {'1', '1', '0'},
+ /* TCK=1, TMS=1, TDI=1 (drive rising TMSC edge) */
+ {'1', '1', '1'},
+ /* TCK=1, TMS=1, TDI=0 (drive falling TMSC edge) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK with TMSC still 0) */
+ {'0', '1', '0'},
+
+ /* 3 TCK pulses for padding */
+ /* TCK=1, TMS=1, TDI=0 (drive rising TCK edge) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (drive falling TCK edge) */
+ {'0', '1', '0'},
+ /* TCK=1, TMS=1, TDI=0 (drive rising TCK edge) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (drive falling TCK edge) */
+ {'0', '1', '0'},
+ /* TCK=1, TMS=1, TDI=0 (drive rising TCK edge) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (drive falling TCK edge) */
+ {'0', '1', '0'},
+
+ /* Drive cJTAG escape sequence for SELECT */
+ /* TCK=1, TMS=1, TDI=0 (rising edge of TCK with TMSC still 0, TAP reset that was just setup occurs here too) */
+ {'1', '1', '0'},
+ /* TCK=1, TMS=1, TDI=1 (drive rising TMSC edge) */
+ {'1', '1', '1'},
+ /* TCK=1, TMS=1, TDI=0 (drive falling TMSC edge) */
+ {'1', '1', '0'},
+ /* TCK=1, TMS=1, TDI=1 (drive rising TMSC edge) */
+ {'1', '1', '1'},
+ /* TCK=1, TMS=1, TDI=0 (drive falling TMSC edge) */
+ {'1', '1', '0'},
+ /* TCK=1, TMS=1, TDI=1 (drive rising TMSC edge) */
+ {'1', '1', '1'},
+ /* TCK=1, TMS=1, TDI=0 (drive falling TMSC edge) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK with TMSC still 0) */
+ {'0', '1', '0'},
+
+ /* Drive cJTAG escape sequence for OScan1 activation -- OAC = 1100 -> 2 wires -- */
+ /* TCK=1, TMS=1, TDI=0 (rising edge TCK with TMSC still 0... online mode activated... also OAC bit0==0) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */
+ {'0', '1', '0'},
+ /* TCK=1, TMS=1, TDI=0 (rising edge TCK... OAC bit1==0) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=1 (falling edge TCK) */
+ {'0', '1', '1'},
+ /* TCK=1, TMS=1, TDI=1 (rising edge TCK... OAC bit2==1) */
+ {'1', '1', '1'},
+ /* TCK=0, TMS=1, TDI=1 (falling edge TCK, TMSC stays high) */
+ {'0', '1', '1'},
+ /* TCK=1, TMS=1, TDI=1 (rising edge TCK... OAC bit3==1) */
+ {'1', '1', '1'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */
+ {'0', '1', '0'},
+ /* TCK=1, TMS=1, TDI=0 (rising edge TCK... EC bit0==0) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */
+ {'0', '1', '0'},
+ /* TCK=1, TMS=1, TDI=0 (rising edge TCK... EC bit1==0) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */
+ {'0', '1', '0'},
+ /* TCK=1, TMS=1, TDI=0 (rising edge TCK... EC bit2==0) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=1 (falling edge TCK) */
+ {'0', '1', '1'},
+ /* TCK=1, TMS=1, TDI=1 (rising edge TCK... EC bit3==1) */
+ {'1', '1', '1'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */
+ {'0', '1', '0'},
+ /* TCK=1, TMS=1, TDI=0 (rising edge TCK... CP bit0==0) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */
+ {'0', '1', '0'},
+ /* TCK=1, TMS=1, TDI=0 (rising edge TCK... CP bit1==0) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */
+ {'0', '1', '0'},
+ /* TCK=1, TMS=1, TDI=0 (rising edge TCK... CP bit2==0) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */
+ {'0', '1', '0'},
+ /* TCK=1, TMS=1, TDI=0 (rising edge TCK... CP bit3==0) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */
+ {'0', '1', '0'},
+ };
+
+ if (!oscan1_mode && !jscan3_mode)
+ return; /* Nothing to do */
+
+ if (oscan1_mode && jscan3_mode) {
+ LOG_ERROR("Both oscan1_mode and jscan3_mode are \"on\". At most one of them can be enabled.");
+ return;
+ }
+
+ if (!tck) {
+ LOG_ERROR("Can't run cJTAG online/activate escape sequences: TCK signal is not defined");
+ return;
+ }
+
+ if (!tdi) {
+ LOG_ERROR("Can't run cJTAG online/activate escape sequences: TDI signal is not defined");
+ return;
+ }
+
+ if (!tms) {
+ LOG_ERROR("Can't run cJTAG online/activate escape sequences: TMS signal is not defined");
+ return;
+ }
+
+ if (!tdo) {
+ LOG_ERROR("Can't run cJTAG online/activate escape sequences: TDO signal is not defined");
+ return;
+ }
+
+ if (jscan3_mode) {
+ /* Update the sequence above to enable JScan3 instead of OScan1 */
+ sequence[ESCAPE_SEQ_OAC_BIT2].tdi = '0';
+ sequence[ESCAPE_SEQ_OAC_BIT2+1].tdi = '0';
+ }
+
+ /* if defined TMSC_EN, replace tms with it */
+ if (tmsc_en)
+ tms = tmsc_en;
+
+ /* Send the sequence to the adapter */
+ for (size_t i = 0; i < sizeof(sequence)/sizeof(sequence[0]); i++)
+ cjtag_set_tck_tms_tdi(tck, sequence[i].tck, tms, sequence[i].tms, tdi, sequence[i].tdi);
+
+ /* If JScan3 mode, configure cJTAG adapter to 4-wire */
+ if (jscan3_mode)
+ ftdi_set_signal(find_signal_by_name("JTAG_SEL"), '1');
+
+ ftdi_get_signal(tdo, &tdovalue); /* Just to force a flush */
+}
+
+#endif /* #if BUILD_FTDI_CJTAG == 1 */
+
COMMAND_HANDLER(ftdi_handle_device_desc_command)
{
if (CMD_ARGC == 1) {
@@ -922,6 +1298,32 @@ COMMAND_HANDLER(ftdi_handle_tdo_sample_edge_command)
return ERROR_OK;
}
+#if BUILD_FTDI_CJTAG == 1
+COMMAND_HANDLER(ftdi_handle_oscan1_mode_command)
+{
+ if (CMD_ARGC > 1)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (CMD_ARGC == 1)
+ COMMAND_PARSE_ON_OFF(CMD_ARGV[0], oscan1_mode);
+
+ command_print(CMD, "oscan1 mode: %s.", oscan1_mode ? "on" : "off");
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(ftdi_handle_jscan3_mode_command)
+{
+ if (CMD_ARGC > 1)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (CMD_ARGC == 1)
+ COMMAND_PARSE_ON_OFF(CMD_ARGV[0], jscan3_mode);
+
+ command_print(CMD, "jscan3 mode: %s.", jscan3_mode ? "on" : "off");
+ return ERROR_OK;
+}
+#endif
+
static const struct command_registration ftdi_subcommand_handlers[] = {
{
.name = "device_desc",
@@ -983,6 +1385,22 @@ static const struct command_registration ftdi_subcommand_handlers[] = {
"allow signalling speed increase)",
.usage = "(rising|falling)",
},
+#if BUILD_FTDI_CJTAG == 1
+ {
+ .name = "oscan1_mode",
+ .handler = &ftdi_handle_oscan1_mode_command,
+ .mode = COMMAND_ANY,
+ .help = "set to 'on' to use OScan1 mode for signaling, otherwise 'off' (default is 'off')",
+ .usage = "(on|off)",
+ },
+ {
+ .name = "jscan3_mode",
+ .handler = &ftdi_handle_jscan3_mode_command,
+ .mode = COMMAND_ANY,
+ .help = "set to 'on' to use JScan3 mode for signaling, otherwise 'off' (default is 'off')",
+ .usage = "(on|off)",
+ },
+#endif
COMMAND_REGISTRATION_DONE
};
diff --git a/src/jtag/drivers/xds110.c b/src/jtag/drivers/xds110.c
index 8e5d638..47d06f1 100644
--- a/src/jtag/drivers/xds110.c
+++ b/src/jtag/drivers/xds110.c
@@ -579,7 +579,7 @@ static bool usb_get_response(uint32_t *total_bytes_read, uint32_t timeout)
static bool usb_send_command(uint16_t size)
{
- int written;
+ int written = 0;
bool success = true;
/* Check the packet length */
diff --git a/src/rtos/FreeRTOS.c b/src/rtos/FreeRTOS.c
index 945c4b8..fbb698b 100644
--- a/src/rtos/FreeRTOS.c
+++ b/src/rtos/FreeRTOS.c
@@ -21,63 +21,219 @@
#include "target/cortex_m.h"
#define FREERTOS_MAX_PRIORITIES 63
-
-/* FIXME: none of the _width parameters are actually observed properly!
- * you WILL need to edit more if you actually attempt to target a 8/16/64
- * bit target!
- */
+#define FREERTOS_THREAD_NAME_STR_SIZE 200
+#define FREERTOS_CURRENT_EXECUTION_ID 1
struct freertos_params {
const char *target_name;
- const unsigned char thread_count_width;
- const unsigned char pointer_width;
- const unsigned char list_next_offset;
- const unsigned char list_width;
- const unsigned char list_elem_next_offset;
- const unsigned char list_elem_content_offset;
- const unsigned char thread_stack_offset;
- const unsigned char thread_name_offset;
- const struct rtos_register_stacking *stacking_info_cm3;
- const struct rtos_register_stacking *stacking_info_cm4f;
- const struct rtos_register_stacking *stacking_info_cm4f_fpu;
+ int (*stacking)(struct rtos *rtos, const struct rtos_register_stacking **stacking,
+ target_addr_t stack_ptr);
+ const struct command_registration *commands;
+};
+
+struct freertos_thread_entry {
+ struct list_head list;
+ threadid_t threadid;
+ target_addr_t tcb;
+};
+
+struct FreeRTOS {
+ const struct freertos_params *param;
+ threadid_t last_threadid;
+ /* Keep track of which threadid we're using for which TCB. We cannot use a
+ * 1:1 mapping because TCB's can be 64 bits, and the gdb protocol doesn't
+ * work well with thread id's that are greater than 32 bits.
+ */
+ struct list_head thread_entry_list;
+ /* sizeof(UBaseType_t) */
+ unsigned ubasetype_size;
+ /* sizeof(void *) */
+ unsigned pointer_size;
+ /* sizeof(TickType_t) */
+ unsigned ticktype_size;
+ unsigned list_width;
+ unsigned list_item_width;
+ unsigned list_elem_next_offset;
+ unsigned list_elem_next_size;
+ unsigned list_elem_content_offset;
+ unsigned list_elem_content_size;
+ unsigned list_uxNumberOfItems_offset;
+ unsigned list_uxNumberOfItems_size;
+ unsigned list_next_offset;
+ unsigned list_next_size;
+ unsigned thread_stack_offset;
+ unsigned thread_stack_size;
+ unsigned thread_name_offset;
+};
+
+static int cortex_m_stacking(struct rtos *rtos, const struct rtos_register_stacking **stacking,
+ target_addr_t stack_ptr)
+{
+ /* Check for armv7m with *enabled* FPU, i.e. a Cortex-M4F */
+ int cm4_fpu_enabled = 0;
+ struct armv7m_common *armv7m_target = target_to_armv7m(rtos->target);
+ if (is_armv7m(armv7m_target)) {
+ if ((armv7m_target->fp_feature == FPV4_SP) || (armv7m_target->fp_feature == FPV5_SP) ||
+ (armv7m_target->fp_feature == FPV5_DP)) {
+ /* Found ARM v7m target which includes a FPU */
+ uint32_t cpacr;
+
+ int retval = target_read_u32(rtos->target, FPU_CPACR, &cpacr);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Could not read CPACR register to check FPU state");
+ return retval;
+ }
+
+ /* Check if CP10 and CP11 are set to full access. */
+ if (cpacr & 0x00F00000) {
+ /* Found target with enabled FPU */
+ cm4_fpu_enabled = 1;
+ }
+ }
+ }
+
+ if (cm4_fpu_enabled == 1) {
+ /* Read the LR to decide between stacking with or without FPU */
+ uint32_t LR_svc = 0;
+ int retval = target_read_u32(rtos->target,
+ stack_ptr + 0x20,
+ &LR_svc);
+ if (retval != ERROR_OK) {
+ LOG_OUTPUT("Error reading stack frame from FreeRTOS thread");
+ return retval;
+ }
+ if ((LR_svc & 0x10) == 0)
+ *stacking = &rtos_standard_cortex_m4f_fpu_stacking;
+ else
+ *stacking = &rtos_standard_cortex_m4f_stacking;
+ } else {
+ *stacking = &rtos_standard_cortex_m3_stacking;
+ }
+
+ return ERROR_OK;
+}
+
+/* take 4 bytes (32 bits) as the default size,
+ * which is suitable for most 32-bit targets and
+ * configuration of configUSE_16_BIT_TICKS = 0. */
+static unsigned int freertos_ticktype_size = 4;
+COMMAND_HANDLER(handle_freertos_ticktype_size)
+{
+ if (CMD_ARGC != 1) {
+ LOG_ERROR("Command takes exactly 1 parameter");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ unsigned int size;
+ COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], size);
+ switch (size) {
+ case 2:
+ case 4:
+ case 8:
+ freertos_ticktype_size = size;
+ break;
+ default:
+ LOG_ERROR("Invalid ticktype size. Should be 2, 4 or 8.");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ return ERROR_OK;
+}
+
+static const struct command_registration freertos_commands[] = {
+ {
+ .name = "freertos_ticktype_size",
+ .handler = handle_freertos_ticktype_size,
+ .mode = COMMAND_ANY,
+ .usage = "(2|4|8)",
+ .help = "Pass the size (in bytes) of TickType_t to OpenOCD. To make sure the "
+ "calculation of offsets and sizes is correct. Defaults to 4."
+ },
+ COMMAND_REGISTRATION_DONE
};
+static enum {
+ STACKING_MAINLINE,
+ STACKING_METAL
+} riscv_freertos_stacking;
+COMMAND_HANDLER(handle_riscv_freertos_stacking)
+{
+ if (CMD_ARGC != 1) {
+ LOG_ERROR("Command takes exactly 1 parameter");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+ if (!strcmp(CMD_ARGV[0], "mainline")) {
+ riscv_freertos_stacking = STACKING_MAINLINE;
+ } else if (!strcmp(CMD_ARGV[0], "metal")) {
+ riscv_freertos_stacking = STACKING_METAL;
+ } else {
+ LOG_ERROR("Only two arguments are supported: mainline and metal");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+ return ERROR_OK;
+}
+
+static const struct command_registration riscv_commands[] = {
+ {
+ .name = "riscv_freertos_stacking",
+ .handler = handle_riscv_freertos_stacking,
+ .mode = COMMAND_ANY,
+ .usage = "mainline|metal",
+ .help = "Select which FreeRTOS branch is being used. OpenOCD needs to "
+ "know because different branches save thread registers on the stack "
+ "in different orders. It is likely that this order on both branches will "
+ "change in the future, so make sure to seek out the very latest OpenOCD if "
+ "debugging is not working right."
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+static int riscv_stacking(struct rtos *rtos, const struct rtos_register_stacking **stacking,
+ target_addr_t stack_ptr)
+{
+ struct FreeRTOS *freertos = (struct FreeRTOS *) rtos->rtos_specific_params;
+ LOG_DEBUG("riscv_freertos_stacking=%d", riscv_freertos_stacking);
+ switch (riscv_freertos_stacking) {
+ case STACKING_MAINLINE:
+ if (freertos->pointer_size == 4)
+ *stacking = &rtos_standard_rv32_stacking;
+ else if (freertos->pointer_size == 8)
+ *stacking = &rtos_standard_rv64_stacking;
+ break;
+ case STACKING_METAL:
+ if (freertos->pointer_size == 4)
+ *stacking = &rtos_metal_rv32_stacking;
+ else if (freertos->pointer_size == 8)
+ *stacking = &rtos_metal_rv64_stacking;
+ break;
+ }
+ return ERROR_OK;
+}
+
static const struct freertos_params freertos_params_list[] = {
{
- "cortex_m", /* target_name */
- 4, /* thread_count_width; */
- 4, /* pointer_width; */
- 16, /* list_next_offset; */
- 20, /* list_width; */
- 8, /* list_elem_next_offset; */
- 12, /* list_elem_content_offset */
- 0, /* thread_stack_offset; */
- 52, /* thread_name_offset; */
- &rtos_standard_cortex_m3_stacking, /* stacking_info */
- &rtos_standard_cortex_m4f_stacking,
- &rtos_standard_cortex_m4f_fpu_stacking,
+ .target_name = "cortex_m",
+ .stacking = cortex_m_stacking
+ },
+ {
+ .target_name = "hla_target",
+ .stacking = cortex_m_stacking
},
{
- "hla_target", /* target_name */
- 4, /* thread_count_width; */
- 4, /* pointer_width; */
- 16, /* list_next_offset; */
- 20, /* list_width; */
- 8, /* list_elem_next_offset; */
- 12, /* list_elem_content_offset */
- 0, /* thread_stack_offset; */
- 52, /* thread_name_offset; */
- &rtos_standard_cortex_m3_stacking, /* stacking_info */
- &rtos_standard_cortex_m4f_stacking,
- &rtos_standard_cortex_m4f_fpu_stacking,
+ .target_name = "riscv",
+ .stacking = riscv_stacking,
+ .commands = riscv_commands,
},
};
static bool freertos_detect_rtos(struct target *target);
static int freertos_create(struct target *target);
static int freertos_update_threads(struct rtos *rtos);
-static int freertos_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
+static int freertos_get_thread_reg_list(struct rtos *rtos, threadid_t thread_id,
struct rtos_reg **reg_list, int *num_regs);
+static int freertos_get_thread_reg_value(struct rtos *rtos, threadid_t thread_id,
+ uint32_t reg_num, uint32_t *size, uint8_t **value);
+static int freertos_set_reg(struct rtos *rtos, uint32_t reg_num, uint8_t *reg_value);
static int freertos_get_symbol_list_to_lookup(struct symbol_table_elem *symbol_list[]);
struct rtos_type freertos_rtos = {
@@ -87,6 +243,8 @@ struct rtos_type freertos_rtos = {
.create = freertos_create,
.update_threads = freertos_update_threads,
.get_thread_reg_list = freertos_get_thread_reg_list,
+ .get_thread_reg_value = freertos_get_thread_reg_value,
+ .set_reg = freertos_set_reg,
.get_symbol_list_to_lookup = freertos_get_symbol_list_to_lookup,
};
@@ -131,32 +289,202 @@ static const struct symbols freertos_symbol_list[] = {
/* may be problems reading if sizes are not 32 bit long integers. */
/* test mallocs for failure */
+static int freertos_read_struct_value(
+ struct target *target, target_addr_t base_address, unsigned offset,
+ unsigned size_bytes, uint64_t *value)
+{
+ uint8_t buf[size_bytes];
+ int retval = target_read_buffer(target, base_address + offset, size_bytes, buf);
+ *value = buf_get_u64(buf, 0, size_bytes * 8);
+ return retval;
+}
+
+typedef struct {
+ enum {
+ TYPE_POINTER,
+ TYPE_UBASE,
+ TYPE_TICKTYPE,
+ TYPE_LIST_ITEM,
+ TYPE_CHAR_ARRAY
+ } type;
+ unsigned offset;
+ unsigned size;
+} type_offset_size_t;
+
+static unsigned populate_offset_size(struct FreeRTOS *freertos,
+ type_offset_size_t *info, unsigned count)
+{
+ unsigned offset = 0;
+ unsigned largest = 0;
+ for (unsigned i = 0; i < count; i++) {
+ unsigned align = 0;
+ switch (info[i].type) {
+ case TYPE_UBASE:
+ info[i].size = freertos->ubasetype_size;
+ align = freertos->ubasetype_size;
+ break;
+ case TYPE_POINTER:
+ info[i].size = freertos->pointer_size;
+ align = freertos->pointer_size;
+ break;
+ case TYPE_TICKTYPE:
+ info[i].size = freertos->ticktype_size;
+ align = freertos->ticktype_size;
+ break;
+ case TYPE_LIST_ITEM:
+ info[i].size = freertos->list_item_width;
+ align = MAX(freertos->ticktype_size, freertos->pointer_size);
+ break;
+ case TYPE_CHAR_ARRAY:
+ /* size is set by the caller. */
+ align = 1;
+ break;
+ }
+
+ assert(info[i].size > 0);
+ assert(align > 0);
+
+ largest = MAX(largest, align);
+
+ if (offset & (align - 1)) {
+ offset = offset & ~(align - 1);
+ offset += align;
+ }
+
+ info[i].offset = offset;
+ offset += info[i].size;
+ }
+
+ /* Now align offset to the largest type used, and return that as the width
+ * of the structure. */
+
+ if (offset & (largest - 1)) {
+ offset = offset & ~(largest - 1);
+ offset += largest;
+ }
+ return offset;
+}
+
+static void freertos_compute_offsets(struct rtos *rtos)
+{
+ struct FreeRTOS *freertos = (struct FreeRTOS *) rtos->rtos_specific_params;
+
+ if (freertos->ticktype_size == freertos_ticktype_size)
+ return;
+
+ freertos->pointer_size = DIV_ROUND_UP(target_address_bits(rtos->target), 8);
+ freertos->ubasetype_size = DIV_ROUND_UP(target_data_bits(rtos->target), 8);
+ freertos->ticktype_size = freertos_ticktype_size;
+
+ /*
+ * FreeRTOS can be compiled with configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES
+ * in which case extra data is inserted and OpenOCD won't work right.
+ */
+
+ /* struct xLIST */
+ type_offset_size_t struct_list_info[] = {
+ {TYPE_UBASE, 0, 0}, /* uxNumberOfItems */
+ {TYPE_POINTER, 0, 0}, /* ListItem_t *pxIndex */
+ {TYPE_TICKTYPE, 0, 0}, /* xItemValue */
+ {TYPE_POINTER, 0, 0}, /* ListItem_t *pxNext */
+ {TYPE_POINTER, 0, 0}, /* ListItem_t *pxPrevious */
+ };
+
+ /* struct xLIST_ITEM */
+ type_offset_size_t struct_list_item_info[] = {
+ {TYPE_TICKTYPE, 0, 0}, /* xItemValue */
+ {TYPE_POINTER, 0, 0}, /* ListItem_t *pxNext */
+ {TYPE_POINTER, 0, 0}, /* ListItem_t *pxPrevious */
+ {TYPE_POINTER, 0, 0}, /* void *pvOwner */
+ {TYPE_POINTER, 0, 0}, /* List_t *pvContainer */
+ };
+
+ /* struct tskTaskControlBlock */
+ type_offset_size_t task_control_block_info[] = {
+ {TYPE_POINTER, 0, 0}, /* StackType_t *pxTopOfStack */
+ {TYPE_LIST_ITEM, 0, 0}, /* ListItem_t xStateListItem */
+ {TYPE_LIST_ITEM, 0, 0}, /* ListItem_t xEventListItem */
+ {TYPE_UBASE, 0, 0}, /* uxPriority */
+ {TYPE_POINTER, 0, 0}, /* StackType_t *pxStack */
+ /* configMAX_TASK_NAME_LEN varies a lot between targets, but luckily the
+ * name is NULL_terminated and we don't need to read anything else in
+ * the TCB. */
+ {TYPE_CHAR_ARRAY, 0, FREERTOS_THREAD_NAME_STR_SIZE}, /* char pcTaskName[configMAX_TASK_NAME_LEN] */
+ /* Lots of more optional stuff, but is is irrelevant to us. */
+ };
+
+ freertos->list_width = populate_offset_size(
+ freertos, struct_list_info, ARRAY_SIZE(struct_list_info));
+ freertos->list_uxNumberOfItems_offset = struct_list_info[0].offset;
+ freertos->list_uxNumberOfItems_size = struct_list_info[0].size;
+ freertos->list_next_offset = struct_list_info[3].offset;
+ freertos->list_next_size = struct_list_info[3].size;
+
+ freertos->list_item_width = populate_offset_size(
+ freertos, struct_list_item_info, ARRAY_SIZE(struct_list_item_info));
+ freertos->list_elem_next_offset = struct_list_item_info[1].offset;
+ freertos->list_elem_next_size = struct_list_item_info[1].size;
+ freertos->list_elem_content_offset = struct_list_item_info[3].offset;
+ freertos->list_elem_content_size = struct_list_item_info[3].size;
+
+ populate_offset_size(
+ freertos, task_control_block_info, ARRAY_SIZE(task_control_block_info));
+ freertos->thread_stack_offset = task_control_block_info[0].offset;
+ freertos->thread_stack_size = task_control_block_info[0].size;
+ freertos->thread_name_offset = task_control_block_info[5].offset;
+}
+
+struct freertos_thread_entry *thread_entry_list_find_by_tcb(
+ struct list_head *list, target_addr_t tcb)
+{
+ struct freertos_thread_entry *t;
+ list_for_each_entry(t, list, list) {
+ if (t->tcb == tcb)
+ return t;
+ }
+ return NULL;
+}
+
+struct freertos_thread_entry *thread_entry_list_find_by_id(
+ struct list_head *list, threadid_t threadid)
+{
+ struct freertos_thread_entry *t;
+ list_for_each_entry(t, list, list) {
+ if (t->threadid == threadid)
+ return t;
+ }
+ return NULL;
+}
+
static int freertos_update_threads(struct rtos *rtos)
{
int retval;
unsigned int tasks_found = 0;
- const struct freertos_params *param;
if (!rtos->rtos_specific_params)
- return -1;
+ return ERROR_FAIL;
- param = (const struct freertos_params *) rtos->rtos_specific_params;
+ freertos_compute_offsets(rtos);
+
+ struct FreeRTOS *freertos = (struct FreeRTOS *) rtos->rtos_specific_params;
if (!rtos->symbols) {
LOG_ERROR("No symbols for FreeRTOS");
- return -3;
+ return ERROR_FAIL;
}
if (rtos->symbols[FREERTOS_VAL_UX_CURRENT_NUMBER_OF_TASKS].address == 0) {
LOG_ERROR("Don't have the number of threads in FreeRTOS");
- return -2;
+ return ERROR_FAIL;
}
- uint32_t thread_list_size = 0;
- retval = target_read_u32(rtos->target,
- rtos->symbols[FREERTOS_VAL_UX_CURRENT_NUMBER_OF_TASKS].address,
- &thread_list_size);
- LOG_DEBUG("FreeRTOS: Read uxCurrentNumberOfTasks at 0x%" PRIx64 ", value %" PRIu32,
+ uint64_t thread_list_size;
+ retval = freertos_read_struct_value(rtos->target,
+ rtos->symbols[FREERTOS_VAL_UX_CURRENT_NUMBER_OF_TASKS].address,
+ 0,
+ freertos->ubasetype_size,
+ &thread_list_size);
+ LOG_DEBUG("FreeRTOS: Read uxCurrentNumberOfTasks at 0x%" PRIx64 ", value %" PRIu64,
rtos->symbols[FREERTOS_VAL_UX_CURRENT_NUMBER_OF_TASKS].address,
thread_list_size);
@@ -169,33 +497,36 @@ static int freertos_update_threads(struct rtos *rtos)
rtos_free_threadlist(rtos);
/* read the current thread */
- uint32_t pointer_casts_are_bad;
- retval = target_read_u32(rtos->target,
- rtos->symbols[FREERTOS_VAL_PX_CURRENT_TCB].address,
- &pointer_casts_are_bad);
+ target_addr_t px_current_tcb;
+ retval = freertos_read_struct_value(rtos->target,
+ rtos->symbols[FREERTOS_VAL_PX_CURRENT_TCB].address,
+ 0,
+ freertos->pointer_size,
+ &px_current_tcb);
if (retval != ERROR_OK) {
LOG_ERROR("Error reading current thread in FreeRTOS thread list");
return retval;
}
- rtos->current_thread = pointer_casts_are_bad;
LOG_DEBUG("FreeRTOS: Read pxCurrentTCB at 0x%" PRIx64 ", value 0x%" PRIx64,
rtos->symbols[FREERTOS_VAL_PX_CURRENT_TCB].address,
- rtos->current_thread);
+ px_current_tcb);
/* read scheduler running */
- uint32_t scheduler_running;
- retval = target_read_u32(rtos->target,
- rtos->symbols[FREERTOS_VAL_X_SCHEDULER_RUNNING].address,
- &scheduler_running);
+ uint64_t scheduler_running;
+ retval = freertos_read_struct_value(rtos->target,
+ rtos->symbols[FREERTOS_VAL_X_SCHEDULER_RUNNING].address,
+ 0,
+ freertos->ubasetype_size,
+ &scheduler_running);
if (retval != ERROR_OK) {
LOG_ERROR("Error reading FreeRTOS scheduler state");
return retval;
}
- LOG_DEBUG("FreeRTOS: Read xSchedulerRunning at 0x%" PRIx64 ", value 0x%" PRIx32,
+ LOG_DEBUG("FreeRTOS: Read xSchedulerRunning at 0x%" PRIx64 ", value 0x%" PRIx64,
rtos->symbols[FREERTOS_VAL_X_SCHEDULER_RUNNING].address,
scheduler_running);
- if ((thread_list_size == 0) || (rtos->current_thread == 0) || (scheduler_running != 1)) {
+ if (thread_list_size == 0 || px_current_tcb == 0 || scheduler_running != 1) {
/* Either : No RTOS threads - there is always at least the current execution though */
/* OR : No current thread - all threads suspended - show the current execution
* of idling */
@@ -205,10 +536,10 @@ static int freertos_update_threads(struct rtos *rtos)
rtos->thread_details = malloc(
sizeof(struct thread_detail) * thread_list_size);
if (!rtos->thread_details) {
- LOG_ERROR("Error allocating memory for %d threads", thread_list_size);
+ LOG_ERROR("Error allocating memory for %" PRIu64 " threads", thread_list_size);
return ERROR_FAIL;
}
- rtos->current_thread = 1;
+ rtos->current_thread = FREERTOS_CURRENT_EXECUTION_ID;
rtos->thread_details->threadid = rtos->current_thread;
rtos->thread_details->exists = true;
rtos->thread_details->extra_info_str = NULL;
@@ -224,27 +555,33 @@ static int freertos_update_threads(struct rtos *rtos)
rtos->thread_details = malloc(
sizeof(struct thread_detail) * thread_list_size);
if (!rtos->thread_details) {
- LOG_ERROR("Error allocating memory for %d threads", thread_list_size);
+ LOG_ERROR("Error allocating memory for %" PRId64 " threads", thread_list_size);
return ERROR_FAIL;
}
}
/* Find out how many lists are needed to be read from pxReadyTasksLists, */
+ uint64_t top_used_priority = 0;
if (rtos->symbols[FREERTOS_VAL_UX_TOP_USED_PRIORITY].address == 0) {
- LOG_ERROR("FreeRTOS: uxTopUsedPriority is not defined, consult the OpenOCD manual for a work-around");
- return ERROR_FAIL;
+ LOG_WARNING("FreeRTOS: uxTopUsedPriority is not defined, consult the OpenOCD manual for a work-around");
+ /* This is a hack specific to the binary I'm debugging.
+ * Ideally we get https://github.com/FreeRTOS/FreeRTOS-Kernel/issues/33
+ * into our FreeRTOS source. */
+ top_used_priority = 6;
+ } else {
+ retval = freertos_read_struct_value(rtos->target,
+ rtos->symbols[FREERTOS_VAL_UX_TOP_USED_PRIORITY].address,
+ 0,
+ freertos->ubasetype_size,
+ &top_used_priority);
+ if (retval != ERROR_OK)
+ return retval;
+ LOG_DEBUG("FreeRTOS: Read uxTopUsedPriority at 0x%" PRIx64 ", value %" PRIu64,
+ rtos->symbols[FREERTOS_VAL_UX_TOP_USED_PRIORITY].address,
+ top_used_priority);
}
- uint32_t top_used_priority = 0;
- retval = target_read_u32(rtos->target,
- rtos->symbols[FREERTOS_VAL_UX_TOP_USED_PRIORITY].address,
- &top_used_priority);
- if (retval != ERROR_OK)
- return retval;
- LOG_DEBUG("FreeRTOS: Read uxTopUsedPriority at 0x%" PRIx64 ", value %" PRIu32,
- rtos->symbols[FREERTOS_VAL_UX_TOP_USED_PRIORITY].address,
- top_used_priority);
if (top_used_priority > FREERTOS_MAX_PRIORITIES) {
- LOG_ERROR("FreeRTOS top used priority is unreasonably big, not proceeding: %" PRIu32,
+ LOG_ERROR("FreeRTOS top used priority is unreasonably big, not proceeding: %" PRIu64,
top_used_priority);
return ERROR_FAIL;
}
@@ -266,7 +603,7 @@ static int freertos_update_threads(struct rtos *rtos)
unsigned int num_lists;
for (num_lists = 0; num_lists < config_max_priorities; num_lists++)
list_of_lists[num_lists] = rtos->symbols[FREERTOS_VAL_PX_READY_TASKS_LISTS].address +
- num_lists * param->list_width;
+ num_lists * freertos->list_width;
list_of_lists[num_lists++] = rtos->symbols[FREERTOS_VAL_X_DELAYED_TASK_LIST1].address;
list_of_lists[num_lists++] = rtos->symbols[FREERTOS_VAL_X_DELAYED_TASK_LIST2].address;
@@ -279,61 +616,87 @@ static int freertos_update_threads(struct rtos *rtos)
continue;
/* Read the number of threads in this list */
- uint32_t list_thread_count = 0;
- retval = target_read_u32(rtos->target,
- list_of_lists[i],
- &list_thread_count);
+ uint64_t list_thread_count = 0;
+ retval = freertos_read_struct_value(rtos->target,
+ list_of_lists[i],
+ freertos->list_uxNumberOfItems_offset,
+ freertos->list_uxNumberOfItems_size,
+ &list_thread_count);
if (retval != ERROR_OK) {
LOG_ERROR("Error reading number of threads in FreeRTOS thread list");
free(list_of_lists);
return retval;
}
- LOG_DEBUG("FreeRTOS: Read thread count for list %u at 0x%" PRIx64 ", value %" PRIu32,
- i, list_of_lists[i], list_thread_count);
+ LOG_DEBUG("FreeRTOS: Read thread count for list %u at 0x%" PRIx64 ", value %" PRIu64,
+ i, list_of_lists[i] + freertos->list_uxNumberOfItems_offset, list_thread_count);
if (list_thread_count == 0)
continue;
/* Read the location of first list item */
- uint32_t prev_list_elem_ptr = -1;
- uint32_t list_elem_ptr = 0;
- retval = target_read_u32(rtos->target,
- list_of_lists[i] + param->list_next_offset,
- &list_elem_ptr);
+ target_addr_t prev_list_elem_ptr = -1;
+ target_addr_t list_elem_ptr = 0;
+ retval = freertos_read_struct_value(rtos->target,
+ list_of_lists[i],
+ freertos->list_next_offset,
+ freertos->list_next_size,
+ &list_elem_ptr);
if (retval != ERROR_OK) {
LOG_ERROR("Error reading first thread item location in FreeRTOS thread list");
free(list_of_lists);
return retval;
}
- LOG_DEBUG("FreeRTOS: Read first item for list %u at 0x%" PRIx64 ", value 0x%" PRIx32,
- i, list_of_lists[i] + param->list_next_offset, list_elem_ptr);
+ LOG_DEBUG("FreeRTOS: Read first item for list %u at 0x%" PRIx64 ", value 0x%" PRIx64,
+ i, list_of_lists[i] + freertos->list_next_offset, list_elem_ptr);
while ((list_thread_count > 0) && (list_elem_ptr != 0) &&
(list_elem_ptr != prev_list_elem_ptr) &&
(tasks_found < thread_list_size)) {
/* Get the location of the thread structure. */
rtos->thread_details[tasks_found].threadid = 0;
- retval = target_read_u32(rtos->target,
- list_elem_ptr + param->list_elem_content_offset,
- &pointer_casts_are_bad);
+ target_addr_t tcb;
+ retval = freertos_read_struct_value(rtos->target,
+ list_elem_ptr,
+ freertos->list_elem_content_offset,
+ freertos->list_elem_content_size,
+ &tcb);
if (retval != ERROR_OK) {
LOG_ERROR("Error reading thread list item object in FreeRTOS thread list");
free(list_of_lists);
return retval;
}
- rtos->thread_details[tasks_found].threadid = pointer_casts_are_bad;
- LOG_DEBUG("FreeRTOS: Read Thread ID at 0x%" PRIx32 ", value 0x%" PRIx64,
- list_elem_ptr + param->list_elem_content_offset,
- rtos->thread_details[tasks_found].threadid);
+
+ const struct freertos_thread_entry *value =
+ thread_entry_list_find_by_tcb(&freertos->thread_entry_list, tcb);
+
+ if (!value) {
+ struct freertos_thread_entry *new_value = calloc(1, sizeof(struct freertos_thread_entry));
+ new_value->tcb = tcb;
+ /* threadid can't be 0.
+ * plus 1 to avoid duplication with "Current Execution" */
+ new_value->threadid = ++freertos->last_threadid + FREERTOS_CURRENT_EXECUTION_ID;
+
+ LOG_DEBUG("FreeRTOS: new thread created, tcb=0x%" PRIx64 ", threadid=0x%" PRIx64,
+ new_value->tcb, new_value->threadid);
+
+ list_add_tail(&new_value->list, &freertos->thread_entry_list);
+ value = new_value;
+ }
+
+ rtos->thread_details[tasks_found].threadid = value->threadid;
+
+ LOG_DEBUG("FreeRTOS: Thread %" PRId64 " has TCB 0x%" TARGET_PRIxADDR
+ "; read from 0x%" PRIx64,
+ value->threadid, value->tcb,
+ list_elem_ptr + freertos->list_elem_content_offset);
/* get thread name */
- #define FREERTOS_THREAD_NAME_STR_SIZE (200)
char tmp_str[FREERTOS_THREAD_NAME_STR_SIZE];
/* Read the thread name */
retval = target_read_buffer(rtos->target,
- rtos->thread_details[tasks_found].threadid + param->thread_name_offset,
+ value->tcb + freertos->thread_name_offset,
FREERTOS_THREAD_NAME_STR_SIZE,
(uint8_t *)&tmp_str);
if (retval != ERROR_OK) {
@@ -343,7 +706,7 @@ static int freertos_update_threads(struct rtos *rtos)
}
tmp_str[FREERTOS_THREAD_NAME_STR_SIZE-1] = '\x00';
LOG_DEBUG("FreeRTOS: Read Thread Name at 0x%" PRIx64 ", value '%s'",
- rtos->thread_details[tasks_found].threadid + param->thread_name_offset,
+ value->tcb + freertos->thread_name_offset,
tmp_str);
if (tmp_str[0] == '\x00')
@@ -354,8 +717,9 @@ static int freertos_update_threads(struct rtos *rtos)
strcpy(rtos->thread_details[tasks_found].thread_name_str, tmp_str);
rtos->thread_details[tasks_found].exists = true;
- if (rtos->thread_details[tasks_found].threadid == rtos->current_thread) {
+ if (value->tcb == px_current_tcb && rtos->current_thread != FREERTOS_CURRENT_EXECUTION_ID) {
char running_str[] = "State: Running";
+ rtos->current_thread = value->threadid;
rtos->thread_details[tasks_found].extra_info_str = malloc(
sizeof(running_str));
strcpy(rtos->thread_details[tasks_found].extra_info_str,
@@ -368,17 +732,20 @@ static int freertos_update_threads(struct rtos *rtos)
prev_list_elem_ptr = list_elem_ptr;
list_elem_ptr = 0;
- retval = target_read_u32(rtos->target,
- prev_list_elem_ptr + param->list_elem_next_offset,
- &list_elem_ptr);
+ retval = freertos_read_struct_value(rtos->target,
+ prev_list_elem_ptr,
+ freertos->list_elem_next_offset,
+ freertos->list_elem_next_size,
+ &list_elem_ptr);
if (retval != ERROR_OK) {
LOG_ERROR("Error reading next thread item location in FreeRTOS thread list");
free(list_of_lists);
return retval;
}
- LOG_DEBUG("FreeRTOS: Read next thread location at 0x%" PRIx32 ", value 0x%" PRIx32,
- prev_list_elem_ptr + param->list_elem_next_offset,
- list_elem_ptr);
+ LOG_DEBUG("FreeRTOS: Read next thread location at " TARGET_ADDR_FMT
+ ", value " TARGET_ADDR_FMT,
+ prev_list_elem_ptr + freertos->list_elem_next_offset,
+ list_elem_ptr);
}
}
@@ -387,77 +754,106 @@ static int freertos_update_threads(struct rtos *rtos)
return 0;
}
-static int freertos_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
- struct rtos_reg **reg_list, int *num_regs)
+static int freertos_get_stacking_info(struct rtos *rtos, threadid_t thread_id,
+ const struct rtos_register_stacking **stacking_info,
+ target_addr_t *stack_ptr)
{
- int retval;
- const struct freertos_params *param;
- int64_t stack_ptr = 0;
-
- if (!rtos)
- return -1;
+ if (!rtos->rtos_specific_params) {
+ LOG_ERROR("rtos_specific_params is NULL!");
+ return ERROR_FAIL;
+ }
- if (thread_id == 0)
- return -2;
+ freertos_compute_offsets(rtos);
- if (!rtos->rtos_specific_params)
- return -1;
+ struct FreeRTOS *freertos = (struct FreeRTOS *) rtos->rtos_specific_params;
+ const struct freertos_params *param = freertos->param;
- param = (const struct freertos_params *) rtos->rtos_specific_params;
+ const struct freertos_thread_entry *entry =
+ thread_entry_list_find_by_id(&freertos->thread_entry_list, thread_id);
+ if (!entry) {
+ LOG_ERROR("Unknown thread id: %" PRId64, thread_id);
+ return ERROR_FAIL;
+ }
/* Read the stack pointer */
- uint32_t pointer_casts_are_bad;
- retval = target_read_u32(rtos->target,
- thread_id + param->thread_stack_offset,
- &pointer_casts_are_bad);
+ int retval = freertos_read_struct_value(rtos->target,
+ entry->tcb,
+ freertos->thread_stack_offset,
+ freertos->thread_stack_size,
+ stack_ptr);
if (retval != ERROR_OK) {
- LOG_ERROR("Error reading stack frame from FreeRTOS thread");
+ LOG_ERROR("Error reading stack frame from FreeRTOS thread %" PRIx64, thread_id);
return retval;
}
- stack_ptr = pointer_casts_are_bad;
- LOG_DEBUG("FreeRTOS: Read stack pointer at 0x%" PRIx64 ", value 0x%" PRIx64,
- thread_id + param->thread_stack_offset,
- stack_ptr);
+ LOG_DEBUG("[%" PRId64 "] FreeRTOS: Read stack pointer at 0x%" PRIx64 ", value 0x%" PRIx64,
+ thread_id, entry->tcb + freertos->thread_stack_offset, *stack_ptr);
- /* Check for armv7m with *enabled* FPU, i.e. a Cortex-M4F */
- int cm4_fpu_enabled = 0;
- struct armv7m_common *armv7m_target = target_to_armv7m(rtos->target);
- if (is_armv7m(armv7m_target)) {
- if ((armv7m_target->fp_feature == FPV4_SP) || (armv7m_target->fp_feature == FPV5_SP) ||
- (armv7m_target->fp_feature == FPV5_DP)) {
- /* Found ARM v7m target which includes a FPU */
- uint32_t cpacr;
+ if (param->stacking(rtos, stacking_info, *stack_ptr) != ERROR_OK) {
+ LOG_ERROR("No stacking info found for %s!", param->target_name);
+ return ERROR_FAIL;
+ }
- retval = target_read_u32(rtos->target, FPU_CPACR, &cpacr);
- if (retval != ERROR_OK) {
- LOG_ERROR("Could not read CPACR register to check FPU state");
- return -1;
- }
+ return ERROR_OK;
+}
- /* Check if CP10 and CP11 are set to full access. */
- if (cpacr & 0x00F00000) {
- /* Found target with enabled FPU */
- cm4_fpu_enabled = 1;
- }
- }
+static int freertos_get_thread_reg_list(struct rtos *rtos, threadid_t thread_id,
+ struct rtos_reg **reg_list, int *num_regs)
+{
+ /* Let the caller read registers directly for the current thread. */
+ if (thread_id == 0)
+ return ERROR_FAIL;
+
+ const struct rtos_register_stacking *stacking_info;
+ target_addr_t stack_ptr;
+ if (freertos_get_stacking_info(rtos, thread_id, &stacking_info, &stack_ptr) != ERROR_OK)
+ return ERROR_FAIL;
+
+ return rtos_generic_stack_read(rtos->target, stacking_info, stack_ptr, reg_list, num_regs);
+}
+
+static int freertos_get_thread_reg_value(struct rtos *rtos, threadid_t thread_id,
+ uint32_t reg_num, uint32_t *size, uint8_t **value)
+{
+ LOG_DEBUG("reg_num=%d", reg_num);
+ /* Let the caller read registers directly for the current thread. */
+ if (thread_id == 0)
+ return ERROR_FAIL;
+
+ const struct rtos_register_stacking *stacking_info;
+ target_addr_t stack_ptr;
+ if (freertos_get_stacking_info(rtos, thread_id, &stacking_info, &stack_ptr) != ERROR_OK)
+ return ERROR_FAIL;
+
+ struct rtos_reg reg;
+ reg.number = reg_num;
+ int result = rtos_generic_stack_read_reg(rtos->target, stacking_info,
+ stack_ptr, reg_num, &reg);
+ *size = reg.size;
+ *value = malloc(DIV_ROUND_UP(reg.size, 8));
+ if (!*value) {
+ LOG_ERROR("Failed to allocate memory for %d-bit register.", reg.size);
+ return ERROR_FAIL;
}
+ memcpy(*value, reg.value, DIV_ROUND_UP(reg.size, 8));
+ return result;
+}
- if (cm4_fpu_enabled == 1) {
- /* Read the LR to decide between stacking with or without FPU */
- uint32_t lr_svc = 0;
- retval = target_read_u32(rtos->target,
- stack_ptr + 0x20,
- &lr_svc);
- if (retval != ERROR_OK) {
- LOG_OUTPUT("Error reading stack frame from FreeRTOS thread");
- return retval;
- }
- if ((lr_svc & 0x10) == 0)
- return rtos_generic_stack_read(rtos->target, param->stacking_info_cm4f_fpu, stack_ptr, reg_list, num_regs);
- else
- return rtos_generic_stack_read(rtos->target, param->stacking_info_cm4f, stack_ptr, reg_list, num_regs);
- } else
- return rtos_generic_stack_read(rtos->target, param->stacking_info_cm3, stack_ptr, reg_list, num_regs);
+static int freertos_set_reg(struct rtos *rtos, uint32_t reg_num, uint8_t *reg_value)
+{
+ LOG_DEBUG("[%" PRId64 "] reg_num=%" PRId32, rtos->current_threadid, reg_num);
+
+ /* Let the caller write registers directly for the current thread. */
+ if (rtos->current_threadid == rtos->current_thread)
+ return ERROR_FAIL;
+
+ const struct rtos_register_stacking *stacking_info;
+ target_addr_t stack_ptr;
+ if (freertos_get_stacking_info(rtos, rtos->current_threadid,
+ &stacking_info, &stack_ptr) != ERROR_OK)
+ return ERROR_FAIL;
+
+ return rtos_generic_stack_write_reg(rtos->target, stacking_info, stack_ptr,
+ reg_num, reg_value);
}
static int freertos_get_symbol_list_to_lookup(struct symbol_table_elem *symbol_list[])
@@ -533,12 +929,32 @@ static bool freertos_detect_rtos(struct target *target)
static int freertos_create(struct target *target)
{
- for (unsigned int i = 0; i < ARRAY_SIZE(freertos_params_list); i++)
- if (strcmp(freertos_params_list[i].target_name, target->type->name) == 0) {
- target->rtos->rtos_specific_params = (void *)&freertos_params_list[i];
- return 0;
- }
+ unsigned int i = 0;
+ while (i < ARRAY_SIZE(freertos_params_list) &&
+ strcmp(freertos_params_list[i].target_name, target->type->name) != 0) {
+ i++;
+ }
+ if (i >= ARRAY_SIZE(freertos_params_list)) {
+ LOG_ERROR("Could not find target in FreeRTOS compatibility list");
+ return ERROR_FAIL;
+ }
+
+ target->rtos->rtos_specific_params = calloc(1, sizeof(struct FreeRTOS));
+ if (!target->rtos->rtos_specific_params) {
+ LOG_ERROR("calloc failed");
+ return ERROR_FAIL;
+ }
+
+ struct FreeRTOS *freertos = (struct FreeRTOS *) target->rtos->rtos_specific_params;
+ INIT_LIST_HEAD(&freertos->thread_entry_list);
+
+ freertos->param = &freertos_params_list[i];
+
+ if (freertos->param->commands) {
+ if (register_commands(target->rtos->cmd_ctx, NULL,
+ freertos->param->commands) != ERROR_OK)
+ return ERROR_FAIL;
+ }
- LOG_ERROR("Could not find target in FreeRTOS compatibility list");
- return -1;
+ return register_commands(target->rtos->cmd_ctx, NULL, freertos_commands);
}
diff --git a/src/rtos/hwthread.c b/src/rtos/hwthread.c
index 03c4d85..eac72fc 100644
--- a/src/rtos/hwthread.c
+++ b/src/rtos/hwthread.c
@@ -18,17 +18,20 @@
static bool hwthread_detect_rtos(struct target *target);
static int hwthread_create(struct target *target);
static int hwthread_update_threads(struct rtos *rtos);
-static int hwthread_get_thread_reg(struct rtos *rtos, int64_t thread_id,
- uint32_t reg_num, struct rtos_reg *rtos_reg);
+static int hwthread_get_thread_reg_value(struct rtos *rtos, int64_t thread_id,
+ uint32_t reg_num, uint32_t *size, uint8_t **value);
static int hwthread_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
struct rtos_reg **reg_list, int *num_regs);
static int hwthread_get_symbol_list_to_lookup(struct symbol_table_elem *symbol_list[]);
static int hwthread_smp_init(struct target *target);
static int hwthread_set_reg(struct rtos *rtos, uint32_t reg_num, uint8_t *reg_value);
+static bool hwthread_needs_fake_step(struct target *target, int64_t thread_id);
static int hwthread_read_buffer(struct rtos *rtos, target_addr_t address,
uint32_t size, uint8_t *buffer);
static int hwthread_write_buffer(struct rtos *rtos, target_addr_t address,
uint32_t size, const uint8_t *buffer);
+struct target *hwthread_swbp_target(struct rtos *rtos, target_addr_t address,
+ uint32_t length, enum breakpoint_type type);
#define HW_THREAD_NAME_STR_SIZE (32)
@@ -45,12 +48,14 @@ const struct rtos_type hwthread_rtos = {
.create = hwthread_create,
.update_threads = hwthread_update_threads,
.get_thread_reg_list = hwthread_get_thread_reg_list,
- .get_thread_reg = hwthread_get_thread_reg,
+ .get_thread_reg_value = hwthread_get_thread_reg_value,
.get_symbol_list_to_lookup = hwthread_get_symbol_list_to_lookup,
.smp_init = hwthread_smp_init,
.set_reg = hwthread_set_reg,
+ .needs_fake_step = hwthread_needs_fake_step,
.read_buffer = hwthread_read_buffer,
.write_buffer = hwthread_write_buffer,
+ .swbp_target = hwthread_swbp_target
};
struct hwthread_params {
@@ -86,19 +91,17 @@ static int hwthread_update_threads(struct rtos *rtos)
enum target_debug_reason current_reason = DBG_REASON_UNDEFINED;
if (!rtos)
- return -1;
+ return ERROR_FAIL;
target = rtos->target;
- /* wipe out previous thread details if any */
- rtos_free_threadlist(rtos);
-
/* determine the number of "threads" */
if (target->smp) {
foreach_smp_target(head, target->smp_targets) {
struct target *curr = head->target;
- if (!target_was_examined(curr))
+ if (!target_was_examined(curr) ||
+ curr->state == TARGET_UNAVAILABLE)
continue;
++thread_list_size;
@@ -123,7 +126,8 @@ static int hwthread_update_threads(struct rtos *rtos)
foreach_smp_target(head, target->smp_targets) {
struct target *curr = head->target;
- if (!target_was_examined(curr))
+ if (!target_was_examined(curr) ||
+ curr->state == TARGET_UNAVAILABLE)
continue;
threadid_t tid = threadid_from_target(curr);
@@ -200,7 +204,7 @@ static int hwthread_update_threads(struct rtos *rtos)
else
rtos->current_thread = threadid_from_target(target);
- LOG_DEBUG("%s current_thread=%i", __func__, (int)rtos->current_thread);
+ LOG_DEBUG("current_thread=%i, threads_found=%d", (int)rtos->current_thread, threads_found);
return 0;
}
@@ -285,8 +289,8 @@ static int hwthread_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
return ERROR_OK;
}
-static int hwthread_get_thread_reg(struct rtos *rtos, int64_t thread_id,
- uint32_t reg_num, struct rtos_reg *rtos_reg)
+static int hwthread_get_thread_reg_value(struct rtos *rtos, int64_t thread_id,
+ uint32_t reg_num, uint32_t *size, uint8_t **value)
{
if (!rtos)
return ERROR_FAIL;
@@ -314,11 +318,14 @@ static int hwthread_get_thread_reg(struct rtos *rtos, int64_t thread_id,
if (reg->type->get(reg) != ERROR_OK)
return ERROR_FAIL;
- rtos_reg->number = reg->number;
- rtos_reg->size = reg->size;
- unsigned bytes = (reg->size + 7) / 8;
- assert(bytes <= sizeof(rtos_reg->value));
- memcpy(rtos_reg->value, reg->value, bytes);
+ *size = reg->size;
+ unsigned bytes = DIV_ROUND_UP(reg->size, 8);
+ *value = malloc(bytes);
+ if (!*value) {
+ LOG_ERROR("Failed to allocate memory for %d-bit register.", reg->size);
+ return ERROR_FAIL;
+ }
+ memcpy(*value, reg->value, bytes);
return ERROR_OK;
}
@@ -410,6 +417,11 @@ static int hwthread_create(struct target *target)
return 0;
}
+static bool hwthread_needs_fake_step(struct target *target, int64_t thread_id)
+{
+ return false;
+}
+
static int hwthread_read_buffer(struct rtos *rtos, target_addr_t address,
uint32_t size, uint8_t *buffer)
{
@@ -439,3 +451,9 @@ static int hwthread_write_buffer(struct rtos *rtos, target_addr_t address,
return target_write_buffer(curr, address, size, buffer);
}
+
+struct target *hwthread_swbp_target(struct rtos *rtos, target_addr_t address,
+ uint32_t length, enum breakpoint_type type)
+{
+ return hwthread_find_thread(rtos->target, rtos->current_thread);
+}
diff --git a/src/rtos/rtos.c b/src/rtos/rtos.c
index 2e76b50..c9da33b 100644
--- a/src/rtos/rtos.c
+++ b/src/rtos/rtos.c
@@ -11,6 +11,7 @@
#include "rtos.h"
#include "target/target.h"
+#include "target/smp.h"
#include "helper/log.h"
#include "helper/binarybuffer.h"
#include "server/gdb_server.h"
@@ -59,7 +60,9 @@ int rtos_smp_init(struct target *target)
return ERROR_TARGET_INIT_FAILED;
}
-static int rtos_target_for_threadid(struct connection *connection, int64_t threadid, struct target **t)
+static int rtos_target_for_threadid(struct connection *connection,
+ threadid_t threadid,
+ struct target **t)
{
struct target *curr = get_target_from_connection(connection);
if (t)
@@ -68,7 +71,8 @@ static int rtos_target_for_threadid(struct connection *connection, int64_t threa
return ERROR_OK;
}
-static int os_alloc(struct target *target, struct rtos_type *ostype)
+static int os_alloc(struct target *target, struct rtos_type *ostype,
+ struct command_context *cmd_ctx)
{
struct rtos *os = target->rtos = calloc(1, sizeof(struct rtos));
@@ -84,6 +88,7 @@ static int os_alloc(struct target *target, struct rtos_type *ostype)
/* RTOS drivers can override the packet handler in _create(). */
os->gdb_thread_packet = rtos_thread_packet;
os->gdb_target_for_threadid = rtos_target_for_threadid;
+ os->cmd_ctx = cmd_ctx;
return JIM_OK;
}
@@ -98,9 +103,10 @@ static void os_free(struct target *target)
target->rtos = NULL;
}
-static int os_alloc_create(struct target *target, struct rtos_type *ostype)
+static int os_alloc_create(struct target *target, struct rtos_type *ostype,
+ struct command_context *cmd_ctx)
{
- int ret = os_alloc(target, ostype);
+ int ret = os_alloc(target, ostype, cmd_ctx);
if (ret == JIM_OK) {
ret = target->rtos->type->create(target);
@@ -123,6 +129,8 @@ int rtos_create(struct jim_getopt_info *goi, struct target *target)
return JIM_ERR;
}
+ struct command_context *cmd_ctx = current_command_context(goi->interp);
+
os_free(target);
e = jim_getopt_string(goi, &cp, NULL);
@@ -140,12 +148,12 @@ int rtos_create(struct jim_getopt_info *goi, struct target *target)
/* rtos_qsymbol() will iterate over all RTOSes. Allocate
* target->rtos here, and set it to the first RTOS type. */
- return os_alloc(target, rtos_types[0]);
+ return os_alloc(target, rtos_types[0], cmd_ctx);
}
for (x = 0; rtos_types[x]; x++)
if (strcmp(cp, rtos_types[x]->name) == 0)
- return os_alloc_create(target, rtos_types[x]);
+ return os_alloc_create(target, rtos_types[x], cmd_ctx);
Jim_SetResultFormatted(goi->interp, "Unknown RTOS type %s, try one of: ", cp);
res = Jim_GetResult(goi->interp);
@@ -518,7 +526,7 @@ static int rtos_put_gdb_reg_list(struct connection *connection,
int rtos_get_gdb_reg(struct connection *connection, int reg_num)
{
struct target *target = get_target_from_connection(connection);
- int64_t current_threadid = target->rtos->current_threadid;
+ threadid_t current_threadid = target->rtos->current_threadid;
if ((target->rtos) && (current_threadid != -1) &&
(current_threadid != 0) &&
((current_threadid != target->rtos->current_thread) ||
@@ -533,15 +541,33 @@ int rtos_get_gdb_reg(struct connection *connection, int reg_num)
target->rtos->current_thread);
int retval;
- if (target->rtos->type->get_thread_reg) {
- reg_list = calloc(1, sizeof(*reg_list));
- num_regs = 1;
- retval = target->rtos->type->get_thread_reg(target->rtos,
- current_threadid, reg_num, &reg_list[0]);
+ if (target->rtos->type->get_thread_reg_value) {
+ uint32_t reg_size;
+ uint8_t *reg_value;
+ retval = target->rtos->type->get_thread_reg_value(target->rtos,
+ current_threadid, reg_num, &reg_size, &reg_value);
if (retval != ERROR_OK) {
LOG_ERROR("RTOS: failed to get register %d", reg_num);
return retval;
}
+
+ /* Create a reg_list with one register that can
+ * accommodate the full size of the one we just got the
+ * value for. To do that we allocate extra space off the
+ * end of the struct, relying on the fact that
+ * rtos_reg.value is the last element in the struct. */
+ reg_list = calloc(1, sizeof(*reg_list) + DIV_ROUND_UP(reg_size, 8));
+ if (!reg_list) {
+ free(reg_value);
+ LOG_ERROR("Failed to allocated reg_list for %d-byte register.",
+ reg_size);
+ return ERROR_FAIL;
+ }
+ reg_list[0].number = reg_num;
+ reg_list[0].size = reg_size;
+ memcpy(&reg_list[0].value, reg_value, DIV_ROUND_UP(reg_size, 8));
+ free(reg_value);
+ num_regs = 1;
} else {
retval = target->rtos->type->get_thread_reg_list(target->rtos,
current_threadid,
@@ -578,10 +604,9 @@ int rtos_get_gdb_reg_list(struct connection *connection)
struct rtos_reg *reg_list;
int num_regs;
- LOG_DEBUG("RTOS: getting register list for thread 0x%" PRIx64
- ", target->rtos->current_thread=0x%" PRIx64 "\r\n",
- current_threadid,
- target->rtos->current_thread);
+ LOG_TARGET_DEBUG(target, "RTOS: getting register list for thread 0x%" PRIx64
+ ", target->rtos->current_thread=0x%" PRIx64,
+ current_threadid, target->rtos->current_thread);
int retval = target->rtos->type->get_thread_reg_list(target->rtos,
current_threadid,
@@ -616,7 +641,7 @@ int rtos_set_reg(struct connection *connection, int reg_num,
int rtos_generic_stack_read(struct target *target,
const struct rtos_register_stacking *stacking,
- int64_t stack_ptr,
+ target_addr_t stack_ptr,
struct rtos_reg **reg_list,
int *num_regs)
{
@@ -628,7 +653,7 @@ int rtos_generic_stack_read(struct target *target,
}
/* Read the stack */
uint8_t *stack_data = malloc(stacking->stack_registers_size);
- uint32_t address = stack_ptr;
+ target_addr_t address = stack_ptr;
if (stacking->stack_growth_direction == 1)
address -= stacking->stack_registers_size;
@@ -638,7 +663,7 @@ int rtos_generic_stack_read(struct target *target,
LOG_ERROR("Error reading stack frame from thread");
return retval;
}
- LOG_DEBUG("RTOS: Read stack frame at 0x%" PRIx32, address);
+ LOG_DEBUG("RTOS: Read stack frame at " TARGET_ADDR_FMT, address);
#if 0
LOG_OUTPUT("Stack Data :");
@@ -668,6 +693,9 @@ int rtos_generic_stack_read(struct target *target,
buf_cpy(&new_stack_ptr, (*reg_list)[i].value, (*reg_list)[i].size);
else if (offset != -1)
buf_cpy(stack_data + offset, (*reg_list)[i].value, (*reg_list)[i].size);
+
+ LOG_DEBUG("register %d has value 0x%" PRIx64, (*reg_list)[i].number,
+ buf_get_u64((*reg_list)[i].value, 0, 64));
}
free(stack_data);
@@ -675,6 +703,100 @@ int rtos_generic_stack_read(struct target *target,
return ERROR_OK;
}
+/* Read an individual register from the RTOS stack. */
+int rtos_generic_stack_read_reg(struct target *target,
+ const struct rtos_register_stacking *stacking,
+ target_addr_t stack_ptr,
+ uint32_t reg_num, struct rtos_reg *reg)
+{
+ LOG_DEBUG("stack_ptr=" TARGET_ADDR_FMT ", reg_num=%d", stack_ptr, reg_num);
+ unsigned total_count = MAX(stacking->total_register_count, stacking->num_output_registers);
+ unsigned i;
+ for (i = 0; i < total_count; i++) {
+ if (stacking->register_offsets[i].number == reg_num)
+ break;
+ }
+ if (i >= total_count) {
+ /* This register is not on the stack. Return error so a caller somewhere
+ * will just read the register directly from the target. */
+ return ERROR_FAIL;
+ }
+
+ const struct stack_register_offset *offsets = &stacking->register_offsets[i];
+ reg->size = offsets->width_bits;
+
+ unsigned width_bytes = DIV_ROUND_UP(offsets->width_bits, 8);
+ if (offsets->offset >= 0) {
+ target_addr_t address = stack_ptr;
+
+ if (stacking->stack_growth_direction == 1)
+ address -= stacking->stack_registers_size;
+
+ if (target_read_buffer(
+ target, address + offsets->offset,
+ width_bytes, reg->value) != ERROR_OK)
+ return ERROR_FAIL;
+ LOG_DEBUG("register %d has value 0x%" PRIx64, reg->number,
+ buf_get_u64(reg->value, 0, 64));
+ } else {
+ memset(reg->value, 0, width_bytes);
+ }
+
+ return ERROR_OK;
+}
+
+int rtos_generic_stack_write_reg(struct target *target,
+ const struct rtos_register_stacking *stacking,
+ target_addr_t stack_ptr,
+ uint32_t reg_num, uint8_t *reg_value)
+{
+ LOG_DEBUG("stack_ptr=" TARGET_ADDR_FMT ", reg_num=%d", stack_ptr, reg_num);
+ unsigned total_count = MAX(stacking->total_register_count, stacking->num_output_registers);
+ unsigned i;
+ for (i = 0; i < total_count; i++) {
+ if (stacking->register_offsets[i].number == reg_num)
+ break;
+ }
+ if (i >= total_count) {
+ /* This register is not on the stack. Return error so a caller somewhere
+ * will just read the register directly from the target. */
+ return ERROR_FAIL;
+ }
+
+ const struct stack_register_offset *offsets = &stacking->register_offsets[i];
+
+ unsigned width_bytes = DIV_ROUND_UP(offsets->width_bits, 8);
+ if (offsets->offset >= 0) {
+ target_addr_t address = stack_ptr;
+
+ if (stacking->stack_growth_direction == 1)
+ address -= stacking->stack_registers_size;
+
+ LOG_DEBUG("write 0x%" PRIx64 " to register %d",
+ buf_get_u64(reg_value, 0, offsets->width_bits), reg_num);
+ if (target_write_buffer(
+ target, address + offsets->offset,
+ width_bytes, reg_value) != ERROR_OK)
+ return ERROR_FAIL;
+ } else if (offsets->offset == -1) {
+ /* This register isn't on the stack, but is listed as one of those. We
+ * read it as 0, and ignore writes. */
+ } else if (offsets->offset == -2) {
+ /* This register requires computation when we "read" it. I'm not sure
+ * how to handle writes. We can't simply return error here because then
+ * the higher level code will end up writing the register in the halted
+ * core, which is definitely not the same as writing it for a thread. */
+ LOG_ERROR("Don't know how to write register %d with offset -2 in a thread.",
+ reg_num);
+ assert(0);
+ } else {
+ LOG_ERROR("Don't know how to handle offset <2.");
+ assert(0);
+ }
+
+ return ERROR_OK;
+}
+
static int rtos_try_next(struct target *target)
{
struct rtos *os = target->rtos;
@@ -697,10 +819,29 @@ static int rtos_try_next(struct target *target)
return 1;
}
-int rtos_update_threads(struct target *target)
+struct rtos *rtos_of_target(struct target *target)
{
+ /* Primarily consider the rtos field of the target itself, secondarily consider
+ * rtos field SMP leader target, then consider rtos field of any other target in the SMP group.
+ * Otherwise NULL return means that no associated non-zero rtos field could be found. */
+
+ struct target_list *pos;
+
if ((target->rtos) && (target->rtos->type))
- target->rtos->type->update_threads(target->rtos);
+ return target->rtos;
+
+ foreach_smp_target(pos, target->smp_targets)
+ if ((pos->target->rtos) && (pos->target->rtos->type))
+ return pos->target->rtos;
+
+ return NULL;
+}
+
+int rtos_update_threads(struct target *target)
+{
+ struct rtos *rtos = rtos_of_target(target);
+ if (rtos)
+ rtos->type->update_threads(rtos);
return ERROR_OK;
}
@@ -722,6 +863,13 @@ void rtos_free_threadlist(struct rtos *rtos)
}
}
+bool rtos_needs_fake_step(struct target *target, int64_t thread_id)
+{
+ if (target->rtos->type->needs_fake_step)
+ return target->rtos->type->needs_fake_step(target, thread_id);
+ return target->rtos->current_thread != thread_id;
+}
+
int rtos_read_buffer(struct target *target, target_addr_t address,
uint32_t size, uint8_t *buffer)
{
@@ -737,3 +885,11 @@ int rtos_write_buffer(struct target *target, target_addr_t address,
return target->rtos->type->write_buffer(target->rtos, address, size, buffer);
return ERROR_NOT_IMPLEMENTED;
}
+
+struct target *rtos_swbp_target(struct target *target, target_addr_t address,
+ uint32_t length, enum breakpoint_type type)
+{
+ if (target->rtos->type->swbp_target)
+ return target->rtos->type->swbp_target(target->rtos, address, length, type);
+ return target;
+}
diff --git a/src/rtos/rtos.h b/src/rtos/rtos.h
index ace5751..6695915 100644
--- a/src/rtos/rtos.h
+++ b/src/rtos/rtos.h
@@ -9,6 +9,7 @@
#define OPENOCD_RTOS_RTOS_H
#include "server/server.h"
+#include "target/breakpoints.h"
#include "target/target.h"
#include <helper/jim-nvp.h>
@@ -46,14 +47,18 @@ struct rtos {
struct thread_detail *thread_details;
int thread_count;
int (*gdb_thread_packet)(struct connection *connection, char const *packet, int packet_size);
- int (*gdb_target_for_threadid)(struct connection *connection, int64_t thread_id, struct target **p_target);
+ int (*gdb_target_for_threadid)(struct connection *connection, threadid_t thread_id, struct target **p_target);
void *rtos_specific_params;
+ /* Populated in rtos.c, so that individual RTOSes can register commands. */
+ struct command_context *cmd_ctx;
};
struct rtos_reg {
uint32_t number;
uint32_t size;
uint8_t value[16];
+ /* WARNING: rtos_get_gdb_reg() relies on the fact that value is the last
+ * element of this struct. Any new fields should be added *before* value. */
};
struct rtos_type {
@@ -63,14 +68,23 @@ struct rtos_type {
int (*smp_init)(struct target *target);
int (*update_threads)(struct rtos *rtos);
/** Return a list of general registers, with their values filled out. */
- int (*get_thread_reg_list)(struct rtos *rtos, int64_t thread_id,
+ int (*get_thread_reg_list)(struct rtos *rtos, threadid_t thread_id,
struct rtos_reg **reg_list, int *num_regs);
- int (*get_thread_reg)(struct rtos *rtos, int64_t thread_id,
- uint32_t reg_num, struct rtos_reg *reg);
+ /** Return the size and value of the specified reg_num. The value is
+ * allocated by the callee and freed by the caller. */
+ int (*get_thread_reg_value)(struct rtos *rtos, threadid_t thread_id,
+ uint32_t reg_num, uint32_t *size, uint8_t **value);
int (*get_symbol_list_to_lookup)(struct symbol_table_elem *symbol_list[]);
int (*clean)(struct target *target);
char * (*ps_command)(struct target *target);
int (*set_reg)(struct rtos *rtos, uint32_t reg_num, uint8_t *reg_value);
+ /**
+ * Possibly work around an annoying gdb behaviour: when the current thread
+ * is changed in gdb, it assumes that the target can follow and also make
+ * the thread current. This is an assumption that cannot hold for a real
+ * target running a multi-threading OS. If an RTOS can do this, override
+ * needs_fake_step(). */
+ bool (*needs_fake_step)(struct target *target, threadid_t thread_id);
/* Implement these if different threads in the RTOS can see memory
* differently (for instance because address translation might be different
* for each thread). */
@@ -78,6 +92,12 @@ struct rtos_type {
uint8_t *buffer);
int (*write_buffer)(struct rtos *rtos, target_addr_t address, uint32_t size,
const uint8_t *buffer);
+ /* When a software breakpoint is set, it is set on only one target,
+ * because we assume memory is shared across them. By default this is the
+ * first target in the SMP group. Override this function to have
+ * breakpoint_add() use a different target. */
+ struct target * (*swbp_target)(struct rtos *rtos, target_addr_t address,
+ uint32_t length, enum breakpoint_type type);
};
struct stack_register_offset {
@@ -89,8 +109,9 @@ struct stack_register_offset {
};
struct rtos_register_stacking {
- unsigned char stack_registers_size;
- signed char stack_growth_direction;
+ unsigned stack_registers_size;
+ int stack_growth_direction;
+ /* The number of gdb general registers, in order. */
unsigned char num_output_registers;
/* Some targets require evaluating the stack to determine the
* actual stack pointer for a process. If this field is NULL,
@@ -102,6 +123,10 @@ struct rtos_register_stacking {
const struct rtos_register_stacking *stacking,
target_addr_t stack_ptr);
const struct stack_register_offset *register_offsets;
+ /* Total number of registers on the stack, including the general ones. This
+ * may be 0 if there are no additional registers on the stack beyond the
+ * general ones. */
+ unsigned total_register_count;
};
#define GDB_THREAD_PACKET_NOT_CONSUMED (-40)
@@ -112,9 +137,17 @@ int rtos_set_reg(struct connection *connection, int reg_num,
uint8_t *reg_value);
int rtos_generic_stack_read(struct target *target,
const struct rtos_register_stacking *stacking,
- int64_t stack_ptr,
+ target_addr_t stack_ptr,
struct rtos_reg **reg_list,
int *num_regs);
+int rtos_generic_stack_read_reg(struct target *target,
+ const struct rtos_register_stacking *stacking,
+ target_addr_t stack_ptr,
+ uint32_t reg_num, struct rtos_reg *reg);
+int rtos_generic_stack_write_reg(struct target *target,
+ const struct rtos_register_stacking *stacking,
+ target_addr_t stack_ptr,
+ uint32_t reg_num, uint8_t *reg_value);
int gdb_thread_packet(struct connection *connection, char const *packet, int packet_size);
int rtos_get_gdb_reg(struct connection *connection, int reg_num);
int rtos_get_gdb_reg_list(struct connection *connection);
@@ -123,9 +156,13 @@ void rtos_free_threadlist(struct rtos *rtos);
int rtos_smp_init(struct target *target);
/* function for handling symbol access */
int rtos_qsymbol(struct connection *connection, char const *packet, int packet_size);
+bool rtos_needs_fake_step(struct target *target, threadid_t thread_id);
int rtos_read_buffer(struct target *target, target_addr_t address,
uint32_t size, uint8_t *buffer);
int rtos_write_buffer(struct target *target, target_addr_t address,
uint32_t size, const uint8_t *buffer);
+struct target *rtos_swbp_target(struct target *target, target_addr_t address,
+ uint32_t length, enum breakpoint_type type);
+struct rtos *rtos_of_target(struct target *target);
#endif /* OPENOCD_RTOS_RTOS_H */
diff --git a/src/rtos/rtos_standard_stackings.c b/src/rtos/rtos_standard_stackings.c
index f83f0a1..7f0c3c7 100644
--- a/src/rtos/rtos_standard_stackings.c
+++ b/src/rtos/rtos_standard_stackings.c
@@ -11,6 +11,7 @@
#include "rtos.h"
#include "target/armv7m.h"
+#include "target/riscv/riscv.h"
static const struct stack_register_offset rtos_standard_cortex_m3_stack_offsets[ARMV7M_NUM_CORE_REGS] = {
{ ARMV7M_R0, 0x20, 32 }, /* r0 */
@@ -102,15 +103,181 @@ static const struct stack_register_offset rtos_standard_cortex_r4_stack_offsets[
{ 26, 0x04, 32 }, /* CSPR */
};
+static const struct stack_register_offset rtos_metal_rv32_stack_offsets[] = {
+ /* zero isn't on the stack. By making its offset -1 we leave the value at 0
+ * inside rtos_generic_stack_read(). */
+ { GDB_REGNO_ZERO, -1, 32 },
+ { GDB_REGNO_RA, 0x04, 32 },
+ { GDB_REGNO_SP, 0x08, 32 },
+ { GDB_REGNO_GP, 0x0c, 32 },
+ { GDB_REGNO_TP, 0x10, 32 },
+ { GDB_REGNO_T0, 0x14, 32 },
+ { GDB_REGNO_T1, 0x18, 32 },
+ { GDB_REGNO_T2, 0x1c, 32 },
+ { GDB_REGNO_FP, 0x20, 32 },
+ { GDB_REGNO_S1, 0x24, 32 },
+ { GDB_REGNO_A0, 0x28, 32 },
+ { GDB_REGNO_A1, 0x2c, 32 },
+ { GDB_REGNO_A2, 0x30, 32 },
+ { GDB_REGNO_A3, 0x34, 32 },
+ { GDB_REGNO_A4, 0x38, 32 },
+ { GDB_REGNO_A5, 0x3c, 32 },
+ { GDB_REGNO_A6, 0x40, 32 },
+ { GDB_REGNO_A7, 0x44, 32 },
+ { GDB_REGNO_S2, 0x48, 32 },
+ { GDB_REGNO_S3, 0x4c, 32 },
+ { GDB_REGNO_S4, 0x50, 32 },
+ { GDB_REGNO_S5, 0x54, 32 },
+ { GDB_REGNO_S6, 0x58, 32 },
+ { GDB_REGNO_S7, 0x5c, 32 },
+ { GDB_REGNO_S8, 0x60, 32 },
+ { GDB_REGNO_S9, 0x64, 32 },
+ { GDB_REGNO_S10, 0x68, 32 },
+ { GDB_REGNO_S11, 0x6c, 32 },
+ { GDB_REGNO_T3, 0x70, 32 },
+ { GDB_REGNO_T4, 0x74, 32 },
+ { GDB_REGNO_T5, 0x78, 32 },
+ { GDB_REGNO_T6, 0x7c, 32 },
+ { GDB_REGNO_PC, 0x80, 32 },
+ /* Registers below are on the stack, but not what gdb expects to return from
+ * a 'g' packet so are only accessible through get_reg. */
+ { GDB_REGNO_MSTATUS, 0x84, 32 },
+};
+
+static const struct stack_register_offset rtos_metal_rv64_stack_offsets[] = {
+ /* zero isn't on the stack. By making its offset -1 we leave the value at 0
+ * inside rtos_generic_stack_read(). */
+ { GDB_REGNO_ZERO, -1, 64 },
+ { GDB_REGNO_RA, 2 * 0x04, 64 },
+ { GDB_REGNO_SP, 2 * 0x08, 64 },
+ { GDB_REGNO_GP, 2 * 0x0c, 64 },
+ { GDB_REGNO_TP, 2 * 0x10, 64 },
+ { GDB_REGNO_T0, 2 * 0x14, 64 },
+ { GDB_REGNO_T1, 2 * 0x18, 64 },
+ { GDB_REGNO_T2, 2 * 0x1c, 64 },
+ { GDB_REGNO_FP, 2 * 0x20, 64 },
+ { GDB_REGNO_S1, 2 * 0x24, 64 },
+ { GDB_REGNO_A0, 2 * 0x28, 64 },
+ { GDB_REGNO_A1, 2 * 0x2c, 64 },
+ { GDB_REGNO_A2, 2 * 0x30, 64 },
+ { GDB_REGNO_A3, 2 * 0x34, 64 },
+ { GDB_REGNO_A4, 2 * 0x38, 64 },
+ { GDB_REGNO_A5, 2 * 0x3c, 64 },
+ { GDB_REGNO_A6, 2 * 0x40, 64 },
+ { GDB_REGNO_A7, 2 * 0x44, 64 },
+ { GDB_REGNO_S2, 2 * 0x48, 64 },
+ { GDB_REGNO_S3, 2 * 0x4c, 64 },
+ { GDB_REGNO_S4, 2 * 0x50, 64 },
+ { GDB_REGNO_S5, 2 * 0x54, 64 },
+ { GDB_REGNO_S6, 2 * 0x58, 64 },
+ { GDB_REGNO_S7, 2 * 0x5c, 64 },
+ { GDB_REGNO_S8, 2 * 0x60, 64 },
+ { GDB_REGNO_S9, 2 * 0x64, 64 },
+ { GDB_REGNO_S10, 2 * 0x68, 64 },
+ { GDB_REGNO_S11, 2 * 0x6c, 64 },
+ { GDB_REGNO_T3, 2 * 0x70, 64 },
+ { GDB_REGNO_T4, 2 * 0x74, 64 },
+ { GDB_REGNO_T5, 2 * 0x78, 64 },
+ { GDB_REGNO_T6, 2 * 0x7c, 64 },
+ { GDB_REGNO_PC, 2 * 0x80, 64 },
+ /* Registers below are on the stack, but not what gdb expects to return from
+ * a 'g' packet so are only accessible through get_reg. */
+ { GDB_REGNO_MSTATUS, 2 * 0x84, 64 },
+};
+
+static const struct stack_register_offset rtos_standard_rv32_stack_offsets[] = {
+ /* zero isn't on the stack. By making its offset -1 we leave the value at 0
+ * inside rtos_generic_stack_read(). */
+ { GDB_REGNO_ZERO, -1, 32 },
+ { GDB_REGNO_RA, 0x04, 32 },
+ { GDB_REGNO_SP, -2, 32 },
+ { GDB_REGNO_GP, -2, 32 },
+ { GDB_REGNO_TP, -2, 32 },
+ { GDB_REGNO_T0, 0x08, 32 },
+ { GDB_REGNO_T1, 0x0c, 32 },
+ { GDB_REGNO_T2, 0x10, 32 },
+ { GDB_REGNO_FP, 0x14, 32 },
+ { GDB_REGNO_S1, 0x18, 32 },
+ { GDB_REGNO_A0, 0x1c, 32 },
+ { GDB_REGNO_A1, 0x20, 32 },
+ { GDB_REGNO_A2, 0x24, 32 },
+ { GDB_REGNO_A3, 0x28, 32 },
+ { GDB_REGNO_A4, 0x2c, 32 },
+ { GDB_REGNO_A5, 0x30, 32 },
+ { GDB_REGNO_A6, 0x34, 32 },
+ { GDB_REGNO_A7, 0x38, 32 },
+ { GDB_REGNO_S2, 0x3c, 32 },
+ { GDB_REGNO_S3, 0x40, 32 },
+ { GDB_REGNO_S4, 0x44, 32 },
+ { GDB_REGNO_S5, 0x48, 32 },
+ { GDB_REGNO_S6, 0x4c, 32 },
+ { GDB_REGNO_S7, 0x50, 32 },
+ { GDB_REGNO_S8, 0x54, 32 },
+ { GDB_REGNO_S9, 0x58, 32 },
+ { GDB_REGNO_S10, 0x5c, 32 },
+ { GDB_REGNO_S11, 0x60, 32 },
+ { GDB_REGNO_T3, 0x64, 32 },
+ { GDB_REGNO_T4, 0x68, 32 },
+ { GDB_REGNO_T5, 0x6c, 32 },
+ { GDB_REGNO_T6, 0x70, 32 },
+ { GDB_REGNO_PC, 0, 32 },
+ /* Registers below are on the stack, but not what gdb expects to return from
+ * a 'g' packet so are only accessible through get_reg. */
+ { GDB_REGNO_MSTATUS, 29 * 4, 32 },
+};
+
+static const struct stack_register_offset rtos_standard_rv64_stack_offsets[] = {
+ /* zero isn't on the stack. By making its offset -1 we leave the value at 0
+ * inside rtos_generic_stack_read(). */
+ { GDB_REGNO_ZERO, -1, 64 },
+ { GDB_REGNO_RA, 2 * 0x04, 64 },
+ { GDB_REGNO_SP, -2, 64 },
+ { GDB_REGNO_GP, -2, 64 },
+ { GDB_REGNO_TP, -2, 64 },
+ { GDB_REGNO_T0, 2 * 0x08, 64 },
+ { GDB_REGNO_T1, 2 * 0x0c, 64 },
+ { GDB_REGNO_T2, 2 * 0x10, 64 },
+ { GDB_REGNO_FP, 2 * 0x14, 64 },
+ { GDB_REGNO_S1, 2 * 0x18, 64 },
+ { GDB_REGNO_A0, 2 * 0x1c, 64 },
+ { GDB_REGNO_A1, 2 * 0x20, 64 },
+ { GDB_REGNO_A2, 2 * 0x24, 64 },
+ { GDB_REGNO_A3, 2 * 0x28, 64 },
+ { GDB_REGNO_A4, 2 * 0x2c, 64 },
+ { GDB_REGNO_A5, 2 * 0x30, 64 },
+ { GDB_REGNO_A6, 2 * 0x34, 64 },
+ { GDB_REGNO_A7, 2 * 0x38, 64 },
+ { GDB_REGNO_S2, 2 * 0x3c, 64 },
+ { GDB_REGNO_S3, 2 * 0x40, 64 },
+ { GDB_REGNO_S4, 2 * 0x44, 64 },
+ { GDB_REGNO_S5, 2 * 0x48, 64 },
+ { GDB_REGNO_S6, 2 * 0x4c, 64 },
+ { GDB_REGNO_S7, 2 * 0x50, 64 },
+ { GDB_REGNO_S8, 2 * 0x54, 64 },
+ { GDB_REGNO_S9, 2 * 0x58, 64 },
+ { GDB_REGNO_S10, 2 * 0x5c, 64 },
+ { GDB_REGNO_S11, 2 * 0x60, 64 },
+ { GDB_REGNO_T3, 2 * 0x64, 64 },
+ { GDB_REGNO_T4, 2 * 0x68, 64 },
+ { GDB_REGNO_T5, 2 * 0x6c, 64 },
+ { GDB_REGNO_T6, 2 * 0x70, 64 },
+ { GDB_REGNO_PC, 0, 64 },
+ /* Registers below are on the stack, but not what gdb expects to return from
+ * a 'g' packet so are only accessible through get_reg. */
+ { GDB_REGNO_MSTATUS, 2 * 29 * 4, 64 },
+};
+
static target_addr_t rtos_generic_stack_align(struct target *target,
const uint8_t *stack_data, const struct rtos_register_stacking *stacking,
target_addr_t stack_ptr, int align)
{
- target_addr_t new_stack_ptr;
- target_addr_t aligned_stack_ptr;
- new_stack_ptr = stack_ptr - stacking->stack_growth_direction *
- stacking->stack_registers_size;
- aligned_stack_ptr = new_stack_ptr & ~((target_addr_t)align - 1);
+ target_addr_t new_stack_ptr = stack_ptr;
+ if (stacking->stack_growth_direction > 0)
+ new_stack_ptr -= stacking->stack_registers_size;
+ else
+ new_stack_ptr += stacking->stack_registers_size;
+ target_addr_t aligned_stack_ptr = new_stack_ptr & ~((int64_t)align - 1);
+
if (aligned_stack_ptr != new_stack_ptr &&
stacking->stack_growth_direction == -1) {
/* If we have a downward growing stack, the simple alignment code
@@ -229,3 +396,39 @@ const struct rtos_register_stacking rtos_standard_cortex_r4_stacking = {
.calculate_process_stack = rtos_generic_stack_align8,
.register_offsets = rtos_standard_cortex_r4_stack_offsets
};
+
+const struct rtos_register_stacking rtos_metal_rv32_stacking = {
+ .stack_registers_size = (32 + 2) * 4,
+ .stack_growth_direction = -1,
+ .num_output_registers = 33,
+ .calculate_process_stack = rtos_generic_stack_align8,
+ .register_offsets = rtos_metal_rv32_stack_offsets,
+ .total_register_count = ARRAY_SIZE(rtos_metal_rv32_stack_offsets)
+};
+
+const struct rtos_register_stacking rtos_standard_rv32_stacking = {
+ .stack_registers_size = (32 + 2) * 4,
+ .stack_growth_direction = -1,
+ .num_output_registers = 33,
+ .calculate_process_stack = rtos_generic_stack_align8,
+ .register_offsets = rtos_standard_rv32_stack_offsets,
+ .total_register_count = ARRAY_SIZE(rtos_standard_rv32_stack_offsets)
+};
+
+const struct rtos_register_stacking rtos_metal_rv64_stacking = {
+ .stack_registers_size = (32 + 2) * 8,
+ .stack_growth_direction = -1,
+ .num_output_registers = 33,
+ .calculate_process_stack = rtos_generic_stack_align8,
+ .register_offsets = rtos_metal_rv64_stack_offsets,
+ .total_register_count = ARRAY_SIZE(rtos_metal_rv64_stack_offsets)
+};
+
+const struct rtos_register_stacking rtos_standard_rv64_stacking = {
+ .stack_registers_size = (32 + 2) * 8,
+ .stack_growth_direction = -1,
+ .num_output_registers = 33,
+ .calculate_process_stack = rtos_generic_stack_align8,
+ .register_offsets = rtos_standard_rv64_stack_offsets,
+ .total_register_count = ARRAY_SIZE(rtos_standard_rv64_stack_offsets)
+};
diff --git a/src/rtos/rtos_standard_stackings.h b/src/rtos/rtos_standard_stackings.h
index 2477fff..70ea034 100644
--- a/src/rtos/rtos_standard_stackings.h
+++ b/src/rtos/rtos_standard_stackings.h
@@ -25,4 +25,9 @@ target_addr_t rtos_cortex_m_stack_align(struct target *target,
const uint8_t *stack_data, const struct rtos_register_stacking *stacking,
target_addr_t stack_ptr, size_t xpsr_offset);
+extern const struct rtos_register_stacking rtos_standard_rv32_stacking;
+extern const struct rtos_register_stacking rtos_standard_rv64_stacking;
+extern const struct rtos_register_stacking rtos_metal_rv32_stacking;
+extern const struct rtos_register_stacking rtos_metal_rv64_stacking;
+
#endif /* OPENOCD_RTOS_RTOS_STANDARD_STACKINGS_H */
diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c
index d8dbc2c..8360ee1 100644
--- a/src/server/gdb_server.c
+++ b/src/server/gdb_server.c
@@ -143,6 +143,29 @@ static int gdb_use_target_description = 1;
/* current processing free-run type, used by file-I/O */
static char gdb_running_type;
+/* Find an available target in the SMP group that gdb is connected to. For
+ * commands that affect an entire SMP group (like memory access and run control)
+ * this will give better results than returning the unavailable target and having
+ * the command fail. If gdb was aware that targets can be unavailable we
+ * wouldn't need this logic.
+ */
+struct target *get_available_target_from_connection(struct connection *connection)
+{
+ struct gdb_service *gdb_service = connection->service->priv;
+ struct target *target = gdb_service->target;
+ if (target->state == TARGET_UNAVAILABLE && target->smp) {
+ struct target_list *tlist;
+ foreach_smp_target(tlist, target->smp_targets) {
+ struct target *t = tlist->target;
+ if (t->state != TARGET_UNAVAILABLE)
+ return t;
+ }
+ /* If we can't find an available target, just return the
+ * original. */
+ }
+ return target;
+}
+
static int gdb_last_signal(struct target *target)
{
switch (target->debug_reason) {
@@ -424,6 +447,8 @@ static int gdb_put_packet_inner(struct connection *connection,
* At this point we should have nothing in the input queue from GDB,
* however sometimes '-' is sent even though we've already received
* an ACK (+) for everything we've sent off.
+ *
+ * This code appears to sometimes eat a ^C coming from gdb.
*/
int gotdata;
for (;; ) {
@@ -442,7 +467,7 @@ static int gdb_put_packet_inner(struct connection *connection,
break;
}
- LOG_WARNING("Discard unexpected char %c", reply);
+ LOG_DEBUG("Discard unexpected char %c", reply);
}
#endif
@@ -790,9 +815,12 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio
sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "W00");
} else {
struct target *ct;
- if (target->rtos) {
- target->rtos->current_threadid = target->rtos->current_thread;
- target->rtos->gdb_target_for_threadid(connection, target->rtos->current_threadid, &ct);
+ struct rtos *rtos;
+
+ rtos = rtos_of_target(target);
+ if (rtos) {
+ rtos->current_threadid = rtos->current_thread;
+ rtos->gdb_target_for_threadid(connection, rtos->current_threadid, &ct);
} else {
ct = target;
}
@@ -829,9 +857,9 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio
}
current_thread[0] = '\0';
- if (target->rtos)
+ if (rtos)
snprintf(current_thread, sizeof(current_thread), "thread:%" PRIx64 ";",
- target->rtos->current_thread);
+ rtos->current_thread);
sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "T%2.2x%s%s",
signal_var, stop_reason, current_thread);
@@ -960,9 +988,9 @@ static int gdb_target_callback_event_handler(struct target *target,
enum target_event event, void *priv)
{
struct connection *connection = priv;
- struct gdb_service *gdb_service = connection->service->priv;
+ struct target *gdb_target = get_available_target_from_connection(connection);
- if (gdb_service->target != target)
+ if (gdb_target != target)
return ERROR_OK;
switch (event) {
@@ -1380,7 +1408,7 @@ static int gdb_get_register_packet(struct connection *connection,
if ((reg_list_size <= reg_num) || !reg_list[reg_num] ||
!reg_list[reg_num]->exist || reg_list[reg_num]->hidden) {
LOG_ERROR("gdb requested a non-existing register (reg_num=%d)", reg_num);
- return ERROR_SERVER_REMOTE_CLOSED;
+ return gdb_error(connection, ERROR_FAIL);
}
if (!reg_list[reg_num]->valid) {
@@ -1491,7 +1519,7 @@ static int gdb_error(struct connection *connection, int retval)
static int gdb_read_memory_packet(struct connection *connection,
char const *packet, int packet_size)
{
- struct target *target = get_target_from_connection(connection);
+ struct target *target = get_available_target_from_connection(connection);
char *separator;
uint64_t addr = 0;
uint32_t len = 0;
@@ -1499,7 +1527,7 @@ static int gdb_read_memory_packet(struct connection *connection,
uint8_t *buffer;
char *hex_buffer;
- int retval = ERROR_OK;
+ int retval;
/* skip command character */
packet++;
@@ -1566,7 +1594,7 @@ static int gdb_read_memory_packet(struct connection *connection,
static int gdb_write_memory_packet(struct connection *connection,
char const *packet, int packet_size)
{
- struct target *target = get_target_from_connection(connection);
+ struct target *target = get_available_target_from_connection(connection);
char *separator;
uint64_t addr = 0;
uint32_t len = 0;
@@ -1617,7 +1645,7 @@ static int gdb_write_memory_packet(struct connection *connection,
static int gdb_write_memory_binary_packet(struct connection *connection,
char const *packet, int packet_size)
{
- struct target *target = get_target_from_connection(connection);
+ struct target *target = get_available_target_from_connection(connection);
char *separator;
uint64_t addr = 0;
uint32_t len = 0;
@@ -1696,7 +1724,7 @@ static int gdb_write_memory_binary_packet(struct connection *connection,
static int gdb_step_continue_packet(struct connection *connection,
char const *packet, int packet_size)
{
- struct target *target = get_target_from_connection(connection);
+ struct target *target = get_available_target_from_connection(connection);
int current = 0;
uint64_t address = 0x0;
int retval = ERROR_OK;
@@ -1724,7 +1752,7 @@ static int gdb_step_continue_packet(struct connection *connection,
static int gdb_breakpoint_watchpoint_packet(struct connection *connection,
char const *packet, int packet_size)
{
- struct target *target = get_target_from_connection(connection);
+ struct target *target = get_available_target_from_connection(connection);
int type;
enum breakpoint_type bp_type = BKPT_SOFT /* dummy init to avoid warning */;
enum watchpoint_rw wp_type = WPT_READ /* dummy init to avoid warning */;
@@ -1773,6 +1801,12 @@ static int gdb_breakpoint_watchpoint_packet(struct connection *connection,
case 0:
case 1:
if (packet[0] == 'Z') {
+ struct target *bp_target = target;
+ if (target->rtos && bp_type == BKPT_SOFT) {
+ bp_target = rtos_swbp_target(target, address, size, bp_type);
+ if (!bp_target)
+ return ERROR_FAIL;
+ }
retval = breakpoint_add(target, address, size, bp_type);
if (retval == ERROR_NOT_IMPLEMENTED) {
/* Send empty reply to report that breakpoints of this type are not supported */
@@ -1906,7 +1940,7 @@ static int gdb_memory_map(struct connection *connection,
* have to regenerate it a couple of times.
*/
- struct target *target = get_target_from_connection(connection);
+ struct target *target = get_available_target_from_connection(connection);
struct flash_bank *p;
char *xml = NULL;
int size = 0;
@@ -2610,6 +2644,7 @@ static int gdb_target_description_supported(struct target *target, int *supporte
&reg_list_size, REG_CLASS_ALL);
if (retval != ERROR_OK) {
LOG_ERROR("get register list failed");
+ reg_list = NULL;
goto error;
}
@@ -2754,6 +2789,7 @@ static int gdb_query_packet(struct connection *connection,
cmd = malloc((packet_size - 6) / 2 + 1);
size_t len = unhexify((uint8_t *)cmd, packet + 6, (packet_size - 6) / 2);
cmd[len] = 0;
+ LOG_DEBUG("qRcmd: %s", cmd);
/* We want to print all debug output to GDB connection */
gdb_connection->output_flag = GDB_OUTPUT_ALL;
@@ -2979,7 +3015,7 @@ static int gdb_query_packet(struct connection *connection,
static bool gdb_handle_vcont_packet(struct connection *connection, const char *packet, int packet_size)
{
struct gdb_connection *gdb_connection = connection->priv;
- struct target *target = get_target_from_connection(connection);
+ struct target *target = get_available_target_from_connection(connection);
const char *parse = packet;
int retval;
@@ -3050,7 +3086,23 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p
if (target->rtos) {
/* FIXME: why is this necessary? rtos state should be up-to-date here already! */
- rtos_update_threads(target);
+
+ /* Sometimes this results in picking a different thread than
+ * gdb just requested to step. Then we fake it, and now there's
+ * a different thread selected than gdb expects, so register
+ * accesses go to the wrong one!
+ * E.g.:
+ * Hg1$
+ * P8=72101ce197869329$ # write r8 on thread 1
+ * g$
+ * vCont?$
+ * vCont;s:1;c$ # rtos_update_threads changes to other thread
+ * g$
+ * qXfer:threads:read::0,fff$
+ * P8=cc060607eb89ca7f$ # write r8 on other thread
+ * g$
+ * */
+ /* rtos_update_threads(target); */
target->rtos->gdb_target_for_threadid(connection, thread_id, &ct);
@@ -3058,8 +3110,7 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p
* check if the thread to be stepped is the current rtos thread
* if not, we must fake the step
*/
- if (target->rtos->current_thread != thread_id)
- fake_step = true;
+ fake_step = rtos_needs_fake_step(target, thread_id);
}
if (parse[0] == ';') {
@@ -3097,19 +3148,15 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p
gdb_connection->output_flag = GDB_OUTPUT_ALL;
target_call_event_callbacks(ct, TARGET_EVENT_GDB_START);
- /*
- * work around an annoying gdb behaviour: when the current thread
- * is changed in gdb, it assumes that the target can follow and also
- * make the thread current. This is an assumption that cannot hold
- * for a real target running a multi-threading OS. We just fake
- * the step to not trigger an internal error in gdb. See
- * https://sourceware.org/bugzilla/show_bug.cgi?id=22925 for details
- */
if (fake_step) {
+ /* We just fake the step to not trigger an internal error in
+ * gdb. See https://sourceware.org/bugzilla/show_bug.cgi?id=22925
+ * for details. */
int sig_reply_len;
char sig_reply[128];
LOG_DEBUG("fake step thread %"PRIx64, thread_id);
+ target->rtos->current_threadid = thread_id;
sig_reply_len = snprintf(sig_reply, sizeof(sig_reply),
"T05thread:%016"PRIx64";", thread_id);
@@ -3206,7 +3253,7 @@ static void gdb_restart_inferior(struct connection *connection, const char *pack
static bool gdb_handle_vrun_packet(struct connection *connection, const char *packet, int packet_size)
{
- struct target *target = get_target_from_connection(connection);
+ struct target *target = get_available_target_from_connection(connection);
const char *parse = packet;
/* Skip "vRun" */
@@ -3252,7 +3299,7 @@ static int gdb_v_packet(struct connection *connection,
struct gdb_connection *gdb_connection = connection->priv;
int result;
- struct target *target = get_target_from_connection(connection);
+ struct target *target = get_available_target_from_connection(connection);
if (strncmp(packet, "vCont", 5) == 0) {
bool handled;
@@ -3423,7 +3470,7 @@ static int gdb_detach(struct connection *connection)
static int gdb_fileio_response_packet(struct connection *connection,
char const *packet, int packet_size)
{
- struct target *target = get_target_from_connection(connection);
+ struct target *target = get_available_target_from_connection(connection);
char *separator;
char *parsing_point;
int fileio_retcode = strtoul(packet + 1, &separator, 16);
@@ -3712,10 +3759,11 @@ static int gdb_input_inner(struct connection *connection)
}
if (gdb_con->ctrl_c) {
- if (target->state == TARGET_RUNNING) {
- struct target *t = target;
- if (target->rtos)
- target->rtos->gdb_target_for_threadid(connection, target->rtos->current_threadid, &t);
+ struct target *available_target = get_available_target_from_connection(connection);
+ if (available_target->state == TARGET_RUNNING) {
+ struct target *t = available_target;
+ if (available_target->rtos)
+ available_target->rtos->gdb_target_for_threadid(connection, target->rtos->current_threadid, &t);
retval = target_halt(t);
if (retval == ERROR_OK)
retval = target_poll(t);
@@ -3723,7 +3771,8 @@ static int gdb_input_inner(struct connection *connection)
target_call_event_callbacks(target, TARGET_EVENT_GDB_HALT);
gdb_con->ctrl_c = false;
} else {
- LOG_INFO("The target is not running when halt was requested, stopping GDB.");
+ LOG_TARGET_INFO(target, "Not running when halt was requested, stopping GDB. (state=%d)",
+ target->state);
target_call_event_callbacks(target, TARGET_EVENT_GDB_HALT);
}
}
diff --git a/src/server/server.c b/src/server/server.c
index 2be9045..4da31ae 100644
--- a/src/server/server.c
+++ b/src/server/server.c
@@ -481,6 +481,7 @@ int server_loop(struct command_context *command_context)
timeout_ms = polling_period;
tv.tv_usec = timeout_ms * 1000;
/* Only while we're sleeping we'll let others run */
+ kept_alive();
retval = socket_select(fd_max + 1, &read_fds, NULL, NULL, &tv);
}
diff --git a/src/target/breakpoints.c b/src/target/breakpoints.c
index bbaff4e..7bdb2a6 100644
--- a/src/target/breakpoints.c
+++ b/src/target/breakpoints.c
@@ -15,6 +15,7 @@
#include "target.h"
#include <helper/log.h>
#include "breakpoints.h"
+#include "rtos/rtos.h"
#include "smp.h"
static const char * const breakpoint_type_strings[] = {
@@ -73,7 +74,7 @@ static int breakpoint_add_internal(struct target *target,
reason = "resource not available";
goto fail;
case ERROR_TARGET_NOT_HALTED:
- reason = "target running";
+ reason = "target not halted";
goto fail;
default:
reason = "unknown reason";
@@ -206,16 +207,12 @@ int breakpoint_add(struct target *target,
uint32_t length,
enum breakpoint_type type)
{
- if (target->smp) {
- struct target_list *head;
-
- if (type == BKPT_SOFT) {
- head = list_first_entry(target->smp_targets, struct target_list, lh);
- return breakpoint_add_internal(head->target, address, length, type);
- }
-
- foreach_smp_target(head, target->smp_targets) {
- struct target *curr = head->target;
+ if (target->smp && type == BKPT_HARD) {
+ struct target_list *list_node;
+ foreach_smp_target(list_node, target->smp_targets) {
+ struct target *curr = list_node->target;
+ if (curr->state == TARGET_UNAVAILABLE)
+ continue;
int retval = breakpoint_add_internal(curr, address, length, type);
if (retval != ERROR_OK)
return retval;
@@ -237,6 +234,8 @@ int context_breakpoint_add(struct target *target,
foreach_smp_target(head, target->smp_targets) {
struct target *curr = head->target;
+ if (curr->state == TARGET_UNAVAILABLE)
+ continue;
int retval = context_breakpoint_add_internal(curr, asid, length, type);
if (retval != ERROR_OK)
return retval;
@@ -259,6 +258,8 @@ int hybrid_breakpoint_add(struct target *target,
foreach_smp_target(head, target->smp_targets) {
struct target *curr = head->target;
+ if (curr->state == TARGET_UNAVAILABLE)
+ continue;
int retval = hybrid_breakpoint_add_internal(curr, address, asid, length, type);
if (retval != ERROR_OK)
return retval;
@@ -435,7 +436,7 @@ static int watchpoint_add_internal(struct target *target, target_addr_t address,
reason = "resource not available";
goto bye;
case ERROR_TARGET_NOT_HALTED:
- reason = "target running";
+ reason = "target not halted";
goto bye;
default:
reason = "unrecognized error";
@@ -467,6 +468,8 @@ int watchpoint_add(struct target *target, target_addr_t address,
foreach_smp_target(head, target->smp_targets) {
struct target *curr = head->target;
+ if (curr->state == TARGET_UNAVAILABLE)
+ continue;
int retval = watchpoint_add_internal(curr, address, length, rw, value, mask);
if (retval != ERROR_OK)
return retval;
diff --git a/src/target/breakpoints.h b/src/target/breakpoints.h
index a9ae484..8a3fbb2 100644
--- a/src/target/breakpoints.h
+++ b/src/target/breakpoints.h
@@ -45,7 +45,7 @@ struct watchpoint {
bool is_set;
unsigned int number;
struct watchpoint *next;
- int unique_id;
+ uint32_t unique_id;
};
void breakpoint_clear_target(struct target *target);
diff --git a/src/target/dsp563xx.c b/src/target/dsp563xx.c
index 36ee853..9db54fb 100644
--- a/src/target/dsp563xx.c
+++ b/src/target/dsp563xx.c
@@ -2146,7 +2146,8 @@ COMMAND_HANDLER(dsp563xx_mem_command)
err = dsp563xx_read_memory(target, mem_type, address, sizeof(uint32_t),
count, buffer);
if (err == ERROR_OK)
- target_handle_md_output(CMD, target, address, sizeof(uint32_t), count, buffer);
+ target_handle_md_output(CMD, target, address, sizeof(uint32_t),
+ count, buffer, true);
} else {
b = buffer;
diff --git a/src/target/riscv/batch.c b/src/target/riscv/batch.c
index 8ec043e..10ad388 100644
--- a/src/target/riscv/batch.c
+++ b/src/target/riscv/batch.c
@@ -115,8 +115,10 @@ int riscv_batch_run(struct riscv_batch *batch)
if (bscan_tunnel_ir_width != 0) {
/* need to right-shift "in" by one bit, because of clock skew between BSCAN TAP and DM TAP */
- for (size_t i = 0; i < batch->used_scans; ++i)
- buffer_shr((batch->fields + i)->in_value, DMI_SCAN_BUF_SIZE, 1);
+ for (size_t i = 0; i < batch->used_scans; ++i) {
+ if ((batch->fields + i)->in_value)
+ buffer_shr((batch->fields + i)->in_value, DMI_SCAN_BUF_SIZE, 1);
+ }
}
for (size_t i = 0; i < batch->used_scans; ++i)
@@ -125,15 +127,20 @@ int riscv_batch_run(struct riscv_batch *batch)
return ERROR_OK;
}
-void riscv_batch_add_dmi_write(struct riscv_batch *batch, unsigned address, uint64_t data)
+void riscv_batch_add_dmi_write(struct riscv_batch *batch, unsigned address, uint64_t data,
+ bool read_back)
{
assert(batch->used_scans < batch->allocated_scans);
struct scan_field *field = batch->fields + batch->used_scans;
field->num_bits = riscv_dmi_write_u64_bits(batch->target);
field->out_value = (void *)(batch->data_out + batch->used_scans * DMI_SCAN_BUF_SIZE);
- field->in_value = (void *)(batch->data_in + batch->used_scans * DMI_SCAN_BUF_SIZE);
riscv_fill_dmi_write_u64(batch->target, (char *)field->out_value, address, data);
- riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value);
+ if (read_back) {
+ field->in_value = (void *)(batch->data_in + batch->used_scans * DMI_SCAN_BUF_SIZE);
+ riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value);
+ } else {
+ field->in_value = NULL;
+ }
batch->last_scan = RISCV_SCAN_TYPE_WRITE;
batch->used_scans++;
}
diff --git a/src/target/riscv/batch.h b/src/target/riscv/batch.h
index 9c42ba8..35384b5 100644
--- a/src/target/riscv/batch.h
+++ b/src/target/riscv/batch.h
@@ -59,7 +59,8 @@ bool riscv_batch_full(struct riscv_batch *batch);
int riscv_batch_run(struct riscv_batch *batch);
/* Adds a DMI write to this batch. */
-void riscv_batch_add_dmi_write(struct riscv_batch *batch, unsigned address, uint64_t data);
+void riscv_batch_add_dmi_write(struct riscv_batch *batch, unsigned address, uint64_t data,
+ bool read_back);
/* DMI reads must be handled in two parts: the first one schedules a read and
* provides a key, the second one actually obtains the result of the read -
diff --git a/src/target/riscv/encoding.h b/src/target/riscv/encoding.h
index c2da4e6..3ac537c 100644
--- a/src/target/riscv/encoding.h
+++ b/src/target/riscv/encoding.h
@@ -1,10 +1,10 @@
/* SPDX-License-Identifier: BSD-3-Clause */
-/* Copyright (c) 2022 RISC-V International */
+/* Copyright (c) 2023 RISC-V International */
/*
* This file is auto-generated by running 'make' in
- * https://github.com/riscv/riscv-opcodes (dcdf8d3)
+ * https://github.com/riscv/riscv-opcodes (ed68c21)
*/
#ifndef RISCV_CSR_ENCODING_H
@@ -156,14 +156,17 @@
#define MENVCFG_CBIE 0x00000030
#define MENVCFG_CBCFE 0x00000040
#define MENVCFG_CBZE 0x00000080
+#define MENVCFG_HADE 0x2000000000000000
#define MENVCFG_PBMTE 0x4000000000000000
#define MENVCFG_STCE 0x8000000000000000
+#define MENVCFGH_HADE 0x20000000
#define MENVCFGH_PBMTE 0x40000000
#define MENVCFGH_STCE 0x80000000
#define MSTATEEN0_CS 0x00000001
#define MSTATEEN0_FCSR 0x00000002
+#define MSTATEEN0_JVT 0x00000004
#define MSTATEEN0_HCONTEXT 0x0200000000000000
#define MSTATEEN0_HENVCFG 0x4000000000000000
#define MSTATEEN_HSTATEEN 0x8000000000000000
@@ -190,14 +193,17 @@
#define HENVCFG_CBIE 0x00000030
#define HENVCFG_CBCFE 0x00000040
#define HENVCFG_CBZE 0x00000080
+#define HENVCFG_HADE 0x2000000000000000
#define HENVCFG_PBMTE 0x4000000000000000
#define HENVCFG_STCE 0x8000000000000000
+#define HENVCFGH_HADE 0x20000000
#define HENVCFGH_PBMTE 0x40000000
#define HENVCFGH_STCE 0x80000000
#define HSTATEEN0_CS 0x00000001
#define HSTATEEN0_FCSR 0x00000002
+#define HSTATEEN0_JVT 0x00000004
#define HSTATEEN0_SCONTEXT 0x0200000000000000
#define HSTATEEN0_SENVCFG 0x4000000000000000
#define HSTATEEN_SSTATEEN 0x8000000000000000
@@ -213,6 +219,7 @@
#define SSTATEEN0_CS 0x00000001
#define SSTATEEN0_FCSR 0x00000002
+#define SSTATEEN0_JVT 0x00000004
#define MSECCFG_MML 0x00000001
#define MSECCFG_MMWP 0x00000002
@@ -220,6 +227,10 @@
#define MSECCFG_USEED 0x00000100
#define MSECCFG_SSEED 0x00000200
+/* jvt fields */
+#define JVT_MODE 0x3F
+#define JVT_BASE (~0x3F)
+
#define PRV_U 0
#define PRV_S 1
#define PRV_M 3
@@ -366,12 +377,8 @@
#define MASK_ADD8 0xfe00707f
#define MATCH_ADD_UW 0x800003b
#define MASK_ADD_UW 0xfe00707f
-#define MATCH_ADDD 0x7b
-#define MASK_ADDD 0xfe00707f
#define MATCH_ADDI 0x13
#define MASK_ADDI 0x707f
-#define MATCH_ADDID 0x5b
-#define MASK_ADDID 0x707f
#define MATCH_ADDIW 0x1b
#define MASK_ADDIW 0x707f
#define MATCH_ADDW 0x3b
@@ -474,10 +481,6 @@
#define MASK_BINV 0xfe00707f
#define MATCH_BINVI 0x68001013
#define MASK_BINVI 0xfc00707f
-#define MATCH_BITREV 0xe6000077
-#define MASK_BITREV 0xfe00707f
-#define MATCH_BITREVI 0xe8000077
-#define MASK_BITREVI 0xfc00707f
#define MATCH_BLT 0x4063
#define MASK_BLT 0x707f
#define MATCH_BLTU 0x6063
@@ -490,8 +493,6 @@
#define MASK_BMATXOR 0xfe00707f
#define MATCH_BNE 0x1063
#define MASK_BNE 0x707f
-#define MATCH_BPICK 0x3077
-#define MASK_BPICK 0x600707f
#define MATCH_BSET 0x28001033
#define MASK_BSET 0xfe00707f
#define MATCH_BSETI 0x28001013
@@ -542,38 +543,48 @@
#define MASK_C_JALR 0xf07f
#define MATCH_C_JR 0x8002
#define MASK_C_JR 0xf07f
+#define MATCH_C_LBU 0x8000
+#define MASK_C_LBU 0xfc03
#define MATCH_C_LD 0x6000
#define MASK_C_LD 0xe003
#define MATCH_C_LDSP 0x6002
#define MASK_C_LDSP 0xe003
+#define MATCH_C_LH 0x8440
+#define MASK_C_LH 0xfc43
+#define MATCH_C_LHU 0x8400
+#define MASK_C_LHU 0xfc43
#define MATCH_C_LI 0x4001
#define MASK_C_LI 0xe003
-#define MATCH_C_LQ 0x2000
-#define MASK_C_LQ 0xe003
-#define MATCH_C_LQSP 0x2002
-#define MASK_C_LQSP 0xe003
#define MATCH_C_LUI 0x6001
#define MASK_C_LUI 0xe003
#define MATCH_C_LW 0x4000
#define MASK_C_LW 0xe003
#define MATCH_C_LWSP 0x4002
#define MASK_C_LWSP 0xe003
+#define MATCH_C_MUL 0x9c41
+#define MASK_C_MUL 0xfc63
#define MATCH_C_MV 0x8002
#define MASK_C_MV 0xf003
#define MATCH_C_NOP 0x1
#define MASK_C_NOP 0xef83
+#define MATCH_C_NOT 0x9c75
+#define MASK_C_NOT 0xfc7f
#define MATCH_C_OR 0x8c41
#define MASK_C_OR 0xfc63
+#define MATCH_C_SB 0x8800
+#define MASK_C_SB 0xfc03
#define MATCH_C_SD 0xe000
#define MASK_C_SD 0xe003
#define MATCH_C_SDSP 0xe002
#define MASK_C_SDSP 0xe003
+#define MATCH_C_SEXT_B 0x9c65
+#define MASK_C_SEXT_B 0xfc7f
+#define MATCH_C_SEXT_H 0x9c6d
+#define MASK_C_SEXT_H 0xfc7f
+#define MATCH_C_SH 0x8c00
+#define MASK_C_SH 0xfc43
#define MATCH_C_SLLI 0x2
#define MASK_C_SLLI 0xe003
-#define MATCH_C_SQ 0xa000
-#define MASK_C_SQ 0xe003
-#define MATCH_C_SQSP 0xa002
-#define MASK_C_SQSP 0xe003
#define MATCH_C_SRAI 0x8401
#define MASK_C_SRAI 0xec03
#define MATCH_C_SRLI 0x8001
@@ -588,6 +599,12 @@
#define MASK_C_SWSP 0xe003
#define MATCH_C_XOR 0x8c21
#define MASK_C_XOR 0xfc63
+#define MATCH_C_ZEXT_B 0x9c61
+#define MASK_C_ZEXT_B 0xfc7f
+#define MATCH_C_ZEXT_H 0x9c69
+#define MASK_C_ZEXT_H 0xfc7f
+#define MATCH_C_ZEXT_W 0x9c71
+#define MASK_C_ZEXT_W 0xfc7f
#define MATCH_CBO_CLEAN 0x10200f
#define MASK_CBO_CLEAN 0xfff07fff
#define MATCH_CBO_FLUSH 0x20200f
@@ -602,12 +619,6 @@
#define MASK_CLMULH 0xfe00707f
#define MATCH_CLMULR 0xa002033
#define MASK_CLMULR 0xfe00707f
-#define MATCH_CLO16 0xaeb00077
-#define MASK_CLO16 0xfff0707f
-#define MATCH_CLO32 0xafb00077
-#define MASK_CLO32 0xfff0707f
-#define MATCH_CLO8 0xae300077
-#define MASK_CLO8 0xfff0707f
#define MATCH_CLRS16 0xae800077
#define MASK_CLRS16 0xfff0707f
#define MATCH_CLRS32 0xaf800077
@@ -624,6 +635,20 @@
#define MASK_CLZ8 0xfff0707f
#define MATCH_CLZW 0x6000101b
#define MASK_CLZW 0xfff0707f
+#define MATCH_CM_JALT 0xa002
+#define MASK_CM_JALT 0xfc03
+#define MATCH_CM_MVA01S 0xac62
+#define MASK_CM_MVA01S 0xfc63
+#define MATCH_CM_MVSA01 0xac22
+#define MASK_CM_MVSA01 0xfc63
+#define MATCH_CM_POP 0xba02
+#define MASK_CM_POP 0xff03
+#define MATCH_CM_POPRET 0xbe02
+#define MASK_CM_POPRET 0xff03
+#define MATCH_CM_POPRETZ 0xbc02
+#define MASK_CM_POPRETZ 0xff03
+#define MATCH_CM_PUSH 0xb802
+#define MASK_CM_PUSH 0xff03
#define MATCH_CMIX 0x6001033
#define MASK_CMIX 0x600707f
#define MATCH_CMOV 0x6005033
@@ -676,6 +701,10 @@
#define MASK_CTZ 0xfff0707f
#define MATCH_CTZW 0x6010101b
#define MASK_CTZW 0xfff0707f
+#define MATCH_CZERO_EQZ 0xe005033
+#define MASK_CZERO_EQZ 0xfe00707f
+#define MATCH_CZERO_NEZ 0xe007033
+#define MASK_CZERO_NEZ 0xfe00707f
#define MATCH_DIV 0x2004033
#define MASK_DIV 0xfe00707f
#define MATCH_DIVU 0x2005033
@@ -1238,14 +1267,10 @@
#define MASK_LBU 0x707f
#define MATCH_LD 0x3003
#define MASK_LD 0x707f
-#define MATCH_LDU 0x7003
-#define MASK_LDU 0x707f
#define MATCH_LH 0x1003
#define MASK_LH 0x707f
#define MATCH_LHU 0x5003
#define MASK_LHU 0x707f
-#define MATCH_LQ 0x300f
-#define MASK_LQ 0x707f
#define MATCH_LR_D 0x1000302f
#define MASK_LR_D 0xf9f0707f
#define MATCH_LR_W 0x1000202f
@@ -1262,14 +1287,10 @@
#define MASK_MAX 0xfe00707f
#define MATCH_MAXU 0xa007033
#define MASK_MAXU 0xfe00707f
-#define MATCH_MAXW 0xf2000077
-#define MASK_MAXW 0xfe00707f
#define MATCH_MIN 0xa004033
#define MASK_MIN 0xfe00707f
#define MATCH_MINU 0xa005033
#define MASK_MINU 0xfe00707f
-#define MATCH_MINW 0xf0000077
-#define MASK_MINW 0xfe00707f
#define MATCH_MRET 0x30200073
#define MASK_MRET 0xffffffff
#define MATCH_MSUBR32 0xc6001077
@@ -1312,8 +1333,6 @@
#define MASK_PBSADA 0xfe00707f
#define MATCH_PKBB16 0xe001077
#define MASK_PKBB16 0xfe00707f
-#define MATCH_PKBB32 0xe002077
-#define MASK_PKBB32 0xfe00707f
#define MATCH_PKBT16 0x1e001077
#define MASK_PKBT16 0xfe00707f
#define MATCH_PKBT32 0x1e002077
@@ -1324,8 +1343,6 @@
#define MASK_PKTB32 0xfe00707f
#define MATCH_PKTT16 0x2e001077
#define MASK_PKTT16 0xfe00707f
-#define MATCH_PKTT32 0x2e002077
-#define MASK_PKTT32 0xfe00707f
#define MATCH_PREFETCH_I 0x6013
#define MASK_PREFETCH_I 0x1f07fff
#define MATCH_PREFETCH_R 0x106013
@@ -1478,20 +1495,18 @@
#define MASK_SLL32 0xfe00707f
#define MATCH_SLL8 0x5c000077
#define MASK_SLL8 0xfe00707f
-#define MATCH_SLLD 0x107b
-#define MASK_SLLD 0xfe00707f
#define MATCH_SLLI 0x1013
-#define MASK_SLLI 0xf800707f
+#define MASK_SLLI 0xfc00707f
#define MATCH_SLLI16 0x74000077
#define MASK_SLLI16 0xff00707f
#define MATCH_SLLI32 0x74002077
#define MASK_SLLI32 0xfe00707f
#define MATCH_SLLI8 0x7c000077
#define MASK_SLLI8 0xff80707f
+#define MATCH_SLLI_RV32 0x1013
+#define MASK_SLLI_RV32 0xfe00707f
#define MATCH_SLLI_UW 0x800101b
#define MASK_SLLI_UW 0xfc00707f
-#define MATCH_SLLID 0x105b
-#define MASK_SLLID 0xfc00707f
#define MATCH_SLLIW 0x101b
#define MASK_SLLIW 0xfe00707f
#define MATCH_SLLW 0x103b
@@ -1604,8 +1619,6 @@
#define MASK_SMXDS 0xfe00707f
#define MATCH_SMXDS32 0x78002077
#define MASK_SMXDS32 0xfe00707f
-#define MATCH_SQ 0x4023
-#define MASK_SQ 0x707f
#define MATCH_SRA 0x40005033
#define MASK_SRA 0xfe00707f
#define MATCH_SRA16 0x50000077
@@ -1622,10 +1635,8 @@
#define MASK_SRA8_U 0xfe00707f
#define MATCH_SRA_U 0x24001077
#define MASK_SRA_U 0xfe00707f
-#define MATCH_SRAD 0x4000507b
-#define MASK_SRAD 0xfe00707f
#define MATCH_SRAI 0x40005013
-#define MASK_SRAI 0xf800707f
+#define MASK_SRAI 0xfc00707f
#define MATCH_SRAI16 0x70000077
#define MASK_SRAI16 0xff00707f
#define MATCH_SRAI16_U 0x71000077
@@ -1638,10 +1649,10 @@
#define MASK_SRAI8 0xff80707f
#define MATCH_SRAI8_U 0x78800077
#define MASK_SRAI8_U 0xff80707f
+#define MATCH_SRAI_RV32 0x40005013
+#define MASK_SRAI_RV32 0xfe00707f
#define MATCH_SRAI_U 0xd4001077
#define MASK_SRAI_U 0xfc00707f
-#define MATCH_SRAID 0x4000505b
-#define MASK_SRAID 0xfc00707f
#define MATCH_SRAIW 0x4000501b
#define MASK_SRAIW 0xfe00707f
#define MATCH_SRAIW_U 0x34001077
@@ -1664,10 +1675,8 @@
#define MASK_SRL8 0xfe00707f
#define MATCH_SRL8_U 0x6a000077
#define MASK_SRL8_U 0xfe00707f
-#define MATCH_SRLD 0x507b
-#define MASK_SRLD 0xfe00707f
#define MATCH_SRLI 0x5013
-#define MASK_SRLI 0xf800707f
+#define MASK_SRLI 0xfc00707f
#define MATCH_SRLI16 0x72000077
#define MASK_SRLI16 0xff00707f
#define MATCH_SRLI16_U 0x73000077
@@ -1680,8 +1689,8 @@
#define MASK_SRLI8 0xff80707f
#define MATCH_SRLI8_U 0x7a800077
#define MASK_SRLI8_U 0xff80707f
-#define MATCH_SRLID 0x505b
-#define MASK_SRLID 0xfc00707f
+#define MATCH_SRLI_RV32 0x5013
+#define MASK_SRLI_RV32 0xfe00707f
#define MATCH_SRLIW 0x501b
#define MASK_SRLIW 0xfe00707f
#define MATCH_SRLW 0x503b
@@ -1712,8 +1721,6 @@
#define MASK_SUB64 0xfe00707f
#define MATCH_SUB8 0x4a000077
#define MASK_SUB8 0xfe00707f
-#define MATCH_SUBD 0x4000007b
-#define MASK_SUBD 0xfe00707f
#define MATCH_SUBW 0x4000003b
#define MASK_SUBW 0xfe00707f
#define MATCH_SUNPKD810 0xac800077
@@ -1728,8 +1735,6 @@
#define MASK_SUNPKD832 0xfff0707f
#define MATCH_SW 0x2023
#define MASK_SW 0x707f
-#define MATCH_SWAP8 0xad800077
-#define MASK_SWAP8 0xfff0707f
#define MATCH_UCLIP16 0x85000077
#define MASK_UCLIP16 0xff00707f
#define MATCH_UCLIP32 0xf4000077
@@ -2750,10 +2755,6 @@
#define MASK_VZEXT_VF4 0xfc0ff07f
#define MATCH_VZEXT_VF8 0x48012057
#define MASK_VZEXT_VF8 0xfc0ff07f
-#define MATCH_WEXT 0xce000077
-#define MASK_WEXT 0xfe00707f
-#define MATCH_WEXTI 0xde000077
-#define MASK_WEXTI 0xfe00707f
#define MATCH_WFI 0x10500073
#define MASK_WFI 0xffffffff
#define MATCH_WRS_NTO 0xd00073
@@ -2793,6 +2794,7 @@
#define CSR_VXRM 0xa
#define CSR_VCSR 0xf
#define CSR_SEED 0x15
+#define CSR_JVT 0x17
#define CSR_CYCLE 0xc00
#define CSR_TIME 0xc01
#define CSR_INSTRET 0xc02
@@ -2845,6 +2847,9 @@
#define CSR_STVAL 0x143
#define CSR_SIP 0x144
#define CSR_STIMECMP 0x14d
+#define CSR_SISELECT 0x150
+#define CSR_SIREG 0x151
+#define CSR_STOPEI 0x15c
#define CSR_SATP 0x180
#define CSR_SCONTEXT 0x5a8
#define CSR_VSSTATUS 0x200
@@ -2856,6 +2861,9 @@
#define CSR_VSTVAL 0x243
#define CSR_VSIP 0x244
#define CSR_VSTIMECMP 0x24d
+#define CSR_VSISELECT 0x250
+#define CSR_VSIREG 0x251
+#define CSR_VSTOPEI 0x25c
#define CSR_VSATP 0x280
#define CSR_HSTATUS 0x600
#define CSR_HEDELEG 0x602
@@ -2864,6 +2872,8 @@
#define CSR_HTIMEDELTA 0x605
#define CSR_HCOUNTEREN 0x606
#define CSR_HGEIE 0x607
+#define CSR_HVIEN 0x608
+#define CSR_HVICTL 0x609
#define CSR_HENVCFG 0x60a
#define CSR_HSTATEEN0 0x60c
#define CSR_HSTATEEN1 0x60d
@@ -2872,11 +2882,15 @@
#define CSR_HTVAL 0x643
#define CSR_HIP 0x644
#define CSR_HVIP 0x645
+#define CSR_HVIPRIO1 0x646
+#define CSR_HVIPRIO2 0x647
#define CSR_HTINST 0x64a
#define CSR_HGATP 0x680
#define CSR_HCONTEXT 0x6a8
#define CSR_HGEIP 0xe12
+#define CSR_VSTOPI 0xeb0
#define CSR_SCOUNTOVF 0xda0
+#define CSR_STOPI 0xdb0
#define CSR_UTVT 0x7
#define CSR_UNXTI 0x45
#define CSR_UINTSTATUS 0x46
@@ -2899,6 +2913,8 @@
#define CSR_MIE 0x304
#define CSR_MTVEC 0x305
#define CSR_MCOUNTEREN 0x306
+#define CSR_MVIEN 0x308
+#define CSR_MVIP 0x309
#define CSR_MENVCFG 0x30a
#define CSR_MSTATEEN0 0x30c
#define CSR_MSTATEEN1 0x30d
@@ -2912,6 +2928,9 @@
#define CSR_MIP 0x344
#define CSR_MTINST 0x34a
#define CSR_MTVAL2 0x34b
+#define CSR_MISELECT 0x350
+#define CSR_MIREG 0x351
+#define CSR_MTOPEI 0x35c
#define CSR_PMPCFG0 0x3a0
#define CSR_PMPCFG1 0x3a1
#define CSR_PMPCFG2 0x3a2
@@ -3070,10 +3089,20 @@
#define CSR_MIMPID 0xf13
#define CSR_MHARTID 0xf14
#define CSR_MCONFIGPTR 0xf15
+#define CSR_MTOPI 0xfb0
+#define CSR_SIEH 0x114
+#define CSR_SIPH 0x154
#define CSR_STIMECMPH 0x15d
+#define CSR_VSIEH 0x214
+#define CSR_VSIPH 0x254
#define CSR_VSTIMECMPH 0x25d
#define CSR_HTIMEDELTAH 0x615
+#define CSR_HIDELEGH 0x613
+#define CSR_HVIENH 0x618
#define CSR_HENVCFGH 0x61a
+#define CSR_HVIPH 0x655
+#define CSR_HVIPRIO1H 0x656
+#define CSR_HVIPRIO2H 0x657
#define CSR_HSTATEEN0H 0x61c
#define CSR_HSTATEEN1H 0x61d
#define CSR_HSTATEEN2H 0x61e
@@ -3111,11 +3140,16 @@
#define CSR_HPMCOUNTER30H 0xc9e
#define CSR_HPMCOUNTER31H 0xc9f
#define CSR_MSTATUSH 0x310
+#define CSR_MIDELEGH 0x313
+#define CSR_MIEH 0x314
+#define CSR_MVIENH 0x318
+#define CSR_MVIPH 0x319
#define CSR_MENVCFGH 0x31a
#define CSR_MSTATEEN0H 0x31c
#define CSR_MSTATEEN1H 0x31d
#define CSR_MSTATEEN2H 0x31e
#define CSR_MSTATEEN3H 0x31f
+#define CSR_MIPH 0x354
#define CSR_MHPMEVENT3H 0x723
#define CSR_MHPMEVENT4H 0x724
#define CSR_MHPMEVENT5H 0x725
@@ -3221,7 +3255,7 @@
#define INSN_FIELD_IMM12LO 0xf80
#define INSN_FIELD_BIMM12LO 0xf80
#define INSN_FIELD_ZIMM 0xf8000
-#define INSN_FIELD_SHAMT 0x7f00000
+#define INSN_FIELD_SHAMTQ 0x7f00000
#define INSN_FIELD_SHAMTW 0x1f00000
#define INSN_FIELD_SHAMTW4 0xf00000
#define INSN_FIELD_SHAMTD 0x3f00000
@@ -3276,6 +3310,11 @@
#define INSN_FIELD_C_UIMM9SPHI 0x1000
#define INSN_FIELD_C_UIMM10SP_S 0x1f80
#define INSN_FIELD_C_UIMM9SP_S 0x1f80
+#define INSN_FIELD_C_UIMM2 0x60
+#define INSN_FIELD_C_UIMM1 0x20
+#define INSN_FIELD_C_RLIST 0xf0
+#define INSN_FIELD_C_SPIMM 0xc
+#define INSN_FIELD_C_INDEX 0x3fc
#define INSN_FIELD_RS1_P 0x380
#define INSN_FIELD_RS2_P 0x1c
#define INSN_FIELD_RD_P 0x1c
@@ -3288,6 +3327,8 @@
#define INSN_FIELD_C_RS2_N0 0x7c
#define INSN_FIELD_C_RS1_N0 0xf80
#define INSN_FIELD_C_RS2 0x7c
+#define INSN_FIELD_C_SREG1 0x380
+#define INSN_FIELD_C_SREG2 0x1c
#endif
#ifdef DECLARE_INSN
DECLARE_INSN(add, MATCH_ADD, MASK_ADD)
@@ -3296,9 +3337,7 @@ DECLARE_INSN(add32, MATCH_ADD32, MASK_ADD32)
DECLARE_INSN(add64, MATCH_ADD64, MASK_ADD64)
DECLARE_INSN(add8, MATCH_ADD8, MASK_ADD8)
DECLARE_INSN(add_uw, MATCH_ADD_UW, MASK_ADD_UW)
-DECLARE_INSN(addd, MATCH_ADDD, MASK_ADDD)
DECLARE_INSN(addi, MATCH_ADDI, MASK_ADDI)
-DECLARE_INSN(addid, MATCH_ADDID, MASK_ADDID)
DECLARE_INSN(addiw, MATCH_ADDIW, MASK_ADDIW)
DECLARE_INSN(addw, MATCH_ADDW, MASK_ADDW)
DECLARE_INSN(aes32dsi, MATCH_AES32DSI, MASK_AES32DSI)
@@ -3350,15 +3389,12 @@ DECLARE_INSN(bge, MATCH_BGE, MASK_BGE)
DECLARE_INSN(bgeu, MATCH_BGEU, MASK_BGEU)
DECLARE_INSN(binv, MATCH_BINV, MASK_BINV)
DECLARE_INSN(binvi, MATCH_BINVI, MASK_BINVI)
-DECLARE_INSN(bitrev, MATCH_BITREV, MASK_BITREV)
-DECLARE_INSN(bitrevi, MATCH_BITREVI, MASK_BITREVI)
DECLARE_INSN(blt, MATCH_BLT, MASK_BLT)
DECLARE_INSN(bltu, MATCH_BLTU, MASK_BLTU)
DECLARE_INSN(bmatflip, MATCH_BMATFLIP, MASK_BMATFLIP)
DECLARE_INSN(bmator, MATCH_BMATOR, MASK_BMATOR)
DECLARE_INSN(bmatxor, MATCH_BMATXOR, MASK_BMATXOR)
DECLARE_INSN(bne, MATCH_BNE, MASK_BNE)
-DECLARE_INSN(bpick, MATCH_BPICK, MASK_BPICK)
DECLARE_INSN(bset, MATCH_BSET, MASK_BSET)
DECLARE_INSN(bseti, MATCH_BSETI, MASK_BSETI)
DECLARE_INSN(c_add, MATCH_C_ADD, MASK_C_ADD)
@@ -3384,22 +3420,27 @@ DECLARE_INSN(c_j, MATCH_C_J, MASK_C_J)
DECLARE_INSN(c_jal, MATCH_C_JAL, MASK_C_JAL)
DECLARE_INSN(c_jalr, MATCH_C_JALR, MASK_C_JALR)
DECLARE_INSN(c_jr, MATCH_C_JR, MASK_C_JR)
+DECLARE_INSN(c_lbu, MATCH_C_LBU, MASK_C_LBU)
DECLARE_INSN(c_ld, MATCH_C_LD, MASK_C_LD)
DECLARE_INSN(c_ldsp, MATCH_C_LDSP, MASK_C_LDSP)
+DECLARE_INSN(c_lh, MATCH_C_LH, MASK_C_LH)
+DECLARE_INSN(c_lhu, MATCH_C_LHU, MASK_C_LHU)
DECLARE_INSN(c_li, MATCH_C_LI, MASK_C_LI)
-DECLARE_INSN(c_lq, MATCH_C_LQ, MASK_C_LQ)
-DECLARE_INSN(c_lqsp, MATCH_C_LQSP, MASK_C_LQSP)
DECLARE_INSN(c_lui, MATCH_C_LUI, MASK_C_LUI)
DECLARE_INSN(c_lw, MATCH_C_LW, MASK_C_LW)
DECLARE_INSN(c_lwsp, MATCH_C_LWSP, MASK_C_LWSP)
+DECLARE_INSN(c_mul, MATCH_C_MUL, MASK_C_MUL)
DECLARE_INSN(c_mv, MATCH_C_MV, MASK_C_MV)
DECLARE_INSN(c_nop, MATCH_C_NOP, MASK_C_NOP)
+DECLARE_INSN(c_not, MATCH_C_NOT, MASK_C_NOT)
DECLARE_INSN(c_or, MATCH_C_OR, MASK_C_OR)
+DECLARE_INSN(c_sb, MATCH_C_SB, MASK_C_SB)
DECLARE_INSN(c_sd, MATCH_C_SD, MASK_C_SD)
DECLARE_INSN(c_sdsp, MATCH_C_SDSP, MASK_C_SDSP)
+DECLARE_INSN(c_sext_b, MATCH_C_SEXT_B, MASK_C_SEXT_B)
+DECLARE_INSN(c_sext_h, MATCH_C_SEXT_H, MASK_C_SEXT_H)
+DECLARE_INSN(c_sh, MATCH_C_SH, MASK_C_SH)
DECLARE_INSN(c_slli, MATCH_C_SLLI, MASK_C_SLLI)
-DECLARE_INSN(c_sq, MATCH_C_SQ, MASK_C_SQ)
-DECLARE_INSN(c_sqsp, MATCH_C_SQSP, MASK_C_SQSP)
DECLARE_INSN(c_srai, MATCH_C_SRAI, MASK_C_SRAI)
DECLARE_INSN(c_srli, MATCH_C_SRLI, MASK_C_SRLI)
DECLARE_INSN(c_sub, MATCH_C_SUB, MASK_C_SUB)
@@ -3407,6 +3448,9 @@ DECLARE_INSN(c_subw, MATCH_C_SUBW, MASK_C_SUBW)
DECLARE_INSN(c_sw, MATCH_C_SW, MASK_C_SW)
DECLARE_INSN(c_swsp, MATCH_C_SWSP, MASK_C_SWSP)
DECLARE_INSN(c_xor, MATCH_C_XOR, MASK_C_XOR)
+DECLARE_INSN(c_zext_b, MATCH_C_ZEXT_B, MASK_C_ZEXT_B)
+DECLARE_INSN(c_zext_h, MATCH_C_ZEXT_H, MASK_C_ZEXT_H)
+DECLARE_INSN(c_zext_w, MATCH_C_ZEXT_W, MASK_C_ZEXT_W)
DECLARE_INSN(cbo_clean, MATCH_CBO_CLEAN, MASK_CBO_CLEAN)
DECLARE_INSN(cbo_flush, MATCH_CBO_FLUSH, MASK_CBO_FLUSH)
DECLARE_INSN(cbo_inval, MATCH_CBO_INVAL, MASK_CBO_INVAL)
@@ -3414,9 +3458,6 @@ DECLARE_INSN(cbo_zero, MATCH_CBO_ZERO, MASK_CBO_ZERO)
DECLARE_INSN(clmul, MATCH_CLMUL, MASK_CLMUL)
DECLARE_INSN(clmulh, MATCH_CLMULH, MASK_CLMULH)
DECLARE_INSN(clmulr, MATCH_CLMULR, MASK_CLMULR)
-DECLARE_INSN(clo16, MATCH_CLO16, MASK_CLO16)
-DECLARE_INSN(clo32, MATCH_CLO32, MASK_CLO32)
-DECLARE_INSN(clo8, MATCH_CLO8, MASK_CLO8)
DECLARE_INSN(clrs16, MATCH_CLRS16, MASK_CLRS16)
DECLARE_INSN(clrs32, MATCH_CLRS32, MASK_CLRS32)
DECLARE_INSN(clrs8, MATCH_CLRS8, MASK_CLRS8)
@@ -3425,6 +3466,13 @@ DECLARE_INSN(clz16, MATCH_CLZ16, MASK_CLZ16)
DECLARE_INSN(clz32, MATCH_CLZ32, MASK_CLZ32)
DECLARE_INSN(clz8, MATCH_CLZ8, MASK_CLZ8)
DECLARE_INSN(clzw, MATCH_CLZW, MASK_CLZW)
+DECLARE_INSN(cm_jalt, MATCH_CM_JALT, MASK_CM_JALT)
+DECLARE_INSN(cm_mva01s, MATCH_CM_MVA01S, MASK_CM_MVA01S)
+DECLARE_INSN(cm_mvsa01, MATCH_CM_MVSA01, MASK_CM_MVSA01)
+DECLARE_INSN(cm_pop, MATCH_CM_POP, MASK_CM_POP)
+DECLARE_INSN(cm_popret, MATCH_CM_POPRET, MASK_CM_POPRET)
+DECLARE_INSN(cm_popretz, MATCH_CM_POPRETZ, MASK_CM_POPRETZ)
+DECLARE_INSN(cm_push, MATCH_CM_PUSH, MASK_CM_PUSH)
DECLARE_INSN(cmix, MATCH_CMIX, MASK_CMIX)
DECLARE_INSN(cmov, MATCH_CMOV, MASK_CMOV)
DECLARE_INSN(cmpeq16, MATCH_CMPEQ16, MASK_CMPEQ16)
@@ -3451,6 +3499,8 @@ DECLARE_INSN(csrrw, MATCH_CSRRW, MASK_CSRRW)
DECLARE_INSN(csrrwi, MATCH_CSRRWI, MASK_CSRRWI)
DECLARE_INSN(ctz, MATCH_CTZ, MASK_CTZ)
DECLARE_INSN(ctzw, MATCH_CTZW, MASK_CTZW)
+DECLARE_INSN(czero_eqz, MATCH_CZERO_EQZ, MASK_CZERO_EQZ)
+DECLARE_INSN(czero_nez, MATCH_CZERO_NEZ, MASK_CZERO_NEZ)
DECLARE_INSN(div, MATCH_DIV, MASK_DIV)
DECLARE_INSN(divu, MATCH_DIVU, MASK_DIVU)
DECLARE_INSN(divuw, MATCH_DIVUW, MASK_DIVUW)
@@ -3732,10 +3782,8 @@ DECLARE_INSN(kwmmul_u, MATCH_KWMMUL_U, MASK_KWMMUL_U)
DECLARE_INSN(lb, MATCH_LB, MASK_LB)
DECLARE_INSN(lbu, MATCH_LBU, MASK_LBU)
DECLARE_INSN(ld, MATCH_LD, MASK_LD)
-DECLARE_INSN(ldu, MATCH_LDU, MASK_LDU)
DECLARE_INSN(lh, MATCH_LH, MASK_LH)
DECLARE_INSN(lhu, MATCH_LHU, MASK_LHU)
-DECLARE_INSN(lq, MATCH_LQ, MASK_LQ)
DECLARE_INSN(lr_d, MATCH_LR_D, MASK_LR_D)
DECLARE_INSN(lr_w, MATCH_LR_W, MASK_LR_W)
DECLARE_INSN(lui, MATCH_LUI, MASK_LUI)
@@ -3744,10 +3792,8 @@ DECLARE_INSN(lwu, MATCH_LWU, MASK_LWU)
DECLARE_INSN(maddr32, MATCH_MADDR32, MASK_MADDR32)
DECLARE_INSN(max, MATCH_MAX, MASK_MAX)
DECLARE_INSN(maxu, MATCH_MAXU, MASK_MAXU)
-DECLARE_INSN(maxw, MATCH_MAXW, MASK_MAXW)
DECLARE_INSN(min, MATCH_MIN, MASK_MIN)
DECLARE_INSN(minu, MATCH_MINU, MASK_MINU)
-DECLARE_INSN(minw, MATCH_MINW, MASK_MINW)
DECLARE_INSN(mret, MATCH_MRET, MASK_MRET)
DECLARE_INSN(msubr32, MATCH_MSUBR32, MASK_MSUBR32)
DECLARE_INSN(mul, MATCH_MUL, MASK_MUL)
@@ -3769,13 +3815,11 @@ DECLARE_INSN(pause, MATCH_PAUSE, MASK_PAUSE)
DECLARE_INSN(pbsad, MATCH_PBSAD, MASK_PBSAD)
DECLARE_INSN(pbsada, MATCH_PBSADA, MASK_PBSADA)
DECLARE_INSN(pkbb16, MATCH_PKBB16, MASK_PKBB16)
-DECLARE_INSN(pkbb32, MATCH_PKBB32, MASK_PKBB32)
DECLARE_INSN(pkbt16, MATCH_PKBT16, MASK_PKBT16)
DECLARE_INSN(pkbt32, MATCH_PKBT32, MASK_PKBT32)
DECLARE_INSN(pktb16, MATCH_PKTB16, MASK_PKTB16)
DECLARE_INSN(pktb32, MATCH_PKTB32, MASK_PKTB32)
DECLARE_INSN(pktt16, MATCH_PKTT16, MASK_PKTT16)
-DECLARE_INSN(pktt32, MATCH_PKTT32, MASK_PKTT32)
DECLARE_INSN(prefetch_i, MATCH_PREFETCH_I, MASK_PREFETCH_I)
DECLARE_INSN(prefetch_r, MATCH_PREFETCH_R, MASK_PREFETCH_R)
DECLARE_INSN(prefetch_w, MATCH_PREFETCH_W, MASK_PREFETCH_W)
@@ -3852,13 +3896,12 @@ DECLARE_INSN(sll, MATCH_SLL, MASK_SLL)
DECLARE_INSN(sll16, MATCH_SLL16, MASK_SLL16)
DECLARE_INSN(sll32, MATCH_SLL32, MASK_SLL32)
DECLARE_INSN(sll8, MATCH_SLL8, MASK_SLL8)
-DECLARE_INSN(slld, MATCH_SLLD, MASK_SLLD)
DECLARE_INSN(slli, MATCH_SLLI, MASK_SLLI)
DECLARE_INSN(slli16, MATCH_SLLI16, MASK_SLLI16)
DECLARE_INSN(slli32, MATCH_SLLI32, MASK_SLLI32)
DECLARE_INSN(slli8, MATCH_SLLI8, MASK_SLLI8)
+DECLARE_INSN(slli_rv32, MATCH_SLLI_RV32, MASK_SLLI_RV32)
DECLARE_INSN(slli_uw, MATCH_SLLI_UW, MASK_SLLI_UW)
-DECLARE_INSN(sllid, MATCH_SLLID, MASK_SLLID)
DECLARE_INSN(slliw, MATCH_SLLIW, MASK_SLLIW)
DECLARE_INSN(sllw, MATCH_SLLW, MASK_SLLW)
DECLARE_INSN(slo, MATCH_SLO, MASK_SLO)
@@ -3915,7 +3958,6 @@ DECLARE_INSN(smulx16, MATCH_SMULX16, MASK_SMULX16)
DECLARE_INSN(smulx8, MATCH_SMULX8, MASK_SMULX8)
DECLARE_INSN(smxds, MATCH_SMXDS, MASK_SMXDS)
DECLARE_INSN(smxds32, MATCH_SMXDS32, MASK_SMXDS32)
-DECLARE_INSN(sq, MATCH_SQ, MASK_SQ)
DECLARE_INSN(sra, MATCH_SRA, MASK_SRA)
DECLARE_INSN(sra16, MATCH_SRA16, MASK_SRA16)
DECLARE_INSN(sra16_u, MATCH_SRA16_U, MASK_SRA16_U)
@@ -3924,7 +3966,6 @@ DECLARE_INSN(sra32_u, MATCH_SRA32_U, MASK_SRA32_U)
DECLARE_INSN(sra8, MATCH_SRA8, MASK_SRA8)
DECLARE_INSN(sra8_u, MATCH_SRA8_U, MASK_SRA8_U)
DECLARE_INSN(sra_u, MATCH_SRA_U, MASK_SRA_U)
-DECLARE_INSN(srad, MATCH_SRAD, MASK_SRAD)
DECLARE_INSN(srai, MATCH_SRAI, MASK_SRAI)
DECLARE_INSN(srai16, MATCH_SRAI16, MASK_SRAI16)
DECLARE_INSN(srai16_u, MATCH_SRAI16_U, MASK_SRAI16_U)
@@ -3932,8 +3973,8 @@ DECLARE_INSN(srai32, MATCH_SRAI32, MASK_SRAI32)
DECLARE_INSN(srai32_u, MATCH_SRAI32_U, MASK_SRAI32_U)
DECLARE_INSN(srai8, MATCH_SRAI8, MASK_SRAI8)
DECLARE_INSN(srai8_u, MATCH_SRAI8_U, MASK_SRAI8_U)
+DECLARE_INSN(srai_rv32, MATCH_SRAI_RV32, MASK_SRAI_RV32)
DECLARE_INSN(srai_u, MATCH_SRAI_U, MASK_SRAI_U)
-DECLARE_INSN(sraid, MATCH_SRAID, MASK_SRAID)
DECLARE_INSN(sraiw, MATCH_SRAIW, MASK_SRAIW)
DECLARE_INSN(sraiw_u, MATCH_SRAIW_U, MASK_SRAIW_U)
DECLARE_INSN(sraw, MATCH_SRAW, MASK_SRAW)
@@ -3945,7 +3986,6 @@ DECLARE_INSN(srl32, MATCH_SRL32, MASK_SRL32)
DECLARE_INSN(srl32_u, MATCH_SRL32_U, MASK_SRL32_U)
DECLARE_INSN(srl8, MATCH_SRL8, MASK_SRL8)
DECLARE_INSN(srl8_u, MATCH_SRL8_U, MASK_SRL8_U)
-DECLARE_INSN(srld, MATCH_SRLD, MASK_SRLD)
DECLARE_INSN(srli, MATCH_SRLI, MASK_SRLI)
DECLARE_INSN(srli16, MATCH_SRLI16, MASK_SRLI16)
DECLARE_INSN(srli16_u, MATCH_SRLI16_U, MASK_SRLI16_U)
@@ -3953,7 +3993,7 @@ DECLARE_INSN(srli32, MATCH_SRLI32, MASK_SRLI32)
DECLARE_INSN(srli32_u, MATCH_SRLI32_U, MASK_SRLI32_U)
DECLARE_INSN(srli8, MATCH_SRLI8, MASK_SRLI8)
DECLARE_INSN(srli8_u, MATCH_SRLI8_U, MASK_SRLI8_U)
-DECLARE_INSN(srlid, MATCH_SRLID, MASK_SRLID)
+DECLARE_INSN(srli_rv32, MATCH_SRLI_RV32, MASK_SRLI_RV32)
DECLARE_INSN(srliw, MATCH_SRLIW, MASK_SRLIW)
DECLARE_INSN(srlw, MATCH_SRLW, MASK_SRLW)
DECLARE_INSN(sro, MATCH_SRO, MASK_SRO)
@@ -3969,7 +4009,6 @@ DECLARE_INSN(sub16, MATCH_SUB16, MASK_SUB16)
DECLARE_INSN(sub32, MATCH_SUB32, MASK_SUB32)
DECLARE_INSN(sub64, MATCH_SUB64, MASK_SUB64)
DECLARE_INSN(sub8, MATCH_SUB8, MASK_SUB8)
-DECLARE_INSN(subd, MATCH_SUBD, MASK_SUBD)
DECLARE_INSN(subw, MATCH_SUBW, MASK_SUBW)
DECLARE_INSN(sunpkd810, MATCH_SUNPKD810, MASK_SUNPKD810)
DECLARE_INSN(sunpkd820, MATCH_SUNPKD820, MASK_SUNPKD820)
@@ -3977,7 +4016,6 @@ DECLARE_INSN(sunpkd830, MATCH_SUNPKD830, MASK_SUNPKD830)
DECLARE_INSN(sunpkd831, MATCH_SUNPKD831, MASK_SUNPKD831)
DECLARE_INSN(sunpkd832, MATCH_SUNPKD832, MASK_SUNPKD832)
DECLARE_INSN(sw, MATCH_SW, MASK_SW)
-DECLARE_INSN(swap8, MATCH_SWAP8, MASK_SWAP8)
DECLARE_INSN(uclip16, MATCH_UCLIP16, MASK_UCLIP16)
DECLARE_INSN(uclip32, MATCH_UCLIP32, MASK_UCLIP32)
DECLARE_INSN(uclip8, MATCH_UCLIP8, MASK_UCLIP8)
@@ -4488,8 +4526,6 @@ DECLARE_INSN(vxor_vx, MATCH_VXOR_VX, MASK_VXOR_VX)
DECLARE_INSN(vzext_vf2, MATCH_VZEXT_VF2, MASK_VZEXT_VF2)
DECLARE_INSN(vzext_vf4, MATCH_VZEXT_VF4, MASK_VZEXT_VF4)
DECLARE_INSN(vzext_vf8, MATCH_VZEXT_VF8, MASK_VZEXT_VF8)
-DECLARE_INSN(wext, MATCH_WEXT, MASK_WEXT)
-DECLARE_INSN(wexti, MATCH_WEXTI, MASK_WEXTI)
DECLARE_INSN(wfi, MATCH_WFI, MASK_WFI)
DECLARE_INSN(wrs_nto, MATCH_WRS_NTO, MASK_WRS_NTO)
DECLARE_INSN(wrs_sto, MATCH_WRS_STO, MASK_WRS_STO)
@@ -4515,6 +4551,7 @@ DECLARE_CSR(vxsat, CSR_VXSAT)
DECLARE_CSR(vxrm, CSR_VXRM)
DECLARE_CSR(vcsr, CSR_VCSR)
DECLARE_CSR(seed, CSR_SEED)
+DECLARE_CSR(jvt, CSR_JVT)
DECLARE_CSR(cycle, CSR_CYCLE)
DECLARE_CSR(time, CSR_TIME)
DECLARE_CSR(instret, CSR_INSTRET)
@@ -4567,6 +4604,9 @@ DECLARE_CSR(scause, CSR_SCAUSE)
DECLARE_CSR(stval, CSR_STVAL)
DECLARE_CSR(sip, CSR_SIP)
DECLARE_CSR(stimecmp, CSR_STIMECMP)
+DECLARE_CSR(siselect, CSR_SISELECT)
+DECLARE_CSR(sireg, CSR_SIREG)
+DECLARE_CSR(stopei, CSR_STOPEI)
DECLARE_CSR(satp, CSR_SATP)
DECLARE_CSR(scontext, CSR_SCONTEXT)
DECLARE_CSR(vsstatus, CSR_VSSTATUS)
@@ -4578,6 +4618,9 @@ DECLARE_CSR(vscause, CSR_VSCAUSE)
DECLARE_CSR(vstval, CSR_VSTVAL)
DECLARE_CSR(vsip, CSR_VSIP)
DECLARE_CSR(vstimecmp, CSR_VSTIMECMP)
+DECLARE_CSR(vsiselect, CSR_VSISELECT)
+DECLARE_CSR(vsireg, CSR_VSIREG)
+DECLARE_CSR(vstopei, CSR_VSTOPEI)
DECLARE_CSR(vsatp, CSR_VSATP)
DECLARE_CSR(hstatus, CSR_HSTATUS)
DECLARE_CSR(hedeleg, CSR_HEDELEG)
@@ -4586,6 +4629,8 @@ DECLARE_CSR(hie, CSR_HIE)
DECLARE_CSR(htimedelta, CSR_HTIMEDELTA)
DECLARE_CSR(hcounteren, CSR_HCOUNTEREN)
DECLARE_CSR(hgeie, CSR_HGEIE)
+DECLARE_CSR(hvien, CSR_HVIEN)
+DECLARE_CSR(hvictl, CSR_HVICTL)
DECLARE_CSR(henvcfg, CSR_HENVCFG)
DECLARE_CSR(hstateen0, CSR_HSTATEEN0)
DECLARE_CSR(hstateen1, CSR_HSTATEEN1)
@@ -4594,11 +4639,15 @@ DECLARE_CSR(hstateen3, CSR_HSTATEEN3)
DECLARE_CSR(htval, CSR_HTVAL)
DECLARE_CSR(hip, CSR_HIP)
DECLARE_CSR(hvip, CSR_HVIP)
+DECLARE_CSR(hviprio1, CSR_HVIPRIO1)
+DECLARE_CSR(hviprio2, CSR_HVIPRIO2)
DECLARE_CSR(htinst, CSR_HTINST)
DECLARE_CSR(hgatp, CSR_HGATP)
DECLARE_CSR(hcontext, CSR_HCONTEXT)
DECLARE_CSR(hgeip, CSR_HGEIP)
+DECLARE_CSR(vstopi, CSR_VSTOPI)
DECLARE_CSR(scountovf, CSR_SCOUNTOVF)
+DECLARE_CSR(stopi, CSR_STOPI)
DECLARE_CSR(utvt, CSR_UTVT)
DECLARE_CSR(unxti, CSR_UNXTI)
DECLARE_CSR(uintstatus, CSR_UINTSTATUS)
@@ -4621,6 +4670,8 @@ DECLARE_CSR(mideleg, CSR_MIDELEG)
DECLARE_CSR(mie, CSR_MIE)
DECLARE_CSR(mtvec, CSR_MTVEC)
DECLARE_CSR(mcounteren, CSR_MCOUNTEREN)
+DECLARE_CSR(mvien, CSR_MVIEN)
+DECLARE_CSR(mvip, CSR_MVIP)
DECLARE_CSR(menvcfg, CSR_MENVCFG)
DECLARE_CSR(mstateen0, CSR_MSTATEEN0)
DECLARE_CSR(mstateen1, CSR_MSTATEEN1)
@@ -4634,6 +4685,9 @@ DECLARE_CSR(mtval, CSR_MTVAL)
DECLARE_CSR(mip, CSR_MIP)
DECLARE_CSR(mtinst, CSR_MTINST)
DECLARE_CSR(mtval2, CSR_MTVAL2)
+DECLARE_CSR(miselect, CSR_MISELECT)
+DECLARE_CSR(mireg, CSR_MIREG)
+DECLARE_CSR(mtopei, CSR_MTOPEI)
DECLARE_CSR(pmpcfg0, CSR_PMPCFG0)
DECLARE_CSR(pmpcfg1, CSR_PMPCFG1)
DECLARE_CSR(pmpcfg2, CSR_PMPCFG2)
@@ -4792,10 +4846,20 @@ DECLARE_CSR(marchid, CSR_MARCHID)
DECLARE_CSR(mimpid, CSR_MIMPID)
DECLARE_CSR(mhartid, CSR_MHARTID)
DECLARE_CSR(mconfigptr, CSR_MCONFIGPTR)
+DECLARE_CSR(mtopi, CSR_MTOPI)
+DECLARE_CSR(sieh, CSR_SIEH)
+DECLARE_CSR(siph, CSR_SIPH)
DECLARE_CSR(stimecmph, CSR_STIMECMPH)
+DECLARE_CSR(vsieh, CSR_VSIEH)
+DECLARE_CSR(vsiph, CSR_VSIPH)
DECLARE_CSR(vstimecmph, CSR_VSTIMECMPH)
DECLARE_CSR(htimedeltah, CSR_HTIMEDELTAH)
+DECLARE_CSR(hidelegh, CSR_HIDELEGH)
+DECLARE_CSR(hvienh, CSR_HVIENH)
DECLARE_CSR(henvcfgh, CSR_HENVCFGH)
+DECLARE_CSR(hviph, CSR_HVIPH)
+DECLARE_CSR(hviprio1h, CSR_HVIPRIO1H)
+DECLARE_CSR(hviprio2h, CSR_HVIPRIO2H)
DECLARE_CSR(hstateen0h, CSR_HSTATEEN0H)
DECLARE_CSR(hstateen1h, CSR_HSTATEEN1H)
DECLARE_CSR(hstateen2h, CSR_HSTATEEN2H)
@@ -4833,11 +4897,16 @@ DECLARE_CSR(hpmcounter29h, CSR_HPMCOUNTER29H)
DECLARE_CSR(hpmcounter30h, CSR_HPMCOUNTER30H)
DECLARE_CSR(hpmcounter31h, CSR_HPMCOUNTER31H)
DECLARE_CSR(mstatush, CSR_MSTATUSH)
+DECLARE_CSR(midelegh, CSR_MIDELEGH)
+DECLARE_CSR(mieh, CSR_MIEH)
+DECLARE_CSR(mvienh, CSR_MVIENH)
+DECLARE_CSR(mviph, CSR_MVIPH)
DECLARE_CSR(menvcfgh, CSR_MENVCFGH)
DECLARE_CSR(mstateen0h, CSR_MSTATEEN0H)
DECLARE_CSR(mstateen1h, CSR_MSTATEEN1H)
DECLARE_CSR(mstateen2h, CSR_MSTATEEN2H)
DECLARE_CSR(mstateen3h, CSR_MSTATEEN3H)
+DECLARE_CSR(miph, CSR_MIPH)
DECLARE_CSR(mhpmevent3h, CSR_MHPMEVENT3H)
DECLARE_CSR(mhpmevent4h, CSR_MHPMEVENT4H)
DECLARE_CSR(mhpmevent5h, CSR_MHPMEVENT5H)
diff --git a/src/target/riscv/gdb_regs.h b/src/target/riscv/gdb_regs.h
index 32bc1d5..ea0c014 100644
--- a/src/target/riscv/gdb_regs.h
+++ b/src/target/riscv/gdb_regs.h
@@ -81,12 +81,15 @@ enum gdb_regno {
GDB_REGNO_VSTART = CSR_VSTART + GDB_REGNO_CSR0,
GDB_REGNO_VXSAT = CSR_VXSAT + GDB_REGNO_CSR0,
GDB_REGNO_VXRM = CSR_VXRM + GDB_REGNO_CSR0,
+ GDB_REGNO_VCSR = CSR_VCSR + GDB_REGNO_CSR0,
GDB_REGNO_VLENB = CSR_VLENB + GDB_REGNO_CSR0,
GDB_REGNO_VL = CSR_VL + GDB_REGNO_CSR0,
GDB_REGNO_VTYPE = CSR_VTYPE + GDB_REGNO_CSR0,
GDB_REGNO_TSELECT = CSR_TSELECT + GDB_REGNO_CSR0,
GDB_REGNO_TDATA1 = CSR_TDATA1 + GDB_REGNO_CSR0,
GDB_REGNO_TDATA2 = CSR_TDATA2 + GDB_REGNO_CSR0,
+ GDB_REGNO_TDATA3 = CSR_TDATA3 + GDB_REGNO_CSR0,
+ GDB_REGNO_TINFO = CSR_TINFO + GDB_REGNO_CSR0,
GDB_REGNO_MISA = CSR_MISA + GDB_REGNO_CSR0,
GDB_REGNO_DPC = CSR_DPC + GDB_REGNO_CSR0,
GDB_REGNO_DCSR = CSR_DCSR + GDB_REGNO_CSR0,
@@ -95,6 +98,11 @@ enum gdb_regno {
GDB_REGNO_MEPC = CSR_MEPC + GDB_REGNO_CSR0,
GDB_REGNO_MCAUSE = CSR_MCAUSE + GDB_REGNO_CSR0,
GDB_REGNO_SATP = CSR_SATP + GDB_REGNO_CSR0,
+ GDB_REGNO_VSATP = CSR_VSATP + GDB_REGNO_CSR0,
+ GDB_REGNO_HGATP = CSR_HGATP + GDB_REGNO_CSR0,
+ GDB_REGNO_HSTATUS = CSR_HSTATUS + GDB_REGNO_CSR0,
+ GDB_REGNO_MTOPI = CSR_MTOPI + GDB_REGNO_CSR0,
+ GDB_REGNO_MTOPEI = CSR_MTOPEI + GDB_REGNO_CSR0,
GDB_REGNO_CSR4095 = GDB_REGNO_CSR0 + 4095,
GDB_REGNO_PRIV = 4161,
/* It's still undecided what register numbers GDB will actually use for
diff --git a/src/target/riscv/opcodes.h b/src/target/riscv/opcodes.h
index 8faa154..59c3413 100644
--- a/src/target/riscv/opcodes.h
+++ b/src/target/riscv/opcodes.h
@@ -294,10 +294,11 @@ static uint32_t srli(unsigned int dest, unsigned int src, uint8_t shamt)
return inst_rs2(shamt) | inst_rs1(src) | inst_rd(dest) | MATCH_SRLI;
}
-static uint32_t fence(void) __attribute__((unused));
-static uint32_t fence(void)
+static uint32_t fence_rw_rw(void) __attribute__((unused));
+static uint32_t fence_rw_rw(void)
{
- return MATCH_FENCE;
+ /* fence rw,rw */
+ return MATCH_FENCE | 0x3300000;
}
static uint32_t auipc(unsigned int dest) __attribute__((unused));
@@ -312,6 +313,12 @@ static uint32_t vsetvli(unsigned int dest, unsigned int src, uint16_t imm)
return (bits(imm, 10, 0) << 20) | inst_rs1(src) | inst_rd(dest) | MATCH_VSETVLI;
}
+static uint32_t vsetvl(unsigned int rd, unsigned int rs1, unsigned int rs2) __attribute__((unused));
+static uint32_t vsetvl(unsigned int rd, unsigned int rs1, unsigned int rs2)
+{
+ return inst_rd(rd) | inst_rs1(rs1) | inst_rs2(rs2) | MATCH_VSETVL;
+}
+
static uint32_t vmv_x_s(unsigned int rd, unsigned int vs2) __attribute__((unused));
static uint32_t vmv_x_s(unsigned int rd, unsigned int vs2)
{
diff --git a/src/target/riscv/program.c b/src/target/riscv/program.c
index 0976539..7cc4a5f 100644
--- a/src/target/riscv/program.c
+++ b/src/target/riscv/program.c
@@ -32,7 +32,8 @@ int riscv_program_init(struct riscv_program *p, struct target *target)
int riscv_program_write(struct riscv_program *program)
{
for (unsigned i = 0; i < program->instruction_count; ++i) {
- LOG_DEBUG("debug_buffer[%02x] = DASM(0x%08x)", i, program->debug_buffer[i]);
+ LOG_TARGET_DEBUG(program->target, "debug_buffer[%02x] = DASM(0x%08x)",
+ i, program->debug_buffer[i]);
if (riscv_write_debug_buffer(program->target, i,
program->debug_buffer[i]) != ERROR_OK)
return ERROR_FAIL;
@@ -48,7 +49,7 @@ int riscv_program_exec(struct riscv_program *p, struct target *t)
riscv_reg_t saved_registers[GDB_REGNO_XPR31 + 1];
for (size_t i = GDB_REGNO_ZERO + 1; i <= GDB_REGNO_XPR31; ++i) {
if (p->writes_xreg[i]) {
- LOG_DEBUG("Saving register %d as used by program", (int)i);
+ LOG_TARGET_DEBUG(t, "Saving register %d as used by program", (int)i);
int result = riscv_get_register(t, &saved_registers[i], i);
if (result != ERROR_OK)
return result;
@@ -56,9 +57,9 @@ int riscv_program_exec(struct riscv_program *p, struct target *t)
}
if (riscv_program_ebreak(p) != ERROR_OK) {
- LOG_ERROR("Unable to write ebreak");
+ LOG_TARGET_ERROR(t, "Unable to insert ebreak into program buffer");
for (size_t i = 0; i < riscv_debug_buffer_size(p->target); ++i)
- LOG_ERROR("ram[%02x]: DASM(0x%08" PRIx32 ") [0x%08" PRIx32 "]",
+ LOG_TARGET_ERROR(t, "ram[%02x]: DASM(0x%08" PRIx32 ") [0x%08" PRIx32 "]",
(int)i, p->debug_buffer[i], p->debug_buffer[i]);
return ERROR_FAIL;
}
@@ -67,14 +68,10 @@ int riscv_program_exec(struct riscv_program *p, struct target *t)
return ERROR_FAIL;
if (riscv_execute_debug_buffer(t) != ERROR_OK) {
- LOG_DEBUG("Unable to execute program %p", p);
+ LOG_TARGET_DEBUG(t, "Unable to execute program %p", p);
return ERROR_FAIL;
}
- for (size_t i = 0; i < riscv_debug_buffer_size(p->target); ++i)
- if (i >= riscv_debug_buffer_size(p->target))
- p->debug_buffer[i] = riscv_read_debug_buffer(t, i);
-
for (size_t i = GDB_REGNO_ZERO; i <= GDB_REGNO_XPR31; ++i)
if (p->writes_xreg[i])
riscv_set_register(t, i, saved_registers[i]);
@@ -151,9 +148,9 @@ int riscv_program_fence_i(struct riscv_program *p)
return riscv_program_insert(p, fence_i());
}
-int riscv_program_fence(struct riscv_program *p)
+int riscv_program_fence_rw_rw(struct riscv_program *p)
{
- return riscv_program_insert(p, fence());
+ return riscv_program_insert(p, fence_rw_rw());
}
int riscv_program_ebreak(struct riscv_program *p)
@@ -175,9 +172,9 @@ int riscv_program_addi(struct riscv_program *p, enum gdb_regno d, enum gdb_regno
int riscv_program_insert(struct riscv_program *p, riscv_insn_t i)
{
if (p->instruction_count >= riscv_debug_buffer_size(p->target)) {
- LOG_ERROR("Unable to insert instruction:");
- LOG_ERROR(" instruction_count=%d", (int)p->instruction_count);
- LOG_ERROR(" buffer size =%d", (int)riscv_debug_buffer_size(p->target));
+ LOG_TARGET_ERROR(p->target, "Unable to insert program into progbuf, "
+ "capacity would be exceeded (progbufsize=%d).",
+ (int)riscv_debug_buffer_size(p->target));
return ERROR_FAIL;
}
diff --git a/src/target/riscv/program.h b/src/target/riscv/program.h
index 62a04f0..6916500 100644
--- a/src/target/riscv/program.h
+++ b/src/target/riscv/program.h
@@ -63,7 +63,7 @@ int riscv_program_csrr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno
int riscv_program_csrw(struct riscv_program *p, enum gdb_regno s, enum gdb_regno csr);
int riscv_program_fence_i(struct riscv_program *p);
-int riscv_program_fence(struct riscv_program *p);
+int riscv_program_fence_rw_rw(struct riscv_program *p);
int riscv_program_ebreak(struct riscv_program *p);
int riscv_program_addi(struct riscv_program *p, enum gdb_regno d, enum gdb_regno s, int16_t i);
diff --git a/src/target/riscv/riscv-011.c b/src/target/riscv/riscv-011.c
index be296cd..e4e6e43 100644
--- a/src/target/riscv/riscv-011.c
+++ b/src/target/riscv/riscv-011.c
@@ -67,6 +67,8 @@
* to the target. Afterwards use cache_get... to read results.
*/
+static int handle_halt(struct target *target, bool announce);
+
#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1)))
#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask)))
@@ -161,15 +163,6 @@ typedef enum slot {
#define DRAM_CACHE_SIZE 16
-struct trigger {
- uint64_t address;
- uint32_t length;
- uint64_t mask;
- uint64_t value;
- bool read, write, execute;
- int unique_id;
-};
-
struct memory_cache_line {
uint32_t data;
bool valid;
@@ -219,7 +212,8 @@ typedef struct {
static int poll_target(struct target *target, bool announce);
static int riscv011_poll(struct target *target);
-static int get_register(struct target *target, riscv_reg_t *value, int regid);
+static int get_register(struct target *target, riscv_reg_t *value,
+ enum gdb_regno regid);
/*** Utility functions. ***/
@@ -1113,6 +1107,9 @@ static int execute_resume(struct target *target, bool step)
LOG_DEBUG("step=%d", step);
+ if (riscv_flush_registers(target) != ERROR_OK)
+ return ERROR_FAIL;
+
maybe_write_tselect(target);
/* TODO: check if dpc is dirty (which also is true if an exception was hit
@@ -1189,17 +1186,7 @@ static int full_step(struct target *target, bool announce)
return ERROR_FAIL;
}
}
- return ERROR_OK;
-}
-
-static int resume(struct target *target, int debug_execution, bool step)
-{
- if (debug_execution) {
- LOG_ERROR("TODO: debug_execution is true");
- return ERROR_FAIL;
- }
-
- return execute_resume(target, step);
+ return handle_halt(target, announce);
}
static uint64_t reg_cache_get(struct target *target, unsigned int number)
@@ -1338,7 +1325,8 @@ static int register_write(struct target *target, unsigned int number,
return ERROR_OK;
}
-static int get_register(struct target *target, riscv_reg_t *value, int regid)
+static int get_register(struct target *target, riscv_reg_t *value,
+ enum gdb_regno regid)
{
riscv011_info_t *info = get_info(target);
@@ -1382,7 +1370,8 @@ static int get_register(struct target *target, riscv_reg_t *value, int regid)
return ERROR_OK;
}
-static int set_register(struct target *target, int regid, uint64_t value)
+static int set_register(struct target *target, enum gdb_regno regid,
+ riscv_reg_t value)
{
return register_write(target, regid, value);
}
@@ -1590,7 +1579,6 @@ static int examine(struct target *target)
return result;
target_set_examined(target);
- riscv_set_current_hartid(target, 0);
for (size_t i = 0; i < 32; ++i)
reg_cache_set(target, i, -1);
LOG_INFO("Examined RISCV core; XLEN=%d, misa=0x%" PRIx64,
@@ -1936,7 +1924,7 @@ static int riscv011_resume(struct target *target, int current,
jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
r->prepped = false;
- return resume(target, debug_execution, false);
+ return execute_resume(target, false);
}
static int assert_reset(struct target *target)
diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c
index 4e6c8dc..10a4bb1 100644
--- a/src/target/riscv/riscv-013.c
+++ b/src/target/riscv/riscv-013.c
@@ -31,41 +31,49 @@
static int riscv013_on_step_or_resume(struct target *target, bool step);
static int riscv013_step_or_resume_current_hart(struct target *target,
- bool step, bool use_hasel);
+ bool step);
static void riscv013_clear_abstract_error(struct target *target);
/* Implementations of the functions in struct riscv_info. */
static int riscv013_get_register(struct target *target,
- riscv_reg_t *value, int rid);
-static int riscv013_set_register(struct target *target, int regid, uint64_t value);
-static int riscv013_select_current_hart(struct target *target);
+ riscv_reg_t *value, enum gdb_regno rid);
+static int riscv013_set_register(struct target *target, enum gdb_regno regid,
+ riscv_reg_t value);
+static int dm013_select_hart(struct target *target, int hart_index);
static int riscv013_halt_prep(struct target *target);
static int riscv013_halt_go(struct target *target);
static int riscv013_resume_go(struct target *target);
static int riscv013_step_current_hart(struct target *target);
-static int riscv013_on_halt(struct target *target);
static int riscv013_on_step(struct target *target);
static int riscv013_resume_prep(struct target *target);
-static bool riscv013_is_halted(struct target *target);
static enum riscv_halt_reason riscv013_halt_reason(struct target *target);
static int riscv013_write_debug_buffer(struct target *target, unsigned index,
riscv_insn_t d);
static riscv_insn_t riscv013_read_debug_buffer(struct target *target, unsigned
index);
+static int riscv013_invalidate_cached_debug_buffer(struct target *target);
static int riscv013_execute_debug_buffer(struct target *target);
static void riscv013_fill_dmi_write_u64(struct target *target, char *buf, int a, uint64_t d);
static void riscv013_fill_dmi_read_u64(struct target *target, char *buf, int a);
static int riscv013_dmi_write_u64_bits(struct target *target);
static void riscv013_fill_dmi_nop_u64(struct target *target, char *buf);
-static int register_read(struct target *target, uint64_t *value, uint32_t number);
-static int register_read_direct(struct target *target, uint64_t *value, uint32_t number);
-static int register_write_direct(struct target *target, unsigned number,
- uint64_t value);
+static unsigned int register_size(struct target *target, enum gdb_regno number);
+static int register_read_direct(struct target *target, riscv_reg_t *value,
+ enum gdb_regno number);
+static int register_write_direct(struct target *target, enum gdb_regno number,
+ riscv_reg_t value);
static int read_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment);
static int write_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, const uint8_t *buffer);
+typedef enum {
+ HALT_GROUP,
+ RESUME_GROUP
+} grouptype_t;
+static int set_group(struct target *target, bool *supported, unsigned int group,
+ grouptype_t grouptype);
+
/**
* Since almost everything can be accomplish by scanning the dbus register, all
* functions here assume dbus is already selected. The exception are functions
@@ -76,13 +84,6 @@ static int write_memory(struct target *target, target_addr_t address,
#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1)))
#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask)))
-#define CSR_DCSR_CAUSE_SWBP 1
-#define CSR_DCSR_CAUSE_TRIGGER 2
-#define CSR_DCSR_CAUSE_DEBUGINT 3
-#define CSR_DCSR_CAUSE_STEP 4
-#define CSR_DCSR_CAUSE_HALT 5
-#define CSR_DCSR_CAUSE_GROUP 6
-
#define RISCV013_INFO(r) riscv013_info_t *r = get_info(target)
/*** JTAG registers. ***/
@@ -113,22 +114,8 @@ typedef enum slot {
#define CMDERR_HALT_RESUME 4
#define CMDERR_OTHER 7
-/*** Info about the core being debugged. ***/
-
-struct trigger {
- uint64_t address;
- uint32_t length;
- uint64_t mask;
- uint64_t value;
- bool read, write, execute;
- int unique_id;
-};
-
-typedef enum {
- YNM_MAYBE,
- YNM_YES,
- YNM_NO
-} yes_no_maybe_t;
+#define HART_INDEX_MULTIPLE -1
+#define HART_INDEX_UNKNOWN -2
typedef struct {
struct list_head list;
@@ -140,8 +127,10 @@ typedef struct {
bool was_reset;
/* Targets that are connected to this DM. */
struct list_head target_list;
- /* The currently selected hartid on this DM. */
+ /* Contains the ID of the hart that is currently selected by this DM.
+ * If multiple harts are selected this is HART_INDEX_MULTIPLE. */
int current_hartid;
+
bool hasel_supported;
/* The program buffer stores executable code. 0 is an illegal instruction,
@@ -213,6 +202,16 @@ typedef struct {
/* DM that provides access to this target. */
dm013_info_t *dm;
+
+ /* This target was selected using hasel. */
+ bool selected;
+
+ /* When false, we need to set dcsr.ebreak*, halting the target if that's
+ * necessary. */
+ bool dcsr_ebreak_is_set;
+
+ /* This hart was placed into a halt group in examine(). */
+ bool haltgroup_supported;
} riscv013_info_t;
static LIST_HEAD(dm_list);
@@ -253,7 +252,7 @@ static dm013_info_t *get_dm(struct target *target)
if (!dm)
return NULL;
dm->abs_chain_position = abs_chain_position;
- dm->current_hartid = -1;
+ dm->current_hartid = 0;
dm->hart_count = -1;
INIT_LIST_HEAD(&dm->target_list);
list_add(&dm->list, &dm_list);
@@ -276,16 +275,23 @@ static dm013_info_t *get_dm(struct target *target)
return dm;
}
-static uint32_t set_hartsel(uint32_t initial, uint32_t index)
+static uint32_t set_dmcontrol_hartsel(uint32_t initial, int hart_index)
{
- initial &= ~DM_DMCONTROL_HARTSELLO;
- initial &= ~DM_DMCONTROL_HARTSELHI;
-
- uint32_t index_lo = index & ((1 << DM_DMCONTROL_HARTSELLO_LENGTH) - 1);
- initial |= index_lo << DM_DMCONTROL_HARTSELLO_OFFSET;
- uint32_t index_hi = index >> DM_DMCONTROL_HARTSELLO_LENGTH;
- assert(index_hi < 1 << DM_DMCONTROL_HARTSELHI_LENGTH);
- initial |= index_hi << DM_DMCONTROL_HARTSELHI_OFFSET;
+ assert(hart_index != HART_INDEX_UNKNOWN);
+
+ if (hart_index >= 0) {
+ initial = set_field(initial, DM_DMCONTROL_HASEL, DM_DMCONTROL_HASEL_SINGLE);
+ uint32_t index_lo = hart_index & ((1 << DM_DMCONTROL_HARTSELLO_LENGTH) - 1);
+ initial = set_field(initial, DM_DMCONTROL_HARTSELLO, index_lo);
+ uint32_t index_hi = hart_index >> DM_DMCONTROL_HARTSELLO_LENGTH;
+ assert(index_hi < (1 << DM_DMCONTROL_HARTSELHI_LENGTH));
+ initial = set_field(initial, DM_DMCONTROL_HARTSELHI, index_hi);
+ } else if (hart_index == HART_INDEX_MULTIPLE) {
+ initial = set_field(initial, DM_DMCONTROL_HASEL, DM_DMCONTROL_HASEL_MULTIPLE);
+ /* TODO: https://github.com/riscv/riscv-openocd/issues/748 */
+ initial = set_field(initial, DM_DMCONTROL_HARTSELLO, 0);
+ initial = set_field(initial, DM_DMCONTROL_HARTSELHI, 0);
+ }
return initial;
}
@@ -482,6 +488,7 @@ static dmi_status_t dmi_scan(struct target *target, uint32_t *address_in,
if (r->reset_delays_wait >= 0) {
r->reset_delays_wait--;
if (r->reset_delays_wait < 0) {
+ LOG_TARGET_DEBUG(target, "reset_delays_wait done");
info->dmi_busy_delay = 0;
info->ac_busy_delay = 0;
}
@@ -561,7 +568,6 @@ static int dmi_op_timeout(struct target *target, uint32_t *data_in,
select_dmi(target);
dmi_status_t status;
- uint32_t address_in;
if (dmi_busy_encountered)
*dmi_busy_encountered = false;
@@ -597,15 +603,15 @@ static int dmi_op_timeout(struct target *target, uint32_t *data_in,
} else if (status == DMI_STATUS_SUCCESS) {
break;
} else {
- LOG_ERROR("failed %s at 0x%x, status=%d", op_name, address, status);
- return ERROR_FAIL;
+ dtmcontrol_scan(target, DTM_DTMCS_DMIRESET);
+ break;
}
if (time(NULL) - start > timeout_sec)
return ERROR_TIMEOUT_REACHED;
}
if (status != DMI_STATUS_SUCCESS) {
- LOG_ERROR("Failed %s at 0x%x; status=%d", op_name, address, status);
+ LOG_TARGET_ERROR(target, "Failed DMI %s at 0x%x; status=%d", op_name, address, status);
return ERROR_FAIL;
}
@@ -614,7 +620,7 @@ static int dmi_op_timeout(struct target *target, uint32_t *data_in,
* Note that NOP can result in a 'busy' result as well, but that would be
* noticed on the next DMI access we do. */
while (1) {
- status = dmi_scan(target, &address_in, data_in, DMI_OP_NOP, address, 0,
+ status = dmi_scan(target, NULL, data_in, DMI_OP_NOP, address, 0,
false);
if (status == DMI_STATUS_BUSY) {
increase_dmi_busy_delay(target);
@@ -624,12 +630,15 @@ static int dmi_op_timeout(struct target *target, uint32_t *data_in,
break;
} else {
if (data_in) {
- LOG_ERROR("Failed %s (NOP) at 0x%x; value=0x%x, status=%d",
+ LOG_TARGET_ERROR(target,
+ "Failed DMI %s (NOP) at 0x%x; value=0x%x, status=%d",
op_name, address, *data_in, status);
} else {
- LOG_ERROR("Failed %s (NOP) at 0x%x; status=%d", op_name, address,
+ LOG_TARGET_ERROR(target,
+ "Failed DMI %s (NOP) at 0x%x; status=%d", op_name, address,
status);
}
+ dtmcontrol_scan(target, DTM_DTMCS_DMIRESET);
return ERROR_FAIL;
}
if (time(NULL) - start > timeout_sec)
@@ -647,10 +656,10 @@ static int dmi_op(struct target *target, uint32_t *data_in,
int result = dmi_op_timeout(target, data_in, dmi_busy_encountered, dmi_op,
address, data_out, riscv_command_timeout_sec, exec, ensure_success);
if (result == ERROR_TIMEOUT_REACHED) {
- LOG_ERROR("DMI operation didn't complete in %d seconds. The target is "
+ LOG_ERROR("[%s] DMI operation didn't complete in %d seconds. The target is "
"either really slow or broken. You could increase the "
"timeout with riscv set_command_timeout_sec.",
- riscv_command_timeout_sec);
+ target_name(target), riscv_command_timeout_sec);
return ERROR_FAIL;
}
return result;
@@ -702,8 +711,14 @@ static int dmstatus_read_timeout(struct target *target, uint32_t *dmstatus,
static int dmstatus_read(struct target *target, uint32_t *dmstatus,
bool authenticated)
{
- return dmstatus_read_timeout(target, dmstatus, authenticated,
+ int result = dmstatus_read_timeout(target, dmstatus, authenticated,
riscv_command_timeout_sec);
+ if (result == ERROR_TIMEOUT_REACHED)
+ LOG_TARGET_ERROR(target, "DMSTATUS read didn't complete in %d seconds. The target is "
+ "either really slow or broken. You could increase the "
+ "timeout with `riscv set_command_timeout_sec`.",
+ riscv_command_timeout_sec);
+ return result;
}
static void increase_ac_busy_delay(struct target *target)
@@ -743,20 +758,6 @@ static int wait_for_idle(struct target *target, uint32_t *abstractcs)
if (time(NULL) - start > riscv_command_timeout_sec) {
info->cmderr = get_field(*abstractcs, DM_ABSTRACTCS_CMDERR);
- if (info->cmderr != CMDERR_NONE) {
- const char *errors[8] = {
- "none",
- "busy",
- "not supported",
- "exception",
- "halt/resume",
- "reserved",
- "reserved",
- "other" };
-
- LOG_ERROR("Abstract command ended in error '%s' (abstractcs=0x%x)",
- errors[info->cmderr], *abstractcs);
- }
LOG_ERROR("Timed out after %ds waiting for busy to go low (abstractcs=0x%x). "
"Increase the timeout with riscv set_command_timeout_sec.",
@@ -767,6 +768,12 @@ static int wait_for_idle(struct target *target, uint32_t *abstractcs)
}
}
+static int dm013_select_target(struct target *target)
+{
+ riscv013_info_t *info = get_info(target);
+ return dm013_select_hart(target, info->index);
+}
+
static int execute_abstract_command(struct target *target, uint32_t command)
{
RISCV013_INFO(info);
@@ -858,8 +865,8 @@ static uint32_t access_register_command(struct target *target, uint32_t number,
command = set_field(command, AC_ACCESS_REGISTER_AARSIZE, 3);
break;
default:
- LOG_ERROR("%d-bit register %s not supported.", size,
- gdb_regno_name(number));
+ LOG_ERROR("[%s] %d-bit register %s not supported.",
+ target_name(target), size, gdb_regno_name(number));
assert(0);
}
@@ -888,8 +895,8 @@ static uint32_t access_register_command(struct target *target, uint32_t number,
return command;
}
-static int register_read_abstract(struct target *target, uint64_t *value,
- uint32_t number, unsigned size)
+static int register_read_abstract_with_size(struct target *target,
+ riscv_reg_t *value, enum gdb_regno number, unsigned int size)
{
RISCV013_INFO(info);
@@ -926,10 +933,19 @@ static int register_read_abstract(struct target *target, uint64_t *value,
return ERROR_OK;
}
-static int register_write_abstract(struct target *target, uint32_t number,
- uint64_t value, unsigned size)
+static int register_read_abstract(struct target *target, riscv_reg_t *value,
+ enum gdb_regno number)
+{
+ const unsigned int size = register_size(target, number);
+
+ return register_read_abstract_with_size(target, value, number, size);
+}
+
+static int register_write_abstract(struct target *target, enum gdb_regno number,
+ riscv_reg_t value)
{
RISCV013_INFO(info);
+ const unsigned int size = register_size(target, number);
if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 &&
!info->abstract_write_fpr_supported)
@@ -989,14 +1005,14 @@ static uint32_t abstract_memory_size(unsigned width)
* Creates a memory access abstract command.
*/
static uint32_t access_memory_command(struct target *target, bool virtual,
- unsigned width, bool postincrement, bool write)
+ unsigned width, bool postincrement, bool is_write)
{
uint32_t command = set_field(0, AC_ACCESS_MEMORY_CMDTYPE, 2);
command = set_field(command, AC_ACCESS_MEMORY_AAMVIRTUAL, virtual);
command |= abstract_memory_size(width);
command = set_field(command, AC_ACCESS_MEMORY_AAMPOSTINCREMENT,
postincrement);
- command = set_field(command, AC_ACCESS_MEMORY_WRITE, write);
+ command = set_field(command, AC_ACCESS_MEMORY_WRITE, is_write);
return command;
}
@@ -1016,8 +1032,7 @@ static int examine_progbuf(struct target *target)
return ERROR_OK;
}
- uint64_t s0;
- if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
+ if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
struct riscv_program program;
@@ -1033,9 +1048,6 @@ static int examine_progbuf(struct target *target)
riscv_program_insert(&program, sw(S0, S0, 0));
int result = riscv_program_exec(&program, target);
- if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
- return ERROR_FAIL;
-
if (result != ERROR_OK) {
/* This program might have failed if the program buffer is not
* writable. */
@@ -1060,7 +1072,7 @@ static int examine_progbuf(struct target *target)
return ERROR_OK;
}
-static int is_fpu_reg(uint32_t gdb_regno)
+static int is_fpu_reg(enum gdb_regno gdb_regno)
{
return (gdb_regno >= GDB_REGNO_FPR0 && gdb_regno <= GDB_REGNO_FPR31) ||
(gdb_regno == GDB_REGNO_CSR0 + CSR_FFLAGS) ||
@@ -1068,46 +1080,61 @@ static int is_fpu_reg(uint32_t gdb_regno)
(gdb_regno == GDB_REGNO_CSR0 + CSR_FCSR);
}
-static int is_vector_reg(uint32_t gdb_regno)
+static int is_vector_reg(enum gdb_regno gdb_regno)
{
return (gdb_regno >= GDB_REGNO_V0 && gdb_regno <= GDB_REGNO_V31) ||
gdb_regno == GDB_REGNO_VSTART ||
gdb_regno == GDB_REGNO_VXSAT ||
gdb_regno == GDB_REGNO_VXRM ||
+ gdb_regno == GDB_REGNO_VCSR ||
gdb_regno == GDB_REGNO_VL ||
gdb_regno == GDB_REGNO_VTYPE ||
gdb_regno == GDB_REGNO_VLENB;
}
-static int prep_for_register_access(struct target *target, uint64_t *mstatus,
- int regno)
+static int prep_for_register_access(struct target *target,
+ riscv_reg_t *orig_mstatus, enum gdb_regno regno)
{
- if (is_fpu_reg(regno) || is_vector_reg(regno)) {
- if (register_read(target, mstatus, GDB_REGNO_MSTATUS) != ERROR_OK)
- return ERROR_FAIL;
- if (is_fpu_reg(regno) && (*mstatus & MSTATUS_FS) == 0) {
- if (register_write_direct(target, GDB_REGNO_MSTATUS,
- set_field(*mstatus, MSTATUS_FS, 1)) != ERROR_OK)
- return ERROR_FAIL;
- } else if (is_vector_reg(regno) && (*mstatus & MSTATUS_VS) == 0) {
- if (register_write_direct(target, GDB_REGNO_MSTATUS,
- set_field(*mstatus, MSTATUS_VS, 1)) != ERROR_OK)
- return ERROR_FAIL;
- }
- } else {
- *mstatus = 0;
- }
+ assert(orig_mstatus);
+
+ if (!is_fpu_reg(regno) && !is_vector_reg(regno))
+ /* No special preparation needed */
+ return ERROR_OK;
+
+ LOG_TARGET_DEBUG(target, "Preparing mstatus to access %s",
+ gdb_regno_name(regno));
+
+ assert(target->state == TARGET_HALTED &&
+ "The target must be halted to modify and then restore mstatus");
+
+ if (riscv_get_register(target, orig_mstatus, GDB_REGNO_MSTATUS) != ERROR_OK)
+ return ERROR_FAIL;
+
+ riscv_reg_t new_mstatus = *orig_mstatus;
+ riscv_reg_t field_mask = is_fpu_reg(regno) ? MSTATUS_FS : MSTATUS_VS;
+
+ if ((new_mstatus & field_mask) != 0)
+ return ERROR_OK;
+
+ new_mstatus = set_field(new_mstatus, field_mask, 1);
+
+ if (riscv_write_register(target, GDB_REGNO_MSTATUS, new_mstatus) != ERROR_OK)
+ return ERROR_FAIL;
+
+ LOG_TARGET_DEBUG(target, "Prepared to access %s (mstatus=0x%" PRIx64 ")",
+ gdb_regno_name(regno), new_mstatus);
return ERROR_OK;
}
static int cleanup_after_register_access(struct target *target,
- uint64_t mstatus, int regno)
+ riscv_reg_t mstatus, enum gdb_regno regno)
{
- if ((is_fpu_reg(regno) && (mstatus & MSTATUS_FS) == 0) ||
- (is_vector_reg(regno) && (mstatus & MSTATUS_VS) == 0))
- if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus) != ERROR_OK)
- return ERROR_FAIL;
- return ERROR_OK;
+ if (!is_fpu_reg(regno) && !is_vector_reg(regno))
+ /* Mstatus was not changed for this register access. No need to restore it. */
+ return ERROR_OK;
+
+ LOG_TARGET_DEBUG(target, "Restoring mstatus to 0x%" PRIx64, mstatus);
+ return riscv_write_register(target, GDB_REGNO_MSTATUS, mstatus);
}
typedef enum {
@@ -1247,6 +1274,7 @@ static int scratch_write64(struct target *target, scratch_mem_t *scratch,
case SPACE_DMI_PROGBUF:
dmi_write(target, DM_PROGBUF0 + scratch->debug_address, value);
dmi_write(target, DM_PROGBUF1 + scratch->debug_address, value >> 32);
+ riscv013_invalidate_cached_debug_buffer(target);
break;
case SPACE_DMI_RAM:
{
@@ -1269,7 +1297,7 @@ static int scratch_write64(struct target *target, scratch_mem_t *scratch,
}
/** Return register size in bits. */
-static unsigned register_size(struct target *target, unsigned number)
+static unsigned int register_size(struct target *target, enum gdb_regno number)
{
/* If reg_cache hasn't been initialized yet, make a guess. We need this for
* when this function is called during examine(). */
@@ -1288,204 +1316,233 @@ static bool has_sufficient_progbuf(struct target *target, unsigned size)
}
/**
- * Immediately write the new value to the requested register. This mechanism
- * bypasses any caches.
+ * This function is used to read a 64-bit value from a register by executing a
+ * program.
+ * The program stores a register to address located in S0.
+ * The caller should save S0.
*/
-static int register_write_direct(struct target *target, unsigned number,
- uint64_t value)
+static int internal_register_read64_progbuf_scratch(struct target *target,
+ struct riscv_program *program, riscv_reg_t *value)
{
- LOG_DEBUG("{%d} %s <- 0x%" PRIx64, riscv_current_hartid(target),
- gdb_regno_name(number), value);
-
- int result = register_write_abstract(target, number, value,
- register_size(target, number));
- if (result == ERROR_OK || !has_sufficient_progbuf(target, 2) ||
- !riscv_is_halted(target))
- return result;
-
- struct riscv_program program;
- riscv_program_init(&program, target);
+ scratch_mem_t scratch;
- uint64_t s0;
- if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
+ if (scratch_reserve(target, &scratch, program, 8) != ERROR_OK)
return ERROR_FAIL;
- uint64_t mstatus;
- if (prep_for_register_access(target, &mstatus, number) != ERROR_OK)
+ if (register_write_abstract(target, GDB_REGNO_S0, scratch.hart_address)
+ != ERROR_OK) {
+ scratch_release(target, &scratch);
+ return ERROR_FAIL;
+ }
+ if (riscv_program_exec(program, target) != ERROR_OK) {
+ scratch_release(target, &scratch);
return ERROR_FAIL;
+ }
- scratch_mem_t scratch;
- bool use_scratch = false;
- if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 &&
- riscv_supports_extension(target, 'D') &&
- riscv_xlen(target) < 64) {
- /* There are no instructions to move all the bits from a register, so
- * we need to use some scratch RAM. */
- use_scratch = true;
- riscv_program_insert(&program, fld(number - GDB_REGNO_FPR0, S0, 0));
-
- if (scratch_reserve(target, &scratch, &program, 8) != ERROR_OK)
- return ERROR_FAIL;
+ int result = scratch_read64(target, &scratch, value);
- if (register_write_direct(target, GDB_REGNO_S0, scratch.hart_address)
- != ERROR_OK) {
- scratch_release(target, &scratch);
- return ERROR_FAIL;
- }
+ scratch_release(target, &scratch);
+ return result;
+}
- if (scratch_write64(target, &scratch, value) != ERROR_OK) {
- scratch_release(target, &scratch);
- return ERROR_FAIL;
- }
+/**
+ * This function reads a register by writing a program to program buffer and
+ * executing it.
+ */
+static int register_read_progbuf(struct target *target, uint64_t *value,
+ enum gdb_regno number)
+{
+ assert(target->state == TARGET_HALTED);
- } else if (number == GDB_REGNO_VTYPE) {
- riscv_program_insert(&program, csrr(S0, CSR_VL));
- riscv_program_insert(&program, vsetvli(ZERO, S0, value));
+ struct riscv_program program;
+ riscv_program_init(&program, target);
- } else {
- if (register_write_direct(target, GDB_REGNO_S0, value) != ERROR_OK)
- return ERROR_FAIL;
+ if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
+ return ERROR_FAIL;
- if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
- if (riscv_supports_extension(target, 'D'))
- riscv_program_insert(&program, fmv_d_x(number - GDB_REGNO_FPR0, S0));
- else
- riscv_program_insert(&program, fmv_w_x(number - GDB_REGNO_FPR0, S0));
- } else if (number == GDB_REGNO_VL) {
- /* "The XLEN-bit-wide read-only vl CSR can only be updated by the
- * vsetvli and vsetvl instructions, and the fault-only-rst vector
- * load instruction variants." */
- riscv_reg_t vtype;
- if (register_read(target, &vtype, GDB_REGNO_VTYPE) != ERROR_OK)
- return ERROR_FAIL;
- if (riscv_program_insert(&program, vsetvli(ZERO, S0, vtype)) != ERROR_OK)
+ if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
+ const unsigned int freg = number - GDB_REGNO_FPR0;
+
+ if (riscv_supports_extension(target, 'D') && riscv_xlen(target) < 64) {
+ /* There are no instructions to move all the bits from a
+ * register, so we need to use some scratch RAM.
+ */
+ if (riscv_program_insert(&program, fsd(freg, S0, 0)) != ERROR_OK)
return ERROR_FAIL;
- } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
- riscv_program_csrw(&program, S0, number);
- } else {
- LOG_ERROR("Unsupported register (enum gdb_regno)(%d)", number);
- return ERROR_FAIL;
+ return internal_register_read64_progbuf_scratch(target, &program,
+ value);
}
+ if (riscv_program_insert(&program,
+ riscv_supports_extension(target, 'D') ?
+ fmv_x_d(S0, freg) : fmv_x_w(S0, freg)) != ERROR_OK)
+ return ERROR_FAIL;
+ } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
+ if (riscv_program_csrr(&program, S0, number) != ERROR_OK)
+ return ERROR_FAIL;
+ } else {
+ LOG_ERROR("Unsupported register: %s", gdb_regno_name(number));
+ return ERROR_FAIL;
}
- int exec_out = riscv_program_exec(&program, target);
- /* Don't message on error. Probably the register doesn't exist. */
- if (exec_out == ERROR_OK && target->reg_cache) {
- struct reg *reg = &target->reg_cache->reg_list[number];
- buf_set_u64(reg->value, 0, reg->size, value);
- }
+ if (riscv_program_exec(&program, target) != ERROR_OK)
+ return ERROR_FAIL;
- if (use_scratch)
- scratch_release(target, &scratch);
+ return register_read_abstract(target, value, GDB_REGNO_S0) != ERROR_OK;
+}
- if (cleanup_after_register_access(target, mstatus, number) != ERROR_OK)
+/**
+ * This function is used to write a 64-bit value to a register by executing a
+ * program.
+ * The program loads a value from address located in S0 to a register.
+ * The caller should save S0.
+ */
+static int internal_register_write64_progbuf_scratch(struct target *target,
+ struct riscv_program *program, riscv_reg_t value)
+{
+ scratch_mem_t scratch;
+
+ if (scratch_reserve(target, &scratch, program, 8) != ERROR_OK)
return ERROR_FAIL;
- /* Restore S0. */
- if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
+ if (register_write_abstract(target, GDB_REGNO_S0, scratch.hart_address)
+ != ERROR_OK) {
+ scratch_release(target, &scratch);
return ERROR_FAIL;
+ }
+ if (scratch_write64(target, &scratch, value) != ERROR_OK) {
+ scratch_release(target, &scratch);
+ return ERROR_FAIL;
+ }
+ int result = riscv_program_exec(program, target);
- return exec_out;
+ scratch_release(target, &scratch);
+ return result;
}
-/** Read register value from the target. Also update the cached value. */
-static int register_read(struct target *target, uint64_t *value, uint32_t number)
+/**
+ * This function writes a register by writing a program to program buffer and
+ * executing it.
+ */
+static int register_write_progbuf(struct target *target, enum gdb_regno number,
+ riscv_reg_t value)
{
- if (number == GDB_REGNO_ZERO) {
- *value = 0;
- return ERROR_OK;
+ assert(target->state == TARGET_HALTED);
+
+ struct riscv_program program;
+
+ riscv_program_init(&program, target);
+
+ if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 &&
+ riscv_supports_extension(target, 'D') && riscv_xlen(target) < 64) {
+ /* There are no instructions to move all the bits from a register,
+ * so we need to use some scratch RAM.
+ */
+ const unsigned int freg = number - GDB_REGNO_FPR0;
+
+ if (riscv_program_insert(&program, fld(freg, S0, 0)) != ERROR_OK)
+ return ERROR_FAIL;
+ return internal_register_write64_progbuf_scratch(target, &program,
+ value);
}
- int result = register_read_direct(target, value, number);
- if (result != ERROR_OK)
+
+ if (register_write_abstract(target, GDB_REGNO_S0, value) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
+ const unsigned int freg = number - GDB_REGNO_FPR0;
+
+ if (riscv_program_insert(&program,
+ riscv_supports_extension(target, 'D') ?
+ fmv_d_x(freg, S0) : fmv_w_x(freg, S0)) != ERROR_OK)
+ return ERROR_FAIL;
+ } else if (number == GDB_REGNO_VTYPE) {
+ if (riscv_save_register(target, GDB_REGNO_S1) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_program_insert(&program, csrr(S1, CSR_VL)) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_program_insert(&program, vsetvl(ZERO, S1, S0)) != ERROR_OK)
+ return ERROR_FAIL;
+ } else if (number == GDB_REGNO_VL) {
+ /* "The XLEN-bit-wide read-only vl CSR can only be updated by the
+ * vsetvli and vsetvl instructions, and the fault-only-rst vector
+ * load instruction variants."
+ */
+ if (riscv_save_register(target, GDB_REGNO_S1) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_program_insert(&program, csrr(S1, CSR_VTYPE)) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_program_insert(&program, vsetvl(ZERO, S0, S1)) != ERROR_OK)
+ return ERROR_FAIL;
+ } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
+ if (riscv_program_csrw(&program, S0, number) != ERROR_OK)
+ return ERROR_FAIL;
+ } else {
+ LOG_ERROR("Unsupported register (enum gdb_regno)(%d)", number);
return ERROR_FAIL;
- if (target->reg_cache) {
- struct reg *reg = &target->reg_cache->reg_list[number];
- buf_set_u64(reg->value, 0, reg->size, *value);
}
- return ERROR_OK;
+ return riscv_program_exec(&program, target);
}
-/** Actually read registers from the target right now. */
-static int register_read_direct(struct target *target, uint64_t *value, uint32_t number)
+/**
+ * Immediately write the new value to the requested register. This mechanism
+ * bypasses any caches.
+ */
+static int register_write_direct(struct target *target, enum gdb_regno number,
+ riscv_reg_t value)
{
- int result = register_read_abstract(target, value, number,
- register_size(target, number));
+ LOG_TARGET_DEBUG(target, "Writing 0x%" PRIx64 " to %s", value,
+ gdb_regno_name(number));
- if (result != ERROR_OK &&
- has_sufficient_progbuf(target, 2) &&
- number > GDB_REGNO_XPR31) {
- struct riscv_program program;
- riscv_program_init(&program, target);
+ if (target->state != TARGET_HALTED)
+ return register_write_abstract(target, number, value);
- scratch_mem_t scratch;
- bool use_scratch = false;
+ riscv_reg_t mstatus;
+ if (prep_for_register_access(target, &mstatus, number) != ERROR_OK)
+ return ERROR_FAIL;
- riscv_reg_t s0;
- if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
- return ERROR_FAIL;
+ int result = register_write_abstract(target, number, value);
- /* Write program to move data into s0. */
+ if (result != ERROR_OK && target->state == TARGET_HALTED)
+ result = register_write_progbuf(target, number, value);
- uint64_t mstatus;
- if (prep_for_register_access(target, &mstatus, number) != ERROR_OK)
- return ERROR_FAIL;
+ if (cleanup_after_register_access(target, mstatus, number) != ERROR_OK)
+ return ERROR_FAIL;
- if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
- if (riscv_supports_extension(target, 'D')
- && riscv_xlen(target) < 64) {
- /* There are no instructions to move all the bits from a
- * register, so we need to use some scratch RAM. */
- riscv_program_insert(&program, fsd(number - GDB_REGNO_FPR0, S0,
- 0));
+ if (result == ERROR_OK)
+ LOG_TARGET_DEBUG(target, "%s <- 0x%" PRIx64, gdb_regno_name(number),
+ value);
- if (scratch_reserve(target, &scratch, &program, 8) != ERROR_OK)
- return ERROR_FAIL;
- use_scratch = true;
+ return result;
+}
- if (register_write_direct(target, GDB_REGNO_S0,
- scratch.hart_address) != ERROR_OK) {
- scratch_release(target, &scratch);
- return ERROR_FAIL;
- }
- } else if (riscv_supports_extension(target, 'D')) {
- riscv_program_insert(&program, fmv_x_d(S0, number - GDB_REGNO_FPR0));
- } else {
- riscv_program_insert(&program, fmv_x_w(S0, number - GDB_REGNO_FPR0));
- }
- } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
- riscv_program_csrr(&program, S0, number);
- } else {
- LOG_ERROR("Unsupported register: %s", gdb_regno_name(number));
- return ERROR_FAIL;
- }
+/** Actually read registers from the target right now. */
+static int register_read_direct(struct target *target, riscv_reg_t *value,
+ enum gdb_regno number)
+{
+ LOG_TARGET_DEBUG(target, "Reading %s", gdb_regno_name(number));
- /* Execute program. */
- result = riscv_program_exec(&program, target);
- /* Don't message on error. Probably the register doesn't exist. */
+ if (target->state != TARGET_HALTED)
+ return register_read_abstract(target, value, number);
- if (use_scratch) {
- result = scratch_read64(target, &scratch, value);
- scratch_release(target, &scratch);
- if (result != ERROR_OK)
- return result;
- } else {
- /* Read S0 */
- if (register_read_direct(target, value, GDB_REGNO_S0) != ERROR_OK)
- return ERROR_FAIL;
- }
+ riscv_reg_t mstatus;
- if (cleanup_after_register_access(target, mstatus, number) != ERROR_OK)
- return ERROR_FAIL;
+ if (prep_for_register_access(target, &mstatus, number) != ERROR_OK)
+ return ERROR_FAIL;
- /* Restore S0. */
- if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
- return ERROR_FAIL;
- }
+ int result = register_read_abstract(target, value, number);
- if (result == ERROR_OK) {
- LOG_DEBUG("{%d} %s = 0x%" PRIx64, riscv_current_hartid(target),
- gdb_regno_name(number), *value);
- }
+ if (result != ERROR_OK && target->state == TARGET_HALTED)
+ result = register_read_progbuf(target, value, number);
+
+ if (cleanup_after_register_access(target, mstatus, number) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (result == ERROR_OK)
+ LOG_TARGET_DEBUG(target, "%s = 0x%" PRIx64, gdb_regno_name(number),
+ *value);
return result;
}
@@ -1513,6 +1570,95 @@ static int wait_for_authbusy(struct target *target, uint32_t *dmstatus)
return ERROR_OK;
}
+static int set_dcsr_ebreak(struct target *target, bool step)
+{
+ LOG_TARGET_DEBUG(target, "Set dcsr.ebreak*");
+
+ if (dm013_select_target(target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ RISCV013_INFO(info);
+ riscv_reg_t original_dcsr, dcsr;
+ /* We want to twiddle some bits in the debug CSR so debugging works. */
+ if (riscv_get_register(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK)
+ return ERROR_FAIL;
+ original_dcsr = dcsr;
+ dcsr = set_field(dcsr, CSR_DCSR_STEP, step);
+ dcsr = set_field(dcsr, CSR_DCSR_EBREAKM, riscv_ebreakm);
+ dcsr = set_field(dcsr, CSR_DCSR_EBREAKS, riscv_ebreaks && riscv_supports_extension(target, 'S'));
+ dcsr = set_field(dcsr, CSR_DCSR_EBREAKU, riscv_ebreaku && riscv_supports_extension(target, 'U'));
+ dcsr = set_field(dcsr, CSR_DCSR_EBREAKVS, riscv_ebreaku && riscv_supports_extension(target, 'H'));
+ dcsr = set_field(dcsr, CSR_DCSR_EBREAKVU, riscv_ebreaku && riscv_supports_extension(target, 'H'));
+ if (dcsr != original_dcsr &&
+ riscv_set_register(target, GDB_REGNO_DCSR, dcsr) != ERROR_OK)
+ return ERROR_FAIL;
+ info->dcsr_ebreak_is_set = true;
+ return ERROR_OK;
+}
+
+static int halt_set_dcsr_ebreak(struct target *target)
+{
+ RISCV_INFO(r);
+ RISCV013_INFO(info);
+ LOG_TARGET_DEBUG(target, "Halt to set DCSR.ebreak*");
+
+ /* Remove this hart from the halt group. This won't work on all targets
+ * because the debug spec allows halt groups to be hard-coded, but I
+ * haven't actually encountered those in the wild yet.
+ *
+ * There is a possible race condition when another hart halts, and
+ * this one is expected to also halt because it's supposed to be in the
+ * same halt group. Or when this hart is halted when that happens.
+ *
+ * A better solution might be to leave the halt groups alone, and track
+ * why we're halting when a halt occurs. When there are halt groups,
+ * that leads to extra halting if not all harts need to set dcsr.ebreak
+ * at the same time. It also makes for more complicated code.
+ *
+ * The perfect solution would be Quick Access, but I'm not aware of any
+ * hardware that implements it.
+ *
+ * We don't need a perfect solution, because we only get here when a
+ * hart spontaneously resets, or when it powers down and back up again.
+ * Those are both relatively rare. (At least I hope so. Maybe some
+ * design just powers each hart down for 90ms out of every 100ms)
+ */
+
+
+ if (info->haltgroup_supported) {
+ bool supported;
+ if (set_group(target, &supported, 0, HALT_GROUP) != ERROR_OK)
+ return ERROR_FAIL;
+ if (!supported)
+ LOG_TARGET_ERROR(target, "Couldn't place hart in halt group 0. "
+ "Some harts may be unexpectedly halted.");
+ }
+
+ int result = ERROR_OK;
+
+ r->prepped = true;
+ if (riscv013_halt_go(target) != ERROR_OK ||
+ set_dcsr_ebreak(target, false) != ERROR_OK ||
+ riscv013_step_or_resume_current_hart(target, false) != ERROR_OK) {
+ result = ERROR_FAIL;
+ } else {
+ target->state = TARGET_RUNNING;
+ target->debug_reason = DBG_REASON_NOTHALTED;
+ }
+
+ /* Add it back to the halt group. */
+ if (info->haltgroup_supported) {
+ bool supported;
+ if (set_group(target, &supported, target->smp, HALT_GROUP) != ERROR_OK)
+ return ERROR_FAIL;
+ if (!supported)
+ LOG_TARGET_ERROR(target, "Couldn't place hart back in halt group %d. "
+ "Some harts may be unexpectedly halted.", target->smp);
+ }
+
+ return result;
+}
+
/*** OpenOCD target functions. ***/
static void deinit_target(struct target *target)
@@ -1527,33 +1673,20 @@ static void deinit_target(struct target *target)
info->version_specific = NULL;
}
-static int set_haltgroup(struct target *target, bool *supported)
+static int set_group(struct target *target, bool *supported, unsigned int group,
+ grouptype_t grouptype)
{
- uint32_t write = set_field(DM_DMCS2_HGWRITE, DM_DMCS2_GROUP, target->smp);
- if (dmi_write(target, DM_DMCS2, write) != ERROR_OK)
+ uint32_t write_val = DM_DMCS2_HGWRITE;
+ assert(group <= 31);
+ write_val = set_field(write_val, DM_DMCS2_GROUP, group);
+ write_val = set_field(write_val, DM_DMCS2_GROUPTYPE, (grouptype == HALT_GROUP) ? 0 : 1);
+ if (dmi_write(target, DM_DMCS2, write_val) != ERROR_OK)
return ERROR_FAIL;
- uint32_t read;
- if (dmi_read(target, &read, DM_DMCS2) != ERROR_OK)
+ uint32_t read_val;
+ if (dmi_read(target, &read_val, DM_DMCS2) != ERROR_OK)
return ERROR_FAIL;
- *supported = get_field(read, DM_DMCS2_GROUP) == (unsigned)target->smp;
- return ERROR_OK;
-}
-
-static int discover_vlenb(struct target *target)
-{
- RISCV_INFO(r);
- riscv_reg_t vlenb;
-
- if (register_read(target, &vlenb, GDB_REGNO_VLENB) != ERROR_OK) {
- LOG_WARNING("Couldn't read vlenb for %s; vector register access won't work.",
- target_name(target));
- r->vlenb = 0;
- return ERROR_OK;
- }
- r->vlenb = vlenb;
-
- LOG_INFO("Vector support with vlenb=%d", r->vlenb);
-
+ if (supported)
+ *supported = (get_field(read_val, DM_DMCS2_GROUP) == group);
return ERROR_OK;
}
@@ -1573,8 +1706,8 @@ static int examine(struct target *target)
return ERROR_FAIL;
}
if (get_field(dtmcontrol, DTM_DTMCS_VERSION) != 1) {
- LOG_ERROR("Unsupported DTM version %d. (dtmcontrol=0x%x)",
- get_field(dtmcontrol, DTM_DTMCS_VERSION), dtmcontrol);
+ LOG_ERROR("[%s] Unsupported DTM version %d. (dtmcontrol=0x%x)",
+ target_name(target), get_field(dtmcontrol, DTM_DTMCS_VERSION), dtmcontrol);
return ERROR_FAIL;
}
@@ -1593,13 +1726,21 @@ static int examine(struct target *target)
dmi_write(target, DM_DMCONTROL, DM_DMCONTROL_DMACTIVE);
dm->was_reset = true;
}
+ /* We're here because we're uncertain about the state of the target. That
+ * includes our progbuf cache. */
+ riscv013_invalidate_cached_debug_buffer(target);
dmi_write(target, DM_DMCONTROL, DM_DMCONTROL_HARTSELLO |
DM_DMCONTROL_HARTSELHI | DM_DMCONTROL_DMACTIVE |
DM_DMCONTROL_HASEL);
+ dm->current_hartid = HART_INDEX_UNKNOWN;
uint32_t dmcontrol;
if (dmi_read(target, &dmcontrol, DM_DMCONTROL) != ERROR_OK)
return ERROR_FAIL;
+ /* Ensure the HART_INDEX_UNKNOWN is flushed out */
+ if (dm013_select_hart(target, 0) != ERROR_OK)
+ return ERROR_FAIL;
+
if (!get_field(dmcontrol, DM_DMCONTROL_DMACTIVE)) {
LOG_ERROR("Debug Module did not become active. dmcontrol=0x%x",
@@ -1642,11 +1783,7 @@ static int examine(struct target *target)
LOG_ERROR("Debugger is not authenticated to target Debug Module. "
"(dmstatus=0x%x). Use `riscv authdata_read` and "
"`riscv authdata_write` commands to authenticate.", dmstatus);
- /* If we return ERROR_FAIL here, then in a multicore setup the next
- * core won't be examined, which means we won't set up the
- * authentication commands for them, which means the config script
- * needs to be a lot more complex. */
- return ERROR_OK;
+ return ERROR_FAIL;
}
if (dmi_read(target, &info->sbcs, DM_SBCS) != ERROR_OK)
@@ -1659,7 +1796,8 @@ static int examine(struct target *target)
info->datacount = get_field(abstractcs, DM_ABSTRACTCS_DATACOUNT);
info->progbufsize = get_field(abstractcs, DM_ABSTRACTCS_PROGBUFSIZE);
- LOG_INFO("datacount=%d progbufsize=%d", info->datacount, info->progbufsize);
+ LOG_INFO("[%s] datacount=%d progbufsize=%d", target_name(target),
+ info->datacount, info->progbufsize);
RISCV_INFO(r);
r->impebreak = get_field(dmstatus, DM_DMSTATUS_IMPEBREAK);
@@ -1681,8 +1819,7 @@ static int examine(struct target *target)
/* Before doing anything else we must first enumerate the harts. */
if (dm->hart_count < 0) {
for (int i = 0; i < MIN(RISCV_MAX_HARTS, 1 << info->hartsellen); ++i) {
- r->current_hartid = i;
- if (riscv013_select_current_hart(target) != ERROR_OK)
+ if (dm013_select_hart(target, i) != ERROR_OK)
return ERROR_FAIL;
uint32_t s;
@@ -1694,14 +1831,12 @@ static int examine(struct target *target)
if (get_field(s, DM_DMSTATUS_ANYHAVERESET))
dmi_write(target, DM_DMCONTROL,
- set_hartsel(DM_DMCONTROL_DMACTIVE | DM_DMCONTROL_ACKHAVERESET, i));
+ set_dmcontrol_hartsel(DM_DMCONTROL_DMACTIVE | DM_DMCONTROL_ACKHAVERESET, i));
}
LOG_DEBUG("Detected %d harts.", dm->hart_count);
}
- r->current_hartid = target->coreid;
-
if (dm->hart_count == 0) {
LOG_ERROR("No harts found!");
return ERROR_FAIL;
@@ -1710,56 +1845,123 @@ static int examine(struct target *target)
/* Don't call any riscv_* functions until after we've counted the number of
* cores and initialized registers. */
- if (riscv013_select_current_hart(target) != ERROR_OK)
+ if (dm013_select_hart(target, info->index) != ERROR_OK)
return ERROR_FAIL;
- bool halted = riscv_is_halted(target);
+ enum riscv_hart_state state;
+ if (riscv_get_hart_state(target, &state) != ERROR_OK)
+ return ERROR_FAIL;
+ bool halted = (state == RISCV_STATE_HALTED);
if (!halted) {
+ r->prepped = true;
if (riscv013_halt_go(target) != ERROR_OK) {
- LOG_ERROR("Fatal: Hart %d failed to halt during examine()", r->current_hartid);
+ LOG_TARGET_ERROR(target, "Fatal: Hart %d failed to halt during examine()",
+ info->index);
return ERROR_FAIL;
}
+ target->state = TARGET_HALTED;
+ target->debug_reason = DBG_REASON_DBGRQ;
+ }
+ /* FIXME: This is needed since register_read_direct relies on target->state
+ * to work correctly, so, if target->state does not represent current state
+ * of target, e.g. if a target is halted, but target->state is
+ * TARGET_UNKNOWN, it can fail early, (e.g. accessing registers via program
+ * buffer can not be done atomically on a running hart becuse mstatus can't
+ * be prepared for a register access and then restored)
+ * See https://github.com/riscv/riscv-openocd/pull/842#discussion_r1179414089
+ */
+ const enum target_state saved_tgt_state = target->state;
+ const enum target_debug_reason saved_dbg_reason = target->debug_reason;
+ if (target->state != TARGET_HALTED) {
+ target->state = TARGET_HALTED;
+ target->debug_reason = DBG_REASON_DBGRQ;
}
/* Without knowing anything else we can at least mess with the
- * program buffer. */
+ * program buffer. */
r->debug_buffer_size = info->progbufsize;
- int result = register_read_abstract(target, NULL, GDB_REGNO_S0, 64);
+ int result = register_read_abstract_with_size(target, NULL, GDB_REGNO_S0, 64);
if (result == ERROR_OK)
r->xlen = 64;
else
r->xlen = 32;
- if (register_read(target, &r->misa, GDB_REGNO_MISA)) {
- LOG_ERROR("Fatal: Failed to read MISA from hart %d.", r->current_hartid);
+ /* Save s0 and s1. The register cache hasn't be initialized yet so we
+ * need to take care of this manually. */
+ uint64_t s0, s1;
+ if (register_read_abstract(target, &s0, GDB_REGNO_S0) != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Fatal: Failed to read s0.");
+ return ERROR_FAIL;
+ }
+ if (register_read_abstract(target, &s1, GDB_REGNO_S1) != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Fatal: Failed to read s1.");
return ERROR_FAIL;
}
- if (riscv_supports_extension(target, 'V')) {
- if (discover_vlenb(target) != ERROR_OK)
- return ERROR_FAIL;
+ if (register_read_direct(target, &r->misa, GDB_REGNO_MISA)) {
+ LOG_TARGET_ERROR(target, "Fatal: Failed to read MISA.");
+ return ERROR_FAIL;
+ }
+
+ uint64_t value;
+ if (register_read_direct(target, &value, GDB_REGNO_VLENB) != ERROR_OK) {
+ if (riscv_supports_extension(target, 'V'))
+ LOG_TARGET_WARNING(target, "Couldn't read vlenb; vector register access won't work.");
+ r->vlenb = 0;
+ } else {
+ r->vlenb = value;
+ LOG_TARGET_INFO(target, "Vector support with vlenb=%d", r->vlenb);
+ }
+
+ if (register_read_direct(target, &value, GDB_REGNO_MTOPI) == ERROR_OK) {
+ r->mtopi_readable = true;
+
+ if (register_read_direct(target, &value, GDB_REGNO_MTOPEI) == ERROR_OK) {
+ LOG_TARGET_INFO(target, "S?aia detected with IMSIC");
+ r->mtopei_readable = true;
+ } else {
+ r->mtopei_readable = false;
+ LOG_TARGET_INFO(target, "S?aia detected without IMSIC");
+ }
+ } else {
+ r->mtopi_readable = false;
+ }
+
+ /* Display this as early as possible to help people who are using
+ * really slow simulators. */
+ LOG_TARGET_DEBUG(target, " XLEN=%d, misa=0x%" PRIx64, r->xlen, r->misa);
+
+ /* Restore s0 and s1. */
+ if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Fatal: Failed to write back s0.");
+ return ERROR_FAIL;
+ }
+ if (register_write_direct(target, GDB_REGNO_S1, s1) != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Fatal: Failed to write back s1.");
+ return ERROR_FAIL;
}
/* Now init registers based on what we discovered. */
if (riscv_init_registers(target) != ERROR_OK)
return ERROR_FAIL;
- /* Display this as early as possible to help people who are using
- * really slow simulators. */
- LOG_DEBUG(" hart %d: XLEN=%d, misa=0x%" PRIx64, r->current_hartid, r->xlen,
- r->misa);
+ if (set_dcsr_ebreak(target, false) != ERROR_OK)
+ return ERROR_FAIL;
- if (!halted)
- riscv013_step_or_resume_current_hart(target, false, false);
+ target->state = saved_tgt_state;
+ target->debug_reason = saved_dbg_reason;
- target_set_examined(target);
+ if (!halted) {
+ riscv013_step_or_resume_current_hart(target, false);
+ target->state = TARGET_RUNNING;
+ target->debug_reason = DBG_REASON_NOTHALTED;
+ }
if (target->smp) {
- bool haltgroup_supported;
- if (set_haltgroup(target, &haltgroup_supported) != ERROR_OK)
+ if (set_group(target, &info->haltgroup_supported, target->smp, HALT_GROUP) != ERROR_OK)
return ERROR_FAIL;
- if (haltgroup_supported)
+ if (info->haltgroup_supported)
LOG_INFO("Core %d made part of halt group %d.", target->coreid,
target->smp);
else
@@ -1770,10 +1972,9 @@ static int examine(struct target *target)
/* Some regression suites rely on seeing 'Examined RISC-V core' to know
* when they can connect with gdb/telnet.
* We will need to update those suites if we want to change that text. */
- LOG_INFO("Examined RISC-V core; found %d harts",
+ LOG_TARGET_INFO(target, "Examined RISC-V core; found %d harts",
riscv_count_harts(target));
- LOG_INFO(" hart %d: XLEN=%d, misa=0x%" PRIx64, r->current_hartid, r->xlen,
- r->misa);
+ LOG_TARGET_INFO(target, " XLEN=%d, misa=0x%" PRIx64, r->xlen, r->misa);
return ERROR_OK;
}
@@ -1815,7 +2016,7 @@ static int riscv013_authdata_write(struct target *target, uint32_t value, unsign
return ERROR_FAIL;
target_list_t *entry;
list_for_each_entry(entry, &dm->target_list, list) {
- if (examine(entry->target) != ERROR_OK)
+ if (target_examine_one(entry->target) != ERROR_OK)
result = ERROR_FAIL;
}
return result;
@@ -1901,82 +2102,109 @@ static COMMAND_HELPER(riscv013_print_info, struct target *target)
return 0;
}
-static int prep_for_vector_access(struct target *target, uint64_t *vtype,
- uint64_t *vl, unsigned *debug_vl)
+static int try_set_vsew(struct target *target, unsigned int *debug_vsew)
{
RISCV_INFO(r);
- /* TODO: this continuous save/restore is terrible for performance. */
- /* Write vtype and vl. */
- unsigned encoded_vsew;
- switch (riscv_xlen(target)) {
- case 32:
- encoded_vsew = 2;
- break;
- case 64:
- encoded_vsew = 3;
- break;
- default:
- LOG_ERROR("Unsupported xlen: %d", riscv_xlen(target));
+ unsigned int encoded_vsew =
+ (riscv_xlen(target) == 64 && r->vsew64_supported != YNM_NO) ? 3 : 2;
+
+ /* Set standard element width to match XLEN, for vmv instruction to move
+ * the least significant bits into a GPR.
+ */
+ if (riscv_write_register(target, GDB_REGNO_VTYPE, encoded_vsew << 3) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (encoded_vsew == 3 && r->vsew64_supported == YNM_MAYBE) {
+ /* Check that it's supported. */
+ riscv_reg_t vtype;
+
+ if (riscv_get_register(target, &vtype, GDB_REGNO_VTYPE) != ERROR_OK)
return ERROR_FAIL;
+ if (vtype >> (riscv_xlen(target) - 1)) {
+ r->vsew64_supported = YNM_NO;
+ /* Try again. */
+ return try_set_vsew(target, debug_vsew);
+ }
+ r->vsew64_supported = YNM_YES;
}
+ *debug_vsew = encoded_vsew == 3 ? 64 : 32;
+ return ERROR_OK;
+}
- /* Save vtype and vl. */
- if (register_read(target, vtype, GDB_REGNO_VTYPE) != ERROR_OK)
+static int prep_for_vector_access(struct target *target,
+ riscv_reg_t *orig_mstatus, riscv_reg_t *orig_vtype, riscv_reg_t *orig_vl,
+ unsigned int *debug_vl, unsigned int *debug_vsew)
+{
+ assert(orig_mstatus);
+ assert(orig_vtype);
+ assert(orig_vl);
+ assert(debug_vl);
+ assert(debug_vsew);
+
+ RISCV_INFO(r);
+ if (target->state != TARGET_HALTED) {
+ LOG_TARGET_ERROR(target,
+ "Unable to access vector register: target not halted");
return ERROR_FAIL;
- if (register_read(target, vl, GDB_REGNO_VL) != ERROR_OK)
+ }
+ if (prep_for_register_access(target, orig_mstatus, GDB_REGNO_VL) != ERROR_OK)
return ERROR_FAIL;
- if (register_write_direct(target, GDB_REGNO_VTYPE, encoded_vsew << 3) != ERROR_OK)
+ /* Save vtype and vl. */
+ if (riscv_get_register(target, orig_vtype, GDB_REGNO_VTYPE) != ERROR_OK)
return ERROR_FAIL;
- *debug_vl = DIV_ROUND_UP(r->vlenb * 8, riscv_xlen(target));
- if (register_write_direct(target, GDB_REGNO_VL, *debug_vl) != ERROR_OK)
+ if (riscv_get_register(target, orig_vl, GDB_REGNO_VL) != ERROR_OK)
return ERROR_FAIL;
- return ERROR_OK;
+ if (try_set_vsew(target, debug_vsew) != ERROR_OK)
+ return ERROR_FAIL;
+ /* Set the number of elements to be updated with results from a vector
+ * instruction, for the vslide1down instruction.
+ * Set it so the entire V register is updated. */
+ *debug_vl = DIV_ROUND_UP(r->vlenb * 8, *debug_vsew);
+ return riscv_write_register(target, GDB_REGNO_VL, *debug_vl);
}
-static int cleanup_after_vector_access(struct target *target, uint64_t vtype,
- uint64_t vl)
+static int cleanup_after_vector_access(struct target *target,
+ riscv_reg_t mstatus, riscv_reg_t vtype, riscv_reg_t vl)
{
/* Restore vtype and vl. */
- if (register_write_direct(target, GDB_REGNO_VTYPE, vtype) != ERROR_OK)
+ if (riscv_write_register(target, GDB_REGNO_VTYPE, vtype) != ERROR_OK)
return ERROR_FAIL;
- if (register_write_direct(target, GDB_REGNO_VL, vl) != ERROR_OK)
+ if (riscv_write_register(target, GDB_REGNO_VL, vl) != ERROR_OK)
return ERROR_FAIL;
- return ERROR_OK;
+ return cleanup_after_register_access(target, mstatus, GDB_REGNO_VL);
}
static int riscv013_get_register_buf(struct target *target,
- uint8_t *value, int regno)
+ uint8_t *value, enum gdb_regno regno)
{
assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31);
- if (riscv_select_current_hart(target) != ERROR_OK)
+ if (dm013_select_target(target) != ERROR_OK)
return ERROR_FAIL;
- riscv_reg_t s0;
- if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
+ if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
- uint64_t mstatus;
- if (prep_for_register_access(target, &mstatus, regno) != ERROR_OK)
- return ERROR_FAIL;
+ riscv_reg_t mstatus, vtype, vl;
+ unsigned int debug_vl, debug_vsew;
- uint64_t vtype, vl;
- unsigned debug_vl;
- if (prep_for_vector_access(target, &vtype, &vl, &debug_vl) != ERROR_OK)
+ if (prep_for_vector_access(target, &mstatus, &vtype, &vl,
+ &debug_vl, &debug_vsew) != ERROR_OK)
return ERROR_FAIL;
- unsigned vnum = regno - GDB_REGNO_V0;
- unsigned xlen = riscv_xlen(target);
-
- struct riscv_program program;
- riscv_program_init(&program, target);
- riscv_program_insert(&program, vmv_x_s(S0, vnum));
- riscv_program_insert(&program, vslide1down_vx(vnum, vnum, S0, true));
+ unsigned int vnum = regno - GDB_REGNO_V0;
int result = ERROR_OK;
- for (unsigned i = 0; i < debug_vl; i++) {
+ for (unsigned int i = 0; i < debug_vl; i++) {
+ /* Can't reuse the same program because riscv_program_exec() adds
+ * ebreak to the end every time. */
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+ riscv_program_insert(&program, vmv_x_s(S0, vnum));
+ riscv_program_insert(&program, vslide1down_vx(vnum, vnum, S0, true));
+
/* Executing the program might result in an exception if there is some
* issue with the vector implementation/instructions we're using. If that
* happens, attempt to restore as usual. We may have clobbered the
@@ -1985,69 +2213,58 @@ static int riscv013_get_register_buf(struct target *target,
* so messed up that attempting to restore isn't going to help. */
result = riscv_program_exec(&program, target);
if (result == ERROR_OK) {
- uint64_t v;
+ riscv_reg_t v;
if (register_read_direct(target, &v, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
- buf_set_u64(value, xlen * i, xlen, v);
+ buf_set_u64(value, debug_vsew * i, debug_vsew, v);
} else {
+ LOG_TARGET_ERROR(target,
+ "Failed to execute vmv/vslide1down while reading %s",
+ gdb_regno_name(regno));
break;
}
}
- if (cleanup_after_vector_access(target, vtype, vl) != ERROR_OK)
- return ERROR_FAIL;
-
- if (cleanup_after_register_access(target, mstatus, regno) != ERROR_OK)
- return ERROR_FAIL;
- if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
+ if (cleanup_after_vector_access(target, mstatus, vtype, vl) != ERROR_OK)
return ERROR_FAIL;
return result;
}
static int riscv013_set_register_buf(struct target *target,
- int regno, const uint8_t *value)
+ enum gdb_regno regno, const uint8_t *value)
{
assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31);
- if (riscv_select_current_hart(target) != ERROR_OK)
+ if (dm013_select_target(target) != ERROR_OK)
return ERROR_FAIL;
- riscv_reg_t s0;
- if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
+ if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
- uint64_t mstatus;
- if (prep_for_register_access(target, &mstatus, regno) != ERROR_OK)
- return ERROR_FAIL;
+ riscv_reg_t mstatus, vtype, vl;
+ unsigned int debug_vl, debug_vsew;
- uint64_t vtype, vl;
- unsigned debug_vl;
- if (prep_for_vector_access(target, &vtype, &vl, &debug_vl) != ERROR_OK)
+ if (prep_for_vector_access(target, &mstatus, &vtype, &vl,
+ &debug_vl, &debug_vsew) != ERROR_OK)
return ERROR_FAIL;
- unsigned vnum = regno - GDB_REGNO_V0;
- unsigned xlen = riscv_xlen(target);
+ unsigned int vnum = regno - GDB_REGNO_V0;
struct riscv_program program;
riscv_program_init(&program, target);
riscv_program_insert(&program, vslide1down_vx(vnum, vnum, S0, true));
int result = ERROR_OK;
- for (unsigned i = 0; i < debug_vl; i++) {
+ for (unsigned int i = 0; i < debug_vl; i++) {
if (register_write_direct(target, GDB_REGNO_S0,
- buf_get_u64(value, xlen * i, xlen)) != ERROR_OK)
+ buf_get_u64(value, debug_vsew * i, debug_vsew)) != ERROR_OK)
return ERROR_FAIL;
result = riscv_program_exec(&program, target);
if (result != ERROR_OK)
break;
}
- if (cleanup_after_vector_access(target, vtype, vl) != ERROR_OK)
- return ERROR_FAIL;
-
- if (cleanup_after_register_access(target, mstatus, regno) != ERROR_OK)
- return ERROR_FAIL;
- if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
+ if (cleanup_after_vector_access(target, mstatus, vtype, vl) != ERROR_OK)
return ERROR_FAIL;
return result;
@@ -2182,7 +2399,7 @@ static int sample_memory_bus_v1(struct target *target,
sbcs_write |= DM_SBCS_SBREADONDATA;
sbcs_write |= sb_sbaccess(config->bucket[i].size_bytes);
if (!sbcs_valid || sbcs_write != sbcs) {
- riscv_batch_add_dmi_write(batch, DM_SBCS, sbcs_write);
+ riscv_batch_add_dmi_write(batch, DM_SBCS, sbcs_write, true);
sbcs = sbcs_write;
sbcs_valid = true;
}
@@ -2191,13 +2408,13 @@ static int sample_memory_bus_v1(struct target *target,
(!sbaddress1_valid ||
sbaddress1 != config->bucket[i].address >> 32)) {
sbaddress1 = config->bucket[i].address >> 32;
- riscv_batch_add_dmi_write(batch, DM_SBADDRESS1, sbaddress1);
+ riscv_batch_add_dmi_write(batch, DM_SBADDRESS1, sbaddress1, true);
sbaddress1_valid = true;
}
if (!sbaddress0_valid ||
sbaddress0 != (config->bucket[i].address & 0xffffffff)) {
sbaddress0 = config->bucket[i].address;
- riscv_batch_add_dmi_write(batch, DM_SBADDRESS0, sbaddress0);
+ riscv_batch_add_dmi_write(batch, DM_SBADDRESS0, sbaddress0, true);
sbaddress0_valid = true;
}
if (config->bucket[i].size_bytes > 4)
@@ -2213,16 +2430,27 @@ static int sample_memory_bus_v1(struct target *target,
break;
}
- size_t sbcs_key = riscv_batch_add_dmi_read(batch, DM_SBCS);
+ size_t sbcs_read_index = riscv_batch_add_dmi_read(batch, DM_SBCS);
int result = batch_run(target, batch);
- if (result != ERROR_OK)
+ if (result != ERROR_OK) {
+ riscv_batch_free(batch);
return result;
+ }
+
+ /* Discard the batch when we encounter a busy state on the DMI level.
+ * It's too much hassle to try to recover partial data. We'll try again
+ * with a larger DMI delay. */
+ unsigned int sbcs_read_op = riscv_batch_get_dmi_read_op(batch, sbcs_read_index);
+ if (sbcs_read_op == DTM_DMI_OP_BUSY) {
+ increase_dmi_busy_delay(target);
+ continue;
+ }
- uint32_t sbcs_read = riscv_batch_get_dmi_read_data(batch, sbcs_key);
+ uint32_t sbcs_read = riscv_batch_get_dmi_read_data(batch, sbcs_read_index);
if (get_field(sbcs_read, DM_SBCS_SBBUSYERROR)) {
- /* Discard this batch (too much hassle to try to recover partial
- * data) and try again with a larger delay. */
+ /* Discard this batch when we encounter "busy error" state on the System Bus level.
+ * We'll try next time with a larger System Bus read delay. */
info->bus_master_read_delay += info->bus_master_read_delay / 10 + 1;
dmi_write(target, DM_SBCS, sbcs_read | DM_SBCS_SBBUSYERROR | DM_SBCS_SBERROR);
riscv_batch_free(batch);
@@ -2235,15 +2463,15 @@ static int sample_memory_bus_v1(struct target *target,
return ERROR_FAIL;
}
- unsigned int read = 0;
+ unsigned int read_count = 0;
for (unsigned int n = 0; n < repeat; n++) {
for (unsigned int i = 0; i < ARRAY_SIZE(config->bucket); i++) {
if (config->bucket[i].enabled) {
assert(i < RISCV_SAMPLE_BUF_TIMESTAMP_BEFORE);
uint64_t value = 0;
if (config->bucket[i].size_bytes > 4)
- value = ((uint64_t)riscv_batch_get_dmi_read_data(batch, read++)) << 32;
- value |= riscv_batch_get_dmi_read_data(batch, read++);
+ value = ((uint64_t)riscv_batch_get_dmi_read_data(batch, read_count++)) << 32;
+ value |= riscv_batch_get_dmi_read_data(batch, read_count++);
buf->buf[buf->used] = i;
buf_set_u64(buf->buf + buf->used + 1, 0, config->bucket[i].size_bytes * 8, value);
@@ -2269,6 +2497,69 @@ static int sample_memory(struct target *target,
return sample_memory_bus_v1(target, buf, config, until_ms);
}
+static int riscv013_get_hart_state(struct target *target, enum riscv_hart_state *state)
+{
+ RISCV013_INFO(info);
+ if (dm013_select_target(target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ uint32_t dmstatus;
+ if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
+ return ERROR_FAIL;
+ if (get_field(dmstatus, DM_DMSTATUS_ANYHAVERESET)) {
+ LOG_TARGET_INFO(target, "Hart unexpectedly reset!");
+ info->dcsr_ebreak_is_set = false;
+ /* TODO: Can we make this more obvious to eg. a gdb user? */
+ uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE |
+ DM_DMCONTROL_ACKHAVERESET;
+ dmcontrol = set_dmcontrol_hartsel(dmcontrol, info->index);
+ /* If we had been halted when we reset, request another halt. If we
+ * ended up running out of reset, then the user will (hopefully) get a
+ * message that a reset happened, that the target is running, and then
+ * that it is halted again once the request goes through.
+ */
+ if (target->state == TARGET_HALTED)
+ dmcontrol |= DM_DMCONTROL_HALTREQ;
+ dmi_write(target, DM_DMCONTROL, dmcontrol);
+ }
+ if (get_field(dmstatus, DM_DMSTATUS_ALLNONEXISTENT)) {
+ *state = RISCV_STATE_NON_EXISTENT;
+ return ERROR_OK;
+ }
+ if (get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL)) {
+ *state = RISCV_STATE_UNAVAILABLE;
+ return ERROR_OK;
+ }
+ if (get_field(dmstatus, DM_DMSTATUS_ALLHALTED)) {
+ *state = RISCV_STATE_HALTED;
+ return ERROR_OK;
+ }
+ if (get_field(dmstatus, DM_DMSTATUS_ALLRUNNING)) {
+ *state = RISCV_STATE_RUNNING;
+ return ERROR_OK;
+ }
+ LOG_TARGET_ERROR(target, "Couldn't determine state. dmstatus=0x%x", dmstatus);
+ return ERROR_FAIL;
+}
+
+static int handle_became_unavailable(struct target *target,
+ enum riscv_hart_state previous_riscv_state)
+{
+ RISCV013_INFO(info);
+ info->dcsr_ebreak_is_set = false;
+ return ERROR_OK;
+}
+
+static int tick(struct target *target)
+{
+ RISCV013_INFO(info);
+ if (!info->dcsr_ebreak_is_set &&
+ target->state == TARGET_RUNNING &&
+ target_was_examined(target))
+ return halt_set_dcsr_ebreak(target);
+ return ERROR_OK;
+}
+
static int init_target(struct command_context *cmd_ctx,
struct target *target)
{
@@ -2279,11 +2570,10 @@ static int init_target(struct command_context *cmd_ctx,
generic_info->set_register = &riscv013_set_register;
generic_info->get_register_buf = &riscv013_get_register_buf;
generic_info->set_register_buf = &riscv013_set_register_buf;
- generic_info->select_current_hart = &riscv013_select_current_hart;
- generic_info->is_halted = &riscv013_is_halted;
+ generic_info->select_target = &dm013_select_target;
+ generic_info->get_hart_state = &riscv013_get_hart_state;
generic_info->resume_go = &riscv013_resume_go;
generic_info->step_current_hart = &riscv013_step_current_hart;
- generic_info->on_halt = &riscv013_on_halt;
generic_info->resume_prep = &riscv013_resume_prep;
generic_info->halt_prep = &riscv013_halt_prep;
generic_info->halt_go = &riscv013_halt_go;
@@ -2292,6 +2582,7 @@ static int init_target(struct command_context *cmd_ctx,
generic_info->read_debug_buffer = &riscv013_read_debug_buffer;
generic_info->write_debug_buffer = &riscv013_write_debug_buffer;
generic_info->execute_debug_buffer = &riscv013_execute_debug_buffer;
+ generic_info->invalidate_cached_debug_buffer = &riscv013_invalidate_cached_debug_buffer;
generic_info->fill_dmi_write_u64 = &riscv013_fill_dmi_write_u64;
generic_info->fill_dmi_read_u64 = &riscv013_fill_dmi_read_u64;
generic_info->fill_dmi_nop_u64 = &riscv013_fill_dmi_nop_u64;
@@ -2304,6 +2595,10 @@ static int init_target(struct command_context *cmd_ctx,
generic_info->hart_count = &riscv013_hart_count;
generic_info->data_bits = &riscv013_data_bits;
generic_info->print_info = &riscv013_print_info;
+
+ generic_info->handle_became_unavailable = &handle_became_unavailable;
+ generic_info->tick = &tick;
+
if (!generic_info->version_specific) {
generic_info->version_specific = calloc(1, sizeof(riscv013_info_t));
if (!generic_info->version_specific)
@@ -2336,152 +2631,140 @@ static int init_target(struct command_context *cmd_ctx,
static int assert_reset(struct target *target)
{
- RISCV_INFO(r);
+ RISCV013_INFO(info);
+ int result;
select_dmi(target);
- uint32_t control_base = set_field(0, DM_DMCONTROL_DMACTIVE, 1);
-
if (target_has_event_action(target, TARGET_EVENT_RESET_ASSERT)) {
/* Run the user-supplied script if there is one. */
target_handle_event(target, TARGET_EVENT_RESET_ASSERT);
- } else if (target->rtos) {
- /* There's only one target, and OpenOCD thinks each hart is a thread.
- * We must reset them all. */
-
- /* TODO: Try to use hasel in dmcontrol */
-
- /* Set haltreq for each hart. */
- uint32_t control = set_hartsel(control_base, target->coreid);
- control = set_field(control, DM_DMCONTROL_HALTREQ,
- target->reset_halt ? 1 : 0);
- dmi_write(target, DM_DMCONTROL, control);
-
- /* Assert ndmreset */
- control = set_field(control, DM_DMCONTROL_NDMRESET, 1);
- dmi_write(target, DM_DMCONTROL, control);
-
} else {
- /* Reset just this hart. */
- uint32_t control = set_hartsel(control_base, r->current_hartid);
+ uint32_t control = set_field(0, DM_DMCONTROL_DMACTIVE, 1);
+ control = set_dmcontrol_hartsel(control, info->index);
control = set_field(control, DM_DMCONTROL_HALTREQ,
target->reset_halt ? 1 : 0);
control = set_field(control, DM_DMCONTROL_NDMRESET, 1);
- dmi_write(target, DM_DMCONTROL, control);
+ result = dmi_write(target, DM_DMCONTROL, control);
+ if (result != ERROR_OK)
+ return result;
}
target->state = TARGET_RESET;
- dm013_info_t *dm = get_dm(target);
- if (!dm)
- return ERROR_FAIL;
-
/* The DM might have gotten reset if OpenOCD called us in some reset that
* involves SRST being toggled. So clear our cache which may be out of
* date. */
- memset(dm->progbuf_cache, 0, sizeof(dm->progbuf_cache));
-
- return ERROR_OK;
+ return riscv013_invalidate_cached_debug_buffer(target);
}
static int deassert_reset(struct target *target)
{
- RISCV_INFO(r);
RISCV013_INFO(info);
- select_dmi(target);
+ int result;
+ select_dmi(target);
/* Clear the reset, but make sure haltreq is still set */
- uint32_t control = 0, control_haltreq;
+ uint32_t control = 0;
control = set_field(control, DM_DMCONTROL_DMACTIVE, 1);
- control_haltreq = set_field(control, DM_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0);
- dmi_write(target, DM_DMCONTROL,
- set_hartsel(control_haltreq, r->current_hartid));
+ control = set_field(control, DM_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0);
+ control = set_dmcontrol_hartsel(control, info->index);
+ result = dmi_write(target, DM_DMCONTROL, control);
+ if (result != ERROR_OK)
+ return result;
uint32_t dmstatus;
- int dmi_busy_delay = info->dmi_busy_delay;
+ const int orig_dmi_busy_delay = info->dmi_busy_delay;
time_t start = time(NULL);
-
- for (int i = 0; i < riscv_count_harts(target); ++i) {
- int index = i;
- if (target->rtos) {
- if (index != target->coreid)
- continue;
- dmi_write(target, DM_DMCONTROL,
- set_hartsel(control_haltreq, index));
- } else {
- index = r->current_hartid;
- }
-
- LOG_DEBUG("Waiting for hart %d to come out of reset.", index);
- while (1) {
- int result = dmstatus_read_timeout(target, &dmstatus, true,
+ LOG_TARGET_DEBUG(target, "Waiting for hart to come out of reset.");
+ do {
+ result = dmstatus_read_timeout(target, &dmstatus, true,
+ riscv_reset_timeout_sec);
+ if (result == ERROR_TIMEOUT_REACHED)
+ LOG_TARGET_ERROR(target, "Hart didn't complete a DMI read coming "
+ "out of reset in %ds; Increase the timeout with riscv "
+ "set_reset_timeout_sec.",
riscv_reset_timeout_sec);
- if (result == ERROR_TIMEOUT_REACHED)
- LOG_ERROR("Hart %d didn't complete a DMI read coming out of "
- "reset in %ds; Increase the timeout with riscv "
- "set_reset_timeout_sec.",
- index, riscv_reset_timeout_sec);
- if (result != ERROR_OK)
- return result;
- /* Certain debug modules, like the one in GD32VF103
- * MCUs, violate the specification's requirement that
- * each hart is in "exactly one of four states" and,
- * during reset, report harts as both unavailable and
- * halted/running. To work around this, we check for
- * the absence of the unavailable state rather than
- * the presence of any other state. */
- if (!get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL))
- break;
- if (time(NULL) - start > riscv_reset_timeout_sec) {
- LOG_ERROR("Hart %d didn't leave reset in %ds; "
- "dmstatus=0x%x; "
- "Increase the timeout with riscv set_reset_timeout_sec.",
- index, riscv_reset_timeout_sec, dmstatus);
- return ERROR_FAIL;
- }
- }
- target->state = TARGET_HALTED;
+ if (result != ERROR_OK)
+ return result;
- if (get_field(dmstatus, DM_DMSTATUS_ALLHAVERESET)) {
- /* Ack reset and clear DM_DMCONTROL_HALTREQ if previously set */
- dmi_write(target, DM_DMCONTROL,
- set_hartsel(control, index) |
- DM_DMCONTROL_ACKHAVERESET);
+ if (time(NULL) - start > riscv_reset_timeout_sec) {
+ LOG_TARGET_ERROR(target, "Hart didn't leave reset in %ds; "
+ "dmstatus=0x%x (allunavail=%s, allhavereset=%s); "
+ "Increase the timeout with riscv set_reset_timeout_sec.",
+ riscv_reset_timeout_sec, dmstatus,
+ get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL) ? "true" : "false",
+ get_field(dmstatus, DM_DMSTATUS_ALLHAVERESET) ? "true" : "false");
+ return ERROR_TIMEOUT_REACHED;
}
-
- if (!target->rtos)
- break;
+ /* Certain debug modules, like the one in GD32VF103
+ * MCUs, violate the specification's requirement that
+ * each hart is in "exactly one of four states" and,
+ * during reset, report harts as both unavailable and
+ * halted/running. To work around this, we check for
+ * the absence of the unavailable state rather than
+ * the presence of any other state. */
+ } while (get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL) &&
+ !get_field(dmstatus, DM_DMSTATUS_ALLHAVERESET));
+
+ info->dmi_busy_delay = orig_dmi_busy_delay;
+
+ if (target->reset_halt) {
+ target->state = TARGET_HALTED;
+ target->debug_reason = DBG_REASON_DBGRQ;
+ } else {
+ target->state = TARGET_RUNNING;
+ target->debug_reason = DBG_REASON_NOTHALTED;
}
- info->dmi_busy_delay = dmi_busy_delay;
- return ERROR_OK;
+ info->dcsr_ebreak_is_set = false;
+
+ /* Ack reset and clear DM_DMCONTROL_HALTREQ if previously set */
+ control = 0;
+ control = set_field(control, DM_DMCONTROL_DMACTIVE, 1);
+ control = set_field(control, DM_DMCONTROL_ACKHAVERESET, 1);
+ control = set_dmcontrol_hartsel(control, info->index);
+ return dmi_write(target, DM_DMCONTROL, control);
}
static int execute_fence(struct target *target)
{
+ if (dm013_select_target(target) != ERROR_OK)
+ return ERROR_FAIL;
+
/* FIXME: For non-coherent systems we need to flush the caches right
* here, but there's no ISA-defined way of doing that. */
- {
- struct riscv_program program;
- riscv_program_init(&program, target);
- riscv_program_fence_i(&program);
- riscv_program_fence(&program);
- int result = riscv_program_exec(&program, target);
- if (result != ERROR_OK)
- LOG_DEBUG("Unable to execute pre-fence");
- }
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+ riscv_program_fence_i(&program);
+ riscv_program_fence_rw_rw(&program);
+ int result = riscv_program_exec(&program, target);
+ if (result != ERROR_OK)
+ LOG_TARGET_DEBUG(target, "Unable to execute pre-fence");
return ERROR_OK;
}
-static void log_memory_access(target_addr_t address, uint64_t value,
- unsigned size_bytes, bool read)
+static void log_memory_access128(target_addr_t address, uint64_t value_h,
+ uint64_t value_l, bool is_read)
+{
+ if (debug_level < LOG_LVL_DEBUG)
+ return;
+
+ char fmt[80];
+ sprintf(fmt, "M[0x%" TARGET_PRIxADDR "] %ss 0x%%016" PRIx64 "%%016" PRIx64,
+ address, is_read ? "read" : "write");
+ LOG_DEBUG(fmt, value_h, value_l);
+}
+
+static void log_memory_access64(target_addr_t address, uint64_t value,
+ unsigned int size_bytes, bool is_read)
{
if (debug_level < LOG_LVL_DEBUG)
return;
char fmt[80];
sprintf(fmt, "M[0x%" TARGET_PRIxADDR "] %ss 0x%%0%d" PRIx64,
- address, read ? "read" : "write", size_bytes * 2);
+ address, is_read ? "read" : "write", size_bytes * 2);
switch (size_bytes) {
case 1:
value &= 0xff;
@@ -2499,23 +2782,35 @@ static void log_memory_access(target_addr_t address, uint64_t value,
}
LOG_DEBUG(fmt, value);
}
+static void log_memory_access(target_addr_t address, uint32_t *sbvalue,
+ unsigned int size_bytes, bool is_read)
+{
+ if (size_bytes == 16) {
+ uint64_t value_h = ((uint64_t)sbvalue[3] << 32) | sbvalue[2];
+ uint64_t value_l = ((uint64_t)sbvalue[1] << 32) | sbvalue[0];
+ log_memory_access128(address, value_h, value_l, is_read);
+ } else {
+ uint64_t value = ((uint64_t)sbvalue[1] << 32) | sbvalue[0];
+ log_memory_access64(address, value, size_bytes, is_read);
+ }
+}
/* Read the relevant sbdata regs depending on size, and put the results into
* buffer. */
static int read_memory_bus_word(struct target *target, target_addr_t address,
uint32_t size, uint8_t *buffer)
{
- uint32_t value;
int result;
+ uint32_t sbvalue[4] = { 0 };
static int sbdata[4] = { DM_SBDATA0, DM_SBDATA1, DM_SBDATA2, DM_SBDATA3 };
assert(size <= 16);
for (int i = (size - 1) / 4; i >= 0; i--) {
- result = dmi_op(target, &value, NULL, DMI_OP_READ, sbdata[i], 0, false, true);
+ result = dmi_op(target, &sbvalue[i], NULL, DMI_OP_READ, sbdata[i], 0, false, true);
if (result != ERROR_OK)
return result;
- buf_set_u32(buffer + i * 4, 0, 8 * MIN(size, 4), value);
- log_memory_access(address + i * 4, value, MIN(size, 4), true);
+ buf_set_u32(buffer + i * 4, 0, 8 * MIN(size, 4), sbvalue[i]);
}
+ log_memory_access(address, sbvalue, size, true);
return ERROR_OK;
}
@@ -2557,18 +2852,18 @@ static int modify_privilege(struct target *target, uint64_t *mstatus, uint64_t *
if (riscv_enable_virtual && has_sufficient_progbuf(target, 5)) {
/* Read DCSR */
uint64_t dcsr;
- if (register_read(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK)
+ if (register_read_direct(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK)
return ERROR_FAIL;
/* Read and save MSTATUS */
- if (register_read(target, mstatus, GDB_REGNO_MSTATUS) != ERROR_OK)
+ if (register_read_direct(target, mstatus, GDB_REGNO_MSTATUS) != ERROR_OK)
return ERROR_FAIL;
*mstatus_old = *mstatus;
/* If we come from m-mode with mprv set, we want to keep mpp */
- if (get_field(dcsr, DCSR_PRV) < 3) {
+ if (get_field(dcsr, CSR_DCSR_PRV) < 3) {
/* MPP = PRIV */
- *mstatus = set_field(*mstatus, MSTATUS_MPP, get_field(dcsr, DCSR_PRV));
+ *mstatus = set_field(*mstatus, MSTATUS_MPP, get_field(dcsr, CSR_DCSR_PRV));
/* MPRV = 1 */
*mstatus = set_field(*mstatus, MSTATUS_MPRV, 1);
@@ -2710,11 +3005,12 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
* to get it. */
static int sbdata[4] = {DM_SBDATA0, DM_SBDATA1, DM_SBDATA2, DM_SBDATA3};
+ uint32_t sbvalue[4] = {0};
assert(size <= 16);
target_addr_t next_read = address - 1;
+ int next_read_j = 0;
for (uint32_t i = (next_address - address) / size; i < count - 1; i++) {
for (int j = (size - 1) / 4; j >= 0; j--) {
- uint32_t value;
unsigned attempt = 0;
while (1) {
if (attempt++ > 100) {
@@ -2723,7 +3019,7 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
return ERROR_FAIL;
}
keep_alive();
- dmi_status_t status = dmi_scan(target, NULL, &value,
+ dmi_status_t status = dmi_scan(target, NULL, &sbvalue[next_read_j],
DMI_OP_READ, sbdata[j], 0, false);
if (status == DMI_STATUS_BUSY)
increase_dmi_busy_delay(target);
@@ -2733,16 +3029,19 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
return ERROR_FAIL;
}
if (next_read != address - 1) {
- buf_set_u32(buffer + next_read - address, 0, 8 * MIN(size, 4), value);
- log_memory_access(next_read, value, MIN(size, 4), true);
+ buf_set_u32(buffer + next_read - address, 0, 8 * MIN(size, 4), sbvalue[next_read_j]);
+ if (next_read_j == 0) {
+ log_memory_access(next_read, sbvalue, size, true);
+ memset(sbvalue, 0, size);
+ }
}
- next_read = address + i * size + j * 4;
+ next_read_j = j;
+ next_read = address + i * size + next_read_j * 4;
}
}
uint32_t sbcs_read = 0;
if (count > 1) {
- uint32_t value;
unsigned attempt = 0;
while (1) {
if (attempt++ > 100) {
@@ -2750,7 +3049,7 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
next_read);
return ERROR_FAIL;
}
- dmi_status_t status = dmi_scan(target, NULL, &value, DMI_OP_NOP, 0, 0, false);
+ dmi_status_t status = dmi_scan(target, NULL, &sbvalue[0], DMI_OP_NOP, 0, 0, false);
if (status == DMI_STATUS_BUSY)
increase_dmi_busy_delay(target);
else if (status == DMI_STATUS_SUCCESS)
@@ -2758,8 +3057,8 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
else
return ERROR_FAIL;
}
- buf_set_u32(buffer + next_read - address, 0, 8 * MIN(size, 4), value);
- log_memory_access(next_read, value, MIN(size, 4), true);
+ buf_set_u32(buffer + next_read - address, 0, 8 * MIN(size, 4), sbvalue[0]);
+ log_memory_access(next_read, sbvalue, size, true);
/* "Writes to sbcs while sbbusy is high result in undefined behavior.
* A debugger must not write to sbcs until it reads sbbusy as 0." */
@@ -2806,7 +3105,7 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
return ERROR_OK;
}
-static void log_mem_access_result(struct target *target, bool success, int method, bool read)
+static void log_mem_access_result(struct target *target, bool success, int method, bool is_read)
{
RISCV_INFO(r);
bool warn = false;
@@ -2815,7 +3114,7 @@ static void log_mem_access_result(struct target *target, bool success, int metho
/* Compose the message */
snprintf(msg, 60, "%s to %s memory via %s.",
success ? "Succeeded" : "Failed",
- read ? "read" : "write",
+ is_read ? "read" : "write",
(method == RISCV_MEM_ACCESS_PROGBUF) ? "program buffer" :
(method == RISCV_MEM_ACCESS_SYSBUS) ? "system bus" : "abstract access");
@@ -2842,37 +3141,37 @@ static void log_mem_access_result(struct target *target, bool success, int metho
}
static bool mem_should_skip_progbuf(struct target *target, target_addr_t address,
- uint32_t size, bool read, char **skip_reason)
+ uint32_t size, bool is_read, char **skip_reason)
{
assert(skip_reason);
if (!has_sufficient_progbuf(target, 3)) {
LOG_DEBUG("Skipping mem %s via progbuf - insufficient progbuf size.",
- read ? "read" : "write");
+ is_read ? "read" : "write");
*skip_reason = "skipped (insufficient progbuf)";
return true;
}
if (target->state != TARGET_HALTED) {
LOG_DEBUG("Skipping mem %s via progbuf - target not halted.",
- read ? "read" : "write");
+ is_read ? "read" : "write");
*skip_reason = "skipped (target not halted)";
return true;
}
if (riscv_xlen(target) < size * 8) {
LOG_DEBUG("Skipping mem %s via progbuf - XLEN (%d) is too short for %d-bit memory access.",
- read ? "read" : "write", riscv_xlen(target), size * 8);
+ is_read ? "read" : "write", riscv_xlen(target), size * 8);
*skip_reason = "skipped (XLEN too short)";
return true;
}
if (size > 8) {
LOG_DEBUG("Skipping mem %s via progbuf - unsupported size.",
- read ? "read" : "write");
+ is_read ? "read" : "write");
*skip_reason = "skipped (unsupported size)";
return true;
}
if ((sizeof(address) * 8 > riscv_xlen(target)) && (address >> riscv_xlen(target))) {
LOG_DEBUG("Skipping mem %s via progbuf - progbuf only supports %u-bit address.",
- read ? "read" : "write", riscv_xlen(target));
+ is_read ? "read" : "write", riscv_xlen(target));
*skip_reason = "skipped (too large address)";
return true;
}
@@ -2881,25 +3180,25 @@ static bool mem_should_skip_progbuf(struct target *target, target_addr_t address
}
static bool mem_should_skip_sysbus(struct target *target, target_addr_t address,
- uint32_t size, uint32_t increment, bool read, char **skip_reason)
+ uint32_t size, uint32_t increment, bool is_read, char **skip_reason)
{
assert(skip_reason);
RISCV013_INFO(info);
if (!sba_supports_access(target, size)) {
LOG_DEBUG("Skipping mem %s via system bus - unsupported size.",
- read ? "read" : "write");
+ is_read ? "read" : "write");
*skip_reason = "skipped (unsupported size)";
return true;
}
unsigned int sbasize = get_field(info->sbcs, DM_SBCS_SBASIZE);
if ((sizeof(address) * 8 > sbasize) && (address >> sbasize)) {
LOG_DEBUG("Skipping mem %s via system bus - sba only supports %u-bit address.",
- read ? "read" : "write", sbasize);
+ is_read ? "read" : "write", sbasize);
*skip_reason = "skipped (too large address)";
return true;
}
- if (read && increment != size && (get_field(info->sbcs, DM_SBCS_SBVERSION) == 0 || increment != 0)) {
+ if (is_read && increment != size && (get_field(info->sbcs, DM_SBCS_SBVERSION) == 0 || increment != 0)) {
LOG_DEBUG("Skipping mem read via system bus - "
"sba reads only support size==increment or also size==0 for sba v1.");
*skip_reason = "skipped (unsupported increment)";
@@ -2910,7 +3209,7 @@ static bool mem_should_skip_sysbus(struct target *target, target_addr_t address,
}
static bool mem_should_skip_abstract(struct target *target, target_addr_t address,
- uint32_t size, uint32_t increment, bool read, char **skip_reason)
+ uint32_t size, uint32_t increment, bool is_read, char **skip_reason)
{
assert(skip_reason);
@@ -2918,17 +3217,17 @@ static bool mem_should_skip_abstract(struct target *target, target_addr_t addres
/* TODO: Add 128b support if it's ever used. Involves modifying
read/write_abstract_arg() to work on two 64b values. */
LOG_DEBUG("Skipping mem %s via abstract access - unsupported size: %d bits",
- read ? "read" : "write", size * 8);
+ is_read ? "read" : "write", size * 8);
*skip_reason = "skipped (unsupported size)";
return true;
}
if ((sizeof(address) * 8 > riscv_xlen(target)) && (address >> riscv_xlen(target))) {
LOG_DEBUG("Skipping mem %s via abstract access - abstract access only supports %u-bit address.",
- read ? "read" : "write", riscv_xlen(target));
+ is_read ? "read" : "write", riscv_xlen(target));
*skip_reason = "skipped (too large address)";
return true;
}
- if (read && size != increment) {
+ if (is_read && size != increment) {
LOG_ERROR("Skipping mem read via abstract access - "
"abstract command reads only support size==increment.");
*skip_reason = "skipped (unsupported increment)";
@@ -3113,7 +3412,7 @@ static int read_memory_progbuf_inner(struct target *target, target_addr_t addres
return result;
if (increment == 0 &&
- register_write_direct(target, GDB_REGNO_S2, 0) != ERROR_OK)
+ register_write_direct(target, GDB_REGNO_A0, 0) != ERROR_OK)
return ERROR_FAIL;
uint32_t command = access_register_command(target, GDB_REGNO_S1,
@@ -3128,7 +3427,7 @@ static int read_memory_progbuf_inner(struct target *target, target_addr_t addres
if (register_read_direct(target, &value, GDB_REGNO_S1) != ERROR_OK)
return ERROR_FAIL;
buf_set_u64(buffer, 0, 8 * size, value);
- log_memory_access(address, value, size, true);
+ log_memory_access64(address, value, size, true);
return ERROR_OK;
}
@@ -3155,7 +3454,7 @@ static int read_memory_progbuf_inner(struct target *target, target_addr_t addres
* dm_data0 contains[read_addr-size*2]
*/
- struct riscv_batch *batch = riscv_batch_alloc(target, 32,
+ struct riscv_batch *batch = riscv_batch_alloc(target, RISCV_BATCH_ALLOC_SIZE,
info->dmi_busy_delay + info->ac_busy_delay);
if (!batch)
return ERROR_FAIL;
@@ -3215,7 +3514,7 @@ static int read_memory_progbuf_inner(struct target *target, target_addr_t addres
/* See how far we got, clobbering dmi_data0. */
if (increment == 0) {
uint64_t counter;
- result = register_read_direct(target, &counter, GDB_REGNO_S2);
+ result = register_read_direct(target, &counter, GDB_REGNO_A0);
next_index = counter;
} else {
uint64_t next_read_addr;
@@ -3230,7 +3529,7 @@ static int read_memory_progbuf_inner(struct target *target, target_addr_t addres
uint64_t value64 = (((uint64_t)dmi_data1) << 32) | dmi_data0;
buf_set_u64(buffer + (next_index - 2) * size, 0, 8 * size, value64);
- log_memory_access(address + (next_index - 2) * size, value64, size, true);
+ log_memory_access64(address + (next_index - 2) * size, value64, size, true);
/* Restore the command, and execute it.
* Now DM_DATA0 contains the next value just as it would if no
@@ -3254,7 +3553,7 @@ static int read_memory_progbuf_inner(struct target *target, target_addr_t addres
/* Now read whatever we got out of the batch. */
dmi_status_t status = DMI_STATUS_SUCCESS;
- unsigned read = 0;
+ unsigned read_count = 0;
assert(index >= 2);
for (unsigned j = index - 2; j < index + reads; j++) {
assert(j < count);
@@ -3263,9 +3562,9 @@ static int read_memory_progbuf_inner(struct target *target, target_addr_t addres
if (j + 3 + ignore_last > next_index)
break;
- status = riscv_batch_get_dmi_read_op(batch, read);
- uint64_t value = riscv_batch_get_dmi_read_data(batch, read);
- read++;
+ status = riscv_batch_get_dmi_read_op(batch, read_count);
+ uint64_t value = riscv_batch_get_dmi_read_data(batch, read_count);
+ read_count++;
if (status != DMI_STATUS_SUCCESS) {
/* If we're here because of busy count, dmi_busy_delay will
* already have been increased and busy state will have been
@@ -3282,7 +3581,7 @@ static int read_memory_progbuf_inner(struct target *target, target_addr_t addres
goto error;
}
if (size > 4) {
- status = riscv_batch_get_dmi_read_op(batch, read);
+ status = riscv_batch_get_dmi_read_op(batch, read_count);
if (status != DMI_STATUS_SUCCESS) {
LOG_WARNING("Batch memory read encountered DMI error %d. "
"Falling back on slower reads.", status);
@@ -3291,12 +3590,12 @@ static int read_memory_progbuf_inner(struct target *target, target_addr_t addres
goto error;
}
value <<= 32;
- value |= riscv_batch_get_dmi_read_data(batch, read);
- read++;
+ value |= riscv_batch_get_dmi_read_data(batch, read_count);
+ read_count++;
}
riscv_addr_t offset = j * size;
buf_set_u64(buffer + offset, 0, 8 * size, value);
- log_memory_access(address + j * increment, value, size, true);
+ log_memory_access64(address + j * increment, value, size, true);
}
index = next_index;
@@ -3315,7 +3614,7 @@ static int read_memory_progbuf_inner(struct target *target, target_addr_t addres
return ERROR_FAIL;
uint64_t value64 = (((uint64_t)dmi_data1) << 32) | dmi_data0;
buf_set_u64(buffer + size * (count - 2), 0, 8 * size, value64);
- log_memory_access(address + size * (count - 2), value64, size, true);
+ log_memory_access64(address + size * (count - 2), value64, size, true);
}
/* Read the last word. */
@@ -3323,8 +3622,8 @@ static int read_memory_progbuf_inner(struct target *target, target_addr_t addres
result = register_read_direct(target, &value, GDB_REGNO_S1);
if (result != ERROR_OK)
goto error;
- buf_set_u64(buffer + size * (count-1), 0, 8 * size, value);
- log_memory_access(address + size * (count-1), value, size, true);
+ buf_set_u64(buffer + size * (count - 1), 0, 8 * size, value);
+ log_memory_access64(address + size * (count - 1), value, size, true);
return ERROR_OK;
@@ -3344,10 +3643,9 @@ static int read_memory_progbuf_one(struct target *target, target_addr_t address,
if (modify_privilege(target, &mstatus, &mstatus_old) != ERROR_OK)
return ERROR_FAIL;
- uint64_t s0;
int result = ERROR_FAIL;
- if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
+ if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
goto restore_mstatus;
/* Write the program (load, increment) */
@@ -3387,19 +3685,15 @@ static int read_memory_progbuf_one(struct target *target, target_addr_t address,
riscv_xlen(target), AC_ACCESS_REGISTER_WRITE |
AC_ACCESS_REGISTER_TRANSFER | AC_ACCESS_REGISTER_POSTEXEC);
if (execute_abstract_command(target, command) != ERROR_OK)
- goto restore_s0;
+ goto restore_mstatus;
uint64_t value;
- if (register_read(target, &value, GDB_REGNO_S0) != ERROR_OK)
- goto restore_s0;
+ if (register_read_direct(target, &value, GDB_REGNO_S0) != ERROR_OK)
+ goto restore_mstatus;
buf_set_u64(buffer, 0, 8 * size, value);
- log_memory_access(address, value, size, true);
+ log_memory_access64(address, value, size, true);
result = ERROR_OK;
-restore_s0:
- if (riscv_set_register(target, GDB_REGNO_S0, s0) != ERROR_OK)
- result = ERROR_FAIL;
-
restore_mstatus:
if (mstatus != mstatus_old)
if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus_old))
@@ -3425,6 +3719,9 @@ static int read_memory_progbuf(struct target *target, target_addr_t address,
LOG_DEBUG("reading %d words of %d bytes from 0x%" TARGET_PRIxADDR, count,
size, address);
+ if (dm013_select_target(target) != ERROR_OK)
+ return ERROR_FAIL;
+
select_dmi(target);
memset(buffer, 0, count*size);
@@ -3442,14 +3739,13 @@ static int read_memory_progbuf(struct target *target, target_addr_t address,
/* s0 holds the next address to read from
* s1 holds the next data value read
- * s2 is a counter in case increment is 0
+ * a0 is a counter in case increment is 0
*/
- uint64_t s0, s1, s2;
- if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
+ if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
- if (register_read(target, &s1, GDB_REGNO_S1) != ERROR_OK)
+ if (riscv_save_register(target, GDB_REGNO_S1) != ERROR_OK)
return ERROR_FAIL;
- if (increment == 0 && register_read(target, &s2, GDB_REGNO_S2) != ERROR_OK)
+ if (increment == 0 && riscv_save_register(target, GDB_REGNO_A0) != ERROR_OK)
return ERROR_FAIL;
/* Write the program (load, increment) */
@@ -3479,7 +3775,7 @@ static int read_memory_progbuf(struct target *target, target_addr_t address,
if (riscv_enable_virtual && has_sufficient_progbuf(target, 5) && get_field(mstatus, MSTATUS_MPRV))
riscv_program_csrrci(&program, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN, GDB_REGNO_DCSR);
if (increment == 0)
- riscv_program_addi(&program, GDB_REGNO_S2, GDB_REGNO_S2, 1);
+ riscv_program_addi(&program, GDB_REGNO_A0, GDB_REGNO_A0, 1);
else
riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, increment);
@@ -3514,11 +3810,6 @@ static int read_memory_progbuf(struct target *target, target_addr_t address,
result = ERROR_OK;
}
- riscv_set_register(target, GDB_REGNO_S0, s0);
- riscv_set_register(target, GDB_REGNO_S1, s1);
- if (increment == 0)
- riscv_set_register(target, GDB_REGNO_S2, s2);
-
/* Restore MSTATUS */
if (mstatus != mstatus_old)
if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus_old))
@@ -3664,7 +3955,7 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address,
struct riscv_batch *batch = riscv_batch_alloc(
target,
- 32,
+ RISCV_BATCH_ALLOC_SIZE,
info->dmi_busy_delay + info->bus_master_write_delay);
if (!batch)
return ERROR_FAIL;
@@ -3675,35 +3966,42 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address,
if (riscv_batch_available_scans(batch) < (size + 3) / 4)
break;
- if (size > 12)
- riscv_batch_add_dmi_write(batch, DM_SBDATA3,
- ((uint32_t) p[12]) |
+ uint32_t sbvalue[4] = { 0 };
+ if (size > 12) {
+ sbvalue[3] = ((uint32_t) p[12]) |
(((uint32_t) p[13]) << 8) |
(((uint32_t) p[14]) << 16) |
- (((uint32_t) p[15]) << 24));
+ (((uint32_t) p[15]) << 24);
+ riscv_batch_add_dmi_write(batch, DM_SBDATA3, sbvalue[3], false);
+ }
- if (size > 8)
- riscv_batch_add_dmi_write(batch, DM_SBDATA2,
- ((uint32_t) p[8]) |
+ if (size > 8) {
+ sbvalue[2] = ((uint32_t) p[8]) |
(((uint32_t) p[9]) << 8) |
(((uint32_t) p[10]) << 16) |
- (((uint32_t) p[11]) << 24));
- if (size > 4)
- riscv_batch_add_dmi_write(batch, DM_SBDATA1,
- ((uint32_t) p[4]) |
+ (((uint32_t) p[11]) << 24);
+ riscv_batch_add_dmi_write(batch, DM_SBDATA2, sbvalue[2], false);
+ }
+ if (size > 4) {
+ sbvalue[1] = ((uint32_t) p[4]) |
(((uint32_t) p[5]) << 8) |
(((uint32_t) p[6]) << 16) |
- (((uint32_t) p[7]) << 24));
- uint32_t value = p[0];
+ (((uint32_t) p[7]) << 24);
+ riscv_batch_add_dmi_write(batch, DM_SBDATA1, sbvalue[1], false);
+ }
+
+ sbvalue[0] = p[0];
if (size > 2) {
- value |= ((uint32_t) p[2]) << 16;
- value |= ((uint32_t) p[3]) << 24;
+ sbvalue[0] |= ((uint32_t) p[2]) << 16;
+ sbvalue[0] |= ((uint32_t) p[3]) << 24;
}
if (size > 1)
- value |= ((uint32_t) p[1]) << 8;
- riscv_batch_add_dmi_write(batch, DM_SBDATA0, value);
+ sbvalue[0] |= ((uint32_t) p[1]) << 8;
+
+ riscv_batch_add_dmi_write(batch, DM_SBDATA0, sbvalue[0], false);
+
+ log_memory_access(address + i * size, sbvalue, size, false);
- log_memory_access(address + i * size, value, size, false);
next_address += size;
}
@@ -3809,10 +4107,9 @@ static int write_memory_progbuf(struct target *target, target_addr_t address,
*/
int result = ERROR_OK;
- uint64_t s0, s1;
- if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
+ if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
- if (register_read(target, &s1, GDB_REGNO_S1) != ERROR_OK)
+ if (riscv_save_register(target, GDB_REGNO_S1) != ERROR_OK)
return ERROR_FAIL;
/* Write the program (store, increment) */
@@ -3850,29 +4147,27 @@ static int write_memory_progbuf(struct target *target, target_addr_t address,
riscv_program_write(&program);
riscv_addr_t cur_addr = address;
- riscv_addr_t fin_addr = address + (count * size);
+ riscv_addr_t distance = (riscv_addr_t)count * size;
bool setup_needed = true;
- LOG_DEBUG("writing until final address 0x%016" PRIx64, fin_addr);
- while (cur_addr < fin_addr) {
+ LOG_DEBUG("writing until final address 0x%016" PRIx64, cur_addr + distance);
+ while (cur_addr - address < distance) {
LOG_DEBUG("transferring burst starting at address 0x%016" PRIx64,
cur_addr);
struct riscv_batch *batch = riscv_batch_alloc(
target,
- 32,
+ RISCV_BATCH_ALLOC_SIZE,
info->dmi_busy_delay + info->ac_busy_delay);
if (!batch)
goto error;
/* To write another word, we put it in S1 and execute the program. */
- unsigned start = (cur_addr - address) / size;
- for (unsigned i = start; i < count; ++i) {
- unsigned offset = size*i;
+ for (riscv_addr_t offset = cur_addr - address; offset < distance; offset += size) {
const uint8_t *t_buffer = buffer + offset;
uint64_t value = buf_get_u64(t_buffer, 0, 8 * size);
- log_memory_access(address + offset, value, size, false);
+ log_memory_access64(cur_addr, value, size, false);
cur_addr += size;
if (setup_needed) {
@@ -3907,9 +4202,13 @@ static int write_memory_progbuf(struct target *target, target_addr_t address,
setup_needed = false;
} else {
+ /* Note that data1 "might not be preserved after
+ * an abstract command is executed," so this
+ * can't be optimized by only writing data1 when
+ * it has changed. */
if (size > 4)
- riscv_batch_add_dmi_write(batch, DM_DATA1, value >> 32);
- riscv_batch_add_dmi_write(batch, DM_DATA0, value);
+ riscv_batch_add_dmi_write(batch, DM_DATA1, value >> 32, false);
+ riscv_batch_add_dmi_write(batch, DM_DATA0, value, false);
if (riscv_batch_full(batch))
break;
}
@@ -3960,11 +4259,6 @@ static int write_memory_progbuf(struct target *target, target_addr_t address,
error:
dmi_write(target, DM_ABSTRACTAUTO, 0);
- if (register_write_direct(target, GDB_REGNO_S1, s1) != ERROR_OK)
- return ERROR_FAIL;
- if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
- return ERROR_FAIL;
-
/* Restore MSTATUS */
if (mstatus != mstatus_old)
if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus_old))
@@ -4063,27 +4357,27 @@ struct target_type riscv013_target = {
/*** 0.13-specific implementations of various RISC-V helper functions. ***/
static int riscv013_get_register(struct target *target,
- riscv_reg_t *value, int rid)
+ riscv_reg_t *value, enum gdb_regno rid)
{
- LOG_DEBUG("[%s] reading register %s", target_name(target),
- gdb_regno_name(rid));
+ LOG_TARGET_DEBUG(target, "reading register %s", gdb_regno_name(rid));
- if (riscv_select_current_hart(target) != ERROR_OK)
+ if (dm013_select_target(target) != ERROR_OK)
return ERROR_FAIL;
int result = ERROR_OK;
if (rid == GDB_REGNO_PC) {
/* TODO: move this into riscv.c. */
- result = register_read(target, value, GDB_REGNO_DPC);
- LOG_DEBUG("[%d] read PC from DPC: 0x%" PRIx64, target->coreid, *value);
+ result = register_read_direct(target, value, GDB_REGNO_DPC);
+ LOG_TARGET_DEBUG(target, "read PC from DPC: 0x%" PRIx64, *value);
} else if (rid == GDB_REGNO_PRIV) {
uint64_t dcsr;
/* TODO: move this into riscv.c. */
- result = register_read(target, &dcsr, GDB_REGNO_DCSR);
+ if (register_read_direct(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK)
+ return ERROR_FAIL;
*value = set_field(0, VIRT_PRIV_V, get_field(dcsr, CSR_DCSR_V));
*value = set_field(*value, VIRT_PRIV_PRV, get_field(dcsr, CSR_DCSR_PRV));
} else {
- result = register_read(target, value, rid);
+ result = register_read_direct(target, value, rid);
if (result != ERROR_OK)
*value = -1;
}
@@ -4091,28 +4385,25 @@ static int riscv013_get_register(struct target *target,
return result;
}
-static int riscv013_set_register(struct target *target, int rid, uint64_t value)
+static int riscv013_set_register(struct target *target, enum gdb_regno rid,
+ riscv_reg_t value)
{
- riscv013_select_current_hart(target);
- LOG_DEBUG("[%d] writing 0x%" PRIx64 " to register %s",
- target->coreid, value, gdb_regno_name(rid));
+ LOG_TARGET_DEBUG(target, "writing 0x%" PRIx64 " to register %s",
+ value, gdb_regno_name(rid));
+
+ if (dm013_select_target(target) != ERROR_OK)
+ return ERROR_FAIL;
if (rid <= GDB_REGNO_XPR31) {
return register_write_direct(target, rid, value);
} else if (rid == GDB_REGNO_PC) {
- LOG_DEBUG("[%d] writing PC to DPC: 0x%" PRIx64, target->coreid, value);
- register_write_direct(target, GDB_REGNO_DPC, value);
- uint64_t actual_value;
- register_read_direct(target, &actual_value, GDB_REGNO_DPC);
- LOG_DEBUG("[%d] actual DPC written: 0x%016" PRIx64, target->coreid, actual_value);
- if (value != actual_value) {
- LOG_ERROR("Written PC (0x%" PRIx64 ") does not match read back "
- "value (0x%" PRIx64 ")", value, actual_value);
- return ERROR_FAIL;
- }
+ LOG_TARGET_DEBUG(target, "writing PC to DPC: 0x%" PRIx64, value);
+ return register_write_direct(target, GDB_REGNO_DPC, value);
} else if (rid == GDB_REGNO_PRIV) {
- uint64_t dcsr;
- register_read(target, &dcsr, GDB_REGNO_DCSR);
+ riscv_reg_t dcsr;
+
+ if (register_read_direct(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK)
+ return ERROR_FAIL;
dcsr = set_field(dcsr, CSR_DCSR_PRV, get_field(value, VIRT_PRIV_PRV));
dcsr = set_field(dcsr, CSR_DCSR_V, get_field(value, VIRT_PRIV_V));
return register_write_direct(target, GDB_REGNO_DCSR, dcsr);
@@ -4123,38 +4414,36 @@ static int riscv013_set_register(struct target *target, int rid, uint64_t value)
return ERROR_OK;
}
-static int riscv013_select_current_hart(struct target *target)
+static int dm013_select_hart(struct target *target, int hart_index)
{
- RISCV_INFO(r);
-
dm013_info_t *dm = get_dm(target);
if (!dm)
return ERROR_FAIL;
- if (r->current_hartid == dm->current_hartid)
+ if (hart_index == dm->current_hartid)
return ERROR_OK;
- uint32_t dmcontrol;
- /* TODO: can't we just "dmcontrol = DMI_DMACTIVE"? */
- if (dmi_read(target, &dmcontrol, DM_DMCONTROL) != ERROR_OK)
+ uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE;
+ dmcontrol = set_dmcontrol_hartsel(dmcontrol, hart_index);
+ if (dmi_write(target, DM_DMCONTROL, dmcontrol) != ERROR_OK) {
+ /* Who knows what the state is? */
+ dm->current_hartid = HART_INDEX_UNKNOWN;
return ERROR_FAIL;
- dmcontrol = set_hartsel(dmcontrol, r->current_hartid);
- int result = dmi_write(target, DM_DMCONTROL, dmcontrol);
- dm->current_hartid = r->current_hartid;
- return result;
+ }
+ dm->current_hartid = hart_index;
+ return ERROR_OK;
}
/* Select all harts that were prepped and that are selectable, clearing the
* prepped flag on the harts that actually were selected. */
-static int select_prepped_harts(struct target *target, bool *use_hasel)
+static int select_prepped_harts(struct target *target)
{
+ RISCV_INFO(r);
dm013_info_t *dm = get_dm(target);
if (!dm)
return ERROR_FAIL;
if (!dm->hasel_supported) {
- RISCV_INFO(r);
r->prepped = false;
- *use_hasel = false;
- return ERROR_OK;
+ return dm013_select_target(target);
}
assert(dm->hart_count);
@@ -4165,27 +4454,33 @@ static int select_prepped_harts(struct target *target, bool *use_hasel)
target_list_t *entry;
unsigned total_selected = 0;
+ unsigned int selected_index = 0;
list_for_each_entry(entry, &dm->target_list, list) {
struct target *t = entry->target;
- struct riscv_info *r = riscv_info(t);
- riscv013_info_t *info = get_info(t);
- unsigned index = info->index;
- LOG_DEBUG("index=%d, coreid=%d, prepped=%d", index, t->coreid, r->prepped);
- r->selected = r->prepped;
- if (r->prepped) {
+ struct riscv_info *info = riscv_info(t);
+ riscv013_info_t *info_013 = get_info(t);
+ unsigned index = info_013->index;
+ LOG_DEBUG("index=%d, coreid=%d, prepped=%d", index, t->coreid, info->prepped);
+ if (info->prepped) {
+ info_013->selected = true;
hawindow[index / 32] |= 1 << (index % 32);
- r->prepped = false;
+ info->prepped = false;
total_selected++;
+ selected_index = index;
}
- index++;
}
- /* Don't use hasel if we only need to talk to one hart. */
- if (total_selected <= 1) {
- *use_hasel = false;
- return ERROR_OK;
+ if (total_selected == 0) {
+ LOG_TARGET_ERROR(target, "No harts were prepped!");
+ return ERROR_FAIL;
+ } else if (total_selected == 1) {
+ /* Don't use hasel if we only need to talk to one hart. */
+ return dm013_select_hart(target, selected_index);
}
+ if (dm013_select_hart(target, HART_INDEX_MULTIPLE) != ERROR_OK)
+ return ERROR_FAIL;
+
for (unsigned i = 0; i < hawindow_count; i++) {
if (dmi_write(target, DM_HAWINDOWSEL, i) != ERROR_OK)
return ERROR_FAIL;
@@ -4193,7 +4488,6 @@ static int select_prepped_harts(struct target *target, bool *use_hasel)
return ERROR_FAIL;
}
- *use_hasel = true;
return ERROR_OK;
}
@@ -4204,72 +4498,105 @@ static int riscv013_halt_prep(struct target *target)
static int riscv013_halt_go(struct target *target)
{
- bool use_hasel = false;
- if (select_prepped_harts(target, &use_hasel) != ERROR_OK)
+ dm013_info_t *dm = get_dm(target);
+ if (!dm)
return ERROR_FAIL;
- RISCV_INFO(r);
- LOG_DEBUG("halting hart %d", r->current_hartid);
+ if (select_prepped_harts(target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ LOG_TARGET_DEBUG(target, "halting hart");
/* Issue the halt command, and then wait for the current hart to halt. */
uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE | DM_DMCONTROL_HALTREQ;
- if (use_hasel)
- dmcontrol |= DM_DMCONTROL_HASEL;
- dmcontrol = set_hartsel(dmcontrol, r->current_hartid);
+ dmcontrol = set_dmcontrol_hartsel(dmcontrol, dm->current_hartid);
dmi_write(target, DM_DMCONTROL, dmcontrol);
- for (size_t i = 0; i < 256; ++i)
- if (riscv_is_halted(target))
- break;
-
- if (!riscv_is_halted(target)) {
- uint32_t dmstatus;
+ uint32_t dmstatus;
+ for (size_t i = 0; i < 256; ++i) {
if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
return ERROR_FAIL;
+ /* When no harts are running, there's no point in continuing this loop. */
+ if (!get_field(dmstatus, DM_DMSTATUS_ANYRUNNING))
+ break;
+ }
+
+ /* We declare success if no harts are running. One or more of them may be
+ * unavailable, though. */
+
+ if ((get_field(dmstatus, DM_DMSTATUS_ANYRUNNING))) {
if (dmi_read(target, &dmcontrol, DM_DMCONTROL) != ERROR_OK)
return ERROR_FAIL;
- LOG_ERROR("unable to halt hart %d", r->current_hartid);
- LOG_ERROR(" dmcontrol=0x%08x", dmcontrol);
- LOG_ERROR(" dmstatus =0x%08x", dmstatus);
+ LOG_TARGET_ERROR(target, "Unable to halt. dmcontrol=0x%08x, dmstatus=0x%08x",
+ dmcontrol, dmstatus);
return ERROR_FAIL;
}
dmcontrol = set_field(dmcontrol, DM_DMCONTROL_HALTREQ, 0);
dmi_write(target, DM_DMCONTROL, dmcontrol);
- if (use_hasel) {
+ if (dm->current_hartid == HART_INDEX_MULTIPLE) {
target_list_t *entry;
- dm013_info_t *dm = get_dm(target);
- if (!dm)
- return ERROR_FAIL;
list_for_each_entry(entry, &dm->target_list, list) {
struct target *t = entry->target;
- t->state = TARGET_HALTED;
- if (t->debug_reason == DBG_REASON_NOTHALTED)
- t->debug_reason = DBG_REASON_DBGRQ;
+ uint32_t t_dmstatus;
+ if (get_field(dmstatus, DM_DMSTATUS_ALLHALTED) ||
+ get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL)) {
+ /* All harts are either halted or unavailable. No
+ * need to read dmstatus for each hart. */
+ t_dmstatus = dmstatus;
+ } else {
+ /* Only some harts were halted/unavailable. Read
+ * dmstatus for this one to see what its status
+ * is. */
+ riscv013_info_t *info = get_info(t);
+ dmcontrol = set_dmcontrol_hartsel(dmcontrol, info->index);
+ if (dmi_write(target, DM_DMCONTROL, dmcontrol) != ERROR_OK)
+ return ERROR_FAIL;
+ dm->current_hartid = info->index;
+ if (dmi_read(target, &t_dmstatus, DM_DMSTATUS) != ERROR_OK)
+ return ERROR_FAIL;
+ }
+ /* Set state for the current target based on its dmstatus. */
+ if (get_field(t_dmstatus, DM_DMSTATUS_ALLHALTED)) {
+ t->state = TARGET_HALTED;
+ if (t->debug_reason == DBG_REASON_NOTHALTED)
+ t->debug_reason = DBG_REASON_DBGRQ;
+ } else if (get_field(t_dmstatus, DM_DMSTATUS_ALLUNAVAIL)) {
+ t->state = TARGET_UNAVAILABLE;
+ }
+ }
+
+ } else {
+ /* Set state for the current target based on its dmstatus. */
+ if (get_field(dmstatus, DM_DMSTATUS_ALLHALTED)) {
+ target->state = TARGET_HALTED;
+ if (target->debug_reason == DBG_REASON_NOTHALTED)
+ target->debug_reason = DBG_REASON_DBGRQ;
+ } else if (get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL)) {
+ target->state = TARGET_UNAVAILABLE;
}
}
- /* The "else" case is handled in halt_go(). */
return ERROR_OK;
}
static int riscv013_resume_go(struct target *target)
{
- bool use_hasel = false;
- if (select_prepped_harts(target, &use_hasel) != ERROR_OK)
+ if (select_prepped_harts(target) != ERROR_OK)
return ERROR_FAIL;
- return riscv013_step_or_resume_current_hart(target, false, use_hasel);
+ return riscv013_step_or_resume_current_hart(target, false);
}
static int riscv013_step_current_hart(struct target *target)
{
- return riscv013_step_or_resume_current_hart(target, true, false);
+ return riscv013_step_or_resume_current_hart(target, true);
}
static int riscv013_resume_prep(struct target *target)
{
+ assert(target->state == TARGET_HALTED);
return riscv013_on_step_or_resume(target, false);
}
@@ -4278,51 +4605,18 @@ static int riscv013_on_step(struct target *target)
return riscv013_on_step_or_resume(target, true);
}
-static int riscv013_on_halt(struct target *target)
-{
- return ERROR_OK;
-}
-
-static bool riscv013_is_halted(struct target *target)
-{
- uint32_t dmstatus;
- if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
- return false;
- if (get_field(dmstatus, DM_DMSTATUS_ANYUNAVAIL))
- LOG_ERROR("Hart %d is unavailable.", riscv_current_hartid(target));
- if (get_field(dmstatus, DM_DMSTATUS_ANYNONEXISTENT))
- LOG_ERROR("Hart %d doesn't exist.", riscv_current_hartid(target));
- if (get_field(dmstatus, DM_DMSTATUS_ANYHAVERESET)) {
- int hartid = riscv_current_hartid(target);
- LOG_INFO("Hart %d unexpectedly reset!", hartid);
- /* TODO: Can we make this more obvious to eg. a gdb user? */
- uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE |
- DM_DMCONTROL_ACKHAVERESET;
- dmcontrol = set_hartsel(dmcontrol, hartid);
- /* If we had been halted when we reset, request another halt. If we
- * ended up running out of reset, then the user will (hopefully) get a
- * message that a reset happened, that the target is running, and then
- * that it is halted again once the request goes through.
- */
- if (target->state == TARGET_HALTED)
- dmcontrol |= DM_DMCONTROL_HALTREQ;
- dmi_write(target, DM_DMCONTROL, dmcontrol);
- }
- return get_field(dmstatus, DM_DMSTATUS_ALLHALTED);
-}
-
static enum riscv_halt_reason riscv013_halt_reason(struct target *target)
{
riscv_reg_t dcsr;
- int result = register_read(target, &dcsr, GDB_REGNO_DCSR);
+ int result = register_read_direct(target, &dcsr, GDB_REGNO_DCSR);
if (result != ERROR_OK)
return RISCV_HALT_UNKNOWN;
- LOG_DEBUG("dcsr.cause: 0x%" PRIx64, get_field(dcsr, CSR_DCSR_CAUSE));
+ LOG_TARGET_DEBUG(target, "dcsr.cause: 0x%" PRIx64, get_field(dcsr, CSR_DCSR_CAUSE));
switch (get_field(dcsr, CSR_DCSR_CAUSE)) {
- case CSR_DCSR_CAUSE_SWBP:
- return RISCV_HALT_BREAKPOINT;
+ case CSR_DCSR_CAUSE_EBREAK:
+ return RISCV_HALT_EBREAK;
case CSR_DCSR_CAUSE_TRIGGER:
/* We could get here before triggers are enumerated if a trigger was
* already set when we connected. Force enumeration now, which has the
@@ -4332,15 +4626,15 @@ static enum riscv_halt_reason riscv013_halt_reason(struct target *target)
return RISCV_HALT_TRIGGER;
case CSR_DCSR_CAUSE_STEP:
return RISCV_HALT_SINGLESTEP;
- case CSR_DCSR_CAUSE_DEBUGINT:
- case CSR_DCSR_CAUSE_HALT:
+ case CSR_DCSR_CAUSE_HALTREQ:
+ case CSR_DCSR_CAUSE_RESETHALTREQ:
return RISCV_HALT_INTERRUPT;
case CSR_DCSR_CAUSE_GROUP:
return RISCV_HALT_GROUP;
}
LOG_ERROR("Unknown DCSR cause field: 0x%" PRIx64, get_field(dcsr, CSR_DCSR_CAUSE));
- LOG_ERROR(" dcsr=0x%016lx", (long)dcsr);
+ LOG_ERROR(" dcsr=0x%" PRIx32, (uint32_t) dcsr);
return RISCV_HALT_UNKNOWN;
}
@@ -4366,6 +4660,20 @@ riscv_insn_t riscv013_read_debug_buffer(struct target *target, unsigned index)
return value;
}
+int riscv013_invalidate_cached_debug_buffer(struct target *target)
+{
+ dm013_info_t *dm = get_dm(target);
+ if (!dm) {
+ LOG_TARGET_DEBUG(target, "No DM is specified for the target");
+ return ERROR_FAIL;
+ }
+
+ LOG_TARGET_DEBUG(target, "Invalidating progbuf cache");
+ for (unsigned int i = 0; i < 15; i++)
+ dm->progbuf_cache[i] = 0;
+ return ERROR_OK;
+}
+
int riscv013_execute_debug_buffer(struct target *target)
{
uint32_t run_program = 0;
@@ -4420,36 +4728,32 @@ static int riscv013_on_step_or_resume(struct target *target, bool step)
if (maybe_execute_fence_i(target) != ERROR_OK)
return ERROR_FAIL;
- /* We want to twiddle some bits in the debug CSR so debugging works. */
- riscv_reg_t dcsr;
- int result = register_read(target, &dcsr, GDB_REGNO_DCSR);
- if (result != ERROR_OK)
- return result;
- dcsr = set_field(dcsr, CSR_DCSR_STEP, step);
- dcsr = set_field(dcsr, CSR_DCSR_EBREAKM, riscv_ebreakm);
- dcsr = set_field(dcsr, CSR_DCSR_EBREAKS, riscv_ebreaks);
- dcsr = set_field(dcsr, CSR_DCSR_EBREAKU, riscv_ebreaku);
- return riscv_set_register(target, GDB_REGNO_DCSR, dcsr);
+ if (set_dcsr_ebreak(target, step) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (riscv_flush_registers(target) != ERROR_OK)
+ return ERROR_FAIL;
+ return ERROR_OK;
}
static int riscv013_step_or_resume_current_hart(struct target *target,
- bool step, bool use_hasel)
+ bool step)
{
- RISCV_INFO(r);
- LOG_DEBUG("resuming hart %d (for step?=%d)", r->current_hartid, step);
- if (!riscv_is_halted(target)) {
- LOG_ERROR("Hart %d is not halted!", r->current_hartid);
+ if (target->state != TARGET_HALTED) {
+ LOG_TARGET_ERROR(target, "Hart is not halted!");
return ERROR_FAIL;
}
+ LOG_TARGET_DEBUG(target, "resuming (for step?=%d)", step);
+ if (riscv_flush_registers(target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ dm013_info_t *dm = get_dm(target);
/* Issue the resume command, and then wait for the current hart to resume. */
uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE | DM_DMCONTROL_RESUMEREQ;
- if (use_hasel)
- dmcontrol |= DM_DMCONTROL_HASEL;
- dmcontrol = set_hartsel(dmcontrol, r->current_hartid);
+ dmcontrol = set_dmcontrol_hartsel(dmcontrol, dm->current_hartid);
dmi_write(target, DM_DMCONTROL, dmcontrol);
- dmcontrol = set_field(dmcontrol, DM_DMCONTROL_HASEL, 0);
dmcontrol = set_field(dmcontrol, DM_DMCONTROL_RESUMEREQ, 0);
uint32_t dmstatus;
@@ -4457,6 +4761,8 @@ static int riscv013_step_or_resume_current_hart(struct target *target,
usleep(10);
if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
return ERROR_FAIL;
+ if (get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL))
+ return ERROR_FAIL;
if (get_field(dmstatus, DM_DMSTATUS_ALLRESUMEACK) == 0)
continue;
if (step && get_field(dmstatus, DM_DMSTATUS_ALLHALTED) == 0)
@@ -4468,10 +4774,10 @@ static int riscv013_step_or_resume_current_hart(struct target *target,
dmi_write(target, DM_DMCONTROL, dmcontrol);
- LOG_ERROR("unable to resume hart %d", r->current_hartid);
+ LOG_TARGET_ERROR(target, "unable to resume");
if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
return ERROR_FAIL;
- LOG_ERROR(" dmstatus =0x%08x", dmstatus);
+ LOG_TARGET_ERROR(target, " dmstatus=0x%08x", dmstatus);
if (step) {
LOG_ERROR(" was stepping, halting");
diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c
index 8dafa02..ef4dd80 100644
--- a/src/target/riscv/riscv.c
+++ b/src/target/riscv/riscv.c
@@ -17,7 +17,10 @@
#include "jtag/jtag.h"
#include "target/register.h"
#include "target/breakpoints.h"
+#include "helper/base64.h"
+#include "helper/time_support.h"
#include "riscv.h"
+#include "program.h"
#include "gdb_regs.h"
#include "rtos/rtos.h"
#include "debug_defines.h"
@@ -25,89 +28,14 @@
#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1)))
#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask)))
-
-/* Constants for legacy SiFive hardware breakpoints. */
-#define CSR_BPCONTROL_X (1<<0)
-#define CSR_BPCONTROL_W (1<<1)
-#define CSR_BPCONTROL_R (1<<2)
-#define CSR_BPCONTROL_U (1<<3)
-#define CSR_BPCONTROL_S (1<<4)
-#define CSR_BPCONTROL_H (1<<5)
-#define CSR_BPCONTROL_M (1<<6)
-#define CSR_BPCONTROL_BPMATCH (0xf<<7)
-#define CSR_BPCONTROL_BPACTION (0xff<<11)
-
-#define DEBUG_ROM_START 0x800
-#define DEBUG_ROM_RESUME (DEBUG_ROM_START + 4)
-#define DEBUG_ROM_EXCEPTION (DEBUG_ROM_START + 8)
-#define DEBUG_RAM_START 0x400
-
-#define SETHALTNOT 0x10c
+#define field_value(mask, val) set_field((riscv_reg_t) 0, mask, val)
/*** JTAG registers. ***/
#define DTMCONTROL 0x10
-#define DTMCONTROL_DBUS_RESET (1<<16)
-#define DTMCONTROL_IDLE (7<<10)
-#define DTMCONTROL_ADDRBITS (0xf<<4)
#define DTMCONTROL_VERSION (0xf)
#define DBUS 0x11
-#define DBUS_OP_START 0
-#define DBUS_OP_SIZE 2
-typedef enum {
- DBUS_OP_NOP = 0,
- DBUS_OP_READ = 1,
- DBUS_OP_WRITE = 2
-} dbus_op_t;
-typedef enum {
- DBUS_STATUS_SUCCESS = 0,
- DBUS_STATUS_FAILED = 2,
- DBUS_STATUS_BUSY = 3
-} dbus_status_t;
-#define DBUS_DATA_START 2
-#define DBUS_DATA_SIZE 34
-#define DBUS_ADDRESS_START 36
-
-typedef enum slot {
- SLOT0,
- SLOT1,
- SLOT_LAST,
-} slot_t;
-
-/*** Debug Bus registers. ***/
-
-#define DMCONTROL 0x10
-#define DMCONTROL_INTERRUPT (((uint64_t)1)<<33)
-#define DMCONTROL_HALTNOT (((uint64_t)1)<<32)
-#define DMCONTROL_BUSERROR (7<<19)
-#define DMCONTROL_SERIAL (3<<16)
-#define DMCONTROL_AUTOINCREMENT (1<<15)
-#define DMCONTROL_ACCESS (7<<12)
-#define DMCONTROL_HARTID (0x3ff<<2)
-#define DMCONTROL_NDRESET (1<<1)
-#define DMCONTROL_FULLRESET 1
-
-#define DMINFO 0x11
-#define DMINFO_ABUSSIZE (0x7fU<<25)
-#define DMINFO_SERIALCOUNT (0xf<<21)
-#define DMINFO_ACCESS128 (1<<20)
-#define DMINFO_ACCESS64 (1<<19)
-#define DMINFO_ACCESS32 (1<<18)
-#define DMINFO_ACCESS16 (1<<17)
-#define DMINFO_ACCESS8 (1<<16)
-#define DMINFO_DRAMSIZE (0x3f<<10)
-#define DMINFO_AUTHENTICATED (1<<5)
-#define DMINFO_AUTHBUSY (1<<4)
-#define DMINFO_AUTHTYPE (3<<2)
-#define DMINFO_VERSION 3
-
-/*** Info about the core being debugged. ***/
-
-#define DBUS_ADDRESS_UNKNOWN 0xffff
-
-#define MAX_HWBPS 16
-#define DRAM_CACHE_SIZE 16
static uint8_t ir_dtmcontrol[4] = {DTMCONTROL};
struct scan_field select_dtmcontrol = {
@@ -127,6 +55,7 @@ struct scan_field select_idcode = {
static bscan_tunnel_type_t bscan_tunnel_type;
int bscan_tunnel_ir_width; /* if zero, then tunneling is not present/active */
+static int bscan_tunnel_ir_id; /* IR ID of the JTAG TAP to access the tunnel. Valid when not 0 */
static const uint8_t bscan_zero[4] = {0};
static const uint8_t bscan_one[4] = {1};
@@ -195,7 +124,7 @@ struct trigger {
uint32_t length;
uint64_t mask;
uint64_t value;
- bool read, write, execute;
+ bool is_read, is_write, is_execute;
int unique_id;
};
@@ -230,6 +159,19 @@ static const virt2phys_info_t sv32 = {
.pa_ppn_mask = {0x3ff, 0xfff},
};
+static const virt2phys_info_t sv32x4 = {
+ .name = "Sv32x4",
+ .va_bits = 34,
+ .level = 2,
+ .pte_shift = 2,
+ .vpn_shift = {12, 22},
+ .vpn_mask = {0x3ff, 0xfff},
+ .pte_ppn_shift = {10, 20},
+ .pte_ppn_mask = {0x3ff, 0xfff},
+ .pa_ppn_shift = {12, 22},
+ .pa_ppn_mask = {0x3ff, 0xfff},
+};
+
static const virt2phys_info_t sv39 = {
.name = "Sv39",
.va_bits = 39,
@@ -243,6 +185,19 @@ static const virt2phys_info_t sv39 = {
.pa_ppn_mask = {0x1ff, 0x1ff, 0x3ffffff},
};
+static const virt2phys_info_t sv39x4 = {
+ .name = "Sv39x4",
+ .va_bits = 41,
+ .level = 3,
+ .pte_shift = 3,
+ .vpn_shift = {12, 21, 30},
+ .vpn_mask = {0x1ff, 0x1ff, 0x7ff},
+ .pte_ppn_shift = {10, 19, 28},
+ .pte_ppn_mask = {0x1ff, 0x1ff, 0x3ffffff},
+ .pa_ppn_shift = {12, 21, 30},
+ .pa_ppn_mask = {0x1ff, 0x1ff, 0x3ffffff},
+};
+
static const virt2phys_info_t sv48 = {
.name = "Sv48",
.va_bits = 48,
@@ -256,7 +211,20 @@ static const virt2phys_info_t sv48 = {
.pa_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ffff},
};
-static enum riscv_halt_reason riscv_halt_reason(struct target *target, int hartid);
+static const virt2phys_info_t sv48x4 = {
+ .name = "Sv48x4",
+ .va_bits = 50,
+ .level = 4,
+ .pte_shift = 3,
+ .vpn_shift = {12, 21, 30, 39},
+ .vpn_mask = {0x1ff, 0x1ff, 0x1ff, 0x7ff},
+ .pte_ppn_shift = {10, 19, 28, 37},
+ .pte_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ffff},
+ .pa_ppn_shift = {12, 21, 30, 39},
+ .pa_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x7ffff},
+};
+
+static enum riscv_halt_reason riscv_halt_reason(struct target *target);
static void riscv_info_init(struct target *target, struct riscv_info *r);
static void riscv_invalidate_register_cache(struct target *target);
static int riscv_step_rtos_hart(struct target *target);
@@ -422,7 +390,8 @@ static struct target_type *get_target_type(struct target *target)
case 1:
return &riscv013_target;
default:
- LOG_ERROR("Unsupported DTM version: %d", info->dtm_version);
+ LOG_ERROR("[%s] Unsupported DTM version: %d",
+ target_name(target), info->dtm_version);
return NULL;
}
}
@@ -451,8 +420,12 @@ static int riscv_init_target(struct command_context *cmd_ctx,
select_idcode.num_bits = target->tap->ir_length;
if (bscan_tunnel_ir_width != 0) {
- assert(target->tap->ir_length >= 6);
- uint32_t ir_user4_raw = 0x23 << (target->tap->ir_length - 6);
+ uint32_t ir_user4_raw = bscan_tunnel_ir_id;
+ /* Provide a default value which target some Xilinx FPGA USER4 IR */
+ if (ir_user4_raw == 0) {
+ assert(target->tap->ir_length >= 6);
+ ir_user4_raw = 0x23 << (target->tap->ir_length - 6);
+ }
h_u32_to_le(ir_user4, ir_user4_raw);
select_user4.num_bits = target->tap->ir_length;
bscan_tunneled_ir_width[0] = bscan_tunnel_ir_width;
@@ -493,6 +466,9 @@ static void riscv_deinit_target(struct target *target)
struct riscv_info *info = target->arch_info;
struct target_type *tt = get_target_type(target);
+ if (riscv_flush_registers(target) != ERROR_OK)
+ LOG_ERROR("[%s] Failed to flush registers. Ignoring this error.", target_name(target));
+
if (tt && info && info->version_specific)
tt->deinit_target(target);
@@ -502,6 +478,11 @@ static void riscv_deinit_target(struct target *target)
return;
range_list_t *entry, *tmp;
+ list_for_each_entry_safe(entry, tmp, &info->hide_csr, list) {
+ free(entry->name);
+ free(entry);
+ }
+
list_for_each_entry_safe(entry, tmp, &info->expose_csr, list) {
free(entry->name);
free(entry);
@@ -524,16 +505,121 @@ static void trigger_from_breakpoint(struct trigger *trigger,
trigger->address = breakpoint->address;
trigger->length = breakpoint->length;
trigger->mask = ~0LL;
- trigger->read = false;
- trigger->write = false;
- trigger->execute = true;
+ trigger->is_read = false;
+ trigger->is_write = false;
+ trigger->is_execute = true;
/* unique_id is unique across both breakpoints and watchpoints. */
trigger->unique_id = breakpoint->unique_id;
}
-static int maybe_add_trigger_t1(struct target *target,
- struct trigger *trigger, uint64_t tdata1)
+static bool can_use_napot_match(struct trigger *trigger)
{
+ riscv_reg_t addr = trigger->address;
+ riscv_reg_t size = trigger->length;
+ bool sizePowerOf2 = (size & (size - 1)) == 0;
+ bool addrAligned = (addr & (size - 1)) == 0;
+ return size > 1 && sizePowerOf2 && addrAligned;
+}
+
+/* Find the next free trigger of the given type, without talking to the target. */
+static int find_next_free_trigger(struct target *target, int type, bool chained,
+ unsigned int *idx)
+{
+ assert(idx);
+ RISCV_INFO(r);
+
+ unsigned int num_found = 0;
+ unsigned int num_required = chained ? 2 : 1;
+
+ for (unsigned i = *idx; i < r->trigger_count; i++) {
+ if (r->trigger_unique_id[i] == -1) {
+ if (r->trigger_tinfo[i] & (1 << type)) {
+ num_found++;
+ if (num_required == num_found) {
+ /* Found num_required consecutive free triggers - success, done. */
+ *idx = i - (num_required - 1);
+ LOG_TARGET_DEBUG(target,
+ "%d trigger(s) of type %d found on index %u, "
+ "chained == %s",
+ num_required, type, *idx,
+ chained ? "true" : "false");
+ return ERROR_OK;
+ }
+ /* Found a trigger but need more consecutive ones */
+ continue;
+ }
+ }
+ /* Trigger already occupied or incompatible type.
+ * Reset the counter of found consecutive triggers */
+ num_found = 0;
+ }
+
+ return ERROR_FAIL;
+}
+
+static int find_first_trigger_by_id(struct target *target, int unique_id)
+{
+ RISCV_INFO(r);
+
+ for (unsigned i = 0; i < r->trigger_count; i++) {
+ if (r->trigger_unique_id[i] == unique_id)
+ return i;
+ }
+ return -1;
+}
+
+static int set_trigger(struct target *target, unsigned int idx, riscv_reg_t tdata1, riscv_reg_t tdata2,
+ riscv_reg_t tdata1_ignore_mask)
+{
+ riscv_reg_t tdata1_rb, tdata2_rb;
+ // Select which trigger to use
+ if (riscv_set_register(target, GDB_REGNO_TSELECT, idx) != ERROR_OK)
+ return ERROR_FAIL;
+
+ // Disable the trigger by writing 0 to it
+ if (riscv_set_register(target, GDB_REGNO_TDATA1, 0) != ERROR_OK)
+ return ERROR_FAIL;
+
+ // Set trigger data for tdata2 (and tdata3 if it was supported)
+ if (riscv_set_register(target, GDB_REGNO_TDATA2, tdata2) != ERROR_OK)
+ return ERROR_FAIL;
+
+ // Set trigger data for tdata1
+ if (riscv_set_register(target, GDB_REGNO_TDATA1, tdata1) != ERROR_OK)
+ return ERROR_FAIL;
+
+ // Read back tdata1, tdata2, (tdata3), and check if the configuration is supported
+ if (riscv_get_register(target, &tdata1_rb, GDB_REGNO_TDATA1) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_get_register(target, &tdata2_rb, GDB_REGNO_TDATA2) != ERROR_OK)
+ return ERROR_FAIL;
+ bool tdata1_config_denied = (tdata1 & ~tdata1_ignore_mask) != (tdata1_rb & ~tdata1_ignore_mask);
+ bool tdata2_config_denied = tdata2 != tdata2_rb;
+ if (tdata1_config_denied || tdata2_config_denied) {
+ LOG_TARGET_DEBUG(target, "Trigger %u doesn't support what we need.", idx);
+
+ if (tdata1_config_denied)
+ LOG_TARGET_DEBUG(target,
+ "After writing 0x%" PRIx64 " to tdata1 it contains 0x%" PRIx64 "; tdata1_ignore_mask=0x%" PRIx64,
+ tdata1, tdata1_rb, tdata1_ignore_mask);
+
+ if (tdata2_config_denied)
+ LOG_TARGET_DEBUG(target,
+ "wrote 0x%" PRIx64 " to tdata2 but read back 0x%" PRIx64,
+ tdata2, tdata2_rb);
+
+ riscv_set_register(target, GDB_REGNO_TDATA1, 0);
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+
+ return ERROR_OK;
+}
+
+static int maybe_add_trigger_t1(struct target *target, struct trigger *trigger)
+{
+ int ret;
+ riscv_reg_t tdata1, tdata2;
+
RISCV_INFO(r);
const uint32_t bpcontrol_x = 1<<0;
@@ -546,202 +632,390 @@ static int maybe_add_trigger_t1(struct target *target,
const uint32_t bpcontrol_bpmatch = 0xf << 7;
const uint32_t bpcontrol_bpaction = 0xff << 11;
+ unsigned int idx = 0;
+ ret = find_next_free_trigger(target, CSR_TDATA1_TYPE_LEGACY, false, &idx);
+ if (ret != ERROR_OK)
+ return ret;
+
+ if (riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK)
+ return ERROR_FAIL;
if (tdata1 & (bpcontrol_r | bpcontrol_w | bpcontrol_x)) {
/* Trigger is already in use, presumably by user code. */
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
- tdata1 = set_field(tdata1, bpcontrol_r, trigger->read);
- tdata1 = set_field(tdata1, bpcontrol_w, trigger->write);
- tdata1 = set_field(tdata1, bpcontrol_x, trigger->execute);
- tdata1 = set_field(tdata1, bpcontrol_u,
- !!(r->misa & BIT('U' - 'A')));
- tdata1 = set_field(tdata1, bpcontrol_s,
- !!(r->misa & BIT('S' - 'A')));
- tdata1 = set_field(tdata1, bpcontrol_h,
- !!(r->misa & BIT('H' - 'A')));
- tdata1 |= bpcontrol_m;
- tdata1 = set_field(tdata1, bpcontrol_bpmatch, 0); /* exact match */
+ tdata1 = 0;
+ tdata1 = set_field(tdata1, bpcontrol_r, trigger->is_read);
+ tdata1 = set_field(tdata1, bpcontrol_w, trigger->is_write);
+ tdata1 = set_field(tdata1, bpcontrol_x, trigger->is_execute);
+ tdata1 = set_field(tdata1, bpcontrol_u, !!(r->misa & BIT('U' - 'A')));
+ tdata1 = set_field(tdata1, bpcontrol_s, !!(r->misa & BIT('S' - 'A')));
+ tdata1 = set_field(tdata1, bpcontrol_h, !!(r->misa & BIT('H' - 'A')));
+ tdata1 = set_field(tdata1, bpcontrol_m, 1);
tdata1 = set_field(tdata1, bpcontrol_bpaction, 0); /* cause bp exception */
+ tdata1 = set_field(tdata1, bpcontrol_bpmatch, 0); /* exact match */
+ tdata2 = trigger->address;
+ ret = set_trigger(target, idx, tdata1, tdata2, 0);
+ if (ret != ERROR_OK)
+ return ret;
+ r->trigger_unique_id[idx] = trigger->unique_id;
+ return ERROR_OK;
+}
- riscv_set_register(target, GDB_REGNO_TDATA1, tdata1);
+struct trigger_request_info {
+ riscv_reg_t tdata1;
+ riscv_reg_t tdata2;
+ riscv_reg_t tdata1_ignore_mask;
+};
- riscv_reg_t tdata1_rb;
- if (riscv_get_register(target, &tdata1_rb, GDB_REGNO_TDATA1) != ERROR_OK)
- return ERROR_FAIL;
- LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb);
+static int try_setup_single_match_trigger(struct target *target,
+ struct trigger *trigger, struct trigger_request_info trig_info)
+{
+ int trigger_type =
+ get_field(trig_info.tdata1, CSR_MCONTROL_TYPE(riscv_xlen(target)));
+ int ret = ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ RISCV_INFO(r);
- if (tdata1 != tdata1_rb) {
- LOG_DEBUG("Trigger doesn't support what we need; After writing 0x%"
- PRIx64 " to tdata1 it contains 0x%" PRIx64,
- tdata1, tdata1_rb);
- riscv_set_register(target, GDB_REGNO_TDATA1, 0);
- return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ /* Find the first trigger, supporting required tdata1 value */
+ for (unsigned int idx = 0;
+ find_next_free_trigger(target, trigger_type, false, &idx) == ERROR_OK;
+ ++idx) {
+ ret = set_trigger(target, idx, trig_info.tdata1, trig_info.tdata2,
+ trig_info.tdata1_ignore_mask);
+ if (ret == ERROR_OK) {
+ r->trigger_unique_id[idx] = trigger->unique_id;
+ return ERROR_OK;
+ }
+ if (ret != ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
+ return ret;
}
-
- riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address);
-
- return ERROR_OK;
+ return ret;
}
-static int maybe_add_trigger_t2(struct target *target,
- struct trigger *trigger, uint64_t tdata1)
+static int try_setup_chained_match_triggers(struct target *target,
+ struct trigger *trigger, struct trigger_request_info t1,
+ struct trigger_request_info t2)
{
+ int trigger_type =
+ get_field(t1.tdata1, CSR_MCONTROL_TYPE(riscv_xlen(target)));
+ int ret = ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
RISCV_INFO(r);
- /* tselect is already set */
- if (tdata1 & (MCONTROL_EXECUTE | MCONTROL_STORE | MCONTROL_LOAD)) {
- /* Trigger is already in use, presumably by user code. */
- return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ /* Find the first 2 consecutive triggers, supporting required tdata1 values */
+ for (unsigned int idx = 0;
+ find_next_free_trigger(target, trigger_type, true, &idx) == ERROR_OK;
+ ++idx) {
+ ret = set_trigger(target, idx, t1.tdata1, t1.tdata2,
+ t1.tdata1_ignore_mask);
+ if (ret != ERROR_OK)
+ continue;
+ ret = set_trigger(target, idx + 1, t2.tdata1, t2.tdata2,
+ t2.tdata1_ignore_mask);
+ if (ret == ERROR_OK) {
+ r->trigger_unique_id[idx] = trigger->unique_id;
+ r->trigger_unique_id[idx + 1] = trigger->unique_id;
+ return ERROR_OK;
+ }
+ /* Undo the setting of the previous trigger */
+ int ret_undo = set_trigger(target, idx, 0, 0, 0);
+ if (ret_undo != ERROR_OK)
+ return ret_undo;
+
+ if (ret != ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
+ return ret;
}
+ return ret;
+}
- /* address/data match trigger */
- tdata1 |= MCONTROL_DMODE(riscv_xlen(target));
- tdata1 = set_field(tdata1, MCONTROL_ACTION,
- MCONTROL_ACTION_DEBUG_MODE);
- tdata1 = set_field(tdata1, MCONTROL_MATCH, MCONTROL_MATCH_EQUAL);
- tdata1 |= MCONTROL_M;
- if (r->misa & (1 << ('S' - 'A')))
- tdata1 |= MCONTROL_S;
- if (r->misa & (1 << ('U' - 'A')))
- tdata1 |= MCONTROL_U;
-
- if (trigger->execute)
- tdata1 |= MCONTROL_EXECUTE;
- if (trigger->read)
- tdata1 |= MCONTROL_LOAD;
- if (trigger->write)
- tdata1 |= MCONTROL_STORE;
-
- riscv_set_register(target, GDB_REGNO_TDATA1, tdata1);
-
- uint64_t tdata1_rb;
- int result = riscv_get_register(target, &tdata1_rb, GDB_REGNO_TDATA1);
- if (result != ERROR_OK)
- return result;
- LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb);
+struct match_triggers_tdata1_fields {
+ riscv_reg_t common;
+ struct {
+ riscv_reg_t any;
+ riscv_reg_t s8bit;
+ } size;
+ struct {
+ riscv_reg_t enable;
+ riscv_reg_t disable;
+ } chain;
+ struct {
+ riscv_reg_t napot;
+ riscv_reg_t lt;
+ riscv_reg_t ge;
+ riscv_reg_t eq;
+ } match;
+ riscv_reg_t tdata1_ignore_mask;
+};
- if (tdata1 != tdata1_rb) {
- LOG_DEBUG("Trigger doesn't support what we need; After writing 0x%"
- PRIx64 " to tdata1 it contains 0x%" PRIx64,
- tdata1, tdata1_rb);
- riscv_set_register(target, GDB_REGNO_TDATA1, 0);
- return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
- }
+static struct match_triggers_tdata1_fields fill_match_triggers_tdata1_fields_t2(
+ struct target *target, struct trigger *trigger)
+{
+ RISCV_INFO(r);
- riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address);
+ struct match_triggers_tdata1_fields result = {
+ .common =
+ field_value(CSR_MCONTROL_TYPE(riscv_xlen(target)), CSR_TDATA1_TYPE_MCONTROL) |
+ field_value(CSR_MCONTROL_DMODE(riscv_xlen(target)), 1) |
+ field_value(CSR_MCONTROL_ACTION, CSR_MCONTROL_ACTION_DEBUG_MODE) |
+ field_value(CSR_MCONTROL_M, 1) |
+ field_value(CSR_MCONTROL_S, !!(r->misa & BIT('S' - 'A'))) |
+ field_value(CSR_MCONTROL_U, !!(r->misa & BIT('U' - 'A'))) |
+ field_value(CSR_MCONTROL_EXECUTE, trigger->is_execute) |
+ field_value(CSR_MCONTROL_LOAD, trigger->is_read) |
+ field_value(CSR_MCONTROL_STORE, trigger->is_write),
+ .size = {
+ .any =
+ field_value(CSR_MCONTROL_SIZELO, CSR_MCONTROL_SIZELO_ANY & 3) |
+ field_value(CSR_MCONTROL_SIZEHI, (CSR_MCONTROL_SIZELO_ANY >> 2) & 3),
+ .s8bit =
+ field_value(CSR_MCONTROL_SIZELO, CSR_MCONTROL_SIZELO_8BIT & 3) |
+ field_value(CSR_MCONTROL_SIZEHI, (CSR_MCONTROL_SIZELO_8BIT >> 2) & 3)
+ },
+ .chain = {
+ .enable = field_value(CSR_MCONTROL_CHAIN, CSR_MCONTROL_CHAIN_ENABLED),
+ .disable = field_value(CSR_MCONTROL_CHAIN, CSR_MCONTROL_CHAIN_DISABLED)
+ },
+ .match = {
+ .napot = field_value(CSR_MCONTROL_MATCH, CSR_MCONTROL_MATCH_NAPOT),
+ .lt = field_value(CSR_MCONTROL_MATCH, CSR_MCONTROL_MATCH_LT),
+ .ge = field_value(CSR_MCONTROL_MATCH, CSR_MCONTROL_MATCH_GE),
+ .eq = field_value(CSR_MCONTROL_MATCH, CSR_MCONTROL_MATCH_EQUAL)
+ },
+ .tdata1_ignore_mask = CSR_MCONTROL_MASKMAX(riscv_xlen(target))
+ };
+ return result;
+}
- return ERROR_OK;
+static struct match_triggers_tdata1_fields fill_match_triggers_tdata1_fields_t6(
+ struct target *target, struct trigger *trigger)
+{
+ bool misa_s = riscv_supports_extension(target, 'S');
+ bool misa_u = riscv_supports_extension(target, 'U');
+ bool misa_h = riscv_supports_extension(target, 'H');
+
+ struct match_triggers_tdata1_fields result = {
+ .common =
+ field_value(CSR_MCONTROL6_TYPE(riscv_xlen(target)), CSR_TDATA1_TYPE_MCONTROL6) |
+ field_value(CSR_MCONTROL6_DMODE(riscv_xlen(target)), 1) |
+ field_value(CSR_MCONTROL6_ACTION, CSR_MCONTROL_ACTION_DEBUG_MODE) |
+ field_value(CSR_MCONTROL6_M, 1) |
+ field_value(CSR_MCONTROL6_S, misa_s) |
+ field_value(CSR_MCONTROL6_U, misa_u) |
+ field_value(CSR_MCONTROL6_VS, misa_h && misa_s) |
+ field_value(CSR_MCONTROL6_VU, misa_h && misa_u) |
+ field_value(CSR_MCONTROL6_EXECUTE, trigger->is_execute) |
+ field_value(CSR_MCONTROL6_LOAD, trigger->is_read) |
+ field_value(CSR_MCONTROL6_STORE, trigger->is_write),
+ .size = {
+ .any = field_value(CSR_MCONTROL6_SIZE, CSR_MCONTROL6_SIZE_ANY),
+ .s8bit = field_value(CSR_MCONTROL6_SIZE, CSR_MCONTROL6_SIZE_8BIT)
+ },
+ .chain = {
+ .enable = field_value(CSR_MCONTROL6_CHAIN, CSR_MCONTROL6_CHAIN_ENABLED),
+ .disable = field_value(CSR_MCONTROL6_CHAIN, CSR_MCONTROL6_CHAIN_DISABLED)
+ },
+ .match = {
+ .napot = field_value(CSR_MCONTROL6_MATCH, CSR_MCONTROL6_MATCH_NAPOT),
+ .lt = field_value(CSR_MCONTROL6_MATCH, CSR_MCONTROL6_MATCH_LT),
+ .ge = field_value(CSR_MCONTROL6_MATCH, CSR_MCONTROL6_MATCH_GE),
+ .eq = field_value(CSR_MCONTROL6_MATCH, CSR_MCONTROL6_MATCH_EQUAL)
+ },
+ .tdata1_ignore_mask = 0
+ };
+ return result;
}
-static int maybe_add_trigger_t6(struct target *target,
- struct trigger *trigger, uint64_t tdata1)
+static int maybe_add_trigger_t2_t6(struct target *target,
+ struct trigger *trigger, struct match_triggers_tdata1_fields fields)
{
- RISCV_INFO(r);
+ int ret = ERROR_OK;
- /* tselect is already set */
- if (tdata1 & (CSR_MCONTROL6_EXECUTE | CSR_MCONTROL6_STORE | CSR_MCONTROL6_LOAD)) {
- /* Trigger is already in use, presumably by user code. */
- return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
- }
+ if (!trigger->is_execute && trigger->length > 1) {
+ /* Setting a load/store trigger ("watchpoint") on a range of addresses */
- /* address/data match trigger */
- tdata1 |= MCONTROL_DMODE(riscv_xlen(target));
- tdata1 = set_field(tdata1, CSR_MCONTROL6_ACTION,
- MCONTROL_ACTION_DEBUG_MODE);
- tdata1 = set_field(tdata1, CSR_MCONTROL6_MATCH, MCONTROL_MATCH_EQUAL);
- tdata1 |= CSR_MCONTROL6_M;
- if (r->misa & (1 << ('H' - 'A')))
- tdata1 |= CSR_MCONTROL6_VS | CSR_MCONTROL6_VU;
- if (r->misa & (1 << ('S' - 'A')))
- tdata1 |= CSR_MCONTROL6_S;
- if (r->misa & (1 << ('U' - 'A')))
- tdata1 |= CSR_MCONTROL6_U;
-
- if (trigger->execute)
- tdata1 |= CSR_MCONTROL6_EXECUTE;
- if (trigger->read)
- tdata1 |= CSR_MCONTROL6_LOAD;
- if (trigger->write)
- tdata1 |= CSR_MCONTROL6_STORE;
-
- riscv_set_register(target, GDB_REGNO_TDATA1, tdata1);
-
- uint64_t tdata1_rb;
- int result = riscv_get_register(target, &tdata1_rb, GDB_REGNO_TDATA1);
- if (result != ERROR_OK)
- return result;
- LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb);
+ if (can_use_napot_match(trigger)) {
+ LOG_TARGET_DEBUG(target, "trying to setup NAPOT match trigger");
+ struct trigger_request_info napot = {
+ .tdata1 = fields.common | fields.size.any |
+ fields.chain.disable | fields.match.napot,
+ .tdata2 = trigger->address | ((trigger->length - 1) >> 1),
+ .tdata1_ignore_mask = fields.tdata1_ignore_mask
+ };
+ ret = try_setup_single_match_trigger(target, trigger, napot);
+ if (ret != ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
+ return ret;
+ }
+ LOG_TARGET_DEBUG(target, "trying to setup GE+LT chained match trigger pair");
+ struct trigger_request_info ge_1 = {
+ .tdata1 = fields.common | fields.size.any | fields.chain.enable |
+ fields.match.ge,
+ .tdata2 = trigger->address,
+ .tdata1_ignore_mask = fields.tdata1_ignore_mask
+ };
+ struct trigger_request_info lt_2 = {
+ .tdata1 = fields.common | fields.size.any | fields.chain.disable |
+ fields.match.lt,
+ .tdata2 = trigger->address + trigger->length,
+ .tdata1_ignore_mask = fields.tdata1_ignore_mask
+ };
+ ret = try_setup_chained_match_triggers(target, trigger, ge_1, lt_2);
+ if (ret != ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
+ return ret;
+
+ LOG_TARGET_DEBUG(target, "trying to setup LT+GE chained match trigger pair");
+ struct trigger_request_info lt_1 = {
+ .tdata1 = fields.common | fields.size.any | fields.chain.enable |
+ fields.match.lt,
+ .tdata2 = trigger->address,
+ .tdata1_ignore_mask = fields.tdata1_ignore_mask
+ };
+ struct trigger_request_info ge_2 = {
+ .tdata1 = fields.common | fields.size.any | fields.chain.disable |
+ fields.match.ge,
+ .tdata2 = trigger->address + trigger->length,
+ .tdata1_ignore_mask = fields.tdata1_ignore_mask
+ };
+ ret = try_setup_chained_match_triggers(target, trigger, lt_1, ge_2);
+ if (ret != ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
+ return ret;
+ }
+ LOG_TARGET_DEBUG(target, "trying to setup equality match trigger");
+ struct trigger_request_info eq = {
+ .tdata1 = fields.common |
+ (trigger->length == 1 ? fields.size.s8bit : fields.size.any) |
+ fields.chain.disable | fields.match.eq,
+ .tdata2 = trigger->address,
+ .tdata1_ignore_mask = fields.tdata1_ignore_mask
+ };
+ return try_setup_single_match_trigger(target, trigger, eq);
+}
- if (tdata1 != tdata1_rb) {
- LOG_DEBUG("Trigger doesn't support what we need; After writing 0x%"
- PRIx64 " to tdata1 it contains 0x%" PRIx64,
- tdata1, tdata1_rb);
- riscv_set_register(target, GDB_REGNO_TDATA1, 0);
- return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
- }
+static int maybe_add_trigger_t3(struct target *target, bool vs, bool vu,
+ bool m, bool s, bool u, bool pending, unsigned int count,
+ int unique_id)
+{
+ int ret;
+ riscv_reg_t tdata1;
- riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address);
+ RISCV_INFO(r);
+ tdata1 = 0;
+ tdata1 = set_field(tdata1, CSR_ICOUNT_TYPE(riscv_xlen(target)), CSR_TDATA1_TYPE_ICOUNT);
+ tdata1 = set_field(tdata1, CSR_ICOUNT_DMODE(riscv_xlen(target)), 1);
+ tdata1 = set_field(tdata1, CSR_ICOUNT_ACTION, CSR_ICOUNT_ACTION_DEBUG_MODE);
+ tdata1 = set_field(tdata1, CSR_ICOUNT_VS, vs);
+ tdata1 = set_field(tdata1, CSR_ICOUNT_VU, vu);
+ tdata1 = set_field(tdata1, CSR_ICOUNT_PENDING, pending);
+ tdata1 = set_field(tdata1, CSR_ICOUNT_M, m);
+ tdata1 = set_field(tdata1, CSR_ICOUNT_S, s);
+ tdata1 = set_field(tdata1, CSR_ICOUNT_U, u);
+ tdata1 = set_field(tdata1, CSR_ICOUNT_COUNT, count);
+
+ unsigned int idx = 0;
+ ret = find_next_free_trigger(target, CSR_TDATA1_TYPE_ICOUNT, false, &idx);
+ if (ret != ERROR_OK)
+ return ret;
+ ret = set_trigger(target, idx, tdata1, 0, 0);
+ if (ret != ERROR_OK)
+ return ret;
+ r->trigger_unique_id[idx] = unique_id;
return ERROR_OK;
}
-static int add_trigger(struct target *target, struct trigger *trigger)
+static int maybe_add_trigger_t4(struct target *target, bool vs, bool vu,
+ bool nmi, bool m, bool s, bool u, riscv_reg_t interrupts,
+ int unique_id)
{
+ int ret;
+ riscv_reg_t tdata1, tdata2;
+
RISCV_INFO(r);
- if (riscv_enumerate_triggers(target) != ERROR_OK)
- return ERROR_FAIL;
+ tdata1 = 0;
+ tdata1 = set_field(tdata1, CSR_ITRIGGER_TYPE(riscv_xlen(target)), CSR_TDATA1_TYPE_ITRIGGER);
+ tdata1 = set_field(tdata1, CSR_ITRIGGER_DMODE(riscv_xlen(target)), 1);
+ tdata1 = set_field(tdata1, CSR_ITRIGGER_ACTION, CSR_ITRIGGER_ACTION_DEBUG_MODE);
+ tdata1 = set_field(tdata1, CSR_ITRIGGER_VS, vs);
+ tdata1 = set_field(tdata1, CSR_ITRIGGER_VU, vu);
+ tdata1 = set_field(tdata1, CSR_ITRIGGER_NMI, nmi);
+ tdata1 = set_field(tdata1, CSR_ITRIGGER_M, m);
+ tdata1 = set_field(tdata1, CSR_ITRIGGER_S, s);
+ tdata1 = set_field(tdata1, CSR_ITRIGGER_U, u);
+
+ tdata2 = interrupts;
+
+ unsigned int idx = 0;
+ ret = find_next_free_trigger(target, CSR_TDATA1_TYPE_ITRIGGER, false, &idx);
+ if (ret != ERROR_OK)
+ return ret;
+ ret = set_trigger(target, idx, tdata1, tdata2, 0);
+ if (ret != ERROR_OK)
+ return ret;
+ r->trigger_unique_id[idx] = unique_id;
+ return ERROR_OK;
+}
- riscv_reg_t tselect;
- if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK)
- return ERROR_FAIL;
+static int maybe_add_trigger_t5(struct target *target, bool vs, bool vu,
+ bool m, bool s, bool u, riscv_reg_t exception_codes,
+ int unique_id)
+{
+ int ret;
+ riscv_reg_t tdata1, tdata2;
- unsigned int i;
- for (i = 0; i < r->trigger_count; i++) {
- if (r->trigger_unique_id[i] != -1)
- continue;
+ RISCV_INFO(r);
- riscv_set_register(target, GDB_REGNO_TSELECT, i);
+ tdata1 = 0;
+ tdata1 = set_field(tdata1, CSR_ETRIGGER_TYPE(riscv_xlen(target)), CSR_TDATA1_TYPE_ETRIGGER);
+ tdata1 = set_field(tdata1, CSR_ETRIGGER_DMODE(riscv_xlen(target)), 1);
+ tdata1 = set_field(tdata1, CSR_ETRIGGER_ACTION, CSR_ETRIGGER_ACTION_DEBUG_MODE);
+ tdata1 = set_field(tdata1, CSR_ETRIGGER_VS, vs);
+ tdata1 = set_field(tdata1, CSR_ETRIGGER_VU, vu);
+ tdata1 = set_field(tdata1, CSR_ETRIGGER_M, m);
+ tdata1 = set_field(tdata1, CSR_ETRIGGER_S, s);
+ tdata1 = set_field(tdata1, CSR_ETRIGGER_U, u);
+
+ tdata2 = exception_codes;
+
+ unsigned int idx = 0;
+ ret = find_next_free_trigger(target, CSR_TDATA1_TYPE_ETRIGGER, false, &idx);
+ if (ret != ERROR_OK)
+ return ret;
+ ret = set_trigger(target, idx, tdata1, tdata2, 0);
+ if (ret != ERROR_OK)
+ return ret;
+ r->trigger_unique_id[idx] = unique_id;
+ return ERROR_OK;
+}
- uint64_t tdata1;
- int result = riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1);
- if (result != ERROR_OK)
- return result;
- int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target)));
+static int add_trigger(struct target *target, struct trigger *trigger)
+{
+ int ret;
+ riscv_reg_t tselect;
- switch (type) {
- case 1:
- result = maybe_add_trigger_t1(target, trigger, tdata1);
- break;
- case 2:
- result = maybe_add_trigger_t2(target, trigger, tdata1);
- break;
- case 6:
- result = maybe_add_trigger_t6(target, trigger, tdata1);
- break;
- default:
- LOG_DEBUG("trigger %d has unknown type %d", i, type);
- continue;
- }
+ ret = riscv_enumerate_triggers(target);
+ if (ret != ERROR_OK)
+ return ret;
- if (result != ERROR_OK)
- continue;
+ ret = riscv_get_register(target, &tselect, GDB_REGNO_TSELECT);
+ if (ret != ERROR_OK)
+ return ret;
- LOG_DEBUG("[%d] Using trigger %d (type %d) for bp %d", target->coreid,
- i, type, trigger->unique_id);
- r->trigger_unique_id[i] = trigger->unique_id;
- break;
- }
+ do {
+ ret = maybe_add_trigger_t1(target, trigger);
+ if (ret == ERROR_OK)
+ break;
+ ret = maybe_add_trigger_t2_t6(target, trigger,
+ fill_match_triggers_tdata1_fields_t2(target, trigger));
+ if (ret == ERROR_OK)
+ break;
+ ret = maybe_add_trigger_t2_t6(target, trigger,
+ fill_match_triggers_tdata1_fields_t6(target, trigger));
+ if (ret == ERROR_OK)
+ break;
+ } while (0);
riscv_set_register(target, GDB_REGNO_TSELECT, tselect);
- if (i >= r->trigger_count) {
- LOG_ERROR("Couldn't find an available hardware trigger.");
- return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
- }
-
- return ERROR_OK;
+ return ret;
}
/**
@@ -864,7 +1138,7 @@ int riscv_read_by_any_size(struct target *target, target_addr_t address, uint32_
static int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint)
{
- LOG_DEBUG("[%d] @0x%" TARGET_PRIxADDR, target->coreid, breakpoint->address);
+ LOG_TARGET_DEBUG(target, "@0x%" TARGET_PRIxADDR, breakpoint->address);
assert(breakpoint);
if (breakpoint->type == BKPT_SOFT) {
/** @todo check RVC for size/alignment */
@@ -910,34 +1184,36 @@ static int riscv_add_breakpoint(struct target *target, struct breakpoint *breakp
return ERROR_OK;
}
-static int remove_trigger(struct target *target, struct trigger *trigger)
+static int remove_trigger(struct target *target, int unique_id)
{
RISCV_INFO(r);
if (riscv_enumerate_triggers(target) != ERROR_OK)
return ERROR_FAIL;
- unsigned int i;
- for (i = 0; i < r->trigger_count; i++) {
- if (r->trigger_unique_id[i] == trigger->unique_id)
- break;
+ riscv_reg_t tselect;
+ int result = riscv_get_register(target, &tselect, GDB_REGNO_TSELECT);
+ if (result != ERROR_OK)
+ return result;
+
+ bool done = false;
+ for (unsigned int i = 0; i < r->trigger_count; i++) {
+ if (r->trigger_unique_id[i] == unique_id) {
+ riscv_set_register(target, GDB_REGNO_TSELECT, i);
+ riscv_set_register(target, GDB_REGNO_TDATA1, 0);
+ r->trigger_unique_id[i] = -1;
+ LOG_TARGET_DEBUG(target, "Stop using resource %d for bp %d",
+ i, unique_id);
+ done = true;
+ }
}
- if (i >= r->trigger_count) {
+ if (!done) {
LOG_ERROR("Couldn't find the hardware resources used by hardware "
"trigger.");
- return ERROR_FAIL;
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
- LOG_DEBUG("[%d] Stop using resource %d for bp %d", target->coreid, i,
- trigger->unique_id);
- riscv_reg_t tselect;
- int result = riscv_get_register(target, &tselect, GDB_REGNO_TSELECT);
- if (result != ERROR_OK)
- return result;
- riscv_set_register(target, GDB_REGNO_TSELECT, i);
- riscv_set_register(target, GDB_REGNO_TDATA1, 0);
riscv_set_register(target, GDB_REGNO_TSELECT, tselect);
- r->trigger_unique_id[i] = -1;
return ERROR_OK;
}
@@ -957,7 +1233,7 @@ static int riscv_remove_breakpoint(struct target *target,
} else if (breakpoint->type == BKPT_HARD) {
struct trigger trigger;
trigger_from_breakpoint(&trigger, breakpoint);
- int result = remove_trigger(target, &trigger);
+ int result = remove_trigger(target, trigger.unique_id);
if (result != ERROR_OK)
return result;
@@ -978,9 +1254,9 @@ static void trigger_from_watchpoint(struct trigger *trigger,
trigger->length = watchpoint->length;
trigger->mask = watchpoint->mask;
trigger->value = watchpoint->value;
- trigger->read = (watchpoint->rw == WPT_READ || watchpoint->rw == WPT_ACCESS);
- trigger->write = (watchpoint->rw == WPT_WRITE || watchpoint->rw == WPT_ACCESS);
- trigger->execute = false;
+ trigger->is_read = (watchpoint->rw == WPT_READ || watchpoint->rw == WPT_ACCESS);
+ trigger->is_write = (watchpoint->rw == WPT_WRITE || watchpoint->rw == WPT_ACCESS);
+ trigger->is_execute = false;
/* unique_id is unique across both breakpoints and watchpoints. */
trigger->unique_id = watchpoint->unique_id;
}
@@ -1006,7 +1282,7 @@ int riscv_remove_watchpoint(struct target *target,
struct trigger trigger;
trigger_from_watchpoint(&trigger, watchpoint);
- int result = remove_trigger(target, &trigger);
+ int result = remove_trigger(target, trigger.unique_id);
if (result != ERROR_OK)
return result;
watchpoint->is_set = false;
@@ -1014,6 +1290,76 @@ int riscv_remove_watchpoint(struct target *target,
return ERROR_OK;
}
+/**
+ * Look at the trigger hit bits to find out which trigger is the reason we're
+ * halted. Sets *unique_id to the unique ID of that trigger. If *unique_id is
+ * ~0, no match was found.
+ */
+static int riscv_hit_trigger_hit_bit(struct target *target, uint32_t *unique_id)
+{
+ RISCV_INFO(r);
+
+ riscv_reg_t tselect;
+ if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK)
+ return ERROR_FAIL;
+
+ *unique_id = ~0;
+ for (unsigned int i = 0; i < r->trigger_count; i++) {
+ if (r->trigger_unique_id[i] == -1)
+ continue;
+
+ if (riscv_set_register(target, GDB_REGNO_TSELECT, i) != ERROR_OK)
+ return ERROR_FAIL;
+
+ uint64_t tdata1;
+ if (riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK)
+ return ERROR_FAIL;
+ int type = get_field(tdata1, CSR_TDATA1_TYPE(riscv_xlen(target)));
+
+ uint64_t hit_mask = 0;
+ switch (type) {
+ case CSR_TDATA1_TYPE_LEGACY:
+ /* Doesn't support hit bit. */
+ break;
+ case CSR_TDATA1_TYPE_MCONTROL:
+ hit_mask = CSR_MCONTROL_HIT;
+ break;
+ case CSR_TDATA1_TYPE_MCONTROL6:
+ hit_mask = CSR_MCONTROL6_HIT;
+ break;
+ case CSR_TDATA1_TYPE_ICOUNT:
+ hit_mask = CSR_ICOUNT_HIT;
+ break;
+ case CSR_TDATA1_TYPE_ITRIGGER:
+ hit_mask = CSR_ITRIGGER_HIT(riscv_xlen(target));
+ break;
+ case CSR_TDATA1_TYPE_ETRIGGER:
+ hit_mask = CSR_ETRIGGER_HIT(riscv_xlen(target));
+ break;
+ default:
+ LOG_DEBUG("trigger %d has unknown type %d", i, type);
+ continue;
+ }
+
+ /* Note: If we ever use chained triggers, then this logic needs
+ * to be changed to ignore triggers that are not the last one in
+ * the chain. */
+ if (tdata1 & hit_mask) {
+ LOG_DEBUG("Trigger %d (unique_id=%d) has hit bit set.", i, r->trigger_unique_id[i]);
+ if (riscv_set_register(target, GDB_REGNO_TDATA1, tdata1 & ~hit_mask) != ERROR_OK)
+ return ERROR_FAIL;
+
+ *unique_id = r->trigger_unique_id[i];
+ break;
+ }
+ }
+
+ if (riscv_set_register(target, GDB_REGNO_TSELECT, tselect) != ERROR_OK)
+ return ERROR_FAIL;
+
+ return ERROR_OK;
+}
+
/* Sets *hit_watchpoint to the first watchpoint identified as causing the
* current halt.
*
@@ -1022,14 +1368,19 @@ int riscv_remove_watchpoint(struct target *target,
* and new value. */
static int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoint)
{
- struct watchpoint *wp = target->watchpoints;
+ RISCV_INFO(r);
- LOG_DEBUG("Current hartid = %d", riscv_current_hartid(target));
+ LOG_TARGET_DEBUG(target, "Hit Watchpoint");
+
+ /* If we identified which trigger caused the halt earlier, then just use
+ * that. */
+ for (struct watchpoint *wp = target->watchpoints; wp; wp = wp->next) {
+ if (wp->unique_id == r->trigger_hit) {
+ *hit_watchpoint = wp;
+ return ERROR_OK;
+ }
+ }
- /*TODO instead of disassembling the instruction that we think caused the
- * trigger, check the hit bit of each watchpoint first. The hit bit is
- * simpler and more reliable to check but as it is optional and relatively
- * new, not all hardware will implement it */
riscv_reg_t dpc;
riscv_get_register(target, &dpc, GDB_REGNO_DPC);
const uint8_t length = 4;
@@ -1078,6 +1429,7 @@ static int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_w
return ERROR_FAIL;
}
+ struct watchpoint *wp = target->watchpoints;
while (wp) {
/*TODO support length/mask */
if (wp->address == mem_addr) {
@@ -1096,7 +1448,6 @@ static int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_w
return ERROR_FAIL;
}
-
static int oldriscv_step(struct target *target, int current, uint32_t address,
int handle_breakpoints)
{
@@ -1109,16 +1460,15 @@ static int old_or_new_riscv_step(struct target *target, int current,
{
RISCV_INFO(r);
LOG_DEBUG("handle_breakpoints=%d", handle_breakpoints);
- if (!r->is_halted)
+ if (!r->get_hart_state)
return oldriscv_step(target, current, address, handle_breakpoints);
else
return riscv_openocd_step(target, current, address, handle_breakpoints);
}
-
static int riscv_examine(struct target *target)
{
- LOG_DEBUG("riscv_examine()");
+ LOG_DEBUG("[%s]", target_name(target));
if (target_was_examined(target)) {
LOG_DEBUG("Target was already examined.");
return ERROR_OK;
@@ -1152,15 +1502,87 @@ static int oldriscv_poll(struct target *target)
static int old_or_new_riscv_poll(struct target *target)
{
RISCV_INFO(r);
- if (!r->is_halted)
+ if (!r->get_hart_state)
return oldriscv_poll(target);
else
return riscv_openocd_poll(target);
}
-int riscv_select_current_hart(struct target *target)
+static struct reg *get_reg_cache_entry(struct target *target,
+ unsigned int number)
{
- return riscv_set_current_hartid(target, target->coreid);
+ assert(target->reg_cache);
+ assert(target->reg_cache->reg_list);
+ assert(number < target->reg_cache->num_regs);
+ return &target->reg_cache->reg_list[number];
+}
+
+int riscv_flush_registers(struct target *target)
+{
+ RISCV_INFO(r);
+
+ if (!target->reg_cache)
+ return ERROR_OK;
+
+ LOG_TARGET_DEBUG(target, "Flushing register cache");
+
+ /* Writing non-GPR registers may require progbuf execution, and some GPRs
+ * may become dirty in the process (e.g. S0, S1). For that reason, flush
+ * registers in reverse order, so that GPRs are flushed last.
+ */
+ for (unsigned int number = target->reg_cache->num_regs; number-- > 0; ) {
+ struct reg *reg = get_reg_cache_entry(target, number);
+ if (reg->valid && reg->dirty) {
+ riscv_reg_t value = buf_get_u64(reg->value, 0, reg->size);
+
+ LOG_TARGET_DEBUG(target, "%s is dirty; write back 0x%" PRIx64,
+ reg->name, value);
+ if (r->set_register(target, number, value) != ERROR_OK)
+ return ERROR_FAIL;
+ reg->dirty = false;
+ }
+ }
+ LOG_TARGET_DEBUG(target, "Flush of register cache completed");
+ return ERROR_OK;
+}
+
+/**
+ * Set OpenOCD's generic debug reason from the RISC-V halt reason.
+ */
+int set_debug_reason(struct target *target, enum riscv_halt_reason halt_reason)
+{
+ RISCV_INFO(r);
+ r->trigger_hit = -1;
+ switch (halt_reason) {
+ case RISCV_HALT_EBREAK:
+ target->debug_reason = DBG_REASON_BREAKPOINT;
+ break;
+ case RISCV_HALT_TRIGGER:
+ if (riscv_hit_trigger_hit_bit(target, &r->trigger_hit) != ERROR_OK)
+ return ERROR_FAIL;
+ target->debug_reason = DBG_REASON_WATCHPOINT;
+ /* Check if we hit a hardware breakpoint. */
+ for (struct breakpoint *bp = target->breakpoints; bp; bp = bp->next) {
+ if (bp->unique_id == r->trigger_hit)
+ target->debug_reason = DBG_REASON_BREAKPOINT;
+ }
+ break;
+ case RISCV_HALT_INTERRUPT:
+ case RISCV_HALT_GROUP:
+ target->debug_reason = DBG_REASON_DBGRQ;
+ break;
+ case RISCV_HALT_SINGLESTEP:
+ target->debug_reason = DBG_REASON_SINGLESTEP;
+ break;
+ case RISCV_HALT_UNKNOWN:
+ target->debug_reason = DBG_REASON_UNDEFINED;
+ break;
+ case RISCV_HALT_ERROR:
+ return ERROR_FAIL;
+ }
+ LOG_DEBUG("[%s] debug_reason=%d", target_name(target), target->debug_reason);
+
+ return ERROR_OK;
}
static int halt_prep(struct target *target)
@@ -1169,11 +1591,11 @@ static int halt_prep(struct target *target)
LOG_DEBUG("[%s] prep hart, debug_reason=%d", target_name(target),
target->debug_reason);
- if (riscv_select_current_hart(target) != ERROR_OK)
- return ERROR_FAIL;
- if (riscv_is_halted(target)) {
- LOG_DEBUG("[%s] Hart is already halted (reason=%d).",
- target_name(target), target->debug_reason);
+ r->prepped = false;
+ if (target->state == TARGET_HALTED) {
+ LOG_TARGET_DEBUG(target, "Hart is already halted.");
+ } else if (target->state == TARGET_UNAVAILABLE) {
+ LOG_TARGET_DEBUG(target, "Hart is unavailable.");
} else {
if (r->halt_prep(target) != ERROR_OK)
return ERROR_FAIL;
@@ -1187,16 +1609,17 @@ static int riscv_halt_go_all_harts(struct target *target)
{
RISCV_INFO(r);
- if (riscv_select_current_hart(target) != ERROR_OK)
+ enum riscv_hart_state state;
+ if (riscv_get_hart_state(target, &state) != ERROR_OK)
return ERROR_FAIL;
- if (riscv_is_halted(target)) {
+ if (state == RISCV_STATE_HALTED) {
LOG_DEBUG("[%s] Hart is already halted.", target_name(target));
} else {
if (r->halt_go(target) != ERROR_OK)
return ERROR_FAIL;
- }
- riscv_invalidate_register_cache(target);
+ riscv_invalidate_register_cache(target);
+ }
return ERROR_OK;
}
@@ -1205,13 +1628,12 @@ static int halt_go(struct target *target)
{
RISCV_INFO(r);
int result;
- if (!r->is_halted) {
+ if (!r->get_hart_state) {
struct target_type *tt = get_target_type(target);
result = tt->halt(target);
} else {
result = riscv_halt_go_all_harts(target);
}
- target->state = TARGET_HALTED;
if (target->debug_reason == DBG_REASON_NOTHALTED)
target->debug_reason = DBG_REASON_DBGRQ;
@@ -1227,12 +1649,12 @@ int riscv_halt(struct target *target)
{
RISCV_INFO(r);
- if (!r->is_halted) {
+ if (!r->get_hart_state) {
struct target_type *tt = get_target_type(target);
return tt->halt(target);
}
- LOG_DEBUG("[%d] halting all harts", target->coreid);
+ LOG_TARGET_DEBUG(target, "halting all harts");
int result = ERROR_OK;
if (target->smp) {
@@ -1285,27 +1707,6 @@ static int riscv_deassert_reset(struct target *target)
return tt->deassert_reset(target);
}
-static int riscv_resume_prep_all_harts(struct target *target)
-{
- RISCV_INFO(r);
-
- LOG_DEBUG("[%s] prep hart", target_name(target));
- if (riscv_select_current_hart(target) != ERROR_OK)
- return ERROR_FAIL;
- if (riscv_is_halted(target)) {
- if (r->resume_prep(target) != ERROR_OK)
- return ERROR_FAIL;
- } else {
- LOG_DEBUG("[%s] hart requested resume, but was already resumed",
- target_name(target));
- }
-
- LOG_DEBUG("[%s] mark as prepped", target_name(target));
- r->prepped = true;
-
- return ERROR_OK;
-}
-
/* state must be riscv_reg_t state[RISCV_MAX_HWBPS] = {0}; */
static int disable_triggers(struct target *target, riscv_reg_t *state)
{
@@ -1327,7 +1728,7 @@ static int disable_triggers(struct target *target, riscv_reg_t *state)
riscv_reg_t tdata1;
if (riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK)
return ERROR_FAIL;
- if (tdata1 & MCONTROL_DMODE(riscv_xlen(target))) {
+ if (tdata1 & CSR_TDATA1_DMODE(riscv_xlen(target))) {
state[t] = tdata1;
if (riscv_set_register(target, GDB_REGNO_TDATA1, 0) != ERROR_OK)
return ERROR_FAIL;
@@ -1398,29 +1799,22 @@ static int enable_triggers(struct target *target, riscv_reg_t *state)
static int resume_prep(struct target *target, int current,
target_addr_t address, int handle_breakpoints, int debug_execution)
{
+ assert(target->state == TARGET_HALTED);
RISCV_INFO(r);
- LOG_DEBUG("[%d]", target->coreid);
-
- if (!current)
- riscv_set_register(target, GDB_REGNO_PC, address);
- if (target->debug_reason == DBG_REASON_WATCHPOINT) {
- /* To be able to run off a trigger, disable all the triggers, step, and
- * then resume as usual. */
- riscv_reg_t trigger_state[RISCV_MAX_HWBPS] = {0};
-
- if (disable_triggers(target, trigger_state) != ERROR_OK)
- return ERROR_FAIL;
-
- if (old_or_new_riscv_step(target, true, 0, false) != ERROR_OK)
- return ERROR_FAIL;
+ if (!current && riscv_set_register(target, GDB_REGNO_PC, address) != ERROR_OK)
+ return ERROR_FAIL;
- if (enable_triggers(target, trigger_state) != ERROR_OK)
+ if (handle_breakpoints) {
+ /* To be able to run off a trigger, we perform a step operation and then
+ * resume. If handle_breakpoints is true then step temporarily disables
+ * pending breakpoints so we can safely perform the step. */
+ if (old_or_new_riscv_step(target, current, address, handle_breakpoints) != ERROR_OK)
return ERROR_FAIL;
}
- if (r->is_halted) {
- if (riscv_resume_prep_all_harts(target) != ERROR_OK)
+ if (r->get_hart_state) {
+ if (r->resume_prep(target) != ERROR_OK)
return ERROR_FAIL;
}
@@ -1437,9 +1831,10 @@ static int resume_prep(struct target *target, int current,
static int resume_go(struct target *target, int current,
target_addr_t address, int handle_breakpoints, int debug_execution)
{
+ assert(target->state == TARGET_HALTED);
RISCV_INFO(r);
int result;
- if (!r->is_halted) {
+ if (!r->get_hart_state) {
struct target_type *tt = get_target_type(target);
result = tt->resume(target, current, address, handle_breakpoints,
debug_execution);
@@ -1450,13 +1845,15 @@ static int resume_go(struct target *target, int current,
return result;
}
-static int resume_finish(struct target *target)
+static int resume_finish(struct target *target, int debug_execution)
{
+ assert(target->state == TARGET_HALTED);
register_cache_invalidate(target->reg_cache);
- target->state = TARGET_RUNNING;
+ target->state = debug_execution ? TARGET_DEBUG_RUNNING : TARGET_RUNNING;
target->debug_reason = DBG_REASON_NOTHALTED;
- return target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
+ return target_call_event_callbacks(target,
+ debug_execution ? TARGET_EVENT_DEBUG_RESUMED : TARGET_EVENT_RESUMED);
}
/**
@@ -1471,57 +1868,100 @@ static int riscv_resume(
int debug_execution,
bool single_hart)
{
- LOG_DEBUG("handle_breakpoints=%d", handle_breakpoints);
int result = ERROR_OK;
- if (target->smp && !single_hart) {
- struct target_list *tlist;
- foreach_smp_target_direction(resume_order == RO_NORMAL,
- tlist, target->smp_targets) {
- struct target *t = tlist->target;
- if (resume_prep(t, current, address, handle_breakpoints,
- debug_execution) != ERROR_OK)
- result = ERROR_FAIL;
- }
- foreach_smp_target_direction(resume_order == RO_NORMAL,
- tlist, target->smp_targets) {
- struct target *t = tlist->target;
- struct riscv_info *i = riscv_info(t);
- if (i->prepped) {
- if (resume_go(t, current, address, handle_breakpoints,
- debug_execution) != ERROR_OK)
- result = ERROR_FAIL;
- }
- }
+ struct list_head *targets;
- foreach_smp_target_direction(resume_order == RO_NORMAL,
- tlist, target->smp_targets) {
- struct target *t = tlist->target;
- if (resume_finish(t) != ERROR_OK)
- return ERROR_FAIL;
- }
+ LIST_HEAD(single_target_list);
+ struct target_list single_target_entry = {
+ .lh = {NULL, NULL},
+ .target = target
+ };
+ if (target->smp && !single_hart) {
+ targets = target->smp_targets;
} else {
- if (resume_prep(target, current, address, handle_breakpoints,
- debug_execution) != ERROR_OK)
- result = ERROR_FAIL;
- if (resume_go(target, current, address, handle_breakpoints,
+ /* Make a list that just contains a single target, so we can
+ * share code below. */
+ list_add(&single_target_entry.lh, &single_target_list);
+ targets = &single_target_list;
+ }
+
+ LOG_TARGET_DEBUG(target, "current=%s, address=0x%"
+ TARGET_PRIxADDR ", handle_breakpoints=%s, debug_exec=%s",
+ current ? "true" : "false",
+ address,
+ handle_breakpoints ? "true" : "false",
+ debug_execution ? "true" : "false");
+
+ struct target_list *tlist;
+ foreach_smp_target_direction(resume_order == RO_NORMAL, tlist, targets) {
+ struct target *t = tlist->target;
+ LOG_TARGET_DEBUG(t, "target->state=%s", target_state_name(t));
+ if (t->state != TARGET_HALTED)
+ LOG_TARGET_DEBUG(t, "skipping this target: target not halted");
+ else if (resume_prep(t, current, address, handle_breakpoints,
debug_execution) != ERROR_OK)
result = ERROR_FAIL;
- if (resume_finish(target) != ERROR_OK)
- return ERROR_FAIL;
+ }
+
+ foreach_smp_target_direction(resume_order == RO_NORMAL, tlist, targets) {
+ struct target *t = tlist->target;
+ struct riscv_info *i = riscv_info(t);
+ if (i->prepped) {
+ if (resume_go(t, current, address, handle_breakpoints,
+ debug_execution) != ERROR_OK)
+ result = ERROR_FAIL;
+ }
+ }
+
+ foreach_smp_target_direction(resume_order == RO_NORMAL, tlist, targets) {
+ struct target *t = tlist->target;
+ if (t->state == TARGET_HALTED) {
+ if (resume_finish(t, debug_execution) != ERROR_OK)
+ result = ERROR_FAIL;
+ }
}
return result;
}
-static int riscv_target_resume(struct target *target, int current, target_addr_t address,
- int handle_breakpoints, int debug_execution)
+static int riscv_target_resume(struct target *target, int current,
+ target_addr_t address, int handle_breakpoints, int debug_execution)
{
+ if (target->state != TARGET_HALTED) {
+ LOG_TARGET_ERROR(target, "not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
return riscv_resume(target, current, address, handle_breakpoints,
debug_execution, false);
}
+static int riscv_effective_privilege_mode(struct target *target, int *v_mode, int *effective_mode)
+{
+ riscv_reg_t priv;
+ if (riscv_get_register(target, &priv, GDB_REGNO_PRIV) != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to read priv register.");
+ return ERROR_FAIL;
+ }
+ *v_mode = get_field(priv, VIRT_PRIV_V);
+
+ riscv_reg_t mstatus;
+ if (riscv_get_register(target, &mstatus, GDB_REGNO_MSTATUS) != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to read mstatus register.");
+ return ERROR_FAIL;
+ }
+
+ if (get_field(mstatus, MSTATUS_MPRV))
+ *effective_mode = get_field(mstatus, MSTATUS_MPP);
+ else
+ *effective_mode = get_field(priv, VIRT_PRIV_PRV);
+
+ LOG_TARGET_DEBUG(target, "Effective mode=%d; v=%d", *effective_mode, *v_mode);
+
+ return ERROR_OK;
+}
+
static int riscv_mmu(struct target *target, int *enabled)
{
if (!riscv_enable_virt2phys) {
@@ -1536,14 +1976,63 @@ static int riscv_mmu(struct target *target, int *enabled)
return ERROR_FAIL;
}
- riscv_reg_t mstatus;
- if (riscv_get_register(target, &mstatus, GDB_REGNO_MSTATUS) != ERROR_OK) {
- LOG_ERROR("Failed to read mstatus register.");
+ int effective_mode;
+ int v_mode;
+ if (riscv_effective_privilege_mode(target, &v_mode, &effective_mode) != ERROR_OK)
return ERROR_FAIL;
+
+ unsigned int xlen = riscv_xlen(target);
+
+ if (v_mode) {
+ /* vsatp and hgatp registers are considered active for the
+ * purposes of the address-translation algorithm unless the
+ * effective privilege mode is U and hstatus.HU=0. */
+ if (effective_mode == PRV_U) {
+ riscv_reg_t hstatus;
+ if (riscv_get_register(target, &hstatus, GDB_REGNO_HSTATUS) != ERROR_OK) {
+ LOG_ERROR("Failed to read hstatus register.");
+ return ERROR_FAIL;
+ }
+
+ if (get_field(hstatus, HSTATUS_HU) == 0)
+ /* In hypervisor mode regular satp translation
+ * doesn't happen. */
+ return ERROR_OK;
+ }
+
+ riscv_reg_t vsatp;
+ if (riscv_get_register(target, &vsatp, GDB_REGNO_VSATP) != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to read vsatp register; priv=0x%" PRIx64,
+ priv);
+ return ERROR_FAIL;
+ }
+ /* vsatp is identical to satp, so we can use the satp macros. */
+ if (RISCV_SATP_MODE(xlen) != SATP_MODE_OFF) {
+ LOG_TARGET_DEBUG(target, "VS-stage translation is enabled.");
+ *enabled = 1;
+ return ERROR_OK;
+ }
+
+ riscv_reg_t hgatp;
+ if (riscv_get_register(target, &hgatp, GDB_REGNO_HGATP) != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to read hgatp register; priv=0x%" PRIx64,
+ priv);
+ return ERROR_FAIL;
+ }
+ if (RISCV_HGATP_MODE(xlen) != HGATP_MODE_OFF) {
+ LOG_TARGET_DEBUG(target, "G-stage address translation is enabled.");
+ *enabled = 1;
+ } else {
+ LOG_TARGET_DEBUG(target, "No V-mode address translation enabled.");
+ *enabled = 0;
+ }
+
+ return ERROR_OK;
}
- if ((get_field(mstatus, MSTATUS_MPRV) ? get_field(mstatus, MSTATUS_MPP) : priv) == PRV_M) {
- LOG_DEBUG("SATP/MMU ignored in Machine mode (mstatus=0x%" PRIx64 ").", mstatus);
+ /* Don't use MMU in explicit or effective M (machine) mode */
+ if (effective_mode == PRV_M) {
+ LOG_TARGET_DEBUG(target, "SATP/MMU ignored in Machine mode.");
*enabled = 0;
return ERROR_OK;
}
@@ -1556,7 +2045,7 @@ static int riscv_mmu(struct target *target, int *enabled)
return ERROR_OK;
}
- if (get_field(satp, RISCV_SATP_MODE(riscv_xlen(target))) == SATP_MODE_OFF) {
+ if (get_field(satp, RISCV_SATP_MODE(xlen)) == SATP_MODE_OFF) {
LOG_DEBUG("MMU is disabled.");
*enabled = 0;
} else {
@@ -1567,44 +2056,19 @@ static int riscv_mmu(struct target *target, int *enabled)
return ERROR_OK;
}
+/* Translate address from virtual to physical, using info and ppn.
+ * If extra_info is non-NULL, then translate page table accesses for the primary
+ * translation using extra_info and extra_ppn. */
static int riscv_address_translate(struct target *target,
+ const virt2phys_info_t *info, target_addr_t ppn,
+ const virt2phys_info_t *extra_info, target_addr_t extra_ppn,
target_addr_t virtual, target_addr_t *physical)
{
RISCV_INFO(r);
- riscv_reg_t satp_value;
- int mode;
- uint64_t ppn_value;
- target_addr_t table_address;
- const virt2phys_info_t *info;
- uint64_t pte = 0;
- int i;
+ unsigned int xlen = riscv_xlen(target);
- int result = riscv_get_register(target, &satp_value, GDB_REGNO_SATP);
- if (result != ERROR_OK)
- return result;
-
- unsigned xlen = riscv_xlen(target);
- mode = get_field(satp_value, RISCV_SATP_MODE(xlen));
- switch (mode) {
- case SATP_MODE_SV32:
- info = &sv32;
- break;
- case SATP_MODE_SV39:
- info = &sv39;
- break;
- case SATP_MODE_SV48:
- info = &sv48;
- break;
- case SATP_MODE_OFF:
- LOG_ERROR("No translation or protection." \
- " (satp: 0x%" PRIx64 ")", satp_value);
- return ERROR_FAIL;
- default:
- LOG_ERROR("The translation mode is not supported." \
- " (satp: 0x%" PRIx64 ")", satp_value);
- return ERROR_FAIL;
- }
- LOG_DEBUG("virtual=0x%" TARGET_PRIxADDR "; mode=%s", virtual, info->name);
+ LOG_TARGET_DEBUG(target, "mode=%s; ppn=0x%" TARGET_PRIxADDR "; virtual=0x%" TARGET_PRIxADDR,
+ info->name, ppn, virtual);
/* verify bits xlen-1:va_bits-1 are all equal */
assert(xlen >= info->va_bits);
@@ -1616,14 +2080,21 @@ static int riscv_address_translate(struct target *target,
return ERROR_FAIL;
}
- ppn_value = get_field(satp_value, RISCV_SATP_PPN(xlen));
- table_address = ppn_value << RISCV_PGSHIFT;
- i = info->level - 1;
+ uint64_t pte = 0;
+ target_addr_t table_address = ppn << RISCV_PGSHIFT;
+ int i = info->level - 1;
while (i >= 0) {
uint64_t vpn = virtual >> info->vpn_shift[i];
vpn &= info->vpn_mask[i];
- target_addr_t pte_address = table_address +
- (vpn << info->pte_shift);
+ target_addr_t pte_address = table_address + (vpn << info->pte_shift);
+
+ if (extra_info) {
+ /* Perform extra stage translation. */
+ if (riscv_address_translate(target, extra_info, extra_ppn,
+ NULL, 0, pte_address, &pte_address) != ERROR_OK)
+ return ERROR_FAIL;
+ }
+
uint8_t buffer[8];
assert(info->pte_shift <= 3);
int retval = r->read_memory(target, pte_address,
@@ -1639,17 +2110,20 @@ static int riscv_address_translate(struct target *target,
LOG_DEBUG("i=%d; PTE @0x%" TARGET_PRIxADDR " = 0x%" PRIx64, i,
pte_address, pte);
- if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W)))
+ if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) {
+ LOG_TARGET_ERROR(target, "invalid PTE @0x%" TARGET_PRIxADDR ": 0x%" PRIx64
+ "; mode=%s; i=%d", pte_address, pte, info->name, i);
return ERROR_FAIL;
+ }
- if ((pte & PTE_R) || (pte & PTE_X)) /* Found leaf PTE. */
+ if ((pte & PTE_R) || (pte & PTE_W) || (pte & PTE_X)) /* Found leaf PTE. */
break;
i--;
if (i < 0)
break;
- ppn_value = pte >> PTE_PPN_SHIFT;
- table_address = ppn_value << RISCV_PGSHIFT;
+ ppn = pte >> PTE_PPN_SHIFT;
+ table_address = ppn << RISCV_PGSHIFT;
}
if (i < 0) {
@@ -1661,15 +2135,115 @@ static int riscv_address_translate(struct target *target,
*physical = virtual & (((target_addr_t)1 << info->va_bits) - 1);
while (i < info->level) {
- ppn_value = pte >> info->pte_ppn_shift[i];
- ppn_value &= info->pte_ppn_mask[i];
+ ppn = pte >> info->pte_ppn_shift[i];
+ ppn &= info->pte_ppn_mask[i];
*physical &= ~(((target_addr_t)info->pa_ppn_mask[i]) <<
info->pa_ppn_shift[i]);
- *physical |= (ppn_value << info->pa_ppn_shift[i]);
+ *physical |= (ppn << info->pa_ppn_shift[i]);
i++;
}
- LOG_DEBUG("0x%" TARGET_PRIxADDR " -> 0x%" TARGET_PRIxADDR, virtual,
- *physical);
+ LOG_TARGET_DEBUG(target, "mode=%s; 0x%" TARGET_PRIxADDR " -> 0x%" TARGET_PRIxADDR,
+ info->name, virtual, *physical);
+ return ERROR_OK;
+}
+
+/* Virtual to physical translation for hypervisor mode. */
+static int riscv_virt2phys_v(struct target *target, target_addr_t virtual, target_addr_t *physical)
+{
+ riscv_reg_t vsatp;
+ if (riscv_get_register(target, &vsatp, GDB_REGNO_VSATP) != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to read vsatp register.");
+ return ERROR_FAIL;
+ }
+ /* vsatp is identical to satp, so we can use the satp macros. */
+ unsigned int xlen = riscv_xlen(target);
+ int vsatp_mode = get_field(vsatp, RISCV_SATP_MODE(xlen));
+ LOG_TARGET_DEBUG(target, "VS-stage translation mode: %d", vsatp_mode);
+ riscv_reg_t hgatp;
+ if (riscv_get_register(target, &hgatp, GDB_REGNO_HGATP) != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to read hgatp register.");
+ return ERROR_FAIL;
+ }
+ int hgatp_mode = get_field(vsatp, RISCV_HGATP_MODE(xlen));
+ LOG_TARGET_DEBUG(target, "G-stage translation mode: %d", hgatp_mode);
+
+ const virt2phys_info_t *vsatp_info;
+ /* VS-stage address translation. */
+ switch (vsatp_mode) {
+ case SATP_MODE_SV32:
+ vsatp_info = &sv32;
+ break;
+ case SATP_MODE_SV39:
+ vsatp_info = &sv39;
+ break;
+ case SATP_MODE_SV48:
+ vsatp_info = &sv48;
+ break;
+ case SATP_MODE_OFF:
+ vsatp_info = NULL;
+ break;
+ default:
+ LOG_TARGET_ERROR(target,
+ "vsatp mode %d is not supported. (vsatp: 0x%" PRIx64 ")",
+ vsatp_mode, vsatp);
+ return ERROR_FAIL;
+ }
+
+ const virt2phys_info_t *hgatp_info;
+ /* G-stage address translation. */
+ switch (hgatp_mode) {
+ case HGATP_MODE_SV32X4:
+ hgatp_info = &sv32x4;
+ break;
+ case HGATP_MODE_SV39X4:
+ hgatp_info = &sv39x4;
+ break;
+ case HGATP_MODE_SV48X4:
+ hgatp_info = &sv48x4;
+ break;
+ case HGATP_MODE_OFF:
+ hgatp_info = NULL;
+ break;
+ default:
+ LOG_TARGET_ERROR(target,
+ "hgatp mode %d is not supported. (hgatp: 0x%" PRIx64 ")",
+ hgatp_mode, hgatp);
+ return ERROR_FAIL;
+ }
+
+ /* For any virtual memory access, the original virtual address is
+ * converted in the first stage by VS-level address translation,
+ * as controlled by the vsatp register, into a guest physical
+ * address. */
+ target_addr_t guest_physical;
+ if (vsatp_info) {
+ /* When V=1, memory accesses that would normally bypass
+ * address translation are subject to G- stage address
+ * translation alone. This includes memory accesses made
+ * in support of VS-stage address translation, such as
+ * reads and writes of VS-level page tables. */
+
+ if (riscv_address_translate(target,
+ vsatp_info, get_field(vsatp, RISCV_SATP_PPN(xlen)),
+ hgatp_info, get_field(hgatp, RISCV_SATP_PPN(xlen)),
+ virtual, &guest_physical) != ERROR_OK)
+ return ERROR_FAIL;
+ } else {
+ guest_physical = virtual;
+ }
+
+ /* The guest physical address is then converted in the second
+ * stage by guest physical address translation, as controlled by
+ * the hgatp register, into a supervisor physical address. */
+ if (hgatp_info) {
+ if (riscv_address_translate(target,
+ hgatp_info, get_field(hgatp, RISCV_HGATP_PPN(xlen)),
+ NULL, 0,
+ guest_physical, physical) != ERROR_OK)
+ return ERROR_FAIL;
+ } else {
+ *physical = guest_physical;
+ }
return ERROR_OK;
}
@@ -1677,23 +2251,59 @@ static int riscv_address_translate(struct target *target,
static int riscv_virt2phys(struct target *target, target_addr_t virtual, target_addr_t *physical)
{
int enabled;
- if (riscv_mmu(target, &enabled) == ERROR_OK) {
- if (!enabled)
- return ERROR_FAIL;
+ if (riscv_mmu(target, &enabled) != ERROR_OK)
+ return ERROR_FAIL;
+ if (!enabled)
+ return ERROR_FAIL;
- if (riscv_address_translate(target, virtual, physical) == ERROR_OK)
- return ERROR_OK;
+
+ riscv_reg_t priv;
+ if (riscv_get_register(target, &priv, GDB_REGNO_PRIV) != ERROR_OK) {
+ LOG_ERROR("Failed to read priv register.");
+ return ERROR_FAIL;
}
- return ERROR_FAIL;
+ if (priv & VIRT_PRIV_V)
+ return riscv_virt2phys_v(target, virtual, physical);
+
+ riscv_reg_t satp_value;
+ int result = riscv_get_register(target, &satp_value, GDB_REGNO_SATP);
+ if (result != ERROR_OK)
+ return result;
+
+ unsigned int xlen = riscv_xlen(target);
+ int satp_mode = get_field(satp_value, RISCV_SATP_MODE(xlen));
+ const virt2phys_info_t *satp_info;
+ switch (satp_mode) {
+ case SATP_MODE_SV32:
+ satp_info = &sv32;
+ break;
+ case SATP_MODE_SV39:
+ satp_info = &sv39;
+ break;
+ case SATP_MODE_SV48:
+ satp_info = &sv48;
+ break;
+ case SATP_MODE_OFF:
+ LOG_ERROR("No translation or protection."
+ " (satp: 0x%" PRIx64 ")", satp_value);
+ return ERROR_FAIL;
+ default:
+ LOG_ERROR("The translation mode is not supported."
+ " (satp: 0x%" PRIx64 ")", satp_value);
+ return ERROR_FAIL;
+ }
+
+ return riscv_address_translate(target,
+ satp_info, get_field(satp_value, RISCV_SATP_PPN(xlen)),
+ NULL, 0,
+ virtual, physical);
}
static int riscv_read_phys_memory(struct target *target, target_addr_t phys_address,
uint32_t size, uint32_t count, uint8_t *buffer)
{
RISCV_INFO(r);
- if (riscv_select_current_hart(target) != ERROR_OK)
- return ERROR_FAIL;
return r->read_memory(target, phys_address, size, count, buffer, size);
}
@@ -1705,9 +2315,6 @@ static int riscv_read_memory(struct target *target, target_addr_t address,
return ERROR_OK;
}
- if (riscv_select_current_hart(target) != ERROR_OK)
- return ERROR_FAIL;
-
target_addr_t physical_addr;
if (target->type->virt2phys(target, address, &physical_addr) == ERROR_OK)
address = physical_addr;
@@ -1719,8 +2326,6 @@ static int riscv_read_memory(struct target *target, target_addr_t address,
static int riscv_write_phys_memory(struct target *target, target_addr_t phys_address,
uint32_t size, uint32_t count, const uint8_t *buffer)
{
- if (riscv_select_current_hart(target) != ERROR_OK)
- return ERROR_FAIL;
struct target_type *tt = get_target_type(target);
return tt->write_memory(target, phys_address, size, count, buffer);
}
@@ -1733,9 +2338,6 @@ static int riscv_write_memory(struct target *target, target_addr_t address,
return ERROR_OK;
}
- if (riscv_select_current_hart(target) != ERROR_OK)
- return ERROR_FAIL;
-
target_addr_t physical_addr;
if (target->type->virt2phys(target, address, &physical_addr) == ERROR_OK)
address = physical_addr;
@@ -1758,20 +2360,15 @@ static const char *riscv_get_gdb_arch(struct target *target)
static int riscv_get_gdb_reg_list_internal(struct target *target,
struct reg **reg_list[], int *reg_list_size,
- enum target_register_class reg_class, bool read)
+ enum target_register_class reg_class, bool is_read)
{
- RISCV_INFO(r);
- LOG_DEBUG("[%s] {%d} reg_class=%d, read=%d",
- target_name(target), r->current_hartid, reg_class, read);
+ LOG_TARGET_DEBUG(target, "reg_class=%d, read=%d", reg_class, is_read);
if (!target->reg_cache) {
LOG_ERROR("Target not initialized. Return ERROR_FAIL.");
return ERROR_FAIL;
}
- if (riscv_select_current_hart(target) != ERROR_OK)
- return ERROR_FAIL;
-
switch (reg_class) {
case REG_CLASS_GENERAL:
*reg_list_size = 33;
@@ -1792,7 +2389,7 @@ static int riscv_get_gdb_reg_list_internal(struct target *target,
assert(!target->reg_cache->reg_list[i].valid ||
target->reg_cache->reg_list[i].size > 0);
(*reg_list)[i] = &target->reg_cache->reg_list[i];
- if (read &&
+ if (is_read &&
target->reg_cache->reg_list[i].exist &&
!target->reg_cache->reg_list[i].valid) {
if (target->reg_cache->reg_list[i].type->get(
@@ -1834,16 +2431,24 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
{
RISCV_INFO(info);
- if (num_mem_params > 0) {
- LOG_ERROR("Memory parameters are not supported for RISC-V algorithms.");
- return ERROR_FAIL;
- }
-
if (target->state != TARGET_HALTED) {
LOG_WARNING("target not halted");
return ERROR_TARGET_NOT_HALTED;
}
+ /* Write memory parameters to the target memory */
+ for (int i = 0; i < num_mem_params; i++) {
+ if (mem_params[i].direction == PARAM_OUT ||
+ mem_params[i].direction == PARAM_IN_OUT) {
+ int retval = target_write_buffer(target, mem_params[i].address, mem_params[i].size, mem_params[i].value);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Couldn't write input mem param into the memory, addr=0x%" TARGET_PRIxADDR " size=0x%" PRIx32,
+ mem_params[i].address, mem_params[i].size);
+ return retval;
+ }
+ }
+ }
+
/* Save registers */
struct reg *reg_pc = register_get_by_name(target->reg_cache, "pc", true);
if (!reg_pc || reg_pc->type->get(reg_pc) != ERROR_OK)
@@ -1881,30 +2486,15 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
}
}
-
/* Disable Interrupts before attempting to run the algorithm. */
uint64_t current_mstatus;
- uint8_t mstatus_bytes[8] = { 0 };
-
- LOG_DEBUG("Disabling Interrupts");
- struct reg *reg_mstatus = register_get_by_name(target->reg_cache,
- "mstatus", true);
- if (!reg_mstatus) {
- LOG_ERROR("Couldn't find mstatus!");
+ uint64_t irq_disabled_mask = MSTATUS_MIE | MSTATUS_HIE | MSTATUS_SIE | MSTATUS_UIE;
+ if (riscv_interrupts_disable(target, irq_disabled_mask, &current_mstatus) != ERROR_OK)
return ERROR_FAIL;
- }
-
- reg_mstatus->type->get(reg_mstatus);
- current_mstatus = buf_get_u64(reg_mstatus->value, 0, reg_mstatus->size);
- uint64_t ie_mask = MSTATUS_MIE | MSTATUS_HIE | MSTATUS_SIE | MSTATUS_UIE;
- buf_set_u64(mstatus_bytes, 0, info->xlen, set_field(current_mstatus,
- ie_mask, 0));
-
- reg_mstatus->type->set(reg_mstatus, mstatus_bytes);
/* Run algorithm */
LOG_DEBUG("resume at 0x%" TARGET_PRIxADDR, entry_point);
- if (riscv_resume(target, 0, entry_point, 0, 0, true) != ERROR_OK)
+ if (riscv_resume(target, 0, entry_point, 0, 1, true) != ERROR_OK)
return ERROR_FAIL;
int64_t start = timeval_ms();
@@ -1942,9 +2532,9 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
return result;
}
- /* The current hart id might have been changed in poll(). */
- if (riscv_select_current_hart(target) != ERROR_OK)
- return ERROR_FAIL;
+ /* TODO: The current hart id might have been changed in poll(). */
+ /* if (riscv_select_current_hart(target) != ERROR_OK)
+ return ERROR_FAIL; */
if (reg_pc->type->get(reg_pc) != ERROR_OK)
return ERROR_FAIL;
@@ -1956,9 +2546,8 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
}
/* Restore Interrupts */
- LOG_DEBUG("Restoring Interrupts");
- buf_set_u64(mstatus_bytes, 0, info->xlen, current_mstatus);
- reg_mstatus->type->set(reg_mstatus, mstatus_bytes);
+ if (riscv_interrupts_restore(target, current_mstatus) != ERROR_OK)
+ return ERROR_FAIL;
/* Restore registers */
uint8_t buf[8] = { 0 };
@@ -1985,6 +2574,20 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
}
}
+ /* Read memory parameters from the target memory */
+ for (int i = 0; i < num_mem_params; i++) {
+ if (mem_params[i].direction == PARAM_IN ||
+ mem_params[i].direction == PARAM_IN_OUT) {
+ int retval = target_read_buffer(target, mem_params[i].address, mem_params[i].size,
+ mem_params[i].value);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Couldn't read output mem param from the memory, addr=0x%" TARGET_PRIxADDR " size=0x%" PRIx32,
+ mem_params[i].address, mem_params[i].size);
+ return retval;
+ }
+ }
+ }
+
return ERROR_OK;
}
@@ -2076,60 +2679,133 @@ static int riscv_checksum_memory(struct target *target,
/*** OpenOCD Helper Functions ***/
-enum riscv_poll_hart {
- RPH_NO_CHANGE,
- RPH_DISCOVERED_HALTED,
- RPH_DISCOVERED_RUNNING,
- RPH_ERROR
+enum riscv_next_action {
+ RPH_NONE,
+ RPH_RESUME,
+ RPH_REMAIN_HALTED
};
-static enum riscv_poll_hart riscv_poll_hart(struct target *target, int hartid)
+static int riscv_poll_hart(struct target *target, enum riscv_next_action *next_action)
{
RISCV_INFO(r);
- if (riscv_set_current_hartid(target, hartid) != ERROR_OK)
- return RPH_ERROR;
- LOG_DEBUG("polling hart %d, target->state=%d", hartid, target->state);
-
- /* If OpenOCD thinks we're running but this hart is halted then it's time
- * to raise an event. */
- bool halted = riscv_is_halted(target);
- if (target->state != TARGET_HALTED && halted) {
- LOG_DEBUG(" triggered a halt");
- r->on_halt(target);
- return RPH_DISCOVERED_HALTED;
- } else if (target->state != TARGET_RUNNING && !halted) {
- LOG_DEBUG(" triggered running");
- target->state = TARGET_RUNNING;
- target->debug_reason = DBG_REASON_NOTHALTED;
- return RPH_DISCOVERED_RUNNING;
- }
+ LOG_TARGET_DEBUG(target, "polling, target->state=%d", target->state);
- return RPH_NO_CHANGE;
-}
+ *next_action = RPH_NONE;
-static int set_debug_reason(struct target *target, enum riscv_halt_reason halt_reason)
-{
- switch (halt_reason) {
- case RISCV_HALT_BREAKPOINT:
- target->debug_reason = DBG_REASON_BREAKPOINT;
+ enum riscv_hart_state previous_riscv_state = 0;
+ enum target_state previous_target_state = target->state;
+ switch (target->state) {
+ case TARGET_UNKNOWN:
+ /* Special case, handled further down. */
+ previous_riscv_state = RISCV_STATE_UNAVAILABLE; /* Need to assign something. */
break;
- case RISCV_HALT_TRIGGER:
- target->debug_reason = DBG_REASON_WATCHPOINT;
+ case TARGET_RUNNING:
+ previous_riscv_state = RISCV_STATE_RUNNING;
break;
- case RISCV_HALT_INTERRUPT:
- case RISCV_HALT_GROUP:
- target->debug_reason = DBG_REASON_DBGRQ;
+ case TARGET_HALTED:
+ previous_riscv_state = RISCV_STATE_HALTED;
break;
- case RISCV_HALT_SINGLESTEP:
- target->debug_reason = DBG_REASON_SINGLESTEP;
+ case TARGET_RESET:
+ previous_riscv_state = RISCV_STATE_HALTED;
break;
- case RISCV_HALT_UNKNOWN:
- target->debug_reason = DBG_REASON_UNDEFINED;
+ case TARGET_DEBUG_RUNNING:
+ previous_riscv_state = RISCV_STATE_RUNNING;
break;
- case RISCV_HALT_ERROR:
+ case TARGET_UNAVAILABLE:
+ previous_riscv_state = RISCV_STATE_UNAVAILABLE;
+ break;
+ }
+
+ /* If OpenOCD thinks we're running but this hart is halted then it's time
+ * to raise an event. */
+ enum riscv_hart_state state;
+ if (riscv_get_hart_state(target, &state) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (state == RISCV_STATE_NON_EXISTENT) {
+ LOG_TARGET_ERROR(target, "Hart is non-existent!");
+ return ERROR_FAIL;
+ }
+
+ if (state == RISCV_STATE_HALTED && timeval_ms() - r->last_activity > 100) {
+ /* If we've been idle for a while, flush the register cache. Just in case
+ * OpenOCD is going to be disconnected without shutting down cleanly. */
+ if (riscv_flush_registers(target) != ERROR_OK)
return ERROR_FAIL;
}
- LOG_DEBUG("[%s] debug_reason=%d", target_name(target), target->debug_reason);
+
+ if (target->state == TARGET_UNKNOWN || state != previous_riscv_state) {
+ switch (state) {
+ case RISCV_STATE_HALTED:
+ LOG_TARGET_DEBUG(target, " triggered a halt; previous_target_state=%d",
+ previous_target_state);
+ target->state = TARGET_HALTED;
+ enum riscv_halt_reason halt_reason = riscv_halt_reason(target);
+ if (set_debug_reason(target, halt_reason) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (halt_reason == RISCV_HALT_EBREAK) {
+ int retval;
+ /* Detect if this EBREAK is a semihosting request. If so, handle it. */
+ switch (riscv_semihosting(target, &retval)) {
+ case SEMIHOSTING_NONE:
+ break;
+ case SEMIHOSTING_WAITING:
+ /* This hart should remain halted. */
+ *next_action = RPH_REMAIN_HALTED;
+ break;
+ case SEMIHOSTING_HANDLED:
+ /* This hart should be resumed, along with any other
+ * harts that halted due to haltgroups. */
+ *next_action = RPH_RESUME;
+ return ERROR_OK;
+ case SEMIHOSTING_ERROR:
+ return retval;
+ }
+ }
+
+ if (r->handle_became_halted &&
+ r->handle_became_halted(target, previous_riscv_state) != ERROR_OK)
+ return ERROR_FAIL;
+
+ /* We shouldn't do the callbacks yet. What if
+ * there are multiple harts that halted at the
+ * same time? We need to set debug reason on each
+ * of them before calling a callback, which is
+ * going to figure out the "current thread". */
+
+ r->halted_needs_event_callback = true;
+ if (previous_target_state == TARGET_DEBUG_RUNNING)
+ r->halted_callback_event = TARGET_EVENT_DEBUG_HALTED;
+ else
+ r->halted_callback_event = TARGET_EVENT_HALTED;
+ break;
+
+ case RISCV_STATE_RUNNING:
+ LOG_TARGET_DEBUG(target, " triggered running");
+ target->state = TARGET_RUNNING;
+ target->debug_reason = DBG_REASON_NOTHALTED;
+ if (r->handle_became_running &&
+ r->handle_became_running(target, previous_riscv_state) != ERROR_OK)
+ return ERROR_FAIL;
+ break;
+
+ case RISCV_STATE_UNAVAILABLE:
+ LOG_TARGET_DEBUG(target, " became unavailable");
+ LOG_TARGET_INFO(target, "became unavailable.");
+ target->state = TARGET_UNAVAILABLE;
+ if (r->handle_became_unavailable &&
+ r->handle_became_unavailable(target, previous_riscv_state) != ERROR_OK)
+ return ERROR_FAIL;
+ break;
+
+ case RISCV_STATE_NON_EXISTENT:
+ LOG_TARGET_ERROR(target, "Hart is non-existent!");
+ target->state = TARGET_UNAVAILABLE;
+ break;
+ }
+ }
+
return ERROR_OK;
}
@@ -2184,153 +2860,188 @@ exit:
int riscv_openocd_poll(struct target *target)
{
LOG_DEBUG("polling all harts");
- int halted_hart = -1;
- if (target->smp) {
- unsigned halts_discovered = 0;
- unsigned should_remain_halted = 0;
- unsigned should_resume = 0;
- struct target_list *list;
- foreach_smp_target(list, target->smp_targets) {
- struct target *t = list->target;
- struct riscv_info *r = riscv_info(t);
- enum riscv_poll_hart out = riscv_poll_hart(t, r->current_hartid);
- switch (out) {
- case RPH_NO_CHANGE:
- break;
- case RPH_DISCOVERED_RUNNING:
- t->state = TARGET_RUNNING;
- t->debug_reason = DBG_REASON_NOTHALTED;
- break;
- case RPH_DISCOVERED_HALTED:
- halts_discovered++;
- t->state = TARGET_HALTED;
- enum riscv_halt_reason halt_reason =
- riscv_halt_reason(t, r->current_hartid);
- if (set_debug_reason(t, halt_reason) != ERROR_OK)
- return ERROR_FAIL;
+ struct list_head *targets;
- if (halt_reason == RISCV_HALT_BREAKPOINT) {
- int retval;
- switch (riscv_semihosting(t, &retval)) {
- case SEMIHOSTING_NONE:
- case SEMIHOSTING_WAITING:
- /* This hart should remain halted. */
- should_remain_halted++;
- break;
- case SEMIHOSTING_HANDLED:
- /* This hart should be resumed, along with any other
- * harts that halted due to haltgroups. */
- should_resume++;
- break;
- case SEMIHOSTING_ERROR:
- return retval;
- }
- } else if (halt_reason != RISCV_HALT_GROUP) {
- should_remain_halted++;
- }
- break;
+ LIST_HEAD(single_target_list);
+ struct target_list single_target_entry = {
+ .lh = {NULL, NULL},
+ .target = target
+ };
- case RPH_ERROR:
- return ERROR_FAIL;
- }
- }
+ if (target->smp) {
+ targets = target->smp_targets;
+ } else {
+ /* Make a list that just contains a single target, so we can
+ * share code below. */
+ list_add(&single_target_entry.lh, &single_target_list);
+ targets = &single_target_list;
+ }
+
+ unsigned should_remain_halted = 0;
+ unsigned should_resume = 0;
+ unsigned halted = 0;
+ unsigned running = 0;
+ struct target_list *entry;
+ foreach_smp_target(entry, targets) {
+ struct target *t = entry->target;
+ struct riscv_info *info = riscv_info(t);
+
+ /* Clear here just in case there were errors and we never got to
+ * check this flag further down. */
+ info->halted_needs_event_callback = false;
+
+ if (!target_was_examined(t))
+ continue;
- LOG_DEBUG("should_remain_halted=%d, should_resume=%d",
- should_remain_halted, should_resume);
- if (should_remain_halted && should_resume) {
- LOG_WARNING("%d harts should remain halted, and %d should resume.",
- should_remain_halted, should_resume);
- }
- if (should_remain_halted) {
- LOG_DEBUG("halt all");
- riscv_halt(target);
- } else if (should_resume) {
- LOG_DEBUG("resume all");
- riscv_resume(target, true, 0, 0, 0, false);
- }
+ enum riscv_next_action next_action;
+ if (riscv_poll_hart(t, &next_action) != ERROR_OK)
+ return ERROR_FAIL;
- /* Sample memory if any target is running. */
- foreach_smp_target(list, target->smp_targets) {
- struct target *t = list->target;
- if (t->state == TARGET_RUNNING) {
- sample_memory(target);
+ switch (next_action) {
+ case RPH_NONE:
+ if (t->state == TARGET_HALTED)
+ halted++;
+ if (t->state == TARGET_RUNNING ||
+ t->state == TARGET_DEBUG_RUNNING)
+ running++;
+ break;
+ case RPH_REMAIN_HALTED:
+ should_remain_halted++;
+ break;
+ case RPH_RESUME:
+ should_resume++;
break;
- }
}
+ }
- return ERROR_OK;
-
+ LOG_DEBUG("should_remain_halted=%d, should_resume=%d",
+ should_remain_halted, should_resume);
+ if (should_remain_halted && should_resume) {
+ LOG_WARNING("%d harts should remain halted, and %d should resume.",
+ should_remain_halted, should_resume);
+ }
+ if (should_remain_halted) {
+ LOG_TARGET_DEBUG(target, "halt all; should_remain_halted=%d",
+ should_remain_halted);
+ riscv_halt(target);
+ } else if (should_resume) {
+ LOG_TARGET_DEBUG(target, "resume all");
+ riscv_resume(target, true, 0, 0, 0, false);
+ } else if (halted && running) {
+ LOG_TARGET_DEBUG(target, "halt all; halted=%d",
+ halted);
+ riscv_halt(target);
} else {
- enum riscv_poll_hart out = riscv_poll_hart(target,
- riscv_current_hartid(target));
- if (out == RPH_NO_CHANGE || out == RPH_DISCOVERED_RUNNING) {
- if (target->state == TARGET_RUNNING)
- sample_memory(target);
- return ERROR_OK;
- } else if (out == RPH_ERROR) {
- return ERROR_FAIL;
+ /* For targets that were discovered to be halted, call the
+ * appropriate callback. */
+ foreach_smp_target(entry, targets)
+ {
+ struct target *t = entry->target;
+ struct riscv_info *info = riscv_info(t);
+ if (info->halted_needs_event_callback) {
+ target_call_event_callbacks(t, info->halted_callback_event);
+ info->halted_needs_event_callback = false;
+ }
}
+ }
- halted_hart = riscv_current_hartid(target);
- LOG_DEBUG(" hart %d halted", halted_hart);
-
- enum riscv_halt_reason halt_reason = riscv_halt_reason(target, halted_hart);
- if (set_debug_reason(target, halt_reason) != ERROR_OK)
+ /* Call tick() for every hart. What happens in tick() is opaque to this
+ * layer. The reason it's outside the previous loop is that at this point
+ * the state of every hart has settled, so any side effects happening in
+ * tick() won't affect the delicate poll() code. */
+ foreach_smp_target(entry, targets) {
+ struct target *t = entry->target;
+ struct riscv_info *info = riscv_info(t);
+ if (info->tick && info->tick(t) != ERROR_OK)
return ERROR_FAIL;
- target->state = TARGET_HALTED;
}
- if (target->debug_reason == DBG_REASON_BREAKPOINT) {
- int retval;
- switch (riscv_semihosting(target, &retval)) {
- case SEMIHOSTING_NONE:
- case SEMIHOSTING_WAITING:
- target_call_event_callbacks(target, TARGET_EVENT_HALTED);
- break;
- case SEMIHOSTING_HANDLED:
- if (riscv_resume(target, true, 0, 0, 0, false) != ERROR_OK)
- return ERROR_FAIL;
- break;
- case SEMIHOSTING_ERROR:
- return retval;
+ /* Sample memory if any target is running. */
+ foreach_smp_target(entry, targets) {
+ struct target *t = entry->target;
+ if (t->state == TARGET_RUNNING) {
+ sample_memory(target);
+ break;
}
- } else {
- target_call_event_callbacks(target, TARGET_EVENT_HALTED);
}
return ERROR_OK;
}
int riscv_openocd_step(struct target *target, int current,
- target_addr_t address, int handle_breakpoints)
+ target_addr_t address, int handle_breakpoints)
{
- LOG_DEBUG("stepping rtos hart");
+ LOG_TARGET_DEBUG(target, "stepping hart");
- if (!current)
- riscv_set_register(target, GDB_REGNO_PC, address);
+ if (!current) {
+ if (riscv_set_register(target, GDB_REGNO_PC, address) != ERROR_OK)
+ return ERROR_FAIL;
+ }
+
+ struct breakpoint *breakpoint = NULL;
+ /* the front-end may request us not to handle breakpoints */
+ if (handle_breakpoints) {
+ if (current) {
+ if (riscv_get_register(target, &address, GDB_REGNO_PC) != ERROR_OK)
+ return ERROR_FAIL;
+ }
+ breakpoint = breakpoint_find(target, address);
+ if (breakpoint && (riscv_remove_breakpoint(target, breakpoint) != ERROR_OK))
+ return ERROR_FAIL;
+ }
riscv_reg_t trigger_state[RISCV_MAX_HWBPS] = {0};
if (disable_triggers(target, trigger_state) != ERROR_OK)
return ERROR_FAIL;
- int out = riscv_step_rtos_hart(target);
- if (out != ERROR_OK) {
+ bool success = true;
+ uint64_t current_mstatus;
+ RISCV_INFO(info);
+
+ if (info->isrmask_mode == RISCV_ISRMASK_STEPONLY) {
+ /* Disable Interrupts before stepping. */
+ uint64_t irq_disabled_mask = MSTATUS_MIE | MSTATUS_HIE | MSTATUS_SIE | MSTATUS_UIE;
+ if (riscv_interrupts_disable(target, irq_disabled_mask,
+ &current_mstatus) != ERROR_OK) {
+ success = false;
+ LOG_ERROR("unable to disable interrupts");
+ goto _exit;
+ }
+ }
+
+ if (riscv_step_rtos_hart(target) != ERROR_OK) {
+ success = false;
LOG_ERROR("unable to step rtos hart");
- return out;
}
register_cache_invalidate(target->reg_cache);
- if (enable_triggers(target, trigger_state) != ERROR_OK)
- return ERROR_FAIL;
+ if (info->isrmask_mode == RISCV_ISRMASK_STEPONLY)
+ if (riscv_interrupts_restore(target, current_mstatus) != ERROR_OK) {
+ success = false;
+ LOG_ERROR("unable to restore interrupts");
+ }
+
+_exit:
+ if (enable_triggers(target, trigger_state) != ERROR_OK) {
+ success = false;
+ LOG_ERROR("unable to enable triggers");
+ }
- target->state = TARGET_RUNNING;
- target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
- target->state = TARGET_HALTED;
- target->debug_reason = DBG_REASON_SINGLESTEP;
- target_call_event_callbacks(target, TARGET_EVENT_HALTED);
- return out;
+ if (breakpoint && (riscv_add_breakpoint(target, breakpoint) != ERROR_OK)) {
+ success = false;
+ LOG_TARGET_ERROR(target, "unable to restore the disabled breakpoint");
+ }
+
+ if (success) {
+ target->state = TARGET_RUNNING;
+ target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
+ target->state = TARGET_HALTED;
+ target->debug_reason = DBG_REASON_SINGLESTEP;
+ target_call_event_callbacks(target, TARGET_EVENT_HALTED);
+ }
+ return success ? ERROR_OK : ERROR_FAIL;
}
/* Command Handlers */
@@ -2594,6 +3305,26 @@ COMMAND_HANDLER(riscv_set_expose_custom)
return ret;
}
+COMMAND_HANDLER(riscv_hide_csrs)
+{
+ if (CMD_ARGC == 0) {
+ LOG_ERROR("Command expects parameters");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ struct target *target = get_current_target(CMD_CTX);
+ RISCV_INFO(info);
+ int ret = ERROR_OK;
+
+ for (unsigned int i = 0; i < CMD_ARGC; i++) {
+ ret = parse_ranges(&info->hide_csr, CMD_ARGV[i], "csr", 0xfff);
+ if (ret != ERROR_OK)
+ break;
+ }
+
+ return ret;
+}
+
COMMAND_HANDLER(riscv_authdata_read)
{
unsigned int index = 0;
@@ -2704,11 +3435,26 @@ COMMAND_HANDLER(riscv_dmi_write)
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value);
if (r->dmi_write) {
- return r->dmi_write(target, address, value);
- } else {
- LOG_ERROR("dmi_write is not implemented for this target.");
- return ERROR_FAIL;
+ /* Perform the DMI write */
+ int retval = r->dmi_write(target, address, value);
+
+ /* Invalidate our cached progbuf copy:
+ - if the user tinkered directly with a progbuf register
+ - if debug module was reset, in which case progbuf registers
+ may not retain their value.
+ */
+ bool progbufTouched = (address >= DM_PROGBUF0 && address <= DM_PROGBUF15);
+ bool dmDeactivated = (address == DM_DMCONTROL && (value & DM_DMCONTROL_DMACTIVE) == 0);
+ if (progbufTouched || dmDeactivated) {
+ if (r->invalidate_cached_debug_buffer)
+ r->invalidate_cached_debug_buffer(target);
+ }
+
+ return retval;
}
+
+ LOG_ERROR("dmi_write is not implemented for this target.");
+ return ERROR_FAIL;
}
COMMAND_HANDLER(riscv_reset_delays)
@@ -2796,6 +3542,49 @@ COMMAND_HANDLER(riscv_use_bscan_tunnel)
return ERROR_OK;
}
+COMMAND_HANDLER(riscv_set_bscan_tunnel_ir)
+{
+ int ir_id = 0;
+
+ if (CMD_ARGC > 1) {
+ LOG_ERROR("Command takes at most one arguments");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ } else if (CMD_ARGC == 1) {
+ COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], ir_id);
+ }
+
+ LOG_INFO("Bscan tunnel IR 0x%x selected", ir_id);
+
+ bscan_tunnel_ir_id = ir_id;
+ return ERROR_OK;
+}
+
+
+COMMAND_HANDLER(riscv_set_maskisr)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ RISCV_INFO(info);
+
+ static const struct jim_nvp nvp_maskisr_modes[] = {
+ { .name = "off", .value = RISCV_ISRMASK_OFF },
+ { .name = "steponly", .value = RISCV_ISRMASK_STEPONLY },
+ { .name = NULL, .value = -1 },
+ };
+ const struct jim_nvp *n;
+
+ if (CMD_ARGC > 0) {
+ n = jim_nvp_name2value_simple(nvp_maskisr_modes, CMD_ARGV[0]);
+ if (!n->name)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ info->isrmask_mode = n->value;
+ } else {
+ n = jim_nvp_value2name_simple(nvp_maskisr_modes, info->isrmask_mode);
+ command_print(CMD, "riscv interrupt mask %s", n->name);
+ }
+
+ return ERROR_OK;
+}
+
COMMAND_HANDLER(riscv_set_enable_virt2phys)
{
if (CMD_ARGC != 1) {
@@ -2836,8 +3625,396 @@ COMMAND_HANDLER(riscv_set_ebreaku)
return ERROR_OK;
}
+COMMAND_HELPER(riscv_clear_trigger, int trigger_id, const char *name)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ if (CMD_ARGC != 1) {
+ LOG_ERROR("clear command takes no extra arguments.");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+ if (find_first_trigger_by_id(target, trigger_id) < 0) {
+ LOG_TARGET_ERROR(target, "No %s is set. Nothing to clear.", name);
+ return ERROR_FAIL;
+ }
+ return remove_trigger(target, trigger_id);
+}
+
+COMMAND_HANDLER(riscv_itrigger)
+{
+ if (CMD_ARGC < 1) {
+ LOG_ERROR("Command takes at least 1 parameter");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ struct target *target = get_current_target(CMD_CTX);
+ const int ITRIGGER_UNIQUE_ID = -CSR_TDATA1_TYPE_ITRIGGER;
+
+ if (riscv_enumerate_triggers(target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (!strcmp(CMD_ARGV[0], "set")) {
+ if (find_first_trigger_by_id(target, ITRIGGER_UNIQUE_ID) >= 0) {
+ LOG_TARGET_ERROR(target, "An itrigger is already set, and OpenOCD "
+ "doesn't support setting more than one at a time.");
+ return ERROR_FAIL;
+ }
+ bool vs = false;
+ bool vu = false;
+ bool nmi = false;
+ bool m = false;
+ bool s = false;
+ bool u = false;
+ riscv_reg_t interrupts = 0;
+
+ for (unsigned int i = 1; i < CMD_ARGC; i++) {
+ if (!strcmp(CMD_ARGV[i], "vs"))
+ vs = true;
+ else if (!strcmp(CMD_ARGV[i], "vu"))
+ vu = true;
+ else if (!strcmp(CMD_ARGV[i], "nmi"))
+ nmi = true;
+ else if (!strcmp(CMD_ARGV[i], "m"))
+ m = true;
+ else if (!strcmp(CMD_ARGV[i], "s"))
+ s = true;
+ else if (!strcmp(CMD_ARGV[i], "u"))
+ u = true;
+ else
+ COMMAND_PARSE_NUMBER(u64, CMD_ARGV[i], interrupts);
+ }
+ if (!nmi && interrupts == 0) {
+ LOG_ERROR("Doesn't make sense to set itrigger with "
+ "mie_bits=0 and without nmi.");
+ return ERROR_FAIL;
+ } else if (!vs && !vu && !m && !s && !u) {
+ LOG_ERROR("Doesn't make sense to set itrigger without at "
+ "least one of vs, vu, m, s, or u.");
+ return ERROR_FAIL;
+ }
+ int result = maybe_add_trigger_t4(target, vs, vu, nmi, m, s, u, interrupts, ITRIGGER_UNIQUE_ID);
+ if (result != ERROR_OK)
+ LOG_TARGET_ERROR(target, "Failed to set requested itrigger.");
+ return result;
+
+ } else if (!strcmp(CMD_ARGV[0], "clear")) {
+ return riscv_clear_trigger(CMD, ITRIGGER_UNIQUE_ID, "itrigger");
+
+ } else {
+ LOG_ERROR("First argument must be either 'set' or 'clear'.");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(riscv_icount)
+{
+ if (CMD_ARGC < 1) {
+ LOG_ERROR("Command takes at least 1 parameter");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ struct target *target = get_current_target(CMD_CTX);
+ const int ICOUNT_UNIQUE_ID = -CSR_TDATA1_TYPE_ICOUNT;
+
+ if (riscv_enumerate_triggers(target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (!strcmp(CMD_ARGV[0], "set")) {
+ if (find_first_trigger_by_id(target, ICOUNT_UNIQUE_ID) >= 0) {
+ LOG_TARGET_ERROR(target, "An icount trigger is already set, and OpenOCD "
+ "doesn't support setting more than one at a time.");
+ return ERROR_FAIL;
+ }
+ bool vs = false;
+ bool vu = false;
+ bool m = false;
+ bool s = false;
+ bool u = false;
+ bool pending = false;
+ unsigned int count = 0;
+
+ for (unsigned int i = 1; i < CMD_ARGC; i++) {
+ if (!strcmp(CMD_ARGV[i], "vs"))
+ vs = true;
+ else if (!strcmp(CMD_ARGV[i], "vu"))
+ vu = true;
+ else if (!strcmp(CMD_ARGV[i], "pending"))
+ pending = true;
+ else if (!strcmp(CMD_ARGV[i], "m"))
+ m = true;
+ else if (!strcmp(CMD_ARGV[i], "s"))
+ s = true;
+ else if (!strcmp(CMD_ARGV[i], "u"))
+ u = true;
+ else
+ COMMAND_PARSE_NUMBER(uint, CMD_ARGV[i], count);
+ }
+ if (count == 0) {
+ LOG_ERROR("Doesn't make sense to set icount trigger with "
+ "count=0.");
+ return ERROR_FAIL;
+ } else if (!vs && !vu && !m && !s && !u) {
+ LOG_ERROR("Doesn't make sense to set itrigger without at "
+ "least one of vs, vu, m, s, or u.");
+ return ERROR_FAIL;
+ }
+ int result = maybe_add_trigger_t3(target, vs, vu, m, s, u, pending, count, ICOUNT_UNIQUE_ID);
+ if (result != ERROR_OK)
+ LOG_TARGET_ERROR(target, "Failed to set requested icount trigger.");
+ return result;
+
+ } else if (!strcmp(CMD_ARGV[0], "clear")) {
+ return riscv_clear_trigger(CMD, ICOUNT_UNIQUE_ID, "icount trigger");
+
+ } else {
+ LOG_ERROR("First argument must be either 'set' or 'clear'.");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(riscv_etrigger)
+{
+ if (CMD_ARGC < 1) {
+ LOG_ERROR("Command takes at least 1 parameter");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ struct target *target = get_current_target(CMD_CTX);
+ const int ETRIGGER_UNIQUE_ID = -CSR_TDATA1_TYPE_ETRIGGER;
+
+ if (riscv_enumerate_triggers(target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (!strcmp(CMD_ARGV[0], "set")) {
+ if (find_first_trigger_by_id(target, ETRIGGER_UNIQUE_ID) >= 0) {
+ LOG_TARGET_ERROR(target, "An etrigger is already set, and OpenOCD "
+ "doesn't support setting more than one at a time.");
+ return ERROR_FAIL;
+ }
+ bool vs = false;
+ bool vu = false;
+ bool m = false;
+ bool s = false;
+ bool u = false;
+ riscv_reg_t exception_codes = 0;
+
+ for (unsigned int i = 1; i < CMD_ARGC; i++) {
+ if (!strcmp(CMD_ARGV[i], "vs"))
+ vs = true;
+ else if (!strcmp(CMD_ARGV[i], "vu"))
+ vu = true;
+ else if (!strcmp(CMD_ARGV[i], "m"))
+ m = true;
+ else if (!strcmp(CMD_ARGV[i], "s"))
+ s = true;
+ else if (!strcmp(CMD_ARGV[i], "u"))
+ u = true;
+ else
+ COMMAND_PARSE_NUMBER(u64, CMD_ARGV[i], exception_codes);
+ }
+ if (exception_codes == 0) {
+ LOG_ERROR("Doesn't make sense to set etrigger with "
+ "exception_codes=0.");
+ return ERROR_FAIL;
+ } else if (!vs && !vu && !m && !s && !u) {
+ LOG_ERROR("Doesn't make sense to set etrigger without at "
+ "least one of vs, vu, m, s, or u.");
+ return ERROR_FAIL;
+ }
+ int result = maybe_add_trigger_t5(target, vs, vu, m, s, u, exception_codes, ETRIGGER_UNIQUE_ID);
+ if (result != ERROR_OK)
+ LOG_TARGET_ERROR(target, "Failed to set requested etrigger.");
+ return result;
+
+ } else if (!strcmp(CMD_ARGV[0], "clear")) {
+ return riscv_clear_trigger(CMD, ETRIGGER_UNIQUE_ID, "etrigger");
+
+ } else {
+ LOG_ERROR("First argument must be either 'set' or 'clear'.");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(handle_repeat_read)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ RISCV_INFO(r);
+
+ if (CMD_ARGC < 2) {
+ LOG_ERROR("Command requires at least count and address arguments.");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+ if (CMD_ARGC > 3) {
+ LOG_ERROR("Command takes at most 3 arguments.");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ uint32_t count;
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], count);
+ target_addr_t address;
+ COMMAND_PARSE_ADDRESS(CMD_ARGV[1], address);
+ uint32_t size = 4;
+ if (CMD_ARGC > 2)
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], size);
+
+ if (count == 0)
+ return ERROR_OK;
+
+ uint8_t *buffer = malloc(size * count);
+ if (!buffer) {
+ LOG_ERROR("malloc failed");
+ return ERROR_FAIL;
+ }
+ int result = r->read_memory(target, address, size, count, buffer, 0);
+ if (result == ERROR_OK) {
+ target_handle_md_output(cmd, target, address, size, count, buffer,
+ false);
+ }
+ free(buffer);
+ return result;
+}
+
+COMMAND_HANDLER(handle_memory_sample_command)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ RISCV_INFO(r);
+
+ if (CMD_ARGC == 0) {
+ command_print(CMD, "Memory sample configuration for %s:", target_name(target));
+ for (unsigned i = 0; i < ARRAY_SIZE(r->sample_config.bucket); i++) {
+ if (r->sample_config.bucket[i].enabled) {
+ command_print(CMD, "bucket %d; address=0x%" TARGET_PRIxADDR "; size=%d", i,
+ r->sample_config.bucket[i].address,
+ r->sample_config.bucket[i].size_bytes);
+ } else {
+ command_print(CMD, "bucket %d; disabled", i);
+ }
+ }
+ return ERROR_OK;
+ }
+
+ if (CMD_ARGC < 2) {
+ LOG_ERROR("Command requires at least bucket and address arguments.");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ uint32_t bucket;
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], bucket);
+ if (bucket > ARRAY_SIZE(r->sample_config.bucket)) {
+ LOG_ERROR("Max bucket number is %d.", (unsigned) ARRAY_SIZE(r->sample_config.bucket));
+ return ERROR_COMMAND_ARGUMENT_INVALID;
+ }
+
+ if (!strcmp(CMD_ARGV[1], "clear")) {
+ r->sample_config.bucket[bucket].enabled = false;
+ } else {
+ COMMAND_PARSE_ADDRESS(CMD_ARGV[1], r->sample_config.bucket[bucket].address);
+
+ if (CMD_ARGC > 2) {
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], r->sample_config.bucket[bucket].size_bytes);
+ if (r->sample_config.bucket[bucket].size_bytes != 4 &&
+ r->sample_config.bucket[bucket].size_bytes != 8) {
+ LOG_ERROR("Only 4-byte and 8-byte sizes are supported.");
+ return ERROR_COMMAND_ARGUMENT_INVALID;
+ }
+ } else {
+ r->sample_config.bucket[bucket].size_bytes = 4;
+ }
+
+ r->sample_config.bucket[bucket].enabled = true;
+ }
+
+ if (!r->sample_buf.buf) {
+ r->sample_buf.size = 1024 * 1024;
+ r->sample_buf.buf = malloc(r->sample_buf.size);
+ }
+
+ /* Clear the buffer when the configuration is changed. */
+ r->sample_buf.used = 0;
+
+ r->sample_config.enabled = true;
+
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(handle_dump_sample_buf_command)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ RISCV_INFO(r);
+
+ if (CMD_ARGC > 1) {
+ LOG_ERROR("Command takes at most 1 arguments.");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+ bool base64 = false;
+ if (CMD_ARGC > 0) {
+ if (!strcmp(CMD_ARGV[0], "base64")) {
+ base64 = true;
+ } else {
+ LOG_ERROR("Unknown argument: %s", CMD_ARGV[0]);
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+ }
+
+ int result = ERROR_OK;
+ if (base64) {
+ unsigned char *encoded = base64_encode(r->sample_buf.buf,
+ r->sample_buf.used, NULL);
+ if (!encoded) {
+ LOG_ERROR("Failed base64 encode!");
+ result = ERROR_FAIL;
+ goto error;
+ }
+ command_print(CMD, "%s", encoded);
+ free(encoded);
+ } else {
+ unsigned i = 0;
+ while (i < r->sample_buf.used) {
+ uint8_t command = r->sample_buf.buf[i++];
+ if (command == RISCV_SAMPLE_BUF_TIMESTAMP_BEFORE) {
+ uint32_t timestamp = buf_get_u32(r->sample_buf.buf + i, 0, 32);
+ i += 4;
+ command_print(CMD, "timestamp before: %u", timestamp);
+ } else if (command == RISCV_SAMPLE_BUF_TIMESTAMP_AFTER) {
+ uint32_t timestamp = buf_get_u32(r->sample_buf.buf + i, 0, 32);
+ i += 4;
+ command_print(CMD, "timestamp after: %u", timestamp);
+ } else if (command < ARRAY_SIZE(r->sample_config.bucket)) {
+ command_print_sameline(CMD, "0x%" TARGET_PRIxADDR ": ",
+ r->sample_config.bucket[command].address);
+ if (r->sample_config.bucket[command].size_bytes == 4) {
+ uint32_t value = buf_get_u32(r->sample_buf.buf + i, 0, 32);
+ i += 4;
+ command_print(CMD, "0x%08" PRIx32, value);
+ } else if (r->sample_config.bucket[command].size_bytes == 8) {
+ uint64_t value = buf_get_u64(r->sample_buf.buf + i, 0, 64);
+ i += 8;
+ command_print(CMD, "0x%016" PRIx64, value);
+ } else {
+ LOG_ERROR("Found invalid size in bucket %d: %d", command,
+ r->sample_config.bucket[command].size_bytes);
+ result = ERROR_FAIL;
+ goto error;
+ }
+ } else {
+ LOG_ERROR("Found invalid command byte in sample buf: 0x%2x at offset 0x%x",
+ command, i - 1);
+ result = ERROR_FAIL;
+ goto error;
+ }
+ }
+ }
+
+error:
+ /* Clear the sample buffer even when there was an error. */
+ r->sample_buf.used = 0;
+ return result;
+}
+
COMMAND_HELPER(riscv_print_info_line, const char *section, const char *key,
- unsigned int value)
+ unsigned value)
{
char full_key[80];
snprintf(full_key, sizeof(full_key), "%s.%s", section, key);
@@ -2863,15 +4040,89 @@ COMMAND_HANDLER(handle_info)
return 0;
}
+COMMAND_HANDLER(riscv_exec_progbuf)
+{
+ if (CMD_ARGC < 1 || CMD_ARGC > 16) {
+ LOG_ERROR("Command 'exec_progbuf' takes 1 to 16 arguments.");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ struct target *target = get_current_target(CMD_CTX);
+
+ RISCV_INFO(r);
+ if (r->dtm_version != 1) {
+ LOG_TARGET_ERROR(target, "exec_progbuf: Program buffer is "
+ "only supported on v0.13 or v1.0 targets.");
+ return ERROR_FAIL;
+ }
+
+ if (target->state != TARGET_HALTED) {
+ LOG_TARGET_ERROR(target, "exec_progbuf: Can't execute "
+ "program buffer, target not halted.");
+ return ERROR_FAIL;
+ }
+
+ if (riscv_debug_buffer_size(target) == 0) {
+ LOG_TARGET_ERROR(target, "exec_progbuf: Program buffer not implemented "
+ "in the target.");
+ return ERROR_FAIL;
+ }
+
+ struct riscv_program prog;
+ riscv_program_init(&prog, target);
+
+ for (unsigned int i = 0; i < CMD_ARGC; i++) {
+ riscv_insn_t instr;
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[i], instr);
+ if (riscv_program_insert(&prog, instr) != ERROR_OK)
+ return ERROR_FAIL;
+ }
+
+ if (riscv_flush_registers(target) != ERROR_OK)
+ return ERROR_FAIL;
+ int error = riscv_program_exec(&prog, target);
+ riscv_invalidate_register_cache(target);
+
+ if (error != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "exec_progbuf: Program buffer execution failed.");
+ return ERROR_FAIL;
+ }
+
+ LOG_TARGET_DEBUG(target, "exec_progbuf: Program buffer execution successful.");
+
+ return ERROR_OK;
+}
+
static const struct command_registration riscv_exec_command_handlers[] = {
{
+ .name = "dump_sample_buf",
+ .handler = handle_dump_sample_buf_command,
+ .mode = COMMAND_ANY,
+ .usage = "[base64]",
+ .help = "Print the contents of the sample buffer, and clear the buffer."
+ },
+ {
.name = "info",
.handler = handle_info,
- .mode = COMMAND_EXEC,
+ .mode = COMMAND_ANY,
.usage = "",
.help = "Displays some information OpenOCD detected about the target."
},
{
+ .name = "memory_sample",
+ .handler = handle_memory_sample_command,
+ .mode = COMMAND_ANY,
+ .usage = "bucket address|clear [size=4]",
+ .help = "Causes OpenOCD to frequently read size bytes at the given address."
+ },
+ {
+ .name = "repeat_read",
+ .handler = handle_repeat_read,
+ .mode = COMMAND_ANY,
+ .usage = "count address [size=4]",
+ .help = "Repeatedly read the value at address."
+ },
+ {
.name = "set_command_timeout_sec",
.handler = riscv_set_command_timeout_sec,
.mode = COMMAND_ANY,
@@ -2921,6 +4172,16 @@ static const struct command_registration riscv_exec_command_handlers[] = {
"etc. This must be executed before `init`."
},
{
+ .name = "hide_csrs",
+ .handler = riscv_hide_csrs,
+ .mode = COMMAND_CONFIG,
+ .usage = "{n0|n-m0}[,n1|n-m1]......",
+ .help = "Configure a list of inclusive ranges for CSRs to hide from gdb. "
+ "Hidden registers are still available, but are not listed in "
+ "gdb target description and `reg` command output. "
+ "This must be executed before `init`."
+ },
+ {
.name = "authdata_read",
.handler = riscv_authdata_read,
.usage = "[index]",
@@ -2988,6 +4249,22 @@ static const struct command_registration riscv_exec_command_handlers[] = {
"1: DATA_REGISTER}"
},
{
+ .name = "set_bscan_tunnel_ir",
+ .handler = riscv_set_bscan_tunnel_ir,
+ .mode = COMMAND_ANY,
+ .usage = "value",
+ .help = "Specify the JTAG TAP IR used to access the bscan tunnel. "
+ "By default it is 0x23 << (ir_length - 6), which map some "
+ "Xilinx FPGA (IR USER4)"
+ },
+ {
+ .name = "set_maskisr",
+ .handler = riscv_set_maskisr,
+ .mode = COMMAND_EXEC,
+ .help = "mask riscv interrupts",
+ .usage = "['off'|'steponly']",
+ },
+ {
.name = "set_enable_virt2phys",
.handler = riscv_set_enable_virt2phys,
.mode = COMMAND_ANY,
@@ -3019,6 +4296,35 @@ static const struct command_registration riscv_exec_command_handlers[] = {
.help = "Control dcsr.ebreaku. When off, U-mode ebreak instructions "
"don't trap to OpenOCD. Defaults to on."
},
+ {
+ .name = "etrigger",
+ .handler = riscv_etrigger,
+ .mode = COMMAND_EXEC,
+ .usage = "set [vs] [vu] [m] [s] [u] <exception_codes>|clear",
+ .help = "Set or clear a single exception trigger."
+ },
+ {
+ .name = "icount",
+ .handler = riscv_icount,
+ .mode = COMMAND_EXEC,
+ .usage = "set [vs] [vu] [m] [s] [u] [pending] <count>|clear",
+ .help = "Set or clear a single instruction count trigger."
+ },
+ {
+ .name = "itrigger",
+ .handler = riscv_itrigger,
+ .mode = COMMAND_EXEC,
+ .usage = "set [vs] [vu] [nmi] [m] [s] [u] <mie_bits>|clear",
+ .help = "Set or clear a single interrupt trigger."
+ },
+ {
+ .name = "exec_progbuf",
+ .handler = riscv_exec_progbuf,
+ .mode = COMMAND_EXEC,
+ .usage = "instr1 [instr2 [... instr16]]",
+ .help = "Execute a sequence of 32-bit instructions using the program buffer. "
+ "The final ebreak instruction is added automatically, if needed."
+ },
COMMAND_REGISTRATION_DONE
};
@@ -3051,6 +4357,9 @@ static const struct command_registration riscv_command_handlers[] = {
.usage = "",
.chain = semihosting_common_handlers
},
+ {
+ .chain = smp_command_handlers
+ },
COMMAND_REGISTRATION_DONE
};
@@ -3126,13 +4435,14 @@ static void riscv_info_init(struct target *target, struct riscv_info *r)
r->common_magic = RISCV_COMMON_MAGIC;
r->dtm_version = 1;
- r->current_hartid = target->coreid;
r->version_specific = NULL;
memset(r->trigger_unique_id, 0xff, sizeof(r->trigger_unique_id));
r->xlen = -1;
+ r->isrmask_mode = RISCV_ISRMASK_OFF;
+
r->mem_access_methods[0] = RISCV_MEM_ACCESS_PROGBUF;
r->mem_access_methods[1] = RISCV_MEM_ACCESS_SYSBUS;
r->mem_access_methods[2] = RISCV_MEM_ACCESS_ABSTRACT;
@@ -3143,16 +4453,17 @@ static void riscv_info_init(struct target *target, struct riscv_info *r)
INIT_LIST_HEAD(&r->expose_csr);
INIT_LIST_HEAD(&r->expose_custom);
+ INIT_LIST_HEAD(&r->hide_csr);
+
+ r->vsew64_supported = YNM_MAYBE;
}
static int riscv_resume_go_all_harts(struct target *target)
{
RISCV_INFO(r);
- LOG_DEBUG("[%s] resuming hart", target_name(target));
- if (riscv_select_current_hart(target) != ERROR_OK)
- return ERROR_FAIL;
- if (riscv_is_halted(target)) {
+ LOG_TARGET_DEBUG(target, "resuming hart, state=%d", target->state);
+ if (target->state == TARGET_HALTED) {
if (r->resume_go(target) != ERROR_OK)
return ERROR_FAIL;
} else {
@@ -3164,26 +4475,65 @@ static int riscv_resume_go_all_harts(struct target *target)
return ERROR_OK;
}
-/* Steps the hart that's currently selected in the RTOS, or if there is no RTOS
- * then the only hart. */
+int riscv_interrupts_disable(struct target *target, uint64_t irq_mask, uint64_t *old_mstatus)
+{
+ LOG_DEBUG("Disabling Interrupts");
+ struct reg *reg_mstatus = register_get_by_name(target->reg_cache,
+ "mstatus", true);
+ if (!reg_mstatus) {
+ LOG_ERROR("Couldn't find mstatus!");
+ return ERROR_FAIL;
+ }
+
+ int retval = reg_mstatus->type->get(reg_mstatus);
+ if (retval != ERROR_OK)
+ return retval;
+
+ RISCV_INFO(info);
+ uint8_t mstatus_bytes[8] = { 0 };
+ uint64_t current_mstatus = buf_get_u64(reg_mstatus->value, 0, reg_mstatus->size);
+ buf_set_u64(mstatus_bytes, 0, info->xlen, set_field(current_mstatus,
+ irq_mask, 0));
+
+ retval = reg_mstatus->type->set(reg_mstatus, mstatus_bytes);
+ if (retval != ERROR_OK)
+ return retval;
+
+ if (old_mstatus)
+ *old_mstatus = current_mstatus;
+
+ return ERROR_OK;
+}
+
+int riscv_interrupts_restore(struct target *target, uint64_t old_mstatus)
+{
+ LOG_DEBUG("Restore Interrupts");
+ struct reg *reg_mstatus = register_get_by_name(target->reg_cache,
+ "mstatus", true);
+ if (!reg_mstatus) {
+ LOG_ERROR("Couldn't find mstatus!");
+ return ERROR_FAIL;
+ }
+
+ RISCV_INFO(info);
+ uint8_t mstatus_bytes[8];
+ buf_set_u64(mstatus_bytes, 0, info->xlen, old_mstatus);
+ return reg_mstatus->type->set(reg_mstatus, mstatus_bytes);
+}
+
static int riscv_step_rtos_hart(struct target *target)
{
RISCV_INFO(r);
- if (riscv_select_current_hart(target) != ERROR_OK)
- return ERROR_FAIL;
LOG_DEBUG("[%s] stepping", target_name(target));
- if (!riscv_is_halted(target)) {
+ if (target->state != TARGET_HALTED) {
LOG_ERROR("Hart isn't halted before single step!");
return ERROR_FAIL;
}
- riscv_invalidate_register_cache(target);
r->on_step(target);
if (r->step_current_hart(target) != ERROR_OK)
return ERROR_FAIL;
- riscv_invalidate_register_cache(target);
- r->on_halt(target);
- if (!riscv_is_halted(target)) {
+ if (target->state != TARGET_HALTED) {
LOG_ERROR("Hart was not halted after single step!");
return ERROR_FAIL;
}
@@ -3209,39 +4559,19 @@ unsigned riscv_xlen(const struct target *target)
return r->xlen;
}
-int riscv_set_current_hartid(struct target *target, int hartid)
-{
- RISCV_INFO(r);
- if (!r->select_current_hart)
- return ERROR_OK;
-
- int previous_hartid = riscv_current_hartid(target);
- r->current_hartid = hartid;
- LOG_DEBUG("setting hartid to %d, was %d", hartid, previous_hartid);
- if (r->select_current_hart(target) != ERROR_OK)
- return ERROR_FAIL;
-
- return ERROR_OK;
-}
-
-/* Invalidates the register cache. */
static void riscv_invalidate_register_cache(struct target *target)
{
- LOG_DEBUG("[%d]", target->coreid);
+ /* Do not invalidate the register cache if it is not yet set up
+ * (e.g. when the target failed to get examined). */
+ if (!target->reg_cache)
+ return;
+
+ LOG_TARGET_DEBUG(target, "Invalidating register cache");
register_cache_invalidate(target->reg_cache);
- for (size_t i = 0; i < target->reg_cache->num_regs; ++i) {
- struct reg *reg = &target->reg_cache->reg_list[i];
- reg->valid = false;
- }
}
-int riscv_current_hartid(const struct target *target)
-{
- RISCV_INFO(r);
- return r->current_hartid;
-}
-int riscv_count_harts(struct target *target)
+unsigned int riscv_count_harts(struct target *target)
{
if (!target)
return 1;
@@ -3259,7 +4589,7 @@ int riscv_count_harts(struct target *target)
* return true iff we are guaranteed that the register will read the same
* value in the future as the value we just read.
*/
-static bool gdb_regno_cacheable(enum gdb_regno regno, bool write)
+static bool gdb_regno_cacheable(enum gdb_regno regno, bool is_write)
{
/* GPRs, FPRs, vector registers are just normal data stores. */
if (regno <= GDB_REGNO_XPR31 ||
@@ -3290,7 +4620,7 @@ static bool gdb_regno_cacheable(enum gdb_regno regno, bool write)
* WARL registers might not contain the value we just wrote, but
* these ones won't spontaneously change their value either. *
*/
- return !write;
+ return !is_write;
case GDB_REGNO_TSELECT: /* I think this should be above, but then it doesn't work. */
case GDB_REGNO_TDATA1: /* Changes value when tselect is changed. */
@@ -3301,86 +4631,187 @@ static bool gdb_regno_cacheable(enum gdb_regno regno, bool write)
}
/**
- * This function is called when the debug user wants to change the value of a
- * register. The new value may be cached, and may not be written until the hart
- * is resumed. */
-int riscv_set_register(struct target *target, enum gdb_regno regid, riscv_reg_t value)
+ * This function is used internally by functions that change register values.
+ * If `write_through` is true, it is ensured that the value of the target's
+ * register is set to be equal to the `value` argument. The cached value is
+ * updated if the register is cacheable.
+ */
+static int riscv_set_or_write_register(struct target *target,
+ enum gdb_regno regid, riscv_reg_t value, bool write_through)
{
RISCV_INFO(r);
- LOG_DEBUG("[%s] %s <- %" PRIx64, target_name(target), gdb_regno_name(regid), value);
assert(r->set_register);
keep_alive();
- /* TODO: Hack to deal with gdb that thinks these registers still exist. */
- if (regid > GDB_REGNO_XPR15 && regid <= GDB_REGNO_XPR31 && value == 0 &&
- riscv_supports_extension(target, 'E'))
- return ERROR_OK;
+ if (!target->reg_cache) {
+ assert(!target_was_examined(target));
+ LOG_TARGET_DEBUG(target,
+ "No cache, writing to target: %s <- 0x%" PRIx64,
+ gdb_regno_name(regid), value);
+ return r->set_register(target, regid, value);
+ }
+
+ struct reg *reg = get_reg_cache_entry(target, regid);
+
+ if (!reg->exist) {
+ LOG_TARGET_DEBUG(target, "Register %s does not exist.", reg->name);
+ return ERROR_FAIL;
+ }
+
+ if (target->state != TARGET_HALTED) {
+ LOG_TARGET_DEBUG(target,
+ "Target not halted, writing to target: %s <- 0x%" PRIx64,
+ reg->name, value);
+ return r->set_register(target, regid, value);
+ }
+
+ const bool need_to_write = !reg->valid || reg->dirty ||
+ value != buf_get_u64(reg->value, 0, reg->size);
+ const bool cacheable = gdb_regno_cacheable(regid, need_to_write);
+
+ if (!cacheable || (write_through && need_to_write)) {
+ LOG_TARGET_DEBUG(target,
+ "Writing to target: %s <- 0x%" PRIx64 " (cacheable=%s, valid=%s, dirty=%s)",
+ reg->name, value, cacheable ? "true" : "false",
+ reg->valid ? "true" : "false",
+ reg->dirty ? "true" : "false");
+ if (r->set_register(target, regid, value) != ERROR_OK)
+ return ERROR_FAIL;
+
+ reg->dirty = false;
+ } else {
+ reg->dirty = need_to_write;
+ }
- struct reg *reg = &target->reg_cache->reg_list[regid];
buf_set_u64(reg->value, 0, reg->size, value);
+ reg->valid = cacheable;
- int result = r->set_register(target, regid, value);
- if (result == ERROR_OK)
- reg->valid = gdb_regno_cacheable(regid, true);
- else
- reg->valid = false;
- LOG_DEBUG("[%s] wrote 0x%" PRIx64 " to %s valid=%d",
- target_name(target), value, reg->name, reg->valid);
- return result;
+ LOG_TARGET_DEBUG(target,
+ "Wrote 0x%" PRIx64 " to %s (cacheable=%s, valid=%s, dirty=%s)",
+ value, reg->name, cacheable ? "true" : "false",
+ reg->valid ? "true" : "false",
+ reg->dirty ? "true" : "false");
+ return ERROR_OK;
}
+/**
+ * This function is used to change the value of a register. The new value may
+ * be cached, and may not be written until the hart is resumed.
+ */
+int riscv_set_register(struct target *target, enum gdb_regno regid,
+ riscv_reg_t value)
+{
+ return riscv_set_or_write_register(target, regid, value,
+ /* write_through */ false);
+}
+
+/**
+ * This function is used to change the value of a register. The new value may
+ * be cached, but it will be written to hart immediately.
+ */
+int riscv_write_register(struct target *target, enum gdb_regno regid,
+ riscv_reg_t value)
+{
+ return riscv_set_or_write_register(target, regid, value,
+ /* write_through */ true);
+}
+
+/**
+ * This function is used to get the value of a register. If possible, the value
+ * in cache will be updated.
+ */
int riscv_get_register(struct target *target, riscv_reg_t *value,
enum gdb_regno regid)
{
RISCV_INFO(r);
+ assert(r->get_register);
keep_alive();
- struct reg *reg = &target->reg_cache->reg_list[regid];
+ if (!target->reg_cache) {
+ assert(!target_was_examined(target));
+ LOG_TARGET_DEBUG(target, "No cache, reading %s from target",
+ gdb_regno_name(regid));
+ return r->get_register(target, value, regid);
+ }
+
+ struct reg *reg = get_reg_cache_entry(target, regid);
if (!reg->exist) {
- LOG_DEBUG("[%s] %s does not exist.",
- target_name(target), gdb_regno_name(regid));
+ LOG_TARGET_DEBUG(target, "Register %s does not exist.", reg->name);
return ERROR_FAIL;
}
- if (reg && reg->valid) {
+ if (reg->valid) {
*value = buf_get_u64(reg->value, 0, reg->size);
- LOG_DEBUG("[%s] %s: %" PRIx64 " (cached)", target_name(target),
- gdb_regno_name(regid), *value);
+ LOG_TARGET_DEBUG(target, "Read %s: 0x%" PRIx64 " (cached)", reg->name,
+ *value);
return ERROR_OK;
}
- /* TODO: Hack to deal with gdb that thinks these registers still exist. */
- if (regid > GDB_REGNO_XPR15 && regid <= GDB_REGNO_XPR31 &&
- riscv_supports_extension(target, 'E')) {
- *value = 0;
+ LOG_TARGET_DEBUG(target, "Reading %s from target", reg->name);
+ if (r->get_register(target, value, regid) != ERROR_OK)
+ return ERROR_FAIL;
+
+ buf_set_u64(reg->value, 0, reg->size, *value);
+ reg->valid = gdb_regno_cacheable(regid, /* is write? */ false);
+ reg->dirty = false;
+
+ LOG_TARGET_DEBUG(target, "Read %s: 0x%" PRIx64, reg->name, *value);
+ return ERROR_OK;
+}
+
+/**
+ * This function is used to save the value of a register in cache. The register
+ * is marked as dirty, and writeback is delayed for as long as possible.
+ */
+int riscv_save_register(struct target *target, enum gdb_regno regid)
+{
+ assert(target->state == TARGET_HALTED &&
+ "Doesn't make sense to populate register cache on non-halted targets.");
+ assert(gdb_regno_cacheable(regid, /* is write? */ false) &&
+ "Only cacheable registers can be saved.");
+
+ RISCV_INFO(r);
+ riscv_reg_t value;
+ if (!target->reg_cache) {
+ assert(!target_was_examined(target));
+ /* To create register cache it is needed to examine the target first,
+ * therefore during examine, any changed register needs to be saved
+ * and restored manually.
+ */
return ERROR_OK;
}
- int result = r->get_register(target, value, regid);
+ struct reg *reg = get_reg_cache_entry(target, regid);
+
+ LOG_TARGET_DEBUG(target, "Saving %s", reg->name);
+ if (riscv_get_register(target, &value, regid) != ERROR_OK)
+ return ERROR_FAIL;
- if (result == ERROR_OK)
- reg->valid = gdb_regno_cacheable(regid, false);
+ assert(reg->valid &&
+ "The register is cacheable, so the cache entry must be valid now.");
+ /* Mark the register dirty. We assume that this function is called
+ * because the caller is about to mess with the underlying value of the
+ * register. */
+ reg->dirty = true;
- LOG_DEBUG("[%s] %s: %" PRIx64, target_name(target),
- gdb_regno_name(regid), *value);
- return result;
+ r->last_activity = timeval_ms();
+
+ return ERROR_OK;
}
-bool riscv_is_halted(struct target *target)
+int riscv_get_hart_state(struct target *target, enum riscv_hart_state *state)
{
RISCV_INFO(r);
- assert(r->is_halted);
- return r->is_halted(target);
+ assert(r->get_hart_state);
+ return r->get_hart_state(target, state);
}
-static enum riscv_halt_reason riscv_halt_reason(struct target *target, int hartid)
+static enum riscv_halt_reason riscv_halt_reason(struct target *target)
{
RISCV_INFO(r);
- if (riscv_set_current_hartid(target, hartid) != ERROR_OK)
- return RISCV_HALT_ERROR;
- if (!riscv_is_halted(target)) {
+ if (target->state != TARGET_HALTED) {
LOG_ERROR("Hart is not halted!");
return RISCV_HALT_UNKNOWN;
}
@@ -3479,29 +4910,54 @@ int riscv_enumerate_triggers(struct target *target)
tselect_rb &= ~(1ULL << (riscv_xlen(target) - 1));
if (tselect_rb != t)
break;
- uint64_t tdata1;
- result = riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1);
- if (result != ERROR_OK)
- return result;
- int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target)));
- if (type == 0)
- break;
- switch (type) {
- case 1:
- /* On these older cores we don't support software using
- * triggers. */
- riscv_set_register(target, GDB_REGNO_TDATA1, 0);
+ uint64_t tinfo;
+ result = riscv_get_register(target, &tinfo, GDB_REGNO_TINFO);
+ if (result == ERROR_OK) {
+ /* tinfo == 0 invalid tinfo
+ * tinfo == 1 trigger doesn’t exist */
+ if (tinfo == 0 || tinfo == 1)
break;
- case 2:
- if (tdata1 & MCONTROL_DMODE(riscv_xlen(target)))
- riscv_set_register(target, GDB_REGNO_TDATA1, 0);
+ r->trigger_tinfo[t] = tinfo;
+ } else {
+ uint64_t tdata1;
+ result = riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1);
+ if (result != ERROR_OK)
+ return result;
+
+ int type = get_field(tdata1, CSR_TDATA1_TYPE(riscv_xlen(target)));
+ if (type == 0)
break;
- case 6:
- if (tdata1 & MCONTROL_DMODE(riscv_xlen(target)))
+ switch (type) {
+ case CSR_TDATA1_TYPE_LEGACY:
+ /* On these older cores we don't support software using
+ * triggers. */
riscv_set_register(target, GDB_REGNO_TDATA1, 0);
- break;
+ break;
+ case CSR_TDATA1_TYPE_MCONTROL:
+ if (tdata1 & CSR_MCONTROL_DMODE(riscv_xlen(target)))
+ riscv_set_register(target, GDB_REGNO_TDATA1, 0);
+ break;
+ case CSR_TDATA1_TYPE_MCONTROL6:
+ if (tdata1 & CSR_MCONTROL6_DMODE(riscv_xlen(target)))
+ riscv_set_register(target, GDB_REGNO_TDATA1, 0);
+ break;
+ case CSR_TDATA1_TYPE_ICOUNT:
+ if (tdata1 & CSR_ICOUNT_DMODE(riscv_xlen(target)))
+ riscv_set_register(target, GDB_REGNO_TDATA1, 0);
+ break;
+ case CSR_TDATA1_TYPE_ITRIGGER:
+ if (tdata1 & CSR_ITRIGGER_DMODE(riscv_xlen(target)))
+ riscv_set_register(target, GDB_REGNO_TDATA1, 0);
+ break;
+ case CSR_TDATA1_TYPE_ETRIGGER:
+ if (tdata1 & CSR_ETRIGGER_DMODE(riscv_xlen(target)))
+ riscv_set_register(target, GDB_REGNO_TDATA1, 0);
+ break;
+ }
+ r->trigger_tinfo[t] = 1 << type;
}
+ LOG_TARGET_DEBUG(target, "Trigger %u: supported types (mask) = 0x%08x", t, r->trigger_tinfo[t]);
}
riscv_set_register(target, GDB_REGNO_TSELECT, tselect);
@@ -3693,21 +5149,34 @@ const char *gdb_regno_name(enum gdb_regno regno)
}
}
+
+/**
+ * This function is the handler of user's request to read a register.
+ */
static int register_get(struct reg *reg)
{
- riscv_reg_info_t *reg_info = reg->arch_info;
- struct target *target = reg_info->target;
+ struct target *target = ((riscv_reg_info_t *)reg->arch_info)->target;
RISCV_INFO(r);
+ /* TODO: Hack to deal with gdb that thinks these registers still exist. */
+ if (reg->number > GDB_REGNO_XPR15 && reg->number <= GDB_REGNO_XPR31 &&
+ riscv_supports_extension(target, 'E')) {
+ buf_set_u64(reg->value, 0, reg->size, 0);
+ return ERROR_OK;
+ }
+
if (reg->number >= GDB_REGNO_V0 && reg->number <= GDB_REGNO_V31) {
if (!r->get_register_buf) {
- LOG_ERROR("Reading register %s not supported on this RISC-V target.",
- gdb_regno_name(reg->number));
+ LOG_TARGET_ERROR(target,
+ "Reading register %s not supported on this target.",
+ reg->name);
return ERROR_FAIL;
}
if (r->get_register_buf(target, reg->value, reg->number) != ERROR_OK)
return ERROR_FAIL;
+
+ reg->valid = gdb_regno_cacheable(reg->number, /* is write? */ false);
} else {
uint64_t value;
int result = riscv_get_register(target, &value, reg->number);
@@ -3715,33 +5184,32 @@ static int register_get(struct reg *reg)
return result;
buf_set_u64(reg->value, 0, reg->size, value);
}
- reg->valid = gdb_regno_cacheable(reg->number, false);
char *str = buf_to_hex_str(reg->value, reg->size);
- LOG_DEBUG("[%s] read 0x%s from %s (valid=%d)", target_name(target),
- str, reg->name, reg->valid);
+ LOG_TARGET_DEBUG(target, "read 0x%s from %s (valid=%d)", str, reg->name,
+ reg->valid);
free(str);
return ERROR_OK;
}
+/**
+ * This function is the handler of user's request to write a register.
+ */
static int register_set(struct reg *reg, uint8_t *buf)
{
- riscv_reg_info_t *reg_info = reg->arch_info;
- struct target *target = reg_info->target;
+ struct target *target = ((riscv_reg_info_t *)reg->arch_info)->target;
RISCV_INFO(r);
char *str = buf_to_hex_str(buf, reg->size);
- LOG_DEBUG("[%s] write 0x%s to %s (valid=%d)", target_name(target),
- str, reg->name, reg->valid);
+ LOG_TARGET_DEBUG(target, "write 0x%s to %s (valid=%d)", str, reg->name,
+ reg->valid);
free(str);
- /* Exit early for writing x0, which on the hardware would be ignored, and we
- * don't want to update our cache. */
- if (reg->number == GDB_REGNO_ZERO)
+ /* TODO: Hack to deal with gdb that thinks these registers still exist. */
+ if (reg->number > GDB_REGNO_XPR15 && reg->number <= GDB_REGNO_XPR31 &&
+ riscv_supports_extension(target, 'E') &&
+ buf_get_u64(buf, 0, reg->size) == 0)
return ERROR_OK;
- memcpy(reg->value, buf, DIV_ROUND_UP(reg->size, 8));
- reg->valid = gdb_regno_cacheable(reg->number, true);
-
if (reg->number == GDB_REGNO_TDATA1 ||
reg->number == GDB_REGNO_TDATA2) {
r->manual_hwbp_set = true;
@@ -3755,15 +5223,19 @@ static int register_set(struct reg *reg, uint8_t *buf)
if (reg->number >= GDB_REGNO_V0 && reg->number <= GDB_REGNO_V31) {
if (!r->set_register_buf) {
- LOG_ERROR("Writing register %s not supported on this RISC-V target.",
- gdb_regno_name(reg->number));
+ LOG_TARGET_ERROR(target,
+ "Writing register %s not supported on this target.",
+ reg->name);
return ERROR_FAIL;
}
- if (r->set_register_buf(target, reg->number, reg->value) != ERROR_OK)
+ if (r->set_register_buf(target, reg->number, buf) != ERROR_OK)
return ERROR_FAIL;
+
+ memcpy(reg->value, buf, DIV_ROUND_UP(reg->size, 8));
+ reg->valid = gdb_regno_cacheable(reg->number, /* is write? */ true);
} else {
- uint64_t value = buf_get_u64(buf, 0, reg->size);
+ const riscv_reg_t value = buf_get_u64(buf, 0, reg->size);
if (riscv_set_register(target, reg->number, value) != ERROR_OK)
return ERROR_FAIL;
}
@@ -3804,8 +5276,8 @@ int riscv_init_registers(struct target *target)
target->reg_cache->num_regs += entry->high - entry->low + 1;
}
- LOG_DEBUG("create register cache for %d registers",
- target->reg_cache->num_regs);
+ LOG_DEBUG("[%s] create register cache for %d registers",
+ target_name(target), target->reg_cache->num_regs);
target->reg_cache->reg_list =
calloc(target->reg_cache->num_regs, sizeof(struct reg));
@@ -4231,18 +5703,27 @@ int riscv_init_registers(struct target *target)
}
switch (csr_number) {
+ case CSR_DCSR:
+ case CSR_MVENDORID:
+ case CSR_MCOUNTINHIBIT:
+ r->size = 32;
+ break;
+ case CSR_FCSR:
+ r->size = 32;
+ /* fall through */
case CSR_FFLAGS:
case CSR_FRM:
- case CSR_FCSR:
r->exist = riscv_supports_extension(target, 'F');
r->group = "float";
r->feature = &feature_fpu;
break;
+ case CSR_SCOUNTEREN:
+ r->size = 32;
+ /* fall through */
case CSR_SSTATUS:
case CSR_STVEC:
case CSR_SIP:
case CSR_SIE:
- case CSR_SCOUNTEREN:
case CSR_SSCRATCH:
case CSR_SEPC:
case CSR_SCAUSE:
@@ -4331,9 +5812,80 @@ int riscv_init_registers(struct target *target)
case CSR_VXSAT:
case CSR_VXRM:
case CSR_VL:
+ case CSR_VCSR:
case CSR_VTYPE:
case CSR_VLENB:
- r->exist = riscv_supports_extension(target, 'V');
+ r->exist = (info->vlenb > 0);
+ break;
+ case CSR_MCOUNTEREN:
+ r->size = 32;
+ r->exist = riscv_supports_extension(target, 'U');
+ break;
+
+ /* Interrupts M-Mode CSRs. */
+ case CSR_MISELECT:
+ case CSR_MIREG:
+ case CSR_MTOPI:
+ case CSR_MVIEN:
+ case CSR_MVIP:
+ r->exist = info->mtopi_readable;
+ break;
+ case CSR_MTOPEI:
+ r->exist = info->mtopei_readable;
+ break;
+ case CSR_MIDELEGH:
+ case CSR_MVIENH:
+ case CSR_MVIPH:
+ r->exist = info->mtopi_readable &&
+ riscv_xlen(target) == 32 &&
+ riscv_supports_extension(target, 'S');
+ break;
+ case CSR_MIEH:
+ case CSR_MIPH:
+ r->exist = info->mtopi_readable;
+ break;
+ /* Interrupts S-Mode CSRs. */
+ case CSR_SISELECT:
+ case CSR_SIREG:
+ case CSR_STOPI:
+ r->exist = info->mtopi_readable &&
+ riscv_supports_extension(target, 'S');
+ break;
+ case CSR_STOPEI:
+ r->exist = info->mtopei_readable &&
+ riscv_supports_extension(target, 'S');
+ break;
+ case CSR_SIEH:
+ case CSR_SIPH:
+ r->exist = info->mtopi_readable &&
+ riscv_xlen(target) == 32 &&
+ riscv_supports_extension(target, 'S');
+ break;
+ /* Interrupts Hypervisor and VS CSRs. */
+ case CSR_HVIEN:
+ case CSR_HVICTL:
+ case CSR_HVIPRIO1:
+ case CSR_HVIPRIO2:
+ case CSR_VSISELECT:
+ case CSR_VSIREG:
+ case CSR_VSTOPI:
+ r->exist = info->mtopi_readable &&
+ riscv_supports_extension(target, 'H');
+ break;
+ case CSR_VSTOPEI:
+ r->exist = info->mtopei_readable &&
+ riscv_supports_extension(target, 'H');
+ break;
+ case CSR_HIDELEGH:
+ case CSR_HVIENH:
+ case CSR_HVIPH:
+ case CSR_HVIPRIO1H:
+ case CSR_HVIPRIO2H:
+ case CSR_VSIEH:
+ case CSR_VSIPH:
+ r->exist = info->mtopi_readable &&
+ riscv_xlen(target) == 32 &&
+ riscv_supports_extension(target, 'H');
break;
}
@@ -4352,6 +5904,14 @@ int riscv_init_registers(struct target *target)
r->exist = true;
break;
}
+ } else if (r->exist && !list_empty(&info->hide_csr)) {
+ range_list_t *entry;
+ list_for_each_entry(entry, &info->hide_csr, list)
+ if ((entry->low <= csr_number) && (csr_number <= entry->high)) {
+ LOG_TARGET_DEBUG(target, "Hiding CSR %d (name=%s)", csr_number, r->name);
+ r->hidden = true;
+ break;
+ }
}
} else if (number == GDB_REGNO_PRIV) {
@@ -4362,7 +5922,7 @@ int riscv_init_registers(struct target *target)
} else if (number >= GDB_REGNO_V0 && number <= GDB_REGNO_V31) {
r->caller_save = false;
- r->exist = riscv_supports_extension(target, 'V') && info->vlenb;
+ r->exist = (info->vlenb > 0);
r->size = info->vlenb * 8;
sprintf(reg_name, "v%d", number - GDB_REGNO_V0);
r->group = "vector";
diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h
index aba0864..4cbe006 100644
--- a/src/target/riscv/riscv.h
+++ b/src/target/riscv/riscv.h
@@ -26,12 +26,16 @@ struct riscv_program;
#define RISCV_SATP_MODE(xlen) ((xlen) == 32 ? SATP32_MODE : SATP64_MODE)
#define RISCV_SATP_PPN(xlen) ((xlen) == 32 ? SATP32_PPN : SATP64_PPN)
+#define RISCV_HGATP_MODE(xlen) ((xlen) == 32 ? HGATP32_MODE : HGATP64_MODE)
+#define RISCV_HGATP_PPN(xlen) ((xlen) == 32 ? HGATP32_PPN : HGATP64_PPN)
#define RISCV_PGSHIFT 12
# define PG_MAX_LEVEL 4
#define RISCV_NUM_MEM_ACCESS_METHODS 3
+#define RISCV_BATCH_ALLOC_SIZE 128
+
extern struct target_type riscv011_target;
extern struct target_type riscv013_target;
@@ -42,6 +46,12 @@ typedef uint64_t riscv_reg_t;
typedef uint32_t riscv_insn_t;
typedef uint64_t riscv_addr_t;
+typedef enum {
+ YNM_MAYBE,
+ YNM_YES,
+ YNM_NO
+} yes_no_maybe_t;
+
enum riscv_mem_access_method {
RISCV_MEM_ACCESS_UNSPECIFIED,
RISCV_MEM_ACCESS_PROGBUF,
@@ -51,7 +61,7 @@ enum riscv_mem_access_method {
enum riscv_halt_reason {
RISCV_HALT_INTERRUPT,
- RISCV_HALT_BREAKPOINT,
+ RISCV_HALT_EBREAK,
RISCV_HALT_SINGLESTEP,
RISCV_HALT_TRIGGER,
RISCV_HALT_UNKNOWN,
@@ -59,6 +69,20 @@ enum riscv_halt_reason {
RISCV_HALT_ERROR
};
+enum riscv_isrmasking_mode {
+ /* RISCV_ISRMASK_AUTO, */ /* not supported yet */
+ RISCV_ISRMASK_OFF,
+ /* RISCV_ISRMASK_ON, */ /* not supported yet */
+ RISCV_ISRMASK_STEPONLY,
+};
+
+enum riscv_hart_state {
+ RISCV_STATE_NON_EXISTENT,
+ RISCV_STATE_RUNNING,
+ RISCV_STATE_HALTED,
+ RISCV_STATE_UNAVAILABLE
+};
+
typedef struct {
struct target *target;
unsigned custom_number;
@@ -95,12 +119,6 @@ struct riscv_info {
struct command_context *cmd_ctx;
void *version_specific;
- /* The hart that is currently being debugged. Note that this is
- * different than the hartid that the RTOS is expected to use. This
- * one will change all the time, it's more of a global argument to
- * every function than an actual */
- int current_hartid;
-
/* Single buffer that contains all register names, instead of calling
* malloc for each register. Needs to be freed when reg_list is freed. */
char *reg_names;
@@ -108,18 +126,33 @@ struct riscv_info {
/* It's possible that each core has a different supported ISA set. */
int xlen;
riscv_reg_t misa;
- /* Cached value of vlenb. 0 if vlenb is not readable for some reason. */
+ /* Cached value of vlenb. 0 indicates there is no vector support.
+ * Note that you can have vector support without misa.V set, because
+ * Zve* extensions implement vector registers without setting misa.V. */
unsigned int vlenb;
+ bool mtopi_readable;
+ bool mtopei_readable;
+
/* The number of triggers per hart. */
unsigned int trigger_count;
- /* For each physical trigger, contains -1 if the hwbp is available, or the
- * unique_id of the breakpoint/watchpoint that is using it.
+ /* record the tinfo of each trigger */
+ unsigned int trigger_tinfo[RISCV_MAX_TRIGGERS];
+
+ /* For each physical trigger contains:
+ * -1: the hwbp is available
+ * -4: The trigger is used by the itrigger command
+ * -5: The trigger is used by the etrigger command
+ * >= 0: unique_id of the breakpoint/watchpoint that is using it.
* Note that in RTOS mode the triggers are the same across all harts the
* target controls, while otherwise only a single hart is controlled. */
int trigger_unique_id[RISCV_MAX_HWBPS];
+ /* The unique id of the trigger that caused the most recent halt. If the
+ * most recent halt was not caused by a trigger, then this is -1. */
+ uint32_t trigger_hit;
+
/* The number of entries in the debug buffer. */
int debug_buffer_size;
@@ -137,21 +170,44 @@ struct riscv_info {
/* This target was selected using hasel. */
bool selected;
+ /* Used by riscv_openocd_poll(). */
+ bool halted_needs_event_callback;
+ enum target_event halted_callback_event;
+
+ enum riscv_isrmasking_mode isrmask_mode;
+
/* Helper functions that target the various RISC-V debug spec
* implementations. */
- int (*get_register)(struct target *target, riscv_reg_t *value, int regid);
- int (*set_register)(struct target *target, int regid, uint64_t value);
- int (*get_register_buf)(struct target *target, uint8_t *buf, int regno);
- int (*set_register_buf)(struct target *target, int regno,
+ int (*get_register)(struct target *target, riscv_reg_t *value,
+ enum gdb_regno regno);
+ int (*set_register)(struct target *target, enum gdb_regno regno,
+ riscv_reg_t value);
+ int (*get_register_buf)(struct target *target, uint8_t *buf,
+ enum gdb_regno regno);
+ int (*set_register_buf)(struct target *target, enum gdb_regno regno,
const uint8_t *buf);
- int (*select_current_hart)(struct target *target);
- bool (*is_halted)(struct target *target);
+ int (*select_target)(struct target *target);
+ int (*get_hart_state)(struct target *target, enum riscv_hart_state *state);
/* Resume this target, as well as every other prepped target that can be
* resumed near-simultaneously. Clear the prepped flag on any target that
* was resumed. */
int (*resume_go)(struct target *target);
int (*step_current_hart)(struct target *target);
- int (*on_halt)(struct target *target);
+
+ /* These get called from riscv_poll_hart(), which is a house of cards
+ * together with openocd_poll(), so be careful not to upset things too
+ * much. */
+ int (*handle_became_halted)(struct target *target,
+ enum riscv_hart_state previous_riscv_state);
+ int (*handle_became_running)(struct target *target,
+ enum riscv_hart_state previous_riscv_state);
+ int (*handle_became_unavailable)(struct target *target,
+ enum riscv_hart_state previous_riscv_state);
+
+ /* Called periodically (no guarantees about frequency), while there's
+ * nothing else going on. */
+ int (*tick)(struct target *target);
+
/* Get this target as ready as possible to resume, without actually
* resuming. */
int (*resume_prep)(struct target *target);
@@ -163,6 +219,7 @@ struct riscv_info {
riscv_insn_t d);
riscv_insn_t (*read_debug_buffer)(struct target *target, unsigned index);
int (*execute_debug_buffer)(struct target *target);
+ int (*invalidate_cached_debug_buffer)(struct target *target);
int (*dmi_write_u64_bits)(struct target *target);
void (*fill_dmi_write_u64)(struct target *target, char *buf, int a, uint64_t d);
void (*fill_dmi_read_u64)(struct target *target, char *buf, int a);
@@ -203,7 +260,7 @@ struct riscv_info {
struct reg_data_type_union vector_union;
struct reg_data_type type_vector;
- /* Set when trigger registers are changed by the user. This indicates we eed
+ /* Set when trigger registers are changed by the user. This indicates we need
* to beware that we may hit a trigger that we didn't realize had been set. */
bool manual_hwbp_set;
@@ -224,8 +281,17 @@ struct riscv_info {
* from range 0xc000 ... 0xffff. */
struct list_head expose_custom;
+ /* The list of registers to mark as "hidden". Hidden registers are available
+ * but do not appear in gdb targets description or reg command output. */
+ struct list_head hide_csr;
+
riscv_sample_config_t sample_config;
struct riscv_sample_buf sample_buf;
+
+ /* Track when we were last asked to do something substantial. */
+ int64_t last_activity;
+
+ yes_no_maybe_t vsew64_supported;
};
COMMAND_HELPER(riscv_print_info_line, const char *section, const char *key,
@@ -240,6 +306,7 @@ typedef struct {
const char *name;
int level;
unsigned va_bits;
+ /* log2(PTESIZE) */
unsigned pte_shift;
unsigned vpn_shift[PG_MAX_LEVEL];
unsigned vpn_mask[PG_MAX_LEVEL];
@@ -299,39 +366,42 @@ int riscv_openocd_step(
int handle_breakpoints
);
-int riscv_openocd_assert_reset(struct target *target);
-int riscv_openocd_deassert_reset(struct target *target);
-
/*** RISC-V Interface ***/
bool riscv_supports_extension(struct target *target, char letter);
/* Returns XLEN for the given (or current) hart. */
unsigned riscv_xlen(const struct target *target);
-int riscv_xlen_of_hart(const struct target *target);
-
-/* Sets the current hart, which is the hart that will actually be used when
- * issuing debug commands. */
-int riscv_set_current_hartid(struct target *target, int hartid);
-int riscv_select_current_hart(struct target *target);
-int riscv_current_hartid(const struct target *target);
/*** Support functions for the RISC-V 'RTOS', which provides multihart support
* without requiring multiple targets. */
/* Lists the number of harts in the system, which are assumed to be
* consecutive and start with mhartid=0. */
-int riscv_count_harts(struct target *target);
+unsigned int riscv_count_harts(struct target *target);
-/** Set register, updating the cache. */
+/**
+ * Set the register value. For cacheable registers, only the cache is updated
+ * (write-back mode).
+ */
int riscv_set_register(struct target *target, enum gdb_regno i, riscv_reg_t v);
+/**
+ * Set the register value and immediately write it to the target
+ * (write-through mode).
+ */
+int riscv_write_register(struct target *target, enum gdb_regno i, riscv_reg_t v);
/** Get register, from the cache if it's in there. */
int riscv_get_register(struct target *target, riscv_reg_t *value,
enum gdb_regno r);
+/** Read the register into the cache, and mark it dirty so it will be restored
+ * before resuming. */
+int riscv_save_register(struct target *target, enum gdb_regno regid);
+/** Write all dirty registers to the target. */
+int riscv_flush_registers(struct target *target);
/* Checks the state of the current hart -- "is_halted" checks the actual
* on-device register. */
-bool riscv_is_halted(struct target *target);
+int riscv_get_hart_state(struct target *target, enum riscv_hart_state *state);
/* These helper functions let the generic program interface get target-specific
* information. */
@@ -364,4 +434,7 @@ void riscv_add_bscan_tunneled_scan(struct target *target, struct scan_field *fie
int riscv_read_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer);
int riscv_write_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer);
+int riscv_interrupts_disable(struct target *target, uint64_t ie_mask, uint64_t *old_mstatus);
+int riscv_interrupts_restore(struct target *target, uint64_t old_mstatus);
+
#endif
diff --git a/src/target/riscv/riscv_semihosting.c b/src/target/riscv/riscv_semihosting.c
index 1bc4e1a..f7e150c 100644
--- a/src/target/riscv/riscv_semihosting.c
+++ b/src/target/riscv/riscv_semihosting.c
@@ -71,33 +71,31 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval)
if (result != ERROR_OK)
return SEMIHOSTING_ERROR;
- uint8_t tmp_buf[12];
+ /*
+ * The instructions that trigger a semihosting call,
+ * always uncompressed, should look like:
+ */
+ uint32_t magic[] = {
+ 0x01f01013, /* slli zero,zero,0x1f */
+ 0x00100073, /* ebreak */
+ 0x40705013 /* srai zero,zero,0x7 */
+ };
/* Read three uncompressed instructions: The previous, the current one (pointed to by PC) and the next one */
for (int i = 0; i < 3; i++) {
+ uint8_t buf[4];
/* Instruction memories may not support arbitrary read size. Use any size that will work. */
- *retval = riscv_read_by_any_size(target, (pc - 4) + 4 * i, 4, tmp_buf + 4 * i);
+ target_addr_t address = (pc - 4) + 4 * i;
+ *retval = riscv_read_by_any_size(target, address, 4, buf);
if (*retval != ERROR_OK)
return SEMIHOSTING_ERROR;
- }
-
- /*
- * The instructions that trigger a semihosting call,
- * always uncompressed, should look like:
- *
- * 01f01013 slli zero,zero,0x1f
- * 00100073 ebreak
- * 40705013 srai zero,zero,0x7
- */
- uint32_t pre = target_buffer_get_u32(target, tmp_buf);
- uint32_t ebreak = target_buffer_get_u32(target, tmp_buf + 4);
- uint32_t post = target_buffer_get_u32(target, tmp_buf + 8);
- LOG_DEBUG("check %08x %08x %08x from 0x%" PRIx64 "-4", pre, ebreak, post, pc);
-
- if (pre != 0x01f01013 || ebreak != 0x00100073 || post != 0x40705013) {
- /* Not the magic sequence defining semihosting. */
- LOG_DEBUG(" -> NONE (no magic)");
- return SEMIHOSTING_NONE;
+ uint32_t value = target_buffer_get_u32(target, buf);
+ LOG_TARGET_DEBUG(target, "compare 0x%08x from 0x%" PRIx64 " against 0x%08x",
+ value, address, magic[i]);
+ if (value != magic[i]) {
+ LOG_DEBUG(" -> NONE (no magic)");
+ return SEMIHOSTING_NONE;
+ }
}
/*
@@ -141,16 +139,16 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval)
}
}
+ /* Resume right after the EBREAK 4 bytes instruction. */
+ *retval = riscv_set_register(target, GDB_REGNO_PC, pc + 4);
+ if (*retval != ERROR_OK)
+ return SEMIHOSTING_ERROR;
+
/*
* Resume target if we are not waiting on a fileio
* operation to complete.
*/
if (semihosting->is_resumable && !semihosting->hit_fileio) {
- /* Resume right after the EBREAK 4 bytes instruction. */
- *retval = riscv_set_register(target, GDB_REGNO_PC, pc + 4);
- if (*retval != ERROR_OK)
- return SEMIHOSTING_ERROR;
-
LOG_DEBUG(" -> HANDLED");
return SEMIHOSTING_HANDLED;
}
diff --git a/src/target/target.c b/src/target/target.c
index ae419ac..581f5ef 100644
--- a/src/target/target.c
+++ b/src/target/target.c
@@ -151,7 +151,7 @@ static struct target_timer_callback *target_timer_callbacks;
static int64_t target_timer_next_event_value;
static LIST_HEAD(target_reset_callback_list);
static LIST_HEAD(target_trace_callback_list);
-static const int polling_interval = TARGET_DEFAULT_POLLING_INTERVAL;
+static const unsigned int polling_interval = TARGET_DEFAULT_POLLING_INTERVAL;
static LIST_HEAD(empty_smp_targets);
static const struct jim_nvp nvp_assert[] = {
@@ -248,6 +248,7 @@ static const struct jim_nvp nvp_target_state[] = {
{ .name = "halted", .value = TARGET_HALTED },
{ .name = "reset", .value = TARGET_RESET },
{ .name = "debug-running", .value = TARGET_DEBUG_RUNNING },
+ { .name = "unavailable", .value = TARGET_UNAVAILABLE },
{ .name = NULL, .value = -1 },
};
@@ -752,6 +753,7 @@ int target_examine_one(struct target *target)
return retval;
}
+ LOG_USER("[%s] Target successfully examined.", target_name(target));
target_set_examined(target);
target_call_event_callbacks(target, TARGET_EVENT_EXAMINE_END);
@@ -770,6 +772,12 @@ static int jtag_enable_callback(enum jtag_event event, void *priv)
return target_examine_one(target);
}
+/* When this is true, it's OK to call examine() again in the hopes that this time
+ * it will work. Earlier than that there is probably other initialization that
+ * needs to happen (like scanning the JTAG chain) before examine should be
+ * called. */
+static bool examine_attempted;
+
/* Targets that correctly implement init + examine, i.e.
* no communication with target during init:
*
@@ -780,6 +788,8 @@ int target_examine(void)
int retval = ERROR_OK;
struct target *target;
+ examine_attempted = true;
+
for (target = all_targets; target; target = target->next) {
/* defer examination, but don't skip it */
if (!target->tap->enabled) {
@@ -3028,51 +3038,42 @@ static int handle_target(void *priv)
is_jtag_poll_safe() && target;
target = target->next) {
- if (!target_was_examined(target))
- continue;
-
- if (!target->tap->enabled)
+ /* This function only gets called every polling_interval, so
+ * allow some slack in the time comparison. Otherwise, if we
+ * schedule for now+polling_interval, the next poll won't
+ * actually happen until a polling_interval later. */
+ bool poll_needed = timeval_ms() + polling_interval / 2 >= target->backoff.next_attempt;
+ if (!target->tap->enabled || power_dropout || srst_asserted || !poll_needed)
continue;
- if (target->backoff.times > target->backoff.count) {
- /* do not poll this time as we failed previously */
- target->backoff.count++;
- continue;
+ /* polling may fail silently until the target has been examined */
+ retval = target_poll(target);
+ if (retval == ERROR_OK) {
+ /* Polling succeeded, reset the back-off interval */
+ target->backoff.interval = polling_interval;
+ } else {
+ /* Increase interval between polling up to 5000ms */
+ target->backoff.interval = MAX(polling_interval,
+ MIN(target->backoff.interval * 2 + 1, 5000));
+ /* Do *not* tell gdb the target halted. This might just
+ * be a hiccup. We have no reason to believe the target
+ * is halted, and if it is running while gdb thinks it's
+ * halted things just get unnecessarily confused. gdb
+ * users can hit ^C if the need to interact with the
+ * target. */
}
- target->backoff.count = 0;
+ target->backoff.next_attempt = timeval_ms() + target->backoff.interval;
+ LOG_TARGET_DEBUG(target, "target_poll() -> %d, next attempt in %dms",
+ retval, target->backoff.interval);
- /* only poll target if we've got power and srst isn't asserted */
- if (!power_dropout && !srst_asserted) {
- /* polling may fail silently until the target has been examined */
- retval = target_poll(target);
+ if (retval != ERROR_OK && examine_attempted) {
+ target_reset_examined(target);
+ retval = target_examine_one(target);
if (retval != ERROR_OK) {
- /* 100ms polling interval. Increase interval between polling up to 5000ms */
- if (target->backoff.times * polling_interval < 5000) {
- target->backoff.times *= 2;
- target->backoff.times++;
- }
-
- /* Tell GDB to halt the debugger. This allows the user to
- * run monitor commands to handle the situation.
- */
- target_call_event_callbacks(target, TARGET_EVENT_GDB_HALT);
- }
- if (target->backoff.times > 0) {
- LOG_USER("Polling target %s failed, trying to reexamine", target_name(target));
- target_reset_examined(target);
- retval = target_examine_one(target);
- /* Target examination could have failed due to unstable connection,
- * but we set the examined flag anyway to repoll it later */
- if (retval != ERROR_OK) {
- target_set_examined(target);
- LOG_USER("Examination failed, GDB will be halted. Polling again in %dms",
- target->backoff.times * polling_interval);
- return retval;
- }
+ LOG_TARGET_DEBUG(target, "Examination failed. Polling again in %dms",
+ target->backoff.interval);
+ return retval;
}
-
- /* Since we succeeded, we reset backoff count */
- target->backoff.times = 0;
}
}
@@ -3110,13 +3111,13 @@ COMMAND_HANDLER(handle_reg_command)
count, reg->name,
reg->size, value,
reg->dirty
- ? " (dirty)"
- : "");
+ ? " (dirty)"
+ : "");
free(value);
} else {
command_print(CMD, "(%i) %s (/%" PRIu32 ")",
- count, reg->name,
- reg->size);
+ count, reg->name,
+ reg->size);
}
}
cache = cache->next;
@@ -3392,7 +3393,7 @@ COMMAND_HANDLER(handle_step_command)
void target_handle_md_output(struct command_invocation *cmd,
struct target *target, target_addr_t address, unsigned size,
- unsigned count, const uint8_t *buffer)
+ unsigned count, const uint8_t *buffer, bool include_address)
{
const unsigned line_bytecnt = 32;
unsigned line_modulo = line_bytecnt / size;
@@ -3421,7 +3422,7 @@ void target_handle_md_output(struct command_invocation *cmd,
}
for (unsigned i = 0; i < count; i++) {
- if (i % line_modulo == 0) {
+ if (include_address && (i % line_modulo == 0)) {
output_len += snprintf(output + output_len,
sizeof(output) - output_len,
TARGET_ADDR_FMT ": ",
@@ -3505,7 +3506,8 @@ COMMAND_HANDLER(handle_md_command)
struct target *target = get_current_target(CMD_CTX);
int retval = fn(target, address, size, count, buffer);
if (retval == ERROR_OK)
- target_handle_md_output(CMD, target, address, size, count, buffer);
+ target_handle_md_output(CMD, target, address, size, count, buffer,
+ true);
free(buffer);
diff --git a/src/target/target.h b/src/target/target.h
index ef9ba10..8589e17 100644
--- a/src/target/target.h
+++ b/src/target/target.h
@@ -45,6 +45,8 @@ struct gdb_fileio_info;
* not sure how this is used with all the recent changes)
* TARGET_DEBUG_RUNNING = 4: the target is running, but it is executing code on
* behalf of the debugger (e.g. algorithm for flashing)
+ * TARGET_UNAVAILABLE = 5: The target is unavailable for some reason. It might
+ * be powered down, for instance.
*
* also see: target_state_name();
*/
@@ -55,6 +57,7 @@ enum target_state {
TARGET_HALTED = 2,
TARGET_RESET = 3,
TARGET_DEBUG_RUNNING = 4,
+ TARGET_UNAVAILABLE = 5
};
enum nvp_assert {
@@ -106,8 +109,8 @@ struct gdb_service {
/* target back off timer */
struct backoff_timer {
- int times;
- int count;
+ int64_t next_attempt;
+ unsigned int interval;
};
/* split target registers into multiple class */
@@ -188,6 +191,9 @@ struct target {
struct rtos *rtos; /* Instance of Real Time Operating System support */
bool rtos_auto_detect; /* A flag that indicates that the RTOS has been specified as "auto"
* and must be detected when symbols are offered */
+ /* Track when next to poll(). If polling is failing, we don't want to
+ * poll too quickly because we'll just overwhelm the user with error
+ * messages. */
struct backoff_timer backoff;
int smp; /* Unique non-zero number for each SMP group */
struct list_head *smp_targets; /* list all targets in this smp group/cluster
@@ -785,7 +791,7 @@ void target_handle_event(struct target *t, enum target_event e);
void target_handle_md_output(struct command_invocation *cmd,
struct target *target, target_addr_t address, unsigned size,
- unsigned count, const uint8_t *buffer);
+ unsigned count, const uint8_t *buffer, bool include_address);
int target_profiling_default(struct target *target, uint32_t *samples, uint32_t
max_num_samples, uint32_t *num_samples, uint32_t seconds);
diff --git a/tcl/board/esp32c2-ftdi.cfg b/tcl/board/esp32c2-ftdi.cfg
new file mode 100644
index 0000000..bc2b82f
--- /dev/null
+++ b/tcl/board/esp32c2-ftdi.cfg
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Example OpenOCD configuration file for ESP32-C2 connected via ESP-Prog.
+#
+# For example, OpenOCD can be started for ESP32-C2 debugging on
+#
+# openocd -f board/esp32c2-ftdi.cfg
+#
+
+# Source the JTAG interface configuration file
+source [find interface/ftdi/esp32_devkitj_v1.cfg]
+# Source the ESP32-C2 configuration file
+source [find target/esp32c2.cfg]
+
+# The speed of the JTAG interface, in kHz. If you get DSR/DIR errors (and they
+# do not relate to OpenOCD trying to read from a memory range without physical
+# memory being present there), you can try lowering this.
+#
+# On DevKit-J, this can go as high as 20MHz if CPU frequency is 80MHz, or 26MHz
+# if CPU frequency is 160MHz or 240MHz.
+adapter speed 20000
diff --git a/tcl/board/esp32c3-ftdi.cfg b/tcl/board/esp32c3-ftdi.cfg
new file mode 100644
index 0000000..5595374
--- /dev/null
+++ b/tcl/board/esp32c3-ftdi.cfg
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Example OpenOCD configuration file for ESP32-C3 connected via ESP-Prog.
+#
+# For example, OpenOCD can be started for ESP32-C3 debugging on
+#
+# openocd -f board/esp32c3-ftdi.cfg
+#
+
+# Source the JTAG interface configuration file
+source [find interface/ftdi/esp32_devkitj_v1.cfg]
+# Source the ESP32-C3 configuration file
+source [find target/esp32c3.cfg]
+
+# The speed of the JTAG interface, in kHz. If you get DSR/DIR errors (and they
+# do not relate to OpenOCD trying to read from a memory range without physical
+# memory being present there), you can try lowering this.
+#
+# On DevKit-J, this can go as high as 20MHz if CPU frequency is 80MHz, or 26MHz
+# if CPU frequency is 160MHz or 240MHz.
+adapter speed 20000
diff --git a/tcl/board/sifive-e31arty-cjtag.cfg b/tcl/board/sifive-e31arty-cjtag.cfg
new file mode 100644
index 0000000..58ba23a
--- /dev/null
+++ b/tcl/board/sifive-e31arty-cjtag.cfg
@@ -0,0 +1,23 @@
+#
+# Be sure you include the speed and interface before this file
+# Example:
+# -c "adapter_khz 5000" -f "interface/ftdi/olimex-arm-usb-tiny-h.cfg" -f "board/sifive-e31arty-cjtag.cfg"
+
+set _CHIPNAME riscv
+jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x20000913
+
+
+set _TARGETNAME $_CHIPNAME.cpu
+
+target create $_TARGETNAME.0 riscv -chain-position $_TARGETNAME
+$_TARGETNAME.0 configure -work-area-phys 0x80000000 -work-area-size 10000 -work-area-backup 1
+
+flash bank spi0 fespi 0x40000000 0 0 0 $_TARGETNAME.0 0x20004000
+init
+if {[ info exists pulse_srst]} {
+ oscan1_ftdi_set_signal nSRST 0
+ oscan1_ftdi_set_signal nSRST z
+}
+halt
+flash protect 0 64 last off
+echo "Ready for Remote Connections"
diff --git a/tcl/board/sifive-e31arty-onboard-ftdi.cfg b/tcl/board/sifive-e31arty-onboard-ftdi.cfg
new file mode 100644
index 0000000..3d40cfa
--- /dev/null
+++ b/tcl/board/sifive-e31arty-onboard-ftdi.cfg
@@ -0,0 +1,25 @@
+#
+# Be sure you include the speed and interface before this file
+# Example:
+# -c "adapter_khz 5000" -f "interface/ftdi/arty-onboard-ftdi.cfg" -f "board/sifive-e31arty-onboard-ftdi.cfg"
+
+set _CHIPNAME riscv
+jtag newtap $_CHIPNAME cpu -irlen 6; # -expected-id 0x0362d093
+
+set _TARGETNAME $_CHIPNAME.cpu
+
+target create $_TARGETNAME.0 riscv -chain-position $_TARGETNAME
+$_TARGETNAME.0 configure -work-area-phys 0x80000000 -work-area-size 10000 -work-area-backup 1
+riscv use_bscan_tunnel 5
+
+# Uncomment if hardware has flash
+# flash bank spi0 fespi 0x40000000 0 0 0 $_TARGETNAME.0 0x20004000
+init
+if {[ info exists pulse_srst]} {
+ ftdi_set_signal nSRST 0
+ ftdi_set_signal nSRST z
+}
+halt
+# Uncomment if hardware has flash
+# flash protect 0 64 last off
+echo "Ready for Remote Connections"
diff --git a/tcl/board/sifive-hifive1-revb.cfg b/tcl/board/sifive-hifive1-revb.cfg
index e5fe104..7fcab0c 100644
--- a/tcl/board/sifive-hifive1-revb.cfg
+++ b/tcl/board/sifive-hifive1-revb.cfg
@@ -12,6 +12,9 @@ set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME.0 riscv -chain-position $_TARGETNAME
$_TARGETNAME.0 configure -work-area-phys 0x80000000 -work-area-size 0x4000 -work-area-backup 0
+riscv set_enable_virt2phys off
+riscv set_enable_virtual off
+
flash bank onboard_spi_flash fespi 0x20000000 0 0 0 $_TARGETNAME.0
init
diff --git a/tcl/interface/ftdi/arty-onboard-ftdi.cfg b/tcl/interface/ftdi/arty-onboard-ftdi.cfg
new file mode 100644
index 0000000..0edc615
--- /dev/null
+++ b/tcl/interface/ftdi/arty-onboard-ftdi.cfg
@@ -0,0 +1,7 @@
+interface ftdi
+# ftdi_device_desc "Arty On-board FTDI interface"
+ftdi vid_pid 0x0403 0x6010
+ftdi channel 0
+ftdi layout_init 0x0088 0x008b
+reset_config none
+
diff --git a/tcl/interface/ftdi/digilent-hs2-cjtag.cfg b/tcl/interface/ftdi/digilent-hs2-cjtag.cfg
new file mode 100644
index 0000000..3daf461
--- /dev/null
+++ b/tcl/interface/ftdi/digilent-hs2-cjtag.cfg
@@ -0,0 +1,17 @@
+adapter driver ftdi
+ftdi device_desc "Digilent Adept USB Device"
+ftdi vid_pid 0x0403 0x6014
+
+ftdi channel 0
+ftdi layout_init 0x60e8 0x60eb
+
+reset_config none
+
+# These signals are used for cJTAG escape sequence on initialization only
+ftdi layout_signal TCK -data 0x0001
+ftdi layout_signal TDI -data 0x0002
+ftdi layout_signal TDO -input 0x0004
+ftdi layout_signal TMS -data 0x0008
+ftdi layout_signal JTAG_SEL -ndata 0x6000 -oe 0x6000
+
+ftdi layout_signal TMSC_EN -data 0x0020 -oe 0x0020
diff --git a/tcl/interface/ftdi/olimex-arm-jtag-cjtag.cfg b/tcl/interface/ftdi/olimex-arm-jtag-cjtag.cfg
new file mode 100644
index 0000000..6939d00
--- /dev/null
+++ b/tcl/interface/ftdi/olimex-arm-jtag-cjtag.cfg
@@ -0,0 +1,27 @@
+#
+# Olimex ARM JTAG SWD adapter
+# https://www.olimex.com/Products/ARM/JTAG/ARM-JTAG-SWD/
+#
+
+#
+# Olimex ARM-USB-TINY-H
+#
+# http://www.olimex.com/dev/arm-usb-tiny-h.html
+#
+
+interface ftdi
+ftdi oscan1_mode on
+ftdi device_desc "Olimex OpenOCD JTAG ARM-USB-TINY-H"
+ftdi vid_pid 0x15ba 0x002a
+
+ftdi layout_init 0x0808 0x0a1b
+ftdi layout_signal nSRST -oe 0x0200
+# oscan1_ftdi_layout_signal nTRST -data 0x0100 -oe 0x0100
+ftdi layout_signal LED -data 0x0800
+
+# These signals are used for cJTAG escape sequence on initialization only
+ftdi layout_signal TCK -data 0x0001
+ftdi layout_signal TDI -data 0x0002
+ftdi layout_signal TDO -input 0x0004
+ftdi layout_signal TMS -data 0x0008
+ftdi layout_signal JTAG_SEL -data 0x0100 -oe 0x0100
diff --git a/tcl/interface/ftdi/olimex-arm-usb-ocd-h-cjtag.cfg b/tcl/interface/ftdi/olimex-arm-usb-ocd-h-cjtag.cfg
new file mode 100644
index 0000000..36ac587
--- /dev/null
+++ b/tcl/interface/ftdi/olimex-arm-usb-ocd-h-cjtag.cfg
@@ -0,0 +1,22 @@
+#
+# Olimex ARM-USB-OCD-H (using cJTAG)
+#
+# http://www.olimex.com/dev/arm-usb-ocd-h.html
+#
+
+interface ftdi
+ftdi oscan1_mode on
+ftdi device_desc "Olimex OpenOCD JTAG ARM-USB-OCD-H"
+ftdi vid_pid 0x15ba 0x002b
+
+ftdi layout_init 0x0808 0x0a1b
+ftdi layout_signal nSRST -oe 0x0200
+# oscan1_ftdi_layout_signal nTRST -data 0x0100 -oe 0x0100
+ftdi layout_signal LED -data 0x0800
+
+# These signals are used for cJTAG escape sequence on initialization only
+ftdi layout_signal TCK -data 0x0001
+ftdi layout_signal TDI -data 0x0002
+ftdi layout_signal TDO -input 0x0004
+ftdi layout_signal TMS -data 0x0008
+ftdi layout_signal JTAG_SEL -data 0x0100 -oe 0x0100
diff --git a/tcl/interface/ftdi/olimex-arm-usb-tiny-h-cjtag.cfg b/tcl/interface/ftdi/olimex-arm-usb-tiny-h-cjtag.cfg
new file mode 100644
index 0000000..13378b3
--- /dev/null
+++ b/tcl/interface/ftdi/olimex-arm-usb-tiny-h-cjtag.cfg
@@ -0,0 +1,27 @@
+#
+# Olimex ARM JTAG SWD adapter
+# https://www.olimex.com/Products/ARM/JTAG/ARM-JTAG-SWD/
+#
+
+#
+# Olimex ARM-USB-TINY-H (using cJTAG)
+#
+# http://www.olimex.com/dev/arm-usb-tiny-h.html
+#
+
+interface ftdi
+ftdi oscan1_mode on
+ftdi device_desc "Olimex OpenOCD JTAG ARM-USB-TINY-H"
+ftdi vid_pid 0x15ba 0x002a
+
+ftdi layout_init 0x0808 0x0a1b
+ftdi layout_signal nSRST -oe 0x0200
+# oscan1_ftdi_layout_signal nTRST -data 0x0100 -oe 0x0100
+ftdi layout_signal LED -data 0x0800
+
+# These signals are used for cJTAG escape sequence on initialization only
+ftdi layout_signal TCK -data 0x0001
+ftdi layout_signal TDI -data 0x0002
+ftdi layout_signal TDO -input 0x0004
+ftdi layout_signal TMS -data 0x0008
+ftdi layout_signal JTAG_SEL -data 0x0100 -oe 0x0100
diff --git a/tcl/target/1986ве1т.cfg b/tcl/target/1986Be1T.cfg
index a3172cc..a3172cc 100644
--- a/tcl/target/1986ве1т.cfg
+++ b/tcl/target/1986Be1T.cfg
diff --git a/tcl/target/к1879xб1я.cfg b/tcl/target/K1879x61R.cfg
index 8dd330d..8dd330d 100644
--- a/tcl/target/к1879xб1я.cfg
+++ b/tcl/target/K1879x61R.cfg
diff --git a/tcl/target/esp32c2.cfg b/tcl/target/esp32c2.cfg
new file mode 100644
index 0000000..2af6dd2
--- /dev/null
+++ b/tcl/target/esp32c2.cfg
@@ -0,0 +1,111 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# The ESP32-C2 only supports JTAG.
+transport select jtag
+
+# Source the ESP common configuration file
+source [find target/esp_common.cfg]
+
+if { [info exists CHIPNAME] } {
+ set _CHIPNAME $CHIPNAME
+} else {
+ set _CHIPNAME esp32c2
+}
+
+if { [info exists CPUTAPID] } {
+ set _CPUTAPID $CPUTAPID
+} else {
+ set _CPUTAPID 0x0000cc25
+}
+
+set _TARGETNAME $_CHIPNAME
+set _CPUNAME cpu
+set _TAPNAME $_CHIPNAME.$_CPUNAME
+
+jtag newtap $_CHIPNAME $_CPUNAME -irlen 5 -expected-id $_CPUTAPID
+
+proc esp32c2_wdt_disable { } {
+ # Halt event can occur during config phase (before "init" is done).
+ # Ignore it since mww commands don't work at that time.
+ if { [string compare [command mode] config] == 0 } {
+ return
+ }
+
+ # Timer Group 0 WDT
+ mww 0x6001f064 0x50D83AA1
+ mww 0x6001F048 0
+ # RTC WDT
+ mww 0x6000809C 0x50D83AA1
+ mww 0x60008084 0
+ # SWD
+ mww 0x600080A4 0x8F1D312A
+ mww 0x600080A0 0x84B00000
+}
+
+# This is almost identical with the esp32c3_soc_reset.
+# Will be refactored with the other common settings.
+proc esp32c2_soc_reset { } {
+ # This procedure does "digital system reset", i.e. resets
+ # all the peripherals except for the RTC block.
+ # It is called from reset-assert-post target event callback,
+ # after assert_reset procedure was called.
+ # Since we need the hart to to execute a write to RTC_CNTL_SW_SYS_RST,
+ # temporarily take it out of reset. Save the dmcontrol state before
+ # doing so.
+ riscv dmi_write 0x10 0x80000001
+ # Trigger the reset
+ mww 0x60008000 0x9c00a000
+ # Workaround for stuck in cpu start during calibration.
+ # By writing zero to TIMG_RTCCALICFG_REG, we are disabling calibration
+ mww 0x6001F068 0
+ # Wait for the reset to happen
+ sleep 10
+ poll
+ # Disable the watchdogs again
+ esp32c2_wdt_disable
+
+ # Here debugger reads allresumeack and allhalted bits as set (0x330a2)
+ # We will clean allhalted state by resuming the core.
+ riscv dmi_write 0x10 0x40000001
+
+ # Put the hart back into reset state. Note that we need to keep haltreq set.
+ riscv dmi_write 0x10 0x80000003
+}
+
+if { $_RTOS == "none" } {
+ target create $_TARGETNAME riscv -chain-position $_TAPNAME
+} else {
+ target create $_TARGETNAME riscv -chain-position $_TAPNAME -rtos $_RTOS
+}
+
+$_TARGETNAME configure -event reset-assert-post { esp32c2_soc_reset }
+$_TARGETNAME configure -event halted {
+ esp32c2_wdt_disable
+}
+$_TARGETNAME configure -event examine-end {
+ # Need this to handle 'apptrace init' syscall correctly because semihosting is not enabled by default
+ arm semihosting enable
+ arm semihosting_resexit enable
+ if { [info exists _SEMIHOST_BASEDIR] } {
+ if { $_SEMIHOST_BASEDIR != "" } {
+ # TODO: cherry-pick from upstream
+ # https://review.openocd.org/c/openocd/+/6888
+ # https://review.openocd.org/c/openocd/+/7005
+ # arm semihosting_basedir $_SEMIHOST_BASEDIR
+ }
+ }
+}
+$_TARGETNAME configure -event gdb-attach {
+ halt 1000
+ # by default mask interrupts while stepping
+ riscv set_maskisr steponly
+}
+
+gdb_breakpoint_override hard
+
+riscv set_reset_timeout_sec 2
+riscv set_command_timeout_sec 5
+riscv set_mem_access sysbus progbuf abstract
+riscv set_ebreakm on
+riscv set_ebreaks on
+riscv set_ebreaku on
diff --git a/tcl/target/esp32c3.cfg b/tcl/target/esp32c3.cfg
new file mode 100644
index 0000000..ecb9bdf
--- /dev/null
+++ b/tcl/target/esp32c3.cfg
@@ -0,0 +1,113 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# The ESP32-C3 only supports JTAG.
+transport select jtag
+
+# Source the ESP common configuration file
+source [find target/esp_common.cfg]
+
+if { [info exists CHIPNAME] } {
+ set _CHIPNAME $CHIPNAME
+} else {
+ set _CHIPNAME esp32c3
+}
+
+if { [info exists CPUTAPID] } {
+ set _CPUTAPID $CPUTAPID
+} else {
+ set _CPUTAPID 0x00005c25
+}
+
+set _TARGETNAME $_CHIPNAME
+set _CPUNAME cpu
+set _TAPNAME $_CHIPNAME.$_CPUNAME
+
+jtag newtap $_CHIPNAME $_CPUNAME -irlen 5 -expected-id $_CPUTAPID
+
+proc esp32c3_wdt_disable { } {
+ # Halt event can occur during config phase (before "init" is done).
+ # Ignore it since mww commands don't work at that time.
+ if { [string compare [command mode] config] == 0 } {
+ return
+ }
+
+ # Timer Group 0 & 1 WDTs
+ mww 0x6001f064 0x50D83AA1
+ mww 0x6001F048 0
+ mww 0x60020064 0x50D83AA1
+ mww 0x60020048 0
+ # RTC WDT
+ mww 0x600080a8 0x50D83AA1
+ mww 0x60008090 0
+ # SWD
+ mww 0x600080b0 0x8F1D312A
+ mww 0x600080ac 0x84B00000
+}
+
+# This is almost identical with the esp32c2_soc_reset.
+# Will be refactored with the other common settings.
+proc esp32c3_soc_reset { } {
+ # This procedure does "digital system reset", i.e. resets
+ # all the peripherals except for the RTC block.
+ # It is called from reset-assert-post target event callback,
+ # after assert_reset procedure was called.
+ # Since we need the hart to to execute a write to RTC_CNTL_SW_SYS_RST,
+ # temporarily take it out of reset. Save the dmcontrol state before
+ # doing so.
+ riscv dmi_write 0x10 0x80000001
+ # Trigger the reset
+ mww 0x60008000 0x9c00a000
+ # Workaround for stuck in cpu start during calibration.
+ # By writing zero to TIMG_RTCCALICFG_REG, we are disabling calibration
+ mww 0x6001F068 0
+ # Wait for the reset to happen
+ sleep 10
+ poll
+ # Disable the watchdogs again
+ esp32c3_wdt_disable
+
+ # Here debugger reads allresumeack and allhalted bits as set (0x330a2)
+ # We will clean allhalted state by resuming the core.
+ riscv dmi_write 0x10 0x40000001
+
+ # Put the hart back into reset state. Note that we need to keep haltreq set.
+ riscv dmi_write 0x10 0x80000003
+}
+
+if { $_RTOS == "none" } {
+ target create $_TARGETNAME riscv -chain-position $_TAPNAME
+} else {
+ target create $_TARGETNAME riscv -chain-position $_TAPNAME -rtos $_RTOS
+}
+
+$_TARGETNAME configure -event reset-assert-post { esp32c3_soc_reset }
+$_TARGETNAME configure -event halted {
+ esp32c3_wdt_disable
+}
+$_TARGETNAME configure -event examine-end {
+ # Need this to handle 'apptrace init' syscall correctly because semihosting is not enabled by default
+ arm semihosting enable
+ arm semihosting_resexit enable
+ if { [info exists _SEMIHOST_BASEDIR] } {
+ if { $_SEMIHOST_BASEDIR != "" } {
+ # TODO: cherry-pick from upstream
+ # https://review.openocd.org/c/openocd/+/6888
+ # https://review.openocd.org/c/openocd/+/7005
+ # arm semihosting_basedir $_SEMIHOST_BASEDIR
+ }
+ }
+}
+$_TARGETNAME configure -event gdb-attach {
+ halt 1000
+ # by default mask interrupts while stepping
+ riscv set_maskisr steponly
+}
+
+gdb_breakpoint_override hard
+
+riscv set_reset_timeout_sec 2
+riscv set_command_timeout_sec 5
+riscv set_mem_access sysbus progbuf abstract
+riscv set_ebreakm on
+riscv set_ebreaks on
+riscv set_ebreaku on
diff --git a/tcl/target/esp_common.cfg b/tcl/target/esp_common.cfg
index 424c0cd..c02bcb2 100644
--- a/tcl/target/esp_common.cfg
+++ b/tcl/target/esp_common.cfg
@@ -2,6 +2,12 @@
#
# Common ESP chips definitions
+if { [info exists ESP_RTOS] } {
+ set _RTOS "$ESP_RTOS"
+} else {
+ set _RTOS "FreeRTOS"
+}
+
if { [info exists ESP_SEMIHOST_BASEDIR] } {
set _SEMIHOST_BASEDIR $ESP_SEMIHOST_BASEDIR
} else {
diff --git a/tcl/target/gd32vf103.cfg b/tcl/target/gd32vf103.cfg
index 0681243..6262697 100644
--- a/tcl/target/gd32vf103.cfg
+++ b/tcl/target/gd32vf103.cfg
@@ -6,37 +6,104 @@
# https://www.gigadevice.com/products/microcontrollers/gd32/risc-v/
#
+adapter speed 1000
source [find mem_helper.tcl]
transport select jtag
-if { [info exists CHIPNAME] } {
- set _CHIPNAME $CHIPNAME
-} else {
- set _CHIPNAME gd32vf103
-}
-
-# The smallest RAM size 6kB (GD32VF103C4/T4/R4)
-if { [info exists WORKAREASIZE] } {
- set _WORKAREASIZE $WORKAREASIZE
-} else {
- set _WORKAREASIZE 0x1800
-}
+reset_config srst_nogate
+set _CHIPNAME gd32vf103
+# The vendor's configuration expects an ID of 0x1e200a6d, but this one is what
+# I have on my board (Sipeed Longan Nano, GD32VF103CBT6).
jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x1000563d
+jtag newtap $_CHIPNAME bs -irlen 5 -expected-id 0x790007a3
set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME riscv -chain-position $_TARGETNAME
+$_TARGETNAME riscv set_enable_virt2phys off
+
+proc default_mem_access {} {
+ riscv set_mem_access progbuf
+}
-$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0
+default_mem_access
+
+$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size 0x1000 -work-area-backup 1
set _FLASHNAME $_CHIPNAME.flash
flash bank $_FLASHNAME stm32f1x 0x08000000 0 0 0 $_TARGETNAME
-# DBGMCU_CR register cannot be set in examine-end event as the running RISC-V CPU
-# does not allow the debugger to access memory.
-# Stop watchdogs at least before flash programming.
-$_TARGETNAME configure -event reset-init {
- # DBGMCU_CR |= DBG_WWDG_STOP | DBG_IWDG_STOP
- mmw 0xE0042004 0x00000300 0
+# Address 0 is only aliased to main flash when the chip is not running its
+# built-in bootloader. When it is, it's instead aliased to a read only section
+# of flash at 0x1fffb000. However, we can't detect or dynamically switch this,
+# so just pretend it's always aliased to main flash. We need to tell OpenOCD
+# about this alias because otherwise we'll try to use software breakpoints on
+# code in flash, which don't work because flash mappings are read-only.
+flash bank $_CHIPNAME.flashalias virtual 0x0 0 0 0 $_TARGETNAME $_FLASHNAME
+
+# On this chip, ndmreset (the debug module bit that triggers a software reset)
+# doesn't work. So for JTAG connections without an SRST, we need to trigger a
+# reset manually. This is an undocumented reset sequence that's used by the
+# JTAG flashing script in the vendor-supplied GD32VF103 PlatformIO plugin:
+#
+# https://github.com/sipeed/platform-gd32v/commit/f9cbb44819bc05dd2010cc815c32be0486800cc2
+#
+$_TARGETNAME configure -event reset-assert {
+ set dmcontrol 0x10
+ set dmcontrol_dmactive [expr 1 << 0]
+ set dmcontrol_haltreq [expr 1 << 31]
+
+ global _RESETMODE
+ global _TARGETNAME
+
+ # Halt the core so that we can write to memory. We do this first so
+ # that it doesn't clobber our dmcontrol configuration.
+ halt
+
+ # Set haltreq appropriately for the type of reset we're doing. This
+ # replicates what the generic RISC-V reset_assert() function would
+ # do if we weren't overriding it. The $_RESETMODE hack sucks, but
+ # it's the least invasive way to determine whether we need to halt,
+ # and psoc6.cfg already uses the same trick. (reset_deassert(), which
+ # does run, also does this, but at that point it may be too late: the
+ # reset has already been triggered, so there's a race between it and
+ # the haltreq write.)
+ #
+ # If we didn't override the generic handler, we'd actually still have
+ # to do this: the default handler sets ndmreset, which prevents memory
+ # access even though it doesn't actually trigger a reset on this chip.
+ # So we'd need to unset it here, which involves a write to dmcontrol,
+ # Since haltreq is write-only and there's no way to leave it unchanged,
+ # we'd have to figure out its proper value anyway.
+ set val $dmcontrol_dmactive
+ if {$_RESETMODE ne "run"} {
+ set val [expr $val | $dmcontrol_haltreq]
+ }
+ $_TARGETNAME riscv dmi_write $dmcontrol $val
+
+ # Unlock 0xe0042008 so that the next write triggers a reset
+ $_TARGETNAME mww 0xe004200c 0x4b5a6978
+
+ # We need to trigger the reset using abstract memory access, since
+ # progbuf access tries to read a status code out of a core register
+ # after the write happens, which fails when the core is in reset.
+ riscv set_mem_access abstract
+
+ # Go!
+ $_TARGETNAME mww 0xe0042008 0x1
+
+ # Put the memory access mode back to what it was.
+ default_mem_access
+}
+
+# Capture the mode of a given reset so that we can use it later in the
+# reset-assert handler.
+proc init_reset { mode } {
+ global _RESETMODE
+ set _RESETMODE $mode
+
+ if {[using_jtag]} {
+ jtag arp_init-reset
+ }
}
diff --git a/tools/filter_openocd_log.py b/tools/filter_openocd_log.py
new file mode 100755
index 0000000..666e166
--- /dev/null
+++ b/tools/filter_openocd_log.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python3
+
+import sys
+import re
+
+# This function is the only OpenOCD-specific part of this script.
+def make_canonical(line):
+ # Remove the line number and time stamp.
+ m = re.match(r"(Debug|Error|Info |User |Warn ): \d+ \d+ (.*)", line)
+ if m:
+ return "%s: - - %s\n" % (m.group(1), m.group(2))
+ else:
+ return line
+
+def buf_startswith(buf, sequence):
+ if len(buf) < len(sequence):
+ return False
+ for i, entry in enumerate(sequence):
+ if entry[1] != buf[i][1]:
+ return False
+ return True
+
+def shorten_buffer(outfd, buf, current_repetition):
+ """Do something to the buffer to make it shorter. If we can't compress
+ anything, then print out the first line and remove it."""
+ length_before = len(buf)
+
+ if current_repetition:
+ while buf_startswith(buf, current_repetition[0]):
+ del buf[:len(current_repetition[0])]
+ current_repetition[1] += 1
+ if len(buf) < length_before:
+ return current_repetition
+ outfd.write("## The following %d lines repeat %d times:\n" % (
+ len(current_repetition[0]), current_repetition[1]))
+ for entry in current_repetition[0]:
+ outfd.write("# %s" % entry[1])
+
+ # Look for repeated sequences...
+ repetitions = []
+ for length in range(1, int(len(buf)/2)):
+ # Is there a repeating sequence of `length` lines?
+ matched_lines = 0
+ for i, entry in enumerate(buf[length:]):
+ if entry[1] == buf[i % length][1]:
+ matched_lines += 1
+ else:
+ break
+ if matched_lines >= length:
+ repetitions.append((matched_lines + length, length))
+
+ if repetitions:
+ repetitions.sort(key=lambda entry: -entry[1])
+ matched_lines, length = repetitions[-1]
+ repeated = int(matched_lines / length)
+ if repeated * length >= 3:
+ sequence = buf[:length]
+ del buf[:repeated * length]
+
+ if matched_lines == length_before:
+ # Could be continued...
+ return [sequence, repeated]
+
+ else:
+ outfd.write("## The following %d lines repeat %d times:\n" %
+ (length, repeated))
+ for entry in sequence:
+ outfd.write("# %s" % entry[1])
+ return None
+
+ if len(buf) >= length_before:
+ line, _ = buf[0]
+ outfd.write(line)
+ buf.pop(0)
+
+ if length_before <= len(buf):
+ print("Buffer:")
+ for entry in buf:
+ print("%r" % entry[0])
+ assert False
+
+ return None
+
+def compress_log(infd, outfd, window):
+ """Compress log by finding repeated runs of lines. Runs in O(lines *
+ window**2), which can probably be improved."""
+ # Contains line, canonical tuples
+ buf = []
+ current_repetition = None
+
+ for line in infd:
+ buf.append((line, make_canonical(line)))
+ if len(buf) > window:
+ current_repetition = shorten_buffer(outfd, buf, current_repetition)
+
+ while len(buf) > 0:
+ current_repetition = shorten_buffer(outfd, buf, current_repetition)
+
+def main(args):
+ import argparse
+ parser = argparse.ArgumentParser(
+ description='Combine repeated OpenOCD debug output lines. This is '
+ 'very helpful when looking at verbose log files where e.g. target '
+ 'polling is repeated over and over.',
+ epilog='If no files are specified, read standard input.',
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+ parser.add_argument('file', nargs='*', help='input file')
+ parser.add_argument('-o', '--output', help='output file', default=sys.stdout)
+ parser.add_argument('-w', '--window', type=int, default=400,
+ help='number of lines to consider when looking for repetitions')
+ args = parser.parse_args(args)
+
+ if args.file:
+ for f in args.file:
+ compress_log(open(f, "r"), args.output, args.window)
+ else:
+ compress_log(sys.stdin, args.output, args.window)
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/tools/scripts/checkpatch.pl b/tools/scripts/checkpatch.pl
index 9dda61c..6ea0241 100755
--- a/tools/scripts/checkpatch.pl
+++ b/tools/scripts/checkpatch.pl
@@ -3562,16 +3562,17 @@ sub process {
}
# Check for FSF mailing addresses.
- if ($rawline =~ /\bwrite to the Free/i ||
- $rawline =~ /\b675\s+Mass\s+Ave/i ||
- $rawline =~ /\b59\s+Temple\s+Pl/i ||
- $rawline =~ /\b51\s+Franklin\s+St/i) {
- my $herevet = "$here\n" . cat_vet($rawline) . "\n";
- my $msg_level = \&ERROR;
- $msg_level = \&CHK if ($file);
- &{$msg_level}("FSF_MAILING_ADDRESS",
- "Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. OpenOCD already includes a copy of the GPL.\n" . $herevet)
- }
+ # Don't care in this branch. This always messes up when we merge changes down.
+ #if ($rawline =~ /\bwrite to the Free/i ||
+ # $rawline =~ /\b675\s+Mass\s+Ave/i ||
+ # $rawline =~ /\b59\s+Temple\s+Pl/i ||
+ # $rawline =~ /\b51\s+Franklin\s+St/i) {
+ # my $herevet = "$here\n" . cat_vet($rawline) . "\n";
+ # my $msg_level = \&ERROR;
+ # $msg_level = \&CHK if ($file);
+ # &{$msg_level}("FSF_MAILING_ADDRESS",
+ # "Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. OpenOCD already includes a copy of the GPL.\n" . $herevet)
+ #}
# check for Kconfig help text having a real description
# Only applies when adding the entry originally, after that we do not have