diff options
66 files changed, 14016 insertions, 3671 deletions
diff --git a/.github/workflows/checkpatch.yml b/.github/workflows/checkpatch.yml new file mode 100644 index 0000000..b7f3eba --- /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/spike-openocd-tests.yml b/.github/workflows/spike-openocd-tests.yml new file mode 100644 index 0000000..880ca57 --- /dev/null +++ b/.github/workflows/spike-openocd-tests.yml @@ -0,0 +1,136 @@ +# 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: + # Run on merges to master to populate the cache with entities that are + # accessible by every pull request. + push: + branches: + - riscv + pull_request: + types: [synchronize, opened, reopened] + +# 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 Spike32 Tests + id: spike32-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 + + - name: Run Spike64-2 Tests + if: success() || steps.spike32-tests.conclusion == 'failure' + run: | + cd riscv-tests/debug + ./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 @@ -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. + - Sparse Static Analyzer Using this tool allows identifying some bug in C code. diff --git a/Makefile.am b/Makefile.am index a4f77ad..ac08e41 100644 --- a/Makefile.am +++ b/Makefile.am @@ -37,13 +37,15 @@ DISTCLEANFILES += jimtcl/jsmn/jsmn.o 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 \ diff --git a/configure.ac b/configure.ac index ecf8384..9d851c0 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]], @@ -375,7 +376,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/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 3348e47..c8a42f4 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -2660,6 +2660,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 @@ -10825,11 +10854,9 @@ addreg rtest 0x1234 org.gnu.gdb.or1k.group0 system @section RISC-V Architecture @uref{http://riscv.org/, RISC-V} is a free and open ISA. OpenOCD supports JTAG -debug of RV32 and RV64 cores in heterogeneous multicore systems of up to 32 -harts. (It's possible to increase this limit to 1024 by changing -RISCV_MAX_HARTS in riscv.h.) OpenOCD primarily supports 0.13 of the RISC-V -Debug Specification, but there is also support for legacy targets that -implement version 0.11. +debug of RV32 and RV64 cores in heterogeneous multicore systems of up to 2^20 +harts. OpenOCD primarily supports 0.13 of the RISC-V Debug Specification, +but there is also support for legacy targets that implement version 0.11. @subsection RISC-V Terminology @@ -10876,6 +10903,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 @@ -10890,13 +10924,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 @@ -10924,8 +10958,43 @@ $_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. +Displays some information OpenOCD detected about the target. Output's format +allows to use it directly with TCL's `array set` function. In case obtaining an +info point failed, the corresponding value is displayed as "unavailable". @end deffn @deffn {Command} {riscv reset_delays} [wait] @@ -10996,26 +11065,80 @@ 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_ebreakm} on|off +@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] Control dcsr.ebreakm. When on (default), M-mode ebreak instructions trap to OpenOCD. When off, they generate a breakpoint exception handled internally. @end deffn -@deffn {Command} {riscv set_ebreaks} on|off +@deffn {Command} {riscv set_ebreaks} [on|off] Control dcsr.ebreaks. When on (default), S-mode ebreak instructions trap to OpenOCD. When off, they generate a breakpoint exception handled internally. @end deffn -@deffn {Command} {riscv set_ebreaku} on|off +@deffn {Command} {riscv set_ebreaku} [on|off] Control dcsr.ebreaku. When on (default), U-mode ebreak instructions trap to OpenOCD. When off, they generate a breakpoint exception handled internally. @end deffn +The commands below can be used to prevent OpenOCD from using certain RISC-V trigger features. +For example in cases when there are known issues in the target hardware. + +@deffn {Command} {riscv set_enable_eq_match_trigger} [on|off] +When on (default), allow OpenOCD to use exact-match triggers in watchpoints. +@end deffn + +@deffn {Command} {riscv set_enable_napot_trigger} [on|off] +When on (default), allow OpenOCD to use NAPOT trigger in watchpoints. +@end deffn + +@deffn {Command} {riscv set_enable_ge_lt_trigger} [on|off] +When on (default), allow OpenOCD to use a pair of chained less-than & greater-than triggers in watchpoints. +@end deffn + @subsection RISC-V Authentication Commands The following commands can be used to authenticate to a RISC-V system. Eg. a @@ -11026,27 +11149,130 @@ 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 -The following commands allow direct access to the Debug Module Interface, which -can be used to interact with custom debug features. +The following commands allow for direct low-level access to the registers +of the Debug Module (DM). They can be useful to access custom features in the DM. + +@deffn {Command} {riscv dm_read} reg_address +Perform a 32-bit read from the register indicated by reg_address from the DM of the +current target. +@end deffn + +@deffn {Command} {riscv dm_write} reg_address value +Write the 32-bit value to the register indicated by reg_address from the DM of the +current target. +@end deffn + +The following commands allow for direct low-level access to the Debug Module +Interface (DMI). They can be useful to access any device that resides on the DMI. @deffn {Command} {riscv dmi_read} address -Perform a 32-bit DMI read at address, returning the value. +Perform a 32-bit read from the given DMI address, returning the value. @end deffn @deffn {Command} {riscv dmi_write} address value -Perform a 32-bit DMI write of value at address. +Perform a 32-bit write to the given DMI 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 @@ -12288,6 +12514,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/src/flash/nor/tcl.c b/src/flash/nor/tcl.c index 720fb60..1c4e154 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 e0fa331..39d93d6 100644 --- a/src/helper/Makefile.am +++ b/src/helper/Makefile.am @@ -34,6 +34,8 @@ noinst_LTLIBRARIES += %D%/libhelper.la %D%/jep106.h \ %D%/jep106.inc \ %D%/jim-nvp.h \ + %D%/base64.c \ + %D%/base64.h \ %D%/nvp.h \ %D%/compiler.h 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/drivers/ftdi.c b/src/jtag/drivers/ftdi.c index 7175925..dd8cb16 100644 --- a/src/jtag/drivers/ftdi.c +++ b/src/jtag/drivers/ftdi.c @@ -75,6 +75,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) @@ -85,6 +95,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 }; @@ -230,6 +273,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 @@ -258,7 +330,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, @@ -312,7 +384,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, @@ -326,7 +398,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; } @@ -361,7 +433,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, @@ -408,7 +480,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, @@ -462,7 +534,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, @@ -472,12 +544,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, @@ -487,7 +555,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, @@ -496,7 +564,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, @@ -505,7 +573,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, @@ -586,7 +654,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; } @@ -598,10 +666,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: @@ -676,6 +753,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); @@ -707,6 +799,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) { @@ -918,6 +1294,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", @@ -979,6 +1381,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/rtos/FreeRTOS.c b/src/rtos/FreeRTOS.c index e8df030..ea37f7f 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[]); const struct rtos_type freertos_rtos = { @@ -87,6 +243,8 @@ const 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,60 +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. */ - retval = target_read_u32(rtos->target, - list_elem_ptr + param->list_elem_content_offset, - &pointer_casts_are_bad); + rtos->thread_details[tasks_found].threadid = 0; + 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) { @@ -342,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') @@ -353,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 +733,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); } } @@ -386,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, ®); + *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[]) @@ -532,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 895f11c..763a97d 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) @@ -43,12 +46,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 { @@ -84,7 +89,7 @@ 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; @@ -96,7 +101,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; ++thread_list_size; @@ -121,7 +127,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); @@ -198,7 +205,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; } @@ -283,8 +290,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; @@ -312,11 +319,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; } @@ -408,6 +418,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) { @@ -437,3 +452,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 136d93b..faf30bd 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" @@ -43,7 +44,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) @@ -52,7 +55,8 @@ static int rtos_target_for_threadid(struct connection *connection, int64_t threa return ERROR_OK; } -static int os_alloc(struct target *target, const struct rtos_type *ostype) +static int os_alloc(struct target *target, const struct rtos_type *ostype, + struct command_context *cmd_ctx) { struct rtos *os = target->rtos = calloc(1, sizeof(struct rtos)); @@ -68,6 +72,7 @@ static int os_alloc(struct target *target, const 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; } @@ -82,9 +87,10 @@ static void os_free(struct target *target) target->rtos = NULL; } -static int os_alloc_create(struct target *target, const struct rtos_type *ostype) +static int os_alloc_create(struct target *target, const 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); @@ -107,6 +113,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); @@ -124,12 +132,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); @@ -502,51 +510,71 @@ 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; - if ((target->rtos) && (current_threadid != -1) && - (current_threadid != 0) && - ((current_threadid != target->rtos->current_thread) || - (target->smp))) { /* in smp several current thread are possible */ - struct rtos_reg *reg_list; - int num_regs; + threadid_t current_threadid = target->rtos->current_threadid; + if (!target->rtos || + current_threadid == -1 || + current_threadid == 0 || + (current_threadid == target->rtos->current_thread && + !target->smp)) { /* in smp several current thread are possible */ + return ERROR_NOT_IMPLEMENTED; + } - LOG_DEBUG("getting register %d for thread 0x%" PRIx64 - ", target->rtos->current_thread=0x%" PRIx64, - reg_num, - current_threadid, - 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, ®_list[0]); - if (retval != ERROR_OK) { - LOG_ERROR("RTOS: failed to get register %d", reg_num); - return retval; - } - } else { - retval = target->rtos->type->get_thread_reg_list(target->rtos, - current_threadid, - ®_list, - &num_regs); - if (retval != ERROR_OK) { - LOG_ERROR("RTOS: failed to get register list"); - return retval; - } + struct rtos_reg *reg_list; + int num_regs; + + LOG_TARGET_DEBUG(target, "getting register %d for thread 0x%" PRIx64 + ", target->rtos->current_thread=0x%" PRIx64, + reg_num, current_threadid, target->rtos->current_thread); + + int retval; + 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, ®_size, ®_value); + if (retval != ERROR_OK) { + LOG_ERROR("RTOS: failed to get register %d", reg_num); + return retval; } - for (int i = 0; i < num_regs; ++i) { - if (reg_list[i].number == (uint32_t)reg_num) { - rtos_put_gdb_reg_list(connection, reg_list + i, 1); - free(reg_list); - return ERROR_OK; - } + /* 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(®_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, + ®_list, + &num_regs); + if (retval != ERROR_OK) { + LOG_ERROR("RTOS: failed to get register list"); + return retval; + } + } - free(reg_list); + for (int i = 0; i < num_regs; ++i) { + if (reg_list[i].number == (uint32_t)reg_num) { + rtos_put_gdb_reg_list(connection, reg_list + i, 1); + free(reg_list); + return ERROR_OK; + } } + + free(reg_list); + return ERROR_FAIL; } @@ -562,10 +590,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, @@ -600,7 +627,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) { @@ -612,7 +639,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; @@ -625,7 +652,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 :"); @@ -655,6 +682,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); @@ -662,6 +692,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; @@ -684,10 +808,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; } @@ -709,6 +852,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) { @@ -724,3 +874,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 5ba8b26..210f4fc 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,11 @@ 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 int total_register_count; + /* Optional field for targets which may have to implement their own stack read function. * Because stack format can be weird or stack data needed to be edited before passing to the gdb. */ @@ -119,9 +145,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_thread_packet(struct connection *connection, const char *packet, int packet_size); int rtos_get_gdb_reg(struct connection *connection, int reg_num); @@ -131,10 +165,14 @@ 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); extern const struct rtos_type chibios_rtos; extern const struct rtos_type chromium_ec_rtos; diff --git a/src/rtos/rtos_standard_stackings.c b/src/rtos/rtos_standard_stackings.c index 5478080..0ca664f 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" #include "rtos_standard_stackings.h" static const struct stack_register_offset rtos_standard_cortex_m3_stack_offsets[ARMV7M_NUM_CORE_REGS] = { @@ -103,15 +104,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 @@ -230,3 +397,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 99fbe07..40a3503 100644 --- a/src/rtos/rtos_standard_stackings.h +++ b/src/rtos/rtos_standard_stackings.h @@ -21,4 +21,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 4a4ea53..57a6d5f 100644 --- a/src/server/gdb_server.c +++ b/src/server/gdb_server.c @@ -143,6 +143,47 @@ 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; +} + +/** Return true iff the given connection includes the given target. */ +static bool gdb_connection_includes_target(struct connection *connection, struct target *target) +{ + struct gdb_service *gdb_service = connection->service->priv; + struct target *service_target = gdb_service->target; + if (service_target->smp) { + struct target_list *tlist; + foreach_smp_target(tlist, service_target->smp_targets) { + struct target *t = tlist->target; + if (t == target) + return true; + } + return false; + } + /* Non-SMP target. */ + return service_target == target; +} + static int gdb_last_signal(struct target *target) { LOG_TARGET_DEBUG(target, "Debug reason is: %s", @@ -409,6 +450,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 (;; ) { @@ -427,7 +470,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 @@ -775,9 +818,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; } @@ -815,9 +861,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); @@ -946,9 +992,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; - if (gdb_service->target != target) + /* Propagate this event if it's for any of the targets on this gdb connection. */ + if (!gdb_connection_includes_target(connection, target)) return ERROR_OK; switch (event) { @@ -1355,8 +1401,13 @@ static int gdb_get_register_packet(struct connection *connection, LOG_DEBUG("-"); #endif - if ((target->rtos) && (rtos_get_gdb_reg(connection, reg_num) == ERROR_OK)) - return ERROR_OK; + if (target->rtos) { + retval = rtos_get_gdb_reg(connection, reg_num); + if (retval == ERROR_OK) + return ERROR_OK; + if (retval != ERROR_NOT_IMPLEMENTED) + return gdb_error(connection, retval); + } retval = target_get_gdb_reg_list_noread(target, ®_list, ®_list_size, REG_CLASS_ALL); @@ -1366,7 +1417,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) { @@ -1477,7 +1528,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; @@ -1485,7 +1536,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++; @@ -1552,7 +1603,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; @@ -1603,7 +1654,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; @@ -1682,7 +1733,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; @@ -1710,7 +1761,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 */; @@ -1759,6 +1810,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 */ @@ -1892,7 +1949,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; @@ -2597,6 +2654,7 @@ static int gdb_target_description_supported(struct target *target, int *supporte ®_list_size, REG_CLASS_ALL); if (retval != ERROR_OK) { LOG_ERROR("get register list failed"); + reg_list = NULL; goto error; } @@ -2741,6 +2799,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; @@ -2988,6 +3047,24 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p /* simple case, a continue packet */ if (parse[0] == 'c') { gdb_running_type = 'c'; + + if (target->state == TARGET_UNAVAILABLE) { + struct target *available_target = get_available_target_from_connection(connection); + if (target == available_target) { + LOG_DEBUG("All targets for this gdb connection " + "are unavailable. Fake to gdb that the resume " + "succeeded and the target is now running."); + gdb_connection->frontend_state = TARGET_RUNNING; + gdb_connection->output_flag = GDB_OUTPUT_ALL; + target_call_event_callbacks(target, TARGET_EVENT_GDB_START); + return true; + } + LOG_TARGET_DEBUG(target, "Target is unavailable. Resume %s instead.", + target_name(available_target)); + /* Resume an available target. */ + target = available_target; + } + LOG_DEBUG("target %s continue", target_name(target)); gdb_connection->output_flag = GDB_OUTPUT_ALL; retval = target_resume(target, 1, 0, 0, 0); @@ -3034,7 +3111,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); @@ -3042,8 +3135,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] == ';') { @@ -3080,19 +3172,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); @@ -3116,9 +3204,15 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p return true; } - retval = target_step(ct, current_pc, 0, 0); - if (retval == ERROR_TARGET_NOT_HALTED) - LOG_INFO("target %s was not halted when step was requested", target_name(ct)); + if (ct->state == TARGET_UNAVAILABLE) { + LOG_TARGET_ERROR(ct, "Target is unavailable, so cannot be stepped. " + "Pretending to gdb that it is running until it's available again."); + retval = ERROR_FAIL; + } else { + retval = target_step(ct, current_pc, 0, 0); + if (retval == ERROR_TARGET_NOT_HALTED) + LOG_INFO("target %s was not halted when step was requested", target_name(ct)); + } /* if step was successful send a reply back to gdb */ if (retval == ERROR_OK) { @@ -3189,7 +3283,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" */ @@ -3235,7 +3329,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; @@ -3406,7 +3500,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); @@ -3695,10 +3789,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); @@ -3706,7 +3801,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/target/breakpoints.c b/src/target/breakpoints.c index 4d268ce..0d7850e 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; @@ -223,6 +220,8 @@ int breakpoint_add(struct target *target, return ERROR_OK; } else { + /* For software breakpoints on SMP targets, only set them on a + * single target. We assume that SMP targets share memory. */ return breakpoint_add_internal(target, address, length, type); } } @@ -237,6 +236,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 +260,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; @@ -269,11 +272,17 @@ int hybrid_breakpoint_add(struct target *target, return hybrid_breakpoint_add_internal(target, address, asid, length, type); } -/* free up a breakpoint */ -static void breakpoint_free(struct target *target, struct breakpoint *breakpoint_to_remove) +/* Free the data structures we use to track a breakpoint on data_target. + * Remove the actual breakpoint from breakpoint_target. + * This separation is useful when a software breakpoint is tracked on a target + * that is currently unavailable, but the breakpoint also affects a target that + * is available. + */ +static void breakpoint_free(struct target *data_target, struct target *breakpoint_target, + struct breakpoint *breakpoint_to_remove) { - struct breakpoint *breakpoint = target->breakpoints; - struct breakpoint **breakpoint_p = &target->breakpoints; + struct breakpoint *breakpoint = data_target->breakpoints; + struct breakpoint **breakpoint_p = &data_target->breakpoints; int retval; while (breakpoint) { @@ -286,7 +295,7 @@ static void breakpoint_free(struct target *target, struct breakpoint *breakpoint if (!breakpoint) return; - retval = target_remove_breakpoint(target, breakpoint); + retval = target_remove_breakpoint(breakpoint_target, breakpoint); LOG_DEBUG("free BPID: %" PRIu32 " --> %d", breakpoint->unique_id, retval); (*breakpoint_p) = breakpoint->next; @@ -294,27 +303,6 @@ static void breakpoint_free(struct target *target, struct breakpoint *breakpoint free(breakpoint); } -static int breakpoint_remove_internal(struct target *target, target_addr_t address) -{ - struct breakpoint *breakpoint = target->breakpoints; - - while (breakpoint) { - if ((breakpoint->address == address) || - (breakpoint->address == 0 && breakpoint->asid == address)) - break; - breakpoint = breakpoint->next; - } - - if (breakpoint) { - breakpoint_free(target, breakpoint); - return 1; - } else { - if (!target->smp) - LOG_ERROR("no breakpoint at address " TARGET_ADDR_FMT " found", address); - return 0; - } -} - static void breakpoint_remove_all_internal(struct target *target) { struct breakpoint *breakpoint = target->breakpoints; @@ -322,24 +310,90 @@ static void breakpoint_remove_all_internal(struct target *target) while (breakpoint) { struct breakpoint *tmp = breakpoint; breakpoint = breakpoint->next; - breakpoint_free(target, tmp); + breakpoint_free(target, target, tmp); } } void breakpoint_remove(struct target *target, target_addr_t address) { - if (target->smp) { - unsigned int num_breakpoints = 0; - struct target_list *head; + if (!target->smp) { + struct breakpoint *breakpoint = breakpoint_find(target, address); + if (breakpoint) + breakpoint_free(target, target, breakpoint); + return; + } - foreach_smp_target(head, target->smp_targets) { - struct target *curr = head->target; - num_breakpoints += breakpoint_remove_internal(curr, address); + unsigned int found = 0; + struct target_list *head; + /* Target where we found a software breakpoint. */ + struct target *software_breakpoint_target = NULL; + struct breakpoint *software_breakpoint = NULL; + /* Target that is available. */ + struct target *available_target = NULL; + /* Target that is available and halted. */ + struct target *halted_target = NULL; + + foreach_smp_target(head, target->smp_targets) { + struct target *curr = head->target; + + if (!available_target && curr->state != TARGET_UNAVAILABLE) + available_target = curr; + if (!halted_target && curr->state == TARGET_HALTED) + halted_target = curr; + + struct breakpoint *breakpoint = breakpoint_find(curr, address); + if (!breakpoint) + continue; + + found++; + + if (breakpoint->type == BKPT_SOFT) { + /* Software breakpoints are set on only one of the SMP + * targets. We can remove them through any of the SMP + * targets. */ + if (software_breakpoint_target) { + LOG_TARGET_WARNING(curr, "Already found software breakpoint at " + TARGET_ADDR_FMT " on %s.", address, target_name(software_breakpoint_target)); + } else { + assert(!software_breakpoint_target); + software_breakpoint_target = curr; + software_breakpoint = breakpoint; + } + } else { + breakpoint_free(curr, curr, breakpoint); + } + } + + if (!found) { + LOG_ERROR("no breakpoint at address " TARGET_ADDR_FMT " found", address); + return; + } + + if (software_breakpoint) { + struct target *remove_target; + if (software_breakpoint_target->state == TARGET_HALTED) + remove_target = software_breakpoint_target; + else if (halted_target) + remove_target = halted_target; + else + remove_target = available_target; + + if (remove_target) { + LOG_DEBUG("Removing software breakpoint found on %s using %s (address=" + TARGET_ADDR_FMT ").", + target_name(software_breakpoint_target), + target_name(remove_target), + address); + /* Remove the software breakpoint through + * remove_target, but update the breakpoints structure + * of software_breakpoint_target. */ + /* TODO: If there is an error, can we try to remove the + * same breakpoint from a different target? */ + breakpoint_free(software_breakpoint_target, remove_target, software_breakpoint); + } else { + LOG_WARNING("No halted target found to remove software breakpoint at " + TARGET_ADDR_FMT ".", address); } - if (!num_breakpoints) - LOG_ERROR("no breakpoint at address " TARGET_ADDR_FMT " found", address); - } else { - breakpoint_remove_internal(target, address); } } @@ -362,7 +416,7 @@ static void breakpoint_clear_target_internal(struct target *target) LOG_DEBUG("Delete all breakpoints for target: %s", target_name(target)); while (target->breakpoints) - breakpoint_free(target, target->breakpoints); + breakpoint_free(target, target, target->breakpoints); } void breakpoint_clear_target(struct target *target) @@ -384,7 +438,8 @@ struct breakpoint *breakpoint_find(struct target *target, target_addr_t address) struct breakpoint *breakpoint = target->breakpoints; while (breakpoint) { - if (breakpoint->address == address) + if (breakpoint->address == address || + (breakpoint->address == 0 && breakpoint->asid == address)) return breakpoint; breakpoint = breakpoint->next; } @@ -435,7 +490,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 +522,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 d447515..afca9b7 100644 --- a/src/target/breakpoints.h +++ b/src/target/breakpoints.h @@ -47,7 +47,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 5789201..16fd149 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/Makefile.am b/src/target/riscv/Makefile.am index 4b6a74f..7f25eca 100644 --- a/src/target/riscv/Makefile.am +++ b/src/target/riscv/Makefile.am @@ -5,6 +5,7 @@ noinst_LTLIBRARIES += %D%/libriscv.la %D%/asm.h \ %D%/batch.h \ %D%/debug_defines.h \ + %D%/debug_reg_printer.h \ %D%/encoding.h \ %D%/gdb_regs.h \ %D%/opcodes.h \ @@ -15,4 +16,6 @@ noinst_LTLIBRARIES += %D%/libriscv.la %D%/riscv-011.c \ %D%/riscv-013.c \ %D%/riscv.c \ - %D%/riscv_semihosting.c + %D%/riscv_semihosting.c \ + %D%/debug_defines.c \ + %D%/debug_reg_printer.c diff --git a/src/target/riscv/batch.c b/src/target/riscv/batch.c index d39967e..2dbf851 100644 --- a/src/target/riscv/batch.c +++ b/src/target/riscv/batch.c @@ -88,7 +88,7 @@ bool riscv_batch_full(struct riscv_batch *batch) int riscv_batch_run(struct riscv_batch *batch) { if (batch->used_scans == 0) { - LOG_DEBUG("Ignoring empty batch."); + LOG_TARGET_DEBUG(batch->target, "Ignoring empty batch."); return ERROR_OK; } @@ -107,7 +107,7 @@ int riscv_batch_run(struct riscv_batch *batch) keep_alive(); if (jtag_execute_queue() != ERROR_OK) { - LOG_ERROR("Unable to execute JTAG queue"); + LOG_TARGET_ERROR(batch->target, "Unable to execute JTAG queue"); return ERROR_FAIL; } @@ -127,28 +127,33 @@ 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_dm_write(struct riscv_batch *batch, unsigned int 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); + riscv_fill_dm_write_u64(batch->target, (char *)field->out_value, address, data); + if (read_back) { + field->in_value = (void *)(batch->data_in + batch->used_scans * DMI_SCAN_BUF_SIZE); + riscv_fill_dm_nop_u64(batch->target, (char *)field->in_value); + } else { + field->in_value = NULL; + } batch->last_scan = RISCV_SCAN_TYPE_WRITE; batch->used_scans++; } -size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, unsigned address) +size_t riscv_batch_add_dm_read(struct riscv_batch *batch, unsigned int address) { 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_read_u64(batch->target, (char *)field->out_value, address); - riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value); + riscv_fill_dm_read_u64(batch->target, (char *)field->out_value, address); + riscv_fill_dm_nop_u64(batch->target, (char *)field->in_value); batch->last_scan = RISCV_SCAN_TYPE_READ; batch->used_scans++; @@ -156,7 +161,7 @@ size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, unsigned address) return batch->read_keys_used++; } -unsigned riscv_batch_get_dmi_read_op(struct riscv_batch *batch, size_t key) +unsigned int riscv_batch_get_dmi_read_op(const struct riscv_batch *batch, size_t key) { assert(key < batch->read_keys_used); size_t index = batch->read_keys[key]; @@ -166,7 +171,7 @@ unsigned riscv_batch_get_dmi_read_op(struct riscv_batch *batch, size_t key) return (unsigned)buf_get_u32(base, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH); } -uint32_t riscv_batch_get_dmi_read_data(struct riscv_batch *batch, size_t key) +uint32_t riscv_batch_get_dmi_read_data(const struct riscv_batch *batch, size_t key) { assert(key < batch->read_keys_used); size_t index = batch->read_keys[key]; @@ -183,13 +188,13 @@ void riscv_batch_add_nop(struct riscv_batch *batch) 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_nop_u64(batch->target, (char *)field->out_value); - riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value); + riscv_fill_dm_nop_u64(batch->target, (char *)field->out_value); + riscv_fill_dm_nop_u64(batch->target, (char *)field->in_value); batch->last_scan = RISCV_SCAN_TYPE_NOP; batch->used_scans++; } -void dump_field(int idle, const struct scan_field *field) +static void dump_field(int idle, const struct scan_field *field) { static const char * const op_string[] = {"-", "r", "w", "?"}; static const char * const status_string[] = {"+", "?", "F", "b"}; diff --git a/src/target/riscv/batch.h b/src/target/riscv/batch.h index 9c42ba8..07de735 100644 --- a/src/target/riscv/batch.h +++ b/src/target/riscv/batch.h @@ -58,15 +58,16 @@ bool riscv_batch_full(struct riscv_batch *batch); /* Executes this scan 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); +/* Adds a DM register write to this batch. */ +void riscv_batch_add_dm_write(struct riscv_batch *batch, unsigned int address, uint64_t data, + bool read_back); -/* DMI reads must be handled in two parts: the first one schedules a read and +/* DM register 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 - * status (op) and the actual data. */ -size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, unsigned address); -unsigned riscv_batch_get_dmi_read_op(struct riscv_batch *batch, size_t key); -uint32_t riscv_batch_get_dmi_read_data(struct riscv_batch *batch, size_t key); +size_t riscv_batch_add_dm_read(struct riscv_batch *batch, unsigned int address); +unsigned int riscv_batch_get_dmi_read_op(const struct riscv_batch *batch, size_t key); +uint32_t riscv_batch_get_dmi_read_data(const struct riscv_batch *batch, size_t key); /* Scans in a NOP. */ void riscv_batch_add_nop(struct riscv_batch *batch); diff --git a/src/target/riscv/debug_defines.c b/src/target/riscv/debug_defines.c new file mode 100644 index 0000000..da20e19 --- /dev/null +++ b/src/target/riscv/debug_defines.c @@ -0,0 +1,4218 @@ +/* + * This file is auto-generated by running 'make debug_defines' in + * https://github.com/riscv/riscv-debug-spec/ (f546ddf) + */ + +#include "debug_defines.h" +#include <stddef.h> +#include <assert.h> +static riscv_debug_reg_field_list_t dtm_idcode_get_version(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "Version", + .lsb = 0x1c, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dtm_idcode_get_partnumber(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "PartNumber", + .lsb = 0xc, + .msb = 0x1b, + .values = NULL + }, + .get_next = dtm_idcode_get_version + }; + return result; +} + +static riscv_debug_reg_field_list_t dtm_idcode_get_manufid(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "ManufId", + .lsb = 1, + .msb = 0xb, + .values = NULL + }, + .get_next = dtm_idcode_get_partnumber + }; + return result; +} + +static riscv_debug_reg_field_list_t dtm_idcode_get_1(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "1", + .lsb = 0, + .msb = 0, + .values = NULL + }, + .get_next = dtm_idcode_get_manufid + }; + return result; +} + +static const char *dtm_dtmcs_errinfo_values[8] = { + [0] = "not implemented", + [1] = "dmi error", + [2] = "communication error", + [3] = "device error", + [4] = "unknown" +}; +static const char *dtm_dtmcs_version_values[16] = { + [0] = "0.11", + [1] = "1.0", + [15] = "custom" +}; +static riscv_debug_reg_field_list_t dtm_dtmcs_get_abits(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "abits", + .lsb = 4, + .msb = 9, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dtm_dtmcs_get_errinfo(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "errinfo", + .lsb = 0x12, + .msb = 0x14, + .values = dtm_dtmcs_errinfo_values + }, + .get_next = dtm_dtmcs_get_abits + }; + return result; +} + +static riscv_debug_reg_field_list_t dtm_dtmcs_get_dtmhardreset(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dtmhardreset", + .lsb = 0x11, + .msb = 0x11, + .values = NULL + }, + .get_next = dtm_dtmcs_get_errinfo + }; + return result; +} + +static riscv_debug_reg_field_list_t dtm_dtmcs_get_dmireset(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dmireset", + .lsb = 0x10, + .msb = 0x10, + .values = NULL + }, + .get_next = dtm_dtmcs_get_dtmhardreset + }; + return result; +} + +static riscv_debug_reg_field_list_t dtm_dtmcs_get_idle(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "idle", + .lsb = 0xc, + .msb = 0xe, + .values = NULL + }, + .get_next = dtm_dtmcs_get_dmireset + }; + return result; +} + +static riscv_debug_reg_field_list_t dtm_dtmcs_get_dmistat(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dmistat", + .lsb = 0xa, + .msb = 0xb, + .values = NULL + }, + .get_next = dtm_dtmcs_get_idle + }; + return result; +} + +static riscv_debug_reg_field_list_t dtm_dtmcs_get_version(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "version", + .lsb = 0, + .msb = 3, + .values = dtm_dtmcs_version_values + }, + .get_next = dtm_dtmcs_get_dmistat + }; + return result; +} + +static const char *dtm_dmi_op_values[4] = {}; +static riscv_debug_reg_field_list_t dtm_dmi_get_address(riscv_debug_reg_ctx_t context) +{ + assert(context.abits.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "address", + .lsb = 0x22, + .msb = (context.abits.value + 0x21), + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dtm_dmi_get_data(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "data", + .lsb = 2, + .msb = 0x21, + .values = NULL + }, + .get_next = dtm_dmi_get_address + }; + return result; +} + +static riscv_debug_reg_field_list_t dtm_dmi_get_op(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "op", + .lsb = 0, + .msb = 1, + .values = dtm_dmi_op_values + }, + .get_next = dtm_dmi_get_data + }; + return result; +} + + +static const char *csr_dcsr_debugver_values[16] = { + [0] = "none", + [4] = "1.0", + [15] = "custom" +}; +static const char *csr_dcsr_ebreakvs_values[2] = { + [0] = "exception", + [1] = "debug mode" +}; +static const char *csr_dcsr_ebreakvu_values[2] = { + [0] = "exception", + [1] = "debug mode" +}; +static const char *csr_dcsr_ebreakm_values[2] = { + [0] = "exception", + [1] = "debug mode" +}; +static const char *csr_dcsr_ebreaks_values[2] = { + [0] = "exception", + [1] = "debug mode" +}; +static const char *csr_dcsr_ebreaku_values[2] = { + [0] = "exception", + [1] = "debug mode" +}; +static const char *csr_dcsr_stepie_values[2] = { + [0] = "interrupts disabled", + [1] = "interrupts enabled" +}; +static const char *csr_dcsr_stopcount_values[2] = { + [0] = "normal", + [1] = "freeze" +}; +static const char *csr_dcsr_stoptime_values[2] = { + [0] = "normal", + [1] = "freeze" +}; +static const char *csr_dcsr_cause_values[8] = { + [1] = "ebreak", + [2] = "trigger", + [3] = "haltreq", + [4] = "step", + [5] = "resethaltreq", + [6] = "group" +}; +static const char *csr_dcsr_mprven_values[2] = { + [0] = "disabled", + [1] = "enabled" +}; +static riscv_debug_reg_field_list_t csr_dcsr_get_stoptime(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "stoptime", + .lsb = 9, + .msb = 9, + .values = csr_dcsr_stoptime_values + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_cause(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "cause", + .lsb = 6, + .msb = 8, + .values = csr_dcsr_cause_values + }, + .get_next = csr_dcsr_get_stoptime + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_v(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "v", + .lsb = 5, + .msb = 5, + .values = NULL + }, + .get_next = csr_dcsr_get_cause + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_mprven(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "mprven", + .lsb = 4, + .msb = 4, + .values = csr_dcsr_mprven_values + }, + .get_next = csr_dcsr_get_v + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_nmip(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "nmip", + .lsb = 3, + .msb = 3, + .values = NULL + }, + .get_next = csr_dcsr_get_mprven + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_debugver(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "debugver", + .lsb = 0x1c, + .msb = 0x1f, + .values = csr_dcsr_debugver_values + }, + .get_next = csr_dcsr_get_nmip + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_step(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "step", + .lsb = 2, + .msb = 2, + .values = NULL + }, + .get_next = csr_dcsr_get_debugver + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_ebreakvs(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "ebreakvs", + .lsb = 0x11, + .msb = 0x11, + .values = csr_dcsr_ebreakvs_values + }, + .get_next = csr_dcsr_get_step + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_ebreakvu(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "ebreakvu", + .lsb = 0x10, + .msb = 0x10, + .values = csr_dcsr_ebreakvu_values + }, + .get_next = csr_dcsr_get_ebreakvs + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_ebreakm(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "ebreakm", + .lsb = 0xf, + .msb = 0xf, + .values = csr_dcsr_ebreakm_values + }, + .get_next = csr_dcsr_get_ebreakvu + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_ebreaks(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "ebreaks", + .lsb = 0xd, + .msb = 0xd, + .values = csr_dcsr_ebreaks_values + }, + .get_next = csr_dcsr_get_ebreakm + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_ebreaku(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "ebreaku", + .lsb = 0xc, + .msb = 0xc, + .values = csr_dcsr_ebreaku_values + }, + .get_next = csr_dcsr_get_ebreaks + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_stepie(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "stepie", + .lsb = 0xb, + .msb = 0xb, + .values = csr_dcsr_stepie_values + }, + .get_next = csr_dcsr_get_ebreaku + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_stopcount(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "stopcount", + .lsb = 0xa, + .msb = 0xa, + .values = csr_dcsr_stopcount_values + }, + .get_next = csr_dcsr_get_stepie + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_prv(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "prv", + .lsb = 0, + .msb = 1, + .values = NULL + }, + .get_next = csr_dcsr_get_stopcount + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dpc_get_dpc(riscv_debug_reg_ctx_t context) +{ + assert(context.DXLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dpc", + .lsb = 0, + .msb = (context.DXLEN.value + -1), + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_tselect_get_index(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "index", + .lsb = 0, + .msb = (context.XLEN.value + -1), + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static const char *csr_tdata1_type_values[16] = { + [0] = "none", + [1] = "legacy", + [2] = "mcontrol", + [3] = "icount", + [4] = "itrigger", + [5] = "etrigger", + [6] = "mcontrol6", + [7] = "tmexttrigger", + [15] = "disabled" +}; +static const char *csr_tdata1_dmode_values[2] = { + [0] = "both", + [1] = "dmode" +}; +static riscv_debug_reg_field_list_t csr_tdata1_get_dmode(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dmode", + .lsb = (context.XLEN.value + -5), + .msb = (context.XLEN.value + -5), + .values = csr_tdata1_dmode_values + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_tdata1_get_type(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "type", + .lsb = (context.XLEN.value + -4), + .msb = (context.XLEN.value + -1), + .values = csr_tdata1_type_values + }, + .get_next = csr_tdata1_get_dmode + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_tdata1_get_data(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "data", + .lsb = 0, + .msb = (context.XLEN.value + -6), + .values = NULL + }, + .get_next = csr_tdata1_get_type + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_tdata2_get_data(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "data", + .lsb = 0, + .msb = (context.XLEN.value + -1), + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_tdata3_get_data(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "data", + .lsb = 0, + .msb = (context.XLEN.value + -1), + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static const char *csr_tinfo_version_values[256] = { + [0] = "0", + [1] = "1" +}; +static riscv_debug_reg_field_list_t csr_tinfo_get_version(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "version", + .lsb = 0x18, + .msb = 0x1f, + .values = csr_tinfo_version_values + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_tinfo_get_info(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "info", + .lsb = 0, + .msb = 0xf, + .values = NULL + }, + .get_next = csr_tinfo_get_version + }; + return result; +} + +static const char *csr_tcontrol_mte_values[2] = { + [0] = "disabled", + [1] = "enabled" +}; +static riscv_debug_reg_field_list_t csr_tcontrol_get_mpte(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "mpte", + .lsb = 7, + .msb = 7, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_tcontrol_get_mte(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "mte", + .lsb = 3, + .msb = 3, + .values = csr_tcontrol_mte_values + }, + .get_next = csr_tcontrol_get_mpte + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_hcontext_get_hcontext(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hcontext", + .lsb = 0, + .msb = (context.XLEN.value + -1), + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_scontext_get_data(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "data", + .lsb = 0, + .msb = (context.XLEN.value + -1), + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static const char *csr_mcontrol_select_values[2] = { + [0] = "address", + [1] = "data" +}; +static const char *csr_mcontrol_timing_values[2] = { + [0] = "before", + [1] = "after" +}; +static const char *csr_mcontrol_sizelo_values[4] = {}; +static const char *csr_mcontrol_action_values[16] = { + [0] = "breakpoint", + [1] = "debug mode", + [2] = "trace on", + [3] = "trace off", + [4] = "trace notify", + [8] = "external0", + [9] = "external1" +}; +static const char *csr_mcontrol_chain_values[2] = { + [0] = "disabled", + [1] = "enabled" +}; +static const char *csr_mcontrol_match_values[16] = { + [0] = "equal", + [1] = "napot", + [2] = "ge", + [3] = "lt", + [4] = "mask low", + [5] = "mask high", + [8] = "not equal", + [9] = "not napot", + [12] = "not mask low", + [13] = "not mask high" +}; +static riscv_debug_reg_field_list_t csr_mcontrol_get_dmode(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dmode", + .lsb = (context.XLEN.value + -5), + .msb = (context.XLEN.value + -5), + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_type(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "type", + .lsb = (context.XLEN.value + -4), + .msb = (context.XLEN.value + -1), + .values = NULL + }, + .get_next = csr_mcontrol_get_dmode + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_maskmax(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "maskmax", + .lsb = (context.XLEN.value + -0xb), + .msb = (context.XLEN.value + -6), + .values = NULL + }, + .get_next = csr_mcontrol_get_type + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_match(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "match", + .lsb = 7, + .msb = 0xa, + .values = csr_mcontrol_match_values + }, + .get_next = csr_mcontrol_get_maskmax + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_m(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "m", + .lsb = 6, + .msb = 6, + .values = NULL + }, + .get_next = csr_mcontrol_get_match + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_s(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "s", + .lsb = 4, + .msb = 4, + .values = NULL + }, + .get_next = csr_mcontrol_get_m + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_u(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "u", + .lsb = 3, + .msb = 3, + .values = NULL + }, + .get_next = csr_mcontrol_get_s + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_sizehi(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sizehi", + .lsb = 0x15, + .msb = 0x16, + .values = NULL + }, + .get_next = csr_mcontrol_get_u + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_hit(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hit", + .lsb = 0x14, + .msb = 0x14, + .values = NULL + }, + .get_next = csr_mcontrol_get_sizehi + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_execute(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "execute", + .lsb = 2, + .msb = 2, + .values = NULL + }, + .get_next = csr_mcontrol_get_hit + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_select(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "select", + .lsb = 0x13, + .msb = 0x13, + .values = csr_mcontrol_select_values + }, + .get_next = csr_mcontrol_get_execute + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_timing(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "timing", + .lsb = 0x12, + .msb = 0x12, + .values = csr_mcontrol_timing_values + }, + .get_next = csr_mcontrol_get_select + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_sizelo(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sizelo", + .lsb = 0x10, + .msb = 0x11, + .values = csr_mcontrol_sizelo_values + }, + .get_next = csr_mcontrol_get_timing + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_action(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "action", + .lsb = 0xc, + .msb = 0xf, + .values = csr_mcontrol_action_values + }, + .get_next = csr_mcontrol_get_sizelo + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_chain(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "chain", + .lsb = 0xb, + .msb = 0xb, + .values = csr_mcontrol_chain_values + }, + .get_next = csr_mcontrol_get_action + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_store(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "store", + .lsb = 1, + .msb = 1, + .values = NULL + }, + .get_next = csr_mcontrol_get_chain + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_load(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "load", + .lsb = 0, + .msb = 0, + .values = NULL + }, + .get_next = csr_mcontrol_get_store + }; + return result; +} + +static const char *csr_mcontrol6_uncertain_values[2] = { + [0] = "certain", + [1] = "uncertain" +}; +static const char *csr_mcontrol6_hit0_values[2] = {}; +static const char *csr_mcontrol6_select_values[2] = { + [0] = "address", + [1] = "data" +}; +static const char *csr_mcontrol6_size_values[8] = { + [0] = "any", + [1] = "8bit", + [2] = "16bit", + [3] = "32bit", + [4] = "48bit", + [5] = "64bit", + [6] = "128bit" +}; +static const char *csr_mcontrol6_action_values[16] = { + [0] = "breakpoint", + [1] = "debug mode", + [2] = "trace on", + [3] = "trace off", + [4] = "trace notify", + [8] = "external0", + [9] = "external1" +}; +static const char *csr_mcontrol6_chain_values[2] = { + [0] = "disabled", + [1] = "enabled" +}; +static const char *csr_mcontrol6_match_values[16] = { + [0] = "equal", + [1] = "napot", + [2] = "ge", + [3] = "lt", + [4] = "mask low", + [5] = "mask high", + [8] = "not equal", + [9] = "not napot", + [12] = "not mask low", + [13] = "not mask high" +}; +static const char *csr_mcontrol6_uncertainen_values[2] = { + [0] = "disabled", + [1] = "enabled" +}; +static riscv_debug_reg_field_list_t csr_mcontrol6_get_dmode(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dmode", + .lsb = (context.XLEN.value + -5), + .msb = (context.XLEN.value + -5), + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_type(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "type", + .lsb = (context.XLEN.value + -4), + .msb = (context.XLEN.value + -1), + .values = NULL + }, + .get_next = csr_mcontrol6_get_dmode + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_match(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "match", + .lsb = 7, + .msb = 0xa, + .values = csr_mcontrol6_match_values + }, + .get_next = csr_mcontrol6_get_type + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_m(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "m", + .lsb = 6, + .msb = 6, + .values = NULL + }, + .get_next = csr_mcontrol6_get_match + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_uncertainen(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "uncertainen", + .lsb = 5, + .msb = 5, + .values = csr_mcontrol6_uncertainen_values + }, + .get_next = csr_mcontrol6_get_m + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_s(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "s", + .lsb = 4, + .msb = 4, + .values = NULL + }, + .get_next = csr_mcontrol6_get_uncertainen + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_u(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "u", + .lsb = 3, + .msb = 3, + .values = NULL + }, + .get_next = csr_mcontrol6_get_s + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_uncertain(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "uncertain", + .lsb = 0x1a, + .msb = 0x1a, + .values = csr_mcontrol6_uncertain_values + }, + .get_next = csr_mcontrol6_get_u + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_hit1(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hit1", + .lsb = 0x19, + .msb = 0x19, + .values = NULL + }, + .get_next = csr_mcontrol6_get_uncertain + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_vs(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "vs", + .lsb = 0x18, + .msb = 0x18, + .values = NULL + }, + .get_next = csr_mcontrol6_get_hit1 + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_vu(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "vu", + .lsb = 0x17, + .msb = 0x17, + .values = NULL + }, + .get_next = csr_mcontrol6_get_vs + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_hit0(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hit0", + .lsb = 0x16, + .msb = 0x16, + .values = csr_mcontrol6_hit0_values + }, + .get_next = csr_mcontrol6_get_vu + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_select(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "select", + .lsb = 0x15, + .msb = 0x15, + .values = csr_mcontrol6_select_values + }, + .get_next = csr_mcontrol6_get_hit0 + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_execute(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "execute", + .lsb = 2, + .msb = 2, + .values = NULL + }, + .get_next = csr_mcontrol6_get_select + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_size(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "size", + .lsb = 0x10, + .msb = 0x12, + .values = csr_mcontrol6_size_values + }, + .get_next = csr_mcontrol6_get_execute + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_action(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "action", + .lsb = 0xc, + .msb = 0xf, + .values = csr_mcontrol6_action_values + }, + .get_next = csr_mcontrol6_get_size + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_chain(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "chain", + .lsb = 0xb, + .msb = 0xb, + .values = csr_mcontrol6_chain_values + }, + .get_next = csr_mcontrol6_get_action + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_store(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "store", + .lsb = 1, + .msb = 1, + .values = NULL + }, + .get_next = csr_mcontrol6_get_chain + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_load(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "load", + .lsb = 0, + .msb = 0, + .values = NULL + }, + .get_next = csr_mcontrol6_get_store + }; + return result; +} + +static const char *csr_icount_action_values[64] = { + [0] = "breakpoint", + [1] = "debug mode", + [2] = "trace on", + [3] = "trace off", + [4] = "trace notify", + [8] = "external0", + [9] = "external1" +}; +static riscv_debug_reg_field_list_t csr_icount_get_dmode(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dmode", + .lsb = (context.XLEN.value + -5), + .msb = (context.XLEN.value + -5), + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_icount_get_type(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "type", + .lsb = (context.XLEN.value + -4), + .msb = (context.XLEN.value + -1), + .values = NULL + }, + .get_next = csr_icount_get_dmode + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_icount_get_m(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "m", + .lsb = 9, + .msb = 9, + .values = NULL + }, + .get_next = csr_icount_get_type + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_icount_get_pending(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "pending", + .lsb = 8, + .msb = 8, + .values = NULL + }, + .get_next = csr_icount_get_m + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_icount_get_s(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "s", + .lsb = 7, + .msb = 7, + .values = NULL + }, + .get_next = csr_icount_get_pending + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_icount_get_u(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "u", + .lsb = 6, + .msb = 6, + .values = NULL + }, + .get_next = csr_icount_get_s + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_icount_get_vs(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "vs", + .lsb = 0x1a, + .msb = 0x1a, + .values = NULL + }, + .get_next = csr_icount_get_u + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_icount_get_vu(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "vu", + .lsb = 0x19, + .msb = 0x19, + .values = NULL + }, + .get_next = csr_icount_get_vs + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_icount_get_hit(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hit", + .lsb = 0x18, + .msb = 0x18, + .values = NULL + }, + .get_next = csr_icount_get_vu + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_icount_get_count(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "count", + .lsb = 0xa, + .msb = 0x17, + .values = NULL + }, + .get_next = csr_icount_get_hit + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_icount_get_action(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "action", + .lsb = 0, + .msb = 5, + .values = csr_icount_action_values + }, + .get_next = csr_icount_get_count + }; + return result; +} + +static const char *csr_itrigger_action_values[64] = { + [0] = "breakpoint", + [1] = "debug mode", + [2] = "trace on", + [3] = "trace off", + [4] = "trace notify", + [8] = "external0", + [9] = "external1" +}; +static riscv_debug_reg_field_list_t csr_itrigger_get_hit(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hit", + .lsb = (context.XLEN.value + -6), + .msb = (context.XLEN.value + -6), + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_itrigger_get_dmode(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dmode", + .lsb = (context.XLEN.value + -5), + .msb = (context.XLEN.value + -5), + .values = NULL + }, + .get_next = csr_itrigger_get_hit + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_itrigger_get_type(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "type", + .lsb = (context.XLEN.value + -4), + .msb = (context.XLEN.value + -1), + .values = NULL + }, + .get_next = csr_itrigger_get_dmode + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_itrigger_get_m(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "m", + .lsb = 9, + .msb = 9, + .values = NULL + }, + .get_next = csr_itrigger_get_type + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_itrigger_get_s(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "s", + .lsb = 7, + .msb = 7, + .values = NULL + }, + .get_next = csr_itrigger_get_m + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_itrigger_get_u(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "u", + .lsb = 6, + .msb = 6, + .values = NULL + }, + .get_next = csr_itrigger_get_s + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_itrigger_get_vs(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "vs", + .lsb = 0xc, + .msb = 0xc, + .values = NULL + }, + .get_next = csr_itrigger_get_u + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_itrigger_get_vu(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "vu", + .lsb = 0xb, + .msb = 0xb, + .values = NULL + }, + .get_next = csr_itrigger_get_vs + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_itrigger_get_nmi(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "nmi", + .lsb = 0xa, + .msb = 0xa, + .values = NULL + }, + .get_next = csr_itrigger_get_vu + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_itrigger_get_action(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "action", + .lsb = 0, + .msb = 5, + .values = csr_itrigger_action_values + }, + .get_next = csr_itrigger_get_nmi + }; + return result; +} + +static const char *csr_etrigger_action_values[64] = { + [0] = "breakpoint", + [1] = "debug mode", + [2] = "trace on", + [3] = "trace off", + [4] = "trace notify", + [8] = "external0", + [9] = "external1" +}; +static riscv_debug_reg_field_list_t csr_etrigger_get_hit(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hit", + .lsb = (context.XLEN.value + -6), + .msb = (context.XLEN.value + -6), + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_etrigger_get_dmode(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dmode", + .lsb = (context.XLEN.value + -5), + .msb = (context.XLEN.value + -5), + .values = NULL + }, + .get_next = csr_etrigger_get_hit + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_etrigger_get_type(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "type", + .lsb = (context.XLEN.value + -4), + .msb = (context.XLEN.value + -1), + .values = NULL + }, + .get_next = csr_etrigger_get_dmode + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_etrigger_get_m(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "m", + .lsb = 9, + .msb = 9, + .values = NULL + }, + .get_next = csr_etrigger_get_type + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_etrigger_get_s(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "s", + .lsb = 7, + .msb = 7, + .values = NULL + }, + .get_next = csr_etrigger_get_m + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_etrigger_get_u(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "u", + .lsb = 6, + .msb = 6, + .values = NULL + }, + .get_next = csr_etrigger_get_s + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_etrigger_get_vs(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "vs", + .lsb = 0xc, + .msb = 0xc, + .values = NULL + }, + .get_next = csr_etrigger_get_u + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_etrigger_get_vu(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "vu", + .lsb = 0xb, + .msb = 0xb, + .values = NULL + }, + .get_next = csr_etrigger_get_vs + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_etrigger_get_action(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "action", + .lsb = 0, + .msb = 5, + .values = csr_etrigger_action_values + }, + .get_next = csr_etrigger_get_vu + }; + return result; +} + +static const char *csr_tmexttrigger_action_values[64] = { + [0] = "breakpoint", + [1] = "debug mode", + [2] = "trace on", + [3] = "trace off", + [4] = "trace notify", + [8] = "external0", + [9] = "external1" +}; +static riscv_debug_reg_field_list_t csr_tmexttrigger_get_hit(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hit", + .lsb = (context.XLEN.value + -6), + .msb = (context.XLEN.value + -6), + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_tmexttrigger_get_dmode(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dmode", + .lsb = (context.XLEN.value + -5), + .msb = (context.XLEN.value + -5), + .values = NULL + }, + .get_next = csr_tmexttrigger_get_hit + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_tmexttrigger_get_type(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "type", + .lsb = (context.XLEN.value + -4), + .msb = (context.XLEN.value + -1), + .values = NULL + }, + .get_next = csr_tmexttrigger_get_dmode + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_tmexttrigger_get_select(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "select", + .lsb = 6, + .msb = 0x15, + .values = NULL + }, + .get_next = csr_tmexttrigger_get_type + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_tmexttrigger_get_intctl(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "intctl", + .lsb = 0x16, + .msb = 0x16, + .values = NULL + }, + .get_next = csr_tmexttrigger_get_select + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_tmexttrigger_get_action(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "action", + .lsb = 0, + .msb = 5, + .values = csr_tmexttrigger_action_values + }, + .get_next = csr_tmexttrigger_get_intctl + }; + return result; +} + +static const char *csr_textra32_mhselect_values[8] = { + [0] = "ignore", + [4] = "mcontext" +}; +static const char *csr_textra32_sselect_values[4] = { + [0] = "ignore", + [1] = "scontext", + [2] = "asid" +}; +static riscv_debug_reg_field_list_t csr_textra32_get_mhvalue(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "mhvalue", + .lsb = 0x1a, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_textra32_get_mhselect(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "mhselect", + .lsb = 0x17, + .msb = 0x19, + .values = csr_textra32_mhselect_values + }, + .get_next = csr_textra32_get_mhvalue + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_textra32_get_svalue(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "svalue", + .lsb = 2, + .msb = 0x11, + .values = NULL + }, + .get_next = csr_textra32_get_mhselect + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_textra32_get_sbytemask(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbytemask", + .lsb = 0x12, + .msb = 0x13, + .values = NULL + }, + .get_next = csr_textra32_get_svalue + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_textra32_get_sselect(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sselect", + .lsb = 0, + .msb = 1, + .values = csr_textra32_sselect_values + }, + .get_next = csr_textra32_get_sbytemask + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_textra64_get_mhvalue(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "mhvalue", + .lsb = 0x33, + .msb = 0x3f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_textra64_get_mhselect(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "mhselect", + .lsb = 0x30, + .msb = 0x32, + .values = NULL + }, + .get_next = csr_textra64_get_mhvalue + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_textra64_get_sbytemask(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbytemask", + .lsb = 0x24, + .msb = 0x28, + .values = NULL + }, + .get_next = csr_textra64_get_mhselect + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_textra64_get_svalue(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "svalue", + .lsb = 2, + .msb = 0x23, + .values = NULL + }, + .get_next = csr_textra64_get_sbytemask + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_textra64_get_sselect(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sselect", + .lsb = 0, + .msb = 1, + .values = NULL + }, + .get_next = csr_textra64_get_svalue + }; + return result; +} + +static const char *dm_dmstatus_ndmresetpending_values[2] = { + [0] = "false", + [1] = "true" +}; +static const char *dm_dmstatus_stickyunavail_values[2] = { + [0] = "current", + [1] = "sticky" +}; +static const char *dm_dmstatus_authenticated_values[2] = { + [0] = "false", + [1] = "true" +}; +static const char *dm_dmstatus_authbusy_values[2] = { + [0] = "ready", + [1] = "busy" +}; +static const char *dm_dmstatus_confstrptrvalid_values[2] = { + [0] = "invalid", + [1] = "valid" +}; +static const char *dm_dmstatus_version_values[16] = { + [0] = "none", + [1] = "0.11", + [2] = "0.13", + [3] = "1.0", + [15] = "custom" +}; +static riscv_debug_reg_field_list_t dm_dmstatus_get_allhalted(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "allhalted", + .lsb = 9, + .msb = 9, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_anyhalted(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "anyhalted", + .lsb = 8, + .msb = 8, + .values = NULL + }, + .get_next = dm_dmstatus_get_allhalted + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_authenticated(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "authenticated", + .lsb = 7, + .msb = 7, + .values = dm_dmstatus_authenticated_values + }, + .get_next = dm_dmstatus_get_anyhalted + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_authbusy(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "authbusy", + .lsb = 6, + .msb = 6, + .values = dm_dmstatus_authbusy_values + }, + .get_next = dm_dmstatus_get_authenticated + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_hasresethaltreq(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hasresethaltreq", + .lsb = 5, + .msb = 5, + .values = NULL + }, + .get_next = dm_dmstatus_get_authbusy + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_confstrptrvalid(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "confstrptrvalid", + .lsb = 4, + .msb = 4, + .values = dm_dmstatus_confstrptrvalid_values + }, + .get_next = dm_dmstatus_get_hasresethaltreq + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_ndmresetpending(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "ndmresetpending", + .lsb = 0x18, + .msb = 0x18, + .values = dm_dmstatus_ndmresetpending_values + }, + .get_next = dm_dmstatus_get_confstrptrvalid + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_stickyunavail(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "stickyunavail", + .lsb = 0x17, + .msb = 0x17, + .values = dm_dmstatus_stickyunavail_values + }, + .get_next = dm_dmstatus_get_ndmresetpending + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_impebreak(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "impebreak", + .lsb = 0x16, + .msb = 0x16, + .values = NULL + }, + .get_next = dm_dmstatus_get_stickyunavail + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_allhavereset(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "allhavereset", + .lsb = 0x13, + .msb = 0x13, + .values = NULL + }, + .get_next = dm_dmstatus_get_impebreak + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_anyhavereset(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "anyhavereset", + .lsb = 0x12, + .msb = 0x12, + .values = NULL + }, + .get_next = dm_dmstatus_get_allhavereset + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_allresumeack(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "allresumeack", + .lsb = 0x11, + .msb = 0x11, + .values = NULL + }, + .get_next = dm_dmstatus_get_anyhavereset + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_anyresumeack(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "anyresumeack", + .lsb = 0x10, + .msb = 0x10, + .values = NULL + }, + .get_next = dm_dmstatus_get_allresumeack + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_allnonexistent(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "allnonexistent", + .lsb = 0xf, + .msb = 0xf, + .values = NULL + }, + .get_next = dm_dmstatus_get_anyresumeack + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_anynonexistent(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "anynonexistent", + .lsb = 0xe, + .msb = 0xe, + .values = NULL + }, + .get_next = dm_dmstatus_get_allnonexistent + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_allunavail(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "allunavail", + .lsb = 0xd, + .msb = 0xd, + .values = NULL + }, + .get_next = dm_dmstatus_get_anynonexistent + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_anyunavail(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "anyunavail", + .lsb = 0xc, + .msb = 0xc, + .values = NULL + }, + .get_next = dm_dmstatus_get_allunavail + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_allrunning(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "allrunning", + .lsb = 0xb, + .msb = 0xb, + .values = NULL + }, + .get_next = dm_dmstatus_get_anyunavail + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_anyrunning(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "anyrunning", + .lsb = 0xa, + .msb = 0xa, + .values = NULL + }, + .get_next = dm_dmstatus_get_allrunning + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_version(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "version", + .lsb = 0, + .msb = 3, + .values = dm_dmstatus_version_values + }, + .get_next = dm_dmstatus_get_anyrunning + }; + return result; +} + +static const char *dm_dmcontrol_ackhavereset_values[2] = { + [0] = "nop", + [1] = "ack" +}; +static const char *dm_dmcontrol_ackunavail_values[2] = { + [0] = "nop", + [1] = "ack" +}; +static const char *dm_dmcontrol_hasel_values[2] = { + [0] = "single", + [1] = "multiple" +}; +static const char *dm_dmcontrol_dmactive_values[2] = { + [0] = "inactive", + [1] = "active" +}; +static riscv_debug_reg_field_list_t dm_dmcontrol_get_hartselhi(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hartselhi", + .lsb = 6, + .msb = 0xf, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcontrol_get_setkeepalive(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "setkeepalive", + .lsb = 5, + .msb = 5, + .values = NULL + }, + .get_next = dm_dmcontrol_get_hartselhi + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcontrol_get_clrkeepalive(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "clrkeepalive", + .lsb = 4, + .msb = 4, + .values = NULL + }, + .get_next = dm_dmcontrol_get_setkeepalive + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcontrol_get_haltreq(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "haltreq", + .lsb = 0x1f, + .msb = 0x1f, + .values = NULL + }, + .get_next = dm_dmcontrol_get_clrkeepalive + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcontrol_get_resumereq(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "resumereq", + .lsb = 0x1e, + .msb = 0x1e, + .values = NULL + }, + .get_next = dm_dmcontrol_get_haltreq + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcontrol_get_setresethaltreq(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "setresethaltreq", + .lsb = 3, + .msb = 3, + .values = NULL + }, + .get_next = dm_dmcontrol_get_resumereq + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcontrol_get_hartreset(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hartreset", + .lsb = 0x1d, + .msb = 0x1d, + .values = NULL + }, + .get_next = dm_dmcontrol_get_setresethaltreq + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcontrol_get_ackhavereset(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "ackhavereset", + .lsb = 0x1c, + .msb = 0x1c, + .values = dm_dmcontrol_ackhavereset_values + }, + .get_next = dm_dmcontrol_get_hartreset + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcontrol_get_ackunavail(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "ackunavail", + .lsb = 0x1b, + .msb = 0x1b, + .values = dm_dmcontrol_ackunavail_values + }, + .get_next = dm_dmcontrol_get_ackhavereset + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcontrol_get_hasel(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hasel", + .lsb = 0x1a, + .msb = 0x1a, + .values = dm_dmcontrol_hasel_values + }, + .get_next = dm_dmcontrol_get_ackunavail + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcontrol_get_clrresethaltreq(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "clrresethaltreq", + .lsb = 2, + .msb = 2, + .values = NULL + }, + .get_next = dm_dmcontrol_get_hasel + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcontrol_get_hartsello(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hartsello", + .lsb = 0x10, + .msb = 0x19, + .values = NULL + }, + .get_next = dm_dmcontrol_get_clrresethaltreq + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcontrol_get_ndmreset(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "ndmreset", + .lsb = 1, + .msb = 1, + .values = NULL + }, + .get_next = dm_dmcontrol_get_hartsello + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcontrol_get_dmactive(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dmactive", + .lsb = 0, + .msb = 0, + .values = dm_dmcontrol_dmactive_values + }, + .get_next = dm_dmcontrol_get_ndmreset + }; + return result; +} + +static const char *dm_hartinfo_dataaccess_values[2] = { + [0] = "csr", + [1] = "memory" +}; +static riscv_debug_reg_field_list_t dm_hartinfo_get_nscratch(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "nscratch", + .lsb = 0x14, + .msb = 0x17, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_hartinfo_get_dataaccess(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dataaccess", + .lsb = 0x10, + .msb = 0x10, + .values = dm_hartinfo_dataaccess_values + }, + .get_next = dm_hartinfo_get_nscratch + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_hartinfo_get_datasize(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "datasize", + .lsb = 0xc, + .msb = 0xf, + .values = NULL + }, + .get_next = dm_hartinfo_get_dataaccess + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_hartinfo_get_dataaddr(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dataaddr", + .lsb = 0, + .msb = 0xb, + .values = NULL + }, + .get_next = dm_hartinfo_get_datasize + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_hawindowsel_get_hawindowsel(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hawindowsel", + .lsb = 0, + .msb = 0xe, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_hawindow_get_maskdata(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "maskdata", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static const char *dm_abstractcs_busy_values[2] = { + [0] = "ready", + [1] = "busy" +}; +static const char *dm_abstractcs_relaxedpriv_values[2] = { + [0] = "full checks", + [1] = "relaxed checks" +}; +static const char *dm_abstractcs_cmderr_values[8] = { + [0] = "none", + [1] = "busy", + [2] = "not supported", + [3] = "exception", + [4] = "halt/resume", + [5] = "bus", + [6] = "reserved", + [7] = "other" +}; +static riscv_debug_reg_field_list_t dm_abstractcs_get_cmderr(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "cmderr", + .lsb = 8, + .msb = 0xa, + .values = dm_abstractcs_cmderr_values + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_abstractcs_get_progbufsize(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "progbufsize", + .lsb = 0x18, + .msb = 0x1c, + .values = NULL + }, + .get_next = dm_abstractcs_get_cmderr + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_abstractcs_get_busy(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "busy", + .lsb = 0xc, + .msb = 0xc, + .values = dm_abstractcs_busy_values + }, + .get_next = dm_abstractcs_get_progbufsize + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_abstractcs_get_relaxedpriv(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "relaxedpriv", + .lsb = 0xb, + .msb = 0xb, + .values = dm_abstractcs_relaxedpriv_values + }, + .get_next = dm_abstractcs_get_busy + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_abstractcs_get_datacount(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "datacount", + .lsb = 0, + .msb = 3, + .values = NULL + }, + .get_next = dm_abstractcs_get_relaxedpriv + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_command_get_cmdtype(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "cmdtype", + .lsb = 0x18, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_command_get_control(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "control", + .lsb = 0, + .msb = 0x17, + .values = NULL + }, + .get_next = dm_command_get_cmdtype + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_abstractauto_get_autoexecprogbuf(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "autoexecprogbuf", + .lsb = 0x10, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_abstractauto_get_autoexecdata(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "autoexecdata", + .lsb = 0, + .msb = 0xb, + .values = NULL + }, + .get_next = dm_abstractauto_get_autoexecprogbuf + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_confstrptr0_get_addr(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "addr", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_confstrptr1_get_addr(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "addr", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_confstrptr2_get_addr(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "addr", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_confstrptr3_get_addr(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "addr", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_nextdm_get_addr(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "addr", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_data0_get_data(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "data", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_progbuf0_get_data(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "data", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_authdata_get_data(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "data", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static const char *dm_dmcs2_grouptype_values[2] = { + [0] = "halt", + [1] = "resume" +}; +static const char *dm_dmcs2_hgselect_values[2] = { + [0] = "harts", + [1] = "triggers" +}; +static riscv_debug_reg_field_list_t dm_dmcs2_get_dmexttrigger(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dmexttrigger", + .lsb = 7, + .msb = 0xa, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcs2_get_group(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "group", + .lsb = 2, + .msb = 6, + .values = NULL + }, + .get_next = dm_dmcs2_get_dmexttrigger + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcs2_get_grouptype(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "grouptype", + .lsb = 0xb, + .msb = 0xb, + .values = dm_dmcs2_grouptype_values + }, + .get_next = dm_dmcs2_get_group + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcs2_get_hgwrite(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hgwrite", + .lsb = 1, + .msb = 1, + .values = NULL + }, + .get_next = dm_dmcs2_get_grouptype + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcs2_get_hgselect(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hgselect", + .lsb = 0, + .msb = 0, + .values = dm_dmcs2_hgselect_values + }, + .get_next = dm_dmcs2_get_hgwrite + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_haltsum0_get_haltsum0(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "haltsum0", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_haltsum1_get_haltsum1(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "haltsum1", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_haltsum2_get_haltsum2(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "haltsum2", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_haltsum3_get_haltsum3(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "haltsum3", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static const char *dm_sbcs_sbversion_values[8] = { + [0] = "legacy", + [1] = "1.0" +}; +static const char *dm_sbcs_sbaccess_values[8] = { + [0] = "8bit", + [1] = "16bit", + [2] = "32bit", + [3] = "64bit", + [4] = "128bit" +}; +static const char *dm_sbcs_sberror_values[8] = { + [0] = "none", + [1] = "timeout", + [2] = "address", + [3] = "alignment", + [4] = "size", + [7] = "other" +}; +static riscv_debug_reg_field_list_t dm_sbcs_get_sbasize(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbasize", + .lsb = 5, + .msb = 0xb, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbcs_get_sbaccess128(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbaccess128", + .lsb = 4, + .msb = 4, + .values = NULL + }, + .get_next = dm_sbcs_get_sbasize + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbcs_get_sbaccess64(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbaccess64", + .lsb = 3, + .msb = 3, + .values = NULL + }, + .get_next = dm_sbcs_get_sbaccess128 + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbcs_get_sbversion(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbversion", + .lsb = 0x1d, + .msb = 0x1f, + .values = dm_sbcs_sbversion_values + }, + .get_next = dm_sbcs_get_sbaccess64 + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbcs_get_sbbusyerror(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbbusyerror", + .lsb = 0x16, + .msb = 0x16, + .values = NULL + }, + .get_next = dm_sbcs_get_sbversion + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbcs_get_sbbusy(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbbusy", + .lsb = 0x15, + .msb = 0x15, + .values = NULL + }, + .get_next = dm_sbcs_get_sbbusyerror + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbcs_get_sbreadonaddr(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbreadonaddr", + .lsb = 0x14, + .msb = 0x14, + .values = NULL + }, + .get_next = dm_sbcs_get_sbbusy + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbcs_get_sbaccess32(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbaccess32", + .lsb = 2, + .msb = 2, + .values = NULL + }, + .get_next = dm_sbcs_get_sbreadonaddr + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbcs_get_sbaccess(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbaccess", + .lsb = 0x11, + .msb = 0x13, + .values = dm_sbcs_sbaccess_values + }, + .get_next = dm_sbcs_get_sbaccess32 + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbcs_get_sbautoincrement(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbautoincrement", + .lsb = 0x10, + .msb = 0x10, + .values = NULL + }, + .get_next = dm_sbcs_get_sbaccess + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbcs_get_sbreadondata(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbreadondata", + .lsb = 0xf, + .msb = 0xf, + .values = NULL + }, + .get_next = dm_sbcs_get_sbautoincrement + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbcs_get_sberror(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sberror", + .lsb = 0xc, + .msb = 0xe, + .values = dm_sbcs_sberror_values + }, + .get_next = dm_sbcs_get_sbreadondata + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbcs_get_sbaccess16(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbaccess16", + .lsb = 1, + .msb = 1, + .values = NULL + }, + .get_next = dm_sbcs_get_sberror + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbcs_get_sbaccess8(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbaccess8", + .lsb = 0, + .msb = 0, + .values = NULL + }, + .get_next = dm_sbcs_get_sbaccess16 + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbaddress0_get_address(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "address", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbaddress1_get_address(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "address", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbaddress2_get_address(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "address", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbaddress3_get_address(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "address", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbdata0_get_data(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "data", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbdata1_get_data(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "data", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbdata2_get_data(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "data", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbdata3_get_data(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "data", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t shortname_get_field(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "field", + .lsb = 0, + .msb = 7, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static const char *ac_access_register_aarsize_values[8] = { + [2] = "32bit", + [3] = "64bit", + [4] = "128bit" +}; +static const char *ac_access_register_aarpostincrement_values[2] = { + [0] = "disabled", + [1] = "enabled" +}; +static const char *ac_access_register_postexec_values[2] = { + [0] = "disabled", + [1] = "enabled" +}; +static const char *ac_access_register_transfer_values[2] = { + [0] = "disabled", + [1] = "enabled" +}; +static const char *ac_access_register_write_values[2] = { + [0] = "arg0", + [1] = "register" +}; +static riscv_debug_reg_field_list_t ac_access_register_get_cmdtype(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "cmdtype", + .lsb = 0x18, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t ac_access_register_get_aarsize(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "aarsize", + .lsb = 0x14, + .msb = 0x16, + .values = ac_access_register_aarsize_values + }, + .get_next = ac_access_register_get_cmdtype + }; + return result; +} + +static riscv_debug_reg_field_list_t ac_access_register_get_aarpostincrement(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "aarpostincrement", + .lsb = 0x13, + .msb = 0x13, + .values = ac_access_register_aarpostincrement_values + }, + .get_next = ac_access_register_get_aarsize + }; + return result; +} + +static riscv_debug_reg_field_list_t ac_access_register_get_postexec(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "postexec", + .lsb = 0x12, + .msb = 0x12, + .values = ac_access_register_postexec_values + }, + .get_next = ac_access_register_get_aarpostincrement + }; + return result; +} + +static riscv_debug_reg_field_list_t ac_access_register_get_transfer(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "transfer", + .lsb = 0x11, + .msb = 0x11, + .values = ac_access_register_transfer_values + }, + .get_next = ac_access_register_get_postexec + }; + return result; +} + +static riscv_debug_reg_field_list_t ac_access_register_get_write(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "write", + .lsb = 0x10, + .msb = 0x10, + .values = ac_access_register_write_values + }, + .get_next = ac_access_register_get_transfer + }; + return result; +} + +static riscv_debug_reg_field_list_t ac_access_register_get_regno(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "regno", + .lsb = 0, + .msb = 0xf, + .values = NULL + }, + .get_next = ac_access_register_get_write + }; + return result; +} + +static riscv_debug_reg_field_list_t ac_quick_access_get_cmdtype(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "cmdtype", + .lsb = 0x18, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static const char *ac_access_memory_aamvirtual_values[2] = { + [0] = "physical", + [1] = "virtual" +}; +static const char *ac_access_memory_aamsize_values[8] = { + [0] = "8bit", + [1] = "16bit", + [2] = "32bit", + [3] = "64bit", + [4] = "128bit" +}; +static const char *ac_access_memory_write_values[2] = { + [0] = "arg0", + [1] = "memory" +}; +static riscv_debug_reg_field_list_t ac_access_memory_get_cmdtype(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "cmdtype", + .lsb = 0x18, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t ac_access_memory_get_aamvirtual(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "aamvirtual", + .lsb = 0x17, + .msb = 0x17, + .values = ac_access_memory_aamvirtual_values + }, + .get_next = ac_access_memory_get_cmdtype + }; + return result; +} + +static riscv_debug_reg_field_list_t ac_access_memory_get_aamsize(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "aamsize", + .lsb = 0x14, + .msb = 0x16, + .values = ac_access_memory_aamsize_values + }, + .get_next = ac_access_memory_get_aamvirtual + }; + return result; +} + +static riscv_debug_reg_field_list_t ac_access_memory_get_aampostincrement(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "aampostincrement", + .lsb = 0x13, + .msb = 0x13, + .values = NULL + }, + .get_next = ac_access_memory_get_aamsize + }; + return result; +} + +static riscv_debug_reg_field_list_t ac_access_memory_get_write(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "write", + .lsb = 0x10, + .msb = 0x10, + .values = ac_access_memory_write_values + }, + .get_next = ac_access_memory_get_aampostincrement + }; + return result; +} + +static riscv_debug_reg_field_list_t ac_access_memory_get_target_specific(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "target-specific", + .lsb = 0xe, + .msb = 0xf, + .values = NULL + }, + .get_next = ac_access_memory_get_write + }; + return result; +} + +static riscv_debug_reg_field_list_t virt_priv_get_v(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "v", + .lsb = 2, + .msb = 2, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t virt_priv_get_prv(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "prv", + .lsb = 0, + .msb = 1, + .values = NULL + }, + .get_next = virt_priv_get_v + }; + return result; +} + +static riscv_debug_reg_field_list_t dmi_sercs_get_full3(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "full3", + .lsb = 9, + .msb = 9, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dmi_sercs_get_error2(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "error2", + .lsb = 8, + .msb = 8, + .values = NULL + }, + .get_next = dmi_sercs_get_full3 + }; + return result; +} + +static riscv_debug_reg_field_list_t dmi_sercs_get_valid2(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "valid2", + .lsb = 7, + .msb = 7, + .values = NULL + }, + .get_next = dmi_sercs_get_error2 + }; + return result; +} + +static riscv_debug_reg_field_list_t dmi_sercs_get_full2(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "full2", + .lsb = 6, + .msb = 6, + .values = NULL + }, + .get_next = dmi_sercs_get_valid2 + }; + return result; +} + +static riscv_debug_reg_field_list_t dmi_sercs_get_error1(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "error1", + .lsb = 5, + .msb = 5, + .values = NULL + }, + .get_next = dmi_sercs_get_full2 + }; + return result; +} + +static riscv_debug_reg_field_list_t dmi_sercs_get_valid1(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "valid1", + .lsb = 4, + .msb = 4, + .values = NULL + }, + .get_next = dmi_sercs_get_error1 + }; + return result; +} + +static riscv_debug_reg_field_list_t dmi_sercs_get_full1(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "full1", + .lsb = 3, + .msb = 3, + .values = NULL + }, + .get_next = dmi_sercs_get_valid1 + }; + return result; +} + +static riscv_debug_reg_field_list_t dmi_sercs_get_serialcount(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "serialcount", + .lsb = 0x1c, + .msb = 0x1f, + .values = NULL + }, + .get_next = dmi_sercs_get_full1 + }; + return result; +} + +static riscv_debug_reg_field_list_t dmi_sercs_get_serial(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "serial", + .lsb = 0x18, + .msb = 0x1a, + .values = NULL + }, + .get_next = dmi_sercs_get_serialcount + }; + return result; +} + +static riscv_debug_reg_field_list_t dmi_sercs_get_error7(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "error7", + .lsb = 0x17, + .msb = 0x17, + .values = NULL + }, + .get_next = dmi_sercs_get_serial + }; + return result; +} + +static riscv_debug_reg_field_list_t dmi_sercs_get_valid7(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "valid7", + .lsb = 0x16, + .msb = 0x16, + .values = NULL + }, + .get_next = dmi_sercs_get_error7 + }; + return result; +} + +static riscv_debug_reg_field_list_t dmi_sercs_get_full7(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "full7", + .lsb = 0x15, + .msb = 0x15, + .values = NULL + }, + .get_next = dmi_sercs_get_valid7 + }; + return result; +} + +static riscv_debug_reg_field_list_t dmi_sercs_get_error6(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "error6", + .lsb = 0x14, + .msb = 0x14, + .values = NULL + }, + .get_next = dmi_sercs_get_full7 + }; + return result; +} + +static riscv_debug_reg_field_list_t dmi_sercs_get_error0(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "error0", + .lsb = 2, + .msb = 2, + .values = NULL + }, + .get_next = dmi_sercs_get_error6 + }; + return result; +} + +static riscv_debug_reg_field_list_t dmi_sercs_get_valid6(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "valid6", + .lsb = 0x13, + .msb = 0x13, + .values = NULL + }, + .get_next = dmi_sercs_get_error0 + }; + return result; +} + +static riscv_debug_reg_field_list_t dmi_sercs_get_full6(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "full6", + .lsb = 0x12, + .msb = 0x12, + .values = NULL + }, + .get_next = dmi_sercs_get_valid6 + }; + return result; +} + +static riscv_debug_reg_field_list_t dmi_sercs_get_error5(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "error5", + .lsb = 0x11, + .msb = 0x11, + .values = NULL + }, + .get_next = dmi_sercs_get_full6 + }; + return result; +} + +static riscv_debug_reg_field_list_t dmi_sercs_get_valid5(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "valid5", + .lsb = 0x10, + .msb = 0x10, + .values = NULL + }, + .get_next = dmi_sercs_get_error5 + }; + return result; +} + +static riscv_debug_reg_field_list_t dmi_sercs_get_full5(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "full5", + .lsb = 0xf, + .msb = 0xf, + .values = NULL + }, + .get_next = dmi_sercs_get_valid5 + }; + return result; +} + +static riscv_debug_reg_field_list_t dmi_sercs_get_error4(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "error4", + .lsb = 0xe, + .msb = 0xe, + .values = NULL + }, + .get_next = dmi_sercs_get_full5 + }; + return result; +} + +static riscv_debug_reg_field_list_t dmi_sercs_get_valid4(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "valid4", + .lsb = 0xd, + .msb = 0xd, + .values = NULL + }, + .get_next = dmi_sercs_get_error4 + }; + return result; +} + +static riscv_debug_reg_field_list_t dmi_sercs_get_full4(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "full4", + .lsb = 0xc, + .msb = 0xc, + .values = NULL + }, + .get_next = dmi_sercs_get_valid4 + }; + return result; +} + +static riscv_debug_reg_field_list_t dmi_sercs_get_error3(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "error3", + .lsb = 0xb, + .msb = 0xb, + .values = NULL + }, + .get_next = dmi_sercs_get_full4 + }; + return result; +} + +static riscv_debug_reg_field_list_t dmi_sercs_get_valid3(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "valid3", + .lsb = 0xa, + .msb = 0xa, + .values = NULL + }, + .get_next = dmi_sercs_get_error3 + }; + return result; +} + +static riscv_debug_reg_field_list_t dmi_sercs_get_valid0(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "valid0", + .lsb = 1, + .msb = 1, + .values = NULL + }, + .get_next = dmi_sercs_get_valid3 + }; + return result; +} + +static riscv_debug_reg_field_list_t dmi_sercs_get_full0(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "full0", + .lsb = 0, + .msb = 0, + .values = NULL + }, + .get_next = dmi_sercs_get_valid0 + }; + return result; +} + +static riscv_debug_reg_field_list_t dmi_sertx_get_data(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "data", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dmi_serrx_get_data(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "data", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +riscv_debug_reg_info_t get_riscv_debug_reg_info(enum riscv_debug_reg_ordinal reg_ordinal) +{ + static const riscv_debug_reg_info_t debug_reg_info[] = { + [DTM_IDCODE_ORDINAL] = { + .name = "idcode", + .get_fields_head = dtm_idcode_get_1 + }, + [DTM_DTMCS_ORDINAL] = { + .name = "dtmcs", + .get_fields_head = dtm_dtmcs_get_version + }, + [DTM_DMI_ORDINAL] = { + .name = "dmi", + .get_fields_head = dtm_dmi_get_op + }, + [DTM_BYPASS_ORDINAL] = { + .name = "bypass", + .get_fields_head = NULL + }, + [CSR_DCSR_ORDINAL] = { + .name = "dcsr", + .get_fields_head = csr_dcsr_get_prv + }, + [CSR_DPC_ORDINAL] = { + .name = "dpc", + .get_fields_head = csr_dpc_get_dpc + }, + [CSR_TSELECT_ORDINAL] = { + .name = "tselect", + .get_fields_head = csr_tselect_get_index + }, + [CSR_TDATA1_ORDINAL] = { + .name = "tdata1", + .get_fields_head = csr_tdata1_get_data + }, + [CSR_TDATA2_ORDINAL] = { + .name = "tdata2", + .get_fields_head = csr_tdata2_get_data + }, + [CSR_TDATA3_ORDINAL] = { + .name = "tdata3", + .get_fields_head = csr_tdata3_get_data + }, + [CSR_TINFO_ORDINAL] = { + .name = "tinfo", + .get_fields_head = csr_tinfo_get_info + }, + [CSR_TCONTROL_ORDINAL] = { + .name = "tcontrol", + .get_fields_head = csr_tcontrol_get_mte + }, + [CSR_HCONTEXT_ORDINAL] = { + .name = "hcontext", + .get_fields_head = csr_hcontext_get_hcontext + }, + [CSR_SCONTEXT_ORDINAL] = { + .name = "scontext", + .get_fields_head = csr_scontext_get_data + }, + [CSR_MCONTROL_ORDINAL] = { + .name = "mcontrol", + .get_fields_head = csr_mcontrol_get_load + }, + [CSR_MCONTROL6_ORDINAL] = { + .name = "mcontrol6", + .get_fields_head = csr_mcontrol6_get_load + }, + [CSR_ICOUNT_ORDINAL] = { + .name = "icount", + .get_fields_head = csr_icount_get_action + }, + [CSR_ITRIGGER_ORDINAL] = { + .name = "itrigger", + .get_fields_head = csr_itrigger_get_action + }, + [CSR_ETRIGGER_ORDINAL] = { + .name = "etrigger", + .get_fields_head = csr_etrigger_get_action + }, + [CSR_TMEXTTRIGGER_ORDINAL] = { + .name = "tmexttrigger", + .get_fields_head = csr_tmexttrigger_get_action + }, + [CSR_TEXTRA32_ORDINAL] = { + .name = "textra32", + .get_fields_head = csr_textra32_get_sselect + }, + [CSR_TEXTRA64_ORDINAL] = { + .name = "textra64", + .get_fields_head = csr_textra64_get_sselect + }, + [DM_DMSTATUS_ORDINAL] = { + .name = "dmstatus", + .get_fields_head = dm_dmstatus_get_version + }, + [DM_DMCONTROL_ORDINAL] = { + .name = "dmcontrol", + .get_fields_head = dm_dmcontrol_get_dmactive + }, + [DM_HARTINFO_ORDINAL] = { + .name = "hartinfo", + .get_fields_head = dm_hartinfo_get_dataaddr + }, + [DM_HAWINDOWSEL_ORDINAL] = { + .name = "hawindowsel", + .get_fields_head = dm_hawindowsel_get_hawindowsel + }, + [DM_HAWINDOW_ORDINAL] = { + .name = "hawindow", + .get_fields_head = dm_hawindow_get_maskdata + }, + [DM_ABSTRACTCS_ORDINAL] = { + .name = "abstractcs", + .get_fields_head = dm_abstractcs_get_datacount + }, + [DM_COMMAND_ORDINAL] = { + .name = "command", + .get_fields_head = dm_command_get_control + }, + [DM_ABSTRACTAUTO_ORDINAL] = { + .name = "abstractauto", + .get_fields_head = dm_abstractauto_get_autoexecdata + }, + [DM_CONFSTRPTR0_ORDINAL] = { + .name = "confstrptr0", + .get_fields_head = dm_confstrptr0_get_addr + }, + [DM_CONFSTRPTR1_ORDINAL] = { + .name = "confstrptr1", + .get_fields_head = dm_confstrptr1_get_addr + }, + [DM_CONFSTRPTR2_ORDINAL] = { + .name = "confstrptr2", + .get_fields_head = dm_confstrptr2_get_addr + }, + [DM_CONFSTRPTR3_ORDINAL] = { + .name = "confstrptr3", + .get_fields_head = dm_confstrptr3_get_addr + }, + [DM_NEXTDM_ORDINAL] = { + .name = "nextdm", + .get_fields_head = dm_nextdm_get_addr + }, + [DM_DATA0_ORDINAL] = { + .name = "data0", + .get_fields_head = dm_data0_get_data + }, + [DM_PROGBUF0_ORDINAL] = { + .name = "progbuf0", + .get_fields_head = dm_progbuf0_get_data + }, + [DM_AUTHDATA_ORDINAL] = { + .name = "authdata", + .get_fields_head = dm_authdata_get_data + }, + [DM_DMCS2_ORDINAL] = { + .name = "dmcs2", + .get_fields_head = dm_dmcs2_get_hgselect + }, + [DM_HALTSUM0_ORDINAL] = { + .name = "haltsum0", + .get_fields_head = dm_haltsum0_get_haltsum0 + }, + [DM_HALTSUM1_ORDINAL] = { + .name = "haltsum1", + .get_fields_head = dm_haltsum1_get_haltsum1 + }, + [DM_HALTSUM2_ORDINAL] = { + .name = "haltsum2", + .get_fields_head = dm_haltsum2_get_haltsum2 + }, + [DM_HALTSUM3_ORDINAL] = { + .name = "haltsum3", + .get_fields_head = dm_haltsum3_get_haltsum3 + }, + [DM_SBCS_ORDINAL] = { + .name = "sbcs", + .get_fields_head = dm_sbcs_get_sbaccess8 + }, + [DM_SBADDRESS0_ORDINAL] = { + .name = "sbaddress0", + .get_fields_head = dm_sbaddress0_get_address + }, + [DM_SBADDRESS1_ORDINAL] = { + .name = "sbaddress1", + .get_fields_head = dm_sbaddress1_get_address + }, + [DM_SBADDRESS2_ORDINAL] = { + .name = "sbaddress2", + .get_fields_head = dm_sbaddress2_get_address + }, + [DM_SBADDRESS3_ORDINAL] = { + .name = "sbaddress3", + .get_fields_head = dm_sbaddress3_get_address + }, + [DM_SBDATA0_ORDINAL] = { + .name = "sbdata0", + .get_fields_head = dm_sbdata0_get_data + }, + [DM_SBDATA1_ORDINAL] = { + .name = "sbdata1", + .get_fields_head = dm_sbdata1_get_data + }, + [DM_SBDATA2_ORDINAL] = { + .name = "sbdata2", + .get_fields_head = dm_sbdata2_get_data + }, + [DM_SBDATA3_ORDINAL] = { + .name = "sbdata3", + .get_fields_head = dm_sbdata3_get_data + }, + [SHORTNAME_ORDINAL] = { + .name = "shortname", + .get_fields_head = shortname_get_field + }, + [AC_ACCESS_REGISTER_ORDINAL] = { + .name = "access register", + .get_fields_head = ac_access_register_get_regno + }, + [AC_QUICK_ACCESS_ORDINAL] = { + .name = "quick access", + .get_fields_head = ac_quick_access_get_cmdtype + }, + [AC_ACCESS_MEMORY_ORDINAL] = { + .name = "access memory", + .get_fields_head = ac_access_memory_get_target_specific + }, + [VIRT_PRIV_ORDINAL] = { + .name = "priv", + .get_fields_head = virt_priv_get_prv + }, + [DMI_SERCS_ORDINAL] = { + .name = "sercs", + .get_fields_head = dmi_sercs_get_full0 + }, + [DMI_SERTX_ORDINAL] = { + .name = "sertx", + .get_fields_head = dmi_sertx_get_data + }, + [DMI_SERRX_ORDINAL] = { + .name = "serrx", + .get_fields_head = dmi_serrx_get_data + }, + }; + return debug_reg_info[reg_ordinal]; +} diff --git a/src/target/riscv/debug_defines.h b/src/target/riscv/debug_defines.h index 8113d47..a1dea5a 100644 --- a/src/target/riscv/debug_defines.h +++ b/src/target/riscv/debug_defines.h @@ -1,8 +1,10 @@ /* - * This file is auto-generated by running 'make debug_defines.h' in - * https://github.com/riscv/riscv-debug-spec/ (d749752) + * This file is auto-generated by running 'make debug_defines' in + * https://github.com/riscv/riscv-debug-spec/ (f546ddf) */ +#ifndef DEBUG_DEFINES_H +#define DEBUG_DEFINES_H #define DTM_IDCODE 0x01 /* * Identifies the release version of this part. @@ -31,6 +33,39 @@ #define DTM_IDCODE_1 1 #define DTM_DTMCS 0x10 /* + * This optional field may provide additional detail about an error + * that occurred when communicating with a DM. It is updated whenever + * \FdtmDmiOp is updated by the hardware or when 1 is written to + * \FdtmDtmcsDmireset. + */ +#define DTM_DTMCS_ERRINFO_OFFSET 0x12 +#define DTM_DTMCS_ERRINFO_LENGTH 3 +#define DTM_DTMCS_ERRINFO 0x1c0000 +/* + * not implemented: This field is not implemented. + */ +#define DTM_DTMCS_ERRINFO_NOT_IMPLEMENTED 0 +/* + * dmi error: There was an error between the DTM and DMI. + */ +#define DTM_DTMCS_ERRINFO_DMI_ERROR 1 +/* + * communication error: There was an error between the DMI and a DMI subordinate. + */ +#define DTM_DTMCS_ERRINFO_COMMUNICATION_ERROR 2 +/* + * device error: The DMI subordinate reported an error. + */ +#define DTM_DTMCS_ERRINFO_DEVICE_ERROR 3 +/* + * unknown: There is no error to report, or no further information available + * about the error. This is the reset value if the field is implemented. + */ +#define DTM_DTMCS_ERRINFO_UNKNOWN 4 +/* + * Other values are reserved for future use by this specification. + */ +/* * Writing 1 to this bit does a hard reset of the DTM, * causing the DTM to forget about any outstanding DMI transactions, and * returning all registers and internal state to their reset value. @@ -39,12 +74,12 @@ * complete (e.g. a reset condition caused an inflight DMI transaction to * be cancelled). */ -#define DTM_DTMCS_DMIHARDRESET_OFFSET 0x11 -#define DTM_DTMCS_DMIHARDRESET_LENGTH 1 -#define DTM_DTMCS_DMIHARDRESET 0x20000 +#define DTM_DTMCS_DTMHARDRESET_OFFSET 0x11 +#define DTM_DTMCS_DTMHARDRESET_LENGTH 1 +#define DTM_DTMCS_DTMHARDRESET 0x20000 /* - * Writing 1 to this bit clears the sticky error state, but does - * not affect outstanding DMI transactions. + * Writing 1 to this bit clears the sticky error state and resets + * \FdtmDtmcsErrinfo, but does not affect outstanding DMI transactions. */ #define DTM_DTMCS_DMIRESET_OFFSET 0x10 #define DTM_DTMCS_DMIRESET_LENGTH 1 @@ -98,10 +133,12 @@ /* * Address used for DMI access. In Update-DR this value is used * to access the DM over the DMI. + * \FdtmDmiOp defines what this register contains after every possible + * operation. */ #define DTM_DMI_ADDRESS_OFFSET 0x22 -#define DTM_DMI_ADDRESS_LENGTH(abits) abits -#define DTM_DMI_ADDRESS(abits) ((0x400000000ULL * (1ULL<<abits)) + -0x400000000ULL) +#define DTM_DMI_ADDRESS_LENGTH(abits) (abits) +#define DTM_DMI_ADDRESS(abits) ((0x400000000ULL * (1ULL << (abits))) + -0x400000000ULL) /* * The data to send to the DM over the DMI during Update-DR, and * the data returned from the DM as a result of the previous operation. @@ -122,14 +159,24 @@ * This operation should never result in a busy or error response. * The address and data reported in the following Capture-DR * are undefined. + * + * This operation leaves the values in \FdtmDmiAddress and \FdtmDmiData + * \unspecified. */ #define DTM_DMI_OP_NOP 0 /* - * read: Read from \FdmSbaddressZeroAddress. + * read: Read from \FdtmDmiAddress. + * + * When this operation succeeds, \FdtmDmiAddress contains the address + * that was read from, and \FdtmDmiData contains the value that was + * read. */ #define DTM_DMI_OP_READ 1 /* - * write: Write \FdmSbdataZeroData to \FdmSbaddressZeroAddress. + * write: Write \FdtmDmiData to \FdtmDmiAddress. + * + * This operation leaves the values in \FdtmDmiAddress and \FdtmDmiData + * \unspecified. */ #define DTM_DMI_OP_WRITE 2 /* @@ -150,10 +197,13 @@ * this access will be ignored. This status is sticky and can be * cleared by writing \FdtmDtmcsDmireset in \RdtmDtmcs. * - * This indicates that the DM itself responded with an error. + * This indicates that the DM itself or the DMI responded with an error. * There are no specified cases in which the DM would * respond with an error, and DMI is not required to support * returning errors. + * + * If a debugger sees this status, there might be additional + * information in \FdtmDtmcsErrinfo. */ #define DTM_DMI_OP_FAILED 2 /* @@ -259,11 +309,14 @@ #define CSR_DCSR_STEPIE_LENGTH 1 #define CSR_DCSR_STEPIE 0x800 /* - * interrupts disabled: Interrupts (including NMI) are disabled during single stepping. + * interrupts disabled: Interrupts (including NMI) are disabled during single stepping + * with \FcsrDcsrStep set. + * This value should be supported. */ #define CSR_DCSR_STEPIE_INTERRUPTS_DISABLED 0 /* - * interrupts enabled: Interrupts (including NMI) are enabled during single stepping. + * interrupts enabled: Interrupts (including NMI) are enabled during single stepping + * with \FcsrDcsrStep set. */ #define CSR_DCSR_STEPIE_INTERRUPTS_ENABLED 1 /* @@ -295,13 +348,16 @@ #define CSR_DCSR_STOPTIME_LENGTH 1 #define CSR_DCSR_STOPTIME 0x200 /* - * normal: Increment \Rtime as usual. + * normal: \Rtime continues to reflect \Rmtime. */ #define CSR_DCSR_STOPTIME_NORMAL 0 /* - * freeze: Don't increment \Rtime while in Debug Mode. If all harts - * have \FcsrDcsrStoptime=1 and are in Debug Mode then \Rmtime - * is also allowed to stop incrementing. + * freeze: \Rtime is frozen at the time that Debug Mode was entered. When + * leaving Debug Mode, \Rtime will reflect the latest + * value of \Rmtime again. + * + * While all harts have \FcsrDcsrStoptime=1 and are in Debug Mode, + * \Rmtime is allowed to stop incrementing. */ #define CSR_DCSR_STOPTIME_FREEZE 1 /* @@ -407,18 +463,18 @@ #define CSR_DCSR_PRV 3 #define CSR_DPC 0x7b1 #define CSR_DPC_DPC_OFFSET 0 -#define CSR_DPC_DPC_LENGTH(DXLEN) DXLEN -#define CSR_DPC_DPC(DXLEN) ((1ULL<<DXLEN) + -1) +#define CSR_DPC_DPC_LENGTH(DXLEN) (DXLEN) +#define CSR_DPC_DPC(DXLEN) ((1ULL << (DXLEN)) + -1) #define CSR_DSCRATCH0 0x7b2 #define CSR_DSCRATCH1 0x7b3 #define CSR_TSELECT 0x7a0 #define CSR_TSELECT_INDEX_OFFSET 0 -#define CSR_TSELECT_INDEX_LENGTH(XLEN) XLEN -#define CSR_TSELECT_INDEX(XLEN) ((1ULL<<XLEN) + -1) +#define CSR_TSELECT_INDEX_LENGTH(XLEN) (XLEN) +#define CSR_TSELECT_INDEX(XLEN) ((1ULL << (XLEN)) + -1) #define CSR_TDATA1 0x7a1 -#define CSR_TDATA1_TYPE_OFFSET(XLEN) (XLEN + -4) +#define CSR_TDATA1_TYPE_OFFSET(XLEN) ((XLEN) + -4) #define CSR_TDATA1_TYPE_LENGTH 4 -#define CSR_TDATA1_TYPE(XLEN) (0xf * (1ULL<<(XLEN + -4))) +#define CSR_TDATA1_TYPE(XLEN) (0xf * (1ULL << ((XLEN) + -4))) /* * none: There is no trigger at this \RcsrTselect. */ @@ -468,8 +524,9 @@ /* * disabled: This trigger is disabled. In this state, \RcsrTdataTwo and * \RcsrTdataThree can be written with any value that is supported for - * any of the types this trigger implements. The remaining bits in this - * register are ignored. + * any of the types this trigger implements. + * The remaining bits in this register, except for \FcsrTdataOneDmode, + * are ignored. */ #define CSR_TDATA1_TYPE_DISABLED 15 /* @@ -478,9 +535,9 @@ /* * If \FcsrTdataOneType is 0, then this bit is hard-wired to 0. */ -#define CSR_TDATA1_DMODE_OFFSET(XLEN) (XLEN + -5) +#define CSR_TDATA1_DMODE_OFFSET(XLEN) ((XLEN) + -5) #define CSR_TDATA1_DMODE_LENGTH 1 -#define CSR_TDATA1_DMODE(XLEN) (1ULL<<(XLEN + -5)) +#define CSR_TDATA1_DMODE(XLEN) (1ULL << ((XLEN) + -5)) /* * both: Both Debug and M-mode can write the {\tt tdata} registers at the * selected \RcsrTselect. @@ -505,18 +562,43 @@ * Trigger-specific data. */ #define CSR_TDATA1_DATA_OFFSET 0 -#define CSR_TDATA1_DATA_LENGTH(XLEN) (XLEN + -5) -#define CSR_TDATA1_DATA(XLEN) ((1ULL<<(XLEN + -5)) + -1) +#define CSR_TDATA1_DATA_LENGTH(XLEN) ((XLEN) + -5) +#define CSR_TDATA1_DATA(XLEN) ((1ULL << ((XLEN) + -5)) + -1) #define CSR_TDATA2 0x7a2 #define CSR_TDATA2_DATA_OFFSET 0 -#define CSR_TDATA2_DATA_LENGTH(XLEN) XLEN -#define CSR_TDATA2_DATA(XLEN) ((1ULL<<XLEN) + -1) +#define CSR_TDATA2_DATA_LENGTH(XLEN) (XLEN) +#define CSR_TDATA2_DATA(XLEN) ((1ULL << (XLEN)) + -1) #define CSR_TDATA3 0x7a3 #define CSR_TDATA3_DATA_OFFSET 0 -#define CSR_TDATA3_DATA_LENGTH(XLEN) XLEN -#define CSR_TDATA3_DATA(XLEN) ((1ULL<<XLEN) + -1) +#define CSR_TDATA3_DATA_LENGTH(XLEN) (XLEN) +#define CSR_TDATA3_DATA(XLEN) ((1ULL << (XLEN)) + -1) #define CSR_TINFO 0x7a4 /* + * Contains the version of the Sdtrig extension implemented. + */ +#define CSR_TINFO_VERSION_OFFSET 0x18 +#define CSR_TINFO_VERSION_LENGTH 8 +#define CSR_TINFO_VERSION 0xff000000U +/* + * 0: Supports triggers as described in this spec at commit 5a5c078, + * made on February 2, 2023. + * + * \begin{steps}{In these older versions:} + * \item \RcsrMcontrolSix has a timing bit identical to + * \FcsrMcontrolTiming + * \item \FcsrMcontrolSixHitZero behaves just as \FcsrMcontrolHit. + * \item \FcsrMcontrolSixHitOne is read-only 0. + * \item Encodings for \FcsrMcontrolSixSize for access sizes larger + * than 64 bits are different. + * \end{steps} + */ +#define CSR_TINFO_VERSION_0 0 +/* + * 1: Supports triggers as described in the ratified version 1.0 of + * this document. + */ +#define CSR_TINFO_VERSION_1 1 +/* * One bit for each possible \FcsrTdataOneType enumerated in \RcsrTdataOne. Bit N * corresponds to type N. If the bit is set, then that type is * supported by the currently selected trigger. @@ -573,8 +655,8 @@ * and 14 on RV64. */ #define CSR_HCONTEXT_HCONTEXT_OFFSET 0 -#define CSR_HCONTEXT_HCONTEXT_LENGTH(XLEN) XLEN -#define CSR_HCONTEXT_HCONTEXT(XLEN) ((1ULL<<XLEN) + -1) +#define CSR_HCONTEXT_HCONTEXT_LENGTH(XLEN) (XLEN) +#define CSR_HCONTEXT_HCONTEXT(XLEN) ((1ULL << (XLEN)) + -1) #define CSR_SCONTEXT 0x5a8 /* * Supervisor mode software can write a context number to this @@ -586,17 +668,17 @@ * 34 on RV64. */ #define CSR_SCONTEXT_DATA_OFFSET 0 -#define CSR_SCONTEXT_DATA_LENGTH(XLEN) XLEN -#define CSR_SCONTEXT_DATA(XLEN) ((1ULL<<XLEN) + -1) +#define CSR_SCONTEXT_DATA_LENGTH(XLEN) (XLEN) +#define CSR_SCONTEXT_DATA(XLEN) ((1ULL << (XLEN)) + -1) #define CSR_MCONTEXT 0x7a8 #define CSR_MSCONTEXT 0x7aa #define CSR_MCONTROL 0x7a1 -#define CSR_MCONTROL_TYPE_OFFSET(XLEN) (XLEN + -4) +#define CSR_MCONTROL_TYPE_OFFSET(XLEN) ((XLEN) + -4) #define CSR_MCONTROL_TYPE_LENGTH 4 -#define CSR_MCONTROL_TYPE(XLEN) (0xf * (1ULL<<(XLEN + -4))) -#define CSR_MCONTROL_DMODE_OFFSET(XLEN) (XLEN + -5) +#define CSR_MCONTROL_TYPE(XLEN) (0xf * (1ULL << ((XLEN) + -4))) +#define CSR_MCONTROL_DMODE_OFFSET(XLEN) ((XLEN) + -5) #define CSR_MCONTROL_DMODE_LENGTH 1 -#define CSR_MCONTROL_DMODE(XLEN) (1ULL<<(XLEN + -5)) +#define CSR_MCONTROL_DMODE(XLEN) (1ULL << ((XLEN) + -5)) /* * Specifies the largest naturally aligned powers-of-two (NAPOT) range * supported by the hardware when \FcsrMcontrolMatch is 1. The value is the @@ -605,9 +687,9 @@ * A value of 63 corresponds to the maximum NAPOT range, which is * $2^{63}$ bytes in size. */ -#define CSR_MCONTROL_MASKMAX_OFFSET(XLEN) (XLEN + -0xb) +#define CSR_MCONTROL_MASKMAX_OFFSET(XLEN) ((XLEN) + -0xb) #define CSR_MCONTROL_MASKMAX_LENGTH 6 -#define CSR_MCONTROL_MASKMAX(XLEN) (0x3f * (1ULL<<(XLEN + -0xb))) +#define CSR_MCONTROL_MASKMAX(XLEN) (0x3f * (1ULL << ((XLEN) + -0xb))) /* * This field only exists when XLEN is at least 64. * It contains the 2 high bits of the access size. The low bits @@ -654,8 +736,8 @@ #define CSR_MCONTROL_TIMING 0x40000 /* * before: The action for this trigger will be taken just before the - * instruction that triggered it is committed, but after all preceding - * instructions are committed. \Rxepc or \RcsrDpc (depending + * instruction that triggered it is retired, but after all preceding + * instructions are retired. \Rxepc or \RcsrDpc (depending * on \FcsrMcontrolAction) must be set to the virtual address of the * instruction that matched. * @@ -669,8 +751,8 @@ #define CSR_MCONTROL_TIMING_BEFORE 0 /* * after: The action for this trigger will be taken after the instruction - * that triggered it is committed. It should be taken before the next - * instruction is committed, but it is better to implement triggers imprecisely + * that triggered it is retired. It should be taken before the next + * instruction is retired, but it is better to implement triggers imprecisely * than to not implement them at all. \Rxepc or * \RcsrDpc (depending on \FcsrMcontrolAction) must be set to * the virtual address of the next instruction that must be executed to @@ -946,12 +1028,33 @@ #define CSR_MCONTROL_LOAD_LENGTH 1 #define CSR_MCONTROL_LOAD 1 #define CSR_MCONTROL6 0x7a1 -#define CSR_MCONTROL6_TYPE_OFFSET(XLEN) (XLEN + -4) +#define CSR_MCONTROL6_TYPE_OFFSET(XLEN) ((XLEN) + -4) #define CSR_MCONTROL6_TYPE_LENGTH 4 -#define CSR_MCONTROL6_TYPE(XLEN) (0xf * (1ULL<<(XLEN + -4))) -#define CSR_MCONTROL6_DMODE_OFFSET(XLEN) (XLEN + -5) +#define CSR_MCONTROL6_TYPE(XLEN) (0xf * (1ULL << ((XLEN) + -4))) +#define CSR_MCONTROL6_DMODE_OFFSET(XLEN) ((XLEN) + -5) #define CSR_MCONTROL6_DMODE_LENGTH 1 -#define CSR_MCONTROL6_DMODE(XLEN) (1ULL<<(XLEN + -5)) +#define CSR_MCONTROL6_DMODE(XLEN) (1ULL << ((XLEN) + -5)) +/* + * This bit should be updated every time that \FcsrMcontrolSixHitZero + * or \FcsrMcontrolSixHitOne is updated. + */ +#define CSR_MCONTROL6_UNCERTAIN_OFFSET 0x1a +#define CSR_MCONTROL6_UNCERTAIN_LENGTH 1 +#define CSR_MCONTROL6_UNCERTAIN 0x4000000 +/* + * certain: The trigger that fired satisfied the configured conditions, or + * this bit is not implemented. + */ +#define CSR_MCONTROL6_UNCERTAIN_CERTAIN 0 +/* + * uncertain: The trigger that fired might not have perfectly satisfied the + * configured conditions. Due to the implementation the hardware + * cannot be certain. + */ +#define CSR_MCONTROL6_UNCERTAIN_UNCERTAIN 1 +#define CSR_MCONTROL6_HIT1_OFFSET 0x19 +#define CSR_MCONTROL6_HIT1_LENGTH 1 +#define CSR_MCONTROL6_HIT1 0x2000000 /* * When set, enable this trigger in VS-mode. * This bit is hard-wired to 0 if the hart does not support @@ -969,16 +1072,50 @@ #define CSR_MCONTROL6_VU_LENGTH 1 #define CSR_MCONTROL6_VU 0x800000 /* - * If this bit is implemented then it must become set when this - * trigger fires and may become set when this trigger matches. - * The trigger's user can set or clear it at any - * time. It is used to determine which - * trigger(s) matched. If the bit is not implemented, it is always 0 - * and writing it has no effect. + * If they are implemented, \FcsrMcontrolSixHitOne (MSB) and + * \FcsrMcontrolSixHitZero (LSB) combine into a single 2-bit field. + * The TM updates this field when the trigger fires. After the debugger + * has seen the update, it will normally write 0 to this field to so it + * can see future changes. + * + * If either of the bits is not implemented, the unimplemented bits + * will be read-only 0. */ -#define CSR_MCONTROL6_HIT_OFFSET 0x16 -#define CSR_MCONTROL6_HIT_LENGTH 1 -#define CSR_MCONTROL6_HIT 0x400000 +#define CSR_MCONTROL6_HIT0_OFFSET 0x16 +#define CSR_MCONTROL6_HIT0_LENGTH 1 +#define CSR_MCONTROL6_HIT0 0x400000 +/* + * false: The trigger did not fire. + */ +#define CSR_MCONTROL6_HIT0_FALSE 0 +/* + * before: The trigger fired just before the instruction that triggered it was + * retired, but after all preceding instructions are retired. + * \Rxepc or \RcsrDpc (depending on \FcsrMcontrolSixAction) must be set + * to the virtual address of the instruction that matched. + * + * If a load operation matched and \FcsrMcontrolSixSelect=1 then a + * memory access has been performed (including any side effects of + * performing such an access) even though the load has not updated its + * destination register. + */ +#define CSR_MCONTROL6_HIT0_BEFORE 1 +/* + * after: The trigger fired after the instruction that triggered and at least + * one additional instruction were retired. + * \Rxepc or \RcsrDpc (depending on \FcsrMcontrolSixAction) must be set + * to the virtual address of the next instruction that must be executed + * to preserve the program flow. + */ +#define CSR_MCONTROL6_HIT0_AFTER 2 +/* + * immediately after: The trigger fired just after the instruction that triggered it was + * retired, but before any subsequent instructions were executed. + * \Rxepc or \RcsrDpc (depending on \FcsrMcontrolSixAction) must be set + * to the virtual address of the next instruction that must be executed + * to preserve the program flow. + */ +#define CSR_MCONTROL6_HIT0_IMMEDIATELY_AFTER 3 /* * This bit determines the contents of the XLEN-bit compare values. */ @@ -1000,54 +1137,9 @@ * Any bits beyond the size of the data access will contain 0. */ #define CSR_MCONTROL6_SELECT_DATA 1 -#define CSR_MCONTROL6_TIMING_OFFSET 0x14 -#define CSR_MCONTROL6_TIMING_LENGTH 1 -#define CSR_MCONTROL6_TIMING 0x100000 -/* - * before: The action for this trigger will be taken just before the - * instruction that triggered it is committed, but after all preceding - * instructions are committed. \Rxepc or \RcsrDpc (depending - * on \FcsrMcontrolSixAction) must be set to the virtual address of the - * instruction that matched. - * - * If this is combined with \FcsrMcontrolSixLoad and - * \FcsrMcontrolSixSelect=1 then a memory access will be - * performed (including any side effects of performing such an access) even - * though the load will not update its destination register. Debuggers - * should consider this when setting such breakpoints on, for example, - * memory-mapped I/O addresses. - */ -#define CSR_MCONTROL6_TIMING_BEFORE 0 -/* - * after: The action for this trigger will be taken after the instruction - * that triggered it is committed. It should be taken before the next - * instruction is committed, but it is better to implement triggers imprecisely - * than to not implement them at all. \Rxepc or - * \RcsrDpc (depending on \FcsrMcontrolSixAction) must be set to - * the virtual address of the next instruction that must be executed to - * preserve the program flow. - */ -#define CSR_MCONTROL6_TIMING_AFTER 1 -/* - * Most hardware will only implement one timing or the other, possibly - * dependent on \FcsrMcontrolSixSelect, \FcsrMcontrolSixExecute, - * \FcsrMcontrolSixLoad, and \FcsrMcontrolSixStore. This bit - * primarily exists for the hardware to communicate to the debugger - * what will happen. Hardware may implement the bit fully writable, in - * which case the debugger has a little more control. - * - * Data load triggers with \FcsrMcontrolSixTiming of 0 will result in the same load - * happening again when the debugger lets the hart run. For data load - * triggers, debuggers must first attempt to set the breakpoint with - * \FcsrMcontrolSixTiming of 1. - * - * If a trigger with \FcsrMcontrolSixTiming of 0 matches, it is - * implementation-dependent whether that prevents a trigger with - * \FcsrMcontrolSixTiming of 1 matching as well. - */ #define CSR_MCONTROL6_SIZE_OFFSET 0x10 -#define CSR_MCONTROL6_SIZE_LENGTH 4 -#define CSR_MCONTROL6_SIZE 0xf0000 +#define CSR_MCONTROL6_SIZE_LENGTH 3 +#define CSR_MCONTROL6_SIZE 0x70000 /* * any: The trigger will attempt to match against an access of any size. * The behavior is only well-defined if $|select|=0$, or if the access @@ -1078,22 +1170,10 @@ */ #define CSR_MCONTROL6_SIZE_64BIT 5 /* - * 80bit: The trigger will only match against execution of 80-bit instructions. - */ -#define CSR_MCONTROL6_SIZE_80BIT 6 -/* - * 96bit: The trigger will only match against execution of 96-bit instructions. - */ -#define CSR_MCONTROL6_SIZE_96BIT 7 -/* - * 112bit: The trigger will only match against execution of 112-bit instructions. - */ -#define CSR_MCONTROL6_SIZE_112BIT 8 -/* * 128bit: The trigger will only match against 128-bit memory accesses or * execution of 128-bit instructions. */ -#define CSR_MCONTROL6_SIZE_128BIT 9 +#define CSR_MCONTROL6_SIZE_128BIT 6 /* * An implementation must support the value of 0, but all other values * are optional. When an implementation supports address triggers @@ -1257,6 +1337,20 @@ #define CSR_MCONTROL6_M_OFFSET 6 #define CSR_MCONTROL6_M_LENGTH 1 #define CSR_MCONTROL6_M 0x40 +#define CSR_MCONTROL6_UNCERTAINEN_OFFSET 5 +#define CSR_MCONTROL6_UNCERTAINEN_LENGTH 1 +#define CSR_MCONTROL6_UNCERTAINEN 0x20 +/* + * disabled: This trigger will only match if the hardware can perfectly + * evaluate it. + */ +#define CSR_MCONTROL6_UNCERTAINEN_DISABLED 0 +/* + * enabled: This trigger will match if it's possible that it would match if + * the Trigger Module had perfect information about the operations + * being performed. + */ +#define CSR_MCONTROL6_UNCERTAINEN_ENABLED 1 /* * When set, enable this trigger in S/HS-mode. * This bit is hard-wired to 0 if the hart does not support @@ -1295,12 +1389,12 @@ #define CSR_MCONTROL6_LOAD_LENGTH 1 #define CSR_MCONTROL6_LOAD 1 #define CSR_ICOUNT 0x7a1 -#define CSR_ICOUNT_TYPE_OFFSET(XLEN) (XLEN + -4) +#define CSR_ICOUNT_TYPE_OFFSET(XLEN) ((XLEN) + -4) #define CSR_ICOUNT_TYPE_LENGTH 4 -#define CSR_ICOUNT_TYPE(XLEN) (0xf * (1ULL<<(XLEN + -4))) -#define CSR_ICOUNT_DMODE_OFFSET(XLEN) (XLEN + -5) +#define CSR_ICOUNT_TYPE(XLEN) (0xf * (1ULL << ((XLEN) + -4))) +#define CSR_ICOUNT_DMODE_OFFSET(XLEN) ((XLEN) + -5) #define CSR_ICOUNT_DMODE_LENGTH 1 -#define CSR_ICOUNT_DMODE(XLEN) (1ULL<<(XLEN + -5)) +#define CSR_ICOUNT_DMODE(XLEN) (1ULL << ((XLEN) + -5)) /* * When set, enable this trigger in VS-mode. * This bit is hard-wired to 0 if the hart does not support @@ -1400,12 +1494,12 @@ */ #define CSR_ICOUNT_ACTION_EXTERNAL1 9 #define CSR_ITRIGGER 0x7a1 -#define CSR_ITRIGGER_TYPE_OFFSET(XLEN) (XLEN + -4) +#define CSR_ITRIGGER_TYPE_OFFSET(XLEN) ((XLEN) + -4) #define CSR_ITRIGGER_TYPE_LENGTH 4 -#define CSR_ITRIGGER_TYPE(XLEN) (0xf * (1ULL<<(XLEN + -4))) -#define CSR_ITRIGGER_DMODE_OFFSET(XLEN) (XLEN + -5) +#define CSR_ITRIGGER_TYPE(XLEN) (0xf * (1ULL << ((XLEN) + -4))) +#define CSR_ITRIGGER_DMODE_OFFSET(XLEN) ((XLEN) + -5) #define CSR_ITRIGGER_DMODE_LENGTH 1 -#define CSR_ITRIGGER_DMODE(XLEN) (1ULL<<(XLEN + -5)) +#define CSR_ITRIGGER_DMODE(XLEN) (1ULL << ((XLEN) + -5)) /* * If this bit is implemented, the hardware sets it when this * trigger matches. The trigger's user can set or clear it at any @@ -1413,9 +1507,9 @@ * trigger(s) matched. If the bit is not implemented, it is always 0 * and writing it has no effect. */ -#define CSR_ITRIGGER_HIT_OFFSET(XLEN) (XLEN + -6) +#define CSR_ITRIGGER_HIT_OFFSET(XLEN) ((XLEN) + -6) #define CSR_ITRIGGER_HIT_LENGTH 1 -#define CSR_ITRIGGER_HIT(XLEN) (1ULL<<(XLEN + -6)) +#define CSR_ITRIGGER_HIT(XLEN) (1ULL << ((XLEN) + -6)) /* * When set, enable this trigger for interrupts that are taken from VS * mode. @@ -1502,12 +1596,12 @@ */ #define CSR_ITRIGGER_ACTION_EXTERNAL1 9 #define CSR_ETRIGGER 0x7a1 -#define CSR_ETRIGGER_TYPE_OFFSET(XLEN) (XLEN + -4) +#define CSR_ETRIGGER_TYPE_OFFSET(XLEN) ((XLEN) + -4) #define CSR_ETRIGGER_TYPE_LENGTH 4 -#define CSR_ETRIGGER_TYPE(XLEN) (0xf * (1ULL<<(XLEN + -4))) -#define CSR_ETRIGGER_DMODE_OFFSET(XLEN) (XLEN + -5) +#define CSR_ETRIGGER_TYPE(XLEN) (0xf * (1ULL << ((XLEN) + -4))) +#define CSR_ETRIGGER_DMODE_OFFSET(XLEN) ((XLEN) + -5) #define CSR_ETRIGGER_DMODE_LENGTH 1 -#define CSR_ETRIGGER_DMODE(XLEN) (1ULL<<(XLEN + -5)) +#define CSR_ETRIGGER_DMODE(XLEN) (1ULL << ((XLEN) + -5)) /* * If this bit is implemented, the hardware sets it when this * trigger matches. The trigger's user can set or clear it at any @@ -1515,9 +1609,9 @@ * trigger(s) matched. If the bit is not implemented, it is always 0 * and writing it has no effect. */ -#define CSR_ETRIGGER_HIT_OFFSET(XLEN) (XLEN + -6) +#define CSR_ETRIGGER_HIT_OFFSET(XLEN) ((XLEN) + -6) #define CSR_ETRIGGER_HIT_LENGTH 1 -#define CSR_ETRIGGER_HIT(XLEN) (1ULL<<(XLEN + -6)) +#define CSR_ETRIGGER_HIT(XLEN) (1ULL << ((XLEN) + -6)) /* * When set, enable this trigger for exceptions that are taken from VS * mode. @@ -1597,12 +1691,12 @@ */ #define CSR_ETRIGGER_ACTION_EXTERNAL1 9 #define CSR_TMEXTTRIGGER 0x7a1 -#define CSR_TMEXTTRIGGER_TYPE_OFFSET(XLEN) (XLEN + -4) +#define CSR_TMEXTTRIGGER_TYPE_OFFSET(XLEN) ((XLEN) + -4) #define CSR_TMEXTTRIGGER_TYPE_LENGTH 4 -#define CSR_TMEXTTRIGGER_TYPE(XLEN) (0xf * (1ULL<<(XLEN + -4))) -#define CSR_TMEXTTRIGGER_DMODE_OFFSET(XLEN) (XLEN + -5) +#define CSR_TMEXTTRIGGER_TYPE(XLEN) (0xf * (1ULL << ((XLEN) + -4))) +#define CSR_TMEXTTRIGGER_DMODE_OFFSET(XLEN) ((XLEN) + -5) #define CSR_TMEXTTRIGGER_DMODE_LENGTH 1 -#define CSR_TMEXTTRIGGER_DMODE(XLEN) (1ULL<<(XLEN + -5)) +#define CSR_TMEXTTRIGGER_DMODE(XLEN) (1ULL << ((XLEN) + -5)) /* * If this bit is implemented, the hardware sets it when this * trigger matches. The trigger's user can set or clear it at any @@ -1610,9 +1704,9 @@ * trigger(s) matched. If the bit is not implemented, it is always 0 * and writing it has no effect. */ -#define CSR_TMEXTTRIGGER_HIT_OFFSET(XLEN) (XLEN + -6) +#define CSR_TMEXTTRIGGER_HIT_OFFSET(XLEN) ((XLEN) + -6) #define CSR_TMEXTTRIGGER_HIT_LENGTH 1 -#define CSR_TMEXTTRIGGER_HIT(XLEN) (1ULL<<(XLEN + -6)) +#define CSR_TMEXTTRIGGER_HIT(XLEN) (1ULL << ((XLEN) + -6)) /* * This optional bit, when set, causes this trigger to fire whenever an attached * interrupt controller signals a trigger. @@ -1621,7 +1715,7 @@ #define CSR_TMEXTTRIGGER_INTCTL_LENGTH 1 #define CSR_TMEXTTRIGGER_INTCTL 0x400000 /* - * Selects any combination of up to 16 external debug trigger inputs + * Selects any combination of up to 16 TM external trigger inputs * that cause this trigger to fire. */ #define CSR_TMEXTTRIGGER_SELECT_OFFSET 6 @@ -1677,15 +1771,17 @@ */ #define CSR_TEXTRA32_MHSELECT_IGNORE 0 /* - * mcontext: This trigger will only match if the low bits of + * mcontext: This trigger will only match or fire if the low bits of * \RcsrMcontext/\RcsrHcontext equal \FcsrTextraThirtytwoMhvalue. */ #define CSR_TEXTRA32_MHSELECT_MCONTEXT 4 /* - * 1, 5 (mcontext\_select): This trigger will only match if the low bits of + * 1, 5 (mcontext\_select): This trigger will only match or fire if the + * low bits of * \RcsrMcontext/\RcsrHcontext equal \{\FcsrTextraThirtytwoMhvalue, mhselect[2]\}. * - * 2, 6 (vmid\_select): This trigger will only match if VMID in hgatp equals the lower VMIDMAX + * 2, 6 (vmid\_select): This trigger will only match or fire if VMID in + * hgatp equals the lower VMIDMAX * (defined in the Privileged Spec) bits of \{\FcsrTextraThirtytwoMhvalue, mhselect[2]\}. * * 3, 7 (reserved): Reserved. @@ -1717,12 +1813,12 @@ */ #define CSR_TEXTRA32_SSELECT_IGNORE 0 /* - * scontext: This trigger will only match if the low bits of + * scontext: This trigger will only match or fire if the low bits of * \RcsrScontext equal \FcsrTextraThirtytwoSvalue. */ #define CSR_TEXTRA32_SSELECT_SCONTEXT 1 /* - * asid: This trigger will only match if: + * asid: This trigger will only match or fire if: * \begin{itemize}[noitemsep,nolistsep] * \item the mode is VS-mode or VU-mode and ASID in \Rvsatp * equals the lower ASIDMAX (defined in the Privileged Spec) bits @@ -2187,9 +2283,8 @@ * If \FdmHartinfoDataaccess is 1: Number of 32-bit words in the memory map * dedicated to shadowing the {\tt data} registers. * - * If this value is non-zero, then the {tt data} registers must go - * beyond being MRs and guarantee they each store a single value, that is - * readable/writable by either side. + * If this value is non-zero, then the {tt data} registers must be + * traditional registers and not MRs. * * Since there are at most 12 {\tt data} registers, the value in this * register must be 12 or smaller. @@ -2250,13 +2345,20 @@ * permission checks that apply based on the current architectural * state of the hart performing the access, or with a relaxed set of * permission checks (e.g. PMP restrictions are ignored). The - * details of the latter are implementation-specific. When set to 0, - * full permissions apply; when set to 1, relaxed permissions apply. + * details of the latter are implementation-specific. */ #define DM_ABSTRACTCS_RELAXEDPRIV_OFFSET 0xb #define DM_ABSTRACTCS_RELAXEDPRIV_LENGTH 1 #define DM_ABSTRACTCS_RELAXEDPRIV 0x800 /* + * full checks: Full permission checks apply. + */ +#define DM_ABSTRACTCS_RELAXEDPRIV_FULL_CHECKS 0 +/* + * relaxed checks: Relaxed permission checks apply. + */ +#define DM_ABSTRACTCS_RELAXEDPRIV_RELAXED_CHECKS 1 +/* * Gets set if an abstract command fails. The bits in this field remain set until * they are cleared by writing 1 to them. No abstract command is * started until the value is reset to 0. @@ -2524,7 +2626,7 @@ #define DM_SBCS_SBBUSYERROR_LENGTH 1 #define DM_SBCS_SBBUSYERROR 0x400000 /* - * When 1, indicates the system bus master is busy. (Whether the + * When 1, indicates the system bus manager is busy. (Whether the * system bus itself is busy is related, but not the same thing.) This * bit goes high immediately when a read or write is requested for any * reason, and does not go low until the access is fully completed. @@ -2589,7 +2691,7 @@ #define DM_SBCS_SBREADONDATA 0x8000 /* * When the Debug Module's system bus - * master encounters an error, this field gets set. The bits in this + * manager encounters an error, this field gets set. The bits in this * field remain set until they are cleared by writing 1 to them. * While this field is non-zero, no more system bus accesses can be * initiated by the Debug Module. @@ -2908,6 +3010,10 @@ * {\tt arg1} (which contains the address used) by the number of bytes * encoded in \FacAccessmemoryAamsize. * + * Implementations that allow this bit to be 1 must implement the + * relevant {\tt data} registers as traditional registers instead of + * MRs. + * * Supporting this variant is optional, but highly recommended for * performance reasons. */ @@ -3059,3 +3165,93 @@ #define DMI_SERRX_DATA_OFFSET 0 #define DMI_SERRX_DATA_LENGTH 0x20 #define DMI_SERRX_DATA 0xffffffffU +enum riscv_debug_reg_ordinal { + DTM_IDCODE_ORDINAL, + DTM_DTMCS_ORDINAL, + DTM_DMI_ORDINAL, + DTM_BYPASS_ORDINAL, + CSR_DCSR_ORDINAL, + CSR_DPC_ORDINAL, + CSR_TSELECT_ORDINAL, + CSR_TDATA1_ORDINAL, + CSR_TDATA2_ORDINAL, + CSR_TDATA3_ORDINAL, + CSR_TINFO_ORDINAL, + CSR_TCONTROL_ORDINAL, + CSR_HCONTEXT_ORDINAL, + CSR_SCONTEXT_ORDINAL, + CSR_MCONTROL_ORDINAL, + CSR_MCONTROL6_ORDINAL, + CSR_ICOUNT_ORDINAL, + CSR_ITRIGGER_ORDINAL, + CSR_ETRIGGER_ORDINAL, + CSR_TMEXTTRIGGER_ORDINAL, + CSR_TEXTRA32_ORDINAL, + CSR_TEXTRA64_ORDINAL, + DM_DMSTATUS_ORDINAL, + DM_DMCONTROL_ORDINAL, + DM_HARTINFO_ORDINAL, + DM_HAWINDOWSEL_ORDINAL, + DM_HAWINDOW_ORDINAL, + DM_ABSTRACTCS_ORDINAL, + DM_COMMAND_ORDINAL, + DM_ABSTRACTAUTO_ORDINAL, + DM_CONFSTRPTR0_ORDINAL, + DM_CONFSTRPTR1_ORDINAL, + DM_CONFSTRPTR2_ORDINAL, + DM_CONFSTRPTR3_ORDINAL, + DM_NEXTDM_ORDINAL, + DM_DATA0_ORDINAL, + DM_PROGBUF0_ORDINAL, + DM_AUTHDATA_ORDINAL, + DM_DMCS2_ORDINAL, + DM_HALTSUM0_ORDINAL, + DM_HALTSUM1_ORDINAL, + DM_HALTSUM2_ORDINAL, + DM_HALTSUM3_ORDINAL, + DM_SBCS_ORDINAL, + DM_SBADDRESS0_ORDINAL, + DM_SBADDRESS1_ORDINAL, + DM_SBADDRESS2_ORDINAL, + DM_SBADDRESS3_ORDINAL, + DM_SBDATA0_ORDINAL, + DM_SBDATA1_ORDINAL, + DM_SBDATA2_ORDINAL, + DM_SBDATA3_ORDINAL, + SHORTNAME_ORDINAL, + AC_ACCESS_REGISTER_ORDINAL, + AC_QUICK_ACCESS_ORDINAL, + AC_ACCESS_MEMORY_ORDINAL, + VIRT_PRIV_ORDINAL, + DMI_SERCS_ORDINAL, + DMI_SERTX_ORDINAL, + DMI_SERRX_ORDINAL +}; +typedef struct { + struct { + unsigned int value; int is_set; + } DXLEN; + struct { + unsigned int value; int is_set; + } XLEN; + struct { + unsigned int value; int is_set; + } abits; +} riscv_debug_reg_ctx_t; + +typedef struct { + const char *name; + unsigned int lsb; // inclusive + unsigned int msb; // inclusive + const char **values; // If non-NULL, array of human-readable string for each possible value +} riscv_debug_reg_field_info_t; +typedef struct riscv_debug_reg_field_list_t { + riscv_debug_reg_field_info_t field; + struct riscv_debug_reg_field_list_t (*get_next)(riscv_debug_reg_ctx_t context); +} riscv_debug_reg_field_list_t; +typedef struct { + const char *name; + struct riscv_debug_reg_field_list_t (* const get_fields_head)(riscv_debug_reg_ctx_t context); +} riscv_debug_reg_info_t; +riscv_debug_reg_info_t get_riscv_debug_reg_info(enum riscv_debug_reg_ordinal reg_ordinal); +#endif diff --git a/src/target/riscv/debug_reg_printer.c b/src/target/riscv/debug_reg_printer.c new file mode 100644 index 0000000..968c983 --- /dev/null +++ b/src/target/riscv/debug_reg_printer.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <stdio.h> +#include <inttypes.h> +#include <assert.h> +#include <stdarg.h> + +#include "debug_reg_printer.h" + +static unsigned int get_len_or_sprintf(char *buf, unsigned int curr, const char *format, ...) +{ + assert(format); + va_list args; + int length; + + va_start(args, format); + if (buf) + length = vsprintf(buf + curr, format, args); + else + length = vsnprintf(NULL, 0, format, args); + va_end(args); + assert(length >= 0); + return (unsigned int)length; +} + +static unsigned int print_number(char *buf, unsigned int offset, uint64_t value) +{ + const char * const format = value > 9 ? "0x%" PRIx64 : "%" PRIx64; + + return get_len_or_sprintf(buf, offset, format, value); +} + +static unsigned int riscv_debug_reg_field_value_to_s(char *buf, unsigned int offset, + const char * const *field_value_names, uint64_t field_value) +{ + const char * const field_value_name = field_value_names ? + field_value_names[field_value] : + NULL; + + if (!field_value_name) + return print_number(buf, offset, field_value); + return get_len_or_sprintf(buf, offset, "%s", field_value_name); +} + +static unsigned int riscv_debug_reg_field_to_s(char *buf, unsigned int offset, + riscv_debug_reg_field_info_t field, riscv_debug_reg_ctx_t context, + uint64_t field_value) +{ + const unsigned int name_len = get_len_or_sprintf(buf, offset, "%s=", field.name); + + return name_len + riscv_debug_reg_field_value_to_s(buf, offset + name_len, + field.values, field_value); +} + +static uint64_t riscv_debug_reg_field_value(riscv_debug_reg_field_info_t field, uint64_t value) +{ + assert(field.msb < 64); + assert(field.msb >= field.lsb); + const uint64_t trailing_ones_mask = (uint64_t)(-1) >> (63 - field.msb); + return (value & trailing_ones_mask) >> field.lsb; +} + +static unsigned int riscv_debug_reg_fields_to_s(char *buf, unsigned int offset, + struct riscv_debug_reg_field_list_t (*get_next)(riscv_debug_reg_ctx_t contex), + riscv_debug_reg_ctx_t context, uint64_t value) +{ + unsigned int curr = offset; + curr += get_len_or_sprintf(buf, curr, " { "); + for (struct riscv_debug_reg_field_list_t list; get_next; get_next = list.get_next) { + list = get_next(context); + curr += riscv_debug_reg_field_to_s(buf, curr, list.field, context, + riscv_debug_reg_field_value(list.field, value)); + curr += get_len_or_sprintf(buf, curr, ", "); + } + curr += get_len_or_sprintf(buf, curr, "}"); + return curr - offset; +} + +unsigned int riscv_debug_reg_to_s(char *buf, enum riscv_debug_reg_ordinal reg_ordinal, + riscv_debug_reg_ctx_t context, uint64_t value) +{ + unsigned int length = 0; + + riscv_debug_reg_info_t reg = get_riscv_debug_reg_info(reg_ordinal); + + length += get_len_or_sprintf(buf, length, "%s=", reg.name); + length += print_number(buf, length, value); + + if (reg.get_fields_head) + length += riscv_debug_reg_fields_to_s(buf, length, + reg.get_fields_head, context, value); + + if (buf) + buf[length] = '\0'; + return length; +} diff --git a/src/target/riscv/debug_reg_printer.h b/src/target/riscv/debug_reg_printer.h new file mode 100644 index 0000000..8b7a261 --- /dev/null +++ b/src/target/riscv/debug_reg_printer.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "debug_defines.h" + +/** + * This function is used to fill a buffer with a decoded string representation + * of register's value. + * @param buf If non-NULL, the buffer to write the string into. + * Otherwise, the function does not perform any writes, + * it just calculates the number of characters used. + * @param reg_ordinal + * The ordinal of the register. + * @param context The structure, containing the information about the target, + * necessary to decode the register. + * @param value The value to be decoded. + * + * Returns the number of characters used by the string representation + * (excluding '\0'). + * + * Example: + * const struct riscv_debug_reg_ctx_t context = { + * .abits = { .value = <abits value>, .is_set = true } + * }; + * char buf[riscv_debug_reg_to_s(NULL, DTM_DMI_ORDINAL, context, <dmi value>) + 1] + * riscv_debug_reg_to_s(buf, DTM_DMI_ORDINAL, context, <dmi value>); + */ +unsigned int riscv_debug_reg_to_s(char *buf, enum riscv_debug_reg_ordinal reg_ordinal, + riscv_debug_reg_ctx_t context, uint64_t value); 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..570c508 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 @@ -112,6 +120,6 @@ enum gdb_regno { GDB_REGNO_COUNT }; -const char *gdb_regno_name(enum gdb_regno regno); +const char *gdb_regno_name(struct target *target, enum gdb_regno regno); #endif 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..1494845 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]); @@ -122,6 +119,23 @@ int riscv_program_lbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno return riscv_program_insert(p, lb(d, b, offset)); } +int riscv_program_load(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset, + unsigned int size) +{ + switch (size) { + case 1: + return riscv_program_lbr(p, d, b, offset); + case 2: + return riscv_program_lhr(p, d, b, offset); + case 4: + return riscv_program_lwr(p, d, b, offset); + case 8: + return riscv_program_ldr(p, d, b, offset); + } + assert(false && "Unsupported size"); + return ERROR_FAIL; +} + int riscv_program_csrrsi(struct riscv_program *p, enum gdb_regno d, unsigned int z, enum gdb_regno csr) { assert(csr >= GDB_REGNO_CSR0 && csr <= GDB_REGNO_CSR4095); @@ -151,9 +165,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 +189,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..accfc41 100644 --- a/src/target/riscv/program.h +++ b/src/target/riscv/program.h @@ -51,6 +51,8 @@ int riscv_program_ldr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno int riscv_program_lwr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o); int riscv_program_lhr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o); int riscv_program_lbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o); +int riscv_program_load(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int o, + unsigned int s); int riscv_program_sdr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o); int riscv_program_swr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o); @@ -63,7 +65,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..c958c88 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. ***/ @@ -279,7 +273,7 @@ static uint16_t dram_address(unsigned int index) return 0x40 + index - 0x10; } -static uint32_t dtmcontrol_scan(struct target *target, uint32_t out) +static int dtmcontrol_scan(struct target *target, uint32_t out, uint32_t *in_ptr) { struct scan_field field; uint8_t in_value[4]; @@ -306,7 +300,9 @@ static uint32_t dtmcontrol_scan(struct target *target, uint32_t out) uint32_t in = buf_get_u32(field.in_value, 0, 32); LOG_DEBUG("DTMCONTROL: 0x%x -> 0x%x", out, in); - return in; + if (in_ptr) + *in_ptr = in; + return ERROR_OK; } static uint32_t idcode_scan(struct target *target) @@ -344,7 +340,7 @@ static void increase_dbus_busy_delay(struct target *target) info->dtmcontrol_idle, info->dbus_busy_delay, info->interrupt_high_delay); - dtmcontrol_scan(target, DTMCONTROL_DBUS_RESET); + dtmcontrol_scan(target, DTMCONTROL_DBUS_RESET, NULL /* discard value */); } static void increase_interrupt_high_delay(struct target *target) @@ -432,7 +428,10 @@ static dbus_status_t dbus_scan(struct target *target, uint16_t *address_in, .in_value = in }; - assert(info->addrbits != 0); + if (info->addrbits == 0) { + LOG_TARGET_ERROR(target, "Can't access DMI because addrbits=0."); + return DBUS_STATUS_FAILED; + } buf_set_u64(out, DBUS_OP_START, DBUS_OP_SIZE, op); buf_set_u64(out, DBUS_DATA_START, DBUS_DATA_SIZE, data_out); @@ -686,18 +685,13 @@ static void dram_write32(struct target *target, unsigned int index, uint32_t val } /** Read the haltnot and interrupt bits. */ -static bits_t read_bits(struct target *target) +static int read_bits(struct target *target, bits_t *result) { uint64_t value; dbus_status_t status; uint16_t address_in; riscv011_info_t *info = get_info(target); - bits_t err_result = { - .haltnot = 0, - .interrupt = 0 - }; - do { unsigned i = 0; do { @@ -706,26 +700,23 @@ static bits_t read_bits(struct target *target) if (address_in == (1<<info->addrbits) - 1 && value == (1ULL<<DBUS_DATA_SIZE) - 1) { LOG_ERROR("TDO seems to be stuck high."); - return err_result; + return ERROR_FAIL; } increase_dbus_busy_delay(target); - } else if (status == DBUS_STATUS_FAILED) { - /* TODO: return an actual error */ - return err_result; } } while (status == DBUS_STATUS_BUSY && i++ < 256); - if (i >= 256) { + if (status != DBUS_STATUS_SUCCESS) { LOG_ERROR("Failed to read from 0x%x; status=%d", address_in, status); - return err_result; + return ERROR_FAIL; } } while (address_in > 0x10 && address_in != DMCONTROL); - bits_t result = { - .haltnot = get_field(value, DMCONTROL_HALTNOT), - .interrupt = get_field(value, DMCONTROL_INTERRUPT) - }; - return result; + if (result) { + result->haltnot = get_field(value, DMCONTROL_HALTNOT); + result->interrupt = get_field(value, DMCONTROL_INTERRUPT); + } + return ERROR_OK; } static int wait_for_debugint_clear(struct target *target, bool ignore_first) @@ -736,10 +727,16 @@ static int wait_for_debugint_clear(struct target *target, bool ignore_first) * result of the read that happened just before debugint was set. * (Assuming the last scan before calling this function was one that * sets debugint.) */ - read_bits(target); + read_bits(target, NULL); } while (1) { - bits_t bits = read_bits(target); + bits_t bits = { + .haltnot = 0, + .interrupt = 0 + }; + if (read_bits(target, &bits) != ERROR_OK) + return ERROR_FAIL; + if (!bits.interrupt) return ERROR_OK; if (time(NULL) - start > riscv_command_timeout_sec) { @@ -1048,7 +1045,7 @@ static int read_remote_csr(struct target *target, uint64_t *value, uint32_t csr) uint32_t exception = cache_get32(target, info->dramsize-1); if (exception) { LOG_WARNING("Got exception 0x%x when reading %s", exception, - gdb_regno_name(GDB_REGNO_CSR0 + csr)); + gdb_regno_name(target, GDB_REGNO_CSR0 + csr)); *value = ~0; return ERROR_FAIL; } @@ -1109,10 +1106,14 @@ static int maybe_write_tselect(struct target *target) static int execute_resume(struct target *target, bool step) { + RISCV_INFO(r); riscv011_info_t *info = get_info(target); 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 @@ -1137,9 +1138,9 @@ static int execute_resume(struct target *target, bool step) } } - info->dcsr = set_field(info->dcsr, DCSR_EBREAKM, riscv_ebreakm); - info->dcsr = set_field(info->dcsr, DCSR_EBREAKS, riscv_ebreaks); - info->dcsr = set_field(info->dcsr, DCSR_EBREAKU, riscv_ebreaku); + info->dcsr = set_field(info->dcsr, DCSR_EBREAKM, r->riscv_ebreakm); + info->dcsr = set_field(info->dcsr, DCSR_EBREAKS, r->riscv_ebreaks); + info->dcsr = set_field(info->dcsr, DCSR_EBREAKU, r->riscv_ebreaku); info->dcsr = set_field(info->dcsr, DCSR_EBREAKH, 1); info->dcsr &= ~DCSR_HALT; @@ -1189,17 +1190,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) @@ -1256,7 +1247,7 @@ static int register_read(struct target *target, riscv_reg_t *value, int regnum) uint32_t exception = cache_get32(target, info->dramsize-1); if (exception) { - LOG_WARNING("Got exception 0x%x when reading %s", exception, gdb_regno_name(regnum)); + LOG_WARNING("Got exception 0x%x when reading %s", exception, gdb_regno_name(target, regnum)); *value = ~0; return ERROR_FAIL; } @@ -1331,14 +1322,15 @@ static int register_write(struct target *target, unsigned int number, uint32_t exception = cache_get32(target, info->dramsize-1); if (exception) { LOG_WARNING("Got exception 0x%x when writing %s", exception, - gdb_regno_name(number)); + gdb_regno_name(target, number)); return ERROR_FAIL; } 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 +1374,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); } @@ -1468,16 +1461,17 @@ static int step(struct target *target, int current, target_addr_t address, static int examine(struct target *target) { /* Don't need to select dbus, since the first thing we do is read dtmcontrol. */ + uint32_t dtmcontrol; + if (dtmcontrol_scan(target, 0, &dtmcontrol) != ERROR_OK || dtmcontrol == 0) { + LOG_ERROR("Could not scan dtmcontrol. Check JTAG connectivity/board power."); + return ERROR_FAIL; + } - uint32_t dtmcontrol = dtmcontrol_scan(target, 0); LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol); LOG_DEBUG(" addrbits=%d", get_field(dtmcontrol, DTMCONTROL_ADDRBITS)); LOG_DEBUG(" version=%d", get_field(dtmcontrol, DTMCONTROL_VERSION)); LOG_DEBUG(" idle=%d", get_field(dtmcontrol, DTMCONTROL_IDLE)); - if (dtmcontrol == 0) { - LOG_ERROR("dtmcontrol is 0. Check JTAG connectivity/board power."); - return ERROR_FAIL; - } + if (get_field(dtmcontrol, DTMCONTROL_VERSION) != 0) { LOG_ERROR("Unsupported DTM version %d. (dtmcontrol=0x%x)", get_field(dtmcontrol, DTMCONTROL_VERSION), dtmcontrol); @@ -1590,7 +1584,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, @@ -1905,7 +1898,13 @@ static int poll_target(struct target *target, bool announce) int old_debug_level = debug_level; if (debug_level >= LOG_LVL_DEBUG) debug_level = LOG_LVL_INFO; - bits_t bits = read_bits(target); + bits_t bits = { + .haltnot = 0, + .interrupt = 0 + }; + if (read_bits(target, &bits) != ERROR_OK) + return ERROR_FAIL; + debug_level = old_debug_level; if (bits.haltnot && bits.interrupt) { @@ -1936,11 +1935,12 @@ 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) { + RISCV_INFO(r); riscv011_info_t *info = get_info(target); /* TODO: Maybe what I implemented here is more like soft_reset_halt()? */ @@ -1954,9 +1954,9 @@ static int assert_reset(struct target *target) /* Not sure what we should do when there are multiple cores. * Here just reset the single hart we're talking to. */ - info->dcsr = set_field(info->dcsr, DCSR_EBREAKM, riscv_ebreakm); - info->dcsr = set_field(info->dcsr, DCSR_EBREAKS, riscv_ebreaks); - info->dcsr = set_field(info->dcsr, DCSR_EBREAKU, riscv_ebreaku); + info->dcsr = set_field(info->dcsr, DCSR_EBREAKM, r->riscv_ebreakm); + info->dcsr = set_field(info->dcsr, DCSR_EBREAKS, r->riscv_ebreaks); + info->dcsr = set_field(info->dcsr, DCSR_EBREAKU, r->riscv_ebreaku); info->dcsr = set_field(info->dcsr, DCSR_EBREAKH, 1); info->dcsr |= DCSR_HALT; if (target->reset_halt) diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index 2f4a8fe..5b84aadc 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -28,44 +28,56 @@ #include "program.h" #include "asm.h" #include "batch.h" +#include "debug_reg_printer.h" 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, +static int riscv013_write_debug_buffer(struct target *target, unsigned int index, riscv_insn_t d); -static riscv_insn_t riscv013_read_debug_buffer(struct target *target, unsigned +static riscv_insn_t riscv013_read_debug_buffer(struct target *target, unsigned int 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 void riscv013_fill_dm_write_u64(struct target *target, char *buf, int a, uint64_t d); +static void riscv013_fill_dm_read_u64(struct target *target, char *buf, int a); +static void riscv013_fill_dm_nop_u64(struct target *target, char *buf); +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 +88,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,35 +118,24 @@ 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; int abs_chain_position; - + /* The base address to access this DM on DMI */ + uint32_t base; /* The number of harts connected to this DM. */ int hart_count; /* Indicates we already reset this DM, so don't need to do it again. */ 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 +207,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); @@ -241,19 +245,25 @@ static dm013_info_t *get_dm(struct target *target) dm013_info_t *entry; dm013_info_t *dm = NULL; list_for_each_entry(entry, &dm_list, list) { - if (entry->abs_chain_position == abs_chain_position) { + if (entry->abs_chain_position == abs_chain_position + && entry->base == target->dbgbase) { dm = entry; break; } } if (!dm) { - LOG_DEBUG("[%d] Allocating new DM", target->coreid); + LOG_TARGET_DEBUG(target, "Coreid [%d] Allocating new DM", target->coreid); dm = calloc(1, sizeof(dm013_info_t)); if (!dm) return NULL; dm->abs_chain_position = abs_chain_position; - dm->current_hartid = -1; + + /* Safety check for dbgbase */ + assert(target->dbgbase_set || target->dbgbase == 0); + + dm->base = target->dbgbase; + dm->current_hartid = 0; dm->hart_count = -1; INIT_LIST_HEAD(&dm->target_list); list_add(&dm->list, &dm_list); @@ -276,100 +286,92 @@ static dm013_info_t *get_dm(struct target *target) return dm; } -static uint32_t set_hartsel(uint32_t initial, uint32_t index) +static riscv_debug_reg_ctx_t get_riscv_debug_reg_ctx(struct target *target) { - initial &= ~DM_DMCONTROL_HARTSELLO; - initial &= ~DM_DMCONTROL_HARTSELHI; + RISCV_INFO(r); + RISCV013_INFO(info); + const riscv_debug_reg_ctx_t context = { + .XLEN = { .value = r->xlen, .is_set = true }, + .DXLEN = { .value = r->xlen, .is_set = true }, + .abits = { .value = info->abits, .is_set = true }, + }; + return context; +} - 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; +static void log_debug_reg(struct target *target, enum riscv_debug_reg_ordinal reg, + riscv_reg_t value) +{ + if (debug_level < LOG_LVL_DEBUG) + return; + const riscv_debug_reg_ctx_t context = get_riscv_debug_reg_ctx(target); + char buf[riscv_debug_reg_to_s(NULL, reg, context, value) + 1]; + riscv_debug_reg_to_s(buf, reg, context, value); + LOG_TARGET_DEBUG(target, "%s", buf); +} + +static uint32_t set_dmcontrol_hartsel(uint32_t initial, int hart_index) +{ + 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; } -static void decode_dmi(char *text, unsigned address, unsigned data) +static unsigned int decode_dm(char *text, unsigned int address, unsigned int data) { static const struct { - unsigned address; - uint64_t mask; - const char *name; + unsigned int address; + enum riscv_debug_reg_ordinal ordinal; } description[] = { - { DM_DMCONTROL, DM_DMCONTROL_HALTREQ, "haltreq" }, - { DM_DMCONTROL, DM_DMCONTROL_RESUMEREQ, "resumereq" }, - { DM_DMCONTROL, DM_DMCONTROL_HARTRESET, "hartreset" }, - { DM_DMCONTROL, DM_DMCONTROL_HASEL, "hasel" }, - { DM_DMCONTROL, DM_DMCONTROL_HARTSELHI, "hartselhi" }, - { DM_DMCONTROL, DM_DMCONTROL_HARTSELLO, "hartsello" }, - { DM_DMCONTROL, DM_DMCONTROL_NDMRESET, "ndmreset" }, - { DM_DMCONTROL, DM_DMCONTROL_DMACTIVE, "dmactive" }, - { DM_DMCONTROL, DM_DMCONTROL_ACKHAVERESET, "ackhavereset" }, - - { DM_DMSTATUS, DM_DMSTATUS_IMPEBREAK, "impebreak" }, - { DM_DMSTATUS, DM_DMSTATUS_ALLHAVERESET, "allhavereset" }, - { DM_DMSTATUS, DM_DMSTATUS_ANYHAVERESET, "anyhavereset" }, - { DM_DMSTATUS, DM_DMSTATUS_ALLRESUMEACK, "allresumeack" }, - { DM_DMSTATUS, DM_DMSTATUS_ANYRESUMEACK, "anyresumeack" }, - { DM_DMSTATUS, DM_DMSTATUS_ALLNONEXISTENT, "allnonexistent" }, - { DM_DMSTATUS, DM_DMSTATUS_ANYNONEXISTENT, "anynonexistent" }, - { DM_DMSTATUS, DM_DMSTATUS_ALLUNAVAIL, "allunavail" }, - { DM_DMSTATUS, DM_DMSTATUS_ANYUNAVAIL, "anyunavail" }, - { DM_DMSTATUS, DM_DMSTATUS_ALLRUNNING, "allrunning" }, - { DM_DMSTATUS, DM_DMSTATUS_ANYRUNNING, "anyrunning" }, - { DM_DMSTATUS, DM_DMSTATUS_ALLHALTED, "allhalted" }, - { DM_DMSTATUS, DM_DMSTATUS_ANYHALTED, "anyhalted" }, - { DM_DMSTATUS, DM_DMSTATUS_AUTHENTICATED, "authenticated" }, - { DM_DMSTATUS, DM_DMSTATUS_AUTHBUSY, "authbusy" }, - { DM_DMSTATUS, DM_DMSTATUS_HASRESETHALTREQ, "hasresethaltreq" }, - { DM_DMSTATUS, DM_DMSTATUS_CONFSTRPTRVALID, "confstrptrvalid" }, - { DM_DMSTATUS, DM_DMSTATUS_VERSION, "version" }, - - { DM_ABSTRACTCS, DM_ABSTRACTCS_PROGBUFSIZE, "progbufsize" }, - { DM_ABSTRACTCS, DM_ABSTRACTCS_BUSY, "busy" }, - { DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR, "cmderr" }, - { DM_ABSTRACTCS, DM_ABSTRACTCS_DATACOUNT, "datacount" }, - - { DM_COMMAND, DM_COMMAND_CMDTYPE, "cmdtype" }, - - { DM_SBCS, DM_SBCS_SBVERSION, "sbversion" }, - { DM_SBCS, DM_SBCS_SBBUSYERROR, "sbbusyerror" }, - { DM_SBCS, DM_SBCS_SBBUSY, "sbbusy" }, - { DM_SBCS, DM_SBCS_SBREADONADDR, "sbreadonaddr" }, - { DM_SBCS, DM_SBCS_SBACCESS, "sbaccess" }, - { DM_SBCS, DM_SBCS_SBAUTOINCREMENT, "sbautoincrement" }, - { DM_SBCS, DM_SBCS_SBREADONDATA, "sbreadondata" }, - { DM_SBCS, DM_SBCS_SBERROR, "sberror" }, - { DM_SBCS, DM_SBCS_SBASIZE, "sbasize" }, - { DM_SBCS, DM_SBCS_SBACCESS128, "sbaccess128" }, - { DM_SBCS, DM_SBCS_SBACCESS64, "sbaccess64" }, - { DM_SBCS, DM_SBCS_SBACCESS32, "sbaccess32" }, - { DM_SBCS, DM_SBCS_SBACCESS16, "sbaccess16" }, - { DM_SBCS, DM_SBCS_SBACCESS8, "sbaccess8" }, + {DM_DMCONTROL, DM_DMCONTROL_ORDINAL}, + {DM_DMSTATUS, DM_DMSTATUS_ORDINAL}, + {DM_ABSTRACTCS, DM_ABSTRACTCS_ORDINAL}, + {DM_COMMAND, DM_COMMAND_ORDINAL}, + {DM_SBCS, DM_SBCS_ORDINAL} }; - text[0] = 0; for (unsigned i = 0; i < ARRAY_SIZE(description); i++) { if (description[i].address == address) { - uint64_t mask = description[i].mask; - unsigned value = get_field(data, mask); - if (value) { - if (i > 0) - *(text++) = ' '; - if (mask & (mask >> 1)) { - /* If the field is more than 1 bit wide. */ - sprintf(text, "%s=%d", description[i].name, value); - } else { - strcpy(text, description[i].name); - } - text += strlen(text); - } + const riscv_debug_reg_ctx_t context = { + .XLEN = { .value = 0, .is_set = false }, + .DXLEN = { .value = 0, .is_set = false }, + .abits = { .value = 0, .is_set = false }, + }; + return riscv_debug_reg_to_s(text, description[i].ordinal, + context, data); } } + if (text) + text[0] = '\0'; + return 0; } -static void dump_field(int idle, const struct scan_field *field) +static unsigned int decode_dmi(struct target *target, char *text, unsigned int address, + unsigned int data) +{ + dm013_info_t *dm = get_dm(target); + if (!dm) { + if (text) + text[0] = '\0'; + return 0; + } + return decode_dm(text, address - dm->base, data); +} + +static void dump_field(struct target *target, int idle, const struct scan_field *field) { static const char * const op_string[] = {"-", "r", "w", "?"}; static const char * const status_string[] = {"+", "?", "F", "b"}; @@ -393,13 +395,13 @@ static void dump_field(int idle, const struct scan_field *field) field->num_bits, op_string[out_op], out_data, out_address, status_string[in_op], in_data, in_address, idle); - char out_text[500]; - char in_text[500]; - decode_dmi(out_text, out_address, out_data); - decode_dmi(in_text, in_address, in_data); + char out_text[decode_dmi(target, NULL, out_address, out_data) + 1]; + unsigned int out_len = decode_dmi(target, out_text, out_address, out_data); + char in_text[decode_dmi(target, NULL, in_address, in_data) + 1]; + unsigned int in_len = decode_dmi(target, in_text, in_address, in_data); if (in_text[0] || out_text[0]) { - log_printf_lf(LOG_LVL_DEBUG, __FILE__, __LINE__, "scan", "%s -> %s", - out_text, in_text); + log_printf_lf(LOG_LVL_DEBUG, __FILE__, __LINE__, "scan", "%.*s -> %.*s", + out_len, out_text, in_len, in_text); } } @@ -414,14 +416,14 @@ static void select_dmi(struct target *target) jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE); } -static uint32_t dtmcontrol_scan(struct target *target, uint32_t out) +static int dtmcontrol_scan(struct target *target, uint32_t out, uint32_t *in_ptr) { struct scan_field field; uint8_t in_value[4]; uint8_t out_value[4] = { 0 }; if (bscan_tunnel_ir_width != 0) - return dtmcontrol_scan_via_bscan(target, out); + return dtmcontrol_scan_via_bscan(target, out, in_ptr); buf_set_u32(out_value, 0, 32, out); @@ -444,18 +446,20 @@ static uint32_t dtmcontrol_scan(struct target *target, uint32_t out) uint32_t in = buf_get_u32(field.in_value, 0, 32); LOG_DEBUG("DTMCS: 0x%x -> 0x%x", out, in); - return in; + if (in_ptr) + *in_ptr = in; + return ERROR_OK; } static void increase_dmi_busy_delay(struct target *target) { riscv013_info_t *info = get_info(target); info->dmi_busy_delay += info->dmi_busy_delay / 10 + 1; - LOG_DEBUG("dtmcs_idle=%d, dmi_busy_delay=%d, ac_busy_delay=%d", + LOG_TARGET_DEBUG(target, "dtmcs_idle=%d, dmi_busy_delay=%d, ac_busy_delay=%d", info->dtmcs_idle, info->dmi_busy_delay, info->ac_busy_delay); - dtmcontrol_scan(target, DTM_DTMCS_DMIRESET); + dtmcontrol_scan(target, DTM_DTMCS_DMIRESET, NULL /* discard result */); } /** @@ -482,6 +486,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; } @@ -490,7 +495,10 @@ static dmi_status_t dmi_scan(struct target *target, uint32_t *address_in, memset(in, 0, num_bytes); memset(out, 0, num_bytes); - assert(info->abits != 0); + if (info->abits == 0) { + LOG_TARGET_ERROR(target, "Can't access DMI because addrbits=0."); + return DMI_STATUS_FAILED; + } buf_set_u32(out, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, op); buf_set_u32(out, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH, data_out); @@ -534,7 +542,7 @@ static dmi_status_t dmi_scan(struct target *target, uint32_t *address_in, if (address_in) *address_in = buf_get_u32(in, DTM_DMI_ADDRESS_OFFSET, info->abits); - dump_field(idle_count, &field); + dump_field(target, idle_count, &field); return buf_get_u32(in, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH); } @@ -544,7 +552,7 @@ static dmi_status_t dmi_scan(struct target *target, uint32_t *address_in, * @param dmi_busy_encountered * If non-NULL, will be updated to reflect whether DMI busy was * encountered while executing this operation or not. - * @param dmi_op The operation to perform (read/write/nop). + * @param op The operation to perform (read/write/nop). * @param address The address argument to that operation. * @param data_out The data to send to the target. * @param timeout_sec @@ -555,19 +563,18 @@ static dmi_status_t dmi_scan(struct target *target, uint32_t *address_in, * DMI operation succeeded. */ static int dmi_op_timeout(struct target *target, uint32_t *data_in, - bool *dmi_busy_encountered, int dmi_op, uint32_t address, + bool *dmi_busy_encountered, int op, uint32_t address, uint32_t data_out, int timeout_sec, bool exec, bool ensure_success) { select_dmi(target); dmi_status_t status; - uint32_t address_in; if (dmi_busy_encountered) *dmi_busy_encountered = false; const char *op_name; - switch (dmi_op) { + switch (op) { case DMI_OP_NOP: op_name = "nop"; break; @@ -578,7 +585,7 @@ static int dmi_op_timeout(struct target *target, uint32_t *data_in, op_name = "write"; break; default: - LOG_ERROR("Invalid DMI operation: %d", dmi_op); + LOG_ERROR("Invalid DMI operation: %d", op); return ERROR_FAIL; } @@ -588,7 +595,7 @@ static int dmi_op_timeout(struct target *target, uint32_t *data_in, /* This first loop performs the request. Note that if for some reason this * stays busy, it is actually due to the previous access. */ while (1) { - status = dmi_scan(target, NULL, NULL, dmi_op, address, data_out, + status = dmi_scan(target, NULL, NULL, op, address, data_out, exec); if (status == DMI_STATUS_BUSY) { increase_dmi_busy_delay(target); @@ -597,16 +604,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); - dtmcontrol_scan(target, DTM_DTMCS_DMIRESET); - return ERROR_FAIL; + dtmcontrol_scan(target, DTM_DTMCS_DMIRESET, NULL /* discard result */); + 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; } @@ -615,7 +621,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); @@ -625,13 +631,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); + dtmcontrol_scan(target, DTM_DTMCS_DMIRESET, NULL /* discard result */); return ERROR_FAIL; } if (time(NULL) - start > timeout_sec) @@ -643,13 +651,13 @@ static int dmi_op_timeout(struct target *target, uint32_t *data_in, } static int dmi_op(struct target *target, uint32_t *data_in, - bool *dmi_busy_encountered, int dmi_op, uint32_t address, + bool *dmi_busy_encountered, int op, uint32_t address, uint32_t data_out, bool exec, bool ensure_success) { - int result = dmi_op_timeout(target, data_in, dmi_busy_encountered, dmi_op, + int result = dmi_op_timeout(target, data_in, dmi_busy_encountered, 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_TARGET_ERROR(target, "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); @@ -679,10 +687,92 @@ static int dmi_write_exec(struct target *target, uint32_t address, return dmi_op(target, NULL, NULL, DMI_OP_WRITE, address, value, true, ensure_success); } +static int dm_op_timeout(struct target *target, uint32_t *data_in, + bool *dmi_busy_encountered, int op, uint32_t address, + uint32_t data_out, int timeout_sec, bool exec, bool ensure_success) +{ + dm013_info_t *dm = get_dm(target); + if (!dm) + return ERROR_FAIL; + return dmi_op_timeout(target, data_in, dmi_busy_encountered, op, address + dm->base, + data_out, timeout_sec, exec, ensure_success); +} + +static int dm_op(struct target *target, uint32_t *data_in, + bool *dmi_busy_encountered, int op, uint32_t address, + uint32_t data_out, bool exec, bool ensure_success) +{ + dm013_info_t *dm = get_dm(target); + if (!dm) + return ERROR_FAIL; + return dmi_op(target, data_in, dmi_busy_encountered, op, address + dm->base, + data_out, exec, ensure_success); +} + +static int dm_read(struct target *target, uint32_t *value, uint32_t address) +{ + dm013_info_t *dm = get_dm(target); + if (!dm) + return ERROR_FAIL; + return dmi_read(target, value, address + dm->base); +} + +static int dm_read_exec(struct target *target, uint32_t *value, uint32_t address) +{ + dm013_info_t *dm = get_dm(target); + if (!dm) + return ERROR_FAIL; + return dmi_read_exec(target, value, address + dm->base); +} + +static int dm_write(struct target *target, uint32_t address, uint32_t value) +{ + dm013_info_t *dm = get_dm(target); + if (!dm) + return ERROR_FAIL; + return dmi_write(target, address + dm->base, value); +} + +static int dm_write_exec(struct target *target, uint32_t address, + uint32_t value, bool ensure_success) +{ + dm013_info_t *dm = get_dm(target); + if (!dm) + return ERROR_FAIL; + return dmi_write_exec(target, address + dm->base, value, ensure_success); +} + +static bool check_dbgbase_exists(struct target *target) +{ + uint32_t next_dm = 0; + unsigned int count = 1; + + LOG_TARGET_DEBUG(target, "Searching for DM with DMI base address (dbgbase) = 0x%x", target->dbgbase); + while (1) { + uint32_t current_dm = next_dm; + if (current_dm == target->dbgbase) + return true; + if (dmi_read(target, &next_dm, DM_NEXTDM + current_dm) != ERROR_OK) + break; + LOG_TARGET_DEBUG(target, "dm @ 0x%x --> nextdm=0x%x", current_dm, next_dm); + /* Check if it's last one in the chain. */ + if (next_dm == 0) { + LOG_TARGET_ERROR(target, "Reached the end of DM chain (detected %u DMs in total).", count); + break; + } + /* Safety: Avoid looping forever in case of buggy nextdm values in the hardware. */ + if (count++ > RISCV_MAX_DMS) { + LOG_TARGET_ERROR(target, "Supporting no more than %d DMs on a DMI bus. Aborting", RISCV_MAX_DMS); + break; + } + } + return false; +} + static int dmstatus_read_timeout(struct target *target, uint32_t *dmstatus, bool authenticated, unsigned timeout_sec) { - int result = dmi_op_timeout(target, dmstatus, NULL, DMI_OP_READ, + int result = dm_op_timeout(target, dmstatus, NULL, DMI_OP_READ, DM_DMSTATUS, 0, timeout_sec, false, true); if (result != ERROR_OK) return result; @@ -704,15 +794,21 @@ 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) { riscv013_info_t *info = get_info(target); info->ac_busy_delay += info->ac_busy_delay / 10 + 1; - LOG_DEBUG("dtmcs_idle=%d, dmi_busy_delay=%d, ac_busy_delay=%d", + LOG_TARGET_DEBUG(target, "dtmcs_idle=%d, dmi_busy_delay=%d, ac_busy_delay=%d", info->dtmcs_idle, info->dmi_busy_delay, info->ac_busy_delay); } @@ -737,7 +833,7 @@ static int wait_for_idle(struct target *target, uint32_t *abstractcs) RISCV013_INFO(info); time_t start = time(NULL); while (1) { - if (dmi_read(target, abstractcs, DM_ABSTRACTCS) != ERROR_OK) + if (dm_read(target, abstractcs, DM_ABSTRACTCS) != ERROR_OK) return ERROR_FAIL; if (get_field(*abstractcs, DM_ABSTRACTCS_BUSY) == 0) @@ -745,22 +841,8 @@ 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). " + LOG_TARGET_ERROR(target, "Timed out after %ds waiting for busy to go low (abstractcs=0x%x). " "Increase the timeout with riscv set_command_timeout_sec.", riscv_command_timeout_sec, *abstractcs); @@ -769,28 +851,27 @@ 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); if (debug_level >= LOG_LVL_DEBUG) { switch (get_field(command, DM_COMMAND_CMDTYPE)) { case 0: - LOG_DEBUG("command=0x%x; access register, size=%d, postexec=%d, " - "transfer=%d, write=%d, regno=0x%x", - command, - 8 << get_field(command, AC_ACCESS_REGISTER_AARSIZE), - get_field(command, AC_ACCESS_REGISTER_POSTEXEC), - get_field(command, AC_ACCESS_REGISTER_TRANSFER), - get_field(command, AC_ACCESS_REGISTER_WRITE), - get_field(command, AC_ACCESS_REGISTER_REGNO)); + log_debug_reg(target, AC_ACCESS_REGISTER_ORDINAL, command); break; default: - LOG_DEBUG("command=0x%x", command); + LOG_TARGET_DEBUG(target, "command=0x%x", command); break; } } - if (dmi_write_exec(target, DM_COMMAND, command, false) != ERROR_OK) + if (dm_write_exec(target, DM_COMMAND, command, false) != ERROR_OK) return ERROR_FAIL; uint32_t abstractcs = 0; @@ -798,9 +879,9 @@ static int execute_abstract_command(struct target *target, uint32_t command) info->cmderr = get_field(abstractcs, DM_ABSTRACTCS_CMDERR); if (info->cmderr != 0 || result != ERROR_OK) { - LOG_DEBUG("command 0x%x failed; abstractcs=0x%x", command, abstractcs); + LOG_TARGET_DEBUG(target, "command 0x%x failed; abstractcs=0x%x", command, abstractcs); /* Clear the error. */ - dmi_write(target, DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR); + dm_write(target, DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR); return ERROR_FAIL; } @@ -815,14 +896,14 @@ static riscv_reg_t read_abstract_arg(struct target *target, unsigned index, unsigned offset = index * size_bits / 32; switch (size_bits) { default: - LOG_ERROR("Unsupported size: %d bits", size_bits); + LOG_TARGET_ERROR(target, "Unsupported size: %d bits", size_bits); return ~0; case 64: - dmi_read(target, &v, DM_DATA0 + offset + 1); + dm_read(target, &v, DM_DATA0 + offset + 1); value |= ((uint64_t) v) << 32; /* falls through */ case 32: - dmi_read(target, &v, DM_DATA0 + offset); + dm_read(target, &v, DM_DATA0 + offset); value |= v; } return value; @@ -834,13 +915,13 @@ static int write_abstract_arg(struct target *target, unsigned index, unsigned offset = index * size_bits / 32; switch (size_bits) { default: - LOG_ERROR("Unsupported size: %d bits", size_bits); + LOG_TARGET_ERROR(target, "Unsupported size: %d bits", size_bits); return ERROR_FAIL; case 64: - dmi_write(target, DM_DATA0 + offset + 1, value >> 32); + dm_write(target, DM_DATA0 + offset + 1, value >> 32); /* falls through */ case 32: - dmi_write(target, DM_DATA0 + offset, value); + dm_write(target, DM_DATA0 + offset, value); } return ERROR_OK; } @@ -860,8 +941,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_TARGET_ERROR(target, "%d-bit register %s not supported.", + size, gdb_regno_name(target, number)); assert(0); } @@ -890,8 +971,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); @@ -913,10 +994,10 @@ static int register_read_abstract(struct target *target, uint64_t *value, if (info->cmderr == CMDERR_NOT_SUPPORTED) { if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { info->abstract_read_fpr_supported = false; - LOG_INFO("Disabling abstract command reads from FPRs."); + LOG_TARGET_INFO(target, "Disabling abstract command reads from FPRs."); } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { info->abstract_read_csr_supported = false; - LOG_INFO("Disabling abstract command reads from CSRs."); + LOG_TARGET_INFO(target, "Disabling abstract command reads from CSRs."); } } return result; @@ -928,10 +1009,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) @@ -952,10 +1042,10 @@ static int register_write_abstract(struct target *target, uint32_t number, if (info->cmderr == CMDERR_NOT_SUPPORTED) { if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { info->abstract_write_fpr_supported = false; - LOG_INFO("Disabling abstract command writes to FPRs."); + LOG_TARGET_INFO(target, "Disabling abstract command writes to FPRs."); } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { info->abstract_write_csr_supported = false; - LOG_INFO("Disabling abstract command writes to CSRs."); + LOG_TARGET_INFO(target, "Disabling abstract command writes to CSRs."); } } return result; @@ -991,14 +1081,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 int 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; } @@ -1014,12 +1104,11 @@ static int examine_progbuf(struct target *target) if (info->progbufsize < 1) { info->progbuf_writable = YNM_NO; - LOG_INFO("No program buffer present."); + LOG_TARGET_INFO(target, "No program buffer present."); 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; @@ -1035,9 +1124,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. */ @@ -1046,15 +1132,15 @@ static int examine_progbuf(struct target *target) } uint32_t written; - if (dmi_read(target, &written, DM_PROGBUF0) != ERROR_OK) + if (dm_read(target, &written, DM_PROGBUF0) != ERROR_OK) return ERROR_FAIL; if (written == (uint32_t) info->progbuf_address) { - LOG_INFO("progbuf is writable at 0x%" PRIx64, + LOG_TARGET_INFO(target, "progbuf is writable at 0x%" PRIx64, info->progbuf_address); info->progbuf_writable = YNM_YES; } else { - LOG_INFO("progbuf is not writeable at 0x%" PRIx64, + LOG_TARGET_INFO(target, "progbuf is not writeable at 0x%" PRIx64, info->progbuf_address); info->progbuf_writable = YNM_NO; } @@ -1062,7 +1148,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) || @@ -1070,46 +1156,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(target, 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(target, 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 { @@ -1187,7 +1288,7 @@ static int scratch_reserve(struct target *target, return ERROR_OK; } - LOG_ERROR("Couldn't find %d bytes of scratch RAM to use. Please configure " + LOG_TARGET_ERROR(target, "Couldn't find %d bytes of scratch RAM to use. Please configure " "a work area with 'configure -work-area-phys'.", size_bytes); return ERROR_FAIL; } @@ -1204,18 +1305,18 @@ static int scratch_read64(struct target *target, scratch_mem_t *scratch, uint32_t v; switch (scratch->memory_space) { case SPACE_DM_DATA: - if (dmi_read(target, &v, DM_DATA0 + scratch->debug_address) != ERROR_OK) + if (dm_read(target, &v, DM_DATA0 + scratch->debug_address) != ERROR_OK) return ERROR_FAIL; *value = v; - if (dmi_read(target, &v, DM_DATA1 + scratch->debug_address) != ERROR_OK) + if (dm_read(target, &v, DM_DATA1 + scratch->debug_address) != ERROR_OK) return ERROR_FAIL; *value |= ((uint64_t) v) << 32; break; case SPACE_DMI_PROGBUF: - if (dmi_read(target, &v, DM_PROGBUF0 + scratch->debug_address) != ERROR_OK) + if (dm_read(target, &v, DM_PROGBUF0 + scratch->debug_address) != ERROR_OK) return ERROR_FAIL; *value = v; - if (dmi_read(target, &v, DM_PROGBUF1 + scratch->debug_address) != ERROR_OK) + if (dm_read(target, &v, DM_PROGBUF1 + scratch->debug_address) != ERROR_OK) return ERROR_FAIL; *value |= ((uint64_t) v) << 32; break; @@ -1243,12 +1344,13 @@ static int scratch_write64(struct target *target, scratch_mem_t *scratch, { switch (scratch->memory_space) { case SPACE_DM_DATA: - dmi_write(target, DM_DATA0 + scratch->debug_address, value); - dmi_write(target, DM_DATA1 + scratch->debug_address, value >> 32); + dm_write(target, DM_DATA0 + scratch->debug_address, value); + dm_write(target, DM_DATA1 + scratch->debug_address, value >> 32); break; case SPACE_DMI_PROGBUF: - dmi_write(target, DM_PROGBUF0 + scratch->debug_address, value); - dmi_write(target, DM_PROGBUF1 + scratch->debug_address, value >> 32); + dm_write(target, DM_PROGBUF0 + scratch->debug_address, value); + dm_write(target, DM_PROGBUF1 + scratch->debug_address, value >> 32); + riscv013_invalidate_cached_debug_buffer(target); break; case SPACE_DMI_RAM: { @@ -1271,7 +1373,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(). */ @@ -1290,204 +1392,308 @@ 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); + scratch_mem_t scratch; - 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; + if (scratch_reserve(target, &scratch, program, 8) != ERROR_OK) + return ERROR_FAIL; + + 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; + } + + int result = scratch_read64(target, &scratch, value); + + scratch_release(target, &scratch); + return result; +} + +static int fpr_read_progbuf(struct target *target, uint64_t *value, + enum gdb_regno number) +{ + assert(target->state == TARGET_HALTED); + assert(number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31); + + const unsigned int freg = number - GDB_REGNO_FPR0; + + if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; struct riscv_program program; riscv_program_init(&program, target); + 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; + 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; - uint64_t s0; - if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK) + if (riscv_program_exec(&program, target) != ERROR_OK) return ERROR_FAIL; - uint64_t mstatus; - if (prep_for_register_access(target, &mstatus, number) != ERROR_OK) + return register_read_abstract(target, value, GDB_REGNO_S0) != ERROR_OK; +} + +static int csr_read_progbuf(struct target *target, uint64_t *value, + enum gdb_regno number) +{ + assert(target->state == TARGET_HALTED); + assert(number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095); + + if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK) return ERROR_FAIL; + struct riscv_program program; + riscv_program_init(&program, target); + if (riscv_program_csrr(&program, S0, number) != ERROR_OK) + return ERROR_FAIL; + if (riscv_program_exec(&program, target) != ERROR_OK) + return ERROR_FAIL; + + return register_read_abstract(target, value, GDB_REGNO_S0) != ERROR_OK; +} + +/** + * 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); + + if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) + return fpr_read_progbuf(target, value, number); + else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) + return csr_read_progbuf(target, value, number); + + LOG_TARGET_ERROR(target, "Unexpected read of %s via program buffer.", + gdb_regno_name(target, number)); + return ERROR_FAIL; +} + +/** + * 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; - 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; - if (register_write_direct(target, GDB_REGNO_S0, scratch.hart_address) - != ERROR_OK) { - scratch_release(target, &scratch); - return ERROR_FAIL; - } + if (scratch_reserve(target, &scratch, program, 8) != ERROR_OK) + return ERROR_FAIL; - if (scratch_write64(target, &scratch, value) != ERROR_OK) { - scratch_release(target, &scratch); - return ERROR_FAIL; - } + 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); - } else if (number == GDB_REGNO_VTYPE) { - riscv_program_insert(&program, csrr(S0, CSR_VL)); - riscv_program_insert(&program, vsetvli(ZERO, S0, value)); + scratch_release(target, &scratch); + return result; +} - } else { - if (register_write_direct(target, GDB_REGNO_S0, value) != ERROR_OK) - return ERROR_FAIL; +static int fpr_write_progbuf(struct target *target, enum gdb_regno number, + riscv_reg_t value) +{ + assert(target->state == TARGET_HALTED); + assert(number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31); + const unsigned int freg = number - GDB_REGNO_FPR0; - 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) - 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); + if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + + struct riscv_program program; + riscv_program_init(&program, target); + + 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, fld(freg, S0, 0)) != ERROR_OK) return ERROR_FAIL; - } + return internal_register_write64_progbuf_scratch(target, &program, value); } - 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 (register_write_abstract(target, GDB_REGNO_S0, value) != ERROR_OK) + return ERROR_FAIL; - if (use_scratch) - scratch_release(target, &scratch); + 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; - if (cleanup_after_register_access(target, mstatus, number) != ERROR_OK) + return riscv_program_exec(&program, target); +} + +static int vtype_write_progbuf(struct target *target, riscv_reg_t value) +{ + assert(target->state == TARGET_HALTED); + + if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + if (register_write_abstract(target, GDB_REGNO_S0, value) != ERROR_OK) + return ERROR_FAIL; + if (riscv_save_register(target, GDB_REGNO_S1) != ERROR_OK) return ERROR_FAIL; - /* Restore S0. */ - if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK) + struct riscv_program program; + riscv_program_init(&program, target); + 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; - return exec_out; + return riscv_program_exec(&program, target); } -/** Read register value from the target. Also update the cached value. */ -static int register_read(struct target *target, uint64_t *value, uint32_t number) +static int vl_write_progbuf(struct target *target, riscv_reg_t value) { - if (number == GDB_REGNO_ZERO) { - *value = 0; - return ERROR_OK; - } - int result = register_read_direct(target, value, number); - if (result != ERROR_OK) + assert(target->state == TARGET_HALTED); + + if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK) 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; + if (register_write_abstract(target, GDB_REGNO_S0, value) != ERROR_OK) + return ERROR_FAIL; + if (riscv_save_register(target, GDB_REGNO_S1) != ERROR_OK) + return ERROR_FAIL; + + struct riscv_program program; + riscv_program_init(&program, target); + 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; + + 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) +static int csr_write_progbuf(struct target *target, enum gdb_regno number, + riscv_reg_t value) { - int result = register_read_abstract(target, value, number, - register_size(target, number)); + assert(target->state == TARGET_HALTED); + assert(number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095); - if (result != ERROR_OK && - has_sufficient_progbuf(target, 2) && - number > GDB_REGNO_XPR31) { - struct riscv_program program; - riscv_program_init(&program, target); + if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + if (register_write_abstract(target, GDB_REGNO_S0, value) != ERROR_OK) + return ERROR_FAIL; - scratch_mem_t scratch; - bool use_scratch = false; + struct riscv_program program; + riscv_program_init(&program, target); + if (riscv_program_csrw(&program, S0, number) != ERROR_OK) + return ERROR_FAIL; - riscv_reg_t s0; - if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK) - return ERROR_FAIL; + return riscv_program_exec(&program, target); +} - /* Write program to move data into s0. */ +/** + * 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) +{ + assert(target->state == TARGET_HALTED); + + if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) + return fpr_write_progbuf(target, number, value); + else if (number == GDB_REGNO_VTYPE) + return vtype_write_progbuf(target, value); + else if (number == GDB_REGNO_VL) + return vl_write_progbuf(target, value); + else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) + return csr_write_progbuf(target, number, value); + + LOG_TARGET_ERROR(target, "Unexpected write to %s via program buffer.", + gdb_regno_name(target, number)); + return ERROR_FAIL; +} - uint64_t mstatus; - if (prep_for_register_access(target, &mstatus, number) != ERROR_OK) - return ERROR_FAIL; +/** + * 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) +{ + LOG_TARGET_DEBUG(target, "Writing 0x%" PRIx64 " to %s", value, + gdb_regno_name(target, number)); - 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 (target->state != TARGET_HALTED) + return register_write_abstract(target, number, value); - if (scratch_reserve(target, &scratch, &program, 8) != ERROR_OK) - return ERROR_FAIL; - use_scratch = true; + riscv_reg_t mstatus; + if (prep_for_register_access(target, &mstatus, number) != ERROR_OK) + return ERROR_FAIL; - 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; - } + int result = register_write_abstract(target, number, value); - /* Execute program. */ - result = riscv_program_exec(&program, target); - /* Don't message on error. Probably the register doesn't exist. */ + if (result != ERROR_OK && target->state == TARGET_HALTED) + result = register_write_progbuf(target, number, value); - 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; - } + if (cleanup_after_register_access(target, mstatus, number) != ERROR_OK) + return ERROR_FAIL; - 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(target, number), + value); - /* Restore S0. */ - if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK) - return ERROR_FAIL; - } + return result; +} - if (result == ERROR_OK) { - LOG_DEBUG("{%d} %s = 0x%" PRIx64, riscv_current_hartid(target), - gdb_regno_name(number), *value); - } +/** 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(target, number)); + + if (target->state != TARGET_HALTED) + return register_read_abstract(target, value, number); + + riscv_reg_t mstatus; + + if (prep_for_register_access(target, &mstatus, number) != ERROR_OK) + return ERROR_FAIL; + + int result = register_read_abstract(target, value, number); + + 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(target, number), + *value); return result; } @@ -1504,7 +1710,7 @@ static int wait_for_authbusy(struct target *target, uint32_t *dmstatus) if (!get_field(value, DM_DMSTATUS_AUTHBUSY)) break; if (time(NULL) - start > riscv_command_timeout_sec) { - LOG_ERROR("Timed out after %ds waiting for authbusy to go low (dmstatus=0x%x). " + LOG_TARGET_ERROR(target, "Timed out after %ds waiting for authbusy to go low (dmstatus=0x%x). " "Increase the timeout with riscv set_command_timeout_sec.", riscv_command_timeout_sec, value); @@ -1515,11 +1721,101 @@ 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; + + RISCV_INFO(r); + 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, r->riscv_ebreakm); + dcsr = set_field(dcsr, CSR_DCSR_EBREAKS, r->riscv_ebreaks && riscv_supports_extension(target, 'S')); + dcsr = set_field(dcsr, CSR_DCSR_EBREAKU, r->riscv_ebreaku && riscv_supports_extension(target, 'U')); + dcsr = set_field(dcsr, CSR_DCSR_EBREAKVS, r->riscv_ebreaku && riscv_supports_extension(target, 'H')); + dcsr = set_field(dcsr, CSR_DCSR_EBREAKVU, r->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) { - LOG_DEBUG("riscv_deinit_target()"); + LOG_TARGET_DEBUG(target, "Deinitializing target."); struct riscv_info *info = target->arch_info; if (!info) return; @@ -1529,82 +1825,90 @@ 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 (dm_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 (dm_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; } static int examine(struct target *target) { + /* We reset target state in case if something goes wrong during examine: + * DTM/DM scans could fail or hart may fail to halt. */ + target->state = TARGET_UNKNOWN; + target->debug_reason = DBG_REASON_UNDEFINED; + /* Don't need to select dbus, since the first thing we do is read dtmcontrol. */ + LOG_TARGET_DEBUG(target, "dbgbase=0x%x", target->dbgbase); - uint32_t dtmcontrol = dtmcontrol_scan(target, 0); - LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol); - LOG_DEBUG(" dmireset=%d", get_field(dtmcontrol, DTM_DTMCS_DMIRESET)); - LOG_DEBUG(" idle=%d", get_field(dtmcontrol, DTM_DTMCS_IDLE)); - LOG_DEBUG(" dmistat=%d", get_field(dtmcontrol, DTM_DTMCS_DMISTAT)); - LOG_DEBUG(" abits=%d", get_field(dtmcontrol, DTM_DTMCS_ABITS)); - LOG_DEBUG(" version=%d", get_field(dtmcontrol, DTM_DTMCS_VERSION)); - if (dtmcontrol == 0) { - LOG_ERROR("dtmcontrol is 0. Check JTAG connectivity/board power."); + uint32_t dtmcontrol; + if (dtmcontrol_scan(target, 0, &dtmcontrol) != ERROR_OK || dtmcontrol == 0) { + LOG_TARGET_ERROR(target, "Could not scan dtmcontrol. Check JTAG connectivity/board power."); return ERROR_FAIL; } + + LOG_TARGET_DEBUG(target, "dtmcontrol=0x%x", dtmcontrol); + LOG_TARGET_DEBUG(target, " dmireset=%d", get_field(dtmcontrol, DTM_DTMCS_DMIRESET)); + LOG_TARGET_DEBUG(target, " idle=%d", get_field(dtmcontrol, DTM_DTMCS_IDLE)); + LOG_TARGET_DEBUG(target, " dmistat=%d", get_field(dtmcontrol, DTM_DTMCS_DMISTAT)); + LOG_TARGET_DEBUG(target, " abits=%d", get_field(dtmcontrol, DTM_DTMCS_ABITS)); + LOG_TARGET_DEBUG(target, " version=%d", get_field(dtmcontrol, DTM_DTMCS_VERSION)); + if (get_field(dtmcontrol, DTM_DTMCS_VERSION) != 1) { - LOG_ERROR("Unsupported DTM version %d. (dtmcontrol=0x%x)", + LOG_TARGET_ERROR(target, "Unsupported DTM version %d. (dtmcontrol=0x%x)", get_field(dtmcontrol, DTM_DTMCS_VERSION), dtmcontrol); return ERROR_FAIL; } riscv013_info_t *info = get_info(target); - /* TODO: This won't be true if there are multiple DMs. */ + info->index = target->coreid; info->abits = get_field(dtmcontrol, DTM_DTMCS_ABITS); info->dtmcs_idle = get_field(dtmcontrol, DTM_DTMCS_IDLE); + if (!check_dbgbase_exists(target)) { + LOG_TARGET_ERROR(target, "Could not find debug module with DMI base address (dbgbase) = 0x%x", target->dbgbase); + return ERROR_FAIL; + } + /* Reset the Debug Module. */ dm013_info_t *dm = get_dm(target); if (!dm) return ERROR_FAIL; if (!dm->was_reset) { - dmi_write(target, DM_DMCONTROL, 0); - dmi_write(target, DM_DMCONTROL, DM_DMCONTROL_DMACTIVE); + dm_write(target, DM_DMCONTROL, 0); + dm_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_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) + if (dm_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", + LOG_TARGET_ERROR(target, "Debug Module did not become active. dmcontrol=0x%x", dmcontrol); return ERROR_FAIL; } @@ -1614,7 +1918,7 @@ static int examine(struct target *target) uint32_t dmstatus; if (dmstatus_read(target, &dmstatus, false) != ERROR_OK) return ERROR_FAIL; - LOG_DEBUG("dmstatus: 0x%08x", dmstatus); + LOG_TARGET_DEBUG(target, "dmstatus: 0x%08x", dmstatus); int dmstatus_version = get_field(dmstatus, DM_DMSTATUS_VERSION); if (dmstatus_version != 2 && dmstatus_version != 3) { /* Error was already printed out in dmstatus_read(). */ @@ -1630,10 +1934,10 @@ static int examine(struct target *target) info->hartsellen++; hartsel >>= 1; } - LOG_DEBUG("hartsellen=%d", info->hartsellen); + LOG_TARGET_DEBUG(target, "hartsellen=%d", info->hartsellen); uint32_t hartinfo; - if (dmi_read(target, &hartinfo, DM_HARTINFO) != ERROR_OK) + if (dm_read(target, &hartinfo, DM_HARTINFO) != ERROR_OK) return ERROR_FAIL; info->datasize = get_field(hartinfo, DM_HARTINFO_DATASIZE); @@ -1641,40 +1945,37 @@ static int examine(struct target *target) info->dataaddr = get_field(hartinfo, DM_HARTINFO_DATAADDR); if (!get_field(dmstatus, DM_DMSTATUS_AUTHENTICATED)) { - LOG_ERROR("Debugger is not authenticated to target Debug Module. " + LOG_TARGET_ERROR(target, "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) + if (dm_read(target, &info->sbcs, DM_SBCS) != ERROR_OK) return ERROR_FAIL; /* Check that abstract data registers are accessible. */ uint32_t abstractcs; - if (dmi_read(target, &abstractcs, DM_ABSTRACTCS) != ERROR_OK) + if (dm_read(target, &abstractcs, DM_ABSTRACTCS) != ERROR_OK) return ERROR_FAIL; 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_TARGET_INFO(target, "datacount=%d progbufsize=%d", + info->datacount, info->progbufsize); RISCV_INFO(r); r->impebreak = get_field(dmstatus, DM_DMSTATUS_IMPEBREAK); if (!has_sufficient_progbuf(target, 2)) { - LOG_WARNING("We won't be able to execute fence instructions on this " + LOG_TARGET_WARNING(target, "We won't be able to execute fence instructions on this " "target. Memory may not always appear consistent. " "(progbufsize=%d, impebreak=%d)", info->progbufsize, r->impebreak); } if (info->progbufsize < 4 && riscv_enable_virtual) { - LOG_ERROR("set_enable_virtual is not available on this target. It " + LOG_TARGET_ERROR(target, "set_enable_virtual is not available on this target. It " "requires a program buffer size of at least 4. (progbufsize=%d) " "Use `riscv set_enable_virtual off` to continue." , info->progbufsize); @@ -1683,8 +1984,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; @@ -1695,107 +1995,158 @@ static int examine(struct target *target) dm->hart_count = i + 1; if (get_field(s, DM_DMSTATUS_ANYHAVERESET)) - dmi_write(target, DM_DMCONTROL, - set_hartsel(DM_DMCONTROL_DMACTIVE | DM_DMCONTROL_ACKHAVERESET, i)); + dm_write(target, DM_DMCONTROL, + set_dmcontrol_hartsel(DM_DMCONTROL_DMACTIVE | DM_DMCONTROL_ACKHAVERESET, i)); } - LOG_DEBUG("Detected %d harts.", dm->hart_count); + LOG_TARGET_DEBUG(target, "Detected %d harts.", dm->hart_count); } - r->current_hartid = target->coreid; - - if (dm->hart_count == 0) { - LOG_ERROR("No harts found!"); + if (dm->hart_count <= 0) { + LOG_TARGET_ERROR(target, "No harts found!"); return ERROR_FAIL; } /* 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); - if (!halted) { + enum riscv_hart_state state_at_examine_start; + if (riscv_get_hart_state(target, &state_at_examine_start) != ERROR_OK) + return ERROR_FAIL; + const bool hart_halted_at_examine_start = state_at_examine_start == RISCV_STATE_HALTED; + if (!hart_halted_at_examine_start) { + 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 %s", + info->index, __func__); return ERROR_FAIL; } } + target->state = TARGET_HALTED; + target->debug_reason = hart_halted_at_examine_start ? DBG_REASON_UNDEFINED : 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 (!halted) - riscv013_step_or_resume_current_hart(target, false, false); + if (set_dcsr_ebreak(target, false) != ERROR_OK) + return ERROR_FAIL; - target_set_examined(target); + if (state_at_examine_start == RISCV_STATE_RUNNING) { + riscv013_step_or_resume_current_hart(target, false); + target->state = TARGET_RUNNING; + target->debug_reason = DBG_REASON_NOTHALTED; + } else if (state_at_examine_start == RISCV_STATE_HALTED) { + target->state = TARGET_HALTED; + target->debug_reason = DBG_REASON_UNDEFINED; + } 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) - LOG_INFO("Core %d made part of halt group %d.", target->coreid, + if (info->haltgroup_supported) + LOG_TARGET_INFO(target, "Core %d made part of halt group %d.", target->coreid, target->smp); else - LOG_INFO("Core %d could not be made part of halt group %d.", + LOG_TARGET_INFO(target, "Core %d could not be made part of halt group %d.", target->coreid, target->smp); } /* 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; } static int riscv013_authdata_read(struct target *target, uint32_t *value, unsigned int index) { if (index > 0) { - LOG_ERROR("Spec 0.13 only has a single authdata register."); + LOG_TARGET_ERROR(target, "Spec 0.13 only has a single authdata register."); return ERROR_FAIL; } if (wait_for_authbusy(target, NULL) != ERROR_OK) return ERROR_FAIL; - return dmi_read(target, value, DM_AUTHDATA); + return dm_read(target, value, DM_AUTHDATA); } static int riscv013_authdata_write(struct target *target, uint32_t value, unsigned int index) { if (index > 0) { - LOG_ERROR("Spec 0.13 only has a single authdata register."); + LOG_TARGET_ERROR(target, "Spec 0.13 only has a single authdata register."); return ERROR_FAIL; } @@ -1803,21 +2154,21 @@ static int riscv013_authdata_write(struct target *target, uint32_t value, unsign if (wait_for_authbusy(target, &before) != ERROR_OK) return ERROR_FAIL; - dmi_write(target, DM_AUTHDATA, value); + dm_write(target, DM_AUTHDATA, value); if (wait_for_authbusy(target, &after) != ERROR_OK) return ERROR_FAIL; if (!get_field(before, DM_DMSTATUS_AUTHENTICATED) && get_field(after, DM_DMSTATUS_AUTHENTICATED)) { - LOG_INFO("authdata_write resulted in successful authentication"); + LOG_TARGET_INFO(target, "authdata_write resulted in successful authentication"); int result = ERROR_OK; dm013_info_t *dm = get_dm(target); if (!dm) 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; @@ -1865,7 +2216,7 @@ static unsigned riscv013_data_bits(struct target *target) /* No further mem access method to try. */ break; } - LOG_ERROR("Unable to determine supported data bits on this target. Assuming 32 bits."); + LOG_TARGET_ERROR(target, "Unable to determine supported data bits on this target. Assuming 32 bits."); return 32; } @@ -1903,82 +2254,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) - return ERROR_FAIL; + riscv_reg_t mstatus, vtype, vl; + unsigned int debug_vl, debug_vsew; - uint64_t mstatus; - if (prep_for_register_access(target, &mstatus, regno) != ERROR_OK) + if (prep_for_vector_access(target, &mstatus, &vtype, &vl, + &debug_vl, &debug_vsew) != ERROR_OK) return ERROR_FAIL; - uint64_t vtype, vl; - unsigned debug_vl; - if (prep_for_vector_access(target, &vtype, &vl, &debug_vl) != ERROR_OK) + if (riscv_save_register(target, GDB_REGNO_S0) != 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 @@ -1987,69 +2365,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(target, 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) - return ERROR_FAIL; + riscv_reg_t mstatus, vtype, vl; + unsigned int debug_vl, debug_vsew; - uint64_t mstatus; - if (prep_for_register_access(target, &mstatus, regno) != ERROR_OK) + if (prep_for_vector_access(target, &mstatus, &vtype, &vl, + &debug_vl, &debug_vsew) != ERROR_OK) return ERROR_FAIL; - uint64_t vtype, vl; - unsigned debug_vl; - if (prep_for_vector_access(target, &vtype, &vl, &debug_vl) != ERROR_OK) + if (riscv_save_register(target, GDB_REGNO_S0) != 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; @@ -2080,12 +2447,12 @@ static int sb_write_address(struct target *target, target_addr_t address, unsigned int sbasize = get_field(info->sbcs, DM_SBCS_SBASIZE); /* There currently is no support for >64-bit addresses in OpenOCD. */ if (sbasize > 96) - dmi_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS3, 0, false, false); + dm_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS3, 0, false, false); if (sbasize > 64) - dmi_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS2, 0, false, false); + dm_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS2, 0, false, false); if (sbasize > 32) - dmi_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS1, address >> 32, false, false); - return dmi_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS0, address, + dm_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS1, address >> 32, false, false); + return dm_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS0, address, false, ensure_success); } @@ -2131,12 +2498,12 @@ static int sample_memory_bus_v1(struct target *target, RISCV013_INFO(info); unsigned int sbasize = get_field(info->sbcs, DM_SBCS_SBASIZE); if (sbasize > 64) { - LOG_ERROR("Memory sampling is only implemented for sbasize <= 64."); + LOG_TARGET_ERROR(target, "Memory sampling is only implemented for sbasize <= 64."); return ERROR_NOT_IMPLEMENTED; } if (get_field(info->sbcs, DM_SBCS_SBVERSION) != 1) { - LOG_ERROR("Memory sampling is only implemented for SBA version 1."); + LOG_TARGET_ERROR(target, "Memory sampling is only implemented for SBA version 1."); return ERROR_NOT_IMPLEMENTED; } @@ -2174,7 +2541,7 @@ static int sample_memory_bus_v1(struct target *target, for (unsigned int i = 0; i < ARRAY_SIZE(config->bucket); i++) { if (config->bucket[i].enabled) { if (!sba_supports_access(target, config->bucket[i].size_bytes)) { - LOG_ERROR("Hardware does not support SBA access for %d-byte memory sampling.", + LOG_TARGET_ERROR(target, "Hardware does not support SBA access for %d-byte memory sampling.", config->bucket[i].size_bytes); return ERROR_NOT_IMPLEMENTED; } @@ -2184,7 +2551,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_dm_write(batch, DM_SBCS, sbcs_write, true); sbcs = sbcs_write; sbcs_valid = true; } @@ -2193,18 +2560,18 @@ 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_dm_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_dm_write(batch, DM_SBADDRESS0, sbaddress0, true); sbaddress0_valid = true; } if (config->bucket[i].size_bytes > 4) - riscv_batch_add_dmi_read(batch, DM_SBDATA1); - riscv_batch_add_dmi_read(batch, DM_SBDATA0); + riscv_batch_add_dm_read(batch, DM_SBDATA1); + riscv_batch_add_dm_read(batch, DM_SBDATA0); result_bytes += 1 + config->bucket[i].size_bytes; } } @@ -2215,37 +2582,48 @@ 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_dm_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); + dm_write(target, DM_SBCS, sbcs_read | DM_SBCS_SBBUSYERROR | DM_SBCS_SBERROR); riscv_batch_free(batch); continue; } if (get_field(sbcs_read, DM_SBCS_SBERROR)) { /* The memory we're sampling was unreadable, somehow. Give up. */ - dmi_write(target, DM_SBCS, DM_SBCS_SBBUSYERROR | DM_SBCS_SBERROR); + dm_write(target, DM_SBCS, DM_SBCS_SBBUSYERROR | DM_SBCS_SBERROR); riscv_batch_free(batch); 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); @@ -2271,21 +2649,83 @@ 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; + dm_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) { - LOG_DEBUG("init"); + LOG_TARGET_DEBUG(target, "Init."); RISCV_INFO(generic_info); generic_info->get_register = &riscv013_get_register; 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; @@ -2294,18 +2734,25 @@ 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->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; + generic_info->invalidate_cached_debug_buffer = &riscv013_invalidate_cached_debug_buffer; + generic_info->fill_dm_write_u64 = &riscv013_fill_dm_write_u64; + generic_info->fill_dm_read_u64 = &riscv013_fill_dm_read_u64; + generic_info->fill_dm_nop_u64 = &riscv013_fill_dm_nop_u64; generic_info->dmi_write_u64_bits = &riscv013_dmi_write_u64_bits; generic_info->authdata_read = &riscv013_authdata_read; generic_info->authdata_write = &riscv013_authdata_write; generic_info->dmi_read = &dmi_read; generic_info->dmi_write = &dmi_write; + generic_info->dm_read = &dm_read; + generic_info->dm_write = &dm_write; generic_info->read_memory = read_memory; 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) @@ -2338,152 +2785,160 @@ 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 = dm_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 = dm_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 dm_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; + int result; + struct riscv_program program; + + if (has_sufficient_progbuf(target, 3)) { + riscv_program_init(&program, target); + riscv_program_fence_rw_rw(&program); + riscv_program_fence_i(&program); + result = riscv_program_exec(&program, target); + if (result != ERROR_OK) + LOG_TARGET_DEBUG(target, "Unable to execute pre-fence"); + return ERROR_OK; + } + + if (has_sufficient_progbuf(target, 2)) { riscv_program_init(&program, target); riscv_program_fence_i(&program); - riscv_program_fence(&program); - int result = riscv_program_exec(&program, target); + result = riscv_program_exec(&program, target); if (result != ERROR_OK) - LOG_DEBUG("Unable to execute pre-fence"); + LOG_TARGET_DEBUG(target, "Unable to execute fence.i"); + + riscv_program_init(&program, target); + riscv_program_fence_rw_rw(&program); + result = riscv_program_exec(&program, target); + if (result != ERROR_OK) + LOG_TARGET_DEBUG(target, "Unable to execute fence rw, rw"); + return ERROR_OK; } - return ERROR_OK; + return ERROR_FAIL; +} + +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_access(target_addr_t address, uint64_t value, - unsigned size_bytes, bool read) +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; @@ -2501,23 +2956,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 = dm_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; } @@ -2528,11 +2995,11 @@ static target_addr_t sb_read_address(struct target *target) target_addr_t address = 0; uint32_t v; if (sbasize > 32) { - dmi_read(target, &v, DM_SBADDRESS1); + dm_read(target, &v, DM_SBADDRESS1); address |= v; address <<= 32; } - dmi_read(target, &v, DM_SBADDRESS0); + dm_read(target, &v, DM_SBADDRESS0); address |= v; return address; } @@ -2541,12 +3008,12 @@ static int read_sbcs_nonbusy(struct target *target, uint32_t *sbcs) { time_t start = time(NULL); while (1) { - if (dmi_read(target, sbcs, DM_SBCS) != ERROR_OK) + if (dm_read(target, sbcs, DM_SBCS) != ERROR_OK) return ERROR_FAIL; if (!get_field(*sbcs, DM_SBCS_SBBUSY)) return ERROR_OK; if (time(NULL) - start > riscv_command_timeout_sec) { - LOG_ERROR("Timed out after %ds waiting for sbbusy to go low (sbcs=0x%x). " + LOG_TARGET_ERROR(target, "Timed out after %ds waiting for sbbusy to go low (sbcs=0x%x). " "Increase the timeout with riscv set_command_timeout_sec.", riscv_command_timeout_sec, *sbcs); return ERROR_FAIL; @@ -2559,18 +3026,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); @@ -2589,11 +3056,11 @@ static int read_memory_bus_v0(struct target *target, target_addr_t address, uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment) { if (size != increment) { - LOG_ERROR("sba v0 reads only support size==increment"); + LOG_TARGET_ERROR(target, "sba v0 reads only support size==increment"); return ERROR_NOT_IMPLEMENTED; } - LOG_DEBUG("System Bus Access: size: %d\tcount:%d\tstart address: 0x%08" + LOG_TARGET_DEBUG(target, "System Bus Access: size: %d\tcount:%d\tstart address: 0x%08" TARGET_PRIxADDR, size, count, address); uint8_t *t_buffer = buffer; riscv_addr_t cur_addr = address; @@ -2609,19 +3076,19 @@ static int read_memory_bus_v0(struct target *target, target_addr_t address, /* ww favorise one off reading if there is an issue */ if (count == 1) { for (uint32_t i = 0; i < count; i++) { - if (dmi_read(target, &access, DM_SBCS) != ERROR_OK) + if (dm_read(target, &access, DM_SBCS) != ERROR_OK) return ERROR_FAIL; - dmi_write(target, DM_SBADDRESS0, cur_addr); + dm_write(target, DM_SBADDRESS0, cur_addr); /* size/2 matching the bit access of the spec 0.13 */ access = set_field(access, DM_SBCS_SBACCESS, size/2); access = set_field(access, DM_SBCS_SBSINGLEREAD, 1); - LOG_DEBUG("\r\nread_memory: sab: access: 0x%08x", access); - dmi_write(target, DM_SBCS, access); + LOG_TARGET_DEBUG(target, "read_memory: sab: access: 0x%08x", access); + dm_write(target, DM_SBCS, access); /* 3) read */ uint32_t value; - if (dmi_read(target, &value, DM_SBDATA0) != ERROR_OK) + if (dm_read(target, &value, DM_SBDATA0) != ERROR_OK) return ERROR_FAIL; - LOG_DEBUG("\r\nread_memory: sab: value: 0x%08x", value); + LOG_TARGET_DEBUG(target, "read_memory: sab: value: 0x%08x", value); buf_set_u32(t_buffer, 0, 8 * size, value); t_buffer += size; cur_addr += size; @@ -2630,26 +3097,26 @@ static int read_memory_bus_v0(struct target *target, target_addr_t address, } /* has to be the same size if we want to read a block */ - LOG_DEBUG("reading block until final address 0x%" PRIx64, fin_addr); - if (dmi_read(target, &access, DM_SBCS) != ERROR_OK) + LOG_TARGET_DEBUG(target, "Reading block until final address 0x%" PRIx64, fin_addr); + if (dm_read(target, &access, DM_SBCS) != ERROR_OK) return ERROR_FAIL; /* set current address */ - dmi_write(target, DM_SBADDRESS0, cur_addr); + dm_write(target, DM_SBADDRESS0, cur_addr); /* 2) write sbaccess=2, sbsingleread,sbautoread,sbautoincrement * size/2 matching the bit access of the spec 0.13 */ access = set_field(access, DM_SBCS_SBACCESS, size/2); access = set_field(access, DM_SBCS_SBAUTOREAD, 1); access = set_field(access, DM_SBCS_SBSINGLEREAD, 1); access = set_field(access, DM_SBCS_SBAUTOINCREMENT, 1); - LOG_DEBUG("\r\naccess: 0x%08x", access); - dmi_write(target, DM_SBCS, access); + LOG_TARGET_DEBUG(target, "access: 0x%08x", access); + dm_write(target, DM_SBCS, access); while (cur_addr < fin_addr) { - LOG_DEBUG("\r\nsab:autoincrement: \r\n size: %d\tcount:%d\taddress: 0x%08" + LOG_TARGET_DEBUG(target, "sab:autoincrement:\r\n\tsize: %d\tcount:%d\taddress: 0x%08" PRIx64, size, count, cur_addr); /* read */ uint32_t value; - if (dmi_read(target, &value, DM_SBDATA0) != ERROR_OK) + if (dm_read(target, &value, DM_SBDATA0) != ERROR_OK) return ERROR_FAIL; buf_set_u32(t_buffer, 0, 8 * size, value); cur_addr += size; @@ -2657,15 +3124,15 @@ static int read_memory_bus_v0(struct target *target, target_addr_t address, /* if we are reaching last address, we must clear autoread */ if (cur_addr == fin_addr && count != 1) { - dmi_write(target, DM_SBCS, 0); - if (dmi_read(target, &value, DM_SBDATA0) != ERROR_OK) + dm_write(target, DM_SBCS, 0); + if (dm_read(target, &value, DM_SBDATA0) != ERROR_OK) return ERROR_FAIL; buf_set_u32(t_buffer, 0, 8 * size, value); } } uint32_t sbcs; - if (dmi_read(target, &sbcs, DM_SBCS) != ERROR_OK) + if (dm_read(target, &sbcs, DM_SBCS) != ERROR_OK) return ERROR_FAIL; return ERROR_OK; @@ -2678,13 +3145,13 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address, uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment) { if (increment != size && increment != 0) { - LOG_ERROR("sba v1 reads only support increment of size or 0"); + LOG_TARGET_ERROR(target, "sba v1 reads only support increment of size or 0"); return ERROR_NOT_IMPLEMENTED; } RISCV013_INFO(info); target_addr_t next_address = address; - target_addr_t end_address = address + count * size; + target_addr_t end_address = address + (increment ? count : 1) * size; while (next_address < end_address) { uint32_t sbcs_write = set_field(0, DM_SBCS_SBREADONADDR, 1); @@ -2693,7 +3160,7 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address, sbcs_write = set_field(sbcs_write, DM_SBCS_SBAUTOINCREMENT, 1); if (count > 1) sbcs_write = set_field(sbcs_write, DM_SBCS_SBREADONDATA, count > 1); - if (dmi_write(target, DM_SBCS, sbcs_write) != ERROR_OK) + if (dm_write(target, DM_SBCS, sbcs_write) != ERROR_OK) return ERROR_FAIL; /* This address write will trigger the first read. */ @@ -2703,7 +3170,7 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address, if (info->bus_master_read_delay) { jtag_add_runtest(info->bus_master_read_delay, TAP_IDLE); if (jtag_execute_queue() != ERROR_OK) { - LOG_ERROR("Failed to scan idle sequence"); + LOG_TARGET_ERROR(target, "Failed to scan idle sequence"); return ERROR_FAIL; } } @@ -2712,20 +3179,22 @@ 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; + uint32_t buffer_offset = 0; + 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) { - LOG_ERROR("DMI keeps being busy in while reading memory just past " TARGET_ADDR_FMT, - next_read); + LOG_TARGET_ERROR(target, "DMI keeps being busy in while reading memory" + " just past " TARGET_ADDR_FMT, next_read); 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); @@ -2735,24 +3204,28 @@ 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 + buffer_offset, 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 * increment + next_read_j * 4; + buffer_offset = 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) { - LOG_ERROR("DMI keeps being busy in while reading memory just past " TARGET_ADDR_FMT, - next_read); + LOG_TARGET_ERROR(target, "DMI keeps being busy in while reading memory" + " just past " TARGET_ADDR_FMT, 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) @@ -2760,8 +3233,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 + buffer_offset, 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." */ @@ -2769,14 +3242,14 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address, return ERROR_FAIL; sbcs_write = set_field(sbcs_write, DM_SBCS_SBREADONDATA, 0); - if (dmi_write(target, DM_SBCS, sbcs_write) != ERROR_OK) + if (dm_write(target, DM_SBCS, sbcs_write) != ERROR_OK) return ERROR_FAIL; } /* Read the last word, after we disabled sbreadondata if necessary. */ if (!get_field(sbcs_read, DM_SBCS_SBERROR) && !get_field(sbcs_read, DM_SBCS_SBBUSYERROR)) { - if (read_memory_bus_word(target, address + (count - 1) * size, size, + if (read_memory_bus_word(target, address + (count - 1) * increment, size, buffer + (count - 1) * size) != ERROR_OK) return ERROR_FAIL; @@ -2786,7 +3259,7 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address, if (get_field(sbcs_read, DM_SBCS_SBBUSYERROR)) { /* We read while the target was busy. Slow down and try again. */ - if (dmi_write(target, DM_SBCS, sbcs_read | DM_SBCS_SBBUSYERROR) != ERROR_OK) + if (dm_write(target, DM_SBCS, sbcs_read | DM_SBCS_SBBUSYERROR) != ERROR_OK) return ERROR_FAIL; next_address = sb_read_address(target); info->bus_master_read_delay += info->bus_master_read_delay / 10 + 1; @@ -2799,7 +3272,7 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address, } else { /* Some error indicating the bus access failed, but not because of * something we did wrong. */ - if (dmi_write(target, DM_SBCS, DM_SBCS_SBERROR) != ERROR_OK) + if (dm_write(target, DM_SBCS, DM_SBCS_SBERROR) != ERROR_OK) return ERROR_FAIL; return ERROR_FAIL; } @@ -2808,7 +3281,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; @@ -2817,7 +3290,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"); @@ -2838,43 +3311,43 @@ static void log_mem_access_result(struct target *target, bool success, int metho } if (warn) - LOG_WARNING("%s", msg); + LOG_TARGET_WARNING(target, "%s", msg); else - LOG_DEBUG("%s", msg); + LOG_TARGET_DEBUG(target, "%s", msg); } 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"); + LOG_TARGET_DEBUG(target, "Skipping mem %s via progbuf - insufficient progbuf size.", + 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"); + LOG_TARGET_DEBUG(target, "Skipping mem %s via progbuf - target not halted.", + 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); + LOG_TARGET_DEBUG(target, "Skipping mem %s via progbuf - XLEN (%d) is too short for %d-bit memory access.", + 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"); + LOG_TARGET_DEBUG(target, "Skipping mem %s via progbuf - unsupported size.", + 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)); + LOG_TARGET_DEBUG(target, "Skipping mem %s via progbuf - progbuf only supports %u-bit address.", + is_read ? "read" : "write", riscv_xlen(target)); *skip_reason = "skipped (too large address)"; return true; } @@ -2883,26 +3356,26 @@ 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"); + LOG_TARGET_DEBUG(target, "Skipping mem %s via system bus - unsupported size.", + 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); + LOG_TARGET_DEBUG(target, "Skipping mem %s via system bus - sba only supports %u-bit address.", + 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)) { - LOG_DEBUG("Skipping mem read via system bus - " + if (is_read && increment != size && (get_field(info->sbcs, DM_SBCS_SBVERSION) == 0 || increment != 0)) { + LOG_TARGET_DEBUG(target, "Skipping mem read via system bus - " "sba reads only support size==increment or also size==0 for sba v1."); *skip_reason = "skipped (unsupported increment)"; return true; @@ -2912,26 +3385,26 @@ 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); if (size > 8) { /* 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); + LOG_TARGET_DEBUG(target, "Skipping mem %s via abstract access - unsupported size: %d bits", + 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)); + LOG_TARGET_DEBUG(target, "Skipping mem %s via abstract access - abstract access only supports %u-bit address.", + is_read ? "read" : "write", riscv_xlen(target)); *skip_reason = "skipped (too large address)"; return true; } - if (read && size != increment) { - LOG_ERROR("Skipping mem read via abstract access - " + if (is_read && size != increment) { + LOG_TARGET_ERROR(target, "Skipping mem read via abstract access - " "abstract command reads only support size==increment."); *skip_reason = "skipped (unsupported increment)"; return true; @@ -2953,7 +3426,7 @@ static int read_memory_abstract(struct target *target, target_addr_t address, int result = ERROR_OK; bool use_aampostincrement = info->has_aampostincrement != YNM_NO; - LOG_DEBUG("reading %d words of %d bytes from 0x%" TARGET_PRIxADDR, count, + LOG_TARGET_DEBUG(target, "Reading %d words of %d bytes from 0x%" TARGET_PRIxADDR, count, size, address); memset(buffer, 0, count * size); @@ -2974,7 +3447,7 @@ static int read_memory_abstract(struct target *target, target_addr_t address, /* Set arg1 to the address: address + c * size */ result = write_abstract_arg(target, 1, address + c * size, riscv_xlen(target)); if (result != ERROR_OK) { - LOG_ERROR("Failed to write arg1 during read_memory_abstract()."); + LOG_TARGET_ERROR(target, "Failed to write arg1."); return result; } } @@ -2987,10 +3460,10 @@ static int read_memory_abstract(struct target *target, target_addr_t address, /* Safety: double-check that the address was really auto-incremented */ riscv_reg_t new_address = read_abstract_arg(target, 1, riscv_xlen(target)); if (new_address == address + size) { - LOG_DEBUG("aampostincrement is supported on this target."); + LOG_TARGET_DEBUG(target, "aampostincrement is supported on this target."); info->has_aampostincrement = YNM_YES; } else { - LOG_WARNING("Buggy aampostincrement! Address not incremented correctly."); + LOG_TARGET_WARNING(target, "Buggy aampostincrement! Address not incremented correctly."); info->has_aampostincrement = YNM_NO; } } else { @@ -2998,7 +3471,7 @@ static int read_memory_abstract(struct target *target, target_addr_t address, command = access_memory_command(target, false, width, false, false); result = execute_abstract_command(target, command); if (result == ERROR_OK) { - LOG_DEBUG("aampostincrement is not supported on this target."); + LOG_TARGET_DEBUG(target, "aampostincrement is not supported on this target."); info->has_aampostincrement = YNM_NO; } } @@ -3031,7 +3504,7 @@ static int write_memory_abstract(struct target *target, target_addr_t address, int result = ERROR_OK; bool use_aampostincrement = info->has_aampostincrement != YNM_NO; - LOG_DEBUG("writing %d words of %d bytes from 0x%" TARGET_PRIxADDR, count, + LOG_TARGET_DEBUG(target, "writing %d words of %d bytes from 0x%" TARGET_PRIxADDR, count, size, address); /* Convert the size (bytes) to width (bits) */ @@ -3048,7 +3521,7 @@ static int write_memory_abstract(struct target *target, target_addr_t address, riscv_reg_t value = buf_get_u64(p, 0, 8 * size); result = write_abstract_arg(target, 0, value, riscv_xlen(target)); if (result != ERROR_OK) { - LOG_ERROR("Failed to write arg0 during write_memory_abstract()."); + LOG_TARGET_ERROR(target, "Failed to write arg0."); return result; } @@ -3057,7 +3530,7 @@ static int write_memory_abstract(struct target *target, target_addr_t address, /* Set arg1 to the address: address + c * size */ result = write_abstract_arg(target, 1, address + c * size, riscv_xlen(target)); if (result != ERROR_OK) { - LOG_ERROR("Failed to write arg1 during write_memory_abstract()."); + LOG_TARGET_ERROR(target, "Failed to write arg1."); return result; } } @@ -3070,10 +3543,10 @@ static int write_memory_abstract(struct target *target, target_addr_t address, /* Safety: double-check that the address was really auto-incremented */ riscv_reg_t new_address = read_abstract_arg(target, 1, riscv_xlen(target)); if (new_address == address + size) { - LOG_DEBUG("aampostincrement is supported on this target."); + LOG_TARGET_DEBUG(target, "aampostincrement is supported on this target."); info->has_aampostincrement = YNM_YES; } else { - LOG_WARNING("Buggy aampostincrement! Address not incremented correctly."); + LOG_TARGET_WARNING(target, "Buggy aampostincrement! Address not incremented correctly."); info->has_aampostincrement = YNM_NO; } } else { @@ -3081,7 +3554,7 @@ static int write_memory_abstract(struct target *target, target_addr_t address, command = access_memory_command(target, false, width, false, true); result = execute_abstract_command(target, command); if (result == ERROR_OK) { - LOG_DEBUG("aampostincrement is not supported on this target."); + LOG_TARGET_DEBUG(target, "aampostincrement is not supported on this target."); info->has_aampostincrement = YNM_NO; } } @@ -3099,315 +3572,514 @@ static int write_memory_abstract(struct target *target, target_addr_t address, } /** - * Read the requested memory, taking care to execute every read exactly once, - * even if cmderr=busy is encountered. + * This function is used to start the memory-reading pipeline. + * The pipeline looks like this: + * memory -> s1 -> dm_data[0:1] -> debugger + * Prior to calling it, the program buffer should contain the appropriate + * program. + * This function sets DM_ABSTRACTAUTO_AUTOEXECDATA to trigger second stage of the + * pipeline (s1 -> dm_data[0:1]) whenever dm_data is read. */ -static int read_memory_progbuf_inner(struct target *target, target_addr_t address, - uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment) +static int read_memory_progbuf_inner_startup(struct target *target, + target_addr_t address, uint32_t increment, uint32_t index) { RISCV013_INFO(info); + /* s0 holds the next address to read from. + * s1 holds the next data value read. + * a0 is a counter in case increment is 0. + */ + if (register_write_direct(target, GDB_REGNO_S0, address + index * increment) + != ERROR_OK) + return ERROR_FAIL; - int result = ERROR_OK; - - /* Write address to S0. */ - result = register_write_direct(target, GDB_REGNO_S0, address); - if (result != ERROR_OK) - return result; - - if (increment == 0 && - register_write_direct(target, GDB_REGNO_S2, 0) != ERROR_OK) + if (/*is_repeated_read*/ increment == 0 && + register_write_direct(target, GDB_REGNO_A0, index) != ERROR_OK) return ERROR_FAIL; - uint32_t command = access_register_command(target, GDB_REGNO_S1, - riscv_xlen(target), + /* AC_ACCESS_REGISTER_POSTEXEC is used to trigger first stage of the + * pipeline (memory -> s1) whenever this command is executed. + */ + const uint32_t startup_command = access_register_command(target, + GDB_REGNO_S1, riscv_xlen(target), AC_ACCESS_REGISTER_TRANSFER | AC_ACCESS_REGISTER_POSTEXEC); - if (execute_abstract_command(target, command) != ERROR_OK) + if (execute_abstract_command(target, startup_command) != ERROR_OK) return ERROR_FAIL; - /* First read has just triggered. Result is in s1. */ - if (count == 1) { - uint64_t value; - 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); + /* First read has just triggered. Result is in s1. + * dm_data registers contain the previous value of s1 (garbage). + */ + if (dm_write(target, DM_ABSTRACTAUTO, + set_field(0, DM_ABSTRACTAUTO_AUTOEXECDATA, 1)) != ERROR_OK) + return ERROR_FAIL; + + /* Read garbage from dm_data0, which triggers another execution of the + * program. Now dm_data contains the first good result (from s1), + * and s1 the next memory value. + */ + if (dm_read_exec(target, NULL, DM_DATA0) != ERROR_OK) + goto clear_abstractauto_and_fail; + + uint32_t abstractcs; + + if (wait_for_idle(target, &abstractcs) != ERROR_OK) + goto clear_abstractauto_and_fail; + + info->cmderr = get_field(abstractcs, DM_ABSTRACTCS_CMDERR); + switch (info->cmderr) { + case CMDERR_NONE: return ERROR_OK; - } + case CMDERR_BUSY: + LOG_TARGET_ERROR(target, "Unexpected busy error. This is probably a hardware bug."); + /* fall through */ + default: + LOG_TARGET_DEBUG(target, "error when reading memory, cmderr=0x%" PRIx32, + info->cmderr); + riscv013_clear_abstract_error(target); + goto clear_abstractauto_and_fail; + } +clear_abstractauto_and_fail: + dm_write(target, DM_ABSTRACTAUTO, 0); + return ERROR_FAIL; +} - if (dmi_write(target, DM_ABSTRACTAUTO, - 1 << DM_ABSTRACTAUTO_AUTOEXECDATA_OFFSET) != ERROR_OK) - goto error; - /* Read garbage from dmi_data0, which triggers another execution of the - * program. Now dmi_data0 contains the first good result, and s1 the next - * memory value. */ - if (dmi_read_exec(target, NULL, DM_DATA0) != ERROR_OK) - goto error; +struct memory_access_info { + uint8_t *buffer_address; + target_addr_t target_address; + uint32_t element_size; + uint32_t increment; +}; - /* read_addr is the next address that the hart will read from, which is the - * value in s0. */ - unsigned index = 2; - while (index < count) { - riscv_addr_t read_addr = address + index * increment; - LOG_DEBUG("i=%d, count=%d, read_addr=0x%" PRIx64, index, count, read_addr); - /* The pipeline looks like this: - * memory -> s1 -> dm_data0 -> debugger - * Right now: - * s0 contains read_addr - * s1 contains mem[read_addr-size] - * dm_data0 contains[read_addr-size*2] - */ +/** + * This function attempts to restore the pipeline after a busy on abstract + * access. + * Target's state is as follows: + * s0 contains address + index_on_target * increment + * s1 contains mem[address + (index_on_target - 1) * increment] + * dm_data[0:1] contains mem[address + (index_on_target - 2) * increment] + */ +static int read_memory_progbuf_inner_on_ac_busy(struct target *target, + uint32_t start_index, uint32_t elements_to_read, uint32_t *elements_read, + struct memory_access_info access) +{ + increase_ac_busy_delay(target); + riscv013_clear_abstract_error(target); - struct riscv_batch *batch = riscv_batch_alloc(target, 32, - info->dmi_busy_delay + info->ac_busy_delay); - if (!batch) - return ERROR_FAIL; + if (dm_write(target, DM_ABSTRACTAUTO, 0) != ERROR_OK) + return ERROR_FAIL; - unsigned reads = 0; - for (unsigned j = index; j < count; j++) { - if (size > 4) - riscv_batch_add_dmi_read(batch, DM_DATA1); - riscv_batch_add_dmi_read(batch, DM_DATA0); + /* See how far we got by reading s0/a0 */ + uint32_t index_on_target; - reads++; - if (riscv_batch_full(batch)) - break; - } + if (/*is_repeated_read*/ access.increment == 0) { + /* s0 is constant, a0 is incremented by one each execution */ + riscv_reg_t counter; - batch_run(target, batch); + if (register_read_direct(target, &counter, GDB_REGNO_A0) != ERROR_OK) + return ERROR_FAIL; + index_on_target = counter; + } else { + target_addr_t address_on_target; - /* Wait for the target to finish performing the last abstract command, - * and update our copy of cmderr. If we see that DMI is busy here, - * dmi_busy_delay will be incremented. */ - uint32_t abstractcs; - if (dmi_read(target, &abstractcs, DM_ABSTRACTCS) != ERROR_OK) + if (register_read_direct(target, &address_on_target, GDB_REGNO_S0) != ERROR_OK) return ERROR_FAIL; - while (get_field(abstractcs, DM_ABSTRACTCS_BUSY)) - if (dmi_read(target, &abstractcs, DM_ABSTRACTCS) != ERROR_OK) - return ERROR_FAIL; - info->cmderr = get_field(abstractcs, DM_ABSTRACTCS_CMDERR); + index_on_target = (address_on_target - access.target_address) / + access.increment; + } - unsigned next_index; - unsigned ignore_last = 0; - switch (info->cmderr) { - case CMDERR_NONE: - LOG_DEBUG("successful (partial?) memory read"); - next_index = index + reads; - break; - case CMDERR_BUSY: - LOG_DEBUG("memory read resulted in busy response"); + /* According to the spec, if an abstract command fails, one can't make any + * assumptions about dm_data registers, so all the values in the pipeline + * are clobbered now and need to be reread. + */ + const uint32_t min_index_on_target = start_index + 2; + if (index_on_target < min_index_on_target) { + LOG_TARGET_ERROR(target, "Arithmetic does not work correctly on the target"); + return ERROR_FAIL; + } else if (index_on_target == min_index_on_target) { + LOG_TARGET_DEBUG(target, "No forward progress"); + } + const uint32_t next_index = (index_on_target - 2); + *elements_read = next_index - start_index; + LOG_TARGET_WARNING(target, "Re-reading memory from addresses 0x%" + TARGET_PRIxADDR " and 0x%" TARGET_PRIxADDR ".", + access.target_address + access.increment * next_index, + access.target_address + access.increment * (next_index + 1)); + return read_memory_progbuf_inner_startup(target, access.target_address, + access.increment, next_index); +} - increase_ac_busy_delay(target); - riscv013_clear_abstract_error(target); +/** + * This function attempts to restore the pipeline after a dmi busy. + */ +static int read_memory_progbuf_inner_on_dmi_busy(struct target *target, + uint32_t start_index, uint32_t next_start_index, + struct memory_access_info access) +{ + LOG_TARGET_DEBUG(target, "DMI_STATUS_BUSY encountered in batch. Memory read [%" + PRIu32 ", %" PRIu32 ")", start_index, next_start_index); + if (start_index == next_start_index) + LOG_TARGET_DEBUG(target, "No forward progress"); - dmi_write(target, DM_ABSTRACTAUTO, 0); + if (dm_write(target, DM_ABSTRACTAUTO, 0) != ERROR_OK) + return ERROR_FAIL; + return read_memory_progbuf_inner_startup(target, access.target_address, + access.increment, next_start_index); +} - uint32_t dmi_data0, dmi_data1 = 0; - /* This is definitely a good version of the value that we - * attempted to read when we discovered that the target was - * busy. */ - if (dmi_read(target, &dmi_data0, DM_DATA0) != ERROR_OK) { - riscv_batch_free(batch); - goto error; - } - if (size > 4 && dmi_read(target, &dmi_data1, DM_DATA1) != ERROR_OK) { - riscv_batch_free(batch); - goto error; - } +/** + * This function extracts the data from the batch. + */ +static int read_memory_progbuf_inner_extract_batch_data(struct target *target, + const struct riscv_batch *batch, + uint32_t start_index, uint32_t elements_to_read, uint32_t *elements_read, + struct memory_access_info access) +{ + const bool two_reads_per_element = access.element_size > 4; + const uint32_t reads_per_element = (two_reads_per_element ? 2 : 1); + assert(!two_reads_per_element || riscv_xlen(target) == 64); + assert(elements_to_read <= UINT32_MAX / reads_per_element); + const uint32_t nreads = elements_to_read * reads_per_element; + for (uint32_t curr_idx = start_index, read = 0; read < nreads; ++read) { + switch (riscv_batch_get_dmi_read_op(batch, read)) { + case DMI_STATUS_BUSY: + *elements_read = curr_idx - start_index; + return read_memory_progbuf_inner_on_dmi_busy(target, start_index, curr_idx + , access); + case DMI_STATUS_FAILED: + LOG_TARGET_DEBUG(target, + "Batch memory read encountered DMI_STATUS_FAILED on read %" + PRIu32, read); + return ERROR_FAIL; + case DMI_STATUS_SUCCESS: + break; + default: + assert(0); + } + const uint32_t value = riscv_batch_get_dmi_read_data(batch, read); + uint8_t * const curr_buff = access.buffer_address + + curr_idx * access.element_size; + const target_addr_t curr_addr = access.target_address + + curr_idx * access.increment; + const uint32_t size = access.element_size; + + assert(size <= 8); + const bool is_odd_read = read % 2; + + if (two_reads_per_element && !is_odd_read) { + buf_set_u32(curr_buff + 4, 0, (size * 8) - 32, value); + continue; + } + const bool is_second_read = two_reads_per_element; - /* See how far we got, clobbering dmi_data0. */ - if (increment == 0) { - uint64_t counter; - result = register_read_direct(target, &counter, GDB_REGNO_S2); - next_index = counter; - } else { - uint64_t next_read_addr; - result = register_read_direct(target, &next_read_addr, - GDB_REGNO_S0); - next_index = (next_read_addr - address) / increment; - } - if (result != ERROR_OK) { - riscv_batch_free(batch); - goto error; - } + buf_set_u32(curr_buff, 0, is_second_read ? 32 : (size * 8), value); + log_memory_access64(curr_addr, buf_get_u64(curr_buff, 0, size * 8), + size, /*is_read*/ true); + ++curr_idx; + } + *elements_read = elements_to_read; + return ERROR_OK; +} - 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); +/** + * This function reads a batch of elements from memory. + * Prior to calling this function the folowing conditions should be met: + * - Appropriate program loaded to program buffer. + * - DM_ABSTRACTAUTO_AUTOEXECDATA is set. + */ +static int read_memory_progbuf_inner_run_and_process_batch(struct target *target, + struct riscv_batch *batch, struct memory_access_info access, + uint32_t start_index, uint32_t elements_to_read, uint32_t *elements_read) +{ + RISCV013_INFO(info); - /* Restore the command, and execute it. - * Now DM_DATA0 contains the next value just as it would if no - * error had occurred. */ - dmi_write_exec(target, DM_COMMAND, command, true); - next_index++; + if (batch_run(target, batch) != ERROR_OK) + return ERROR_FAIL; - dmi_write(target, DM_ABSTRACTAUTO, - 1 << DM_ABSTRACTAUTO_AUTOEXECDATA_OFFSET); + uint32_t abstractcs; - ignore_last = 1; + if (wait_for_idle(target, &abstractcs) != ERROR_OK) + return ERROR_FAIL; - break; - default: - LOG_DEBUG("error when reading memory, abstractcs=0x%08lx", (long)abstractcs); - riscv013_clear_abstract_error(target); - riscv_batch_free(batch); - result = ERROR_FAIL; - goto error; - } + uint32_t elements_to_extract_from_batch; - /* Now read whatever we got out of the batch. */ - dmi_status_t status = DMI_STATUS_SUCCESS; - unsigned read = 0; - assert(index >= 2); - for (unsigned j = index - 2; j < index + reads; j++) { - assert(j < count); - LOG_DEBUG("index=%d, reads=%d, next_index=%d, ignore_last=%d, j=%d", - index, reads, next_index, ignore_last, j); - if (j + 3 + ignore_last > next_index) - break; + info->cmderr = get_field(abstractcs, DM_ABSTRACTCS_CMDERR); + switch (info->cmderr) { + case CMDERR_NONE: + LOG_TARGET_DEBUG(target, "successful (partial?) memory read [%" + PRIu32 ", %" PRIu32 ")", start_index, start_index + elements_to_read); + elements_to_extract_from_batch = elements_to_read; + break; + case CMDERR_BUSY: + LOG_TARGET_DEBUG(target, "memory read resulted in busy response"); + if (read_memory_progbuf_inner_on_ac_busy(target, start_index, + elements_to_read, &elements_to_extract_from_batch, access) + != ERROR_OK) + return ERROR_FAIL; + break; + default: + LOG_TARGET_DEBUG(target, "error when reading memory, cmderr=0x%" PRIx32, + info->cmderr); + riscv013_clear_abstract_error(target); + return ERROR_FAIL; + } - status = riscv_batch_get_dmi_read_op(batch, read); - uint64_t value = riscv_batch_get_dmi_read_data(batch, read); - read++; - 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 - * cleared in dmi_read(). */ - /* In at least some implementations, we issue a read, and then - * can get busy back when we try to scan out the read result, - * and the actual read value is lost forever. Since this is - * rare in any case, we return error here and rely on our - * caller to reread the entire block. */ - LOG_WARNING("Batch memory read encountered DMI error %d. " - "Falling back on slower reads.", status); - riscv_batch_free(batch); - result = ERROR_FAIL; - goto error; - } - if (size > 4) { - status = riscv_batch_get_dmi_read_op(batch, read); - if (status != DMI_STATUS_SUCCESS) { - LOG_WARNING("Batch memory read encountered DMI error %d. " - "Falling back on slower reads.", status); - riscv_batch_free(batch); - result = ERROR_FAIL; - goto error; - } - value <<= 32; - value |= riscv_batch_get_dmi_read_data(batch, read); - read++; - } - riscv_addr_t offset = j * size; - buf_set_u64(buffer + offset, 0, 8 * size, value); - log_memory_access(address + j * increment, value, size, true); - } + if (read_memory_progbuf_inner_extract_batch_data(target, batch, start_index, + elements_to_extract_from_batch, elements_read, access) != ERROR_OK) + return ERROR_FAIL; - index = next_index; + return ERROR_OK; +} - riscv_batch_free(batch); - } +static uint32_t read_memory_progbuf_inner_fill_batch(struct riscv_batch *batch, + uint32_t count, uint32_t size) +{ + assert(size <= 8); + const uint32_t two_regs_used[] = {DM_DATA1, DM_DATA0}; + const uint32_t one_reg_used[] = {DM_DATA0}; + const uint32_t reads_per_element = size > 4 ? 2 : 1; + const uint32_t * const used_regs = size > 4 ? two_regs_used : one_reg_used; + const uint32_t batch_capacity = riscv_batch_available_scans(batch) / reads_per_element; + const uint32_t end = MIN(batch_capacity, count); + + for (uint32_t j = 0; j < end; ++j) + for (uint32_t i = 0; i < reads_per_element; ++i) + riscv_batch_add_dm_read(batch, used_regs[i]); + return end; +} - dmi_write(target, DM_ABSTRACTAUTO, 0); +static int read_memory_progbuf_inner_try_to_read(struct target *target, + struct memory_access_info access, uint32_t *elements_read, + uint32_t index, uint32_t loop_count) +{ + RISCV013_INFO(info); + 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; - if (count > 1) { - /* Read the penultimate word. */ - uint32_t dmi_data0, dmi_data1 = 0; - if (dmi_read(target, &dmi_data0, DM_DATA0) != ERROR_OK) - return ERROR_FAIL; - if (size > 4 && dmi_read(target, &dmi_data1, DM_DATA1) != ERROR_OK) - 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); + const uint32_t elements_to_read = read_memory_progbuf_inner_fill_batch(batch, + loop_count - index, access.element_size); + + int result = read_memory_progbuf_inner_run_and_process_batch(target, batch, + access, index, elements_to_read, elements_read); + riscv_batch_free(batch); + return result; +} + +/** + * read_memory_progbuf_inner_startup() must be called before calling this function + * with the address argument equal to curr_target_address. + */ +static int read_memory_progbuf_inner_ensure_forward_progress(struct target *target, + struct memory_access_info access, uint32_t start_index) +{ + LOG_TARGET_DEBUG(target, + "Executing one loop iteration to ensure forward progress (index=%" + PRIu32 ")", start_index); + const target_addr_t curr_target_address = access.target_address + + start_index * access.increment; + uint8_t * const curr_buffer_address = access.buffer_address + + start_index * access.element_size; + const struct memory_access_info curr_access = { + .buffer_address = curr_buffer_address, + .target_address = curr_target_address, + .element_size = access.element_size, + .increment = access.increment, + }; + uint32_t elements_read; + if (read_memory_progbuf_inner_try_to_read(target, curr_access, &elements_read, + /*index*/ 0, /*loop_count*/ 1) != ERROR_OK) + return ERROR_FAIL; + + if (elements_read != 1) { + assert(elements_read == 0); + LOG_TARGET_DEBUG(target, "Can not ensure forward progress"); + /* FIXME: Here it would be better to retry the read and fail only if the + * delay is greater then some threshold. + */ + return ERROR_FAIL; } + return ERROR_OK; +} - /* Read the last word. */ - uint64_t value; - 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); +static void set_buffer_and_log_read(struct memory_access_info access, + uint32_t index, uint64_t value) +{ + uint8_t * const buffer = access.buffer_address; + const uint32_t size = access.element_size; + const uint32_t increment = access.increment; + const target_addr_t address = access.target_address; + + assert(size <= 8); + buf_set_u64(buffer + index * size, 0, 8 * size, value); + log_memory_access64(address + index * increment, value, size, + /*is_read*/ true); +} +static int read_word_from_dm_data_regs(struct target *target, + struct memory_access_info access, uint32_t index) +{ + assert(access.element_size <= 8); + const uint64_t value = read_abstract_arg(target, /*index*/ 0, + access.element_size > 4 ? 64 : 32); + set_buffer_and_log_read(access, index, value); return ERROR_OK; +} -error: - dmi_write(target, DM_ABSTRACTAUTO, 0); +static int read_word_from_s1(struct target *target, + struct memory_access_info access, uint32_t index) +{ + uint64_t value; - return result; + if (register_read_direct(target, &value, GDB_REGNO_S1) != ERROR_OK) + return ERROR_FAIL; + set_buffer_and_log_read(access, index, value); + return ERROR_OK; } -/* Only need to save/restore one GPR to read a single word, and the progbuf - * program doesn't need to increment. */ -static int read_memory_progbuf_one(struct target *target, target_addr_t address, - uint32_t size, uint8_t *buffer) +static int riscv_program_load_mprv(struct riscv_program *p, enum gdb_regno d, + enum gdb_regno b, int offset, unsigned int size, bool mprven) { - uint64_t mstatus = 0; - uint64_t mstatus_old = 0; - if (modify_privilege(target, &mstatus, &mstatus_old) != ERROR_OK) + if (mprven && riscv_program_csrrsi(p, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN, + GDB_REGNO_DCSR) != ERROR_OK) + return ERROR_FAIL; + + if (riscv_program_load(p, d, b, offset, size) != ERROR_OK) return ERROR_FAIL; - uint64_t s0; - int result = ERROR_FAIL; + if (mprven && riscv_program_csrrci(p, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN, + GDB_REGNO_DCSR) != ERROR_OK) + return ERROR_FAIL; + + return ERROR_OK; +} - if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK) - goto restore_mstatus; +static int read_memory_progbuf_inner_fill_progbuf(struct target *target, + uint32_t increment, uint32_t size, bool mprven) +{ + const bool is_repeated_read = increment == 0; + + if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + if (riscv_save_register(target, GDB_REGNO_S1) != ERROR_OK) + return ERROR_FAIL; + if (is_repeated_read && riscv_save_register(target, GDB_REGNO_A0) != ERROR_OK) + return ERROR_FAIL; - /* Write the program (load, increment) */ struct riscv_program program; + riscv_program_init(&program, target); - if (riscv_enable_virtual && has_sufficient_progbuf(target, 5) && get_field(mstatus, MSTATUS_MPRV)) - riscv_program_csrrsi(&program, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN, GDB_REGNO_DCSR); - switch (size) { - case 1: - riscv_program_lbr(&program, GDB_REGNO_S0, GDB_REGNO_S0, 0); - break; - case 2: - riscv_program_lhr(&program, GDB_REGNO_S0, GDB_REGNO_S0, 0); - break; - case 4: - riscv_program_lwr(&program, GDB_REGNO_S0, GDB_REGNO_S0, 0); - break; - case 8: - riscv_program_ldr(&program, GDB_REGNO_S0, GDB_REGNO_S0, 0); - break; - default: - LOG_ERROR("Unsupported size: %d", size); - goto restore_mstatus; + if (riscv_program_load_mprv(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0, size, + mprven) != ERROR_OK) + return ERROR_FAIL; + if (is_repeated_read) { + if (riscv_program_addi(&program, GDB_REGNO_A0, GDB_REGNO_A0, 1) + != ERROR_OK) + return ERROR_FAIL; + } else { + if (riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, + increment) + != ERROR_OK) + return ERROR_FAIL; } - 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 (riscv_program_ebreak(&program) != ERROR_OK) + return ERROR_FAIL; + if (riscv_program_write(&program) != ERROR_OK) + return ERROR_FAIL; + + return ERROR_OK; +} + +/** + * Read the requested memory, taking care to minimize the number of reads and + * re-read the data only if `abstract command busy` or `DMI busy` + * is encountered in the process. + */ +static int read_memory_progbuf_inner(struct target *target, + struct memory_access_info access, uint32_t count, bool mprven) +{ + assert(count > 1 && "If count == 1, read_memory_progbuf_inner_one must be called"); + + if (read_memory_progbuf_inner_fill_progbuf(target, access.increment, + access.element_size, mprven) != ERROR_OK) + return ERROR_FAIL; + + if (read_memory_progbuf_inner_startup(target, access.target_address, + access.increment, /*index*/ 0) + != ERROR_OK) + return ERROR_FAIL; + /* The program in program buffer is executed twice during + * read_memory_progbuf_inner_startup(). + * Here: + * dm_data[0:1] == M[address] + * s1 == M[address + increment] + * s0 == address + increment * 2 + * `count - 2` program executions are performed in this loop. + * No need to execute the program any more, since S1 will already contain + * M[address + increment * (count - 1)] and we can read it directly. + */ + const uint32_t loop_count = count - 2; + + for (uint32_t index = 0; index < loop_count;) { + uint32_t elements_read; + if (read_memory_progbuf_inner_try_to_read(target, access, &elements_read, + index, loop_count) != ERROR_OK) { + dm_write(target, DM_ABSTRACTAUTO, 0); + return ERROR_FAIL; + } + if (elements_read == 0) { + if (read_memory_progbuf_inner_ensure_forward_progress(target, access, + index) != ERROR_OK) { + dm_write(target, DM_ABSTRACTAUTO, 0); + return ERROR_FAIL; + } + elements_read = 1; + } + index += elements_read; + assert(index <= loop_count); + } + if (dm_write(target, DM_ABSTRACTAUTO, 0) != ERROR_OK) + return ERROR_FAIL; + + /* Read the penultimate word. */ + if (read_word_from_dm_data_regs(target, access, count - 2) + != ERROR_OK) + return ERROR_FAIL; + /* Read the last word. */ + return read_word_from_s1(target, access, count - 1); +} + +/** + * Only need to save/restore one GPR to read a single word, and the progbuf + * program doesn't need to increment. + */ +static int read_memory_progbuf_inner_one(struct target *target, + struct memory_access_info access, bool mprven) +{ + if (riscv_save_register(target, GDB_REGNO_S1) != ERROR_OK) + return ERROR_FAIL; + + struct riscv_program program; + riscv_program_init(&program, target); + if (riscv_program_load_mprv(&program, GDB_REGNO_S1, GDB_REGNO_S1, 0, + access.element_size, mprven) != ERROR_OK) + return ERROR_FAIL; if (riscv_program_ebreak(&program) != ERROR_OK) - goto restore_mstatus; + return ERROR_FAIL; if (riscv_program_write(&program) != ERROR_OK) - goto restore_mstatus; + return ERROR_FAIL; - /* Write address to S0, and execute buffer. */ - if (write_abstract_arg(target, 0, address, riscv_xlen(target)) != ERROR_OK) - goto restore_mstatus; - uint32_t command = access_register_command(target, GDB_REGNO_S0, + /* Write address to S1, and execute buffer. */ + if (write_abstract_arg(target, 0, access.target_address, riscv_xlen(target)) + != ERROR_OK) + return ERROR_FAIL; + uint32_t command = access_register_command(target, GDB_REGNO_S1, 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; - - uint64_t value; - if (register_read(target, &value, GDB_REGNO_S0) != ERROR_OK) - goto restore_s0; - buf_set_u64(buffer, 0, 8 * size, value); - log_memory_access(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)) - result = ERROR_FAIL; + return ERROR_FAIL; - return result; + return read_word_from_s1(target, access, 0); } /** @@ -3417,15 +4089,16 @@ static int read_memory_progbuf(struct target *target, target_addr_t address, uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment) { if (riscv_xlen(target) < size * 8) { - LOG_ERROR("XLEN (%d) is too short for %d-bit memory read.", - riscv_xlen(target), size * 8); + LOG_TARGET_ERROR(target, "XLEN (%d) is too short for %" + PRIu32 "-bit memory read.", riscv_xlen(target), size * 8); return ERROR_FAIL; } - int result = ERROR_OK; + LOG_TARGET_DEBUG(target, "reading %" PRIu32 " elements of %" PRIu32 + " bytes from 0x%" TARGET_PRIxADDR, count, size, 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); @@ -3434,98 +4107,26 @@ static int read_memory_progbuf(struct target *target, target_addr_t address, if (execute_fence(target) != ERROR_OK) return ERROR_FAIL; - if (count == 1) - return read_memory_progbuf_one(target, address, size, buffer); - uint64_t mstatus = 0; uint64_t mstatus_old = 0; if (modify_privilege(target, &mstatus, &mstatus_old) != ERROR_OK) return ERROR_FAIL; - /* s0 holds the next address to read from - * s1 holds the next data value read - * s2 is a counter in case increment is 0 - */ - uint64_t s0, s1, s2; - if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK) - return ERROR_FAIL; - if (register_read(target, &s1, GDB_REGNO_S1) != ERROR_OK) - return ERROR_FAIL; - if (increment == 0 && register_read(target, &s2, GDB_REGNO_S2) != ERROR_OK) - return ERROR_FAIL; - - /* Write the program (load, increment) */ - struct riscv_program program; - riscv_program_init(&program, target); - if (riscv_enable_virtual && has_sufficient_progbuf(target, 5) && get_field(mstatus, MSTATUS_MPRV)) - riscv_program_csrrsi(&program, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN, GDB_REGNO_DCSR); - - switch (size) { - case 1: - riscv_program_lbr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0); - break; - case 2: - riscv_program_lhr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0); - break; - case 4: - riscv_program_lwr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0); - break; - case 8: - riscv_program_ldr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0); - break; - default: - LOG_ERROR("Unsupported size: %d", size); - return ERROR_FAIL; - } - - 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); - else - riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, increment); + const bool mprven = riscv_enable_virtual && get_field(mstatus, MSTATUS_MPRV); + const struct memory_access_info access = { + .target_address = address, + .increment = increment, + .buffer_address = buffer, + .element_size = size, + }; + int result = (count == 1) ? + read_memory_progbuf_inner_one(target, access, mprven) : + read_memory_progbuf_inner(target, access, count, mprven); - if (riscv_program_ebreak(&program) != ERROR_OK) - return ERROR_FAIL; - if (riscv_program_write(&program) != ERROR_OK) + if (mstatus != mstatus_old && + register_write_direct(target, GDB_REGNO_MSTATUS, mstatus_old) != ERROR_OK) return ERROR_FAIL; - result = read_memory_progbuf_inner(target, address, size, count, buffer, increment); - - if (result != ERROR_OK) { - /* The full read did not succeed, so we will try to read each word individually. */ - /* This will not be fast, but reading outside actual memory is a special case anyway. */ - /* It will make the toolchain happier, especially Eclipse Memory View as it reads ahead. */ - target_addr_t address_i = address; - uint32_t count_i = 1; - uint8_t *buffer_i = buffer; - - for (uint32_t i = 0; i < count; i++, address_i += increment, buffer_i += size) { - /* TODO: This is much slower than it needs to be because we end up - * writing the address to read for every word we read. */ - result = read_memory_progbuf_inner(target, address_i, size, count_i, buffer_i, increment); - - /* The read of a single word failed, so we will just return 0 for that instead */ - if (result != ERROR_OK) { - LOG_DEBUG("error reading single word of %d bytes from 0x%" TARGET_PRIxADDR, - size, address_i); - - buf_set_u64(buffer_i, 0, 8 * size, 0); - } - } - 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)) - return ERROR_FAIL; - return result; } @@ -3536,7 +4137,7 @@ static int read_memory(struct target *target, target_addr_t address, return ERROR_OK; if (size != 1 && size != 2 && size != 4 && size != 8 && size != 16) { - LOG_ERROR("BUG: Unsupported size for memory read: %d", size); + LOG_TARGET_ERROR(target, "BUG: Unsupported size for memory read: %d", size); return ERROR_FAIL; } @@ -3588,8 +4189,8 @@ static int read_memory(struct target *target, target_addr_t address, return ret; } - LOG_ERROR("Target %s: Failed to read memory (addr=0x%" PRIx64 ")", target_name(target), address); - LOG_ERROR(" progbuf=%s, sysbus=%s, abstract=%s", progbuf_result, sysbus_result, abstract_result); + LOG_TARGET_ERROR(target, "Failed to read memory (addr=0x%" PRIx64 ")", address); + LOG_TARGET_ERROR(target, " progbuf=%s, sysbus=%s, abstract=%s", progbuf_result, sysbus_result, abstract_result); return ret; } @@ -3597,9 +4198,9 @@ static int write_memory_bus_v0(struct target *target, target_addr_t address, uint32_t size, uint32_t count, const uint8_t *buffer) { /*1) write sbaddress: for singlewrite and autoincrement, we need to write the address once*/ - LOG_DEBUG("System Bus Access: size: %d\tcount:%d\tstart address: 0x%08" + LOG_TARGET_DEBUG(target, "System Bus Access: size: %d\tcount:%d\tstart address: 0x%08" TARGET_PRIxADDR, size, count, address); - dmi_write(target, DM_SBADDRESS0, address); + dm_write(target, DM_SBADDRESS0, address); int64_t value = 0; int64_t access = 0; riscv_addr_t offset = 0; @@ -3612,10 +4213,10 @@ static int write_memory_bus_v0(struct target *target, target_addr_t address, access = 0; access = set_field(access, DM_SBCS_SBACCESS, size/2); - dmi_write(target, DM_SBCS, access); - LOG_DEBUG("\r\naccess: 0x%08" PRIx64, access); - LOG_DEBUG("\r\nwrite_memory:SAB: ONE OFF: value 0x%08" PRIx64, value); - dmi_write(target, DM_SBDATA0, value); + dm_write(target, DM_SBCS, access); + LOG_TARGET_DEBUG(target, " access: 0x%08" PRIx64, access); + LOG_TARGET_DEBUG(target, " write_memory:SAB: ONE OFF: value 0x%08" PRIx64, value); + dm_write(target, DM_SBDATA0, value); return ERROR_OK; } @@ -3624,8 +4225,8 @@ static int write_memory_bus_v0(struct target *target, target_addr_t address, access = 0; access = set_field(access, DM_SBCS_SBACCESS, size/2); access = set_field(access, DM_SBCS_SBAUTOINCREMENT, 1); - LOG_DEBUG("\r\naccess: 0x%08" PRIx64, access); - dmi_write(target, DM_SBCS, access); + LOG_TARGET_DEBUG(target, " access: 0x%08" PRIx64, access); + dm_write(target, DM_SBCS, access); /*2)set the value according to the size required and write*/ for (riscv_addr_t i = 0; i < count; ++i) { @@ -3635,13 +4236,13 @@ static int write_memory_bus_v0(struct target *target, target_addr_t address, t_buffer = buffer + offset; value = buf_get_u64(t_buffer, 0, 8 * size); - LOG_DEBUG("SAB:autoincrement: expected address: 0x%08x value: 0x%08x" + LOG_TARGET_DEBUG(target, "SAB:autoincrement: expected address: 0x%08x value: 0x%08x" PRIx64, (uint32_t)t_addr, (uint32_t)value); - dmi_write(target, DM_SBDATA0, value); + dm_write(target, DM_SBDATA0, value); } /*reset the autoincrement when finished (something weird is happening if this is not done at the end*/ access = set_field(access, DM_SBCS_SBAUTOINCREMENT, 0); - dmi_write(target, DM_SBCS, access); + dm_write(target, DM_SBCS, access); return ERROR_OK; } @@ -3652,7 +4253,7 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address, RISCV013_INFO(info); uint32_t sbcs = sb_sbaccess(size); sbcs = set_field(sbcs, DM_SBCS_SBAUTOINCREMENT, 1); - dmi_write(target, DM_SBCS, sbcs); + dm_write(target, DM_SBCS, sbcs); target_addr_t next_address = address; target_addr_t end_address = address + count * size; @@ -3661,12 +4262,12 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address, sb_write_address(target, next_address, true); while (next_address < end_address) { - LOG_DEBUG("transferring burst starting at address 0x%" TARGET_PRIxADDR, + LOG_TARGET_DEBUG(target, "Transferring burst starting at address 0x%" TARGET_PRIxADDR, next_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; @@ -3677,35 +4278,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) p[13]) << 8) | - (((uint32_t) p[14]) << 16) | - (((uint32_t) p[15]) << 24)); - - if (size > 8) - riscv_batch_add_dmi_write(batch, DM_SBDATA2, - ((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[5]) << 8) | - (((uint32_t) p[6]) << 16) | - (((uint32_t) p[7]) << 24)); - uint32_t value = p[0]; + 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); + riscv_batch_add_dm_write(batch, DM_SBDATA3, sbvalue[3], false); + } + + if (size > 8) { + sbvalue[2] = ((uint32_t)p[8]) | + (((uint32_t)p[9]) << 8) | + (((uint32_t)p[10]) << 16) | + (((uint32_t)p[11]) << 24); + riscv_batch_add_dm_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); + riscv_batch_add_dm_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_dm_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; } @@ -3718,30 +4326,30 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address, /* Read sbcs value. * At the same time, detect if DMI busy has occurred during the batch write. */ bool dmi_busy_encountered; - if (dmi_op(target, &sbcs, &dmi_busy_encountered, DMI_OP_READ, + if (dm_op(target, &sbcs, &dmi_busy_encountered, DMI_OP_READ, DM_SBCS, 0, false, true) != ERROR_OK) return ERROR_FAIL; if (dmi_busy_encountered) - LOG_DEBUG("DMI busy encountered during system bus write."); + LOG_TARGET_DEBUG(target, "DMI busy encountered during system bus write."); /* Wait until sbbusy goes low */ time_t start = time(NULL); while (get_field(sbcs, DM_SBCS_SBBUSY)) { if (time(NULL) - start > riscv_command_timeout_sec) { - LOG_ERROR("Timed out after %ds waiting for sbbusy to go low (sbcs=0x%x). " + LOG_TARGET_ERROR(target, "Timed out after %ds waiting for sbbusy to go low (sbcs=0x%x). " "Increase the timeout with riscv set_command_timeout_sec.", riscv_command_timeout_sec, sbcs); return ERROR_FAIL; } - if (dmi_read(target, &sbcs, DM_SBCS) != ERROR_OK) + if (dm_read(target, &sbcs, DM_SBCS) != ERROR_OK) return ERROR_FAIL; } if (get_field(sbcs, DM_SBCS_SBBUSYERROR)) { /* We wrote while the target was busy. */ - LOG_DEBUG("Sbbusyerror encountered during system bus write."); + LOG_TARGET_DEBUG(target, "Sbbusyerror encountered during system bus write."); /* Clear the sticky error flag. */ - dmi_write(target, DM_SBCS, sbcs | DM_SBCS_SBBUSYERROR); + dm_write(target, DM_SBCS, sbcs | DM_SBCS_SBBUSYERROR); /* Slow down before trying again. */ info->bus_master_write_delay += info->bus_master_write_delay / 10 + 1; } @@ -3752,7 +4360,7 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address, next_address = sb_read_address(target); if (next_address < address) { /* This should never happen, probably buggy hardware. */ - LOG_DEBUG("unexpected sbaddress=0x%" TARGET_PRIxADDR + LOG_TARGET_DEBUG(target, "unexpected sbaddress=0x%" TARGET_PRIxADDR " - buggy sbautoincrement in hw?", next_address); /* Fail the whole operation. */ return ERROR_FAIL; @@ -3768,16 +4376,16 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address, * (unless sbautoincrement in the HW is buggy). */ target_addr_t sbaddress = sb_read_address(target); - LOG_DEBUG("System bus access failed with sberror=%u (sbaddress=0x%" TARGET_PRIxADDR ")", + LOG_TARGET_DEBUG(target, "System bus access failed with sberror=%u (sbaddress=0x%" TARGET_PRIxADDR ")", sberror, sbaddress); if (sbaddress < address) { /* This should never happen, probably buggy hardware. * Make a note to the user not to trust the sbaddress value. */ - LOG_DEBUG("unexpected sbaddress=0x%" TARGET_PRIxADDR + LOG_TARGET_DEBUG(target, "unexpected sbaddress=0x%" TARGET_PRIxADDR " - buggy sbautoincrement in hw?", next_address); } /* Clear the sticky error flag */ - dmi_write(target, DM_SBCS, DM_SBCS_SBERROR); + dm_write(target, DM_SBCS, DM_SBCS_SBERROR); /* Fail the whole operation */ return ERROR_FAIL; } @@ -3792,12 +4400,12 @@ static int write_memory_progbuf(struct target *target, target_addr_t address, RISCV013_INFO(info); if (riscv_xlen(target) < size * 8) { - LOG_ERROR("XLEN (%d) is too short for %d-bit memory write.", + LOG_TARGET_ERROR(target, "XLEN (%d) is too short for %d-bit memory write.", riscv_xlen(target), size * 8); return ERROR_FAIL; } - LOG_DEBUG("writing %d words of %d bytes to 0x%08lx", count, size, (long)address); + LOG_TARGET_DEBUG(target, "writing %d words of %d bytes to 0x%08lx", count, size, (long)address); select_dmi(target); @@ -3811,10 +4419,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) */ @@ -3837,7 +4444,7 @@ static int write_memory_progbuf(struct target *target, target_addr_t address, riscv_program_sdr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0); break; default: - LOG_ERROR("write_memory_progbuf(): Unsupported size: %d", size); + LOG_TARGET_ERROR(target, "Unsupported size: %d", size); result = ERROR_FAIL; goto error; } @@ -3852,29 +4459,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("transferring burst starting at address 0x%016" PRIx64, + LOG_TARGET_DEBUG(target, "Writing until final address 0x%016" PRIx64, cur_addr + distance); + while (cur_addr - address < distance) { + LOG_TARGET_DEBUG(target, "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) { @@ -3887,8 +4492,8 @@ static int write_memory_progbuf(struct target *target, target_addr_t address, /* Write value. */ if (size > 4) - dmi_write(target, DM_DATA1, value >> 32); - dmi_write(target, DM_DATA0, value); + dm_write(target, DM_DATA1, value >> 32); + dm_write(target, DM_DATA0, value); /* Write and execute command that moves value into S1 and * executes program buffer. */ @@ -3904,14 +4509,18 @@ static int write_memory_progbuf(struct target *target, target_addr_t address, } /* Turn on autoexec */ - dmi_write(target, DM_ABSTRACTAUTO, + dm_write(target, DM_ABSTRACTAUTO, 1 << DM_ABSTRACTAUTO_AUTOEXECDATA_OFFSET); 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_dm_write(batch, DM_DATA1, value >> 32, false); + riscv_batch_add_dm_write(batch, DM_DATA0, value, false); if (riscv_batch_full(batch)) break; } @@ -3928,31 +4537,31 @@ static int write_memory_progbuf(struct target *target, target_addr_t address, uint32_t abstractcs; bool dmi_busy_encountered; - result = dmi_op(target, &abstractcs, &dmi_busy_encountered, + result = dm_op(target, &abstractcs, &dmi_busy_encountered, DMI_OP_READ, DM_ABSTRACTCS, 0, false, true); if (result != ERROR_OK) goto error; while (get_field(abstractcs, DM_ABSTRACTCS_BUSY)) - if (dmi_read(target, &abstractcs, DM_ABSTRACTCS) != ERROR_OK) + if (dm_read(target, &abstractcs, DM_ABSTRACTCS) != ERROR_OK) return ERROR_FAIL; info->cmderr = get_field(abstractcs, DM_ABSTRACTCS_CMDERR); if (info->cmderr == CMDERR_NONE && !dmi_busy_encountered) { - LOG_DEBUG("successful (partial?) memory write"); + LOG_TARGET_DEBUG(target, "Successful (partial?) memory write"); } else if (info->cmderr == CMDERR_BUSY || dmi_busy_encountered) { if (info->cmderr == CMDERR_BUSY) - LOG_DEBUG("Memory write resulted in abstract command busy response."); + LOG_TARGET_DEBUG(target, "Memory write resulted in abstract command busy response."); else if (dmi_busy_encountered) - LOG_DEBUG("Memory write resulted in DMI busy response."); + LOG_TARGET_DEBUG(target, "Memory write resulted in DMI busy response."); riscv013_clear_abstract_error(target); increase_ac_busy_delay(target); - dmi_write(target, DM_ABSTRACTAUTO, 0); + dm_write(target, DM_ABSTRACTAUTO, 0); result = register_read_direct(target, &cur_addr, GDB_REGNO_S0); if (result != ERROR_OK) goto error; setup_needed = true; } else { - LOG_ERROR("error when writing memory, abstractcs=0x%08lx", (long)abstractcs); + LOG_TARGET_ERROR(target, "Error when writing memory, abstractcs=0x%08lx", (long)abstractcs); riscv013_clear_abstract_error(target); result = ERROR_FAIL; goto error; @@ -3960,12 +4569,7 @@ 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; + dm_write(target, DM_ABSTRACTAUTO, 0); /* Restore MSTATUS */ if (mstatus != mstatus_old) @@ -3982,7 +4586,7 @@ static int write_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t count, const uint8_t *buffer) { if (size != 1 && size != 2 && size != 4 && size != 8 && size != 16) { - LOG_ERROR("BUG: Unsupported size for memory write: %d", size); + LOG_TARGET_ERROR(target, "BUG: Unsupported size for memory write: %d", size); return ERROR_FAIL; } @@ -4034,8 +4638,8 @@ static int write_memory(struct target *target, target_addr_t address, return ret; } - LOG_ERROR("Target %s: Failed to write memory (addr=0x%" PRIx64 ")", target_name(target), address); - LOG_ERROR(" progbuf=%s, sysbus=%s, abstract=%s", progbuf_result, sysbus_result, abstract_result); + LOG_TARGET_ERROR(target, "Target %s: Failed to write memory (addr=0x%" PRIx64 ")", target_name(target), address); + LOG_TARGET_ERROR(target, " progbuf=%s, sysbus=%s, abstract=%s", progbuf_result, sysbus_result, abstract_result); return ret; } @@ -4065,137 +4669,116 @@ 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(target, 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); - } else if (rid == GDB_REGNO_PRIV) { - uint64_t dcsr; - /* TODO: move this into riscv.c. */ - result = register_read(target, &dcsr, GDB_REGNO_DCSR); - *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); - if (result != ERROR_OK) - *value = -1; + if (register_read_direct(target, value, rid) != ERROR_OK) { + *value = -1; + return ERROR_FAIL; } - return result; + return ERROR_OK; } -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(target, rid)); - 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; - } - } else if (rid == GDB_REGNO_PRIV) { - uint64_t dcsr; - register_read(target, &dcsr, GDB_REGNO_DCSR); - 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); - } else { - return register_write_direct(target, rid, value); - } + if (dm013_select_target(target) != ERROR_OK) + return ERROR_FAIL; - return ERROR_OK; + return register_write_direct(target, rid, value); } -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 (dm_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); unsigned hawindow_count = (dm->hart_count + 31) / 32; - uint32_t hawindow[hawindow_count]; - - memset(hawindow, 0, sizeof(uint32_t) * hawindow_count); + uint32_t *hawindow = calloc(hawindow_count, sizeof(uint32_t)); + if (!hawindow) + return ERROR_FAIL; 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 int index = info_013->index; + LOG_TARGET_DEBUG(target, "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!"); + free(hawindow); + return ERROR_FAIL; + } else if (total_selected == 1) { + /* Don't use hasel if we only need to talk to one hart. */ + free(hawindow); + return dm013_select_hart(target, selected_index); + } + + if (dm013_select_hart(target, HART_INDEX_MULTIPLE) != ERROR_OK) { + free(hawindow); + return ERROR_FAIL; } for (unsigned i = 0; i < hawindow_count; i++) { - if (dmi_write(target, DM_HAWINDOWSEL, i) != ERROR_OK) + if (dm_write(target, DM_HAWINDOWSEL, i) != ERROR_OK) { + free(hawindow); return ERROR_FAIL; - if (dmi_write(target, DM_HAWINDOW, hawindow[i]) != ERROR_OK) + } + if (dm_write(target, DM_HAWINDOW, hawindow[i]) != ERROR_OK) { + free(hawindow); return ERROR_FAIL; + } } - *use_hasel = true; + free(hawindow); return ERROR_OK; } @@ -4206,72 +4789,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); - 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; + dmcontrol = set_dmcontrol_hartsel(dmcontrol, dm->current_hartid); + dm_write(target, DM_DMCONTROL, dmcontrol); + uint32_t dmstatus; + for (size_t i = 0; i < 256; ++i) { if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) return ERROR_FAIL; - if (dmi_read(target, &dmcontrol, DM_DMCONTROL) != ERROR_OK) + /* 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 (dm_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); + dm_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 (dm_write(target, DM_DMCONTROL, dmcontrol) != ERROR_OK) + return ERROR_FAIL; + dm->current_hartid = info->index; + if (dm_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); } @@ -4280,95 +4896,76 @@ 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 * side effect of clearing any triggers we did not set. */ riscv_enumerate_triggers(target); - LOG_DEBUG("{%d} halted because of trigger", target->coreid); + LOG_TARGET_DEBUG(target, "Coreid: [%d] halted because of trigger", target->coreid); 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_TARGET_ERROR(target, "Unknown DCSR cause field: 0x%" PRIx64, get_field(dcsr, CSR_DCSR_CAUSE)); + LOG_TARGET_ERROR(target, " dcsr=0x%" PRIx32, (uint32_t)dcsr); return RISCV_HALT_UNKNOWN; } -int riscv013_write_debug_buffer(struct target *target, unsigned index, riscv_insn_t data) +static int riscv013_write_debug_buffer(struct target *target, unsigned int index, riscv_insn_t data) { dm013_info_t *dm = get_dm(target); if (!dm) return ERROR_FAIL; if (dm->progbuf_cache[index] != data) { - if (dmi_write(target, DM_PROGBUF0 + index, data) != ERROR_OK) + if (dm_write(target, DM_PROGBUF0 + index, data) != ERROR_OK) return ERROR_FAIL; dm->progbuf_cache[index] = data; } else { - LOG_DEBUG("cache hit for 0x%" PRIx32 " @%d", data, index); + LOG_TARGET_DEBUG(target, "Cache hit for 0x%" PRIx32 " @%d", data, index); } return ERROR_OK; } -riscv_insn_t riscv013_read_debug_buffer(struct target *target, unsigned index) +static riscv_insn_t riscv013_read_debug_buffer(struct target *target, unsigned int index) { uint32_t value; - dmi_read(target, &value, DM_PROGBUF0 + index); + dm_read(target, &value, DM_PROGBUF0 + index); return value; } -int riscv013_execute_debug_buffer(struct target *target) +static 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; +} + +static int riscv013_execute_debug_buffer(struct target *target) { uint32_t run_program = 0; run_program = set_field(run_program, AC_ACCESS_REGISTER_AARSIZE, 2); @@ -4379,7 +4976,7 @@ int riscv013_execute_debug_buffer(struct target *target) return execute_abstract_command(target, run_program); } -void riscv013_fill_dmi_write_u64(struct target *target, char *buf, int a, uint64_t d) +static void riscv013_fill_dmi_write_u64(struct target *target, char *buf, int a, uint64_t d) { RISCV013_INFO(info); buf_set_u64((unsigned char *)buf, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, DMI_OP_WRITE); @@ -4387,7 +4984,7 @@ void riscv013_fill_dmi_write_u64(struct target *target, char *buf, int a, uint64 buf_set_u64((unsigned char *)buf, DTM_DMI_ADDRESS_OFFSET, info->abits, a); } -void riscv013_fill_dmi_read_u64(struct target *target, char *buf, int a) +static void riscv013_fill_dmi_read_u64(struct target *target, char *buf, int a) { RISCV013_INFO(info); buf_set_u64((unsigned char *)buf, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, DMI_OP_READ); @@ -4395,7 +4992,7 @@ void riscv013_fill_dmi_read_u64(struct target *target, char *buf, int a) buf_set_u64((unsigned char *)buf, DTM_DMI_ADDRESS_OFFSET, info->abits, a); } -void riscv013_fill_dmi_nop_u64(struct target *target, char *buf) +static void riscv013_fill_dmi_nop_u64(struct target *target, char *buf) { RISCV013_INFO(info); buf_set_u64((unsigned char *)buf, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, DMI_OP_NOP); @@ -4403,15 +5000,36 @@ void riscv013_fill_dmi_nop_u64(struct target *target, char *buf) buf_set_u64((unsigned char *)buf, DTM_DMI_ADDRESS_OFFSET, info->abits, 0); } -int riscv013_dmi_write_u64_bits(struct target *target) +static int riscv013_dmi_write_u64_bits(struct target *target) { RISCV013_INFO(info); return info->abits + DTM_DMI_DATA_LENGTH + DTM_DMI_OP_LENGTH; } +void riscv013_fill_dm_write_u64(struct target *target, char *buf, int a, uint64_t d) +{ + dm013_info_t *dm = get_dm(target); + if (!dm) + return; + riscv013_fill_dmi_write_u64(target, buf, a + dm->base, d); +} + +void riscv013_fill_dm_read_u64(struct target *target, char *buf, int a) +{ + dm013_info_t *dm = get_dm(target); + if (!dm) + return; + riscv013_fill_dmi_read_u64(target, buf, a + dm->base); +} + +void riscv013_fill_dm_nop_u64(struct target *target, char *buf) +{ + riscv013_fill_dmi_nop_u64(target, buf); +} + static int maybe_execute_fence_i(struct target *target) { - if (has_sufficient_progbuf(target, 3)) + if (has_sufficient_progbuf(target, 2)) return execute_fence(target); return ERROR_OK; } @@ -4422,36 +5040,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); - dmi_write(target, DM_DMCONTROL, dmcontrol); + dmcontrol = set_dmcontrol_hartsel(dmcontrol, dm->current_hartid); + dm_write(target, DM_DMCONTROL, dmcontrol); - dmcontrol = set_field(dmcontrol, DM_DMCONTROL_HASEL, 0); dmcontrol = set_field(dmcontrol, DM_DMCONTROL_RESUMEREQ, 0); uint32_t dmstatus; @@ -4459,24 +5073,26 @@ 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) continue; - dmi_write(target, DM_DMCONTROL, dmcontrol); + dm_write(target, DM_DMCONTROL, dmcontrol); return ERROR_OK; } - dmi_write(target, DM_DMCONTROL, dmcontrol); + dm_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"); + LOG_TARGET_ERROR(target, " was stepping, halting"); riscv_halt(target); return ERROR_OK; } @@ -4484,17 +5100,17 @@ static int riscv013_step_or_resume_current_hart(struct target *target, return ERROR_FAIL; } -void riscv013_clear_abstract_error(struct target *target) +static void riscv013_clear_abstract_error(struct target *target) { /* Wait for busy to go away. */ time_t start = time(NULL); uint32_t abstractcs; - dmi_read(target, &abstractcs, DM_ABSTRACTCS); + dm_read(target, &abstractcs, DM_ABSTRACTCS); while (get_field(abstractcs, DM_ABSTRACTCS_BUSY)) { - dmi_read(target, &abstractcs, DM_ABSTRACTCS); + dm_read(target, &abstractcs, DM_ABSTRACTCS); if (time(NULL) - start > riscv_command_timeout_sec) { - LOG_ERROR("abstractcs.busy is not going low after %d seconds " + LOG_TARGET_ERROR(target, "abstractcs.busy is not going low after %d seconds " "(abstractcs=0x%x). The target is either really slow or " "broken. You could increase the timeout with riscv " "set_command_timeout_sec.", @@ -4503,5 +5119,5 @@ void riscv013_clear_abstract_error(struct target *target) } } /* Clear the error status. */ - dmi_write(target, DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR); + dm_write(target, DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR); } diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index cb8d04f..7be96cf 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; }; @@ -206,9 +135,6 @@ int riscv_command_timeout_sec = DEFAULT_COMMAND_TIMEOUT_SEC; int riscv_reset_timeout_sec = DEFAULT_RESET_TIMEOUT_SEC; static bool riscv_enable_virt2phys = true; -bool riscv_ebreakm = true; -bool riscv_ebreaks = true; -bool riscv_ebreaku = true; bool riscv_enable_virtual; @@ -230,6 +156,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 +182,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 +208,46 @@ 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, 0x1ffff}, +}; + +static const virt2phys_info_t sv57 = { + .name = "Sv57", + .va_bits = 57, + .level = 5, + .pte_shift = 3, + .vpn_shift = {12, 21, 30, 39, 48}, + .vpn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ff, 0x1ff}, + .pte_ppn_shift = {10, 19, 28, 37, 46}, + .pte_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ff, 0xff}, + .pa_ppn_shift = {12, 21, 30, 39, 48}, + .pa_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ff, 0xff}, +}; + +static const virt2phys_info_t sv57x4 = { + .name = "Sv57x4", + .va_bits = 59, + .level = 5, + .pte_shift = 3, + .vpn_shift = {12, 21, 30, 39, 48}, + .vpn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ff, 0x7ff}, + .pte_ppn_shift = {10, 19, 28, 37, 46}, + .pte_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ff, 0xff}, + .pa_ppn_shift = {12, 21, 30, 39, 48}, + .pa_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ff, 0xff}, +}; + +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); @@ -290,7 +281,7 @@ void select_dmi_via_bscan(struct target *target) bscan_tunnel_nested_tap_select_dmi, TAP_IDLE); } -uint32_t dtmcontrol_scan_via_bscan(struct target *target, uint32_t out) +int dtmcontrol_scan_via_bscan(struct target *target, uint32_t out, uint32_t *in_ptr) { /* On BSCAN TAP: Select IR=USER4, issue tunneled IR scan via BSCAN TAP's DR */ uint8_t tunneled_ir_width[4] = {bscan_tunnel_ir_width}; @@ -371,18 +362,19 @@ uint32_t dtmcontrol_scan_via_bscan(struct target *target, uint32_t out) uint32_t in = buf_get_u32(in_value, 1, 32); LOG_DEBUG("DTMCS: 0x%x -> 0x%x", out, in); - return in; + if (in_ptr) + *in_ptr = in; + return ERROR_OK; } -static uint32_t dtmcontrol_scan(struct target *target, uint32_t out) +static int dtmcontrol_scan(struct target *target, uint32_t out, uint32_t *in_ptr) { struct scan_field field; uint8_t in_value[4]; uint8_t out_value[4] = { 0 }; if (bscan_tunnel_ir_width != 0) - return dtmcontrol_scan_via_bscan(target, out); - + return dtmcontrol_scan_via_bscan(target, out, in_ptr); buf_set_u32(out_value, 0, 32, out); @@ -398,41 +390,46 @@ static uint32_t dtmcontrol_scan(struct target *target, uint32_t out) int retval = jtag_execute_queue(); if (retval != ERROR_OK) { - LOG_ERROR("failed jtag scan: %d", retval); + LOG_TARGET_ERROR(target, "dtmcontrol scan failed, error code = %d", retval); return retval; } uint32_t in = buf_get_u32(field.in_value, 0, 32); LOG_DEBUG("DTMCONTROL: 0x%x -> 0x%x", out, in); - return in; + if (in_ptr) + *in_ptr = in; + return ERROR_OK; } static struct target_type *get_target_type(struct target *target) { if (!target->arch_info) { - LOG_ERROR("Target has not been initialized"); + LOG_TARGET_ERROR(target, "Target has not been initialized."); return NULL; } RISCV_INFO(info); switch (info->dtm_version) { - case 0: + case DTM_DTMCS_VERSION_0_11: return &riscv011_target; - case 1: + case DTM_DTMCS_VERSION_1_0: return &riscv013_target; default: - LOG_ERROR("Unsupported DTM version: %d", info->dtm_version); + /* TODO: once we have proper support for non-examined targets + * we should have an assert here */ + LOG_TARGET_ERROR(target, "Unsupported DTM version: %d", + info->dtm_version); return NULL; } } static int riscv_create_target(struct target *target, Jim_Interp *interp) { - LOG_DEBUG("riscv_create_target()"); + LOG_TARGET_DEBUG(target, "riscv_create_target()"); target->arch_info = calloc(1, sizeof(struct riscv_info)); if (!target->arch_info) { - LOG_ERROR("Failed to allocate RISC-V target structure."); + LOG_TARGET_ERROR(target, "Failed to allocate RISC-V target structure."); return ERROR_FAIL; } riscv_info_init(target, target->arch_info); @@ -442,7 +439,7 @@ static int riscv_create_target(struct target *target, Jim_Interp *interp) static int riscv_init_target(struct command_context *cmd_ctx, struct target *target) { - LOG_DEBUG("riscv_init_target()"); + LOG_TARGET_DEBUG(target, "riscv_init_target()"); RISCV_INFO(info); info->cmd_ctx = cmd_ctx; @@ -451,8 +448,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; @@ -483,15 +484,36 @@ static void riscv_free_registers(struct target *target) free(target->reg_cache->reg_list); } free(target->reg_cache); + target->reg_cache = NULL; } } +static void free_reg_names(struct target *target); + +static void free_custom_register_names(struct target *target) +{ + RISCV_INFO(info); + + if (!info->custom_register_names.reg_names) + return; + + for (unsigned int i = 0; i < info->custom_register_names.num_entries; i++) + free(info->custom_register_names.reg_names[i]); + free(info->custom_register_names.reg_names); + info->custom_register_names.reg_names = NULL; +} + static void riscv_deinit_target(struct target *target) { - LOG_DEBUG("riscv_deinit_target()"); + LOG_TARGET_DEBUG(target, "riscv_deinit_target()"); struct riscv_info *info = target->arch_info; struct target_type *tt = get_target_type(target); + if (!tt) + LOG_TARGET_ERROR(target, "Could not identify target type."); + + if (riscv_flush_registers(target) != ERROR_OK) + LOG_TARGET_ERROR(target, "Failed to flush registers. Ignoring this error."); if (tt && info && info->version_specific) tt->deinit_target(target); @@ -502,6 +524,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); @@ -512,7 +539,7 @@ static void riscv_deinit_target(struct target *target) free(entry); } - free(info->reg_names); + free_reg_names(target); free(target->arch_info); target->arch_info = NULL; @@ -524,16 +551,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 size_power_of_2 = (size & (size - 1)) == 0; + bool addr_aligned = (addr & (size - 1)) == 0; + return size > 1 && size_power_of_2 && addr_aligned; +} + +/* 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 int 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 int 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 +678,455 @@ 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 void log_trigger_request_info(struct trigger_request_info trig_info) +{ + LOG_DEBUG("tdata1=%" PRIx64 ", tdata2=%" PRIx64 ", tdata1_ignore_mask=%" PRIx64, + trig_info.tdata1, trig_info.tdata2, trig_info.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 int try_setup_single_match_trigger(struct target *target, + struct trigger *trigger, struct trigger_request_info trig_info) +{ + LOG_TARGET_DEBUG(target, "trying to set up a match trigger"); + log_trigger_request_info(trig_info); - riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address); + int trigger_type = + get_field(trig_info.tdata1, CSR_MCONTROL_TYPE(riscv_xlen(target))); + int ret = ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + RISCV_INFO(r); - return ERROR_OK; + /* 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; + } + 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) { + LOG_TARGET_DEBUG(target, "trying to set up a chain of match triggers"); + log_trigger_request_info(t1); + log_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 { + /* Other values are available for this field, + * but currently only `any` is needed. + */ + riscv_reg_t any; + } 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) + }, + .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) + }, + .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_for_wp(struct target *target, + struct trigger *trigger, struct match_triggers_tdata1_fields fields) { RISCV_INFO(r); + int ret = ERROR_FAIL; + + if (trigger->length > 0) { + /* Setting a load/store trigger ("watchpoint") on a range of addresses */ + + if (r->enable_napot_trigger && 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; + } - /* 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 (r->enable_ge_lt_trigger) { + 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 + trigger->length, + .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, + .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; + } } - /* 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 (r->enable_equality_match_trigger) { + LOG_TARGET_DEBUG(target, "trying to setup equality match trigger"); + struct trigger_request_info eq = { + .tdata1 = fields.common | fields.size.any | fields.chain.disable | + fields.match.eq, + .tdata2 = trigger->address, + .tdata1_ignore_mask = fields.tdata1_ignore_mask + }; + ret = try_setup_single_match_trigger(target, trigger, eq); + if (ret != ERROR_OK) + return ERROR_FAIL; + } - 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; + if (ret == ERROR_OK && trigger->length > 1) { + LOG_TARGET_DEBUG(target, "Trigger will match accesses at address 0x%" TARGET_PRIxADDR + ", but may not match accesses at addresses in the inclusive range from 0x%" + TARGET_PRIxADDR " to 0x%" TARGET_PRIxADDR ".", trigger->address, + trigger->address + 1, trigger->address + trigger->length - 1); + RISCV_INFO(info); + if (!info->range_trigger_fallback_encountered) + /* This message is displayed only once per target to avoid + * overwhelming the user with such messages on resume. + */ + LOG_TARGET_WARNING(target, + "Could not set a trigger that will match a whole address range. " + "As a fallback, this trigger (and maybe others) will only match " + "against the first address of the range."); + info->range_trigger_fallback_encountered = true; } - riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address); + return ret; +} + +static int maybe_add_trigger_t2_t6_for_bp(struct target *target, + struct trigger *trigger, struct match_triggers_tdata1_fields fields) +{ + LOG_TARGET_DEBUG(target, "trying to setup equality match trigger"); + struct trigger_request_info eq = { + .tdata1 = fields.common | fields.size.any | fields.chain.disable | + fields.match.eq, + .tdata2 = trigger->address, + .tdata1_ignore_mask = fields.tdata1_ignore_mask + }; - return ERROR_OK; + return try_setup_single_match_trigger(target, trigger, eq); } -static int add_trigger(struct target *target, struct trigger *trigger) +static int maybe_add_trigger_t2_t6(struct target *target, + struct trigger *trigger, struct match_triggers_tdata1_fields fields) { + if (trigger->is_execute) { + assert(!trigger->is_read && !trigger->is_write); + return maybe_add_trigger_t2_t6_for_bp(target, trigger, fields); + } + + assert(trigger->is_read || trigger->is_write); + return maybe_add_trigger_t2_t6_for_wp(target, trigger, fields); +} + +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_INFO(r); - if (riscv_enumerate_triggers(target) != ERROR_OK) - return ERROR_FAIL; + 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; +} - riscv_reg_t tselect; - if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK) - return ERROR_FAIL; +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; - 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_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; +} - 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 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; - 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; - } + RISCV_INFO(r); - if (result != ERROR_OK) - continue; + 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; +} - 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; - } +static int add_trigger(struct target *target, struct trigger *trigger) +{ + int ret; + riscv_reg_t tselect; - riscv_set_register(target, GDB_REGNO_TSELECT, tselect); + ret = riscv_enumerate_triggers(target); + if (ret != ERROR_OK) + return ret; - if (i >= r->trigger_count) { - LOG_ERROR("Couldn't find an available hardware trigger."); - return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; - } + ret = riscv_get_register(target, &tselect, GDB_REGNO_TSELECT); + if (ret != ERROR_OK) + return ret; - return ERROR_OK; + 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); + + if (riscv_set_register(target, GDB_REGNO_TSELECT, tselect) != ERROR_OK && + ret == ERROR_OK) + return ERROR_FAIL; + + return ret; } /** @@ -864,24 +1249,25 @@ 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 */ if (!(breakpoint->length == 4 || breakpoint->length == 2)) { - LOG_ERROR("Invalid breakpoint length %d", breakpoint->length); + LOG_TARGET_ERROR(target, "Invalid breakpoint length %d", breakpoint->length); return ERROR_FAIL; } if (0 != (breakpoint->address % 2)) { - LOG_ERROR("Invalid breakpoint alignment for address 0x%" TARGET_PRIxADDR, breakpoint->address); + LOG_TARGET_ERROR(target, "Invalid breakpoint alignment for address 0x%" TARGET_PRIxADDR, + breakpoint->address); return ERROR_FAIL; } /* Read the original instruction. */ if (riscv_read_by_any_size( target, breakpoint->address, breakpoint->length, breakpoint->orig_instr) != ERROR_OK) { - LOG_ERROR("Failed to read original instruction at 0x%" TARGET_PRIxADDR, + LOG_TARGET_ERROR(target, "Failed to read original instruction at 0x%" TARGET_PRIxADDR, breakpoint->address); return ERROR_FAIL; } @@ -890,7 +1276,7 @@ static int riscv_add_breakpoint(struct target *target, struct breakpoint *breakp buf_set_u32(buff, 0, breakpoint->length * CHAR_BIT, breakpoint->length == 4 ? ebreak() : ebreak_c()); /* Write the ebreak instruction. */ if (riscv_write_by_any_size(target, breakpoint->address, breakpoint->length, buff) != ERROR_OK) { - LOG_ERROR("Failed to write %d-byte breakpoint instruction at 0x%" + LOG_TARGET_ERROR(target, "Failed to write %d-byte breakpoint instruction at 0x%" TARGET_PRIxADDR, breakpoint->length, breakpoint->address); return ERROR_FAIL; } @@ -902,7 +1288,7 @@ static int riscv_add_breakpoint(struct target *target, struct breakpoint *breakp if (result != ERROR_OK) return result; } else { - LOG_INFO("OpenOCD only supports hardware and software breakpoints."); + LOG_TARGET_INFO(target, "OpenOCD only supports hardware and software breakpoints."); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } @@ -910,34 +1296,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; - } - if (i >= r->trigger_count) { - LOG_ERROR("Couldn't find the hardware resources used by hardware " - "trigger."); - return ERROR_FAIL; - } - 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); + + 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 (!done) { + LOG_TARGET_ERROR(target, + "Couldn't find the hardware resources used by hardware trigger."); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + riscv_set_register(target, GDB_REGNO_TSELECT, tselect); - r->trigger_unique_id[i] = -1; return ERROR_OK; } @@ -949,7 +1337,7 @@ static int riscv_remove_breakpoint(struct target *target, /* Write the original instruction. */ if (riscv_write_by_any_size( target, breakpoint->address, breakpoint->length, breakpoint->orig_instr) != ERROR_OK) { - LOG_ERROR("Failed to restore instruction for %d-byte breakpoint at " + LOG_TARGET_ERROR(target, "Failed to restore instruction for %d-byte breakpoint at " "0x%" TARGET_PRIxADDR, breakpoint->length, breakpoint->address); return ERROR_FAIL; } @@ -957,12 +1345,12 @@ 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; } else { - LOG_INFO("OpenOCD only supports hardware and software breakpoints."); + LOG_TARGET_INFO(target, "OpenOCD only supports hardware and software breakpoints."); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } @@ -978,15 +1366,23 @@ 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; } int riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint) { + // NOTE: typeof is needed because of upstream OpenOCD bug. This should be + // replaced by WATCHPOINT_IGNORE_DATA_VALUE_MASK once it is available + // See: https://review.openocd.org/c/openocd/+/7840 + if (watchpoint->mask != ~(typeof(watchpoint->mask))0) { + LOG_TARGET_ERROR(target, "Watchpoints on data values are not implemented"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + struct trigger trigger; trigger_from_watchpoint(&trigger, watchpoint); @@ -1001,12 +1397,12 @@ int riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint) int riscv_remove_watchpoint(struct target *target, struct watchpoint *watchpoint) { - LOG_DEBUG("[%d] @0x%" TARGET_PRIxADDR, target->coreid, watchpoint->address); + LOG_TARGET_DEBUG(target, "Removing watchpoint @0x%" TARGET_PRIxADDR, watchpoint->address); 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 +1410,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_HIT0 | CSR_MCONTROL6_HIT1; + 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_TARGET_DEBUG(target, "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_TARGET_DEBUG(target, "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,33 +1488,38 @@ 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; - LOG_DEBUG("dpc is 0x%" PRIx64, dpc); + LOG_TARGET_DEBUG(target, "dpc is 0x%" PRIx64, dpc); /* fetch the instruction at dpc */ uint8_t buffer[length]; if (target_read_buffer(target, dpc, length, buffer) != ERROR_OK) { - LOG_ERROR("Failed to read instruction at dpc 0x%" PRIx64, dpc); + LOG_TARGET_ERROR(target, "Failed to read instruction at dpc 0x%" PRIx64, dpc); return ERROR_FAIL; } uint32_t instruction = 0; for (int i = 0; i < length; i++) { - LOG_DEBUG("Next byte is %x", buffer[i]); + LOG_TARGET_DEBUG(target, "Next byte is %x", buffer[i]); instruction += (buffer[i] << 8 * i); } - LOG_DEBUG("Full instruction is %x", instruction); + LOG_TARGET_DEBUG(target, "Full instruction is %x", instruction); /* find out which memory address is accessed by the instruction at dpc */ /* opcode is first 7 bits of the instruction */ @@ -1062,27 +1533,28 @@ static int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_w riscv_get_register(target, &mem_addr, rs1); if (opcode == MATCH_SB) { - LOG_DEBUG("%x is store instruction", instruction); + LOG_TARGET_DEBUG(target, "%x is store instruction", instruction); imm = ((instruction & 0xf80) >> 7) | ((instruction & 0xfe000000) >> 20); } else { - LOG_DEBUG("%x is load instruction", instruction); + LOG_TARGET_DEBUG(target, "%x is load instruction", instruction); imm = (instruction & 0xfff00000) >> 20; } /* sign extend 12-bit imm to 16-bits */ if (imm & (1 << 11)) imm |= 0xf000; mem_addr += imm; - LOG_DEBUG("memory address=0x%" PRIx64, mem_addr); + LOG_TARGET_DEBUG(target, "Memory address=0x%" PRIx64, mem_addr); } else { - LOG_DEBUG("%x is not a RV32I load or store", instruction); + LOG_TARGET_DEBUG(target, "%x is not a RV32I load or store", instruction); return ERROR_FAIL; } + struct watchpoint *wp = target->watchpoints; while (wp) { /*TODO support length/mask */ if (wp->address == mem_addr) { *hit_watchpoint = wp; - LOG_DEBUG("Hit address=%" TARGET_PRIxADDR, wp->address); + LOG_TARGET_DEBUG(target, "Hit address=%" TARGET_PRIxADDR, wp->address); return ERROR_OK; } wp = wp->next; @@ -1096,11 +1568,12 @@ 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) { struct target_type *tt = get_target_type(target); + if (!tt) + return ERROR_FAIL; return tt->step(target, current, address, handle_breakpoints); } @@ -1108,72 +1581,157 @@ static int old_or_new_riscv_step(struct target *target, int current, target_addr_t address, int handle_breakpoints) { RISCV_INFO(r); - LOG_DEBUG("handle_breakpoints=%d", handle_breakpoints); - if (!r->is_halted) + LOG_TARGET_DEBUG(target, "handle_breakpoints=%d", handle_breakpoints); + 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_TARGET_DEBUG(target, "Starting examination"); if (target_was_examined(target)) { - LOG_DEBUG("Target was already examined."); + LOG_TARGET_DEBUG(target, "Target was already examined."); return ERROR_OK; } /* Don't need to select dbus, since the first thing we do is read dtmcontrol. */ RISCV_INFO(info); - uint32_t dtmcontrol = dtmcontrol_scan(target, 0); - LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol); + uint32_t dtmcontrol; + if (dtmcontrol_scan(target, 0, &dtmcontrol) != ERROR_OK || dtmcontrol == 0) { + LOG_TARGET_ERROR(target, "Could not read dtmcontrol. Check JTAG connectivity/board power."); + return ERROR_FAIL; + } + LOG_TARGET_DEBUG(target, "dtmcontrol=0x%x", dtmcontrol); info->dtm_version = get_field(dtmcontrol, DTMCONTROL_VERSION); - LOG_DEBUG(" version=0x%x", info->dtm_version); + LOG_TARGET_DEBUG(target, "version=0x%x", info->dtm_version); + int examine_status = ERROR_FAIL; struct target_type *tt = get_target_type(target); if (!tt) - return ERROR_FAIL; + goto examine_fail; - int result = tt->init_target(info->cmd_ctx, target); - if (result != ERROR_OK) - return result; + examine_status = tt->init_target(info->cmd_ctx, target); + if (examine_status != ERROR_OK) + goto examine_fail; - return tt->examine(target); + examine_status = tt->examine(target); + if (examine_status != ERROR_OK) + goto examine_fail; + + return ERROR_OK; + +examine_fail: + info->dtm_version = DTM_DTMCS_VERSION_UNKNOWN; + return examine_status; } static int oldriscv_poll(struct target *target) { struct target_type *tt = get_target_type(target); + if (!tt) + return ERROR_FAIL; return tt->poll(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. + */ +static 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_TARGET_DEBUG(target, "debug_reason=%d", target->debug_reason); + + return ERROR_OK; } static int halt_prep(struct target *target) { RISCV_INFO(r); - 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); + LOG_TARGET_DEBUG(target, "prep hart, debug_reason=%d", 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 +1745,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)) { - LOG_DEBUG("[%s] Hart is already halted.", target_name(target)); + if (state == RISCV_STATE_HALTED) { + LOG_TARGET_DEBUG(target, "Hart is already halted."); } 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 +1764,14 @@ 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); + if (!tt) + return ERROR_FAIL; 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 +1787,14 @@ 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); + if (!tt) + return ERROR_FAIL; 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) { @@ -1272,38 +1834,21 @@ int riscv_halt(struct target *target) static int riscv_assert_reset(struct target *target) { - LOG_DEBUG("[%d]", target->coreid); + LOG_TARGET_DEBUG(target, "coreid: [%d]", target->coreid); struct target_type *tt = get_target_type(target); + if (!tt) + return ERROR_FAIL; riscv_invalidate_register_cache(target); return tt->assert_reset(target); } static int riscv_deassert_reset(struct target *target) { - LOG_DEBUG("[%d]", target->coreid); + LOG_TARGET_DEBUG(target, "coreid: [%d]", target->coreid); struct target_type *tt = get_target_type(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) + if (!tt) 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; + return tt->deassert_reset(target); } /* state must be riscv_reg_t state[RISCV_MAX_HWBPS] = {0}; */ @@ -1311,7 +1856,7 @@ static int disable_triggers(struct target *target, riscv_reg_t *state) { RISCV_INFO(r); - LOG_DEBUG("deal with triggers"); + LOG_TARGET_DEBUG(target, "Disabling triggers."); if (riscv_enumerate_triggers(target) != ERROR_OK) return ERROR_FAIL; @@ -1327,7 +1872,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; @@ -1341,7 +1886,7 @@ static int disable_triggers(struct target *target, riscv_reg_t *state) struct watchpoint *watchpoint = target->watchpoints; int i = 0; while (watchpoint) { - LOG_DEBUG("watchpoint %d: set=%d", i, watchpoint->is_set); + LOG_TARGET_DEBUG(target, "Watchpoint %d: set=%d", i, watchpoint->is_set); state[i] = watchpoint->is_set; if (watchpoint->is_set) { if (riscv_remove_watchpoint(target, watchpoint) != ERROR_OK) @@ -1379,7 +1924,7 @@ static int enable_triggers(struct target *target, riscv_reg_t *state) struct watchpoint *watchpoint = target->watchpoints; int i = 0; while (watchpoint) { - LOG_DEBUG("watchpoint %d: cleared=%" PRId64, i, state[i]); + LOG_TARGET_DEBUG(target, "Watchpoint %d: cleared=%" PRId64, i, state[i]); if (state[i]) { if (riscv_add_watchpoint(target, watchpoint) != ERROR_OK) return ERROR_FAIL; @@ -1398,33 +1943,26 @@ 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; } - LOG_DEBUG("[%d] mark as prepped", target->coreid); + LOG_TARGET_DEBUG(target, "Mark as prepped."); r->prepped = true; return ERROR_OK; @@ -1437,10 +1975,13 @@ 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); + if (!tt) + return ERROR_FAIL; result = tt->resume(target, current, address, handle_breakpoints, debug_execution); } else { @@ -1450,13 +1991,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,159 +2014,230 @@ 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) { - *enabled = 0; + *enabled = 0; + + if (!riscv_enable_virt2phys) return ERROR_OK; - } /* Don't use MMU in explicit or effective M (machine) mode */ riscv_reg_t priv; if (riscv_get_register(target, &priv, GDB_REGNO_PRIV) != ERROR_OK) { - LOG_ERROR("Failed to read priv register."); + LOG_TARGET_ERROR(target, "Failed to read priv register."); 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_TARGET_ERROR(target, "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."); + } + + 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); - *enabled = 0; + /* 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."); return ERROR_OK; } riscv_reg_t satp; if (riscv_get_register(target, &satp, GDB_REGNO_SATP) != ERROR_OK) { - LOG_DEBUG("Couldn't read SATP."); + LOG_TARGET_DEBUG(target, "Couldn't read SATP."); /* If we can't read SATP, then there must not be an MMU. */ - *enabled = 0; return ERROR_OK; } - if (get_field(satp, RISCV_SATP_MODE(riscv_xlen(target))) == SATP_MODE_OFF) { - LOG_DEBUG("MMU is disabled."); - *enabled = 0; + if (get_field(satp, RISCV_SATP_MODE(xlen)) == SATP_MODE_OFF) { + LOG_TARGET_DEBUG(target, "MMU is disabled."); } else { - LOG_DEBUG("MMU is enabled."); + LOG_TARGET_DEBUG(target, "MMU is enabled."); *enabled = 1; } 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); target_addr_t mask = ((target_addr_t)1 << (xlen - (info->va_bits - 1))) - 1; target_addr_t masked_msbs = (virtual >> (info->va_bits - 1)) & mask; if (masked_msbs != 0 && masked_msbs != mask) { - LOG_ERROR("Virtual address 0x%" TARGET_PRIxADDR " is not sign-extended " + LOG_TARGET_ERROR(target, "Virtual address 0x%" TARGET_PRIxADDR " is not sign-extended " "for %s mode.", virtual, info->name); 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, @@ -1636,24 +2250,27 @@ static int riscv_address_translate(struct target *target, else pte = buf_get_u64(buffer, 0, 64); - LOG_DEBUG("i=%d; PTE @0x%" TARGET_PRIxADDR " = 0x%" PRIx64, i, + LOG_TARGET_DEBUG(target, "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) { - LOG_ERROR("Couldn't find the PTE."); + LOG_TARGET_ERROR(target, "Couldn't find the PTE."); return ERROR_FAIL; } @@ -1661,15 +2278,125 @@ 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(hgatp, 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_SV57: + vsatp_info = &sv57; + break; + case SATP_MODE_OFF: + vsatp_info = NULL; + LOG_TARGET_DEBUG(target, "vsatp mode is %d. No VS-stage translation. (vsatp: 0x%" PRIx64 ")", + vsatp_mode, vsatp); + 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_SV57X4: + hgatp_info = &sv57x4; + break; + case HGATP_MODE_OFF: + hgatp_info = NULL; + LOG_TARGET_DEBUG(target, "hgatp mode is %d. No G-stage translation. (hgatp: 0x%" PRIx64 ")", + hgatp_mode, hgatp); + 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 +2404,65 @@ 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) { + *physical = virtual; + LOG_TARGET_DEBUG(target, "MMU is disabled. 0x%" TARGET_PRIxADDR " -> 0x%" TARGET_PRIxADDR, virtual, *physical); + return ERROR_OK; + } - 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_TARGET_ERROR(target, "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; + if (riscv_get_register(target, &satp_value, GDB_REGNO_SATP) != ERROR_OK) { + LOG_TARGET_ERROR(target, "Failed to read SATP register."); + return ERROR_FAIL; + } + + 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_SV57: + satp_info = &sv57; + break; + case SATP_MODE_OFF: + LOG_TARGET_ERROR(target, "No translation or protection." + " (satp: 0x%" PRIx64 ")", satp_value); + return ERROR_FAIL; + default: + LOG_TARGET_ERROR(target, "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); } @@ -1701,27 +2470,27 @@ static int riscv_read_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t count, uint8_t *buffer) { if (count == 0) { - LOG_WARNING("0-length read from 0x%" TARGET_PRIxADDR, address); + LOG_TARGET_WARNING(target, "0-length read from 0x%" TARGET_PRIxADDR, 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; + int result = target->type->virt2phys(target, address, &physical_addr); + if (result != ERROR_OK) { + LOG_TARGET_ERROR(target, "Address translation failed."); + return result; + } RISCV_INFO(r); - return r->read_memory(target, address, size, count, buffer, size); + return r->read_memory(target, physical_addr, size, count, buffer, size); } 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); + if (!tt) + return ERROR_FAIL; return tt->write_memory(target, phys_address, size, count, buffer); } @@ -1729,19 +2498,21 @@ static int riscv_write_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t count, const uint8_t *buffer) { if (count == 0) { - LOG_WARNING("0-length write to 0x%" TARGET_PRIxADDR, address); + LOG_TARGET_WARNING(target, "0-length write to 0x%" TARGET_PRIxADDR, 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; + int result = target->type->virt2phys(target, address, &physical_addr); + if (result != ERROR_OK) { + LOG_TARGET_ERROR(target, "Address translation failed."); + return result; + } struct target_type *tt = get_target_type(target); - return tt->write_memory(target, address, size, count, buffer); + if (!tt) + return ERROR_FAIL; + return tt->write_memory(target, physical_addr, size, count, buffer); } static const char *riscv_get_gdb_arch(struct target *target) @@ -1752,26 +2523,21 @@ static const char *riscv_get_gdb_arch(struct target *target) case 64: return "riscv:rv64"; } - LOG_ERROR("Unsupported xlen: %d", riscv_xlen(target)); + LOG_TARGET_ERROR(target, "Unsupported xlen: %d", riscv_xlen(target)); return NULL; } 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."); + LOG_TARGET_ERROR(target, "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; @@ -1780,7 +2546,7 @@ static int riscv_get_gdb_reg_list_internal(struct target *target, *reg_list_size = target->reg_cache->num_regs; break; default: - LOG_ERROR("Unsupported reg_class: %d", reg_class); + LOG_TARGET_ERROR(target, "Unsupported reg_class: %d", reg_class); return ERROR_FAIL; } @@ -1792,7 +2558,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( @@ -1823,6 +2589,8 @@ static int riscv_get_gdb_reg_list(struct target *target, static int riscv_arch_state(struct target *target) { struct target_type *tt = get_target_type(target); + if (!tt) + return ERROR_FAIL; return tt->arch_state(target); } @@ -1834,40 +2602,48 @@ 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_TARGET_ERROR(target, "not halted (run target algo)"); 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_TARGET_ERROR(target, "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) return ERROR_FAIL; uint64_t saved_pc = buf_get_u64(reg_pc->value, 0, reg_pc->size); - LOG_DEBUG("saved_pc=0x%" PRIx64, saved_pc); + LOG_TARGET_DEBUG(target, "saved_pc=0x%" PRIx64, saved_pc); uint64_t saved_regs[32]; for (int i = 0; i < num_reg_params; i++) { - LOG_DEBUG("save %s", reg_params[i].reg_name); + LOG_TARGET_DEBUG(target, "save %s", reg_params[i].reg_name); struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, false); if (!r) { - LOG_ERROR("Couldn't find register named '%s'", reg_params[i].reg_name); + LOG_TARGET_ERROR(target, "Couldn't find register named '%s'", reg_params[i].reg_name); return ERROR_FAIL; } if (r->size != reg_params[i].size) { - LOG_ERROR("Register %s is %d bits instead of %d bits.", + LOG_TARGET_ERROR(target, "Register %s is %d bits instead of %d bits.", reg_params[i].reg_name, r->size, reg_params[i].size); return ERROR_FAIL; } if (r->number > GDB_REGNO_XPR31) { - LOG_ERROR("Only GPRs can be use as argument registers."); + LOG_TARGET_ERROR(target, "Only GPRs can be use as argument registers."); return ERROR_FAIL; } @@ -1881,38 +2657,23 @@ 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, ¤t_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) + LOG_TARGET_DEBUG(target, "Resume at 0x%" TARGET_PRIxADDR, entry_point); + if (riscv_resume(target, 0, entry_point, 0, 1, true) != ERROR_OK) return ERROR_FAIL; int64_t start = timeval_ms(); while (target->state != TARGET_HALTED) { - LOG_DEBUG("poll()"); + LOG_TARGET_DEBUG(target, "poll()"); int64_t now = timeval_ms(); if (now - start > timeout_ms) { - LOG_ERROR("Algorithm timed out after %" PRId64 " ms.", now - start); + LOG_TARGET_ERROR(target, "Algorithm timed out after %" PRId64 " ms.", now - start); riscv_halt(target); old_or_new_riscv_poll(target); enum gdb_regno regnums[] = { @@ -1932,7 +2693,8 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, riscv_reg_t reg_value; if (riscv_get_register(target, ®_value, regno) != ERROR_OK) break; - LOG_ERROR("%s = 0x%" PRIx64, gdb_regno_name(regno), reg_value); + + LOG_TARGET_ERROR(target, "%s = 0x%" PRIx64, gdb_regno_name(target, regno), reg_value); } return ERROR_TARGET_TIMEOUT; } @@ -1942,23 +2704,22 @@ 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; uint64_t final_pc = buf_get_u64(reg_pc->value, 0, reg_pc->size); if (exit_point && final_pc != exit_point) { - LOG_ERROR("PC ended up at 0x%" PRIx64 " instead of 0x%" + LOG_TARGET_ERROR(target, "PC ended up at 0x%" PRIx64 " instead of 0x%" TARGET_PRIxADDR, final_pc, exit_point); return ERROR_FAIL; } /* 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 }; @@ -1971,20 +2732,35 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, reg_params[i].direction == PARAM_IN_OUT) { struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, false); if (r->type->get(r) != ERROR_OK) { - LOG_ERROR("get(%s) failed", r->name); + LOG_TARGET_ERROR(target, "get(%s) failed", r->name); return ERROR_FAIL; } buf_cpy(r->value, reg_params[i].value, reg_params[i].size); } - LOG_DEBUG("restore %s", reg_params[i].reg_name); + LOG_TARGET_DEBUG(target, "restore %s", reg_params[i].reg_name); struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, false); buf_set_u64(buf, 0, info->xlen, saved_regs[r->number]); if (r->type->set(r, buf) != ERROR_OK) { - LOG_ERROR("set(%s) failed", r->name); + LOG_TARGET_ERROR(target, "set(%s) failed", r->name); return ERROR_FAIL; } } + /* 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_TARGET_ERROR(target, "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; } @@ -1996,7 +2772,7 @@ static int riscv_checksum_memory(struct target *target, struct reg_param reg_params[2]; int retval; - LOG_DEBUG("address=0x%" TARGET_PRIxADDR "; count=0x%" PRIx32, address, count); + LOG_TARGET_DEBUG(target, "address=0x%" TARGET_PRIxADDR "; count=0x%" PRIx32, address, count); static const uint8_t riscv32_crc_code[] = { #include "../../../contrib/loaders/checksum/riscv32_crc.inc" @@ -2040,7 +2816,7 @@ static int riscv_checksum_memory(struct target *target, retval = target_write_buffer(target, crc_algorithm->address, crc_code_size, crc_code); if (retval != ERROR_OK) { - LOG_ERROR("Failed to write code to " TARGET_ADDR_FMT ": %d", + LOG_TARGET_ERROR(target, "Failed to write code to " TARGET_ADDR_FMT ": %d", crc_algorithm->address, retval); target_free_working_area(target, crc_algorithm); return retval; @@ -2062,74 +2838,153 @@ static int riscv_checksum_memory(struct target *target, if (retval == ERROR_OK) *checksum = buf_get_u32(reg_params[0].value, 0, 32); else - LOG_ERROR("error executing RISC-V CRC algorithm"); + LOG_TARGET_ERROR(target, "Error executing RISC-V CRC algorithm."); destroy_reg_param(®_params[0]); destroy_reg_param(®_params[1]); target_free_working_area(target, crc_algorithm); - LOG_DEBUG("checksum=0x%" PRIx32 ", result=%d", *checksum, retval); + LOG_TARGET_DEBUG(target, "checksum=0x%" PRIx32 ", result=%d", *checksum, retval); return retval; } /*** 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: + if (previous_riscv_state == RISCV_STATE_UNAVAILABLE) + LOG_TARGET_INFO(target, "became available (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: + if (previous_riscv_state == RISCV_STATE_UNAVAILABLE) + LOG_TARGET_INFO(target, "became available (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; } @@ -2140,7 +2995,7 @@ static int sample_memory(struct target *target) if (!r->sample_buf.buf || !r->sample_config.enabled) return ERROR_OK; - LOG_DEBUG("buf used/size: %d/%d", r->sample_buf.used, r->sample_buf.size); + LOG_TARGET_DEBUG(target, "buf used/size: %d/%d", r->sample_buf.used, r->sample_buf.size); uint64_t start = timeval_ms(); riscv_sample_buf_maybe_add_timestamp(target, true); @@ -2174,7 +3029,7 @@ static int sample_memory(struct target *target) exit: riscv_sample_buf_maybe_add_timestamp(target, false); if (result != ERROR_OK) { - LOG_INFO("Turning off memory sampling because it failed."); + LOG_TARGET_INFO(target, "Turning off memory sampling because it failed."); r->sample_config.enabled = false; } return result; @@ -2183,159 +3038,196 @@ exit: /*** OpenOCD Interface ***/ int riscv_openocd_poll(struct target *target) { - LOG_DEBUG("polling all harts"); - int halted_hart = -1; + LOG_TARGET_DEBUG(target, "Polling all harts."); - if (target->smp) { - 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: - 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 int should_remain_halted = 0; + unsigned int should_resume = 0; + unsigned int halted = 0; + unsigned int 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_TARGET_DEBUG(target, "should_remain_halted=%d, should_resume=%d", + should_remain_halted, should_resume); + if (should_remain_halted && should_resume) { + LOG_TARGET_WARNING(target, "%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) { + if (riscv_set_register(target, GDB_REGNO_PC, address) != ERROR_OK) + return ERROR_FAIL; + } - if (!current) - riscv_set_register(target, GDB_REGNO_PC, address); + 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) { - LOG_ERROR("unable to step rtos hart"); - return out; + 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, + ¤t_mstatus) != ERROR_OK) { + success = false; + LOG_TARGET_ERROR(target, "Unable to disable interrupts."); + goto _exit; + } + } + + if (riscv_step_rtos_hart(target) != ERROR_OK) { + success = false; + LOG_TARGET_ERROR(target, "Unable to step rtos hart."); } 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_TARGET_ERROR(target, "Unable to restore interrupts."); + } + +_exit: + if (enable_triggers(target, trigger_state) != ERROR_OK) { + success = false; + LOG_TARGET_ERROR(target, "Unable to enable triggers."); + } + + if (breakpoint && (riscv_add_breakpoint(target, breakpoint) != ERROR_OK)) { + success = false; + LOG_TARGET_ERROR(target, "Unable to restore the disabled breakpoint."); + } - 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 (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 */ COMMAND_HANDLER(riscv_set_command_timeout_sec) { if (CMD_ARGC != 1) { - LOG_ERROR("Command takes exactly 1 parameter"); + LOG_ERROR("Command takes exactly 1 parameter."); return ERROR_COMMAND_SYNTAX_ERROR; } int timeout = atoi(CMD_ARGV[0]); @@ -2352,7 +3244,7 @@ COMMAND_HANDLER(riscv_set_command_timeout_sec) COMMAND_HANDLER(riscv_set_reset_timeout_sec) { if (CMD_ARGC != 1) { - LOG_ERROR("Command takes exactly 1 parameter"); + LOG_ERROR("Command takes exactly 1 parameter."); return ERROR_COMMAND_SYNTAX_ERROR; } int timeout = atoi(CMD_ARGV[0]); @@ -2555,7 +3447,7 @@ static int parse_ranges(struct list_head *ranges, const char *tcl_arg, const cha COMMAND_HANDLER(riscv_set_expose_csrs) { if (CMD_ARGC == 0) { - LOG_ERROR("Command expects parameters"); + LOG_ERROR("Command expects parameters."); return ERROR_COMMAND_SYNTAX_ERROR; } @@ -2575,7 +3467,7 @@ COMMAND_HANDLER(riscv_set_expose_csrs) COMMAND_HANDLER(riscv_set_expose_custom) { if (CMD_ARGC == 0) { - LOG_ERROR("Command expects parameters"); + LOG_ERROR("Command expects parameters."); return ERROR_COMMAND_SYNTAX_ERROR; } @@ -2592,6 +3484,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; @@ -2600,7 +3512,7 @@ COMMAND_HANDLER(riscv_authdata_read) } else if (CMD_ARGC == 1) { COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], index); } else { - LOG_ERROR("Command takes at most one parameter"); + LOG_ERROR("Command takes at most one parameter."); return ERROR_COMMAND_SYNTAX_ERROR; } @@ -2612,7 +3524,7 @@ COMMAND_HANDLER(riscv_authdata_read) RISCV_INFO(r); if (!r) { - LOG_ERROR("riscv_info is NULL!"); + LOG_TARGET_ERROR(target, "riscv_info is NULL!"); return ERROR_FAIL; } @@ -2623,7 +3535,7 @@ COMMAND_HANDLER(riscv_authdata_read) command_print_sameline(CMD, "0x%08" PRIx32, value); return ERROR_OK; } else { - LOG_ERROR("authdata_read is not implemented for this target."); + LOG_TARGET_ERROR(target, "authdata_read is not implemented for this target."); return ERROR_FAIL; } } @@ -2647,7 +3559,7 @@ COMMAND_HANDLER(riscv_authdata_write) RISCV_INFO(r); if (!r->authdata_write) { - LOG_ERROR("authdata_write is not implemented for this target."); + LOG_TARGET_ERROR(target, "authdata_write is not implemented for this target."); return ERROR_FAIL; } @@ -2669,7 +3581,7 @@ COMMAND_HANDLER(riscv_dmi_read) RISCV_INFO(r); if (!r) { - LOG_ERROR("riscv_info is NULL!"); + LOG_TARGET_ERROR(target, "riscv_info is NULL!"); return ERROR_FAIL; } @@ -2679,14 +3591,13 @@ COMMAND_HANDLER(riscv_dmi_read) if (r->dmi_read(target, &value, address) != ERROR_OK) return ERROR_FAIL; command_print(CMD, "0x%" PRIx32, value); - return ERROR_OK; } else { - LOG_ERROR("dmi_read is not implemented for this target."); + LOG_TARGET_ERROR(target, "dmi_read is not implemented for this target."); return ERROR_FAIL; } + return ERROR_OK; } - COMMAND_HANDLER(riscv_dmi_write) { if (CMD_ARGC != 2) { @@ -2702,11 +3613,96 @@ COMMAND_HANDLER(riscv_dmi_write) COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value); if (r->dmi_write) { - return r->dmi_write(target, address, value); + /* 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 progbuf_touched = (address >= DM_PROGBUF0 && address <= DM_PROGBUF15); + bool dm_deactivated = (address == DM_DMCONTROL && (value & DM_DMCONTROL_DMACTIVE) == 0); + if (progbuf_touched || dm_deactivated) { + if (r->invalidate_cached_debug_buffer) + r->invalidate_cached_debug_buffer(target); + } + + return retval; + } + + LOG_TARGET_ERROR(target, "dmi_write is not implemented for this target."); + return ERROR_FAIL; +} + + +COMMAND_HANDLER(riscv_dm_read) +{ + if (CMD_ARGC != 1) { + LOG_ERROR("Command takes 1 parameter"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + struct target *target = get_current_target(CMD_CTX); + if (!target) { + LOG_ERROR("target is NULL!"); + return ERROR_FAIL; + } + + RISCV_INFO(r); + if (!r) { + LOG_TARGET_ERROR(target, "riscv_info is NULL!"); + return ERROR_FAIL; + } + + if (r->dm_read) { + uint32_t address, value; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address); + if (r->dm_read(target, &value, address) != ERROR_OK) + return ERROR_FAIL; + command_print(CMD, "0x%" PRIx32, value); } else { - LOG_ERROR("dmi_write is not implemented for this target."); + LOG_TARGET_ERROR(target, "dm_read is not implemented for this target."); return ERROR_FAIL; } + return ERROR_OK; +} + +COMMAND_HANDLER(riscv_dm_write) +{ + if (CMD_ARGC != 2) { + LOG_ERROR("Command takes exactly 2 arguments"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + + uint32_t address, value; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value); + + if (r->dm_write) { + /* Perform the DM write */ + int retval = r->dm_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 progbuf_touched = (address >= DM_PROGBUF0 && address <= DM_PROGBUF15); + bool dm_deactivated = (address == DM_DMCONTROL && (value & DM_DMCONTROL_DMACTIVE) == 0); + if (progbuf_touched || dm_deactivated) { + if (r->invalidate_cached_debug_buffer) + r->invalidate_cached_debug_buffer(target); + } + + return retval; + } + + LOG_TARGET_ERROR(target, "dm_write is not implemented for this target."); + return ERROR_FAIL; } COMMAND_HANDLER(riscv_reset_delays) @@ -2794,6 +3790,48 @@ 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) { @@ -2806,43 +3844,462 @@ COMMAND_HANDLER(riscv_set_enable_virt2phys) COMMAND_HANDLER(riscv_set_ebreakm) { + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + + if (CMD_ARGC == 0) { + command_print(CMD, "riscv_ebreakm enabled: %s", r->riscv_ebreakm ? "on" : "off"); + return ERROR_OK; + } else if (CMD_ARGC == 1) { + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], r->riscv_ebreakm); + return ERROR_OK; + } + + LOG_ERROR("Command takes 0 or 1 parameters"); + return ERROR_COMMAND_SYNTAX_ERROR; +} + +COMMAND_HANDLER(riscv_set_ebreaks) +{ + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + + if (CMD_ARGC == 0) { + command_print(CMD, "riscv_ebreaks enabled: %s", r->riscv_ebreaks ? "on" : "off"); + return ERROR_OK; + } else if (CMD_ARGC == 1) { + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], r->riscv_ebreaks); + return ERROR_OK; + } + + LOG_ERROR("Command takes 0 or 1 parameters"); + return ERROR_COMMAND_SYNTAX_ERROR; +} + +COMMAND_HANDLER(riscv_set_ebreaku) +{ + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + + if (CMD_ARGC == 0) { + command_print(CMD, "riscv_ebreaku enabled: %s", r->riscv_ebreaku ? "on" : "off"); + return ERROR_OK; + } else if (CMD_ARGC == 1) { + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], r->riscv_ebreaku); + return ERROR_OK; + } + + LOG_ERROR("Command takes 0 or 1 parameters"); + return ERROR_COMMAND_SYNTAX_ERROR; +} + +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("Command takes exactly 1 parameter"); + 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; } - COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_ebreakm); return ERROR_OK; } -COMMAND_HANDLER(riscv_set_ebreaks) +COMMAND_HANDLER(riscv_icount) { - if (CMD_ARGC != 1) { - LOG_ERROR("Command takes exactly 1 parameter"); + 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; } - COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_ebreaks); return ERROR_OK; } -COMMAND_HANDLER(riscv_set_ebreaku) +COMMAND_HANDLER(riscv_etrigger) { - if (CMD_ARGC != 1) { - LOG_ERROR("Command takes exactly 1 parameter"); + 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; } - COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_ebreaku); return ERROR_OK; } -COMMAND_HELPER(riscv_print_info_line, const char *section, const char *key, - unsigned int value) +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 int 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_TARGET_ERROR(target, "Max bucket number is %zd.", 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_TARGET_ERROR(target, "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_TARGET_ERROR(target, "Failed base64 encode!"); + result = ERROR_FAIL; + goto error; + } + command_print(CMD, "%s", encoded); + free(encoded); + } else { + unsigned int 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_TARGET_ERROR(target, "Found invalid size in bucket %d: %d", command, + r->sample_config.bucket[command].size_bytes); + result = ERROR_FAIL; + goto error; + } + } else { + LOG_TARGET_ERROR(target, "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; +} + +static COMMAND_HELPER(riscv_print_info_line_if_available, const char *section, + const char *key, unsigned int value, bool is_available) { char full_key[80]; snprintf(full_key, sizeof(full_key), "%s.%s", section, key); - command_print(CMD, "%-21s %3d", full_key, value); + if (is_available) + command_print(CMD, "%-21s %3d", full_key, value); + else + command_print(CMD, "%-21s unavailable", full_key); return 0; } +COMMAND_HELPER(riscv_print_info_line, const char *section, const char *key, + unsigned int value) +{ + return CALL_COMMAND_HANDLER(riscv_print_info_line_if_available, section, + key, value, /*is_available*/ true); +} + COMMAND_HANDLER(handle_info) { struct target *target = get_current_target(CMD_CTX); @@ -2851,25 +4308,151 @@ COMMAND_HANDLER(handle_info) /* This output format can be fed directly into TCL's "array set". */ riscv_print_info_line(CMD, "hart", "xlen", riscv_xlen(target)); - riscv_enumerate_triggers(target); - riscv_print_info_line(CMD, "hart", "trigger_count", - r->trigger_count); + const bool trigger_count_available = + riscv_enumerate_triggers(target) == ERROR_OK; + riscv_print_info_line_if_available(CMD, "hart", "trigger_count", + r->trigger_count, trigger_count_available); if (r->print_info) return CALL_COMMAND_HANDLER(r->print_info, target); 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 != DTM_DTMCS_VERSION_1_0) { + 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; +} + +COMMAND_HANDLER(riscv_set_enable_eq_match_trigger) +{ + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + + if (CMD_ARGC == 0) { + command_print(CMD, "equality match trigger enabled: %s", r->enable_equality_match_trigger ? "on" : "off"); + return ERROR_OK; + } else if (CMD_ARGC == 1) { + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], r->enable_equality_match_trigger); + return ERROR_OK; + } + + LOG_ERROR("Command takes 0 or 1 parameters"); + return ERROR_COMMAND_SYNTAX_ERROR; +} + +COMMAND_HANDLER(riscv_set_enable_napot_trigger) +{ + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + + if (CMD_ARGC == 0) { + command_print(CMD, "NAPOT trigger enabled: %s", r->enable_napot_trigger ? "on" : "off"); + return ERROR_OK; + } else if (CMD_ARGC == 1) { + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], r->enable_napot_trigger); + return ERROR_OK; + } + + LOG_ERROR("Command takes 0 or 1 parameters"); + return ERROR_COMMAND_SYNTAX_ERROR; +} + +COMMAND_HANDLER(riscv_set_enable_ge_lt_trigger) +{ + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + + if (CMD_ARGC == 0) { + command_print(CMD, "ge-lt triggers enabled: %s", r->enable_ge_lt_trigger ? "on" : "off"); + return ERROR_OK; + } else if (CMD_ARGC == 1) { + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], r->enable_ge_lt_trigger); + return ERROR_OK; + } + + LOG_ERROR("Command takes 0 or 1 parameters"); + return ERROR_COMMAND_SYNTAX_ERROR; +} + 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, @@ -2919,6 +4502,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]", @@ -2949,6 +4542,20 @@ static const struct command_registration riscv_exec_command_handlers[] = { .help = "Perform a 32-bit DMI write of value at address." }, { + .name = "dm_read", + .handler = riscv_dm_read, + .mode = COMMAND_ANY, + .usage = "reg_address", + .help = "Perform a 32-bit read from DM register at reg_address, returning the value." + }, + { + .name = "dm_write", + .handler = riscv_dm_write, + .mode = COMMAND_ANY, + .usage = "reg_address value", + .help = "Write a 32-bit value to the DM register at reg_address." + }, + { .name = "reset_delays", .handler = riscv_reset_delays, .mode = COMMAND_ANY, @@ -2986,6 +4593,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, @@ -2997,7 +4620,7 @@ static const struct command_registration riscv_exec_command_handlers[] = { .name = "set_ebreakm", .handler = riscv_set_ebreakm, .mode = COMMAND_ANY, - .usage = "on|off", + .usage = "[on|off]", .help = "Control dcsr.ebreakm. When off, M-mode ebreak instructions " "don't trap to OpenOCD. Defaults to on." }, @@ -3005,7 +4628,7 @@ static const struct command_registration riscv_exec_command_handlers[] = { .name = "set_ebreaks", .handler = riscv_set_ebreaks, .mode = COMMAND_ANY, - .usage = "on|off", + .usage = "[on|off]", .help = "Control dcsr.ebreaks. When off, S-mode ebreak instructions " "don't trap to OpenOCD. Defaults to on." }, @@ -3013,10 +4636,60 @@ static const struct command_registration riscv_exec_command_handlers[] = { .name = "set_ebreaku", .handler = riscv_set_ebreaku, .mode = COMMAND_ANY, - .usage = "on|off", + .usage = "[on|off]", .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." + }, + { + .name = "set_enable_eq_match_trigger", + .handler = riscv_set_enable_eq_match_trigger, + .mode = COMMAND_CONFIG, + .usage = "[on|off]", + .help = "When on, allow OpenOCD to use equality match trigger in wp." + }, + { + .name = "set_enable_napot_trigger", + .handler = riscv_set_enable_napot_trigger, + .mode = COMMAND_CONFIG, + .usage = "[on|off]", + .help = "When on, allow OpenOCD to use NAPOT trigger in wp." + }, + { + .name = "set_enable_ge_lt_trigger", + .handler = riscv_set_enable_ge_lt_trigger, + .mode = COMMAND_CONFIG, + .usage = "[on|off]", + .help = "When on, allow OpenOCD to use GE/LT triggers in wp." + }, COMMAND_REGISTRATION_DONE }; @@ -3049,10 +4722,13 @@ static const struct command_registration riscv_command_handlers[] = { .usage = "", .chain = semihosting_common_handlers }, + { + .chain = smp_command_handlers + }, COMMAND_REGISTRATION_DONE }; -static unsigned riscv_xlen_nonconst(struct target *target) +static unsigned int riscv_xlen_nonconst(struct target *target) { return riscv_xlen(target); } @@ -3123,14 +4799,15 @@ 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->dtm_version = DTM_DTMCS_VERSION_UNKNOWN; 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; @@ -3141,48 +4818,95 @@ 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; + + r->riscv_ebreakm = true; + r->riscv_ebreaks = true; + r->riscv_ebreaku = true; + + r->enable_equality_match_trigger = true; + r->enable_ge_lt_trigger = true; + r->enable_napot_trigger = true; } 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 { - LOG_DEBUG("[%s] hart requested resume, but was already resumed", - target_name(target)); + LOG_TARGET_DEBUG(target, "Hart requested resume, but was already resumed."); } riscv_invalidate_register_cache(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_TARGET_DEBUG(target, "Disabling interrupts."); + struct reg *reg_mstatus = register_get_by_name(target->reg_cache, + "mstatus", true); + if (!reg_mstatus) { + LOG_TARGET_ERROR(target, "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_TARGET_DEBUG(target, "Restoring interrupts."); + struct reg *reg_mstatus = register_get_by_name(target->reg_cache, + "mstatus", true); + if (!reg_mstatus) { + LOG_TARGET_ERROR(target, "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)); + LOG_TARGET_DEBUG(target, "Stepping."); - if (!riscv_is_halted(target)) { - LOG_ERROR("Hart isn't halted before single step!"); + if (target->state != TARGET_HALTED) { + LOG_TARGET_ERROR(target, "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)) { - LOG_ERROR("Hart was not halted after single step!"); + if (target->state != TARGET_HALTED) { + LOG_TARGET_ERROR(target, "Hart was not halted after single step!"); return ERROR_FAIL; } return ERROR_OK; @@ -3207,39 +4931,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; @@ -3257,8 +4961,11 @@ 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) { + if (regno == GDB_REGNO_ZERO) + return !is_write; + /* GPRs, FPRs, vector registers are just normal data stores. */ if (regno <= GDB_REGNO_XPR31 || (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) || @@ -3288,98 +4995,225 @@ 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. */ - case GDB_REGNO_TDATA2: /* Changse value when tselect is changed. */ + case GDB_REGNO_TDATA2: /* Changes value when tselect is changed. */ default: return false; } } /** - * 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 (regid == GDB_REGNO_PC) { + return riscv_set_or_write_register(target, GDB_REGNO_DPC, value, write_through); + } else if (regid == GDB_REGNO_PRIV) { + riscv_reg_t dcsr; + + if (riscv_get_register(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 riscv_set_or_write_register(target, GDB_REGNO_DCSR, dcsr, write_through); + } + + if (!target->reg_cache) { + assert(!target_was_examined(target)); + LOG_TARGET_DEBUG(target, + "No cache, writing to target: %s <- 0x%" PRIx64, + gdb_regno_name(target, 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 (regid == GDB_REGNO_PC) { + return riscv_get_register(target, value, GDB_REGNO_DPC); + } else if (regid == GDB_REGNO_PRIV) { + uint64_t dcsr; + if (riscv_get_register(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)); + return ERROR_OK; + } + + if (!target->reg_cache) { + assert(!target_was_examined(target)); + LOG_TARGET_DEBUG(target, "No cache, reading %s from target", + gdb_regno_name(target, 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) +{ + if (target->state != TARGET_HALTED) { + LOG_TARGET_ERROR(target, "Can't save register %s on a hart that is not halted.", + gdb_regno_name(target, regid)); + return ERROR_FAIL; + } + 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); - if (result == ERROR_OK) - reg->valid = gdb_regno_cacheable(regid, false); + LOG_TARGET_DEBUG(target, "Saving %s", reg->name); + if (riscv_get_register(target, &value, regid) != ERROR_OK) + return ERROR_FAIL; - LOG_DEBUG("[%s] %s: %" PRIx64, target_name(target), - gdb_regno_name(regid), *value); - return result; + 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; + + 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)) { - LOG_ERROR("Hart is not halted!"); + if (target->state != TARGET_HALTED) { + LOG_TARGET_ERROR(target, "Hart is not halted!"); return RISCV_HALT_UNKNOWN; } return r->halt_reason(target); @@ -3410,22 +5244,22 @@ int riscv_execute_debug_buffer(struct target *target) return r->execute_debug_buffer(target); } -void riscv_fill_dmi_write_u64(struct target *target, char *buf, int a, uint64_t d) +void riscv_fill_dm_write_u64(struct target *target, char *buf, int a, uint64_t d) { RISCV_INFO(r); - r->fill_dmi_write_u64(target, buf, a, d); + r->fill_dm_write_u64(target, buf, a, d); } -void riscv_fill_dmi_read_u64(struct target *target, char *buf, int a) +void riscv_fill_dm_read_u64(struct target *target, char *buf, int a) { RISCV_INFO(r); - r->fill_dmi_read_u64(target, buf, a); + r->fill_dm_read_u64(target, buf, a); } -void riscv_fill_dmi_nop_u64(struct target *target, char *buf) +void riscv_fill_dm_nop_u64(struct target *target, char *buf) { RISCV_INFO(r); - r->fill_dmi_nop_u64(target, buf); + r->fill_dm_nop_u64(target, buf); } int riscv_dmi_write_u64_bits(struct target *target) @@ -3434,13 +5268,85 @@ int riscv_dmi_write_u64_bits(struct target *target) return r->dmi_write_u64_bits(target); } +static int check_if_trigger_exists(struct target *target, unsigned int index) +{ + /* If we can't write tselect, then this hart does not support triggers. */ + if (riscv_set_register(target, GDB_REGNO_TSELECT, index) != ERROR_OK) + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + riscv_reg_t tselect_rb; + if (riscv_get_register(target, &tselect_rb, GDB_REGNO_TSELECT) != ERROR_OK) + return ERROR_FAIL; + /* Mask off the top bit, which is used as tdrmode in legacy RISC-V Debug Spec + * (old revisions of v0.11 spec). */ + tselect_rb &= ~(1ULL << (riscv_xlen(target) - 1)); + if (tselect_rb != index) + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + return ERROR_OK; +} + +/** + * This function reads `tinfo` or `tdata1`, when reading `tinfo` fails, + * to determine trigger types supported by a trigger. + * It is assumed that the trigger is already selected via writing `tselect`. + */ +static int get_trigger_types(struct target *target, unsigned int *trigger_tinfo, + riscv_reg_t tdata1) +{ + assert(trigger_tinfo); + riscv_reg_t tinfo; + if (riscv_get_register(target, &tinfo, GDB_REGNO_TINFO) == ERROR_OK) { + /* tinfo.INFO == 1: trigger doesn’t exist + * tinfo == 0 or tinfo.INFO != 1 and tinfo LSB is set: invalid tinfo */ + if (tinfo == 0 || tinfo & 0x1) + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + *trigger_tinfo = tinfo; + return ERROR_OK; + } + const unsigned int type = get_field(tdata1, CSR_TDATA1_TYPE(riscv_xlen(target))); + if (type == 0) + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + *trigger_tinfo = 1 << type; + return ERROR_OK; +} + +static int disable_trigger_if_dmode(struct target *target, riscv_reg_t tdata1) +{ + bool dmode_is_set = false; + switch (get_field(tdata1, CSR_TDATA1_TYPE(riscv_xlen(target)))) { + case CSR_TDATA1_TYPE_LEGACY: + /* On these older cores we don't support software using + * triggers. */ + dmode_is_set = true; + break; + case CSR_TDATA1_TYPE_MCONTROL: + dmode_is_set = tdata1 & CSR_MCONTROL_DMODE(riscv_xlen(target)); + break; + case CSR_TDATA1_TYPE_MCONTROL6: + dmode_is_set = tdata1 & CSR_MCONTROL6_DMODE(riscv_xlen(target)); + break; + case CSR_TDATA1_TYPE_ICOUNT: + dmode_is_set = tdata1 & CSR_ICOUNT_DMODE(riscv_xlen(target)); + break; + case CSR_TDATA1_TYPE_ITRIGGER: + dmode_is_set = tdata1 & CSR_ITRIGGER_DMODE(riscv_xlen(target)); + break; + case CSR_TDATA1_TYPE_ETRIGGER: + dmode_is_set = tdata1 & CSR_ETRIGGER_DMODE(riscv_xlen(target)); + break; + } + if (!dmode_is_set) + /* Nothing to do */ + return ERROR_OK; + return riscv_set_register(target, GDB_REGNO_TDATA1, 0); +} + /** * Count triggers, and initialize trigger_count for each hart. * trigger_count is initialized even if this function fails to discover * something. * Disable any hardware triggers that have dmode set. We can't have set them * ourselves. Maybe they're left over from some killed debug session. - * */ + */ int riscv_enumerate_triggers(struct target *target) { RISCV_INFO(r); @@ -3448,264 +5354,253 @@ int riscv_enumerate_triggers(struct target *target) if (r->triggers_enumerated) return ERROR_OK; - r->triggers_enumerated = true; /* At the very least we tried. */ + if (target->state != TARGET_HALTED) { + LOG_TARGET_ERROR(target, "Unable to enumerate triggers: target not halted."); + return ERROR_FAIL; + } - riscv_reg_t tselect; - int result = riscv_get_register(target, &tselect, GDB_REGNO_TSELECT); + riscv_reg_t orig_tselect; + int result = riscv_get_register(target, &orig_tselect, GDB_REGNO_TSELECT); /* If tselect is not readable, the trigger module is likely not - * implemented. There are no triggers to enumerate then and no error - * should be thrown. */ + * implemented. */ if (result != ERROR_OK) { - LOG_DEBUG("[%s] Cannot access tselect register. " - "Assuming that triggers are not implemented.", target_name(target)); + LOG_TARGET_INFO(target, "Cannot access tselect register. " + "Assuming that triggers are not implemented."); + r->triggers_enumerated = true; r->trigger_count = 0; return ERROR_OK; } - for (unsigned int t = 0; t < RISCV_MAX_TRIGGERS; ++t) { - r->trigger_count = t; - - /* If we can't write tselect, then this hart does not support triggers. */ - if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK) - break; - uint64_t tselect_rb; - result = riscv_get_register(target, &tselect_rb, GDB_REGNO_TSELECT); - if (result != ERROR_OK) - return result; - /* Mask off the top bit, which is used as tdrmode in old - * implementations. */ - tselect_rb &= ~(1ULL << (riscv_xlen(target) - 1)); - if (tselect_rb != t) + unsigned int t = 0; + for (; t < ARRAY_SIZE(r->trigger_tinfo); ++t) { + result = check_if_trigger_exists(target, t); + if (result == ERROR_FAIL) + return ERROR_FAIL; + if (result == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) 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) + riscv_reg_t tdata1; + if (riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK) + return ERROR_FAIL; + + result = get_trigger_types(target, &r->trigger_tinfo[t], tdata1); + if (result == ERROR_FAIL) + return ERROR_FAIL; + if (result == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) break; - switch (type) { - case 1: - /* On these older cores we don't support software using - * triggers. */ - riscv_set_register(target, GDB_REGNO_TDATA1, 0); - break; - case 2: - if (tdata1 & MCONTROL_DMODE(riscv_xlen(target))) - riscv_set_register(target, GDB_REGNO_TDATA1, 0); - break; - case 6: - if (tdata1 & MCONTROL_DMODE(riscv_xlen(target))) - riscv_set_register(target, GDB_REGNO_TDATA1, 0); - break; - } - } - riscv_set_register(target, GDB_REGNO_TSELECT, tselect); + LOG_TARGET_DEBUG(target, "Trigger %u: supported types (mask) = 0x%08x", + t, r->trigger_tinfo[t]); - LOG_INFO("[%s] Found %d triggers", target_name(target), r->trigger_count); + if (disable_trigger_if_dmode(target, tdata1) != ERROR_OK) + return ERROR_FAIL; + } + + if (riscv_set_register(target, GDB_REGNO_TSELECT, orig_tselect) != ERROR_OK) + return ERROR_FAIL; + r->triggers_enumerated = true; + r->trigger_count = t; + LOG_TARGET_INFO(target, "Found %d triggers", r->trigger_count); return ERROR_OK; } -const char *gdb_regno_name(enum gdb_regno regno) +static char *init_reg_name(const char *name) { - static char buf[32]; + const int size_buf = strlen(name) + 1; - switch (regno) { - case GDB_REGNO_ZERO: - return "zero"; - case GDB_REGNO_RA: - return "ra"; - case GDB_REGNO_SP: - return "sp"; - case GDB_REGNO_GP: - return "gp"; - case GDB_REGNO_TP: - return "tp"; - case GDB_REGNO_T0: - return "t0"; - case GDB_REGNO_T1: - return "t1"; - case GDB_REGNO_T2: - return "t2"; - case GDB_REGNO_S0: - return "s0"; - case GDB_REGNO_S1: - return "s1"; - case GDB_REGNO_A0: - return "a0"; - case GDB_REGNO_A1: - return "a1"; - case GDB_REGNO_A2: - return "a2"; - case GDB_REGNO_A3: - return "a3"; - case GDB_REGNO_A4: - return "a4"; - case GDB_REGNO_A5: - return "a5"; - case GDB_REGNO_A6: - return "a6"; - case GDB_REGNO_A7: - return "a7"; - case GDB_REGNO_S2: - return "s2"; - case GDB_REGNO_S3: - return "s3"; - case GDB_REGNO_S4: - return "s4"; - case GDB_REGNO_S5: - return "s5"; - case GDB_REGNO_S6: - return "s6"; - case GDB_REGNO_S7: - return "s7"; - case GDB_REGNO_S8: - return "s8"; - case GDB_REGNO_S9: - return "s9"; - case GDB_REGNO_S10: - return "s10"; - case GDB_REGNO_S11: - return "s11"; - case GDB_REGNO_T3: - return "t3"; - case GDB_REGNO_T4: - return "t4"; - case GDB_REGNO_T5: - return "t5"; - case GDB_REGNO_T6: - return "t6"; - case GDB_REGNO_PC: - return "pc"; - case GDB_REGNO_FPR0: - return "fpr0"; - case GDB_REGNO_FPR31: - return "fpr31"; - case GDB_REGNO_CSR0: - return "csr0"; - case GDB_REGNO_TSELECT: - return "tselect"; - case GDB_REGNO_TDATA1: - return "tdata1"; - case GDB_REGNO_TDATA2: - return "tdata2"; - case GDB_REGNO_MISA: - return "misa"; - case GDB_REGNO_DPC: - return "dpc"; - case GDB_REGNO_DCSR: - return "dcsr"; - case GDB_REGNO_DSCRATCH0: - return "dscratch0"; - case GDB_REGNO_MSTATUS: - return "mstatus"; - case GDB_REGNO_MEPC: - return "mepc"; - case GDB_REGNO_MCAUSE: - return "mcause"; - case GDB_REGNO_PRIV: - return "priv"; - case GDB_REGNO_SATP: - return "satp"; - case GDB_REGNO_VTYPE: - return "vtype"; - case GDB_REGNO_VL: - return "vl"; - case GDB_REGNO_V0: - return "v0"; - case GDB_REGNO_V1: - return "v1"; - case GDB_REGNO_V2: - return "v2"; - case GDB_REGNO_V3: - return "v3"; - case GDB_REGNO_V4: - return "v4"; - case GDB_REGNO_V5: - return "v5"; - case GDB_REGNO_V6: - return "v6"; - case GDB_REGNO_V7: - return "v7"; - case GDB_REGNO_V8: - return "v8"; - case GDB_REGNO_V9: - return "v9"; - case GDB_REGNO_V10: - return "v10"; - case GDB_REGNO_V11: - return "v11"; - case GDB_REGNO_V12: - return "v12"; - case GDB_REGNO_V13: - return "v13"; - case GDB_REGNO_V14: - return "v14"; - case GDB_REGNO_V15: - return "v15"; - case GDB_REGNO_V16: - return "v16"; - case GDB_REGNO_V17: - return "v17"; - case GDB_REGNO_V18: - return "v18"; - case GDB_REGNO_V19: - return "v19"; - case GDB_REGNO_V20: - return "v20"; - case GDB_REGNO_V21: - return "v21"; - case GDB_REGNO_V22: - return "v22"; - case GDB_REGNO_V23: - return "v23"; - case GDB_REGNO_V24: - return "v24"; - case GDB_REGNO_V25: - return "v25"; - case GDB_REGNO_V26: - return "v26"; - case GDB_REGNO_V27: - return "v27"; - case GDB_REGNO_V28: - return "v28"; - case GDB_REGNO_V29: - return "v29"; - case GDB_REGNO_V30: - return "v30"; - case GDB_REGNO_V31: - return "v31"; - default: - if (regno <= GDB_REGNO_XPR31) - sprintf(buf, "x%d", regno - GDB_REGNO_ZERO); - else if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095) - sprintf(buf, "csr%d", regno - GDB_REGNO_CSR0); - else if (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) - sprintf(buf, "f%d", regno - GDB_REGNO_FPR0); - else - sprintf(buf, "gdb_regno_%d", regno); - return buf; + char * const buf = calloc(size_buf, sizeof(char)); + if (!buf) { + LOG_ERROR("Failed to allocate memory for a register name."); + return NULL; + } + strcpy(buf, name); + return buf; +} + +static char *init_reg_name_with_prefix(const char *name_prefix, + unsigned int num) +{ + const int size_buf = snprintf(NULL, 0, "%s%d", name_prefix, num) + 1; + + char * const buf = calloc(size_buf, sizeof(char)); + if (!buf) { + LOG_ERROR("Failed to allocate memory for a register name."); + return NULL; } + int result = snprintf(buf, size_buf, "%s%d", name_prefix, num); + assert(result > 0 && result <= (size_buf - 1)); + return buf; } +static const char * const default_reg_names[GDB_REGNO_COUNT] = { + [GDB_REGNO_ZERO] = "zero", + [GDB_REGNO_RA] = "ra", + [GDB_REGNO_SP] = "sp", + [GDB_REGNO_GP] = "gp", + [GDB_REGNO_TP] = "tp", + [GDB_REGNO_T0] = "t0", + [GDB_REGNO_T1] = "t1", + [GDB_REGNO_T2] = "t2", + [GDB_REGNO_FP] = "fp", + [GDB_REGNO_S1] = "s1", + [GDB_REGNO_A0] = "a0", + [GDB_REGNO_A1] = "a1", + [GDB_REGNO_A2] = "a2", + [GDB_REGNO_A3] = "a3", + [GDB_REGNO_A4] = "a4", + [GDB_REGNO_A5] = "a5", + [GDB_REGNO_A6] = "a6", + [GDB_REGNO_A7] = "a7", + [GDB_REGNO_S2] = "s2", + [GDB_REGNO_S3] = "s3", + [GDB_REGNO_S4] = "s4", + [GDB_REGNO_S5] = "s5", + [GDB_REGNO_S6] = "s6", + [GDB_REGNO_S7] = "s7", + [GDB_REGNO_S8] = "s8", + [GDB_REGNO_S9] = "s9", + [GDB_REGNO_S10] = "s10", + [GDB_REGNO_S11] = "s11", + [GDB_REGNO_T3] = "t3", + [GDB_REGNO_T4] = "t4", + [GDB_REGNO_T5] = "t5", + [GDB_REGNO_T6] = "t6", + [GDB_REGNO_PC] = "pc", + [GDB_REGNO_CSR0] = "csr0", + [GDB_REGNO_PRIV] = "priv", + [GDB_REGNO_FT0] = "ft0", + [GDB_REGNO_FT1] = "ft1", + [GDB_REGNO_FT2] = "ft2", + [GDB_REGNO_FT3] = "ft3", + [GDB_REGNO_FT4] = "ft4", + [GDB_REGNO_FT5] = "ft5", + [GDB_REGNO_FT6] = "ft6", + [GDB_REGNO_FT7] = "ft7", + [GDB_REGNO_FS0] = "fs0", + [GDB_REGNO_FS1] = "fs1", + [GDB_REGNO_FA0] = "fa0", + [GDB_REGNO_FA1] = "fa1", + [GDB_REGNO_FA2] = "fa2", + [GDB_REGNO_FA3] = "fa3", + [GDB_REGNO_FA4] = "fa4", + [GDB_REGNO_FA5] = "fa5", + [GDB_REGNO_FA6] = "fa6", + [GDB_REGNO_FA7] = "fa7", + [GDB_REGNO_FS2] = "fs2", + [GDB_REGNO_FS3] = "fs3", + [GDB_REGNO_FS4] = "fs4", + [GDB_REGNO_FS5] = "fs5", + [GDB_REGNO_FS6] = "fs6", + [GDB_REGNO_FS7] = "fs7", + [GDB_REGNO_FS8] = "fs8", + [GDB_REGNO_FS9] = "fs9", + [GDB_REGNO_FS10] = "fs10", + [GDB_REGNO_FS11] = "fs11", + [GDB_REGNO_FT8] = "ft8", + [GDB_REGNO_FT9] = "ft9", + [GDB_REGNO_FT10] = "ft10", + [GDB_REGNO_FT11] = "ft11", + + #define DECLARE_CSR(csr_name, number)[(number) + GDB_REGNO_CSR0] = #csr_name, + #include "encoding.h" + #undef DECLARE_CSR +}; + +static void free_reg_names(struct target *target) +{ + RISCV_INFO(info); + + if (!info->reg_names) + return; + + for (unsigned int i = 0; i < GDB_REGNO_COUNT; ++i) + free(info->reg_names[i]); + free(info->reg_names); + info->reg_names = NULL; + + free_custom_register_names(target); +} + +static void init_custom_csr_names(struct target *target) +{ + RISCV_INFO(info); + range_list_t *entry; + + list_for_each_entry(entry, &info->expose_csr, list) { + if (!entry->name) + continue; + assert(entry->low == entry->high); + const unsigned int regno = entry->low + GDB_REGNO_CSR0; + assert(regno <= GDB_REGNO_CSR4095); + if (info->reg_names[regno]) + return; + info->reg_names[regno] = init_reg_name(entry->name); + } +} + +const char *gdb_regno_name(struct target *target, unsigned int regno) +{ + RISCV_INFO(info); + + if (regno >= GDB_REGNO_COUNT) { + assert(info->custom_register_names.reg_names); + assert(regno - GDB_REGNO_COUNT <= info->custom_register_names.num_entries); + return info->custom_register_names.reg_names[regno - GDB_REGNO_COUNT]; + } + + if (!info->reg_names) + info->reg_names = calloc(GDB_REGNO_COUNT, sizeof(char *)); + + if (info->reg_names[regno]) + return info->reg_names[regno]; + if (default_reg_names[regno]) + return default_reg_names[regno]; + if (regno <= GDB_REGNO_XPR31) { + info->reg_names[regno] = init_reg_name_with_prefix("x", regno - GDB_REGNO_ZERO); + return info->reg_names[regno]; + } + if (regno <= GDB_REGNO_V31 && regno >= GDB_REGNO_V0) { + info->reg_names[regno] = init_reg_name_with_prefix("v", regno - GDB_REGNO_V0); + return info->reg_names[regno]; + } + if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095) { + init_custom_csr_names(target); + info->reg_names[regno] = init_reg_name_with_prefix("csr", regno - GDB_REGNO_CSR0); + return info->reg_names[regno]; + } + assert(!"Encountered uninitialized entry in reg_names table"); + + return NULL; +} + +/** + * 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); @@ -3713,33 +5608,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; @@ -3753,15 +5647,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; } @@ -3774,14 +5672,54 @@ static struct reg_arch_type riscv_reg_arch_type = { .set = register_set }; -struct csr_info { - unsigned number; - const char *name; -}; +static int init_custom_register_names(struct list_head *expose_custom, + struct reg_name_table *custom_register_names) +{ + unsigned int custom_regs_num = 0; + if (!list_empty(expose_custom)) { + range_list_t *entry; + list_for_each_entry(entry, expose_custom, list) + custom_regs_num += entry->high - entry->low + 1; + } + + if (!custom_regs_num) + return ERROR_OK; -static int cmp_csr_info(const void *p1, const void *p2) + custom_register_names->reg_names = calloc(custom_regs_num, sizeof(char *)); + if (!custom_register_names->reg_names) { + LOG_ERROR("Failed to allocate memory for custom_register_names->reg_names"); + return ERROR_FAIL; + } + custom_register_names->num_entries = custom_regs_num; + char **reg_names = custom_register_names->reg_names; + range_list_t *range; + unsigned int next_custom_reg_index = 0; + list_for_each_entry(range, expose_custom, list) { + for (unsigned int custom_number = range->low; custom_number <= range->high; ++custom_number) { + if (range->name) + reg_names[next_custom_reg_index] = init_reg_name(range->name); + else + reg_names[next_custom_reg_index] = + init_reg_name_with_prefix("custom", custom_number); + + if (!reg_names[next_custom_reg_index]) + return ERROR_FAIL; + ++next_custom_reg_index; + } + } + return ERROR_OK; +} + +static bool is_known_standard_csr(unsigned int csr_num) { - return (int) (((struct csr_info *)p1)->number) - (int) (((struct csr_info *)p2)->number); + static const bool is_csr_in_buf[GDB_REGNO_CSR4095 - GDB_REGNO_CSR0 + 1] = { + #define DECLARE_CSR(csr_name, number)[number] = true, + #include "encoding.h" + #undef DECLARE_CSR + }; + assert(csr_num < ARRAY_SIZE(is_csr_in_buf)); + + return is_csr_in_buf[csr_num]; } int riscv_init_registers(struct target *target) @@ -3791,32 +5729,27 @@ int riscv_init_registers(struct target *target) riscv_free_registers(target); target->reg_cache = calloc(1, sizeof(*target->reg_cache)); - if (!target->reg_cache) + if (!target->reg_cache) { + LOG_TARGET_ERROR(target, "Failed to allocate memory for target->reg_cache"); return ERROR_FAIL; + } target->reg_cache->name = "RISC-V Registers"; - target->reg_cache->num_regs = GDB_REGNO_COUNT; - if (!list_empty(&info->expose_custom)) { - range_list_t *entry; - list_for_each_entry(entry, &info->expose_custom, list) - target->reg_cache->num_regs += entry->high - entry->low + 1; + if (init_custom_register_names(&info->expose_custom, &info->custom_register_names) != ERROR_OK) { + LOG_TARGET_ERROR(target, "init_custom_register_names failed"); + return ERROR_FAIL; } - LOG_DEBUG("create register cache for %d registers", + target->reg_cache->num_regs = GDB_REGNO_COUNT + info->custom_register_names.num_entries; + LOG_TARGET_DEBUG(target, "create register cache for %d registers", target->reg_cache->num_regs); target->reg_cache->reg_list = calloc(target->reg_cache->num_regs, sizeof(struct reg)); - if (!target->reg_cache->reg_list) + if (!target->reg_cache->reg_list) { + LOG_TARGET_ERROR(target, "Failed to allocate memory for target->reg_cache->reg_list"); return ERROR_FAIL; - - const unsigned int max_reg_name_len = 12; - free(info->reg_names); - info->reg_names = - calloc(target->reg_cache->num_regs, max_reg_name_len); - if (!info->reg_names) - return ERROR_FAIL; - char *reg_name = info->reg_names; + } static struct reg_feature feature_cpu = { .name = "org.gnu.gdb.riscv.cpu" @@ -3948,152 +5881,45 @@ int riscv_init_registers(struct target *target) info->type_vector.type_class = REG_TYPE_CLASS_UNION; info->type_vector.reg_type_union = &info->vector_union; - struct csr_info csr_info[] = { -#define DECLARE_CSR(name, number) { number, #name }, -#include "encoding.h" -#undef DECLARE_CSR - }; - /* encoding.h does not contain the registers in sorted order. */ - qsort(csr_info, ARRAY_SIZE(csr_info), sizeof(*csr_info), cmp_csr_info); - unsigned csr_info_index = 0; - - int custom_within_range = 0; - riscv_reg_info_t *shared_reg_info = calloc(1, sizeof(riscv_reg_info_t)); if (!shared_reg_info) return ERROR_FAIL; shared_reg_info->target = target; + int custom_within_range = 0; + /* When gdb requests register N, gdb_get_register_packet() assumes that this * is register at index N in reg_list. So if there are certain registers * that don't exist, we need to leave holes in the list (or renumber, but * it would be nice not to have yet another set of numbers to translate * between). */ - for (uint32_t number = 0; number < target->reg_cache->num_regs; number++) { - struct reg *r = &target->reg_cache->reg_list[number]; + for (uint32_t reg_num = 0; reg_num < target->reg_cache->num_regs; reg_num++) { + struct reg *r = &target->reg_cache->reg_list[reg_num]; r->dirty = false; r->valid = false; r->exist = true; r->type = &riscv_reg_arch_type; r->arch_info = shared_reg_info; - r->number = number; + r->number = reg_num; r->size = riscv_xlen(target); /* r->size is set in riscv_invalidate_register_cache, maybe because the * target is in theory allowed to change XLEN on us. But I expect a lot * of other things to break in that case as well. */ - if (number <= GDB_REGNO_XPR31) { - r->exist = number <= GDB_REGNO_XPR15 || + r->name = gdb_regno_name(target, reg_num); + if (reg_num <= GDB_REGNO_XPR31) { + r->exist = reg_num <= GDB_REGNO_XPR15 || !riscv_supports_extension(target, 'E'); /* TODO: For now we fake that all GPRs exist because otherwise gdb * doesn't work. */ r->exist = true; r->caller_save = true; - switch (number) { - case GDB_REGNO_ZERO: - r->name = "zero"; - break; - case GDB_REGNO_RA: - r->name = "ra"; - break; - case GDB_REGNO_SP: - r->name = "sp"; - break; - case GDB_REGNO_GP: - r->name = "gp"; - break; - case GDB_REGNO_TP: - r->name = "tp"; - break; - case GDB_REGNO_T0: - r->name = "t0"; - break; - case GDB_REGNO_T1: - r->name = "t1"; - break; - case GDB_REGNO_T2: - r->name = "t2"; - break; - case GDB_REGNO_FP: - r->name = "fp"; - break; - case GDB_REGNO_S1: - r->name = "s1"; - break; - case GDB_REGNO_A0: - r->name = "a0"; - break; - case GDB_REGNO_A1: - r->name = "a1"; - break; - case GDB_REGNO_A2: - r->name = "a2"; - break; - case GDB_REGNO_A3: - r->name = "a3"; - break; - case GDB_REGNO_A4: - r->name = "a4"; - break; - case GDB_REGNO_A5: - r->name = "a5"; - break; - case GDB_REGNO_A6: - r->name = "a6"; - break; - case GDB_REGNO_A7: - r->name = "a7"; - break; - case GDB_REGNO_S2: - r->name = "s2"; - break; - case GDB_REGNO_S3: - r->name = "s3"; - break; - case GDB_REGNO_S4: - r->name = "s4"; - break; - case GDB_REGNO_S5: - r->name = "s5"; - break; - case GDB_REGNO_S6: - r->name = "s6"; - break; - case GDB_REGNO_S7: - r->name = "s7"; - break; - case GDB_REGNO_S8: - r->name = "s8"; - break; - case GDB_REGNO_S9: - r->name = "s9"; - break; - case GDB_REGNO_S10: - r->name = "s10"; - break; - case GDB_REGNO_S11: - r->name = "s11"; - break; - case GDB_REGNO_T3: - r->name = "t3"; - break; - case GDB_REGNO_T4: - r->name = "t4"; - break; - case GDB_REGNO_T5: - r->name = "t5"; - break; - case GDB_REGNO_T6: - r->name = "t6"; - break; - } r->group = "general"; r->feature = &feature_cpu; - } else if (number == GDB_REGNO_PC) { + } else if (reg_num == GDB_REGNO_PC) { r->caller_save = true; - sprintf(reg_name, "pc"); r->group = "general"; r->feature = &feature_cpu; - } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { + } else if (reg_num >= GDB_REGNO_FPR0 && reg_num <= GDB_REGNO_FPR31) { r->caller_save = true; if (riscv_supports_extension(target, 'D')) { r->size = 64; @@ -4107,119 +5933,14 @@ int riscv_init_registers(struct target *target) } else { r->exist = false; } - switch (number) { - case GDB_REGNO_FT0: - r->name = "ft0"; - break; - case GDB_REGNO_FT1: - r->name = "ft1"; - break; - case GDB_REGNO_FT2: - r->name = "ft2"; - break; - case GDB_REGNO_FT3: - r->name = "ft3"; - break; - case GDB_REGNO_FT4: - r->name = "ft4"; - break; - case GDB_REGNO_FT5: - r->name = "ft5"; - break; - case GDB_REGNO_FT6: - r->name = "ft6"; - break; - case GDB_REGNO_FT7: - r->name = "ft7"; - break; - case GDB_REGNO_FS0: - r->name = "fs0"; - break; - case GDB_REGNO_FS1: - r->name = "fs1"; - break; - case GDB_REGNO_FA0: - r->name = "fa0"; - break; - case GDB_REGNO_FA1: - r->name = "fa1"; - break; - case GDB_REGNO_FA2: - r->name = "fa2"; - break; - case GDB_REGNO_FA3: - r->name = "fa3"; - break; - case GDB_REGNO_FA4: - r->name = "fa4"; - break; - case GDB_REGNO_FA5: - r->name = "fa5"; - break; - case GDB_REGNO_FA6: - r->name = "fa6"; - break; - case GDB_REGNO_FA7: - r->name = "fa7"; - break; - case GDB_REGNO_FS2: - r->name = "fs2"; - break; - case GDB_REGNO_FS3: - r->name = "fs3"; - break; - case GDB_REGNO_FS4: - r->name = "fs4"; - break; - case GDB_REGNO_FS5: - r->name = "fs5"; - break; - case GDB_REGNO_FS6: - r->name = "fs6"; - break; - case GDB_REGNO_FS7: - r->name = "fs7"; - break; - case GDB_REGNO_FS8: - r->name = "fs8"; - break; - case GDB_REGNO_FS9: - r->name = "fs9"; - break; - case GDB_REGNO_FS10: - r->name = "fs10"; - break; - case GDB_REGNO_FS11: - r->name = "fs11"; - break; - case GDB_REGNO_FT8: - r->name = "ft8"; - break; - case GDB_REGNO_FT9: - r->name = "ft9"; - break; - case GDB_REGNO_FT10: - r->name = "ft10"; - break; - case GDB_REGNO_FT11: - r->name = "ft11"; - break; - } r->group = "float"; r->feature = &feature_fpu; - } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { + } else if (reg_num >= GDB_REGNO_CSR0 && reg_num <= GDB_REGNO_CSR4095) { r->group = "csr"; r->feature = &feature_csr; - unsigned csr_number = number - GDB_REGNO_CSR0; + const unsigned int csr_num = reg_num - GDB_REGNO_CSR0; - while (csr_info[csr_info_index].number < csr_number && - csr_info_index < ARRAY_SIZE(csr_info) - 1) { - csr_info_index++; - } - if (csr_info[csr_info_index].number == csr_number) { - r->name = csr_info[csr_info_index].name; - } else { - sprintf(reg_name, "csr%d", csr_number); + if (!is_known_standard_csr(csr_num)) { /* Assume unnamed registers don't exist, unless we have some * configuration that tells us otherwise. That's important * because eg. Eclipse crashes if a target has too many @@ -4228,19 +5949,28 @@ int riscv_init_registers(struct target *target) r->exist = false; } - switch (csr_number) { + switch (csr_num) { + 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: @@ -4329,68 +6059,140 @@ 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; } if (!r->exist && !list_empty(&info->expose_csr)) { range_list_t *entry; list_for_each_entry(entry, &info->expose_csr, list) - if ((entry->low <= csr_number) && (csr_number <= entry->high)) { - if (entry->name) { - *reg_name = 0; - r->name = entry->name; - } - - LOG_DEBUG("Exposing additional CSR %d (name=%s)", - csr_number, entry->name ? entry->name : reg_name); - + if (entry->low <= csr_num && csr_num <= entry->high) { + LOG_TARGET_DEBUG(target, "Exposing additional CSR %d (name=%s)", + csr_num, r->name); 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_num && csr_num <= entry->high) { + LOG_TARGET_DEBUG(target, "Hiding CSR %d (name=%s).", csr_num, r->name); + r->hidden = true; + break; + } } - } else if (number == GDB_REGNO_PRIV) { - sprintf(reg_name, "priv"); + } else if (reg_num == GDB_REGNO_PRIV) { r->group = "general"; r->feature = &feature_virtual; r->size = 8; - } else if (number >= GDB_REGNO_V0 && number <= GDB_REGNO_V31) { + } else if (reg_num >= GDB_REGNO_V0 && reg_num <= 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"; r->feature = &feature_vector; r->reg_data_type = &info->type_vector; - } else if (number >= GDB_REGNO_COUNT) { + } else if (reg_num >= GDB_REGNO_COUNT) { /* Custom registers. */ + const unsigned int custom_reg_index = reg_num - GDB_REGNO_COUNT; + assert(!list_empty(&info->expose_custom)); + assert(custom_reg_index < info->custom_register_names.num_entries); range_list_t *range = list_first_entry(&info->expose_custom, range_list_t, list); - unsigned custom_number = range->low + custom_within_range; + const unsigned int custom_number = range->low + custom_within_range; r->group = "custom"; r->feature = &feature_custom; r->arch_info = calloc(1, sizeof(riscv_reg_info_t)); - if (!r->arch_info) + if (!r->arch_info) { + LOG_ERROR("Failed to allocate memory for r->arch_info"); return ERROR_FAIL; - ((riscv_reg_info_t *) r->arch_info)->target = target; - ((riscv_reg_info_t *) r->arch_info)->custom_number = custom_number; - sprintf(reg_name, "custom%d", custom_number); - - if (range->name) { - *reg_name = 0; - r->name = range->name; } + ((riscv_reg_info_t *)r->arch_info)->target = target; + ((riscv_reg_info_t *)r->arch_info)->custom_number = custom_number; - LOG_DEBUG("Exposing additional custom register %d (name=%s)", - number, range->name ? range->name : reg_name); + char **reg_names = info->custom_register_names.reg_names; + r->name = reg_names[custom_reg_index]; + + LOG_TARGET_DEBUG(target, "Exposing additional custom register %d (name=%s)", reg_num, r->name); custom_within_range++; if (custom_within_range > range->high - range->low) { @@ -4399,12 +6201,6 @@ int riscv_init_registers(struct target *target) } } - if (reg_name[0]) { - r->name = reg_name; - reg_name += strlen(reg_name) + 1; - assert(reg_name < info->reg_names + target->reg_cache->num_regs * - max_reg_name_len); - } r->value = calloc(1, DIV_ROUND_UP(r->size, 8)); } diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h index aba0864..f2a9d68 100644 --- a/src/target/riscv/riscv.h +++ b/src/target/riscv/riscv.h @@ -12,26 +12,30 @@ struct riscv_program; #include "target/register.h" #include "target/semihosting_common.h" #include <helper/command.h> +#include <helper/bits.h> #define RISCV_COMMON_MAGIC 0x52495356U -/* The register cache is statically allocated. */ -#define RISCV_MAX_HARTS 1024 -#define RISCV_MAX_REGISTERS 5000 +#define RISCV_MAX_HARTS ((int)BIT(20)) #define RISCV_MAX_TRIGGERS 32 #define RISCV_MAX_HWBPS 16 +#define RISCV_MAX_DMS 100 #define DEFAULT_COMMAND_TIMEOUT_SEC 2 #define DEFAULT_RESET_TIMEOUT_SEC 30 #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 PG_MAX_LEVEL 5 #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; @@ -87,39 +111,54 @@ typedef struct { char *name; } range_list_t; +#define DTM_DTMCS_VERSION_UNKNOWN ((unsigned int)-1) + +struct reg_name_table { + unsigned int num_entries; + char **reg_names; +}; + struct riscv_info { unsigned int common_magic; - unsigned dtm_version; + unsigned int dtm_version; 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; + struct reg_name_table custom_register_names; + char **reg_names; /* 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 +176,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,10 +225,11 @@ 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); - void (*fill_dmi_nop_u64)(struct target *target, char *buf); + void (*fill_dm_write_u64)(struct target *target, char *buf, int a, uint64_t d); + void (*fill_dm_read_u64)(struct target *target, char *buf, int a); + void (*fill_dm_nop_u64)(struct target *target, char *buf); int (*authdata_read)(struct target *target, uint32_t *value, unsigned int index); int (*authdata_write)(struct target *target, uint32_t value, unsigned int index); @@ -174,6 +237,9 @@ struct riscv_info { int (*dmi_read)(struct target *target, uint32_t *value, uint32_t address); int (*dmi_write)(struct target *target, uint32_t address, uint32_t value); + int (*dm_read)(struct target *target, uint32_t *value, uint32_t address); + int (*dm_write)(struct target *target, uint32_t address, uint32_t value); + int (*sample_memory)(struct target *target, struct riscv_sample_buf *buf, riscv_sample_config_t *config, @@ -203,7 +269,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 +290,27 @@ 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; + + bool range_trigger_fallback_encountered; + + bool riscv_ebreakm; + bool riscv_ebreaks; + bool riscv_ebreaku; + + bool enable_equality_match_trigger; + bool enable_napot_trigger; + bool enable_ge_lt_trigger; }; COMMAND_HELPER(riscv_print_info_line, const char *section, const char *key, @@ -240,6 +325,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]; @@ -256,9 +342,6 @@ extern int riscv_command_timeout_sec; extern int riscv_reset_timeout_sec; extern bool riscv_enable_virtual; -extern bool riscv_ebreakm; -extern bool riscv_ebreaks; -extern bool riscv_ebreaku; /* Everything needs the RISC-V specific info structure, so here's a nice macro * that provides that. */ @@ -284,7 +367,7 @@ extern uint32_t bscan_tunneled_select_dmi_num_fields; typedef enum { BSCAN_TUNNEL_NESTED_TAP, BSCAN_TUNNEL_DATA_REGISTER } bscan_tunnel_type_t; extern int bscan_tunnel_ir_width; -uint32_t dtmcontrol_scan_via_bscan(struct target *target, uint32_t out); +int dtmcontrol_scan_via_bscan(struct target *target, uint32_t out, uint32_t *in_ptr); void select_dmi_via_bscan(struct target *target); /*** OpenOCD Interface */ @@ -299,39 +382,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. */ @@ -341,9 +427,9 @@ riscv_insn_t riscv_read_debug_buffer(struct target *target, int index); int riscv_write_debug_buffer(struct target *target, int index, riscv_insn_t insn); int riscv_execute_debug_buffer(struct target *target); -void riscv_fill_dmi_nop_u64(struct target *target, char *buf); -void riscv_fill_dmi_write_u64(struct target *target, char *buf, int a, uint64_t d); -void riscv_fill_dmi_read_u64(struct target *target, char *buf, int a); +void riscv_fill_dm_nop_u64(struct target *target, char *buf); +void riscv_fill_dm_write_u64(struct target *target, char *buf, int a, uint64_t d); +void riscv_fill_dm_read_u64(struct target *target, char *buf, int a); int riscv_dmi_write_u64_bits(struct target *target); int riscv_enumerate_triggers(struct target *target); @@ -364,4 +450,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 da237ef..9c708c8 100644 --- a/src/target/riscv/riscv_semihosting.c +++ b/src/target/riscv/riscv_semihosting.c @@ -57,12 +57,12 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval) { struct semihosting *semihosting = target->semihosting; if (!semihosting) { - LOG_DEBUG(" -> NONE (!semihosting)"); + LOG_TARGET_DEBUG(target, " -> NONE (!semihosting)"); return SEMIHOSTING_NONE; } if (!semihosting->is_active) { - LOG_DEBUG(" -> NONE (!semihosting->is_active)"); + LOG_TARGET_DEBUG(target, " -> NONE (!semihosting->is_active)"); return SEMIHOSTING_NONE; } @@ -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_TARGET_DEBUG(target, " -> NONE (no magic)"); + return SEMIHOSTING_NONE; + } } /* @@ -111,13 +109,13 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval) result = riscv_get_register(target, &r0, GDB_REGNO_A0); if (result != ERROR_OK) { - LOG_DEBUG(" -> ERROR (couldn't read a0)"); + LOG_TARGET_ERROR(target, "Could not read semihosting operation code (register a0)"); return SEMIHOSTING_ERROR; } result = riscv_get_register(target, &r1, GDB_REGNO_A1); if (result != ERROR_OK) { - LOG_DEBUG(" -> ERROR (couldn't read a1)"); + LOG_TARGET_ERROR(target, "Could not read semihosting operation code (register a1)"); return SEMIHOSTING_ERROR; } @@ -131,12 +129,12 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval) *retval = semihosting_common(target); if (*retval != ERROR_OK) { - LOG_ERROR("Failed semihosting operation (0x%02X)", semihosting->op); + LOG_TARGET_ERROR(target, "Failed semihosting operation (0x%02X)", semihosting->op); return SEMIHOSTING_ERROR; } } else { /* Unknown operation number, not a semihosting call. */ - LOG_DEBUG(" -> NONE (unknown operation number)"); + LOG_TARGET_ERROR(target, "Unknown semihosting operation requested (op = 0x%x)", semihosting->op); return SEMIHOSTING_NONE; } } @@ -151,11 +149,11 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval) * operation to complete. */ if (semihosting->is_resumable && !semihosting->hit_fileio) { - LOG_DEBUG(" -> HANDLED"); + LOG_TARGET_DEBUG(target, " -> HANDLED"); return SEMIHOSTING_HANDLED; } - LOG_DEBUG(" -> WAITING"); + LOG_TARGET_DEBUG(target, " -> WAITING"); return SEMIHOSTING_WAITING; } @@ -168,7 +166,7 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval) */ static int riscv_semihosting_setup(struct target *target, int enable) { - LOG_DEBUG("[%s] enable=%d", target_name(target), enable); + LOG_TARGET_DEBUG(target, "enable=%d", enable); struct semihosting *semihosting = target->semihosting; if (semihosting) @@ -185,7 +183,7 @@ static int riscv_semihosting_post_result(struct target *target) return 0; } - LOG_DEBUG("0x%" PRIx64, semihosting->result); + LOG_TARGET_DEBUG(target, "Result: 0x%" PRIx64, semihosting->result); riscv_set_register(target, GDB_REGNO_A0, semihosting->result); return 0; } diff --git a/src/target/target.c b/src/target/target.c index 96f4ae7..a7942ac 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -114,7 +114,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); enum nvp_assert { @@ -216,6 +216,7 @@ static const struct 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 }, }; @@ -720,6 +721,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); @@ -738,6 +740,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: * @@ -748,6 +756,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) { @@ -2996,51 +3006,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; } } @@ -3078,13 +3079,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; @@ -3360,7 +3361,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; @@ -3389,7 +3390,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 ": ", @@ -3473,7 +3474,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 abeb8ed..b8f3b01 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 target_reset_mode { @@ -101,8 +104,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 */ @@ -183,6 +186,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 @@ -780,7 +786,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-builtin.cfg b/tcl/board/esp32c3-builtin.cfg new file mode 100644 index 0000000..9e19b1b --- /dev/null +++ b/tcl/board/esp32c3-builtin.cfg @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Example OpenOCD configuration file for ESP32-C3 connected via builtin USB-JTAG adapter. +# +# For example, OpenOCD can be started for ESP32-C3 debugging on +# +# openocd -f board/esp32c3-builtin.cfg +# + +# Source the JTAG interface configuration file +source [find interface/esp_usb_jtag.cfg] +# Source the ESP32-C3 configuration file +source [find target/esp32c3.cfg] + +adapter speed 40000 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/esp32c6-builtin.cfg b/tcl/board/esp32c6-builtin.cfg new file mode 100644 index 0000000..abc96b2 --- /dev/null +++ b/tcl/board/esp32c6-builtin.cfg @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Example OpenOCD configuration file for ESP32-C6 connected via builtin USB-JTAG adapter. +# +# For example, OpenOCD can be started for ESP32-C6 debugging on +# +# openocd -f board/esp32c6-builtin.cfg +# + +# Source the JTAG interface configuration file +source [find interface/esp_usb_jtag.cfg] +# Source the ESP32-C6 configuration file +source [find target/esp32c6.cfg] + +adapter speed 40000 diff --git a/tcl/board/esp32h2-builtin.cfg b/tcl/board/esp32h2-builtin.cfg new file mode 100644 index 0000000..1ce5961 --- /dev/null +++ b/tcl/board/esp32h2-builtin.cfg @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Example OpenOCD configuration file for ESP32-C3 connected via builtin USB-JTAG adapter. +# +# For example, OpenOCD can be started for ESP32-C3 debugging on +# +# openocd -f board/esp32c3-builtin.cfg +# + +# Source the JTAG interface configuration file +source [find interface/esp_usb_jtag.cfg] +# Source the ESP32-C3 configuration file +source [find target/esp32h2.cfg] + +adapter speed 40000 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..42aeb0a --- /dev/null +++ b/tcl/target/esp32c2.cfg @@ -0,0 +1,117 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# + +# Source the ESP common configuration file. +source [find target/esp_common.cfg] + +# Target specific global variables +set _CHIPNAME "riscv" +set _CPUTAPID 0x0000cc25 +set _ESP_ARCH "riscv" +set _ONLYCPU 1 +set _ESP_SMP_TARGET 0 +set _ESP_SMP_BREAK 0 +set _ESP_EFUSE_MAC_ADDR_REG 0x60008840 + +# Target specific functions should be implemented for each riscv chips. +proc riscv_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 +} + +proc riscv_soc_reset { } { + global _RISCV_DMCONTROL + + # 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 $_RISCV_DMCONTROL 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 + riscv_wdt_disable + + # Here debugger reads allresumeack and allhalted bits as set (0x330a2) + # We will clean allhalted state by resuming the core. + riscv dmi_write $_RISCV_DMCONTROL 0x40000001 + + # Put the hart back into reset state. Note that we need to keep haltreq set. + riscv dmi_write $_RISCV_DMCONTROL 0x80000003 +} + +proc riscv_memprot_is_enabled { } { + global _RISCV_ABS_CMD _RISCV_ABS_DATA0 + + # PMPADDR 0-1 covers entire valid IRAM range and PMPADDR 2-3 covers entire DRAM region + # pmpcfg0 holds the configuration for the PMP 0-3 address registers + + # read pmpcfg0 and extract into 8-bit variables. + riscv dmi_write $_RISCV_ABS_CMD 0x2203a0 + set pmpcfg0 [riscv dmi_read $_RISCV_ABS_DATA0] + + set pmp0cfg [expr {($pmpcfg0 >> (8 * 0)) & 0xFF}] + set pmp1cfg [expr {($pmpcfg0 >> (8 * 1)) & 0xFF}] + set pmp2cfg [expr {($pmpcfg0 >> (8 * 2)) & 0xFF}] + set pmp3cfg [expr {($pmpcfg0 >> (8 * 3)) & 0xFF}] + + # read PMPADDR 0-3 + riscv dmi_write $_RISCV_ABS_CMD 0x2203b0 + set pmpaddr0 [expr {[riscv dmi_read $_RISCV_ABS_DATA0] << 2}] + riscv dmi_write $_RISCV_ABS_CMD 0x2203b1 + set pmpaddr1 [expr {[riscv dmi_read $_RISCV_ABS_DATA0] << 2}] + riscv dmi_write $_RISCV_ABS_CMD 0x2203b2 + set pmpaddr2 [expr {[riscv dmi_read $_RISCV_ABS_DATA0] << 2}] + riscv dmi_write $_RISCV_ABS_CMD 0x2203b3 + set pmpaddr3 [expr {[riscv dmi_read $_RISCV_ABS_DATA0] << 2}] + + set IRAM_LOW 0x40380000 + set IRAM_HIGH 0x403C0000 + set DRAM_LOW 0x3FCA0000 + set DRAM_HIGH 0x3FCE0000 + set PMP_RWX 0x07 + set PMP_RW 0x03 + + # The lock bit remains unset during the execution of the 2nd stage bootloader. + # Thus we do not perform a lock bit check for IRAM and DRAM regions. + + # Check OpenOCD can write and execute from IRAM. + if {$pmpaddr0 >= $IRAM_LOW && $pmpaddr1 <= $IRAM_HIGH} { + if {($pmp0cfg & $PMP_RWX) != 0 || ($pmp1cfg & $PMP_RWX) != $PMP_RWX} { + return 1 + } + } + + # Check OpenOCD can read/write entire DRAM region. + if {$pmpaddr2 >= $DRAM_LOW && $pmpaddr3 <= $DRAM_HIGH} { + if {($pmp2cfg & $PMP_RW) != 0 && ($pmp3cfg & $PMP_RW) != $PMP_RW} { + return 1 + } + } + + return 0 +} + +create_esp_target $_ESP_ARCH diff --git a/tcl/target/esp32c3.cfg b/tcl/target/esp32c3.cfg new file mode 100644 index 0000000..d266ad5 --- /dev/null +++ b/tcl/target/esp32c3.cfg @@ -0,0 +1,81 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# + +# Source the ESP common configuration file. +source [find target/esp_common.cfg] + +# Target specific global variables +set _CHIPNAME "riscv" +set _CPUTAPID 0x00005c25 +set _ESP_ARCH "riscv" +set _ONLYCPU 1 +set _ESP_SMP_TARGET 0 +set _ESP_SMP_BREAK 0 +set _ESP_EFUSE_MAC_ADDR_REG 0x60008844 + +# Target specific functions should be implemented for each riscv chips. +proc riscv_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 riscv_soc_reset { } { + global _RISCV_DMCONTROL + + # 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 $_RISCV_DMCONTROL 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 + riscv_wdt_disable + + # Here debugger reads allresumeack and allhalted bits as set (0x330a2) + # We will clean allhalted state by resuming the core. + riscv dmi_write $_RISCV_DMCONTROL 0x40000001 + + # Put the hart back into reset state. Note that we need to keep haltreq set. + riscv dmi_write $_RISCV_DMCONTROL 0x80000003 +} + +proc riscv_memprot_is_enabled { } { + # IRAM0 PMS lock, SENSITIVE_CORE_X_IRAM0_PMS_CONSTRAIN_0_REG + if { [get_mmr_bit 0x600C10A8 0] != 0 } { + return 1 + } + # DRAM0 PMS lock, SENSITIVE_CORE_X_DRAM0_PMS_CONSTRAIN_0_REG + if { [get_mmr_bit 0x600C10C0 0] != 0 } { + return 1 + } + return 0 +} + +create_esp_target $_ESP_ARCH diff --git a/tcl/target/esp32c6.cfg b/tcl/target/esp32c6.cfg new file mode 100644 index 0000000..e1ef10a --- /dev/null +++ b/tcl/target/esp32c6.cfg @@ -0,0 +1,142 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# + +# Source the ESP common configuration file. +source [find target/esp_common.cfg] + +# Target specific global variables +set _CHIPNAME "riscv" +set _CPUTAPID 0x0000dc25 +set _ESP_ARCH "riscv" +set _ONLYCPU 1 +set _ESP_SMP_TARGET 0 +set _ESP_SMP_BREAK 0 +set _ESP_EFUSE_MAC_ADDR_REG 0x600B0844 + +# Target specific functions should be implemented for each riscv chips. +proc riscv_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 0x60008064 0x50D83AA1 + mww 0x60008048 0 + mww 0x60009064 0x50D83AA1 + mww 0x60009048 0 + # LP_WDT_RTC + mww 0x600b1c18 0x50D83AA1 + mww 0x600B1C00 0 + # LP_WDT_SWD + mww 0x600b1c20 0x50D83AA1 + mww 0x600b1c1c 0x40000000 +} + +proc riscv_soc_reset { } { + global _RISCV_DMCONTROL _RISCV_SB_CS _RISCV_SB_ADDR0 _RISCV_SB_DATA0 + + riscv dmi_write $_RISCV_DMCONTROL 0x80000001 + riscv dmi_write $_RISCV_SB_CS 0x48000 + riscv dmi_write $_RISCV_SB_ADDR0 0x600b1034 + riscv dmi_write $_RISCV_SB_DATA0 0x80000000 + # clear dmactive to clear sbbusy otherwise debug module gets stuck + riscv dmi_write $_RISCV_DMCONTROL 0 + + riscv dmi_write $_RISCV_SB_CS 0x48000 + riscv dmi_write $_RISCV_SB_ADDR0 0x600b1038 + riscv dmi_write $_RISCV_SB_DATA0 0x10000000 + + # clear dmactive to clear sbbusy otherwise debug module gets stuck + riscv dmi_write $_RISCV_DMCONTROL 0 + riscv dmi_write $_RISCV_DMCONTROL 0x40000001 + # Here debugger reads dmstatus as 0xc03a2 + + # Wait for the reset to happen + sleep 10 + poll + # Here debugger reads dmstatus as 0x3a2 + + # Disable the watchdogs again + riscv_wdt_disable + + # Here debugger reads anyhalted and allhalted bits as set (0x3a2) + # We will clean allhalted state by resuming the core. + riscv dmi_write $_RISCV_DMCONTROL 0x40000001 + + # Put the hart back into reset state. Note that we need to keep haltreq set. + riscv dmi_write $_RISCV_DMCONTROL 0x80000003 +} + +proc riscv_memprot_is_enabled { } { + global _RISCV_ABS_CMD _RISCV_ABS_DATA0 + + # If IRAM/DRAM split is enabled TOR address match mode is used. + # If IRAM/DRAM split is disabled NAPOT mode is used. + # In order to determine if the IRAM/DRAM regions are protected against RWX/RW, + # it is necessary to first read the mode and then apply the appropriate method for checking. + # We can understand the mode reading pmp5cfg in pmpcfg1 register. + # If it is none we know that pmp6cfg and pmp7cfg is in TOR mode. + + # Read pmpcfg1 and extract into 8-bit variables. + riscv dmi_write $_RISCV_ABS_CMD 0x2203a1 + set pmpcfg1 [riscv dmi_read $_RISCV_ABS_DATA0] + + set pmp5cfg [expr {($pmpcfg1 >> (8 * 1)) & 0xFF}] + set pmp6cfg [expr {($pmpcfg1 >> (8 * 2)) & 0xFF}] + set pmp7cfg [expr {($pmpcfg1 >> (8 * 3)) & 0xFF}] + + set IRAM_LOW 0x40800000 + set IRAM_HIGH 0x40880000 + set DRAM_LOW 0x40800000 + set DRAM_HIGH 0x40880000 + set PMP_RWX 0x07 + set PMP_RW 0x03 + set PMP_A [expr {($pmp5cfg >> 3) & 0x03}] + + if {$PMP_A == 0} { + # TOR mode used to protect valid address space. + + # Read PMPADDR 5-7 + riscv dmi_write $_RISCV_ABS_CMD 0x2203b5 + set pmpaddr5 [expr {[riscv dmi_read $_RISCV_ABS_DATA0] << 2}] + riscv dmi_write $_RISCV_ABS_CMD 0x2203b6 + set pmpaddr6 [expr {[riscv dmi_read $_RISCV_ABS_DATA0] << 2}] + riscv dmi_write $_RISCV_ABS_CMD 0x2203b7 + set pmpaddr7 [expr {[riscv dmi_read $_RISCV_ABS_DATA0] << 2}] + + # The lock bit remains unset during the execution of the 2nd stage bootloader. + # Thus we do not perform a lock bit check for IRAM and DRAM regions. + + # Check OpenOCD can write and execute from IRAM. + if {$pmpaddr5 >= $IRAM_LOW && $pmpaddr6 <= $IRAM_HIGH} { + if {($pmp5cfg & $PMP_RWX) != 0 || ($pmp6cfg & $PMP_RWX) != $PMP_RWX} { + return 1 + } + } + + # Check OpenOCD can read/write entire DRAM region. + if {$pmpaddr7 >= $DRAM_LOW && $pmpaddr7 <= $DRAM_HIGH} { + if {($pmp7cfg & $PMP_RW) != $PMP_RW} { + return 1 + } + } + } elseif {$PMP_A == 3} { + # NAPOT mode used to protect valid address space. + + # Read PMPADDR 5 + riscv dmi_write $_RISCV_ABS_CMD 0x2203b5 + set pmpaddr5 [expr {[riscv dmi_read $_RISCV_ABS_DATA0]}] + + # Expected value written to the pmpaddr5 + set pmpaddr_napot [expr {($IRAM_LOW | (($IRAM_HIGH - $IRAM_LOW - 1) >> 1)) >> 2}] + if {($pmpaddr_napot != $pmpaddr5) || ($pmp5cfg & $PMP_RWX) != $PMP_RWX} { + return 1 + } + } + + return 0 +} + +create_esp_target $_ESP_ARCH diff --git a/tcl/target/esp32h2.cfg b/tcl/target/esp32h2.cfg new file mode 100644 index 0000000..45f598f --- /dev/null +++ b/tcl/target/esp32h2.cfg @@ -0,0 +1,122 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# + +# Source the ESP common configuration file. +source [find target/esp_common.cfg] + +# Target specific global variables +set _CHIPNAME "riscv" +set _CPUTAPID 0x00010c25 +set _ESP_ARCH "riscv" +set _ONLYCPU 1 +set _ESP_SMP_TARGET 0 +set _ESP_SMP_BREAK 0 +set _ESP_EFUSE_MAC_ADDR_REG 0x600B0844 + +# Target specific functions should be implemented for each riscv chips. +proc riscv_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 0x60009064 0x50D83AA1 + mww 0x60009048 0 + mww 0x6000A064 0x50D83AA1 + mww 0x6000A048 0 + # WDT_RTC + #mww 0x600b1c18 0x50D83AA1 + #mww 0x600B1C00 0 + # WDT_SWD + #mww 0x600b1c20 0x8F1D312A + #mww 0x600b1c1c 0x84B00000 +} + +proc riscv_soc_reset { } { + global _RISCV_DMCONTROL _RISCV_SB_CS _RISCV_SB_ADDR0 _RISCV_SB_DATA0 + + riscv dmi_write $_RISCV_DMCONTROL 0x80000001 + riscv dmi_write $_RISCV_SB_CS 0x48000 + riscv dmi_write $_RISCV_SB_ADDR0 0x600b1034 + riscv dmi_write $_RISCV_SB_DATA0 0x80000000 + # clear dmactive to clear sbbusy otherwise debug module gets stuck + riscv dmi_write $_RISCV_DMCONTROL 0 + + riscv dmi_write $_RISCV_SB_CS 0x48000 + riscv dmi_write $_RISCV_SB_ADDR0 0x600b1038 + riscv dmi_write $_RISCV_SB_DATA0 0x10000000 + + # clear dmactive to clear sbbusy otherwise debug module gets stuck + riscv dmi_write $_RISCV_DMCONTROL 0 + riscv dmi_write $_RISCV_DMCONTROL 0x40000001 + # Here debugger reads dmstatus as 0xc03a2 + + # Wait for the reset to happen + sleep 10 + poll + # Here debugger reads dmstatus as 0x3a2 + + # Disable the watchdogs again + riscv_wdt_disable + + # Here debugger reads anyhalted and allhalted bits as set (0x3a2) + # We will clean allhalted state by resuming the core. + riscv dmi_write $_RISCV_DMCONTROL 0x40000001 + + # Put the hart back into reset state. Note that we need to keep haltreq set. + riscv dmi_write $_RISCV_DMCONTROL 0x80000003 +} + +proc riscv_memprot_is_enabled { } { + global _RISCV_ABS_CMD _RISCV_ABS_DATA0 + # If IRAM/DRAM split is enabled, PMPADDR 5-6 will cover valid IRAM region and PMPADDR 7 will cover valid DRAM region + # Only TOR mode is used for IRAM and DRAM protections. + + # Read pmpcfg1 and extract into 8-bit variables. + riscv dmi_write $_RISCV_ABS_CMD 0x2203a1 + set pmpcfg1 [riscv dmi_read $_RISCV_ABS_DATA0] + + set pmp5cfg [expr {($pmpcfg1 >> (8 * 1)) & 0xFF}] + set pmp6cfg [expr {($pmpcfg1 >> (8 * 2)) & 0xFF}] + set pmp7cfg [expr {($pmpcfg1 >> (8 * 3)) & 0xFF}] + + # Read PMPADDR 5-7 + riscv dmi_write $_RISCV_ABS_CMD 0x2203b5 + set pmpaddr5 [expr {[riscv dmi_read $_RISCV_ABS_DATA0] << 2}] + riscv dmi_write $_RISCV_ABS_CMD 0x2203b6 + set pmpaddr6 [expr {[riscv dmi_read $_RISCV_ABS_DATA0] << 2}] + riscv dmi_write $_RISCV_ABS_CMD 0x2203b7 + set pmpaddr7 [expr {[riscv dmi_read $_RISCV_ABS_DATA0] << 2}] + + set IRAM_LOW 0x40800000 + set IRAM_HIGH 0x40850000 + set DRAM_LOW 0x40800000 + set DRAM_HIGH 0x40850000 + + set PMP_RWX 0x07 + set PMP_RW 0x03 + + # The lock bit remains unset during the execution of the 2nd stage bootloader. + # Thus, we do not perform a lock bit check for IRAM and DRAM regions. + + # Check OpenOCD can write and execute from IRAM. + if {$pmpaddr5 >= $IRAM_LOW && $pmpaddr6 <= $IRAM_HIGH} { + if {($pmp5cfg & $PMP_RWX) != 0 || ($pmp6cfg & $PMP_RWX) != $PMP_RWX} { + return 1 + } + } + + # Check OpenOCD can read/write entire DRAM region. + # If IRAM/DRAM split is disabled, pmpaddr7 will be zero, checking only IRAM region is enough. + if {$pmpaddr7 != 0 && $pmpaddr7 >= $DRAM_LOW && $pmpaddr7 <= $DRAM_HIGH} { + if {($pmp7cfg & $PMP_RW) != $PMP_RW} { + return 1 + } + } + + return 0 +} + +create_esp_target $_ESP_ARCH diff --git a/tcl/target/esp_common.cfg b/tcl/target/esp_common.cfg index ac8cd6a..af2f6ad 100644 --- a/tcl/target/esp_common.cfg +++ b/tcl/target/esp_common.cfg @@ -6,6 +6,14 @@ source [find bitsbytes.tcl] source [find memory.tcl] source [find mmr_helpers.tcl] +# Riscv Debug Module Registers which are used around esp configuration files. +set _RISCV_ABS_DATA0 0x04 +set _RISCV_DMCONTROL 0x10 +set _RISCV_ABS_CMD 0x17 +set _RISCV_SB_CS 0x38 +set _RISCV_SB_ADDR0 0x39 +set _RISCV_SB_DATA0 0x3C + # Common ESP chips definitions # Espressif supports only NuttX in the upstream. @@ -69,13 +77,12 @@ proc create_esp_target { ARCH } { set_esp_common_variables create_esp_jtag create_openocd_targets - configure_openocd_events + configure_openocd_events $ARCH if { $ARCH == "xtensa"} { configure_esp_xtensa_default_settings } else { - # riscv targets are not upstreamed yet. - # they can be found at the official Espressif fork. + configure_esp_riscv_default_settings } } @@ -131,7 +138,6 @@ proc configure_event_halted { } { $_TARGETNAME_0 configure -event halted { global _ESP_WDT_DISABLE $_ESP_WDT_DISABLE - esp halted_event_handler } } @@ -167,12 +173,25 @@ proc configure_event_gdb_attach { } { } } -proc configure_openocd_events { } { +proc configure_openocd_events { ARCH } { + if { $ARCH == "riscv" } { + configure_event_halted + } configure_event_examine_end configure_event_reset_assert_post configure_event_gdb_attach } +proc configure_esp_riscv_default_settings { } { + 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 +} + proc configure_esp_xtensa_default_settings { } { global _TARGETNAME_0 _ESP_SMP_BREAK _FLASH_VOLTAGE _CHIPNAME 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:])) |