diff --git a/src/Makefile b/src/Makefile index 55e0107533f..71ae28f5c2d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -23,62 +23,66 @@ ifeq ($(ENABLE_DEBUG), 1) CFLAGS += -DENABLE_DEBUG endif -SRC = \ - adiv5.c \ - adiv5_jtag.c \ - adiv5_swd.c \ - command.c \ - cortexa.c \ - cortexm.c \ - crc32.c \ - efm32.c \ - exception.c \ - gdb_if.c \ - gdb_main.c \ - gdb_hostio.c \ - gdb_packet.c \ - gdb_reg.c \ - hex_utils.c \ - hc32l110.c \ - imxrt.c \ - jtag_devs.c \ - jtag_scan.c \ - lmi.c \ - lpc_common.c \ - lpc11xx.c \ - lpc17xx.c \ - lpc15xx.c \ - lpc40xx.c \ - lpc43xx.c \ - lpc546xx.c \ - lpc55xx.c \ - kinetis.c \ - main.c \ - maths_utils.c \ - morse.c \ - msp432e4.c \ - msp432p4.c \ - nrf51.c \ - nxpke04.c \ - remote.c \ - rp.c \ - sam3x.c \ - sam4l.c \ - samd.c \ - samx5x.c \ - sfdp.c \ - spi.c \ - stm32f1.c \ - ch32f1.c \ - stm32f4.c \ - stm32h5.c \ - stm32h7.c \ - stm32l0.c \ - stm32l4.c \ - stm32g0.c \ - renesas.c \ - target.c \ - target_flash.c \ +SRC = \ + adiv5.c \ + adiv5_jtag.c \ + adiv5_swd.c \ + command.c \ + cortexa.c \ + cortexm.c \ + crc32.c \ + efm32.c \ + exception.c \ + gdb_if.c \ + gdb_main.c \ + gdb_hostio.c \ + gdb_packet.c \ + gdb_reg.c \ + hex_utils.c \ + hc32l110.c \ + imxrt.c \ + jtag_devs.c \ + jtag_scan.c \ + lmi.c \ + lpc_common.c \ + lpc11xx.c \ + lpc17xx.c \ + lpc15xx.c \ + lpc40xx.c \ + lpc43xx.c \ + lpc546xx.c \ + lpc55xx.c \ + kinetis.c \ + main.c \ + maths_utils.c \ + morse.c \ + msp432e4.c \ + msp432p4.c \ + nrf51.c \ + nxpke04.c \ + remote.c \ + riscv32.c \ + riscv64.c \ + riscv_debug.c \ + riscv_jtag_dtm.c \ + rp.c \ + sam3x.c \ + sam4l.c \ + samd.c \ + samx5x.c \ + sfdp.c \ + spi.c \ + stm32f1.c \ + ch32f1.c \ + stm32f4.c \ + stm32h5.c \ + stm32h7.c \ + stm32l0.c \ + stm32l4.c \ + stm32g0.c \ + renesas.c \ + target.c \ + target_flash.c \ target_probe.c ifeq (,$(filter all_platforms,$(MAKECMDGOALS))) diff --git a/src/gdb_main.c b/src/gdb_main.c index ddc00b0ccb2..fb392cb9928 100644 --- a/src/gdb_main.c +++ b/src/gdb_main.c @@ -682,10 +682,13 @@ static void handle_z_packet(char *packet, const size_t plen) else ret = target_breakwatch_clear(cur_target, type, addr, len); + /* If the target handler was unable to set/clear the break/watch-point, return an error */ if (ret < 0) gdb_putpacketz("E01"); + /* If the handler does not support the kind requested, return empty string */ else if (ret > 0) gdb_putpacketz(""); + /* Otherwise let GDB know that everything went well */ else gdb_putpacketz("OK"); } diff --git a/src/target/adiv5.h b/src/target/adiv5.h index 9928a80dfec..7f87e77c012 100644 --- a/src/target/adiv5.h +++ b/src/target/adiv5.h @@ -185,6 +185,11 @@ #define JTAG_IDCODE_PARTNO_MASK (0xffffU << JTAG_IDCODE_PARTNO_OFFSET) #define JTAG_IDCODE_DESIGNER_OFFSET 1U #define JTAG_IDCODE_DESIGNER_MASK (0x7ffU << JTAG_IDCODE_DESIGNER_OFFSET) +/* Bits 10:7 - JEP-106 Continuation code */ +/* Bits 6:0 - JEP-106 Identity code */ +#define JTAG_IDCODE_DESIGNER_JEP106_CONT_OFFSET 7U +#define JTAG_IDCODE_DESIGNER_JEP106_CONT_MASK (0xfU << ADIV5_DP_DESIGNER_JEP106_CONT_OFFSET) +#define JTAG_IDCODE_DESIGNER_JEP106_CODE_MASK (0x7fU) #define JTAG_IDCODE_ARM_DPv0 UINT32_C(0x4ba00477) diff --git a/src/target/jep106.h b/src/target/jep106.h index 83317b9f9dd..bc410a982ec 100644 --- a/src/target/jep106.h +++ b/src/target/jep106.h @@ -68,6 +68,11 @@ #define JEP106_MANUFACTURER_RASPBERRY 0x913U /* Raspberry Pi */ #define JEP106_MANUFACTURER_RENESAS 0x423U /* Renesas */ #define JEP106_MANUFACTURER_XILINX 0x309U /* Xilinx */ +/* + * This JEP code should belong to "Andes Technology Corporation", but is used on RISC-V by GigaDevice, + * so in the unlikely event we need to support chips by them, here be dragons. + */ +#define JEP106_MANUFACTURER_RV_GIGADEVICE 0x61eU /* * This code is not listed in the JEP106 standard, but is used by some stm32f1 clones diff --git a/src/target/jtag_devs.c b/src/target/jtag_devs.c index 7ea769faa59..e1c092a3098 100644 --- a/src/target/jtag_devs.c +++ b/src/target/jtag_devs.c @@ -22,6 +22,7 @@ #include "general.h" #include "jtag_scan.h" #include "adiv5.h" +#include "riscv_debug.h" #include "jtag_devs.h" const jtag_dev_descr_s dev_descr[] = { @@ -112,9 +113,10 @@ const jtag_dev_descr_s dev_descr[] = { }, }, { - .idcode = 0x0000063dU, - .idmask = 0x00000fffU, - .descr = "Xambala: RVDBG013.", + .idcode = 0x0000563dU, + .idmask = 0x0fffffffU, + .descr = "RISC-V debug v0.13.", + .handler = riscv_jtag_dtm_handler, }, { .idcode = 0x000007a3U, diff --git a/src/target/riscv32.c b/src/target/riscv32.c new file mode 100644 index 00000000000..dc37d7e67a0 --- /dev/null +++ b/src/target/riscv32.c @@ -0,0 +1,306 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rachel Mant + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "general.h" +#include "target.h" +#include "target_internal.h" +#include "target_probe.h" +#include "jep106.h" +#include "riscv_debug.h" +#include "gdb_packet.h" + +typedef struct riscv32_regs { + uint32_t gprs[32]; + uint32_t pc; +} riscv32_regs_s; + +/* This defines a match trigger that's for an address or data location */ +#define RV32_MATCH_ADDR_DATA_TRIGGER 0x20000000U +/* A dmode of 1 restricts the writability of the trigger to debug mode only */ +#define RV32_MATCH_DMODE_DEBUG 0x08000000U +/* Match when the processor tries to execute the location */ +#define RV32_MATCH_EXECUTE 0x00000004U +/* Match when the processor tries to read the location */ +#define RV32_MATCH_READ 0x00000001U +/* Match when the processor tries to write the location */ +#define RV32_MATCH_WRITE 0x00000002U +/* Define that the match should occur in all/any mode */ +#define RV32_MATCH_ANY_MODE 0x00000058U +/* Set the match action to raise a breakpoint exception */ +#define RV32_MATCH_ACTION_EXCEPTION 0x00000000U +/* Set the match action to enter debug mode */ +#define RV32_MATCH_ACTION_DEBUG_MODE 0x00001000U +/* These two define whether the match should be performed on the address, or specific data */ +#define RV32_MATCH_ADDR 0x00000000U +#define RV32_MATCH_DATA 0x00080000U +/* These two define the match timing (before-or-after operation execution) */ +#define RV32_MATCH_BEFORE 0x00000000U +#define RV32_MATCH_AFTER 0x00040000U + +static void riscv32_regs_read(target_s *target, void *data); +static void riscv32_regs_write(target_s *target, const void *data); +static void riscv32_mem_read(target_s *target, void *dest, target_addr_t src, size_t len); +static void riscv32_mem_write(target_s *target, target_addr_t dest, const void *src, size_t len); + +static int riscv32_breakwatch_set(target_s *target, breakwatch_s *breakwatch); +static int riscv32_breakwatch_clear(target_s *target, breakwatch_s *breakwatch); + +#define STRINGIFY(x) #x +#define PROBE(x) \ + do { \ + DEBUG_INFO("Calling " STRINGIFY(x) "\n"); \ + if ((x)(target)) \ + return true; \ + } while (0) + +bool riscv32_probe(target_s *const target) +{ + /* Finish setting up the target structure with generic rv32 functions */ + target->core = "rv32"; + /* Provide the length of a suitable registers structure */ + target->regs_size = sizeof(riscv32_regs_s); + target->regs_read = riscv32_regs_read; + target->regs_write = riscv32_regs_write; + target->mem_read = riscv32_mem_read; + target->mem_write = riscv32_mem_write; + + target->breakwatch_set = riscv32_breakwatch_set; + target->breakwatch_clear = riscv32_breakwatch_clear; + + switch (target->designer_code) { + case JEP106_MANUFACTURER_RV_GIGADEVICE: + PROBE(gd32vf1_probe); + break; + } +#if PC_HOSTED == 0 + gdb_outf("Please report unknown device with Designer 0x%x\n", target->designer_code); +#else + DEBUG_WARN("Please report unknown device with Designer 0x%x\n", target->designer_code); +#endif +#undef PROBE + return false; +} + +static void riscv32_regs_read(target_s *const target, void *const data) +{ + /* Grab the hart structure and figure out how many registers need reading out */ + riscv_hart_s *const hart = riscv_hart_struct(target); + riscv32_regs_s *const regs = (riscv32_regs_s *)data; + const size_t gprs_count = hart->extensions & RV_ISA_EXT_EMBEDDED ? 16U : 32U; + /* Loop through reading out the GPRs */ + for (size_t gpr = 0; gpr < gprs_count; ++gpr) { + // TODO: handle when this fails.. + riscv_csr_read(hart, RV_GPR_BASE + gpr, ®s->gprs[gpr]); + } + /* Special access to grab the program counter that would be executed on resuming the hart */ + riscv_csr_read(hart, RV_DPC, ®s->pc); +} + +static void riscv32_regs_write(target_s *const target, const void *const data) +{ + /* Grab the hart structure and figure out how many registers need reading out */ + riscv_hart_s *const hart = riscv_hart_struct(target); + riscv32_regs_s *const regs = (riscv32_regs_s *)data; + const size_t gprs_count = hart->extensions & RV_ISA_EXT_EMBEDDED ? 16U : 32U; + /* Loop through writing out the GPRs, except for the first which is always 0 */ + for (size_t gpr = 1; gpr < gprs_count; ++gpr) { + // TODO: handle when this fails.. + riscv_csr_write(hart, RV_GPR_BASE + gpr, ®s->gprs[gpr]); + } + /* Special access to poke in the program counter that will be executed on resuming the hart */ + riscv_csr_write(hart, RV_DPC, ®s->pc); +} + +/* Takes in data from abstract command arg0 and, based on the access width, unpacks it to dest */ +void riscv32_unpack_data(void *const dest, const uint32_t data, const uint8_t access_width) +{ + switch (access_width) { + case RV_MEM_ACCESS_8_BIT: { + const uint8_t value = data & 0xffU; + memcpy(dest, &value, sizeof(value)); + break; + } + case RV_MEM_ACCESS_16_BIT: { + const uint16_t value = data & 0xffffU; + memcpy(dest, &value, sizeof(value)); + break; + } + case RV_MEM_ACCESS_32_BIT: + memcpy(dest, &data, sizeof(data)); + break; + } +} + +/* Takes in data from src, based on the access width, to be written to abstract command arg0 and packs it */ +uint32_t riscv32_pack_data(const void *const src, const uint8_t access_width) +{ + switch (access_width) { + case RV_MEM_ACCESS_8_BIT: { + uint8_t value = 0; + memcpy(&value, src, sizeof(value)); + return value; + } + case RV_MEM_ACCESS_16_BIT: { + uint16_t value = 0; + memcpy(&value, src, sizeof(value)); + return value; + } + case RV_MEM_ACCESS_32_BIT: { + uint32_t value = 0; + memcpy(&value, src, sizeof(value)); + return value; + } + } + return 0; +} + +static void riscv32_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) +{ + DEBUG_TARGET("Performing %zu byte read of %08" PRIx32 "\n", len, src); + /* If we're asked to do a 0-byte read, do nothing */ + if (!len) + return; + riscv_hart_s *const hart = riscv_hart_struct(target); + /* Figure out the maxmial width of access to perform, up to the bitness of the target */ + const uint8_t access_width = riscv_mem_access_width(hart, src, len); + const uint8_t access_length = 1U << access_width; + /* Build the access command */ + const uint32_t command = RV_DM_ABST_CMD_ACCESS_MEM | RV_ABST_READ | (access_width << RV_MEM_ACCESS_SHIFT) | + (access_length < len ? RV_MEM_ADDR_POST_INC : 0U); + /* Write the address to read to arg1 */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA1, src)) + return; + uint8_t *const data = (uint8_t *)dest; + for (size_t offset = 0; offset < len; offset += access_length) { + /* Execute the read */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, command) || !riscv_command_wait_complete(hart)) + return; + /* Extract back the data from arg0 */ + uint32_t value = 0; + if (!riscv_dm_read(hart->dbg_module, RV_DM_DATA0, &value)) + return; + riscv32_unpack_data(data + offset, value, access_width); + } +} + +static void riscv32_mem_write(target_s *const target, const target_addr_t dest, const void *const src, const size_t len) +{ + DEBUG_TARGET("Performing %zu byte write of %08" PRIx32 "\n", len, dest); + /* If we're asked to do a 0-byte read, do nothing */ + if (!len) + return; + riscv_hart_s *const hart = riscv_hart_struct(target); + /* Figure out the maxmial width of access to perform, up to the bitness of the target */ + const uint8_t access_width = riscv_mem_access_width(hart, dest, len); + const uint8_t access_length = 1U << access_width; + /* Build the access command */ + const uint32_t command = RV_DM_ABST_CMD_ACCESS_MEM | RV_ABST_WRITE | (access_width << RV_MEM_ACCESS_SHIFT) | + (access_length < len ? RV_MEM_ADDR_POST_INC : 0U); + /* Write the address to write to arg1 */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA1, dest)) + return; + const uint8_t *const data = (const uint8_t *)src; + for (size_t offset = 0; offset < len; offset += access_length) { + /* Pack the data to write into arg0 */ + uint32_t value = riscv32_pack_data(data + offset, access_width); + if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA0, value)) + return; + /* Execute the write */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, command) || !riscv_command_wait_complete(hart)) + return; + } +} + +/* + * The following can be used as a key for understanding the various return results from the breakwatch functions: + * 0 -> success + * 1 -> not supported + * -1 -> an error occured + */ + +static int riscv32_breakwatch_set(target_s *const target, breakwatch_s *const breakwatch) +{ + riscv_hart_s *const hart = riscv_hart_struct(target); + size_t trigger = 0; + /* Find the first unused trigger slot */ + for (; trigger < hart->triggers; ++trigger) { + const uint32_t trigger_use = hart->trigger_uses[trigger]; + /* Make sure it's unused and that it supports breakwatch mode */ + if ((trigger_use & RV_TRIGGER_MODE_MASK) == RISCV_TRIGGER_MODE_UNUSED && + (trigger_use & RV_TRIGGER_SUPPORT_BREAKWATCH)) + break; + } + /* If none was available, return an error */ + if (trigger == hart->triggers) + return -1; + + /* Build the mcontrol config for the requested breakwatch type */ + uint32_t config = RV32_MATCH_ADDR_DATA_TRIGGER | RV32_MATCH_DMODE_DEBUG | RV32_MATCH_ANY_MODE | + RV32_MATCH_ACTION_DEBUG_MODE | RV32_MATCH_ADDR | riscv_breakwatch_match_size(breakwatch->size); + // RV32_MATCH_DATA (bit 19) + riscv_trigger_state_e mode = RISCV_TRIGGER_MODE_WATCHPOINT; + switch (breakwatch->type) { + case TARGET_BREAK_HARD: + config |= RV32_MATCH_EXECUTE | RV32_MATCH_BEFORE; + mode = RISCV_TRIGGER_MODE_BREAKPOINT; + break; + case TARGET_WATCH_READ: + config |= RV32_MATCH_READ | RV32_MATCH_AFTER; + break; + case TARGET_WATCH_WRITE: + config |= RV32_MATCH_WRITE | RV32_MATCH_BEFORE; + break; + case TARGET_WATCH_ACCESS: + config |= RV32_MATCH_READ | RV32_MATCH_WRITE | RV32_MATCH_AFTER; + break; + default: + /* If the breakwatch type is not one of the above, tell the debugger we don't support it */ + return 1; + } + /* Grab the address to set the breakwatch on and configure the hardware */ + const uint32_t address = breakwatch->addr; + const bool result = riscv_config_trigger(hart, trigger, mode, &config, &address); + /* If configuration succeeds, store the trigger index in the breakwatch structure */ + if (result) + breakwatch->reserved[0] = trigger; + /* Return based on whether setting up the hardware worked or not */ + return result ? 0 : -1; +} + +static int riscv32_breakwatch_clear(target_s *const target, breakwatch_s *const breakwatch) +{ + riscv_hart_s *const hart = riscv_hart_struct(target); + const uint32_t config = RV32_MATCH_ADDR_DATA_TRIGGER; + const uint32_t address = 0; + return riscv_config_trigger(hart, breakwatch->reserved[0], RISCV_TRIGGER_MODE_UNUSED, &config, &address) ? 0 : -1; +} diff --git a/src/target/riscv64.c b/src/target/riscv64.c new file mode 100644 index 00000000000..54ab5aa500a --- /dev/null +++ b/src/target/riscv64.c @@ -0,0 +1,142 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rachel Mant + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "general.h" +#include "target.h" +#include "target_internal.h" +#include "target_probe.h" +#include "riscv_debug.h" + +typedef struct riscv64_regs { + uint64_t gprs[32]; + uint64_t pc; +} riscv64_regs_s; + +static void riscv64_regs_read(target_s *target, void *data); +static void riscv64_regs_write(target_s *target, const void *data); +static void riscv64_mem_read(target_s *target, void *dest, target_addr_t src, size_t len); + +bool riscv64_probe(target_s *const target) +{ + /* Finish setting up the target structure with generic rv64 functions */ + target->core = "rv64"; + /* Provide the length of a suitable registers structure */ + target->regs_size = sizeof(riscv64_regs_s); + target->regs_read = riscv64_regs_read; + target->regs_write = riscv64_regs_write; + target->mem_read = riscv64_mem_read; + + return false; +} + +static void riscv64_regs_read(target_s *const target, void *const data) +{ + /* Grab the hart structure and figure out how many registers need reading out */ + riscv_hart_s *const hart = riscv_hart_struct(target); + riscv64_regs_s *const regs = (riscv64_regs_s *)data; + const size_t gprs_count = hart->extensions & RV_ISA_EXT_EMBEDDED ? 16U : 32U; + /* Loop through reading out the GPRs */ + for (size_t gpr = 0; gpr < gprs_count; ++gpr) { + // TODO: handle when this fails.. + riscv_csr_read(hart, RV_GPR_BASE + gpr, ®s->gprs[gpr]); + } + /* Special access to grab the program counter that would be executed on resuming the hart */ + riscv_csr_read(hart, RV_DPC, ®s->pc); +} + +static void riscv64_regs_write(target_s *const target, const void *const data) +{ + /* Grab the hart structure and figure out how many registers need reading out */ + riscv_hart_s *const hart = riscv_hart_struct(target); + riscv64_regs_s *const regs = (riscv64_regs_s *)data; + const size_t gprs_count = hart->extensions & RV_ISA_EXT_EMBEDDED ? 16U : 32U; + /* Loop through writing out the GPRs, except for the first which is always 0 */ + for (size_t gpr = 1; gpr < gprs_count; ++gpr) { + // TODO: handle when this fails.. + riscv_csr_write(hart, RV_GPR_BASE + gpr, ®s->gprs[gpr]); + } + /* Special access to poke in the program counter that will be executed on resuming the hart */ + riscv_csr_write(hart, RV_DPC, ®s->pc); +} + +/* Takes in data from abstract command arg0 and, based on the access width, unpacks it to dest */ +void riscv64_unpack_data( + void *const dest, const uint32_t data_low, const uint32_t data_high, const uint8_t access_width) +{ + switch (access_width) { + /* If the access was 32-bit or less, ignore data_high and delegate to the RV32 version of this function */ + case RV_MEM_ACCESS_8_BIT: + case RV_MEM_ACCESS_16_BIT: + case RV_MEM_ACCESS_32_BIT: + riscv32_unpack_data(dest, data_low, access_width); + break; + case RV_MEM_ACCESS_64_BIT: { + /* Reconstruct the 64-bit value and copy it into the destination */ + uint64_t value = ((uint64_t)data_high << 32U) | data_low; + memcpy(dest, &value, sizeof(value)); + break; + } + } +} + +/* XXX: target_addr_t supports only 32-bit addresses, artificially limiting this function */ +static void riscv64_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) +{ + DEBUG_TARGET("Performing %zu byte read of %08" PRIx32 "\n", len, src); + /* If we're asked to do a 0-byte read, do nothing */ + if (!len) + return; + riscv_hart_s *const hart = riscv_hart_struct(target); + /* Figure out the maxmial width of access to perform, up to the bitness of the target */ + const uint8_t access_width = riscv_mem_access_width(hart, src, len); + const uint8_t access_length = 1U << access_width; + /* Build the access command */ + const uint32_t command = RV_DM_ABST_CMD_ACCESS_MEM | RV_ABST_READ | (access_width << RV_MEM_ACCESS_SHIFT) | + (access_length < len ? RV_MEM_ADDR_POST_INC : 0U); + /* Write the address to read to arg1 */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA2, src) || !riscv_dm_write(hart->dbg_module, RV_DM_DATA3, 0U)) + return; + uint8_t *const data = (uint8_t *)dest; + for (size_t offset = 0; offset < len; offset += access_length) { + /* Execute the read */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, command) || !riscv_command_wait_complete(hart)) + return; + /* Extract back the data from arg0 */ + uint32_t value_low = 0; + uint32_t value_high = 0; + if (!riscv_dm_read(hart->dbg_module, RV_DM_DATA0, &value_low) || + !riscv_dm_read(hart->dbg_module, RV_DM_DATA1, &value_high)) + return; + riscv64_unpack_data(data + offset, value_low, value_high, access_width); + } +} diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c new file mode 100644 index 00000000000..7995fcfcce3 --- /dev/null +++ b/src/target/riscv_debug.c @@ -0,0 +1,971 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rachel Mant + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "general.h" +#include "target_probe.h" +#include "target_internal.h" +#include "gdb_reg.h" +#include "riscv_debug.h" + +#include + +/* + * Links to exact specifications used in this code are listed here for ease: + * riscv-debug-spec v0.11 as used by SiFive: + * https://static.dev.sifive.com/riscv-debug-spec-0.11nov12.pdf + * riscv-debug-spec v0.13.2 (release): + * https://raw.githubusercontent.com/riscv/riscv-debug-spec/v0.13-release/riscv-debug-release.pdf + * riscv-debug-spec v1.0 (stable): + * https://github.com/riscv/riscv-debug-spec/blob/master/riscv-debug-stable.pdf + */ + +#define RV_DM_CONTROL 0x10U +#define RV_DM_STATUS 0x11U +#define RV_DM_NEXT_DM 0x1dU + +#define RV_DM_CTRL_ACTIVE 0x00000001U +#define RV_DM_CTRL_HARTSEL_MASK 0x03ffffc0U +#define RV_DM_CTRL_HARTSELLO_MASK 0x03ff0000U +#define RV_DM_CTRL_HARTSELHI_MASK 0x0000ffc0U +#define RV_DM_CTRL_HALT_REQ 0x80000000U +#define RV_DM_CTRL_RESUME_REQ 0x40000000U +#define RV_DM_CTRL_HART_RESET 0x20000000U +#define RV_DM_CTRL_HART_ACK_RESET 0x10000000U +#define RV_DM_CTRL_SYSTEM_RESET 0x00000002U +#define RV_DM_CTRL_HARTSELLO_SHIFT 16U +#define RV_DM_CTRL_HARTSELHI_SHIFT 4U + +#define RV_DM_STAT_ALL_RESUME_ACK 0x00020000U +#define RV_DM_STAT_NON_EXISTENT 0x00004000U +#define RV_DM_STAT_ALL_HALTED 0x00000200U +#define RV_DM_STAT_ALL_RESET 0x00080000U + +#define RV_DM_ABST_STATUS_BUSY 0x00001000U +#define RV_DM_ABST_STATUS_DATA_COUNT 0x0000000fU + +#define RV_CSR_FORCE_MASK 0xc000U +#define RV_CSR_FORCE_32_BIT 0x4000U +#define RV_CSR_FORCE_64_BIT 0x8000U + +/* The following is a set of CSR address definitions */ +/* misa -> The Hart's machine ISA register */ +#define RV_ISA 0x301U +/* dcsr -> Debug Control/Status Register */ +#define RV_DCSR 0x7b0U +/* mvendorid -> The JEP-106 code for the vendor implementing this Hart */ +#define RV_VENDOR_ID 0xf11U +/* marchid -> The RISC-V International architecture ID code */ +#define RV_ARCH_ID 0xf12U +/* mimplid -> Hart's processor implementation ID */ +#define RV_IMPL_ID 0xf13U +/* mhartid -> machine ID of the Hart */ +#define RV_HART_ID 0xf14U + +/* tselect -> Trigger selection register */ +#define RV_TRIG_SELECT 0x7a0U +/* tinfo -> selected trigger information register */ +#define RV_TRIG_INFO 0x7a4U +/* tdata1 -> selected trigger configuration register 1 */ +#define RV_TRIG_DATA_1 0x7a1U +/* tdata2 -> selected trigger configuration register 2 */ +#define RV_TRIG_DATA_2 0x7a2U + +#define RV_ISA_EXTENSIONS_MASK 0x03ffffffU + +#define RV_VENDOR_JEP106_CONT_MASK 0x7fffff80U +#define RV_VENDOR_JEP106_CODE_MASK 0x7fU + +#define RV_DCSR_STEP 0x00000004U +#define RV_DCSR_CAUSE_MASK 0x000001c0U +#define RV_DCSR_STEPIE 0x00000800U + +#define RV_GPRS_COUNT 32U + +/* This enum defines the set of currently known and valid halt causes */ +typedef enum riscv_halt_cause { + /* Halt was caused by an `ebreak` instruction executing */ + RV_HALT_CAUSE_EBREAK = (1U << 6U), + /* Halt was caused by a breakpoint or watchpoint (set in the trigger module) */ + RV_HALT_CAUSE_TRIGGER = (2U << 6U), + /* Halt was caused by debugger request (haltreq) */ + RV_HALT_CAUSE_REQUEST = (3U << 6U), + /* Halt was caused by single-step execution */ + RV_HALT_CAUSE_STEP = (4U << 6U), + /* Halt was caused by request out of reset (resethaltreq) */ + RV_HALT_CAUSE_RESET = (5U << 6U), +} riscv_halt_cause_e; + +// clang-format off +/* General-purpose register name strings */ +static const char *const riscv_gpr_names[RV_GPRS_COUNT] = { + "zero", "ra", "sp", "gp", + "tp", "t0", "t1", "t2", + "fp", "s1", "a0", "a1", + "a2", "a3", "a4", "a5", + "a6", "a7", "s2", "s3", + "s4", "s5", "s6", "s7", + "s8", "s9", "s10", "s11", + "t3", "t4", "t5", "t6", +}; +// clang-format on + +/* General-purpose register types */ +static const gdb_reg_type_e riscv_gpr_types[RV_GPRS_COUNT] = { + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_CODE_PTR, + GDB_TYPE_DATA_PTR, + GDB_TYPE_DATA_PTR, + GDB_TYPE_DATA_PTR, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_DATA_PTR, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, +}; + +// clang-format off +static_assert(ARRAY_LENGTH(riscv_gpr_names) == ARRAY_LENGTH(riscv_gpr_types), + "GPR array length mismatch! GPR type array should have the same length as GPR name array." +); +// clang-format on + +static void riscv_dm_init(riscv_dm_s *dbg_module); +static bool riscv_hart_init(riscv_hart_s *hart); +static void riscv_hart_free(void *priv); +static bool riscv_dmi_read(riscv_dmi_s *dmi, uint32_t address, uint32_t *value); +static bool riscv_dmi_write(riscv_dmi_s *dmi, uint32_t address, uint32_t value); +static void riscv_dm_ref(riscv_dm_s *dbg_module); +static void riscv_dm_unref(riscv_dm_s *dbg_module); +static riscv_debug_version_e riscv_dm_version(uint32_t status); + +static uint32_t riscv_hart_discover_isa(riscv_hart_s *hart); +static void riscv_hart_discover_triggers(riscv_hart_s *hart); + +static bool riscv_attach(target_s *target); +static void riscv_detach(target_s *target); + +static const char *riscv_target_description(target_s *target); + +static bool riscv_check_error(target_s *target); +static void riscv_halt_request(target_s *target); +static void riscv_halt_resume(target_s *target, bool step); +static target_halt_reason_e riscv_halt_poll(target_s *target, target_addr_t *watch); +static void riscv_reset(target_s *target); + +void riscv_dmi_init(riscv_dmi_s *const dmi) +{ + /* If we don't currently know how to talk to this DMI, warn and fail */ + if (dmi->version == RISCV_DEBUG_UNKNOWN) + return; + if (dmi->version == RISCV_DEBUG_0_11) { + DEBUG_INFO("RISC-V debug v0.11 not presently supported\n"); + return; + } + + /* Iterate through the possible DMs and probe implemented ones */ + /* The first DM is always at base address 0 */ + uint32_t base_addr = 0U; + do { + /* Read out the DM's status register */ + uint32_t dm_status = 0; + if (!riscv_dmi_read(dmi, base_addr + RV_DM_STATUS, &dm_status)) { + /* If we fail to read the status register, abort */ + break; + } + const riscv_debug_version_e dm_version = riscv_dm_version(dm_status); + + /* If the DM is not unimplemented, allocate a structure for it and do further processing */ + if (dm_version != RISCV_DEBUG_UNIMPL) { + riscv_dm_s *dbg_module = calloc(1, sizeof(*dbg_module)); + if (!dbg_module) { /* calloc failed: heap exhaustion */ + DEBUG_WARN("calloc: failed in %s\n", __func__); + return; + } + /* Setup and try to discover the DM's Harts */ + dbg_module->dmi_bus = dmi; + dbg_module->base = base_addr; + dbg_module->version = dm_version; + riscv_dm_init(dbg_module); + /* If we failed to discover any Harts, free the structure */ + if (!dbg_module->ref_count) + free(dbg_module); + } + + /* Read out the address of the next DM */ + if (!riscv_dmi_read(dmi, base_addr + RV_DM_NEXT_DM, &base_addr)) { + /* If this fails then abort further scanning */ + DEBUG_INFO("Error while reading the next DM base address\n"); + break; + } + /* A new base address of 0 indicates this is the last one on the chain and we should stop. */ + } while (base_addr != 0U); +} + +static void riscv_dm_init(riscv_dm_s *const dbg_module) +{ + /* Attempt to activate the DM */ + if (!riscv_dm_write(dbg_module, RV_DM_CONTROL, RV_DM_CTRL_ACTIVE)) + return; + /* Now find out how many hartsel bits are present */ + uint32_t control = RV_DM_CTRL_ACTIVE | RV_DM_CTRL_HARTSEL_MASK; + if (!riscv_dm_write(dbg_module, RV_DM_CONTROL, control) || !riscv_dm_read(dbg_module, RV_DM_CONTROL, &control)) + return; + /* Extract the maximum number of harts present and iterate through the harts */ + const uint32_t harts_max = ((control & RV_DM_CTRL_HARTSELLO_MASK) >> RV_DM_CTRL_HARTSELLO_SHIFT) | + ((control & RV_DM_CTRL_HARTSELHI_MASK) << RV_DM_CTRL_HARTSELHI_SHIFT); + for (uint32_t hart_idx = 0; hart_idx <= harts_max; ++hart_idx) { + /* Select the hart */ + control = ((hart_idx << RV_DM_CTRL_HARTSELLO_SHIFT) & RV_DM_CTRL_HARTSELLO_MASK) | + ((hart_idx >> RV_DM_CTRL_HARTSELHI_SHIFT) & RV_DM_CTRL_HARTSELHI_MASK) | RV_DM_CTRL_ACTIVE; + uint32_t status = 0; + if (!riscv_dm_write(dbg_module, RV_DM_CONTROL, control) || !riscv_dm_read(dbg_module, RV_DM_STATUS, &status)) + return; + /* If the hart doesn't exist, the spec says to terminate scan */ + if (status & RV_DM_STAT_NON_EXISTENT) + break; + + riscv_hart_s *hart = calloc(1, sizeof(*hart)); + if (!hart) { /* calloc failed: heap exhaustion */ + DEBUG_WARN("calloc: failed in %s\n", __func__); + return; + } + /* Setup the hart structure and discover the target core */ + hart->dbg_module = dbg_module; + hart->hart_idx = hart_idx; + hart->hartsel = control; + if (!riscv_hart_init(hart)) + free(hart); + } +} + +static uint8_t riscv_isa_address_width(const uint32_t isa) +{ + switch (isa >> 30U) { + case 1: + return 32U; + case 2: + return 64U; + case 3: + return 128U; + } + DEBUG_INFO("Unknown address width, defaulting to 32\n"); + return 32U; +} + +static void riscv_hart_read_ids(riscv_hart_s *const hart) +{ + /* Read out the vendor ID */ + riscv_csr_read(hart, RV_VENDOR_ID | RV_CSR_FORCE_32_BIT, &hart->vendorid); + /* Adjust the value to fit our view of JEP-106 codes */ + hart->vendorid = + ((hart->vendorid & RV_VENDOR_JEP106_CONT_MASK) << 1U) | (hart->vendorid & RV_VENDOR_JEP106_CODE_MASK); + /* Depending on the bus width, read out the other IDs suitably */ + if (hart->access_width == 32U) { + riscv_csr_read(hart, RV_ARCH_ID, &hart->archid); + riscv_csr_read(hart, RV_IMPL_ID, &hart->implid); + riscv_csr_read(hart, RV_HART_ID, &hart->hartid); + } else if (hart->access_width == 64U) { + /* For now, on rv64, we just truncate these down after read */ + uint64_t ident = 0; + riscv_csr_read(hart, RV_ARCH_ID, &ident); + hart->archid = ident & 0xffffffffU; + riscv_csr_read(hart, RV_IMPL_ID, &ident); + hart->implid = ident & 0xffffffffU; + riscv_csr_read(hart, RV_HART_ID, &ident); + hart->hartid = ident & 0xffffffffU; + } + /* rv128 is unimpl. */ +} + +static bool riscv_hart_init(riscv_hart_s *const hart) +{ + /* Allocate a new target */ + target_s *target = target_new(); + if (!target) + return false; + + /* Grab a reference to the DMI and DM structurues and do preliminary setup of the target structure */ + riscv_dm_ref(hart->dbg_module); + target->driver = "RISC-V"; + target->priv = hart; + target->priv_free = riscv_hart_free; + + /* Request halt and read certain key registers */ + riscv_halt_request(target); + uint32_t isa = riscv_hart_discover_isa(hart); + hart->address_width = riscv_isa_address_width(isa); + hart->extensions = isa & RV_ISA_EXTENSIONS_MASK; + riscv_hart_read_ids(hart); + + DEBUG_INFO("Hart %" PRIx32 ": %u-bit RISC-V (arch = %08" PRIx32 "), vendor = %" PRIx32 ", impl = %" PRIx32 + ", exts = %08" PRIx32 "\n", + hart->hartid, hart->access_width, hart->archid, hart->vendorid, hart->implid, hart->extensions); + + /* We don't support rv128, so tell the user and fast-quit on this target. */ + if (hart->access_width == 128U) { + target->core = "(unsup) rv128"; + DEBUG_WARN("rv128 is unsupported, ignoring this hart\n"); + return true; + } + + /* If the hart implements mvendorid, this gives us the JEP-106, otherwise use the DTM designer code */ + target->designer_code = hart->vendorid ? hart->vendorid : hart->dbg_module->dmi_bus->designer_code; + target->cpuid = hart->archid; + + riscv_hart_discover_triggers(hart); + + /* Setup core-agnostic target functions */ + target->attach = riscv_attach; + target->detach = riscv_detach; + + target->regs_description = riscv_target_description; + + target->check_error = riscv_check_error; + target->halt_request = riscv_halt_request; + target->halt_resume = riscv_halt_resume; + target->halt_poll = riscv_halt_poll; + target->reset = riscv_reset; + + if (hart->access_width == 32U) { + DEBUG_INFO("-> riscv32_probe\n"); + if (!riscv32_probe(target)) + DEBUG_INFO("Probing failed, please report unknown RISC-V 32 device\n"); + } + if (hart->access_width == 64U) { + DEBUG_INFO("-> riscv64_probe\n"); + if (!riscv64_probe(target)) + DEBUG_INFO("Probing failed, please report unknown RISC-V 64 device\n"); + } + riscv_halt_resume(target, false); + return true; +} + +riscv_hart_s *riscv_hart_struct(target_s *const target) +{ + return (riscv_hart_s *)target->priv; +} + +static void riscv_hart_free(void *const priv) +{ + riscv_dm_unref(((riscv_hart_s *)priv)->dbg_module); + free(priv); +} + +static bool riscv_dmi_read(riscv_dmi_s *const dmi, const uint32_t address, uint32_t *const value) +{ + return dmi->read(dmi, address, value); +} + +static bool riscv_dmi_write(riscv_dmi_s *const dmi, const uint32_t address, const uint32_t value) +{ + return dmi->write(dmi, address, value); +} + +bool riscv_dm_read(riscv_dm_s *dbg_module, const uint8_t address, uint32_t *const value) +{ + return riscv_dmi_read(dbg_module->dmi_bus, dbg_module->base + address, value); +} + +bool riscv_dm_write(riscv_dm_s *dbg_module, const uint8_t address, const uint32_t value) +{ + return riscv_dmi_write(dbg_module->dmi_bus, dbg_module->base + address, value); +} + +static riscv_debug_version_e riscv_dm_version(const uint32_t status) +{ + uint8_t version = status & RV_STATUS_VERSION_MASK; + switch (version) { + case 0: + return RISCV_DEBUG_UNIMPL; + case 1: + DEBUG_INFO("RISC-V debug v0.11 DM\n"); + return RISCV_DEBUG_0_11; + case 2: + DEBUG_INFO("RISC-V debug v0.13 DM\n"); + return RISCV_DEBUG_0_13; + case 3: + DEBUG_INFO("RISC-V debug v1.0 DM\n"); + return RISCV_DEBUG_1_0; + } + DEBUG_INFO("Please report part with unknown RISC-V debug DM version %x\n", version); + return RISCV_DEBUG_UNKNOWN; +} + +static inline void riscv_dmi_ref(riscv_dmi_s *const dmi) +{ + ++dmi->ref_count; +} + +static inline void riscv_dmi_unref(riscv_dmi_s *const dmi) +{ + --dmi->ref_count; + if (!dmi->ref_count) + free(dmi); +} + +static void riscv_dm_ref(riscv_dm_s *const dbg_module) +{ + if (!dbg_module->ref_count) + riscv_dmi_ref(dbg_module->dmi_bus); + ++dbg_module->ref_count; +} + +static void riscv_dm_unref(riscv_dm_s *const dbg_module) +{ + --dbg_module->ref_count; + if (!dbg_module->ref_count) { + riscv_dmi_unref(dbg_module->dmi_bus); + free(dbg_module); + } +} + +static uint32_t riscv_hart_discover_isa(riscv_hart_s *const hart) +{ + /* Read out the abstract command control/status register */ + uint32_t data_registers = 0; + if (!riscv_dm_read(hart->dbg_module, RV_DM_ABST_CTRLSTATUS, &data_registers)) + return 0U; + /* And use the data count bits to divine an initial guess on the platform width */ + data_registers &= RV_DM_ABST_STATUS_DATA_COUNT; + DEBUG_INFO("Hart has %" PRIu32 " data registers\n", data_registers); + /* Check we have at least enough data registers for arg0 */ + if (data_registers >= 4) + hart->access_width = 128U; + else if (data_registers >= 2) + hart->access_width = 64U; + else if (data_registers) + hart->access_width = 32U; + /* If the control/status register contains an invalid count, abort */ + else + return 0; + + do { + DEBUG_INFO("Attempting %u-bit read on misa\n", hart->access_width); + /* Try reading the register on the guessed width */ + uint32_t isa_data[4] = {}; + bool result = riscv_csr_read(hart, RV_ISA, isa_data); + if (result) { + if (hart->access_width == 128U) + return (isa_data[3] & 0xc0000000) | (isa_data[0] & 0x3fffffffU); + if (hart->access_width == 64U) + return (isa_data[1] & 0xc0000000) | (isa_data[0] & 0x3fffffffU); + return isa_data[0]; + } + /* If that failed, then find out why and instead try the next narrower width */ + switch (hart->status) { + case RISCV_HART_BUS_ERROR: + case RISCV_HART_EXCEPTION: + case RISCV_HART_NOT_SUPP: // WCH CH32Vx chips reply that + break; + default: + return 0; + break; + } + if (hart->access_width == 32U) { + hart->access_width = 0U; + return 0; /* We are unable to read the misa register */ + } + if (hart->access_width == 64U) + hart->access_width = 32U; + if (hart->access_width == 128U) + hart->access_width = 64U; + } while (hart->access_width != 0U); + DEBUG_WARN("Unable to read misa register\n"); + /* If the above loop failed, we're done.. */ + return 0U; +} + +static uint32_t riscv_hart_access_width(uint8_t access_width) +{ + if (access_width == 128U) + return RV_REG_ACCESS_128_BIT; + if (access_width == 64U) + return RV_REG_ACCESS_64_BIT; + return RV_REG_ACCESS_32_BIT; +} + +static uint32_t riscv_csr_access_width(const uint16_t reg) +{ + const uint16_t access_width = reg & RV_CSR_FORCE_MASK; + if (access_width == RV_CSR_FORCE_32_BIT) + return 32U; + if (access_width == RV_CSR_FORCE_64_BIT) + return 64U; + return 128U; +} + +bool riscv_command_wait_complete(riscv_hart_s *const hart) +{ + uint32_t status = RV_DM_ABST_STATUS_BUSY; + while (status & RV_DM_ABST_STATUS_BUSY) { + if (!riscv_dm_read(hart->dbg_module, RV_DM_ABST_CTRLSTATUS, &status)) + return false; + } + /* Shift out and mask off the command status, then reset the status on the Hart */ + hart->status = (status >> 8U) & RISCV_HART_OTHER; + if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_CTRLSTATUS, RISCV_HART_OTHER << 8U)) + return false; + if (hart->status != RISCV_HART_NO_ERROR) + DEBUG_WARN("CSR access failed: %u\n", hart->status); + /* If the command failed, return the failure */ + return hart->status == RISCV_HART_NO_ERROR; +} + +bool riscv_csr_read(riscv_hart_s *const hart, const uint16_t reg, void *const data) +{ + const uint8_t access_width = (reg & RV_CSR_FORCE_MASK) ? riscv_csr_access_width(reg) : hart->access_width; + DEBUG_TARGET("Reading %u-bit CSR %03x\n", access_width, reg & ~RV_CSR_FORCE_MASK); + /* Set up the register read and wait for it to complete */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, + RV_DM_ABST_CMD_ACCESS_REG | RV_ABST_READ | RV_REG_XFER | riscv_hart_access_width(access_width) | + (reg & ~RV_CSR_FORCE_MASK)) || + !riscv_command_wait_complete(hart)) + return false; + uint32_t *const value = (uint32_t *)data; + /* If we're doing a 128-bit read, grab the upper-most 2 uint32_t's */ + if (access_width == 128U && + !(riscv_dm_read(hart->dbg_module, RV_DM_DATA3, value + 3) && + riscv_dm_read(hart->dbg_module, RV_DM_DATA2, value + 2))) + return false; + /* If we're doing at least a 64-bit read, grab the next uint32_t */ + if (access_width >= 64U && !riscv_dm_read(hart->dbg_module, RV_DM_DATA1, value + 1)) + return false; + /* Finally grab the last and lowest uint32_t */ + return riscv_dm_read(hart->dbg_module, RV_DM_DATA0, value); +} + +bool riscv_csr_write(riscv_hart_s *const hart, const uint16_t reg, const void *const data) +{ + const uint8_t access_width = (reg & RV_CSR_FORCE_MASK) ? riscv_csr_access_width(reg) : hart->access_width; + DEBUG_TARGET("Writing %u-bit CSR %03x\n", access_width, reg & ~RV_CSR_FORCE_MASK); + /* Set up the data registers based on the Hart native access size */ + const uint32_t *const value = (const uint32_t *)data; + if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA0, value[0])) + return false; + if (access_width >= 64U && !riscv_dm_write(hart->dbg_module, RV_DM_DATA1, value[1])) + return false; + if (access_width == 128 && + !(riscv_dm_write(hart->dbg_module, RV_DM_DATA2, value[2]) && + riscv_dm_write(hart->dbg_module, RV_DM_DATA3, value[3]))) + return false; + /* Configure and run the write */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, + RV_DM_ABST_CMD_ACCESS_REG | RV_ABST_WRITE | RV_REG_XFER | riscv_hart_access_width(access_width) | + (reg & ~RV_CSR_FORCE_MASK))) + return false; + return riscv_command_wait_complete(hart); +} + +uint8_t riscv_mem_access_width(const riscv_hart_s *const hart, const target_addr_t address, const size_t length) +{ + /* Grab the Hart's most maxmimally aligned possible write width */ + uint8_t access_width = riscv_hart_access_width(hart->address_width) >> RV_MEM_ACCESS_SHIFT; + /* Convert the hart access width to a mask - for example, for 32-bit harts, this gives (1U << 2U) - 1U = 3U */ + uint8_t align_mask = (1U << access_width) - 1U; + /* Mask out the bottom bits of both the address and length - anything that determines the alignment */ + const uint8_t addr_bits = address & align_mask; + const uint8_t len_bits = length & align_mask; + /* bitwise-OR together the result so, for example, an odd address 2-byte read results in a pattern like 0bxx11 */ + const uint8_t align = addr_bits | len_bits; + /* Loop down through the possible access widths till we find one suitably aligned */ + for (; access_width; --access_width) { + if ((align & align_mask) == 0) + return access_width; + align_mask >>= 1U; + } + return access_width; +} + +static void riscv_hart_discover_triggers(riscv_hart_s *const hart) +{ + /* Discover how many breakpoints this hart supports */ + hart->triggers = UINT32_MAX; + if (!riscv_csr_write(hart, RV_TRIG_SELECT | RV_CSR_FORCE_32_BIT, &hart->triggers) || + !riscv_csr_read(hart, RV_TRIG_SELECT | RV_CSR_FORCE_32_BIT, &hart->triggers)) { + hart->triggers = 0; + return; + } + /* + * The value we read back will always be one less than the actual number supported + * as it represents the last valid index, rather than the last valid breakpoint. + */ + ++hart->triggers; + DEBUG_INFO("Hart has %" PRIu32 " trigger slots available\n", hart->triggers); + /* If the hardware supports more slots than we do, cap it. */ + if (hart->triggers > RV_TRIGGERS_MAX) + hart->triggers = RV_TRIGGERS_MAX; + + /* Next, go through each one and map what it supports out into the trigger_uses slots */ + for (uint32_t trigger = 0; trigger < hart->triggers; ++trigger) { + /* Select the trigger */ + riscv_csr_write(hart, RV_TRIG_SELECT | RV_CSR_FORCE_32_BIT, &trigger); + /* Try reading the trigger info */ + uint32_t info = 0; + /* Some chips reply ok but returns 0 in the following call (WCH)*/ + if (!riscv_csr_read(hart, RV_TRIG_INFO | RV_CSR_FORCE_32_BIT, &info) || !info) { + /* + * If that fails, it's probably because the tinfo register isn't implemented, so read + * the tdata1 register instead and extract the type from the MSb and build the info bitset from that + */ + if (hart->access_width == 32U) { + uint32_t data = 0; + riscv_csr_read(hart, RV_TRIG_DATA_1, &data); + /* The last 4 bits contain the trigger info */ + info = data >> 28U; + } else { + uint64_t data = 0; + riscv_csr_read(hart, RV_TRIG_DATA_1, &data); + /* The last 4 bits contain the trigger info */ + info = data >> 60U; + } + /* Info now needs converting from a value from 0 to 15 to having the correct bit set */ + info = 1U << info; + } + /* If the 0th bit is set, this means the trigger is unsupported. Clear it to make testing easy */ + info &= RV_TRIGGER_SUPPORT_MASK; + /* Now info's bottom 16 bits contain the supported trigger modes, so write this info to the slot in the hart */ + hart->trigger_uses[trigger] = info; + DEBUG_TARGET("Hart trigger slot %" PRIu32 " modes: %04" PRIx32 "\n", trigger, info); + } +} + +riscv_match_size_e riscv_breakwatch_match_size(const size_t size) +{ + switch (size) { + case 8U: + return RV_MATCH_SIZE_8_BIT; + case 16U: + return RV_MATCH_SIZE_16_BIT; + case 32U: + return RV_MATCH_SIZE_32_BIT; + case 48U: + return RV_MATCH_SIZE_48_BIT; + case 64U: + return RV_MATCH_SIZE_64_BIT; + case 80U: + return RV_MATCH_SIZE_80_BIT; + case 96U: + return RV_MATCH_SIZE_96_BIT; + case 112U: + return RV_MATCH_SIZE_112_BIT; + case 128U: + return RV_MATCH_SIZE_128_BIT; + } + return 0; +} + +bool riscv_config_trigger(riscv_hart_s *const hart, const uint32_t trigger, const riscv_trigger_state_e mode, + const void *const config, const void *const address) +{ + /* + * Select the trigger and write the new configuration to it provided by config. + * tdata1 (RV_TRIG_DATA_1) becomes mcontrol (match control) for this - + * see ยง5.2.9 pg53 of the RISC-V debug spec v0.13.2 for more details. + */ + const bool result = riscv_csr_write(hart, RV_TRIG_SELECT | RV_CSR_FORCE_32_BIT, &trigger) && + riscv_csr_write(hart, RV_TRIG_DATA_1, config) && riscv_csr_write(hart, RV_TRIG_DATA_2, address); + if (result) { + /* If that succeeds, update the slot with the new mode it's in */ + hart->trigger_uses[trigger] &= ~RV_TRIGGER_MODE_MASK; + hart->trigger_uses[trigger] |= mode; + } + return result; +} + +static bool riscv_attach(target_s *const target) +{ + riscv_hart_s *const hart = riscv_hart_struct(target); + /* If the DMI requires special preparation, do that first */ + if (hart->dbg_module->dmi_bus->prepare) + hart->dbg_module->dmi_bus->prepare(target); + /* We then also need to select the Hart again so we're poking with the right one on the target */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel)) + return false; + /* We then need to halt the hart so the attach process can function */ + riscv_halt_request(target); + return true; +} + +static void riscv_detach(target_s *const target) +{ + riscv_hart_s *const hart = riscv_hart_struct(target); + /* Once we get done and the user's asked us to detach, we need to resume the hart */ + riscv_halt_resume(target, false); + /* If the DMI needs steps done to quiesce it, finsh up with that */ + if (hart->dbg_module->dmi_bus->quiesce) + hart->dbg_module->dmi_bus->quiesce(target); +} + +static bool riscv_check_error(target_s *const target) +{ + return riscv_hart_struct(target)->status != RISCV_HART_NO_ERROR; +} + +static bool riscv_dm_poll_state(riscv_dm_s *const dbg_module, const uint32_t state) +{ + platform_timeout_s timeout; + platform_timeout_set(&timeout, 500U); + /* Poll for the requested state to become set */ + uint32_t status = 0; + while (!(status & state)) { + if (!riscv_dm_read(dbg_module, RV_DM_STATUS, &status) || platform_timeout_is_expired(&timeout)) + return false; + } + return true; +} + +static void riscv_halt_request(target_s *const target) +{ + riscv_hart_s *const hart = riscv_hart_struct(target); + /* Request the hart to halt */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel | RV_DM_CTRL_HALT_REQ)) + return; + /* Poll for the hart to become halted */ + if (!riscv_dm_poll_state(hart->dbg_module, RV_DM_STAT_ALL_HALTED)) + return; + /* Clear the request now we've got it halted */ + (void)riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel); +} + +static void riscv_halt_resume(target_s *target, const bool step) +{ + riscv_hart_s *const hart = riscv_hart_struct(target); + /* Configure the debug controller for single-stepping as appropriate */ + uint32_t stepping_config = 0U; + if (!riscv_csr_read(hart, RV_DCSR | RV_CSR_FORCE_32_BIT, &stepping_config)) + return; + if (step) + stepping_config |= RV_DCSR_STEP | RV_DCSR_STEPIE; + else + stepping_config &= ~(RV_DCSR_STEP | RV_DCSR_STEPIE); + if (!riscv_csr_write(hart, RV_DCSR | RV_CSR_FORCE_32_BIT, &stepping_config)) + return; + /* Request the hart to resume */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel | RV_DM_CTRL_RESUME_REQ)) + return; + /* Poll for the hart to become resumed */ + if (!riscv_dm_poll_state(hart->dbg_module, RV_DM_STAT_ALL_RESUME_ACK)) + return; + /* Clear the request now we've got it resumed */ + (void)riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel); +} + +static target_halt_reason_e riscv_halt_poll(target_s *const target, target_addr_t *const watch) +{ + (void)watch; + riscv_hart_s *const hart = riscv_hart_struct(target); + uint32_t status = 0; + /* Check if the hart is currently halted */ + if (!riscv_dm_read(hart->dbg_module, RV_DM_STATUS, &status)) + return TARGET_HALT_ERROR; + /* If the hart is currently running, exit out early */ + if (!(status & RV_DM_STAT_ALL_HALTED)) + return TARGET_HALT_RUNNING; + /* Read out DCSR to find out why we're halted */ + if (!riscv_csr_read(hart, RV_DCSR, &status)) + return TARGET_HALT_ERROR; + status &= RV_DCSR_CAUSE_MASK; + /* Dispatch on the cause code */ + switch (status) { + case RV_HALT_CAUSE_TRIGGER: + /* XXX: Need to read out the triggers to find the one causing this, and grab the watch value */ + return TARGET_HALT_BREAKPOINT; + case RV_HALT_CAUSE_STEP: + return TARGET_HALT_STEPPING; + default: + break; + } + /* In the default case, assume it was by request (ebreak, haltreq, resethaltreq) */ + return TARGET_HALT_REQUEST; +} + +/* Do note that this can be used with a riscv_halt_request() call to initiate halt-on-reset debugging */ +static void riscv_reset(target_s *const target) +{ + riscv_hart_s *const hart = riscv_hart_struct(target); + /* If the target does not have the nRST pin inhibited, use that to initiate reset */ + if (!(target->target_options & RV_TOPT_INHIBIT_NRST)) { + platform_nrst_set_val(true); + riscv_dm_poll_state(hart->dbg_module, RV_DM_STAT_ALL_RESET); + platform_nrst_set_val(false); + /* In theory we're done at this point and no debug state was perturbed */ + } else { + /* + * Otherwise, if nRST is not usable, use instead reset via dmcontrol. In this case, + * when reset is requested, use the ndmreset bit to perform a system reset + */ + riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel | RV_DM_CTRL_SYSTEM_RESET); + riscv_dm_poll_state(hart->dbg_module, RV_DM_STAT_ALL_RESET); + /* Complete the reset by resetting ndmreset */ + riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel); + } + /* Acknowledge the reset */ + riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel | RV_DM_CTRL_HART_ACK_RESET); + target_check_error(target); +} + +static const char *riscv_fpu_ext_string(const uint32_t extensions) +{ + if (extensions & RV_ISA_EXT_QUAD_FLOAT) + return "q"; + if (extensions & RV_ISA_EXT_DOUBLE_FLOAT) + return "d"; + if (extensions & RV_ISA_EXT_SINGLE_FLOAT) + return "f"; + return ""; +} + +/* + * This function creates the target description XML string for a RISC-V part. + * This is done this way to decrease string duplication and thus code size, making it + * unfortunately much less readable than the string literal it is equivalent to. + * + * This string it creates is the XML-equivalent to the following: + * "" + * "" + * "" + * " riscv:rv[address_width][exts]" + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * The following are only generated for an I core, not an E: + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * Both are then continued with: + * " " + * " " + * "" + */ +static size_t riscv_build_target_description( + char *const buffer, size_t max_length, const uint8_t address_width, const uint32_t extensions) +{ + const bool embedded = extensions & RV_ISA_EXT_EMBEDDED; + const uint32_t fpu = extensions & RV_ISA_EXT_ANY_FLOAT; + + size_t print_size = max_length; + /* Start with the "preamble" chunks, which are mostly common across targets save for 2 words. */ + int offset = snprintf(buffer, print_size, "%s target %sriscv:rv%u%c%s%s ", + gdb_xml_preamble_first, gdb_xml_preamble_second, address_width, embedded ? 'e' : 'i', riscv_fpu_ext_string(fpu), + gdb_xml_preamble_third); + + const uint8_t gprs = embedded ? 16U : 32U; + /* Then build the general purpose register descriptions using the arrays at top of file */ + /* Note that in a device using the embedded (E) extension, we only generate the first 16. */ + for (uint8_t i = 0; i < gprs; ++i) { + if (max_length != 0) + print_size = max_length - (size_t)offset; + + const char *const name = riscv_gpr_names[i]; + const gdb_reg_type_e type = riscv_gpr_types[i]; + + offset += snprintf(buffer + offset, print_size, "", name, address_width, + gdb_reg_type_strings[type], i == 0 ? " regnum=\"0\"" : ""); + } + + /* Then build the program counter register description, which has the same bitsize as the GPRs. */ + if (max_length != 0) + print_size = max_length - (size_t)offset; + offset += snprintf(buffer + offset, print_size, "", address_width, + gdb_reg_type_strings[GDB_TYPE_CODE_PTR]); + + /* XXX: TODO - implement generation of the FPU feature and registers */ + + /* Add the closing tags required */ + if (max_length != 0) + print_size = max_length - (size_t)offset; + + offset += snprintf(buffer + offset, print_size, ""); + /* offset is now the total length of the string created, discard the sign and return it. */ + return (size_t)offset; +} + +static const char *riscv_target_description(target_s *const target) +{ + const riscv_hart_s *const hart = riscv_hart_struct(target); + const size_t description_length = + riscv_build_target_description(NULL, 0, hart->address_width, hart->extensions) + 1U; + char *const description = malloc(description_length); + if (description) + (void)riscv_build_target_description(description, description_length, hart->address_width, hart->extensions); + return description; +} diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h new file mode 100644 index 00000000000..c1b71790c78 --- /dev/null +++ b/src/target/riscv_debug.h @@ -0,0 +1,200 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rachel Mant + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TARGET_RISCV_DEBUG_H +#define TARGET_RISCV_DEBUG_H + +#include +#include +#include "target.h" + +typedef enum riscv_debug_version { + RISCV_DEBUG_UNKNOWN, + RISCV_DEBUG_UNIMPL, + RISCV_DEBUG_0_11, + RISCV_DEBUG_0_13, + RISCV_DEBUG_1_0, +} riscv_debug_version_e; + +/* This enum describes the Hart status (eg after a CSR read/write) */ +typedef enum riscv_hart_status { + /* The Hart is in a good state */ + RISCV_HART_NO_ERROR = 0, + /* The Hart was bust when the status was read */ + RISCV_HART_BUSY = 1, + /* The operation requested of the Hart was not supported */ + RISCV_HART_NOT_SUPP = 2, + /* An exception occured on the Hart while running the operation */ + RISCV_HART_EXCEPTION = 3, + /* The Hart is in the wrong state for the requested operation */ + RISCV_HART_WRONG_STATE = 4, + /* The operation triggered a Hart bus error (bad alignment, access size, or timeout) */ + RISCV_HART_BUS_ERROR = 5, + /* The operation failed for other (unknown) reasons */ + RISCV_HART_OTHER = 7, +} riscv_hart_status_e; + +/* This enum describes the current state of a trigger in the TM */ +typedef enum riscv_trigger_state { + RISCV_TRIGGER_MODE_UNUSED = 0x00000000U, + RISCV_TRIGGER_MODE_BREAKPOINT = 0x00010000U, + RISCV_TRIGGER_MODE_WATCHPOINT = 0x00020000U, +} riscv_trigger_state_e; + +/* The size bits are 22:21 + 17:16, but the upper 2 are only valid on rv64 */ +typedef enum riscv_match_size { + RV_MATCH_SIZE_8_BIT = 0x00010000U, + RV_MATCH_SIZE_16_BIT = 0x00020000U, + RV_MATCH_SIZE_32_BIT = 0x00030000U, + RV_MATCH_SIZE_48_BIT = 0x00200000U, + RV_MATCH_SIZE_64_BIT = 0x00210000U, + RV_MATCH_SIZE_80_BIT = 0x00220000U, + RV_MATCH_SIZE_96_BIT = 0x00230000U, + RV_MATCH_SIZE_112_BIT = 0x00400000U, + RV_MATCH_SIZE_128_BIT = 0x00410000U, +} riscv_match_size_e; + +typedef struct riscv_dmi riscv_dmi_s; + +/* This structure represents a version-agnostic Debug Module Interface on a RISC-V device */ +struct riscv_dmi { + uint32_t ref_count; + + uint16_t designer_code; + riscv_debug_version_e version; + + uint8_t dev_index; + uint8_t idle_cycles; + uint8_t address_width; + uint8_t fault; + + void (*prepare)(target_s *target); + void (*quiesce)(target_s *target); + bool (*read)(riscv_dmi_s *dmi, uint32_t address, uint32_t *value); + bool (*write)(riscv_dmi_s *dmi, uint32_t address, uint32_t value); +}; + +/* This represents a specific Debug Module on the DMI bus */ +typedef struct riscv_dm { + uint32_t ref_count; + + riscv_dmi_s *dmi_bus; + uint32_t base; + riscv_debug_version_e version; +} riscv_dm_s; + +#define RV_TRIGGERS_MAX 8U + +/* This represents a specifc Hart on a DM */ +typedef struct riscv_hart { + riscv_dm_s *dbg_module; + uint32_t hart_idx; + uint32_t hartsel; + uint8_t access_width; + uint8_t address_width; + riscv_hart_status_e status; + + uint32_t extensions; + uint32_t vendorid; + uint32_t archid; + uint32_t implid; + uint32_t hartid; + + uint32_t triggers; + uint32_t trigger_uses[RV_TRIGGERS_MAX]; +} riscv_hart_s; + +#define RV_STATUS_VERSION_MASK 0x0000000fU + +#define RV_DM_DATA0 0x04U +#define RV_DM_DATA1 0x05U +#define RV_DM_DATA2 0x06U +#define RV_DM_DATA3 0x07U +#define RV_DM_ABST_CTRLSTATUS 0x16U +#define RV_DM_ABST_COMMAND 0x17U + +#define RV_DM_ABST_CMD_ACCESS_REG 0x00000000U +#define RV_DM_ABST_CMD_ACCESS_MEM 0x02000000U + +#define RV_ABST_READ 0x00000000U +#define RV_ABST_WRITE 0x00010000U +#define RV_REG_XFER 0x00020000U +#define RV_REG_ACCESS_32_BIT 0x00200000U +#define RV_REG_ACCESS_64_BIT 0x00300000U +#define RV_REG_ACCESS_128_BIT 0x00400000U +#define RV_MEM_ADDR_POST_INC 0x00080000U + +#define RV_MEM_ACCESS_8_BIT 0x0U +#define RV_MEM_ACCESS_16_BIT 0x1U +#define RV_MEM_ACCESS_32_BIT 0x2U +#define RV_MEM_ACCESS_64_BIT 0x3U +#define RV_MEM_ACCESS_128_BIT 0x4U +#define RV_MEM_ACCESS_SHIFT 20U + +/* dpc -> Debug Program Counter */ +#define RV_DPC 0x7b1U +/* The GPR base defines the starting register space address for the CPU state registers */ +#define RV_GPR_BASE 0x1000U +/* The FP base defines the starting register space address for the floating point registers */ +#define RV_FP_BASE 0x1020U + +#define RV_ISA_EXT_EMBEDDED 0x00000010U +#define RV_ISA_EXT_ANY_FLOAT 0x00010028U +#define RV_ISA_EXT_SINGLE_FLOAT 0x00000020U +#define RV_ISA_EXT_DOUBLE_FLOAT 0x00000008U +#define RV_ISA_EXT_QUAD_FLOAT 0x00010000U + +#define RV_TRIGGER_SUPPORT_MASK 0x0000fffeU +#define RV_TRIGGER_MODE_MASK 0xffff0000U +#define RV_TRIGGER_SUPPORT_BREAKWATCH 0x00000004U + +#define RV_TOPT_INHIBIT_NRST 0x00000001U + +void riscv_jtag_dtm_handler(uint8_t dev_index); +void riscv_dmi_init(riscv_dmi_s *dmi); +riscv_hart_s *riscv_hart_struct(target_s *target); + +bool riscv_dm_read(riscv_dm_s *dbg_module, uint8_t address, uint32_t *value); +bool riscv_dm_write(riscv_dm_s *dbg_module, uint8_t address, uint32_t value); +bool riscv_command_wait_complete(riscv_hart_s *hart); +bool riscv_csr_read(riscv_hart_s *hart, uint16_t reg, void *data); +bool riscv_csr_write(riscv_hart_s *hart, uint16_t reg, const void *data); +riscv_match_size_e riscv_breakwatch_match_size(size_t size); +bool riscv_config_trigger( + riscv_hart_s *hart, uint32_t trigger, riscv_trigger_state_e mode, const void *config, const void *address); + +uint8_t riscv_mem_access_width(const riscv_hart_s *hart, target_addr_t address, size_t length); +void riscv32_unpack_data(void *dest, uint32_t data, uint8_t access_width); +uint32_t riscv32_pack_data(const void *src, uint8_t access_width); + +#endif /*TARGET_RISCV_DEBUG_H*/ diff --git a/src/target/riscv_jtag_dtm.c b/src/target/riscv_jtag_dtm.c new file mode 100644 index 00000000000..ea36d6298ad --- /dev/null +++ b/src/target/riscv_jtag_dtm.c @@ -0,0 +1,204 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rachel Mant + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "general.h" +#include "jtag_scan.h" +#include "jtagtap.h" +#include "riscv_debug.h" +#include "adiv5.h" + +#define IR_DTMCS 0x10U +#define IR_DMI 0x11U +#define IR_BYPASS 0x1fU + +#define RV_DTMCS_NOOP 0x00000000U +#define RV_DTMCS_DMI_RESET 0x00010000U +#define RV_DTMCS_DMI_HARD_RESET 0x00020000U +#define RV_DTMCS_IDLE_CYCLES_MASK 0x00007000U +#define RV_DTMCS_IDLE_CYCLES_SHIFT 12U +#define RV_DTMCS_DMI_STATUS_MASK 0x00000c00U +#define RV_DTMCS_DMI_STATUS_SHIFT 10U +#define RV_DTMCS_ADDRESS_MASK 0x000003f0U +#define RV_DTMCS_ADDRESS_SHIFT 4U + +#define RV_DMI_NOOP 0U +#define RV_DMI_READ 1U +#define RV_DMI_WRITE 2U +#define RV_DMI_SUCCESS 0U +#define RV_DMI_FAILURE 2U +#define RV_DMI_TOO_SOON 3U + +static void riscv_jtag_dtm_init(riscv_dmi_s *dmi); +static uint32_t riscv_shift_dtmcs(const riscv_dmi_s *dmi, uint32_t control); +static bool riscv_jtag_dmi_read(riscv_dmi_s *dmi, uint32_t address, uint32_t *value); +static bool riscv_jtag_dmi_write(riscv_dmi_s *dmi, uint32_t address, uint32_t value); +static riscv_debug_version_e riscv_dtmcs_version(uint32_t dtmcs); + +static void riscv_jtag_prepare(target_s *target); +static void riscv_jtag_quiesce(target_s *target); + +void riscv_jtag_dtm_handler(const uint8_t dev_index) +{ + riscv_dmi_s *dmi = calloc(1, sizeof(*dmi)); + if (!dmi) { /* calloc failed: heap exhaustion */ + DEBUG_WARN("calloc: failed in %s\n", __func__); + return; + } + + /* Setup and try to discover the DMI bus */ + dmi->dev_index = dev_index; + /* + * The code in JTAG_IDCODE_DESIGNER is in the form + * Bits 10:7 - JEP-106 Continuation code + * Bits 6:0 - JEP-106 Identity code + * here we convert it to our internal representation, See JEP-106 code list + */ + const uint16_t designer = + (jtag_devs[dev_index].jd_idcode & JTAG_IDCODE_DESIGNER_MASK) >> JTAG_IDCODE_DESIGNER_OFFSET; + dmi->designer_code = + (designer & JTAG_IDCODE_DESIGNER_JEP106_CONT_MASK) << 1U | (designer & JTAG_IDCODE_DESIGNER_JEP106_CODE_MASK); + + riscv_jtag_dtm_init(dmi); + /* If we failed to find any DMs or Harts, free the structure */ + if (!dmi->ref_count) + free(dmi); + + /* Reset the JTAG machinary back to bypass to scan the next device in the chain */ + jtag_dev_write_ir(dev_index, IR_BYPASS); +} + +static void riscv_jtag_dtm_init(riscv_dmi_s *const dmi) +{ + const uint32_t dtmcs = riscv_shift_dtmcs(dmi, RV_DTMCS_NOOP); + dmi->version = riscv_dtmcs_version(dtmcs); + /* Configure the TAP idle cylces based on what we've read */ + dmi->idle_cycles = (dtmcs & RV_DTMCS_IDLE_CYCLES_MASK) >> RV_DTMCS_IDLE_CYCLES_SHIFT; + /* And figure out how many address bits the DMI address space has */ + dmi->address_width = (dtmcs & RV_DTMCS_ADDRESS_MASK) >> RV_DTMCS_ADDRESS_SHIFT; + /* Switch into DMI access mode for speed */ + jtag_dev_write_ir(dmi->dev_index, IR_DMI); + + dmi->prepare = riscv_jtag_prepare; + dmi->quiesce = riscv_jtag_quiesce; + dmi->read = riscv_jtag_dmi_read; + dmi->write = riscv_jtag_dmi_write; + + riscv_dmi_init(dmi); +} + +/* Shift (read + write) the Debug Transport Module Control/Status (DTMCS) register */ +uint32_t riscv_shift_dtmcs(const riscv_dmi_s *const dmi, const uint32_t control) +{ + jtag_dev_write_ir(dmi->dev_index, IR_DTMCS); + uint32_t status = 0; + jtag_dev_shift_dr(dmi->dev_index, (uint8_t *)&status, (const uint8_t *)&control, 32); + return status; +} + +static bool riscv_shift_dmi(riscv_dmi_s *const dmi, const uint8_t operation, const uint32_t address, + const uint32_t data_in, uint32_t *const data_out) +{ + jtag_dev_s *device = &jtag_devs[dmi->dev_index]; + jtagtap_shift_dr(); + jtag_proc.jtagtap_tdi_seq(false, ones, device->dr_prescan); + /* Shift out the 2 bits for the operation, and get the status bits for the previous back */ + uint8_t status = 0; + jtag_proc.jtagtap_tdi_tdo_seq(&status, false, &operation, 2); + /* Then the data component */ + if (data_out) + jtag_proc.jtagtap_tdi_tdo_seq((uint8_t *)data_out, false, (const uint8_t *)&data_in, 32); + else + jtag_proc.jtagtap_tdi_seq(false, (const uint8_t *)&data_in, 32); + /* And finally the address component */ + jtag_proc.jtagtap_tdi_seq(!device->dr_postscan, (const uint8_t *)&address, dmi->address_width); + jtag_proc.jtagtap_tdi_seq(true, ones, device->dr_postscan); + jtagtap_return_idle(dmi->idle_cycles); + /* XXX: Need to deal with when status is 3. */ + dmi->fault = status; + return status == RV_DMI_SUCCESS; +} + +static bool riscv_jtag_dmi_read(riscv_dmi_s *const dmi, const uint32_t address, uint32_t *const value) +{ + /* Setup the location to read from */ + bool result = riscv_shift_dmi(dmi, RV_DMI_READ, address, 0, NULL); + if (result) + /* If that worked, read back the value and check the operation status */ + result = riscv_shift_dmi(dmi, RV_DMI_NOOP, 0, 0, value); + if (!result) + DEBUG_WARN("DMI read at 0x%08" PRIx32 " failed with status %u\n", address, dmi->fault); + return result; +} + +static bool riscv_jtag_dmi_write(riscv_dmi_s *const dmi, const uint32_t address, const uint32_t value) +{ + /* Write a value to the requested register */ + bool result = riscv_shift_dmi(dmi, RV_DMI_WRITE, address, value, NULL); + if (result) + /* If that worked, read back the operation status to ensure the write actually worked */ + result = riscv_shift_dmi(dmi, RV_DMI_NOOP, 0, 0, NULL); + if (!result) + DEBUG_WARN("DMI write at 0x%08" PRIx32 " failed with status %u\n", address, dmi->fault); + return result; +} + +static riscv_debug_version_e riscv_dtmcs_version(const uint32_t dtmcs) +{ + uint8_t version = dtmcs & RV_STATUS_VERSION_MASK; + switch (version) { + case 0: + DEBUG_INFO("RISC-V debug v0.11 DMI\n"); + return RISCV_DEBUG_0_11; + case 1: + /* The stable version of the spec (v1.0) does not currently provide a way to distinguish between */ + /* a device built against v0.13 of the spec or v1.0 of the spec. They use the same value here. */ + DEBUG_INFO("RISC-V debug v0.13/v1.0 DMI\n"); + return RISCV_DEBUG_0_13; + } + DEBUG_INFO("Please report part with unknown RISC-V debug DMI version %x\n", version); + return RISCV_DEBUG_UNKNOWN; +} + +static void riscv_jtag_prepare(target_s *const target) +{ + riscv_hart_s *const hart = riscv_hart_struct(target); + /* We put the TAP into bypass at the end of the JTAG handler, so put it back into DMI */ + jtag_dev_write_ir(hart->dbg_module->dmi_bus->dev_index, IR_DMI); +} + +static void riscv_jtag_quiesce(target_s *const target) +{ + riscv_hart_s *const hart = riscv_hart_struct(target); + /* On detaching, stick the TAP back into bypass */ + jtag_dev_write_ir(hart->dbg_module->dmi_bus->dev_index, IR_BYPASS); +} diff --git a/src/target/stm32f1.c b/src/target/stm32f1.c index 893b1ea9aad..643283cb6b3 100644 --- a/src/target/stm32f1.c +++ b/src/target/stm32f1.c @@ -40,6 +40,7 @@ #include "target.h" #include "target_internal.h" #include "cortexm.h" +#include "jep106.h" static bool stm32f1_cmd_option(target_s *target, int argc, const char **argv); @@ -181,6 +182,35 @@ bool gd32f1_probe(target_s *target) return true; } +/* Identify RISC-V GD32VF1 chips */ +bool gd32vf1_probe(target_s *const target) +{ + /* Make sure the architechture ID matches */ + if (target->cpuid != 0x80000022U) + return false; + /* Then read out the device ID */ + const uint16_t device_id = target_mem_read32(target, DBGMCU_IDCODE) & 0xfffU; + switch (device_id) { + case 0x410U: /* GD32VF103 */ + target->driver = "GD32VF1"; + break; + default: + return false; + } + + const uint32_t signature = target_mem_read32(target, GD32Fx_FLASHSIZE); + const uint16_t flash_size = signature & 0xffffU; + const uint16_t ram_size = signature >> 16U; + + target->part_id = device_id; + target->mass_erase = stm32f1_mass_erase; + target_add_ram(target, 0x20000000, ram_size * 1024U); + stm32f1_add_flash(target, 0x8000000, (size_t)flash_size * 1024U, 0x400U); + target_add_commands(target, stm32f1_cmd_list, target->driver); + + return true; +} + static bool at32f40_detect(target_s *target, const uint16_t part_id) { // Current driver supports only *default* memory layout (256 KB Flash / 96 KB SRAM) @@ -698,7 +728,11 @@ static bool stm32f1_flash_write(target_flash_s *flash, target_addr_t dest, const stm32f1_flash_clear_eop(target, FLASH_BANK1_OFFSET); target_mem_write32(target, FLASH_CR, FLASH_CR_PG); - cortexm_mem_write_sized(target, dest, src, offset, ALIGN_HALFWORD); + /* Use the target API instead of a direct Cortex-M call for GD32VF103 parts */ + if (target->designer_code == JEP106_MANUFACTURER_RV_GIGADEVICE && target->cpuid == 0x80000022U) + target_mem_write(target, dest, src, offset); + else + cortexm_mem_write_sized(target, dest, src, offset, ALIGN_HALFWORD); /* Wait for completion or an error */ if (!stm32f1_flash_busy_wait(target, FLASH_BANK1_OFFSET, NULL)) @@ -712,7 +746,11 @@ static bool stm32f1_flash_write(target_flash_s *flash, target_addr_t dest, const stm32f1_flash_clear_eop(target, FLASH_BANK2_OFFSET); target_mem_write32(target, FLASH_CR + FLASH_BANK2_OFFSET, FLASH_CR_PG); - cortexm_mem_write_sized(target, dest + offset, data + offset, remainder, ALIGN_HALFWORD); + /* Use the target API instead of a direct Cortex-M call for GD32VF103 parts */ + if (target->designer_code == JEP106_MANUFACTURER_RV_GIGADEVICE && target->cpuid == 0x80000022U) + target_mem_write(target, dest + offset, data + offset, remainder); + else + cortexm_mem_write_sized(target, dest + offset, data + offset, remainder, ALIGN_HALFWORD); /* Wait for completion or an error */ if (!stm32f1_flash_busy_wait(target, FLASH_BANK2_OFFSET, NULL)) diff --git a/src/target/target_probe.c b/src/target/target_probe.c index a183fa28c3c..0cc5418a647 100644 --- a/src/target/target_probe.c +++ b/src/target/target_probe.c @@ -75,6 +75,9 @@ CORTEXA_PROBE_WEAK_NOP(cortexa_probe) CORTEXM_PROBE_WEAK_NOP(cortexm_probe) +TARGET_PROBE_WEAK_NOP(riscv32_probe) +TARGET_PROBE_WEAK_NOP(riscv64_probe) + CORTEXM_PROBE_WEAK_NOP(kinetis_mdm_probe) CORTEXM_PROBE_WEAK_NOP(nrf51_mdm_probe) CORTEXM_PROBE_WEAK_NOP(efm32_aap_probe) @@ -83,6 +86,7 @@ CORTEXM_PROBE_WEAK_NOP(lpc55_dmap_probe) TARGET_PROBE_WEAK_NOP(ch32f1_probe) TARGET_PROBE_WEAK_NOP(gd32f1_probe) +TARGET_PROBE_WEAK_NOP(gd32vf1_probe) TARGET_PROBE_WEAK_NOP(gd32f4_probe) TARGET_PROBE_WEAK_NOP(stm32f1_probe) TARGET_PROBE_WEAK_NOP(at32fxx_probe) diff --git a/src/target/target_probe.h b/src/target/target_probe.h index eadfea264c3..54a3ad9bca4 100644 --- a/src/target/target_probe.h +++ b/src/target/target_probe.h @@ -32,6 +32,9 @@ bool cortexa_probe(adiv5_access_port_s *apb, uint32_t debug_base); bool cortexm_probe(adiv5_access_port_s *ap); +bool riscv32_probe(target_s *target); +bool riscv64_probe(target_s *target); + bool kinetis_mdm_probe(adiv5_access_port_s *ap); bool nrf51_mdm_probe(adiv5_access_port_s *ap); bool efm32_aap_probe(adiv5_access_port_s *ap); @@ -43,6 +46,7 @@ bool at32fxx_probe(target_s *target); // STM32 clones from Artery bool mm32l0xx_probe(target_s *target); bool mm32f3xx_probe(target_s *target); bool gd32f1_probe(target_s *target); +bool gd32vf1_probe(target_s *target); bool gd32f4_probe(target_s *target); bool stm32f1_probe(target_s *target); bool stm32f4_probe(target_s *target);