aboutsummaryrefslogtreecommitdiff
path: root/src/target/riscv/riscv-013.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/target/riscv/riscv-013.c')
-rw-r--r--src/target/riscv/riscv-013.c283
1 files changed, 175 insertions, 108 deletions
diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c
index ba3a3a0..271dced 100644
--- a/src/target/riscv/riscv-013.c
+++ b/src/target/riscv/riscv-013.c
@@ -1055,6 +1055,54 @@ static int examine_progbuf(struct target *target)
return ERROR_OK;
}
+static int is_fpu_reg(uint32_t gdb_regno)
+{
+ return (gdb_regno >= GDB_REGNO_FPR0 && gdb_regno <= GDB_REGNO_FPR31) ||
+ (gdb_regno == GDB_REGNO_CSR0 + CSR_FFLAGS) ||
+ (gdb_regno == GDB_REGNO_CSR0 + CSR_FRM) ||
+ (gdb_regno == GDB_REGNO_CSR0 + CSR_FCSR);
+}
+
+static int is_vector_reg(uint32_t 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_VL ||
+ gdb_regno == GDB_REGNO_VTYPE ||
+ gdb_regno == GDB_REGNO_VLENB;
+}
+
+int prep_for_register_access(struct target *target, uint64_t *mstatus,
+ int 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;
+ }
+ }
+ return ERROR_OK;
+}
+
+int cleanup_after_register_access(struct target *target, uint64_t mstatus,
+ int 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;
+}
+
typedef enum {
SPACE_DMI_DATA,
SPACE_DMI_PROGBUF,
@@ -1253,6 +1301,10 @@ static int register_write_direct(struct target *target, unsigned number,
if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
+ uint64_t mstatus;
+ if (prep_for_register_access(target, &mstatus, number) != ERROR_OK)
+ return ERROR_FAIL;
+
scratch_mem_t scratch;
bool use_scratch = false;
if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 &&
@@ -1277,6 +1329,10 @@ static int register_write_direct(struct target *target, unsigned number,
return ERROR_FAIL;
}
+ } else if (number == GDB_REGNO_VTYPE) {
+ riscv_program_insert(&program, csrr(S0, CSR_VL));
+ riscv_program_insert(&program, vsetvli(ZERO, S0, value));
+
} else {
if (register_write_direct(target, GDB_REGNO_S0, value) != ERROR_OK)
return ERROR_FAIL;
@@ -1313,6 +1369,9 @@ static int register_write_direct(struct target *target, unsigned number,
if (use_scratch)
scratch_release(target, &scratch);
+ if (cleanup_after_register_access(target, mstatus, number) != ERROR_OK)
+ return ERROR_FAIL;
+
/* Restore S0. */
if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
return ERROR_FAIL;
@@ -1337,25 +1396,6 @@ static int register_read(struct target *target, uint64_t *value, uint32_t number
return ERROR_OK;
}
-static int is_fpu_reg(uint32_t gdb_regno)
-{
- return (gdb_regno >= GDB_REGNO_FPR0 && gdb_regno <= GDB_REGNO_FPR31) ||
- (gdb_regno == GDB_REGNO_CSR0 + CSR_FFLAGS) ||
- (gdb_regno == GDB_REGNO_CSR0 + CSR_FRM) ||
- (gdb_regno == GDB_REGNO_CSR0 + CSR_FCSR);
-}
-
-static int is_vector_reg(uint32_t 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_VL ||
- gdb_regno == GDB_REGNO_VTYPE ||
- gdb_regno == GDB_REGNO_VLENB;
-}
-
/** Actually read registers from the target right now. */
static int register_read_direct(struct target *target, uint64_t *value, uint32_t number)
{
@@ -1381,19 +1421,8 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t
/* Write program to move data into s0. */
uint64_t mstatus;
- if (is_fpu_reg(number) || is_vector_reg(number)) {
- if (register_read(target, &mstatus, GDB_REGNO_MSTATUS) != ERROR_OK)
- return ERROR_FAIL;
- if (is_fpu_reg(number) && (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(number) && (mstatus & MSTATUS_VS) == 0) {
- if (register_write_direct(target, GDB_REGNO_MSTATUS,
- set_field(mstatus, MSTATUS_VS, 1)) != ERROR_OK)
- return ERROR_FAIL;
- }
- }
+ if (prep_for_register_access(target, &mstatus, number) != ERROR_OK)
+ return ERROR_FAIL;
if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
if (riscv_supports_extension(target, riscv_current_hartid(target), 'D')
@@ -1420,78 +1449,6 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t
}
} else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
riscv_program_csrr(&program, S0, number);
- } else if (number >= GDB_REGNO_V0 && number <= GDB_REGNO_V31) {
- /*
- * The idea is that you read a vector register destructively: read
- * element 0 using vmv.x.s into t0; send t0 to the debugger; then
- * vslide1down with t0 as the scalar argument. This is effectively
- * rotating the vector one element at a time, so after vl steps,
- * the vector register is back to its original value."
- *
- * The two instructions vmv.x.s and vmv.s.x are described in
- * section 17.1.
- *
- * The vmv.x.s instruction copies a single SEW-wide element from
- * index 0 of the source vector register to a destination integer
- * register.
- *
- * The vmv.s.x instruction copies the scalar integer register to
- * element 0 of the destination vector register.
- *
- * Executing the two instructions in the PROGBUF and reading or
- * writing the x register in a loop would not be that much slower
- * than executing the instruction to move a vector reg to memory
- * then reading memory sequentially.
- *
- * I recommend not using memory-based vector register read/writes -
- * it would add user complication to have to allocate target memory
- * for a vector register buffer just for debug - and require adding
- * to the linker script to do this allocation and then informing
- * the debugger where it is. */
- /* TODO: this continuous save/restore is terrible for performance. */
-
- uint64_t vtype, vl;
- /* Save vtype and vl. */
- if (register_read(target, &vtype, GDB_REGNO_VTYPE) != ERROR_OK)
- return ERROR_FAIL;
- if (register_read(target, &vl, GDB_REGNO_VL) != ERROR_OK)
- return ERROR_FAIL;
-
- /* Restore 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));
- return ERROR_FAIL;
- }
- if (register_write_direct(target, GDB_REGNO_VTYPE, encoded_vsew << 2) != ERROR_OK)
- return ERROR_FAIL;
- unsigned debug_vl = DIV_ROUND_UP(r->vlenb[r->current_hartid] * 8,
- riscv_xlen(target));
- if (register_write_direct(target, GDB_REGNO_VL, debug_vl) != ERROR_OK)
- return ERROR_FAIL;
-
- unsigned vnum = number - GDB_REGNO_V0;
- riscv_program_insert(&program, vmv_x_s(S0, vnum));
- riscv_program_insert(&program, vslide1down_vx(vnum, vnum, S0, false));
- for (unsigned i = 0; i < debug_vl; i++) {
- if (riscv_program_exec(&program, target) != ERROR_OK)
- return ERROR_FAIL;
- if (register_read_direct(target, value, GDB_REGNO_S0) != ERROR_OK)
- return ERROR_FAIL;
- }
-
- /* Restore vtype and vl. */
- if (register_write_direct(target, GDB_REGNO_VTYPE, vtype) != ERROR_OK)
- return ERROR_FAIL;
- if (register_write_direct(target, GDB_REGNO_VL, vl) != ERROR_OK)
- return ERROR_FAIL;
} else {
LOG_ERROR("Unsupported register: %s", gdb_regno_name(number));
return ERROR_FAIL;
@@ -1512,10 +1469,8 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t
return ERROR_FAIL;
}
- if ((is_fpu_reg(number) && (mstatus & MSTATUS_FS) == 0) ||
- (is_vector_reg(number) && (mstatus & MSTATUS_VS) == 0))
- if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus) != ERROR_OK)
- return ERROR_FAIL;
+ if (cleanup_after_register_access(target, mstatus, number) != ERROR_OK)
+ return ERROR_FAIL;
/* Restore S0. */
if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
@@ -1881,6 +1836,114 @@ static unsigned riscv013_data_bits(struct target *target)
return riscv_xlen(target);
}
+static int riscv013_get_register_buf(struct target *target,
+ uint8_t *value, int regno)
+{
+ RISCV_INFO(r);
+ assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31);
+
+ riscv_reg_t s0;
+ if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
+ return ERROR_FAIL;
+
+ uint64_t mstatus;
+ if (prep_for_register_access(target, &mstatus, regno) != ERROR_OK)
+ return ERROR_FAIL;
+
+ /*
+ * The idea is that you read a vector register destructively: read
+ * element 0 using vmv.x.s into t0; send t0 to the debugger; then
+ * vslide1down with t0 as the scalar argument. This is effectively
+ * rotating the vector one element at a time, so after vl steps,
+ * the vector register is back to its original value."
+ *
+ * The two instructions vmv.x.s and vmv.s.x are described in
+ * section 17.1.
+ *
+ * The vmv.x.s instruction copies a single SEW-wide element from
+ * index 0 of the source vector register to a destination integer
+ * register.
+ *
+ * The vmv.s.x instruction copies the scalar integer register to
+ * element 0 of the destination vector register.
+ *
+ * Executing the two instructions in the PROGBUF and reading or
+ * writing the x register in a loop would not be that much slower
+ * than executing the instruction to move a vector reg to memory
+ * then reading memory sequentially.
+ *
+ * I recommend not using memory-based vector register read/writes -
+ * it would add user complication to have to allocate target memory
+ * for a vector register buffer just for debug - and require adding
+ * to the linker script to do this allocation and then informing
+ * the debugger where it is. */
+
+ /* TODO: this continuous save/restore is terrible for performance. */
+
+ uint64_t vtype, vl;
+ /* Save vtype and vl. */
+ if (register_read(target, &vtype, GDB_REGNO_VTYPE) != ERROR_OK)
+ return ERROR_FAIL;
+ if (register_read(target, &vl, GDB_REGNO_VL) != ERROR_OK)
+ return ERROR_FAIL;
+
+ /* 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));
+ return ERROR_FAIL;
+ }
+ if (register_write_direct(target, GDB_REGNO_VTYPE, encoded_vsew << 2) != ERROR_OK)
+ return ERROR_FAIL;
+ unsigned debug_vl = DIV_ROUND_UP(r->vlenb[r->current_hartid] * 8,
+ riscv_xlen(target));
+ if (register_write_direct(target, GDB_REGNO_VL, debug_vl) != 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, false));
+ for (unsigned i = 0; i < debug_vl; i++) {
+ if (riscv_program_exec(&program, target) != ERROR_OK)
+ return ERROR_FAIL;
+ uint64_t v;
+ if (register_read_direct(target, &v, GDB_REGNO_S0) != ERROR_OK)
+ return ERROR_FAIL;
+ buf_set_u64(value, xlen * i, xlen, v);
+ }
+
+ /* Restore vtype and vl. */
+ if (register_write_direct(target, GDB_REGNO_VTYPE, vtype) != ERROR_OK)
+ return ERROR_FAIL;
+ if (register_write_direct(target, GDB_REGNO_VL, 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)
+ return ERROR_FAIL;
+
+ return ERROR_OK;
+}
+
+static int riscv013_set_register_buf(struct target *target,
+ int regno, const uint8_t *value)
+{
+ assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31);
+ return ERROR_FAIL;
+}
+
static int init_target(struct command_context *cmd_ctx,
struct target *target)
{
@@ -1889,6 +1952,8 @@ static int init_target(struct command_context *cmd_ctx,
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->resume_go = &riscv013_resume_go;
@@ -3505,10 +3570,12 @@ static int riscv013_get_register(struct target *target,
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 = get_field(dcsr, CSR_DCSR_PRV);
} else {