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 bb2cceac25
10 changed files with 1252 additions and 564 deletions

View File

@ -213,14 +213,9 @@ static int riscv_gdb_thread_packet(struct connection *connection, const char *pa
return JIM_OK; return JIM_OK;
case 'R': case 'R':
{ gdb_put_packet(connection, "E00", 3);
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);
return JIM_OK; return JIM_OK;
}
default: default:
LOG_ERROR("Unknown packet of type 0x%2.2x", packet[0]); LOG_ERROR("Unknown packet of type 0x%2.2x", packet[0]);
gdb_put_packet(connection, NULL, 0); gdb_put_packet(connection, NULL, 0);
@ -285,10 +280,10 @@ static int riscv_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char
*hex_reg_list[0] = '\0'; *hex_reg_list[0] = '\0';
for (size_t i = 0; i < n_regs; ++i) { for (size_t i = 0; i < n_regs; ++i) {
if (riscv_has_register(rtos->target, thread_id, 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) { for (size_t byte = 0; byte < xlen / 8; ++byte) {
uint8_t reg_byte = reg_value >> (byte * 8); uint8_t reg_byte = reg_value >> (byte * 8);
char hex[3]; char hex[3] = {'x', 'x', 'x'};
snprintf(hex, 3, "%02x", reg_byte); snprintf(hex, 3, "%02x", reg_byte);
strncat(*hex_reg_list, hex, hex_reg_list_length); strncat(*hex_reg_list, hex, hex_reg_list_length);
} }
@ -297,6 +292,7 @@ static int riscv_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char
strncat(*hex_reg_list, "xx", hex_reg_list_length); strncat(*hex_reg_list, "xx", hex_reg_list_length);
} }
} }
LOG_DEBUG(*hex_reg_list);
return JIM_OK; return JIM_OK;
} }

View File

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

View File

@ -26,15 +26,28 @@
#define DTM_IDCODE_1 (0x1 << DTM_IDCODE_1_OFFSET) #define DTM_IDCODE_1 (0x1 << DTM_IDCODE_1_OFFSET)
#define DTM_DTMCS 0x10 #define DTM_DTMCS 0x10
/* /*
* Writing 1 to this bit resets the DMI controller, clearing any * Writing 1 to this bit does a hard reset of the DTM,
* sticky error state. * 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_OFFSET 16
#define DTM_DTMCS_DMIRESET_LENGTH 1 #define DTM_DTMCS_DMIRESET_LENGTH 1
#define DTM_DTMCS_DMIRESET (0x1 << DTM_DTMCS_DMIRESET_OFFSET) #define DTM_DTMCS_DMIRESET (0x1 << DTM_DTMCS_DMIRESET_OFFSET)
/* /*
* This is the minimum number of cycles a debugger should spend in * This is a hint to the debugger of the minimum number of
* Run-Test/Idle after every DMI scan to avoid a 'busy' * 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 * return code (\Fdmistat of 3). A debugger must still
* check \Fdmistat when necessary. * check \Fdmistat when necessary.
* *
@ -146,26 +159,26 @@
#define CSR_DCSR_XDEBUGVER_LENGTH 2 #define CSR_DCSR_XDEBUGVER_LENGTH 2
#define CSR_DCSR_XDEBUGVER (0x3 << CSR_DCSR_XDEBUGVER_OFFSET) #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_OFFSET 15
#define CSR_DCSR_EBREAKM_LENGTH 1 #define CSR_DCSR_EBREAKM_LENGTH 1
#define CSR_DCSR_EBREAKM (0x1 << CSR_DCSR_EBREAKM_OFFSET) #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_OFFSET 14
#define CSR_DCSR_EBREAKH_LENGTH 1 #define CSR_DCSR_EBREAKH_LENGTH 1
#define CSR_DCSR_EBREAKH (0x1 << CSR_DCSR_EBREAKH_OFFSET) #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_OFFSET 13
#define CSR_DCSR_EBREAKS_LENGTH 1 #define CSR_DCSR_EBREAKS_LENGTH 1
#define CSR_DCSR_EBREAKS (0x1 << CSR_DCSR_EBREAKS_OFFSET) #define CSR_DCSR_EBREAKS (0x1 << CSR_DCSR_EBREAKS_OFFSET)
/* /*
* When 1, {\tt ebreak} instructions in User/Application Mode enter * When 1, {\tt ebreak} instructions in User/Application Mode enter
* Halt Mode. * Debug Mode.
*/ */
#define CSR_DCSR_EBREAKU_OFFSET 12 #define CSR_DCSR_EBREAKU_OFFSET 12
#define CSR_DCSR_EBREAKU_LENGTH 1 #define CSR_DCSR_EBREAKU_LENGTH 1
@ -173,7 +186,7 @@
/* /*
* 0: Increment counters as usual. * 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 * the {\tt cycle} and {\tt instret} CSRs. This is preferred for most
* debugging scenarios. * debugging scenarios.
* *
@ -187,7 +200,7 @@
/* /*
* 0: Increment timers as usual. * 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. * An implementation may choose not to support writing to this bit.
* The debugger must read back the value it writes to check whether * 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_LENGTH 1
#define CSR_DCSR_STOPTIME (0x1 << CSR_DCSR_STOPTIME_OFFSET) #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. * 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) * 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) * 4: The hart single stepped because \Fstep was set. (priority 1)
* *
* 5: \Fhaltreq was set. (priority 0)
*
* Other values are reserved for future use. * Other values are reserved for future use.
*/ */
#define CSR_DCSR_CAUSE_OFFSET 6 #define CSR_DCSR_CAUSE_OFFSET 6
#define CSR_DCSR_CAUSE_LENGTH 3 #define CSR_DCSR_CAUSE_LENGTH 3
#define CSR_DCSR_CAUSE (0x7 << CSR_DCSR_CAUSE_OFFSET) #define CSR_DCSR_CAUSE (0x7 << CSR_DCSR_CAUSE_OFFSET)
/* /*
* When set and not in Halt Mode, the hart will only execute a single * When set and not in Debug Mode, the hart will only execute a single
* instruction, and then enter Halt Mode. Interrupts are disabled * instruction, and then enter Debug Mode. Interrupts are disabled
* when this bit is set. * when this bit is set.
*/ */
#define CSR_DCSR_STEP_OFFSET 2 #define CSR_DCSR_STEP_OFFSET 2
@ -227,9 +238,9 @@
#define CSR_DCSR_STEP (0x1 << CSR_DCSR_STEP_OFFSET) #define CSR_DCSR_STEP (0x1 << CSR_DCSR_STEP_OFFSET)
/* /*
* Contains the privilege level the hart was operating in when Debug * 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 * \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 * Not all privilege levels are supported on all harts. If the
* encoding written is not supported or the debugger is not allowed to * encoding written is not supported or the debugger is not allowed to
@ -247,9 +258,9 @@
#define CSR_PRIV virtual #define CSR_PRIV virtual
/* /*
* Contains the privilege level the hart was operating in when Debug * 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 * \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_OFFSET 0
#define CSR_PRIV_PRV_LENGTH 2 #define CSR_PRIV_PRV_LENGTH 2
@ -283,10 +294,10 @@
* 0: Both Debug and M Mode can write the {\tt tdata} registers at the * 0: Both Debug and M Mode can write the {\tt tdata} registers at the
* selected \Rtselect. * 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. * 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_OFFSET XLEN-5
#define CSR_TDATA1_HMODE_LENGTH 1 #define CSR_TDATA1_HMODE_LENGTH 1
@ -366,7 +377,7 @@
* 0: Raise a breakpoint exception. (Used when software wants to use * 0: Raise a breakpoint exception. (Used when software wants to use
* the trigger module without an external debugger attached.) * 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. * 2: Start tracing.
* *
@ -504,10 +515,10 @@
/* /*
* Determines what happens when this trigger matches. * 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.) * 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. * 2: Start tracing.
* *
@ -524,6 +535,18 @@
#define CSR_ICOUNT_ACTION (0x3fL << CSR_ICOUNT_ACTION_OFFSET) #define CSR_ICOUNT_ACTION (0x3fL << CSR_ICOUNT_ACTION_OFFSET)
#define DMI_DMSTATUS 0x11 #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. * This field is 1 when all currently selected harts do not exist in this system.
*/ */
#define DMI_DMSTATUS_ALLNONEXISTENT_OFFSET 15 #define DMI_DMSTATUS_ALLNONEXISTENT_OFFSET 15
@ -617,7 +640,7 @@
#define DMI_DMSTATUS_VERSIONLO (0x3 << DMI_DMSTATUS_VERSIONLO_OFFSET) #define DMI_DMSTATUS_VERSIONLO (0x3 << DMI_DMSTATUS_VERSIONLO_OFFSET)
#define DMI_DMCONTROL 0x10 #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. * hart will halt if it is not currently halted.
* Setting both \Fhaltreq and \Fresumereq leads to undefined behavior. * Setting both \Fhaltreq and \Fresumereq leads to undefined behavior.
* *
@ -627,7 +650,7 @@
#define DMI_DMCONTROL_HALTREQ_LENGTH 1 #define DMI_DMCONTROL_HALTREQ_LENGTH 1
#define DMI_DMCONTROL_HALTREQ (0x1 << DMI_DMCONTROL_HALTREQ_OFFSET) #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. * the hart will resume if it is currently halted.
* Setting both \Fhaltreq and \Fresumereq leads to undefined behavior. * Setting both \Fhaltreq and \Fresumereq leads to undefined behavior.
* *
@ -942,31 +965,12 @@
#define DMI_DATA0_DATA_OFFSET 0 #define DMI_DATA0_DATA_OFFSET 0
#define DMI_DATA0_DATA_LENGTH 32 #define DMI_DATA0_DATA_LENGTH 32
#define DMI_DATA0_DATA (0xffffffff << DMI_DATA0_DATA_OFFSET) #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_DATA11 0x0f
#define DMI_PROGBUF0 0x20 #define DMI_PROGBUF0 0x20
#define DMI_PROGBUF0_DATA_OFFSET 0 #define DMI_PROGBUF0_DATA_OFFSET 0
#define DMI_PROGBUF0_DATA_LENGTH 32 #define DMI_PROGBUF0_DATA_LENGTH 32
#define DMI_PROGBUF0_DATA (0xffffffff << DMI_PROGBUF0_DATA_OFFSET) #define DMI_PROGBUF0_DATA (0xffffffff << DMI_PROGBUF0_DATA_OFFSET)
#define DMI_PROGBUF1 0x21 #define DMI_PROGBUF15 0x2f
#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_AUTHDATA 0x30 #define DMI_AUTHDATA 0x30
#define DMI_AUTHDATA_DATA_OFFSET 0 #define DMI_AUTHDATA_DATA_OFFSET 0
#define DMI_AUTHDATA_DATA_LENGTH 32 #define DMI_AUTHDATA_DATA_LENGTH 32
@ -1233,93 +1237,6 @@
#define DMI_SBDATA3_DATA_OFFSET 0 #define DMI_SBDATA3_DATA_OFFSET 0
#define DMI_SBDATA3_DATA_LENGTH 32 #define DMI_SBDATA3_DATA_LENGTH 32
#define DMI_SBDATA3_DATA (0xffffffff << DMI_SBDATA3_DATA_OFFSET) #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 #define TRACE 0x728
/* /*
* 1 if the trace buffer has wrapped since the last time \Fdiscard was * 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) #define AC_ACCESS_REGISTER_SIZE (0x7 << AC_ACCESS_REGISTER_SIZE_OFFSET)
/* /*
* When 1, execute the program in the Program Buffer exactly once * 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. * after performing the transfer, if any.
*/ */
#define AC_ACCESS_REGISTER_POSTEXEC_OFFSET 18 #define AC_ACCESS_REGISTER_POSTEXEC_OFFSET 18

View File

@ -3,6 +3,8 @@
enum gdb_regno { enum gdb_regno {
GDB_REGNO_XPR0 = 0, 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_S0 = GDB_REGNO_XPR0 + 8,
GDB_REGNO_S1 = GDB_REGNO_XPR0 + 9, GDB_REGNO_S1 = GDB_REGNO_XPR0 + 9,
GDB_REGNO_XPR31 = GDB_REGNO_XPR0 + 31, GDB_REGNO_XPR31 = GDB_REGNO_XPR0 + 31,
@ -16,6 +18,7 @@ enum gdb_regno {
GDB_REGNO_MISA = CSR_MISA + GDB_REGNO_CSR0, GDB_REGNO_MISA = CSR_MISA + GDB_REGNO_CSR0,
GDB_REGNO_DPC = CSR_DPC + GDB_REGNO_CSR0, GDB_REGNO_DPC = CSR_DPC + GDB_REGNO_CSR0,
GDB_REGNO_DCSR = CSR_DCSR + 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_MSTATUS = CSR_MSTATUS + GDB_REGNO_CSR0,
GDB_REGNO_CSR4095 = GDB_REGNO_CSR0 + 4095, GDB_REGNO_CSR4095 = GDB_REGNO_CSR0 + 4095,
GDB_REGNO_PRIV = 4161, 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; 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) __attribute__ ((unused));
static uint32_t fsw(unsigned int src, unsigned int base, uint16_t offset) 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; 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) __attribute__ ((unused));
static uint32_t lui(unsigned int dest, uint32_t imm) 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; MATCH_LUI;
} }
/*
static uint32_t csrci(unsigned int csr, uint16_t imm) __attribute__ ((unused)); static uint32_t csrci(unsigned int csr, uint16_t imm) __attribute__ ((unused));
static uint32_t csrci(unsigned int csr, uint16_t imm) { static uint32_t csrci(unsigned int csr, uint16_t imm) {
return (csr << 20) | return (csr << 20) |
@ -277,3 +287,9 @@ static uint32_t fence(void)
{ {
return MATCH_FENCE; return MATCH_FENCE;
} }
static uint32_t auipc(unsigned int dest) __attribute__((unused));
static uint32_t auipc(unsigned int dest)
{
return MATCH_AUIPC | (dest << 7);
}

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

@ -0,0 +1,459 @@
#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);
/* Program interface. */
int riscv_program_init(struct riscv_program *p, struct target *target)
{
LOG_DEBUG("riscv_program_init: p=0x%016lx", p);
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");
for(size_t i = 0; i < riscv_debug_buffer_size(p->target); ++i)
LOG_ERROR("ram[%02x]: DASM(0x%08lx) [0x%08lx]", i, p->debug_buffer[i], p->debug_buffer[i]);
abort();
return ERROR_FAIL;
}
if (riscv_program_ebreak(p) != ERROR_OK) {
LOG_ERROR("Unable to write ebreak");
for(size_t i = 0; i < riscv_debug_buffer_size(p->target); ++i)
LOG_ERROR("ram[%02x]: DASM(0x%08lx) [0x%08lx]", i, p->debug_buffer[i], p->debug_buffer[i]);
abort();
return ERROR_FAIL;
}
for (size_t i = 0; i < riscv_debug_buffer_size(p->target); ++i) {
LOG_DEBUG("Executing program 0x%016lx: debug_buffer[%02x] = DASM(0x%08lx)", p, i, p->debug_buffer[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);
p->data_count =
+ riscv_debug_buffer_size(p->target)
- (addr - riscv_debug_buffer_addr(p->target)) / sizeof(p->debug_buffer[0]);
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;
if (addr > riscv_debug_buffer_addr(p->target) + (riscv_debug_buffer_size(p->target) * sizeof(p->debug_buffer[0])))
return;
int off = (addr - riscv_debug_buffer_addr(p->target)) / sizeof(p->debug_buffer[0]);
p->debug_buffer[off] = d;
}
int riscv_program_swr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
{
p->writes_memory = 1;
return riscv_program_insert(p, sw(d, b, offset));
}
int riscv_program_shr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
{
p->writes_memory = 1;
return riscv_program_insert(p, sh(d, b, offset));
}
int riscv_program_sbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
{
p->writes_memory = 1;
return riscv_program_insert(p, sb(d, b, offset));
}
int riscv_program_lwr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
{
p->writes_memory = 1;
return riscv_program_insert(p, lw(d, b, offset));
}
int riscv_program_lhr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
{
p->writes_memory = 1;
return riscv_program_insert(p, lh(d, b, offset));
}
int riscv_program_lbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
{
p->writes_memory = 1;
return riscv_program_insert(p, lb(d, b, offset));
}
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;
return ERROR_OK;
}
int riscv_program_do_restore_register(struct riscv_program *p, enum gdb_regno r)
{
assert(r < RISCV_REGISTER_COUNT);
p->writes_xreg[r] = 1;
return ERROR_OK;
}
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)
{
LOG_DEBUG("instruction_count: %d (p=0x%016lx)", p->instruction_count, p);
if (p->instruction_count + p->data_count + 1 > riscv_debug_buffer_size(p->target)) {
LOG_DEBUG("Unable to insert instruction:");
LOG_DEBUG(" instruction_count=%d", p->instruction_count);
LOG_DEBUG(" data_count =%d", p->data_count);
LOG_DEBUG(" buffer size =%d", 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++;
return ERROR_OK;
}

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

@ -0,0 +1,135 @@
#ifndef TARGET__RISCV__PROGRAM_H
#define TARGET__RISCV__PROGRAM_H
#include "riscv.h"
#define RISCV_MAX_DEBUG_BUFFER_SIZE 32
#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_lxr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
int riscv_program_ldr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
int riscv_program_lwr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
int riscv_program_lhr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
int riscv_program_lbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
int riscv_program_sxr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);
int riscv_program_sdr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);
int riscv_program_swr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);
int riscv_program_shr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);
int riscv_program_sbr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);
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

File diff suppressed because it is too large Load Diff

View File

@ -618,9 +618,17 @@ static int riscv_poll_hart(struct target *target, int hartid)
RISCV_INFO(r); RISCV_INFO(r);
LOG_DEBUG("polling hart %d", hartid); 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); 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)) if (riscv_was_halted(target) || !riscv_is_halted(target))
return 0; return 0;
@ -696,7 +704,9 @@ int riscv_openocd_halt(struct target *target)
if (out != ERROR_OK) if (out != ERROR_OK)
return out; 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; return out;
} }
@ -717,6 +727,7 @@ int riscv_openocd_resume(
return out; return out;
target->state = TARGET_RUNNING; target->state = TARGET_RUNNING;
riscv_openocd_poll(target);
return out; return out;
} }
@ -738,7 +749,7 @@ int riscv_openocd_step(
return out; return out;
/* step_rtos_hart blocks until the hart has actually stepped, but we /* 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; target->state = TARGET_RUNNING;
riscv_openocd_poll(target); riscv_openocd_poll(target);
@ -756,55 +767,15 @@ void riscv_info_init(riscv_info_t *r)
r->rtos_enabled = true; r->rtos_enabled = true;
for (size_t h = 0; h < RISCV_MAX_HARTS; ++h) { for (size_t h = 0; h < RISCV_MAX_HARTS; ++h) {
/* FIXME: I need to rip out Tim's probing sequence, as it r->xlen[h] = -1;
* disrupts the running code. For now, I'm just hard-coding
* XLEN to 64 for all cores at reset. */
r->xlen[h] = 64;
r->hart_state[h] = RISCV_HART_UNKNOWN; r->hart_state[h] = RISCV_HART_UNKNOWN;
r->debug_buffer_addr[h] = -1;
for (size_t e = 0; e < RISCV_MAX_REGISTERS; ++e) for (size_t e = 0; e < RISCV_MAX_REGISTERS; ++e)
r->valid_saved_registers[h][e] = false; 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) int riscv_halt_all_harts(struct target *target)
{ {
if (riscv_rtos_enabled(target)) { if (riscv_rtos_enabled(target)) {
@ -837,6 +808,8 @@ int riscv_halt_one_hart(struct target *target, int hartid)
} }
r->halt_current_hart(target); 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; return ERROR_OK;
} }
@ -894,6 +867,10 @@ int riscv_step_rtos_hart(struct target *target)
assert(r->hart_state[hartid] == RISCV_HART_HALTED); assert(r->hart_state[hartid] == RISCV_HART_HALTED);
r->on_step(target); r->on_step(target);
r->step_current_hart(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; return ERROR_OK;
} }
@ -924,11 +901,16 @@ bool riscv_rtos_enabled(const struct target *target)
void riscv_set_current_hartid(struct target *target, int hartid) void riscv_set_current_hartid(struct target *target, int hartid)
{ {
RISCV_INFO(r); RISCV_INFO(r);
register_cache_invalidate(target->reg_cache);
r->current_hartid = hartid; r->current_hartid = hartid;
r->select_current_hart(target); r->select_current_hart(target);
/* This might get called during init, in which case we shouldn't be
* setting up the register cache. */
if (!target_was_examined(target))
return;
/* Update the register list's widths. */ /* Update the register list's widths. */
register_cache_invalidate(target->reg_cache);
for (size_t i = 0; i < GDB_REGNO_COUNT; ++i) { for (size_t i = 0; i < GDB_REGNO_COUNT; ++i) {
struct reg *reg = &target->reg_cache->reg_list[i]; struct reg *reg = &target->reg_cache->reg_list[i];
@ -979,14 +961,24 @@ bool riscv_has_register(struct target *target, int hartid, int regid)
return 1; 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); RISCV_INFO(r);
LOG_DEBUG("writing register %d on hart %d", regid, hartid); LOG_DEBUG("writing register %d on hart %d", regid, hartid);
return r->set_register(target, hartid, regid, value); 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); RISCV_INFO(r);
LOG_DEBUG("reading register %d on hart %d", regid, hartid); LOG_DEBUG("reading register %d on hart %d", regid, hartid);
@ -1025,3 +1017,51 @@ int riscv_count_triggers_of_hart(struct target *target, int hartid)
assert(hartid < riscv_count_harts(target)); assert(hartid < riscv_count_harts(target));
return r->trigger_count[hartid]; 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 #ifndef RISCV_H
#define RISCV_H #define RISCV_H
struct riscv_program;
#include <stdint.h>
#include "opcodes.h" #include "opcodes.h"
#include "gdb_regs.h" #include "gdb_regs.h"
@ -16,6 +19,8 @@ extern struct target_type riscv013_target;
* Definitions shared by code supporting all RISC-V versions. * Definitions shared by code supporting all RISC-V versions.
*/ */
typedef uint64_t riscv_reg_t; typedef uint64_t riscv_reg_t;
typedef uint32_t riscv_insn_t;
typedef int64_t riscv_addr_t;
enum riscv_hart_state { enum riscv_hart_state {
RISCV_HART_UNKNOWN, RISCV_HART_UNKNOWN,
@ -68,6 +73,12 @@ typedef struct {
/* The number of triggers per hart. */ /* The number of triggers per hart. */
int trigger_count[RISCV_MAX_HARTS]; 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 /* Helper functions that target the various RISC-V debug spec
* implementations. */ * implementations. */
riscv_reg_t (*get_register)(struct target *, int, int); riscv_reg_t (*get_register)(struct target *, int, int);
@ -81,6 +92,11 @@ typedef struct {
void (*on_resume)(struct target *target); void (*on_resume)(struct target *target);
void (*on_step)(struct target *target); void (*on_step)(struct target *target);
enum riscv_halt_reason (*halt_reason)(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; } riscv_info_t;
/* Everything needs the RISC-V specific info structure, so here's a nice macro /* 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. */ /* Initializes the shared RISC-V structure. */
void riscv_info_init(riscv_info_t *r); 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 /* 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 * all the enabled harts, which when running in RTOS mode is all the harts on
* the system. */ * 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 /* Returns the value of the given register on the given hart. 32-bit registers
* are zero extended to 64 bits. */ * are zero extended to 64 bits. */
void riscv_set_register(struct target *target, int hid, enum gdb_regno rid, uint64_t v); void riscv_set_register(struct target *target, enum gdb_regno i, riscv_reg_t v);
riscv_reg_t riscv_get_register(struct target *target, int hid, enum gdb_regno rid); 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 /* Checks the state of the current hart -- "is_halted" checks the actual
* on-device register, while "was_halted" checks the machine's state. */ * 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(struct target *target);
int riscv_count_triggers_of_hart(struct target *target, int hartid); 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 #endif