Replace the 0.13-specific "program_t" with a generic one

This new version changes how we handle temporary registers: rather than
tracking them by hand (like in the old code), they're now tracked as
part of assembling programs.  The register save/restore assertion that
used to fire all the time no longer exists.
This commit is contained in:
Palmer Dabbelt 2017-04-12 16:29:39 -07:00
parent 0e54044ac7
commit e481524362
10 changed files with 983 additions and 509 deletions

View File

@ -213,14 +213,9 @@ static int riscv_gdb_thread_packet(struct connection *connection, const char *pa
return JIM_OK;
case 'R':
{
char *packet_str = malloc(packet_size + 1);
memset(packet_str, '\0', packet_size + 1);
memcpy(packet_str, packet, packet_size);
LOG_WARNING("riscv_gdb_thread_packet(%s): unimplemented", packet_str);
gdb_put_packet(connection, NULL, 0);
gdb_put_packet(connection, "E00", 3);
return JIM_OK;
}
default:
LOG_ERROR("Unknown packet of type 0x%2.2x", packet[0]);
gdb_put_packet(connection, NULL, 0);
@ -285,7 +280,7 @@ static int riscv_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char
*hex_reg_list[0] = '\0';
for (size_t i = 0; i < n_regs; ++i) {
if (riscv_has_register(rtos->target, thread_id, i)) {
uint64_t reg_value = riscv_get_register(rtos->target, thread_id - 1, i);
uint64_t reg_value = riscv_get_register_on_hart(rtos->target, thread_id - 1, i);
for (size_t byte = 0; byte < xlen / 8; ++byte) {
uint8_t reg_byte = reg_value >> (byte * 8);
char hex[3];

View File

@ -138,7 +138,8 @@ INTEL_IA32_SRC = \
RISCV_SRC = \
riscv/riscv-011.c \
riscv/riscv-013.c \
riscv/riscv.c
riscv/riscv.c \
riscv/program.c
noinst_HEADERS = \
algorithm.h \

View File

@ -26,15 +26,28 @@
#define DTM_IDCODE_1 (0x1 << DTM_IDCODE_1_OFFSET)
#define DTM_DTMCS 0x10
/*
* Writing 1 to this bit resets the DMI controller, clearing any
* sticky error state.
* Writing 1 to this bit does a hard reset of the DTM,
* causing the DTM to forget about any outstanding DMI transactions.
* In general this should only be used when the Debugger has
* reason to expect that the outstanding DMI transaction will never
* complete (e.g. a reset condition caused an inflight DMI transaction to
* be cancelled).
*/
#define DTM_DTMCS_DMIHARDRESET_OFFSET 17
#define DTM_DTMCS_DMIHARDRESET_LENGTH 1
#define DTM_DTMCS_DMIHARDRESET (0x1 << DTM_DTMCS_DMIHARDRESET_OFFSET)
/*
* Writing 1 to this bit clears the sticky error state
* and allows the DTM to retry or complete the previous
* transaction.
*/
#define DTM_DTMCS_DMIRESET_OFFSET 16
#define DTM_DTMCS_DMIRESET_LENGTH 1
#define DTM_DTMCS_DMIRESET (0x1 << DTM_DTMCS_DMIRESET_OFFSET)
/*
* This is the minimum number of cycles a debugger should spend in
* Run-Test/Idle after every DMI scan to avoid a 'busy'
* This is a hint to the debugger of the minimum number of
* cycles a debugger should spend in
* Run-Test/Idle after every DMI scan to avoid a `busy'
* return code (\Fdmistat of 3). A debugger must still
* check \Fdmistat when necessary.
*
@ -146,26 +159,26 @@
#define CSR_DCSR_XDEBUGVER_LENGTH 2
#define CSR_DCSR_XDEBUGVER (0x3 << CSR_DCSR_XDEBUGVER_OFFSET)
/*
* When 1, {\tt ebreak} instructions in Machine Mode enter Halt Mode.
* When 1, {\tt ebreak} instructions in Machine Mode enter Debug Mode.
*/
#define CSR_DCSR_EBREAKM_OFFSET 15
#define CSR_DCSR_EBREAKM_LENGTH 1
#define CSR_DCSR_EBREAKM (0x1 << CSR_DCSR_EBREAKM_OFFSET)
/*
* When 1, {\tt ebreak} instructions in Hypervisor Mode enter Halt Mode.
* When 1, {\tt ebreak} instructions in Hypervisor Mode enter Debug Mode.
*/
#define CSR_DCSR_EBREAKH_OFFSET 14
#define CSR_DCSR_EBREAKH_LENGTH 1
#define CSR_DCSR_EBREAKH (0x1 << CSR_DCSR_EBREAKH_OFFSET)
/*
* When 1, {\tt ebreak} instructions in Supervisor Mode enter Halt Mode.
* When 1, {\tt ebreak} instructions in Supervisor Mode enter Debug Mode.
*/
#define CSR_DCSR_EBREAKS_OFFSET 13
#define CSR_DCSR_EBREAKS_LENGTH 1
#define CSR_DCSR_EBREAKS (0x1 << CSR_DCSR_EBREAKS_OFFSET)
/*
* When 1, {\tt ebreak} instructions in User/Application Mode enter
* Halt Mode.
* Debug Mode.
*/
#define CSR_DCSR_EBREAKU_OFFSET 12
#define CSR_DCSR_EBREAKU_LENGTH 1
@ -173,7 +186,7 @@
/*
* 0: Increment counters as usual.
*
* 1: Don't increment any counters while in Halt Mode. This includes
* 1: Don't increment any counters while in Debug Mode. This includes
* the {\tt cycle} and {\tt instret} CSRs. This is preferred for most
* debugging scenarios.
*
@ -187,7 +200,7 @@
/*
* 0: Increment timers as usual.
*
* 1: Don't increment any hart-local timers while in Halt Mode.
* 1: Don't increment any hart-local timers while in Debug Mode.
*
* An implementation may choose not to support writing to this bit.
* The debugger must read back the value it writes to check whether
@ -197,29 +210,27 @@
#define CSR_DCSR_STOPTIME_LENGTH 1
#define CSR_DCSR_STOPTIME (0x1 << CSR_DCSR_STOPTIME_OFFSET)
/*
* Explains why Halt Mode was entered.
* Explains why Debug Mode was entered.
*
* When there are multiple reasons to enter Halt Mode in a single
* When there are multiple reasons to enter Debug Mode in a single
* cycle, the cause with the highest priority is the one written.
*
* 1: A software breakpoint was hit. (priority 3)
* 1: An {\tt ebreak} instruction was executed. (priority 3)
*
* 2: The Trigger Module caused a halt. (priority 4)
*
* 3: The debug interrupt was asserted by the Debug Module. (priority 2)
* 3: \Fhaltreq was set. (priority 2)
*
* 4: The hart single stepped because \Fstep was set. (priority 1)
*
* 5: \Fhaltreq was set. (priority 0)
*
* Other values are reserved for future use.
*/
#define CSR_DCSR_CAUSE_OFFSET 6
#define CSR_DCSR_CAUSE_LENGTH 3
#define CSR_DCSR_CAUSE (0x7 << CSR_DCSR_CAUSE_OFFSET)
/*
* When set and not in Halt Mode, the hart will only execute a single
* instruction, and then enter Halt Mode. Interrupts are disabled
* When set and not in Debug Mode, the hart will only execute a single
* instruction, and then enter Debug Mode. Interrupts are disabled
* when this bit is set.
*/
#define CSR_DCSR_STEP_OFFSET 2
@ -227,9 +238,9 @@
#define CSR_DCSR_STEP (0x1 << CSR_DCSR_STEP_OFFSET)
/*
* Contains the privilege level the hart was operating in when Debug
* Mode was entered. The encoding is describe in Table
* Mode was entered. The encoding is described in Table
* \ref{tab:privlevel}. A debugger can change this value to change
* the hart's privilege level when exiting Halt Mode.
* the hart's privilege level when exiting Debug Mode.
*
* Not all privilege levels are supported on all harts. If the
* encoding written is not supported or the debugger is not allowed to
@ -247,9 +258,9 @@
#define CSR_PRIV virtual
/*
* Contains the privilege level the hart was operating in when Debug
* Mode was entered. The encoding is describe in Table
* Mode was entered. The encoding is described in Table
* \ref{tab:privlevel}. A user can write this value to change the
* hart's privilege level when exiting Halt Mode.
* hart's privilege level when exiting Debug Mode.
*/
#define CSR_PRIV_PRV_OFFSET 0
#define CSR_PRIV_PRV_LENGTH 2
@ -283,10 +294,10 @@
* 0: Both Debug and M Mode can write the {\tt tdata} registers at the
* selected \Rtselect.
*
* 1: Only Halt Mode can write the {\tt tdata} registers at the
* 1: Only Debug Mode can write the {\tt tdata} registers at the
* selected \Rtselect. Writes from other modes are ignored.
*
* This bit is only writable from Halt Mode.
* This bit is only writable from Debug Mode.
*/
#define CSR_TDATA1_HMODE_OFFSET XLEN-5
#define CSR_TDATA1_HMODE_LENGTH 1
@ -366,7 +377,7 @@
* 0: Raise a breakpoint exception. (Used when software wants to use
* the trigger module without an external debugger attached.)
*
* 1: Enter Halt Mode. (Only supported when \Fhmode is 1.)
* 1: Enter Debug Mode. (Only supported when \Fhmode is 1.)
*
* 2: Start tracing.
*
@ -504,10 +515,10 @@
/*
* Determines what happens when this trigger matches.
*
* 0: Raise a debug exception. (Used when software wants to use the
* 0: Raise a breakpoint exception. (Used when software wants to use the
* trigger module without an external debugger attached.)
*
* 1: Enter Halt Mode. (Only supported when \Fhmode is 1.)
* 1: Enter Debug Mode. (Only supported when \Fhmode is 1.)
*
* 2: Start tracing.
*
@ -524,6 +535,18 @@
#define CSR_ICOUNT_ACTION (0x3fL << CSR_ICOUNT_ACTION_OFFSET)
#define DMI_DMSTATUS 0x11
/*
* This field is 1 when all currently selected harts have acknowledged the previous \Fresumereq.
*/
#define DMI_DMSTATUS_ALLRESUMEACK_OFFSET 17
#define DMI_DMSTATUS_ALLRESUMEACK_LENGTH 1
#define DMI_DMSTATUS_ALLRESUMEACK (0x1 << DMI_DMSTATUS_ALLRESUMEACK_OFFSET)
/*
* This field is 1 when any currently selected hart has acknowledged the previous \Fresumereq.
*/
#define DMI_DMSTATUS_ANYRESUMEACK_OFFSET 16
#define DMI_DMSTATUS_ANYRESUMEACK_LENGTH 1
#define DMI_DMSTATUS_ANYRESUMEACK (0x1 << DMI_DMSTATUS_ANYRESUMEACK_OFFSET)
/*
* This field is 1 when all currently selected harts do not exist in this system.
*/
#define DMI_DMSTATUS_ALLNONEXISTENT_OFFSET 15
@ -617,7 +640,7 @@
#define DMI_DMSTATUS_VERSIONLO (0x3 << DMI_DMSTATUS_VERSIONLO_OFFSET)
#define DMI_DMCONTROL 0x10
/*
* Halt request signal for all currently selected harts. When 1, the
* Halt request signal for all currently selected harts. When set to 1, the
* hart will halt if it is not currently halted.
* Setting both \Fhaltreq and \Fresumereq leads to undefined behavior.
*
@ -627,7 +650,7 @@
#define DMI_DMCONTROL_HALTREQ_LENGTH 1
#define DMI_DMCONTROL_HALTREQ (0x1 << DMI_DMCONTROL_HALTREQ_OFFSET)
/*
* Resume request signal for all currently selected harts. When 1,
* Resume request signal for all currently selected harts. When set to 1,
* the hart will resume if it is currently halted.
* Setting both \Fhaltreq and \Fresumereq leads to undefined behavior.
*
@ -942,31 +965,12 @@
#define DMI_DATA0_DATA_OFFSET 0
#define DMI_DATA0_DATA_LENGTH 32
#define DMI_DATA0_DATA (0xffffffff << DMI_DATA0_DATA_OFFSET)
#define DMI_DATA1 0x05
#define DMI_DATA2 0x06
#define DMI_DATA3 0x07
#define DMI_DATA4 0x08
#define DMI_DATA5 0x09
#define DMI_DATA6 0x0a
#define DMI_DATA7 0x0b
#define DMI_DATA8 0x0c
#define DMI_DATA9 0x0d
#define DMI_DATA10 0x0e
#define DMI_DATA11 0x0f
#define DMI_PROGBUF0 0x20
#define DMI_PROGBUF0_DATA_OFFSET 0
#define DMI_PROGBUF0_DATA_LENGTH 32
#define DMI_PROGBUF0_DATA (0xffffffff << DMI_PROGBUF0_DATA_OFFSET)
#define DMI_PROGBUF1 0x21
#define DMI_PROGBUF2 0x22
#define DMI_PROGBUF3 0x23
#define DMI_PROGBUF4 0x24
#define DMI_PROGBUF5 0x25
#define DMI_PROGBUF6 0x26
#define DMI_PROGBUF7 0x27
#define DMI_PROGBUF8 0x28
#define DMI_PROGBUF9 0x29
#define DMI_PROGBUF10 0x2a
#define DMI_PROGBUF15 0x2f
#define DMI_AUTHDATA 0x30
#define DMI_AUTHDATA_DATA_OFFSET 0
#define DMI_AUTHDATA_DATA_LENGTH 32
@ -1233,93 +1237,6 @@
#define DMI_SBDATA3_DATA_OFFSET 0
#define DMI_SBDATA3_DATA_LENGTH 32
#define DMI_SBDATA3_DATA (0xffffffff << DMI_SBDATA3_DATA_OFFSET)
#define SERINFO 0x280
/*
* Like \Fserialzero.
*/
#define SERINFO_SERIAL7_OFFSET 7
#define SERINFO_SERIAL7_LENGTH 1
#define SERINFO_SERIAL7 (0x1 << SERINFO_SERIAL7_OFFSET)
/*
* Like \Fserialzero.
*/
#define SERINFO_SERIAL6_OFFSET 6
#define SERINFO_SERIAL6_LENGTH 1
#define SERINFO_SERIAL6 (0x1 << SERINFO_SERIAL6_OFFSET)
/*
* Like \Fserialzero.
*/
#define SERINFO_SERIAL5_OFFSET 5
#define SERINFO_SERIAL5_LENGTH 1
#define SERINFO_SERIAL5 (0x1 << SERINFO_SERIAL5_OFFSET)
/*
* Like \Fserialzero.
*/
#define SERINFO_SERIAL4_OFFSET 4
#define SERINFO_SERIAL4_LENGTH 1
#define SERINFO_SERIAL4 (0x1 << SERINFO_SERIAL4_OFFSET)
/*
* Like \Fserialzero.
*/
#define SERINFO_SERIAL3_OFFSET 3
#define SERINFO_SERIAL3_LENGTH 1
#define SERINFO_SERIAL3 (0x1 << SERINFO_SERIAL3_OFFSET)
/*
* Like \Fserialzero.
*/
#define SERINFO_SERIAL2_OFFSET 2
#define SERINFO_SERIAL2_LENGTH 1
#define SERINFO_SERIAL2 (0x1 << SERINFO_SERIAL2_OFFSET)
/*
* Like \Fserialzero.
*/
#define SERINFO_SERIAL1_OFFSET 1
#define SERINFO_SERIAL1_LENGTH 1
#define SERINFO_SERIAL1 (0x1 << SERINFO_SERIAL1_OFFSET)
/*
* 1 means serial interface 0 is supported.
*/
#define SERINFO_SERIAL0_OFFSET 0
#define SERINFO_SERIAL0_LENGTH 1
#define SERINFO_SERIAL0 (0x1 << SERINFO_SERIAL0_OFFSET)
#define SERSEND0 0x200
#define SERRECV0 0x204
#define SERSTAT0 0x208
/*
* Send ready. 1 when the core-to-debugger queue is not full. 0
* otherwise.
*/
#define SERSTAT0_SENDR_OFFSET 1
#define SERSTAT0_SENDR_LENGTH 1
#define SERSTAT0_SENDR (0x1 << SERSTAT0_SENDR_OFFSET)
/*
* Receive ready. 1 when the debugger-to-core queue is not empty. 0
* otherwise.
*/
#define SERSTAT0_RECVR_OFFSET 0
#define SERSTAT0_RECVR_LENGTH 1
#define SERSTAT0_RECVR (0x1 << SERSTAT0_RECVR_OFFSET)
#define SERSEND1 0x210
#define SERRECV1 0x214
#define SERSTAT1 0x218
#define SERSEND2 0x220
#define SERRECV2 0x224
#define SERSTAT2 0x228
#define SERSEND3 0x230
#define SERRECV3 0x234
#define SERSTAT3 0x238
#define SERSEND4 0x240
#define SERRECV4 0x244
#define SERSTAT4 0x248
#define SERSEND5 0x250
#define SERRECV5 0x254
#define SERSTAT5 0x258
#define SERSEND6 0x260
#define SERRECV6 0x264
#define SERSTAT6 0x268
#define SERSEND7 0x274
#define SERRECV7 0x278
#define SERSTAT7 0x27c
#define TRACE 0x728
/*
* 1 if the trace buffer has wrapped since the last time \Fdiscard was
@ -1454,14 +1371,6 @@
#define AC_ACCESS_REGISTER_SIZE (0x7 << AC_ACCESS_REGISTER_SIZE_OFFSET)
/*
* When 1, execute the program in the Program Buffer exactly once
* before performing the transfer.
* \textbf{WARNING: preexec is considered for removal.}
*/
#define AC_ACCESS_REGISTER_PREEXEC_OFFSET 19
#define AC_ACCESS_REGISTER_PREEXEC_LENGTH 1
#define AC_ACCESS_REGISTER_PREEXEC (0x1 << AC_ACCESS_REGISTER_PREEXEC_OFFSET)
/*
* When 1, execute the program in the Program Buffer exactly once
* after performing the transfer, if any.
*/
#define AC_ACCESS_REGISTER_POSTEXEC_OFFSET 18

View File

@ -3,6 +3,8 @@
enum gdb_regno {
GDB_REGNO_XPR0 = 0,
GDB_REGNO_X0 = GDB_REGNO_XPR0 + 0,
GDB_REGNO_ZERO = GDB_REGNO_XPR0 + 0,
GDB_REGNO_S0 = GDB_REGNO_XPR0 + 8,
GDB_REGNO_S1 = GDB_REGNO_XPR0 + 9,
GDB_REGNO_XPR31 = GDB_REGNO_XPR0 + 31,
@ -16,6 +18,7 @@ enum gdb_regno {
GDB_REGNO_MISA = CSR_MISA + GDB_REGNO_CSR0,
GDB_REGNO_DPC = CSR_DPC + GDB_REGNO_CSR0,
GDB_REGNO_DCSR = CSR_DCSR + GDB_REGNO_CSR0,
GDB_REGNO_DSCRATCH = CSR_DSCRATCH + GDB_REGNO_CSR0,
GDB_REGNO_MSTATUS = CSR_MSTATUS + GDB_REGNO_CSR0,
GDB_REGNO_CSR4095 = GDB_REGNO_CSR0 + 4095,
GDB_REGNO_PRIV = 4161,

View File

@ -125,6 +125,16 @@ static uint32_t csrr(unsigned int rd, unsigned int csr) {
return (csr << 20) | (rd << 7) | MATCH_CSRRS;
}
static uint32_t csrrs(unsigned int rd, unsigned int rs, unsigned int csr) __attribute__ ((unused));
static uint32_t csrrs(unsigned int rd, unsigned int rs, unsigned int csr) {
return (csr << 20) | (rs << 15) | (rd << 7) | MATCH_CSRRS;
}
static uint32_t csrrw(unsigned int rd, unsigned int rs, unsigned int csr) __attribute__ ((unused));
static uint32_t csrrw(unsigned int rd, unsigned int rs, unsigned int csr) {
return (csr << 20) | (rs << 15) | (rd << 7) | MATCH_CSRRW;
}
static uint32_t fsw(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused));
static uint32_t fsw(unsigned int src, unsigned int base, uint16_t offset)
{
@ -206,7 +216,6 @@ static uint32_t fence_i(void)
return MATCH_FENCE_I;
}
/*
static uint32_t lui(unsigned int dest, uint32_t imm) __attribute__ ((unused));
static uint32_t lui(unsigned int dest, uint32_t imm)
{
@ -215,6 +224,7 @@ static uint32_t lui(unsigned int dest, uint32_t imm)
MATCH_LUI;
}
/*
static uint32_t csrci(unsigned int csr, uint16_t imm) __attribute__ ((unused));
static uint32_t csrci(unsigned int csr, uint16_t imm) {
return (csr << 20) |
@ -277,3 +287,9 @@ static uint32_t fence(void)
{
return MATCH_FENCE;
}
static uint32_t auipc(unsigned int dest) __attribute__((unused));
static uint32_t auipc(unsigned int dest)
{
return MATCH_AUIPC | (dest << 7);
}

402
src/target/riscv/program.c Normal file
View File

@ -0,0 +1,402 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "target/target.h"
#include "riscv.h"
#include "program.h"
#include "helper/log.h"
#include "asm.h"
#include "encoding.h"
riscv_addr_t riscv_program_gah(struct riscv_program *p, riscv_addr_t addr);
riscv_addr_t riscv_program_gal(struct riscv_program *p, riscv_addr_t addr);
int riscv_program_lah(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr);
int riscv_program_lal(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr);
int riscv_program_insert(struct riscv_program *p, riscv_insn_t i);
/* Program interface. */
int riscv_program_init(struct riscv_program *p, struct target *target)
{
memset(p, 0, sizeof(*p));
p->target = target;
p->instruction_count = 0;
p->data_count = 0;
p->writes_memory = 0;
p->target_xlen = riscv_xlen(target);
for (size_t i = 0; i < RISCV_REGISTER_COUNT; ++i) {
p->writes_xreg[i] = 0;
p->in_use[i] = 0;
}
for(size_t i = 0; i < RISCV_MAX_DEBUG_BUFFER_SIZE; ++i)
p->debug_buffer[i] = -1;
if (riscv_debug_buffer_enter(target, p) != ERROR_OK) {
LOG_ERROR("unable to write progam buffer enter code");
return ERROR_FAIL;
}
return ERROR_OK;
}
int riscv_program_exec(struct riscv_program *p, struct target *t)
{
if (riscv_debug_buffer_leave(t, p) != ERROR_OK) {
LOG_ERROR("unable to write program buffer exit code");
return ERROR_FAIL;
}
riscv_reg_t saved_registers[GDB_REGNO_XPR31 + 1];
for (size_t i = GDB_REGNO_XPR0 + 1; i <= GDB_REGNO_XPR31; ++i) {
if (p->writes_xreg[i]) {
LOG_DEBUG("Saving register %d as used by program", i);
saved_registers[i] = riscv_get_register(t, i);
}
}
if (p->writes_memory && (riscv_program_fence(p) != ERROR_OK)) {
LOG_ERROR("Unable to write fence");
return ERROR_FAIL;
}
if (riscv_program_ebreak(p) != ERROR_OK) {
LOG_ERROR("Unable to write ebreak");
return ERROR_FAIL;
}
for (size_t i = 0; i < riscv_debug_buffer_size(p->target); ++i)
riscv_write_debug_buffer(t, i, p->debug_buffer[i]);
riscv_execute_debug_buffer(t);
for (size_t i = 0; i < riscv_debug_buffer_size(p->target); ++i)
p->debug_buffer[i] = riscv_read_debug_buffer(t, i);
for (size_t i = GDB_REGNO_XPR0; i <= GDB_REGNO_XPR31; ++i)
if (p->writes_xreg[i])
riscv_set_register(t, i, saved_registers[i]);
return ERROR_OK;
}
riscv_addr_t riscv_program_alloc_data(struct riscv_program *p, size_t bytes)
{
LOG_DEBUG("allocating %d bytes of data", bytes);
riscv_addr_t addr =
riscv_debug_buffer_addr(p->target)
+ riscv_debug_buffer_size(p->target) * sizeof(p->debug_buffer[0])
- p->data_count * sizeof(p->debug_buffer[0])
- bytes;
while (addr % bytes != 0) addr--;
riscv_addr_t ptop =
riscv_debug_buffer_addr(p->target)
+ p->instruction_count * sizeof(p->debug_buffer[0]);
if (addr <= ptop) {
LOG_DEBUG("unable to allocate %d bytes", bytes);
return RISCV_PROGRAM_ALLOC_FAIL;
}
LOG_DEBUG("allocated %d bytes at 0x%08lx", bytes, addr);
return addr;
}
riscv_addr_t riscv_program_alloc_x(struct riscv_program *p)
{
return riscv_program_alloc_data(p, p->target_xlen / 8);
}
riscv_addr_t riscv_program_alloc_d(struct riscv_program *p)
{
return riscv_program_alloc_data(p, 8);
}
riscv_addr_t riscv_program_alloc_w(struct riscv_program *p)
{
return riscv_program_alloc_data(p, 4);
}
riscv_addr_t riscv_program_alloc_h(struct riscv_program *p)
{
return riscv_program_alloc_data(p, 2);
}
riscv_addr_t riscv_program_alloc_b(struct riscv_program *p)
{
return riscv_program_alloc_data(p, 1);
}
riscv_insn_t riscv_program_read_ram(struct riscv_program *p, riscv_addr_t addr)
{
if (addr < riscv_debug_buffer_addr(p->target))
return -1;
if (addr > riscv_debug_buffer_addr(p->target) + (riscv_debug_buffer_size(p->target) * sizeof(p->debug_buffer[0])))
return -1;
int off = (addr - riscv_debug_buffer_addr(p->target)) / sizeof(p->debug_buffer[0]);
return p->debug_buffer[off];
}
void riscv_program_write_ram(struct riscv_program *p, riscv_addr_t addr, uint64_t d)
{
if (addr < riscv_debug_buffer_addr(p->target))
return -1;
if (addr > riscv_debug_buffer_addr(p->target) + (riscv_debug_buffer_size(p->target) * sizeof(p->debug_buffer[0])))
return -1;
int off = (addr - riscv_debug_buffer_addr(p->target)) / sizeof(p->debug_buffer[0]);
p->debug_buffer[off] = d;
}
int riscv_program_lx(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
{
switch (p->target_xlen) {
case 64: return riscv_program_ld(p, d, addr);
case 32: return riscv_program_lw(p, d, addr);
}
LOG_ERROR("unknown xlen %d", p->target_xlen);
abort();
return -1;
}
int riscv_program_ld(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
{
enum gdb_regno t = riscv_program_gah(p, addr) == 0 ? GDB_REGNO_X0 : d;
if (riscv_program_lah(p, d, addr) != ERROR_OK)
return ERROR_FAIL;
if (riscv_program_insert(p, ld(d, t, riscv_program_gal(p, addr))) != ERROR_OK)
return ERROR_FAIL;
return ERROR_OK;
}
int riscv_program_lw(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
{
enum gdb_regno t = riscv_program_gah(p, addr) == 0 ? GDB_REGNO_X0 : d;
if (riscv_program_lah(p, d, addr) != ERROR_OK)
return ERROR_FAIL;
if (riscv_program_insert(p, lw(d, t, riscv_program_gal(p, addr))) != ERROR_OK)
return ERROR_FAIL;
return ERROR_OK;
}
int riscv_program_lh(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
{
enum gdb_regno t = riscv_program_gah(p, addr) == 0 ? GDB_REGNO_X0 : d;
if (riscv_program_lah(p, d, addr) != ERROR_OK)
return ERROR_FAIL;
if (riscv_program_insert(p, lh(d, t, riscv_program_gal(p, addr))) != ERROR_OK)
return ERROR_FAIL;
return ERROR_OK;
}
int riscv_program_lb(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
{
enum gdb_regno t = riscv_program_gah(p, addr) == 0 ? GDB_REGNO_X0 : d;
if (riscv_program_lah(p, t, addr) != ERROR_OK)
return ERROR_FAIL;
if (riscv_program_insert(p, lb(d, t, riscv_program_gal(p, addr))) != ERROR_OK)
return ERROR_FAIL;
return ERROR_OK;
}
int riscv_program_sx(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
{
switch (p->target_xlen) {
case 64: return riscv_program_sd(p, d, addr);
case 32: return riscv_program_sw(p, d, addr);
}
LOG_ERROR("unknown xlen %d", p->target_xlen);
abort();
return -1;
}
int riscv_program_sd(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
{
enum gdb_regno t = riscv_program_gah(p, addr) == 0
? GDB_REGNO_X0
: riscv_program_gettemp(p);
if (riscv_program_lah(p, t, addr) != ERROR_OK)
return ERROR_FAIL;
if (riscv_program_insert(p, sd(d, t, riscv_program_gal(p, addr))) != ERROR_OK)
return ERROR_FAIL;
riscv_program_puttemp(p, t);
p->writes_memory = true;
return ERROR_OK;
}
int riscv_program_sw(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
{
enum gdb_regno t = riscv_program_gah(p, addr) == 0
? GDB_REGNO_X0
: riscv_program_gettemp(p);
if (riscv_program_lah(p, t, addr) != ERROR_OK)
return ERROR_FAIL;
if (riscv_program_insert(p, sw(d, t, riscv_program_gal(p, addr))) != ERROR_OK)
return ERROR_FAIL;
riscv_program_puttemp(p, t);
p->writes_memory = true;
return ERROR_OK;
}
int riscv_program_sh(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
{
enum gdb_regno t = riscv_program_gah(p, addr) == 0
? GDB_REGNO_X0
: riscv_program_gettemp(p);
if (riscv_program_lah(p, t, addr) != ERROR_OK)
return ERROR_FAIL;
if (riscv_program_insert(p, sh(d, t, riscv_program_gal(p, addr))) != ERROR_OK)
return ERROR_FAIL;
riscv_program_puttemp(p, t);
p->writes_memory = true;
return ERROR_OK;
}
int riscv_program_sb(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
{
enum gdb_regno t = riscv_program_gah(p, addr) == 0
? GDB_REGNO_X0
: riscv_program_gettemp(p);
if (riscv_program_lah(p, t, addr) != ERROR_OK)
return ERROR_FAIL;
if (riscv_program_insert(p, sb(d, t, riscv_program_gal(p, addr))) != ERROR_OK)
return ERROR_FAIL;
riscv_program_puttemp(p, t);
p->writes_memory = true;
return ERROR_OK;
}
int riscv_program_csrr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno csr)
{
assert(csr >= GDB_REGNO_CSR0);
return riscv_program_insert(p, csrrs(d, GDB_REGNO_X0, csr - GDB_REGNO_CSR0));
}
int riscv_program_csrw(struct riscv_program *p, enum gdb_regno s, enum gdb_regno csr)
{
assert(csr >= GDB_REGNO_CSR0);
return riscv_program_insert(p, csrrw(GDB_REGNO_X0, s, csr - GDB_REGNO_CSR0));
}
int riscv_program_csrrw(struct riscv_program *p, enum gdb_regno d, enum gdb_regno s, enum gdb_regno csr)
{
assert(csr >= GDB_REGNO_CSR0);
return riscv_program_insert(p, csrrw(d, s, csr - GDB_REGNO_CSR0));
}
int riscv_program_fence_i(struct riscv_program *p)
{
return riscv_program_insert(p, fence_i());
}
int riscv_program_fence(struct riscv_program *p)
{
return riscv_program_insert(p, fence());
}
int riscv_program_ebreak(struct riscv_program *p)
{
return riscv_program_insert(p, ebreak());
}
int riscv_program_lui(struct riscv_program *p, enum gdb_regno d, int32_t u)
{
return riscv_program_insert(p, lui(d, u));
}
int riscv_program_addi(struct riscv_program *p, enum gdb_regno d, enum gdb_regno s, int16_t u)
{
return riscv_program_insert(p, addi(d, s, u));
}
int riscv_program_li(struct riscv_program *p, enum gdb_regno d, riscv_reg_t c)
{
if (riscv_program_lui(p, d, c >> 12) != ERROR_OK)
return ERROR_FAIL;
if (riscv_program_addi(p, d, d, c & 0xFFF) != ERROR_OK)
return ERROR_FAIL;
return ERROR_OK;
}
int riscv_program_dont_restore_register(struct riscv_program *p, enum gdb_regno r)
{
assert(r < RISCV_REGISTER_COUNT);
p->writes_xreg[r] = 0;
}
int riscv_program_do_restore_register(struct riscv_program *p, enum gdb_regno r)
{
assert(r < RISCV_REGISTER_COUNT);
p->writes_xreg[r] = 1;
}
void riscv_program_reserve_register(struct riscv_program *p, enum gdb_regno r)
{
assert(r < RISCV_REGISTER_COUNT);
assert(p->in_use[r] == 0);
p->in_use[r] = 1;
}
enum gdb_regno riscv_program_gettemp(struct riscv_program *p)
{
for (size_t i = GDB_REGNO_S0; i <= GDB_REGNO_XPR31; ++i) {
if (p->in_use[i]) continue;
riscv_program_do_restore_register(p, i);
p->in_use[i] = 1;
return i;
}
LOG_ERROR("You've run out of temporary registers. This is impossible.");
abort();
}
void riscv_program_puttemp(struct riscv_program *p, enum gdb_regno r)
{
assert(r < RISCV_REGISTER_COUNT);
p->in_use[r] = 0;
}
/* Helper functions. */
riscv_addr_t riscv_program_gah(struct riscv_program *p, riscv_addr_t addr)
{
return addr >> 12;
}
riscv_addr_t riscv_program_gal(struct riscv_program *p, riscv_addr_t addr)
{
return ((addr > 0) ? 1 : 0) * (abs(addr) & 0x7FF);
}
int riscv_program_lah(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
{
riscv_addr_t ah = riscv_program_gah(p, addr);
if (ah == 0)
return ERROR_OK;
return riscv_program_lui(p, d, ah);
}
int riscv_program_lal(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr)
{
riscv_addr_t al = riscv_program_gal(p, addr);
if (al == 0)
return ERROR_OK;
return riscv_program_addi(p, d, d, al);
}
int riscv_program_insert(struct riscv_program *p, riscv_insn_t i)
{
if (p->instruction_count + p->data_count + 1 > riscv_debug_buffer_size(p->target))
return ERROR_FAIL;
LOG_DEBUG("PROGBUF[%d] = DASM(0x%08x) [0x%08x]", p->instruction_count, i, i);
p->debug_buffer[p->instruction_count] = i;
p->instruction_count++;
}

123
src/target/riscv/program.h Normal file
View File

@ -0,0 +1,123 @@
#ifndef TARGET__RISCV__PROGRAM_H
#define TARGET__RISCV__PROGRAM_H
#include "riscv.h"
#define RISCV_MAX_DEBUG_BUFFER_SIZE 16
#define RISCV_REGISTER_COUNT 32
#define RISCV_DSCRATCH_COUNT 2
/* The various RISC-V debug specifications all revolve around setting up
* program buffers and executing them on the target. This structure contains a
* single program, which can then be executed on targets. */
struct riscv_program {
struct target *target;
uint32_t debug_buffer[RISCV_MAX_DEBUG_BUFFER_SIZE];
/* The debug buffer is allocated in two directions: instructions go at
* the start, while data goes at the end. When they meet in the middle
* this blows up. */
size_t instruction_count;
size_t data_count;
/* Side effects of executing this program. These must be accounted for
* in order to maintain correct executing of the target system. */
bool writes_xreg[RISCV_REGISTER_COUNT];
bool writes_memory;
/* When a register is used it will be set in this array. */
bool in_use[RISCV_REGISTER_COUNT];
/* Remembers the registers that have been saved into dscratch
* registers. These are restored */
enum gdb_regno dscratch_saved[RISCV_DSCRATCH_COUNT];
/* XLEN on the target. */
int target_xlen;
};
/* Initializes a program with the header. */
int riscv_program_init(struct riscv_program *p, struct target *t);
/* Executes a program, returning 0 if the program successfully executed. Note
* that this may cause registers to be saved or restored, which could result to
* calls to things like riscv_save_register which itself could require a
* program to execute. That's OK, just make sure this eventually terminates.
* */
int riscv_program_exec(struct riscv_program *p, struct target *t);
/* Clears a program, removing all the state associated with it. */
int riscv_program_clear(struct riscv_program *p, struct target *t);
/* A lower level interface, you shouldn't use this unless you have a reason. */
int riscv_program_insert(struct riscv_program *p, riscv_insn_t i);
/* There is hardware support for saving at least one register. This register
* doesn't need to be saved/restored the usual way, which is useful during
* early initialization when we can't save/restore arbitrary registerrs to host
* memory. */
int riscv_program_save_to_dscratch(struct riscv_program *p, enum gdb_regno to_save);
/* Allocates data of various sizes. Either returns the absolute physical
* address or RISCV_PROGRAM_ALLOC_FAIL on failure. */
riscv_addr_t riscv_program_alloc_data(struct riscv_program *p, size_t bytes);
riscv_addr_t riscv_program_alloc_x(struct riscv_program *p);
riscv_addr_t riscv_program_alloc_d(struct riscv_program *p);
riscv_addr_t riscv_program_alloc_w(struct riscv_program *p);
riscv_addr_t riscv_program_alloc_h(struct riscv_program *p);
riscv_addr_t riscv_program_alloc_b(struct riscv_program *p);
#define RISCV_PROGRAM_ALLOC_FAIL ((riscv_addr_t)(-1))
/* Reads a word of memory from this program's internal view of the debug RAM.
* This is what you want to use to get data back from the program after it
* executes. */
riscv_insn_t riscv_program_read_ram(struct riscv_program *p, riscv_addr_t addr);
void riscv_program_write_ram(struct riscv_program *p, riscv_addr_t a, uint64_t d);
/* Helpers to assembly various instructions. Return 0 on success. These might
* assembly into a multi-instruction sequence that overwrites some other
* register, but those will be properly saved and restored. */
int riscv_program_lx(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr);
int riscv_program_ld(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr);
int riscv_program_lw(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr);
int riscv_program_lh(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr);
int riscv_program_lb(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr);
int riscv_program_sx(struct riscv_program *p, enum gdb_regno s, riscv_addr_t addr);
int riscv_program_sd(struct riscv_program *p, enum gdb_regno s, riscv_addr_t addr);
int riscv_program_sw(struct riscv_program *p, enum gdb_regno s, riscv_addr_t addr);
int riscv_program_sh(struct riscv_program *p, enum gdb_regno s, riscv_addr_t addr);
int riscv_program_sb(struct riscv_program *p, enum gdb_regno s, riscv_addr_t addr);
int riscv_program_csrr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno csr);
int riscv_program_csrw(struct riscv_program *p, enum gdb_regno s, enum gdb_regno csr);
int riscv_program_csrrw(struct riscv_program *p, enum gdb_regno d, 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_ebreak(struct riscv_program *p);
int riscv_program_lui(struct riscv_program *p, enum gdb_regno d, int32_t u);
int riscv_program_addi(struct riscv_program *p, enum gdb_regno d, enum gdb_regno s, int16_t i);
/* Assembler macros. */
int riscv_program_li(struct riscv_program *p, enum gdb_regno d, riscv_reg_t c);
int riscv_program_la(struct riscv_program *p, enum gdb_regno d, riscv_addr_t a);
/* Register allocation. The user is expected to have obtained temporary
* registers using these fuctions. Additionally, there is an interface for
* reserving registers -- it's expected that this has been called as the first
* thing in the program's execution to reserve registers that can't be touched
* by the program's execution. */
void riscv_program_reserve_register(struct riscv_program *p, enum gdb_regno r);
enum gdb_regno riscv_program_gettemp(struct riscv_program *p);
void riscv_program_puttemp(struct riscv_program *p, enum gdb_regno r);
/* Executing a program usually causes the registers that get overwritten to be
* saved and restored. Calling this prevents the given register from actually
* being restored as a result of all activity in this program. */
int riscv_program_dont_restore_register(struct riscv_program *p, enum gdb_regno r);
int riscv_program_do_restore_register(struct riscv_program *p, enum gdb_regno r);
#endif

View File

@ -22,9 +22,16 @@
#include "riscv.h"
#include "debug_defines.h"
#include "rtos/rtos.h"
#include "program.h"
#include "asm.h"
#define DMI_DATA1 (DMI_DATA0 + 1)
static void riscv013_on_step_or_resume(struct target *target, bool step);
static void riscv013_step_or_resume_current_hart(struct target *target, bool step);
static size_t riscv013_progbuf_size(struct target *target);
static size_t riscv013_data_size(struct target *target);
static size_t riscv013_data_addr(struct target *target);
/* Implementations of the functions in riscv_info_t. */
static riscv_reg_t riscv013_get_register(struct target *target, int hartid, int regid);
@ -38,6 +45,11 @@ static void riscv013_on_step(struct target *target);
static void riscv013_on_resume(struct target *target);
static bool riscv013_is_halted(struct target *target);
static enum riscv_halt_reason riscv013_halt_reason(struct target *target);
static void riscv013_debug_buffer_enter(struct target *target, struct riscv_program *p);
static void riscv013_debug_buffer_leave(struct target *target, struct riscv_program *p);
static void riscv013_write_debug_buffer(struct target *target, int i, riscv_insn_t d);
static riscv_insn_t riscv013_read_debug_buffer(struct target *target, int i);
static void riscv013_execute_debug_buffer(struct target *target);
/**
* Since almost everything can be accomplish by scanning the dbus register, all
@ -470,98 +482,6 @@ static int execute_abstract_command(struct target *target, uint32_t command)
return ERROR_OK;
}
/*** program "class" ***/
/* This class allows a debug program to be built up piecemeal, and then be
* executed. If necessary, the program is split up to fit in the program
* buffer. */
typedef struct {
uint8_t code[12 * 4];
unsigned length;
bool write;
unsigned regno;
uint64_t write_value;
} program_t;
static void program_add32(program_t *program, uint32_t instruction);
static program_t *program_new(void)
{
program_t *program = malloc(sizeof(program_t));
if (program) {
program->length = 0;
// Default to read zero.
program->write = false;
program->regno = 0x1000;
}
program_add32(program, fence_i());
return program;
}
static void program_delete(program_t *program)
{
free(program);
}
static void program_add32(program_t *program, uint32_t instruction)
{
assert(program->length + 4 < sizeof(program->code));
program->code[program->length++] = instruction & 0xff;
program->code[program->length++] = (instruction >> 8) & 0xff;
program->code[program->length++] = (instruction >> 16) & 0xff;
program->code[program->length++] = (instruction >> 24) & 0xff;
}
static void program_set_read(program_t *program, unsigned reg_num)
{
program->write = false;
program->regno = reg_number_to_no(reg_num);
}
static void program_set_write(program_t *program, unsigned reg_num, uint64_t value)
{
program->write = true;
program->regno = reg_number_to_no(reg_num);
program->write_value = value;
}
/*** end of program class ***/
static void write_program(struct target *target, const program_t *program)
{
riscv013_info_t *info = get_info(target);
assert(program->length <= info->progsize * 4);
for (unsigned i = 0; i < program->length; i += 4) {
uint32_t value =
program->code[i] |
((uint32_t) program->code[i+1] << 8) |
((uint32_t) program->code[i+2] << 16) |
((uint32_t) program->code[i+3] << 24);
dmi_write(target, DMI_PROGBUF0 + i / 4, value);
}
}
static int execute_program(struct target *target, const program_t *program)
{
write_program(target, program);
uint32_t command = 0;
if (program->write) {
if (get_field(command, AC_ACCESS_REGISTER_SIZE) > 2) {
dmi_write(target, DMI_DATA1, program->write_value >> 32);
}
dmi_write(target, DMI_DATA0, program->write_value);
command |= AC_ACCESS_REGISTER_WRITE | AC_ACCESS_REGISTER_POSTEXEC;
} else {
command |= AC_ACCESS_REGISTER_PREEXEC;
}
command |= abstract_register_size(riscv_xlen(target));
command |= program->regno;
return execute_abstract_command(target, command);
}
static int abstract_read_register(struct target *target,
uint64_t *value,
uint32_t reg_number,
@ -570,6 +490,7 @@ static int abstract_read_register(struct target *target,
uint32_t command = abstract_register_size(width);
command |= reg_number_to_no(reg_number);
command |= AC_ACCESS_REGISTER_TRANSFER;
int result = execute_abstract_command(target, command);
if (result != ERROR_OK) {
@ -601,6 +522,7 @@ static int abstract_write_register(struct target *target,
command |= reg_number_to_no(reg_number);
command |= AC_ACCESS_REGISTER_WRITE;
command |= AC_ACCESS_REGISTER_TRANSFER;
switch (width) {
case 128:
@ -638,107 +560,69 @@ static int update_mstatus_actual(struct target *target)
static int register_write_direct(struct target *target, unsigned number,
uint64_t value)
{
riscv013_info_t *info = get_info(target);
LOG_DEBUG("register 0x%x <- 0x%" PRIx64, number, value);
struct riscv_program program;
riscv_program_init(&program, target);
if (number == GDB_REGNO_MSTATUS) {
info->mstatus_actual = value;
riscv_addr_t input = riscv_program_alloc_x(&program);
switch (riscv_xlen(target)) {
case 64:
riscv_program_write_ram(&program, input + 4, value >> 32);
case 32:
riscv_program_write_ram(&program, input, value);
}
int result = abstract_write_register(target, number, riscv_xlen(target), value);
if (result == ERROR_OK)
return result;
// Fall back to program buffer.
if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
result = update_mstatus_actual(target);
if (result != ERROR_OK) {
return result;
}
if ((info->mstatus_actual & MSTATUS_FS) == 0) {
result = register_write_direct(target, GDB_REGNO_MSTATUS,
set_field(info->mstatus_actual, MSTATUS_FS, 1));
if (result != ERROR_OK)
return result;
}
program_t *program = program_new();
// TODO: Fully support D extension on RV32.
if (supports_extension(target, 'D') && riscv_xlen(target) >= 64) {
program_add32(program, fmv_d_x(number - GDB_REGNO_FPR0, S0));
} else {
program_add32(program, fmv_s_x(number - GDB_REGNO_FPR0, S0));
}
program_add32(program, ebreak());
program_set_write(program, S0, value);
result = execute_program(target, program);
program_delete(program);
if (number >= GDB_REGNO_XPR0 && number <= GDB_REGNO_XPR31) {
riscv_program_lx(&program, number, input);
} else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
LOG_ERROR("FIXME: I don't support floating-point");
abort();
} else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
program_t *program = program_new();
program_add32(program, csrw(S0, number - GDB_REGNO_CSR0));
program_add32(program, ebreak());
program_set_write(program, S0, value);
result = execute_program(target, program);
program_delete(program);
enum gdb_regno temp = riscv_program_gettemp(&program);
riscv_program_lx(&program, temp, input);
riscv_program_csrw(&program, temp, number);
} else {
return result;
LOG_ERROR("Unsupported register (enum gdb_regno)(%d)", number);
abort();
}
return result;
int exec_out = riscv_program_exec(&program, target);
if (exec_out != ERROR_OK) {
LOG_ERROR("Unable to execute program");
return exec_out;
}
return ERROR_OK;
}
/** Actually read registers from the target right now. */
static int register_read_direct(struct target *target, uint64_t *value, uint32_t number)
{
riscv013_info_t *info = get_info(target);
int result = abstract_read_register(target, value, number, riscv_xlen(target));
if (result == ERROR_OK)
return result;
struct riscv_program program;
riscv_program_init(&program, target);
riscv_addr_t output = riscv_program_alloc_d(&program);
// Fall back to program buffer.
if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
result = update_mstatus_actual(target);
if (result != ERROR_OK) {
return result;
}
if ((info->mstatus_actual & MSTATUS_FS) == 0) {
result = register_write_direct(target, GDB_REGNO_MSTATUS,
set_field(info->mstatus_actual, MSTATUS_FS, 1));
if (result != ERROR_OK)
return result;
}
LOG_DEBUG("mstatus_actual=0x%lx", info->mstatus_actual);
program_t *program = program_new();
if (supports_extension(target, 'D') && riscv_xlen(target) >= 64) {
program_add32(program, fmv_x_d(S0, number - GDB_REGNO_FPR0));
} else {
program_add32(program, fmv_x_s(S0, number - GDB_REGNO_FPR0));
}
program_add32(program, ebreak());
program_set_read(program, S0);
result = execute_program(target, program);
program_delete(program);
if (number >= GDB_REGNO_XPR0 && number <= GDB_REGNO_XPR31) {
riscv_program_sx(&program, number, output);
} else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
LOG_ERROR("FIXME: I don't support floating-point");
abort();
} else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
program_t *program = program_new();
program_add32(program, csrr(S0, number - GDB_REGNO_CSR0));
program_add32(program, ebreak());
program_set_read(program, S0);
result = execute_program(target, program);
program_delete(program);
enum gdb_regno temp = riscv_program_gettemp(&program);
riscv_program_csrr(&program, temp, number);
riscv_program_sx(&program, temp, output);
} else {
return result;
LOG_ERROR("Unsupported register (enum gdb_regno)(%d)", number);
abort();
}
if (result != ERROR_OK)
return result;
result = register_read_direct(target, value, S0);
if (result != ERROR_OK)
return result;
int exec_out = riscv_program_exec(&program, target);
if (exec_out != ERROR_OK) {
LOG_ERROR("Unable to execute program");
return exec_out;
}
*value = riscv_program_read_ram(&program, output);
LOG_DEBUG("register 0x%x = 0x%" PRIx64, number, *value);
return ERROR_OK;
}
@ -783,13 +667,13 @@ static int register_get(struct reg *reg)
register_read_direct(target, reg->value, reg->number);
return ERROR_OK;
} else if (reg->number == GDB_REGNO_PC) {
buf_set_u32(reg->value, 0, 32, riscv_peek_register(target, GDB_REGNO_DPC));
buf_set_u32(reg->value, 0, 32, riscv_get_register(target, GDB_REGNO_DPC));
reg->valid = true;
return ERROR_OK;
} else if (reg->number == GDB_REGNO_PRIV) {
uint64_t dcsr = riscv_peek_register(target, CSR_DCSR);
uint64_t dcsr = riscv_get_register(target, CSR_DCSR);
buf_set_u64(reg->value, 0, 8, get_field(dcsr, CSR_DCSR_PRV));
riscv_overwrite_register(target, CSR_DCSR, dcsr);
riscv_set_register(target, CSR_DCSR, dcsr);
return ERROR_OK;
} else {
uint64_t value;
@ -815,11 +699,11 @@ static int register_write(struct target *target, unsigned int number,
maybe_write_tselect(target);
if (number == GDB_REGNO_PC) {
riscv_overwrite_register(target, GDB_REGNO_DPC, value);
riscv_set_register(target, GDB_REGNO_DPC, value);
} else if (number == GDB_REGNO_PRIV) {
uint64_t dcsr = riscv_peek_register(target, CSR_DCSR);
uint64_t dcsr = riscv_get_register(target, CSR_DCSR);
dcsr = set_field(dcsr, CSR_DCSR_PRV, value);
riscv_overwrite_register(target, GDB_REGNO_DCSR, dcsr);
riscv_set_register(target, GDB_REGNO_DCSR, dcsr);
} else {
return register_write_direct(target, number, value);
}
@ -864,6 +748,11 @@ static int init_target(struct command_context *cmd_ctx,
generic_info->on_resume = &riscv013_on_resume;
generic_info->on_step = &riscv013_on_step;
generic_info->halt_reason = &riscv013_halt_reason;
generic_info->debug_buffer_enter = &riscv013_debug_buffer_enter;
generic_info->debug_buffer_leave = &riscv013_debug_buffer_leave;
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->version_specific = calloc(1, sizeof(riscv013_info_t));
if (!generic_info->version_specific)
@ -1234,19 +1123,64 @@ static int examine(struct target *target)
/* Halt every hart so we can probe them. */
riscv_halt_all_harts(target);
/* Examines every hart, first checking XLEN. */
/* Find the address of the program buffer, which must be done without
* knowing anything about the target. */
for (int i = 0; i < riscv_count_harts(target); ++i) {
riscv_set_current_hartid(target, i);
if (abstract_read_register(target, NULL, S0, 128) == ERROR_OK) {
r->xlen[i] = 128;
} else if (abstract_read_register(target, NULL, S0, 64) == ERROR_OK) {
/* Without knowing anything else we can at least mess with the
* program buffer. */
r->debug_buffer_size[i] = riscv013_progbuf_size(target);
/* First find the low 32 bits of the program buffer. This is
* used to check for alignment. */
struct riscv_program program32;
riscv_program_init(&program32, target);
riscv_program_csrrw(&program32, GDB_REGNO_S0, GDB_REGNO_S0, GDB_REGNO_DSCRATCH);
riscv_program_insert(&program32, auipc(GDB_REGNO_S0));
riscv_program_insert(&program32, sw(GDB_REGNO_S0, GDB_REGNO_S0, -4));
riscv_program_csrrw(&program32, GDB_REGNO_S0, GDB_REGNO_S0, GDB_REGNO_DSCRATCH);
riscv_program_fence(&program32);
riscv_program_exec(&program32, target);
riscv_addr_t progbuf_addr = dmi_read(target, DMI_PROGBUF0) - 4;
if (get_field(dmi_read(target, DMI_ABSTRACTCS), DMI_ABSTRACTCS_CMDERR) != 0) {
LOG_ERROR("Unable to find the address of the program buffer on hart %d", i);
r->xlen[i] = -1;
continue;
}
r->xlen[i] = 32;
r->debug_buffer_addr[i] = progbuf_addr;
/* Check to see if the core can execute 64 bit instructions.
* In order to make this work we first need to */
int offset = (progbuf_addr % 8 == 0) ? -4 : 0;
struct riscv_program program64;
riscv_program_init(&program64, target);
riscv_program_csrrw(&program64, GDB_REGNO_S0, GDB_REGNO_S0, GDB_REGNO_DSCRATCH);
riscv_program_insert(&program64, auipc(GDB_REGNO_S0));
riscv_program_insert(&program64, sd(GDB_REGNO_S0, GDB_REGNO_S0, offset));
riscv_program_csrrw(&program64, GDB_REGNO_S0, GDB_REGNO_S0, GDB_REGNO_DSCRATCH);
riscv_program_fence(&program64);
riscv_program_exec(&program64, target);
if (get_field(dmi_read(target, DMI_ABSTRACTCS), DMI_ABSTRACTCS_CMDERR) == 0) {
r->debug_buffer_addr[i] =
(dmi_read(target, DMI_PROGBUF0 + (8 + offset) / 4) << 32)
+ dmi_read(target, DMI_PROGBUF0 + (4 + offset) / 4)
- 4;
r->xlen[i] = 64;
} else if (abstract_read_register(target, NULL, S0, 32) == ERROR_OK) {
r->xlen[i] = 32;
} else {
LOG_ERROR("Failed to discover size using abstract register reads.");
return ERROR_FAIL;
}
LOG_DEBUG("hart %d has XLEN=%d", i, r->xlen[i]);
LOG_DEBUG("found program buffer at 0x%08lx", (long)(r->debug_buffer_addr[i]));
/* Check to see if we can use the data words as an extended
* program buffer or not. */
if (r->debug_buffer_addr[i] + (4 * r->debug_buffer_size[i]) == riscv013_data_addr(target)) {
r->debug_buffer_size[i] += riscv013_data_size(target);
LOG_DEBUG("extending the debug buffer using data words, total size %d", r->debug_buffer_size[i]);
}
}
@ -1308,64 +1242,60 @@ static int read_memory(struct target *target, uint32_t address,
uint32_t t_addr = address + offset;
uint8_t *t_buffer = buffer + offset;
abstract_write_register(target, S0, riscv_xlen(target), t_addr);
struct riscv_program program;
riscv_program_init(&program, target);
enum gdb_regno temp = riscv_program_gettemp(&program);
riscv_addr_t outaddr;
program_t *program = program_new();
switch (size) {
case 1:
program_add32(program, lb(S1, S0, 0));
riscv_program_lb(&program, temp, t_addr);
outaddr = riscv_program_alloc_b(&program);
riscv_program_sb(&program, temp, outaddr);
break;
case 2:
program_add32(program, lh(S1, S0, 0));
riscv_program_lh(&program, temp, t_addr);
outaddr = riscv_program_alloc_h(&program);
riscv_program_sh(&program, temp, outaddr);
break;
case 4:
program_add32(program, lw(S1, S0, 0));
riscv_program_lw(&program, temp, t_addr);
outaddr = riscv_program_alloc_w(&program);
riscv_program_sw(&program, temp, outaddr);
break;
default:
LOG_ERROR("Unsupported size: %d", size);
return ERROR_FAIL;
}
program_add32(program, fence());
program_add32(program, ebreak());
program_set_read(program, S1);
write_program(target, program);
execute_program(target, program);
uint32_t abstractcs;
wait_for_idle(target, &abstractcs);
program_delete(program);
uint32_t value = dmi_read(target, DMI_DATA0);
if (riscv_program_exec(&program, target) != ERROR_OK) {
LOG_ERROR("failed to execute program");
return ERROR_FAIL;
}
uint64_t value = riscv_program_read_ram(&program, outaddr);
switch (size) {
case 1:
t_buffer[0] = value;
break;
case 2:
t_buffer[0] = value;
t_buffer[1] = value >> 8;
break;
t_buffer[0] = value;
t_buffer[1] = value >> 8;
break;
case 4:
t_buffer[0] = value;
t_buffer[1] = value >> 8;
t_buffer[2] = value >> 16;
t_buffer[3] = value >> 24;
break;
t_buffer[0] = value;
t_buffer[1] = value >> 8;
t_buffer[2] = value >> 16;
t_buffer[3] = value >> 24;
break;
default:
return ERROR_FAIL;
}
if (check_dmi_error(target)) {
LOG_ERROR("DMI error");
LOG_ERROR("unsupported access size: %d", size);
return ERROR_FAIL;
}
}
program_t *program = program_new();
program_add32(program, ebreak());
program_add32(program, ebreak());
program_add32(program, ebreak());
program_add32(program, ebreak());
write_program(target, program);
program_delete(program);
LOG_DEBUG("M[0x%08lx] reads 0x%08lx", t_addr, value);
}
return ERROR_OK;
}
@ -1381,25 +1311,6 @@ static int write_memory(struct target *target, uint32_t address,
uint32_t t_addr = address + offset;
const uint8_t *t_buffer = buffer + offset;
abstract_write_register(target, S0, riscv_xlen(target), t_addr);
program_t *program = program_new();
switch (size) {
case 1:
program_add32(program, sb(S1, S0, 0));
break;
case 2:
program_add32(program, sh(S1, S0, 0));
break;
case 4:
program_add32(program, sw(S1, S0, 0));
break;
default:
LOG_ERROR("Unsupported size: %d", size);
return ERROR_FAIL;
}
program_add32(program, fence());
program_add32(program, ebreak());
uint32_t value;
switch (size) {
case 1:
@ -1415,30 +1326,38 @@ static int write_memory(struct target *target, uint32_t address,
((uint32_t) t_buffer[3] << 24);
break;
default:
LOG_ERROR("unsupported access size: %d", size);
return ERROR_FAIL;
}
abstract_write_register(target, S1, riscv_xlen(target), value);
program_set_write(program, S1, value);
write_program(target, program);
execute_program(target, program);
uint32_t abstractcs;
wait_for_idle(target, &abstractcs);
program_delete(program);
struct riscv_program program;
riscv_program_init(&program, target);
enum gdb_regno data = riscv_program_gettemp(&program);
riscv_program_li(&program, data, value);
if (check_dmi_error(target)) {
LOG_ERROR("DMI error");
switch (size) {
case 1:
riscv_program_sb(&program, data, t_addr);
break;
case 2:
riscv_program_sh(&program, data, t_addr);
break;
case 4:
riscv_program_sw(&program, data, t_addr);
break;
default:
LOG_ERROR("Unsupported size: %d", size);
return ERROR_FAIL;
}
if (riscv_program_exec(&program, target) != ERROR_OK) {
LOG_ERROR("failed to execute program");
return ERROR_FAIL;
}
LOG_DEBUG("M[0x%08lx] writes 0x%08lx", t_addr, value);
}
program_t *program = program_new();
program_add32(program, ebreak());
program_add32(program, ebreak());
program_add32(program, ebreak());
program_add32(program, ebreak());
write_program(target, program);
program_delete(program);
return ERROR_OK;
}
@ -1551,23 +1470,21 @@ static void riscv013_on_step(struct target *target)
static void riscv013_on_halt(struct target *target)
{
RISCV_INFO(r);
LOG_DEBUG("saving register state for hart %d", r->current_hartid);
riscv_save_register(target, GDB_REGNO_S0);
riscv_save_register(target, GDB_REGNO_S1);
riscv_save_register(target, GDB_REGNO_DPC);
riscv_save_register(target, GDB_REGNO_DCSR);
}
static bool riscv013_is_halted(struct target *target)
{
uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS);
if (get_field(dmstatus, DMI_DMSTATUS_ANYUNAVAIL))
LOG_ERROR("hart %d is unavailiable", riscv_current_hartid(target));
if (get_field(dmstatus, DMI_DMSTATUS_ANYNONEXISTENT))
LOG_ERROR("hart %d doesn't exist", riscv_current_hartid(target));
return get_field(dmstatus, DMI_DMSTATUS_ALLHALTED);
}
static enum riscv_halt_reason riscv013_halt_reason(struct target *target)
{
uint64_t dcsr = riscv_peek_register(target, GDB_REGNO_DCSR);
uint64_t dcsr = riscv_get_register(target, GDB_REGNO_DCSR);
switch (get_field(dcsr, CSR_DCSR_CAUSE)) {
case CSR_DCSR_CAUSE_SWBP:
case CSR_DCSR_CAUSE_TRIGGER:
@ -1580,37 +1497,76 @@ static enum riscv_halt_reason riscv013_halt_reason(struct target *target)
}
LOG_ERROR("Unknown DCSR cause field: %x", (int)get_field(dcsr, CSR_DCSR_CAUSE));
LOG_ERROR(" dcsr=0x%08x", dcsr);
abort();
}
void riscv013_debug_buffer_enter(struct target *target, struct riscv_program *program)
{
}
void riscv013_debug_buffer_leave(struct target *target, struct riscv_program *program)
{
}
void riscv013_write_debug_buffer(struct target *target, int index, riscv_insn_t data)
{
if (index > riscv013_progbuf_size(target))
return dmi_write(target, DMI_DATA0 + index - riscv013_progbuf_size(target), data);
return dmi_write(target, DMI_PROGBUF0 + index, data);
}
riscv_insn_t riscv013_read_debug_buffer(struct target *target, int index)
{
if (index > riscv013_progbuf_size(target))
return dmi_read(target, DMI_DATA0 + index - riscv013_progbuf_size(target));
return dmi_read(target, DMI_PROGBUF0 + index);
}
void riscv013_execute_debug_buffer(struct target *target)
{
uint32_t abstractcs = dmi_read(target, DMI_ABSTRACTCS);
set_field(abstractcs, DMI_ABSTRACTCS_CMDERR, 0);
dmi_write(target, DMI_ABSTRACTCS, abstractcs);
uint32_t run_program = 0;
run_program = set_field(run_program, AC_ACCESS_REGISTER_SIZE, 2);
run_program = set_field(run_program, AC_ACCESS_REGISTER_POSTEXEC, 1);
run_program = set_field(run_program, AC_ACCESS_REGISTER_TRANSFER, 0);
run_program = set_field(run_program, AC_ACCESS_REGISTER_REGNO, 0x1000);
dmi_write(target, DMI_COMMAND, run_program);
{
uint32_t dmstatus = 0;
wait_for_idle(target, &dmstatus);
}
uint32_t cs = dmi_read(target, DMI_ABSTRACTCS);
if (get_field(cs, DMI_ABSTRACTCS_CMDERR) != 0) {
LOG_ERROR("unable to execute program: (abstractcs=0x%08x)", cs);
dmi_read(target, DMI_DMSTATUS);
}
}
/* Helper Functions. */
static void riscv013_on_step_or_resume(struct target *target, bool step)
{
RISCV_INFO(r);
LOG_DEBUG("restoring register state for hart %d", r->current_hartid);
program_t *program = program_new();
program_add32(program, fence_i());
program_add32(program, ebreak());
write_program(target, program);
if (execute_program(target, program) != ERROR_OK) {
struct riscv_program program;
riscv_program_init(&program, target);
riscv_program_fence_i(&program);
if (riscv_program_exec(&program, target) != ERROR_OK)
LOG_ERROR("Unable to execute fence.i");
}
program_delete(program);
/* We want to twiddle some bits in the debug CSR so debugging works. */
uint64_t dcsr = riscv_peek_register(target, GDB_REGNO_DCSR);
uint64_t dcsr = riscv_get_register(target, GDB_REGNO_DCSR);
dcsr = set_field(dcsr, CSR_DCSR_STEP, step);
dcsr = set_field(dcsr, CSR_DCSR_EBREAKM, 1);
dcsr = set_field(dcsr, CSR_DCSR_EBREAKH, 1);
dcsr = set_field(dcsr, CSR_DCSR_EBREAKS, 1);
dcsr = set_field(dcsr, CSR_DCSR_EBREAKU, 1);
riscv_overwrite_register(target, GDB_REGNO_DCSR, dcsr);
riscv_restore_register(target, GDB_REGNO_DCSR);
riscv_restore_register(target, GDB_REGNO_DPC);
riscv_restore_register(target, GDB_REGNO_S1);
riscv_restore_register(target, GDB_REGNO_S0);
riscv_set_register(target, GDB_REGNO_DCSR, dcsr);
}
static void riscv013_step_or_resume_current_hart(struct target *target, bool step)
@ -1624,31 +1580,38 @@ static void riscv013_step_or_resume_current_hart(struct target *target, bool ste
dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_RESUMEREQ, 1);
dmi_write(target, DMI_DMCONTROL, dmcontrol);
#if 1
/* FIXME: ... well, after a short time. */
usleep(100);
#else
/* FIXME: there's a race condition in stepping now, so just return
* right away... */
for (size_t i = 0; i < 256; ++i) {
if (!riscv_is_halted(target))
break;
}
if (riscv_is_halted(target)) {
uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS);
dmcontrol = dmi_read(target, DMI_DMCONTROL);
if (get_field(dmstatus, DMI_DMSTATUS_ALLRESUMEACK) == 0)
continue;
LOG_ERROR("unable to resume hart %d", r->current_hartid);
LOG_ERROR(" dmcontrol=0x%08x", dmcontrol);
LOG_ERROR(" dmstatus =0x%08x", dmstatus);
abort();
dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_RESUMEREQ, 0);
dmi_write(target, DMI_DMCONTROL, dmcontrol);
return;
}
#endif
dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_RESUMEREQ, 0);
dmi_write(target, DMI_DMCONTROL, dmcontrol);
/* When stepping we need to go and restore the relevant registers. */
if (step) riscv013_on_halt(target);
uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS);
dmcontrol = dmi_read(target, DMI_DMCONTROL);
LOG_ERROR("unable to resume hart %d", r->current_hartid);
LOG_ERROR(" dmcontrol=0x%08x", dmcontrol);
LOG_ERROR(" dmstatus =0x%08x", dmstatus);
abort();
}
size_t riscv013_progbuf_size(struct target *target)
{
uint32_t acs = dmi_read(target, DMI_ABSTRACTCS);
return get_field(acs, DMI_ABSTRACTCS_PROGSIZE);
}
size_t riscv013_data_size(struct target *target)
{
uint32_t acs = dmi_read(target, DMI_HARTINFO);
return get_field(acs, DMI_HARTINFO_DATASIZE);
}
size_t riscv013_data_addr(struct target *target)
{
uint32_t acs = dmi_read(target, DMI_HARTINFO);
return get_field(acs, DMI_HARTINFO_DATAACCESS) ? get_field(acs, DMI_HARTINFO_DATAADDR) : 0;
}

View File

@ -618,9 +618,17 @@ static int riscv_poll_hart(struct target *target, int hartid)
RISCV_INFO(r);
LOG_DEBUG("polling hart %d", hartid);
/* If there's no new event then there's nothing to do. */
/* Polling can only detect one state change: a hart that was previously
* running but has gone to sleep. A state change in the other
* direction is invalid and indicates that one of the previous calls
* didn't correctly block. */
riscv_set_current_hartid(target, hartid);
assert((riscv_was_halted(target) && riscv_is_halted(target)) || !riscv_was_halted(target));
if (riscv_was_halted(target) && !riscv_is_halted(target)) {
LOG_ERROR("unexpected wakeup on hart %d", hartid);
abort();
}
/* If there's no new event then there's nothing to do. */
if (riscv_was_halted(target) || !riscv_is_halted(target))
return 0;
@ -696,7 +704,9 @@ int riscv_openocd_halt(struct target *target)
if (out != ERROR_OK)
return out;
target->state = TARGET_HALTED;
/* Don't change the target state right here, it'll get updated by the
* poll. */
riscv_openocd_poll(target);
return out;
}
@ -717,6 +727,7 @@ int riscv_openocd_resume(
return out;
target->state = TARGET_RUNNING;
riscv_openocd_poll(target);
return out;
}
@ -738,7 +749,7 @@ int riscv_openocd_step(
return out;
/* step_rtos_hart blocks until the hart has actually stepped, but we
* need to cycle through OpenOCD to */
* need to cycle through OpenOCD to actually get this to trigger. */
target->state = TARGET_RUNNING;
riscv_openocd_poll(target);
@ -761,50 +772,13 @@ void riscv_info_init(riscv_info_t *r)
* XLEN to 64 for all cores at reset. */
r->xlen[h] = 64;
r->hart_state[h] = RISCV_HART_UNKNOWN;
r->debug_buffer_addr[h] = -1;
for (size_t e = 0; e < RISCV_MAX_REGISTERS; ++e)
r->valid_saved_registers[h][e] = false;
}
}
void riscv_save_register(struct target *target, int regno)
{
RISCV_INFO(r);
int hartno = r->current_hartid;
LOG_DEBUG("riscv_save_register(%d, %d)", hartno, regno);
assert(r->valid_saved_registers[hartno][regno] == false);
r->valid_saved_registers[hartno][regno] = true;
r->saved_registers[hartno][regno] = riscv_get_register(target, hartno, regno);
}
uint64_t riscv_peek_register(struct target *target, int regno)
{
RISCV_INFO(r);
int hartno = r->current_hartid;
LOG_DEBUG("riscv_peek_register(%d, %d)", hartno, regno);
assert(r->valid_saved_registers[hartno][regno] == true);
return r->saved_registers[hartno][regno];
}
void riscv_overwrite_register(struct target *target, int regno, uint64_t newval)
{
RISCV_INFO(r);
int hartno = r->current_hartid;
LOG_DEBUG("riscv_overwrite_register(%d, %d)", hartno, regno);
assert(r->valid_saved_registers[hartno][regno] == true);
r->saved_registers[hartno][regno] = newval;
}
void riscv_restore_register(struct target *target, int regno)
{
RISCV_INFO(r);
int hartno = r->current_hartid;
LOG_DEBUG("riscv_restore_register(%d, %d)", hartno, regno);
assert(r->valid_saved_registers[hartno][regno] == true);
r->valid_saved_registers[hartno][regno] = false;
riscv_set_register(target, hartno, regno, r->saved_registers[hartno][regno]);
}
int riscv_halt_all_harts(struct target *target)
{
if (riscv_rtos_enabled(target)) {
@ -837,6 +811,8 @@ int riscv_halt_one_hart(struct target *target, int hartid)
}
r->halt_current_hart(target);
/* Here we don't actually update 'hart_state' because we want poll to
* pick that up. We can't actually wait until */
return ERROR_OK;
}
@ -894,6 +870,10 @@ int riscv_step_rtos_hart(struct target *target)
assert(r->hart_state[hartid] == RISCV_HART_HALTED);
r->on_step(target);
r->step_current_hart(target);
r->hart_state[hartid] = RISCV_HART_RUNNING;
r->on_halt(target);
r->hart_state[hartid] = RISCV_HART_HALTED;
assert(riscv_is_halted(target));
return ERROR_OK;
}
@ -979,14 +959,24 @@ bool riscv_has_register(struct target *target, int hartid, int regid)
return 1;
}
void riscv_set_register(struct target *target, int hartid, enum gdb_regno regid, uint64_t value)
void riscv_set_register(struct target *target, enum gdb_regno r, riscv_reg_t v)
{
return riscv_set_register_on_hart(target, riscv_current_hartid(target), r, v);
}
void riscv_set_register_on_hart(struct target *target, int hartid, enum gdb_regno regid, uint64_t value)
{
RISCV_INFO(r);
LOG_DEBUG("writing register %d on hart %d", regid, hartid);
return r->set_register(target, hartid, regid, value);
}
uint64_t riscv_get_register(struct target *target, int hartid, enum gdb_regno regid)
riscv_reg_t riscv_get_register(struct target *target, enum gdb_regno r)
{
return riscv_get_register_on_hart(target, riscv_current_hartid(target), r);
}
uint64_t riscv_get_register_on_hart(struct target *target, int hartid, enum gdb_regno regid)
{
RISCV_INFO(r);
LOG_DEBUG("reading register %d on hart %d", regid, hartid);
@ -1025,3 +1015,51 @@ int riscv_count_triggers_of_hart(struct target *target, int hartid)
assert(hartid < riscv_count_harts(target));
return r->trigger_count[hartid];
}
size_t riscv_debug_buffer_size(struct target *target)
{
RISCV_INFO(r);
return r->debug_buffer_size[riscv_current_hartid(target)];
}
riscv_addr_t riscv_debug_buffer_addr(struct target *target)
{
RISCV_INFO(r);
riscv_addr_t out = r->debug_buffer_addr[riscv_current_hartid(target)];
assert(out != -1);
return out;
}
int riscv_debug_buffer_enter(struct target *target, struct riscv_program *program)
{
RISCV_INFO(r);
r->debug_buffer_enter(target, program);
return ERROR_OK;
}
int riscv_debug_buffer_leave(struct target *target, struct riscv_program *program)
{
RISCV_INFO(r);
r->debug_buffer_leave(target, program);
return ERROR_OK;
}
int riscv_write_debug_buffer(struct target *target, int index, riscv_insn_t insn)
{
RISCV_INFO(r);
r->write_debug_buffer(target, index, insn);
return ERROR_OK;
}
riscv_insn_t riscv_read_debug_buffer(struct target *target, int index)
{
RISCV_INFO(r);
return r->read_debug_buffer(target, index);
}
int riscv_execute_debug_buffer(struct target *target)
{
RISCV_INFO(r);
r->execute_debug_buffer(target);
return ERROR_OK;
}

View File

@ -1,6 +1,9 @@
#ifndef RISCV_H
#define RISCV_H
struct riscv_program;
#include <stdint.h>
#include "opcodes.h"
#include "gdb_regs.h"
@ -16,6 +19,8 @@ extern struct target_type riscv013_target;
* Definitions shared by code supporting all RISC-V versions.
*/
typedef uint64_t riscv_reg_t;
typedef uint32_t riscv_insn_t;
typedef int64_t riscv_addr_t;
enum riscv_hart_state {
RISCV_HART_UNKNOWN,
@ -68,6 +73,12 @@ typedef struct {
/* The number of triggers per hart. */
int trigger_count[RISCV_MAX_HARTS];
/* The address of the debug RAM buffer. */
riscv_addr_t debug_buffer_addr[RISCV_MAX_HARTS];
/* The number of entries in the debug buffer. */
int debug_buffer_size[RISCV_MAX_HARTS];
/* Helper functions that target the various RISC-V debug spec
* implementations. */
riscv_reg_t (*get_register)(struct target *, int, int);
@ -81,6 +92,11 @@ typedef struct {
void (*on_resume)(struct target *target);
void (*on_step)(struct target *target);
enum riscv_halt_reason (*halt_reason)(struct target *target);
void (*debug_buffer_enter)(struct target *target, struct riscv_program *program);
void (*debug_buffer_leave)(struct target *target, struct riscv_program *program);
void (*write_debug_buffer)(struct target *target, int i, riscv_insn_t d);
riscv_insn_t (*read_debug_buffer)(struct target *target, int i);
void (*execute_debug_buffer)(struct target *target);
} riscv_info_t;
/* Everything needs the RISC-V specific info structure, so here's a nice macro
@ -122,12 +138,6 @@ int riscv_openocd_step(
/* Initializes the shared RISC-V structure. */
void riscv_info_init(riscv_info_t *r);
/* Functions that save and restore registers. */
void riscv_save_register(struct target *target, int regno);
uint64_t riscv_peek_register(struct target *target, int regno);
void riscv_overwrite_register(struct target *target, int regno, uint64_t newval);
void riscv_restore_register(struct target *target, int regno);
/* Run control, possibly for multiple harts. The _all_harts versions resume
* all the enabled harts, which when running in RTOS mode is all the harts on
* the system. */
@ -172,8 +182,10 @@ bool riscv_has_register(struct target *target, int hartid, int regid);
/* Returns the value of the given register on the given hart. 32-bit registers
* are zero extended to 64 bits. */
void riscv_set_register(struct target *target, int hid, enum gdb_regno rid, uint64_t v);
riscv_reg_t riscv_get_register(struct target *target, int hid, enum gdb_regno rid);
void riscv_set_register(struct target *target, enum gdb_regno i, riscv_reg_t v);
void riscv_set_register_on_hart(struct target *target, int hid, enum gdb_regno rid, uint64_t v);
riscv_reg_t riscv_get_register(struct target *target, enum gdb_regno i);
riscv_reg_t riscv_get_register_on_hart(struct target *target, int hid, enum gdb_regno rid);
/* Checks the state of the current hart -- "is_halted" checks the actual
* on-device register, while "was_halted" checks the machine's state. */
@ -186,4 +198,16 @@ enum riscv_halt_reason riscv_halt_reason(struct target *target, int hartid);
int riscv_count_triggers(struct target *target);
int riscv_count_triggers_of_hart(struct target *target, int hartid);
/* These helper functions let the generic program interface get target-specific
* information. */
size_t riscv_debug_buffer_size(struct target *target);
riscv_addr_t riscv_debug_buffer_addr(struct target *target);
int riscv_debug_buffer_enter(struct target *target, struct riscv_program *program);
int riscv_debug_buffer_leave(struct target *target, struct riscv_program *program);
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);
#endif