Upstream tons of RISC-V changes.

These are all the changes from https://github.com/riscv/riscv-openocd
(approximately 91dc0c0c) made just to src/target/riscv/*. Some of the
new code is disabled because it requires some other target-independent
changes which I didn't want to include here.

Built like this, OpenOCD passes:
* All single-RV32 tests against spike.
* All single-RV64 tests against spike.
* Enough HiFive1 tests. (I suspect the failures are due to the test
suite rotting.)
* Many dual-RV32 (-rtos hwthread) against spike.
* Many dual-RV64 (-rtos hwthread) against spike.

I suspect this is an overall improvement compared to what's in mainline
right now, and it gets me a lot closer to getting all the riscv-openocd
work upstreamed.

Change-Id: Ide2f80c9397400780ff6780d78a206bc6a6e2f98
Signed-off-by: Tim Newsome <tim@sifive.com>
Reviewed-on: http://openocd.zylin.com/5821
Tested-by: jenkins
Reviewed-by: Jan Matyas <matyas@codasip.com>
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-by: Karl Palsson <karlp@tweak.net.au>
This commit is contained in:
Tim Newsome 2020-08-19 13:24:56 -07:00 committed by Tomas Vanek
parent fc7edd57ac
commit b68674a1da
16 changed files with 7148 additions and 2537 deletions

View File

@ -9714,8 +9714,31 @@ This is used to access 64-bit floating point registers on 32-bit targets.
@end deffn
@deffn Command {riscv set_prefer_sba} on|off
When on, prefer to use System Bus Access to access memory. When off, prefer to
use the Program Buffer to access memory.
When on, prefer to use System Bus Access to access memory. When off (default),
prefer to use the Program Buffer to access memory.
@end deffn
@deffn Command {riscv set_enable_virtual} on|off
When on, memory accesses are performed on physical or virtual memory depending
on the current system configuration. When off (default), all memory accessses are performed
on physical memory.
@end deffn
@deffn Command {riscv set_enable_virt2phys} on|off
When on (default), memory accesses are performed on physical or virtual memory
depending on the current satp configuration. When off, all memory accessses are
performed on physical memory.
@end deffn
@deffn Command {riscv resume_order} normal|reversed
Some software assumes all harts are executing nearly continuously. Such
software may be sensitive to the order that harts are resumed in. On harts
that don't support hasel, this option allows the user to choose the order the
harts are resumed in. If you are using this option, it's probably masking a
race condition problem in your code.
Normal order is from lowest hart index to highest. This is the default
behavior. Reversed order is from highest hart index to lowest.
@end deffn
@deffn Command {riscv set_ir} (@option{idcode}|@option{dtmcs}|@option{dmi}) [value]
@ -9729,6 +9752,26 @@ When utilizing version 0.11 of the RISC-V Debug Specification,
and DBUS registers, respectively.
@end deffn
@deffn Command {riscv use_bscan_tunnel} value
Enable or disable use of a BSCAN tunnel to reach DM. Supply the width of
the DM transport TAP's instruction register to enable. Supply a value of 0 to disable.
@end deffn
@deffn Command {riscv set_ebreakm} on|off
Control dcsr.ebreakm. When on (default), M-mode ebreak instructions trap to
OpenOCD. When off, they generate a breakpoint exception handled internally.
@end deffn
@deffn Command {riscv set_ebreaks} on|off
Control dcsr.ebreaks. When on (default), S-mode ebreak instructions trap to
OpenOCD. When off, they generate a breakpoint exception handled internally.
@end deffn
@deffn Command {riscv set_ebreaku} on|off
Control dcsr.ebreaku. When on (default), U-mode ebreak instructions trap to
OpenOCD. When off, they generate a breakpoint exception handled internally.
@end deffn
@subsection RISC-V Authentication Commands
The following commands can be used to authenticate to a RISC-V system. Eg. a
@ -9752,7 +9795,7 @@ Write the 32-bit value to authdata.
The following commands allow direct access to the Debug Module Interface, which
can be used to interact with custom debug features.
@deffn Command {riscv dmi_read}
@deffn Command {riscv dmi_read} address
Perform a 32-bit DMI read at address, returning the value.
@end deffn

View File

@ -154,6 +154,7 @@ extern int debug_level;
#define ERROR_WAIT (-5)
/* ERROR_TIMEOUT is already taken by winerror.h. */
#define ERROR_TIMEOUT_REACHED (-6)
#define ERROR_NOT_IMPLEMENTED (-7)
#endif /* OPENOCD_HELPER_LOG_H */

View File

@ -1,3 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef TARGET__RISCV__ASM_H
#define TARGET__RISCV__ASM_H

View File

@ -1,3 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
@ -9,21 +11,53 @@
#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1)))
#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask)))
#define DTM_DMI_MAX_ADDRESS_LENGTH ((1<<DTM_DTMCS_ABITS_LENGTH)-1)
#define DMI_SCAN_MAX_BIT_LENGTH (DTM_DMI_MAX_ADDRESS_LENGTH + DTM_DMI_DATA_LENGTH + DTM_DMI_OP_LENGTH)
#define DMI_SCAN_BUF_SIZE (DIV_ROUND_UP(DMI_SCAN_MAX_BIT_LENGTH, 8))
static void dump_field(int idle, const struct scan_field *field);
struct riscv_batch *riscv_batch_alloc(struct target *target, size_t scans, size_t idle)
{
scans += 4;
struct riscv_batch *out = calloc(1, sizeof(*out));
if (!out)
goto error0;
out->target = target;
out->allocated_scans = scans;
out->idle_count = idle;
out->data_out = malloc(sizeof(*out->data_out) * (scans) * sizeof(uint64_t));
out->data_in = malloc(sizeof(*out->data_in) * (scans) * sizeof(uint64_t));
out->data_out = malloc(sizeof(*out->data_out) * (scans) * DMI_SCAN_BUF_SIZE);
if (!out->data_out)
goto error1;
out->data_in = malloc(sizeof(*out->data_in) * (scans) * DMI_SCAN_BUF_SIZE);
if (!out->data_in)
goto error2;
out->fields = malloc(sizeof(*out->fields) * (scans));
if (!out->fields)
goto error3;
if (bscan_tunnel_ir_width != 0) {
out->bscan_ctxt = malloc(sizeof(*out->bscan_ctxt) * (scans));
if (!out->bscan_ctxt)
goto error4;
}
out->last_scan = RISCV_SCAN_TYPE_INVALID;
out->read_keys = malloc(sizeof(*out->read_keys) * (scans));
if (!out->read_keys)
goto error5;
return out;
error5:
free(out->bscan_ctxt);
error4:
free(out->fields);
error3:
free(out->data_in);
error2:
free(out->data_out);
error1:
free(out);
error0:
return NULL;
}
void riscv_batch_free(struct riscv_batch *batch)
@ -31,6 +65,8 @@ void riscv_batch_free(struct riscv_batch *batch)
free(batch->data_in);
free(batch->data_out);
free(batch->fields);
free(batch->bscan_ctxt);
free(batch->read_keys);
free(batch);
}
@ -51,7 +87,11 @@ int riscv_batch_run(struct riscv_batch *batch)
riscv_batch_add_nop(batch);
for (size_t i = 0; i < batch->used_scans; ++i) {
jtag_add_dr_scan(batch->target->tap, 1, batch->fields + i, TAP_IDLE);
if (bscan_tunnel_ir_width != 0)
riscv_add_bscan_tunneled_scan(batch->target, batch->fields+i, batch->bscan_ctxt+i);
else
jtag_add_dr_scan(batch->target->tap, 1, batch->fields + i, TAP_IDLE);
if (batch->idle_count > 0)
jtag_add_runtest(batch->idle_count, TAP_IDLE);
}
@ -61,6 +101,12 @@ int riscv_batch_run(struct riscv_batch *batch)
return ERROR_FAIL;
}
if (bscan_tunnel_ir_width != 0) {
/* need to right-shift "in" by one bit, because of clock skew between BSCAN TAP and DM TAP */
for (size_t i = 0; i < batch->used_scans; ++i)
buffer_shr((batch->fields + i)->in_value, DMI_SCAN_BUF_SIZE, 1);
}
for (size_t i = 0; i < batch->used_scans; ++i)
dump_field(batch->idle_count, batch->fields + i);
@ -72,8 +118,8 @@ void riscv_batch_add_dmi_write(struct riscv_batch *batch, unsigned address, uint
assert(batch->used_scans < batch->allocated_scans);
struct scan_field *field = batch->fields + batch->used_scans;
field->num_bits = riscv_dmi_write_u64_bits(batch->target);
field->out_value = (void *)(batch->data_out + batch->used_scans * sizeof(uint64_t));
field->in_value = (void *)(batch->data_in + batch->used_scans * sizeof(uint64_t));
field->out_value = (void *)(batch->data_out + batch->used_scans * DMI_SCAN_BUF_SIZE);
field->in_value = (void *)(batch->data_in + batch->used_scans * DMI_SCAN_BUF_SIZE);
riscv_fill_dmi_write_u64(batch->target, (char *)field->out_value, address, data);
riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value);
batch->last_scan = RISCV_SCAN_TYPE_WRITE;
@ -85,35 +131,35 @@ size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, unsigned address)
assert(batch->used_scans < batch->allocated_scans);
struct scan_field *field = batch->fields + batch->used_scans;
field->num_bits = riscv_dmi_write_u64_bits(batch->target);
field->out_value = (void *)(batch->data_out + batch->used_scans * sizeof(uint64_t));
field->in_value = (void *)(batch->data_in + batch->used_scans * sizeof(uint64_t));
field->out_value = (void *)(batch->data_out + batch->used_scans * DMI_SCAN_BUF_SIZE);
field->in_value = (void *)(batch->data_in + batch->used_scans * DMI_SCAN_BUF_SIZE);
riscv_fill_dmi_read_u64(batch->target, (char *)field->out_value, address);
riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value);
batch->last_scan = RISCV_SCAN_TYPE_READ;
batch->used_scans++;
/* FIXME We get the read response back on the next scan. For now I'm
* just sticking a NOP in there, but this should be coalesced away. */
riscv_batch_add_nop(batch);
batch->read_keys[batch->read_keys_used] = batch->used_scans - 1;
batch->read_keys[batch->read_keys_used] = batch->used_scans;
return batch->read_keys_used++;
}
uint64_t riscv_batch_get_dmi_read(struct riscv_batch *batch, size_t key)
unsigned riscv_batch_get_dmi_read_op(struct riscv_batch *batch, size_t key)
{
assert(key < batch->read_keys_used);
size_t index = batch->read_keys[key];
assert(index <= batch->used_scans);
uint8_t *base = batch->data_in + 8 * index;
return base[0] |
((uint64_t) base[1]) << 8 |
((uint64_t) base[2]) << 16 |
((uint64_t) base[3]) << 24 |
((uint64_t) base[4]) << 32 |
((uint64_t) base[5]) << 40 |
((uint64_t) base[6]) << 48 |
((uint64_t) base[7]) << 56;
uint8_t *base = batch->data_in + DMI_SCAN_BUF_SIZE * index;
/* extract "op" field from the DMI read result */
return (unsigned)buf_get_u32(base, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH);
}
uint32_t riscv_batch_get_dmi_read_data(struct riscv_batch *batch, size_t key)
{
assert(key < batch->read_keys_used);
size_t index = batch->read_keys[key];
assert(index <= batch->used_scans);
uint8_t *base = batch->data_in + DMI_SCAN_BUF_SIZE * index;
/* extract "data" field from the DMI read result */
return buf_get_u32(base, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH);
}
void riscv_batch_add_nop(struct riscv_batch *batch)
@ -121,8 +167,8 @@ void riscv_batch_add_nop(struct riscv_batch *batch)
assert(batch->used_scans < batch->allocated_scans);
struct scan_field *field = batch->fields + batch->used_scans;
field->num_bits = riscv_dmi_write_u64_bits(batch->target);
field->out_value = (void *)(batch->data_out + batch->used_scans * sizeof(uint64_t));
field->in_value = (void *)(batch->data_in + batch->used_scans * sizeof(uint64_t));
field->out_value = (void *)(batch->data_out + batch->used_scans * DMI_SCAN_BUF_SIZE);
field->in_value = (void *)(batch->data_in + batch->used_scans * DMI_SCAN_BUF_SIZE);
riscv_fill_dmi_nop_u64(batch->target, (char *)field->out_value);
riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value);
batch->last_scan = RISCV_SCAN_TYPE_NOP;
@ -151,13 +197,17 @@ void dump_field(int idle, const struct scan_field *field)
log_printf_lf(LOG_LVL_DEBUG,
__FILE__, __LINE__, __PRETTY_FUNCTION__,
"%db %di %s %08x @%02x -> %s %08x @%02x",
field->num_bits, idle,
op_string[out_op], out_data, out_address,
status_string[in_op], in_data, in_address);
"%db %s %08x @%02x -> %s %08x @%02x; %di",
field->num_bits, op_string[out_op], out_data, out_address,
status_string[in_op], in_data, in_address, idle);
} else {
log_printf_lf(LOG_LVL_DEBUG,
__FILE__, __LINE__, __PRETTY_FUNCTION__, "%db %di %s %08x @%02x -> ?",
field->num_bits, idle, op_string[out_op], out_data, out_address);
__FILE__, __LINE__, __PRETTY_FUNCTION__, "%db %s %08x @%02x -> ?; %di",
field->num_bits, op_string[out_op], out_data, out_address, idle);
}
}
size_t riscv_batch_available_scans(struct riscv_batch *batch)
{
return batch->allocated_scans - batch->used_scans - 4;
}

View File

@ -1,8 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef TARGET__RISCV__SCANS_H
#define TARGET__RISCV__SCANS_H
#include "target/target.h"
#include "jtag/jtag.h"
#include "riscv.h"
enum riscv_scan_type {
RISCV_SCAN_TYPE_INVALID,
@ -27,6 +30,11 @@ struct riscv_batch {
uint8_t *data_in;
struct scan_field *fields;
/* If in BSCAN mode, this field will be allocated (one per scan),
and utilized to tunnel all the scans in the batch. If not in
BSCAN mode, this field is unallocated and stays NULL */
riscv_bscan_tunneled_scan_context_t *bscan_ctxt;
/* In JTAG we scan out the previous value's output when performing a
* scan. This is a pain for users, so we just provide them the
* illusion of not having to do this by eliding all but the last NOP.
@ -54,11 +62,16 @@ int riscv_batch_run(struct riscv_batch *batch);
void riscv_batch_add_dmi_write(struct riscv_batch *batch, unsigned address, uint64_t data);
/* DMI reads must be handled in two parts: the first one schedules a read and
* provides a key, the second one actually obtains the value of that read .*/
* provides a key, the second one actually obtains the result of the read -
* status (op) and the actual data. */
size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, unsigned address);
uint64_t riscv_batch_get_dmi_read(struct riscv_batch *batch, size_t key);
unsigned riscv_batch_get_dmi_read_op(struct riscv_batch *batch, size_t key);
uint32_t riscv_batch_get_dmi_read_data(struct riscv_batch *batch, size_t key);
/* Scans in a NOP. */
void riscv_batch_add_nop(struct riscv_batch *batch);
/* Returns the number of available scans. */
size_t riscv_batch_available_scans(struct riscv_batch *batch);
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef TARGET__RISCV__GDB_REGS_H
#define TARGET__RISCV__GDB_REGS_H
@ -21,6 +23,7 @@ enum gdb_regno {
GDB_REGNO_A3,
GDB_REGNO_A4,
GDB_REGNO_A5,
GDB_REGNO_XPR15 = GDB_REGNO_A5,
GDB_REGNO_A6,
GDB_REGNO_A7,
GDB_REGNO_S2,
@ -75,16 +78,37 @@ enum gdb_regno {
GDB_REGNO_FT11,
GDB_REGNO_FPR31 = GDB_REGNO_FT11,
GDB_REGNO_CSR0 = 65,
GDB_REGNO_VSTART = CSR_VSTART + GDB_REGNO_CSR0,
GDB_REGNO_VXSAT = CSR_VXSAT + GDB_REGNO_CSR0,
GDB_REGNO_VXRM = CSR_VXRM + GDB_REGNO_CSR0,
GDB_REGNO_VLENB = CSR_VLENB + GDB_REGNO_CSR0,
GDB_REGNO_VL = CSR_VL + GDB_REGNO_CSR0,
GDB_REGNO_VTYPE = CSR_VTYPE + GDB_REGNO_CSR0,
GDB_REGNO_TSELECT = CSR_TSELECT + GDB_REGNO_CSR0,
GDB_REGNO_TDATA1 = CSR_TDATA1 + GDB_REGNO_CSR0,
GDB_REGNO_TDATA2 = CSR_TDATA2 + GDB_REGNO_CSR0,
GDB_REGNO_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_DSCRATCH0 = CSR_DSCRATCH0 + GDB_REGNO_CSR0,
GDB_REGNO_MSTATUS = CSR_MSTATUS + GDB_REGNO_CSR0,
GDB_REGNO_MEPC = CSR_MEPC + GDB_REGNO_CSR0,
GDB_REGNO_MCAUSE = CSR_MCAUSE + GDB_REGNO_CSR0,
GDB_REGNO_SATP = CSR_SATP + GDB_REGNO_CSR0,
GDB_REGNO_CSR4095 = GDB_REGNO_CSR0 + 4095,
GDB_REGNO_PRIV = 4161,
/* It's still undecided what register numbers GDB will actually use for
* these. See
* https://groups.google.com/a/groups.riscv.org/d/msg/sw-dev/7lQYiTUN9Ms/gTxGhzaYBQAJ
*/
GDB_REGNO_V0, GDB_REGNO_V1, GDB_REGNO_V2, GDB_REGNO_V3,
GDB_REGNO_V4, GDB_REGNO_V5, GDB_REGNO_V6, GDB_REGNO_V7,
GDB_REGNO_V8, GDB_REGNO_V9, GDB_REGNO_V10, GDB_REGNO_V11,
GDB_REGNO_V12, GDB_REGNO_V13, GDB_REGNO_V14, GDB_REGNO_V15,
GDB_REGNO_V16, GDB_REGNO_V17, GDB_REGNO_V18, GDB_REGNO_V19,
GDB_REGNO_V20, GDB_REGNO_V21, GDB_REGNO_V22, GDB_REGNO_V23,
GDB_REGNO_V24, GDB_REGNO_V25, GDB_REGNO_V26, GDB_REGNO_V27,
GDB_REGNO_V28, GDB_REGNO_V29, GDB_REGNO_V30, GDB_REGNO_V31,
GDB_REGNO_COUNT
};

View File

@ -1,3 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "encoding.h"
#define ZERO 0
@ -143,6 +145,18 @@ 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 csrrci(unsigned int rd, unsigned int zimm, unsigned int csr) __attribute__ ((unused));
static uint32_t csrrci(unsigned int rd, unsigned int zimm, unsigned int csr)
{
return (csr << 20) | (zimm << 15) | (rd << 7) | MATCH_CSRRCI;
}
static uint32_t csrrsi(unsigned int rd, unsigned int zimm, unsigned int csr) __attribute__ ((unused));
static uint32_t csrrsi(unsigned int rd, unsigned int zimm, unsigned int csr)
{
return (csr << 20) | (zimm << 15) | (rd << 7) | MATCH_CSRRSI;
}
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)
{
@ -311,3 +325,33 @@ static uint32_t auipc(unsigned int dest)
{
return MATCH_AUIPC | (dest << 7);
}
static uint32_t vsetvli(unsigned int dest, unsigned int src, uint16_t imm) __attribute__((unused));
static uint32_t vsetvli(unsigned int dest, unsigned int src, uint16_t imm)
{
return (bits(imm, 10, 0) << 20) |
(src << 15) |
(dest << 7) |
MATCH_VSETVLI;
}
static uint32_t vmv_x_s(unsigned int rd, unsigned int vs2) __attribute__((unused));
static uint32_t vmv_x_s(unsigned int rd, unsigned int vs2)
{
return (vs2 << 20) | (rd << 7) | MATCH_VMV_X_S;
}
static uint32_t vmv_s_x(unsigned int vd, unsigned int vs2) __attribute__((unused));
static uint32_t vmv_s_x(unsigned int vd, unsigned int rs1)
{
return (rs1 << 15) | (vd << 7) | MATCH_VMV_S_X;
}
static uint32_t vslide1down_vx(unsigned int vd, unsigned int vs2,
unsigned int rs1, unsigned int vm) __attribute__((unused));
static uint32_t vslide1down_vx(unsigned int vd, unsigned int vs2,
unsigned int rs1, unsigned int vm)
{
return (vm << 25) | (vs2 << 20) | (rs1 << 15) | (vd << 7) |
MATCH_VSLIDE1DOWN_VX;
}

View File

@ -1,3 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
@ -30,7 +32,7 @@ int riscv_program_init(struct riscv_program *p, struct target *target)
int riscv_program_write(struct riscv_program *program)
{
for (unsigned i = 0; i < program->instruction_count; ++i) {
LOG_DEBUG("%p: debug_buffer[%02x] = DASM(0x%08x)", program, i, program->debug_buffer[i]);
LOG_DEBUG("debug_buffer[%02x] = DASM(0x%08x)", i, program->debug_buffer[i]);
if (riscv_write_debug_buffer(program->target, i,
program->debug_buffer[i]) != ERROR_OK)
return ERROR_FAIL;
@ -56,7 +58,8 @@ int riscv_program_exec(struct riscv_program *p, struct target *t)
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]", (int)i, (long)p->debug_buffer[i], (long)p->debug_buffer[i]);
LOG_ERROR("ram[%02x]: DASM(0x%08" PRIx32 ") [0x%08" PRIx32 "]",
(int)i, p->debug_buffer[i], p->debug_buffer[i]);
return ERROR_FAIL;
}
@ -79,6 +82,11 @@ int riscv_program_exec(struct riscv_program *p, struct target *t)
return ERROR_OK;
}
int riscv_program_sdr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
{
return riscv_program_insert(p, sd(d, b, offset));
}
int riscv_program_swr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
{
return riscv_program_insert(p, sw(d, b, offset));
@ -94,6 +102,11 @@ int riscv_program_sbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno
return riscv_program_insert(p, sb(d, b, offset));
}
int riscv_program_ldr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
{
return riscv_program_insert(p, ld(d, b, offset));
}
int riscv_program_lwr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
{
return riscv_program_insert(p, lw(d, b, offset));
@ -109,6 +122,18 @@ int riscv_program_lbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno
return riscv_program_insert(p, lb(d, b, offset));
}
int riscv_program_csrrsi(struct riscv_program *p, enum gdb_regno d, unsigned int z, enum gdb_regno csr)
{
assert(csr >= GDB_REGNO_CSR0 && csr <= GDB_REGNO_CSR4095);
return riscv_program_insert(p, csrrsi(d, z, csr - GDB_REGNO_CSR0));
}
int riscv_program_csrrci(struct riscv_program *p, enum gdb_regno d, unsigned int z, enum gdb_regno csr)
{
assert(csr >= GDB_REGNO_CSR0 && csr <= GDB_REGNO_CSR4095);
return riscv_program_insert(p, csrrci(d, z, csr - GDB_REGNO_CSR0));
}
int riscv_program_csrr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno csr)
{
assert(csr >= GDB_REGNO_CSR0 && csr <= GDB_REGNO_CSR4095);

View File

@ -1,3 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef TARGET__RISCV__PROGRAM_H
#define TARGET__RISCV__PROGRAM_H
@ -55,14 +57,18 @@ int riscv_program_save_to_dscratch(struct riscv_program *p, enum gdb_regno to_sa
/* Helpers to assemble various instructions. Return 0 on success. These might
* assemble into a multi-instruction sequence that overwrites some other
* register, but those will be properly saved and restored. */
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_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_csrrsi(struct riscv_program *p, enum gdb_regno d, unsigned int z, enum gdb_regno csr);
int riscv_program_csrrci(struct riscv_program *p, enum gdb_regno d, unsigned int z, enum gdb_regno csr);
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);

View File

@ -1,3 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Support for RISC-V, debug version 0.11. This was never an officially adopted
* spec, but SiFive made some silicon that uses it.
@ -204,7 +206,6 @@ typedef struct {
* before the interrupt is cleared. */
unsigned int interrupt_high_delay;
bool need_strict_step;
bool never_halted;
} riscv011_info_t;
@ -519,6 +520,8 @@ typedef struct {
static scans_t *scans_new(struct target *target, unsigned int scan_count)
{
scans_t *scans = malloc(sizeof(scans_t));
if (!scans)
goto error0;
scans->scan_count = scan_count;
/* This code also gets called before xlen is detected. */
if (riscv_xlen(target))
@ -527,10 +530,25 @@ static scans_t *scans_new(struct target *target, unsigned int scan_count)
scans->scan_size = 2 + 128 / 8;
scans->next_scan = 0;
scans->in = calloc(scans->scan_size, scans->scan_count);
if (!scans->in)
goto error1;
scans->out = calloc(scans->scan_size, scans->scan_count);
if (!scans->out)
goto error2;
scans->field = calloc(scans->scan_count, sizeof(struct scan_field));
if (!scans->field)
goto error3;
scans->target = target;
return scans;
error3:
free(scans->out);
error2:
free(scans->in);
error1:
free(scans);
error0:
return NULL;
}
static scans_t *scans_delete(scans_t *scans)
@ -844,6 +862,8 @@ static int cache_write(struct target *target, unsigned int address, bool run)
LOG_DEBUG("enter");
riscv011_info_t *info = get_info(target);
scans_t *scans = scans_new(target, info->dramsize + 2);
if (!scans)
return ERROR_FAIL;
unsigned int last = info->dramsize;
for (unsigned int i = 0; i < info->dramsize; i++) {
@ -1012,7 +1032,7 @@ static int wait_for_state(struct target *target, enum target_state state)
}
}
static int read_csr(struct target *target, uint64_t *value, uint32_t csr)
static int read_remote_csr(struct target *target, uint64_t *value, uint32_t csr)
{
riscv011_info_t *info = get_info(target);
cache_set32(target, 0, csrr(S0, csr));
@ -1034,7 +1054,7 @@ static int read_csr(struct target *target, uint64_t *value, uint32_t csr)
return ERROR_OK;
}
static int write_csr(struct target *target, uint32_t csr, uint64_t value)
static int write_remote_csr(struct target *target, uint32_t csr, uint64_t value)
{
LOG_DEBUG("csr 0x%x <- 0x%" PRIx64, csr, value);
cache_set_load(target, 0, S0, SLOT0);
@ -1062,7 +1082,7 @@ static int maybe_read_tselect(struct target *target)
riscv011_info_t *info = get_info(target);
if (info->tselect_dirty) {
int result = read_csr(target, &info->tselect, CSR_TSELECT);
int result = read_remote_csr(target, &info->tselect, CSR_TSELECT);
if (result != ERROR_OK)
return result;
info->tselect_dirty = false;
@ -1076,7 +1096,7 @@ static int maybe_write_tselect(struct target *target)
riscv011_info_t *info = get_info(target);
if (!info->tselect_dirty) {
int result = write_csr(target, CSR_TSELECT, info->tselect);
int result = write_remote_csr(target, CSR_TSELECT, info->tselect);
if (result != ERROR_OK)
return result;
info->tselect_dirty = true;
@ -1115,7 +1135,10 @@ static int execute_resume(struct target *target, bool step)
}
}
info->dcsr |= DCSR_EBREAKM | DCSR_EBREAKH | DCSR_EBREAKS | DCSR_EBREAKU;
info->dcsr = set_field(info->dcsr, DCSR_EBREAKM, riscv_ebreakm);
info->dcsr = set_field(info->dcsr, DCSR_EBREAKS, riscv_ebreaks);
info->dcsr = set_field(info->dcsr, DCSR_EBREAKU, riscv_ebreaku);
info->dcsr = set_field(info->dcsr, DCSR_EBREAKH, 1);
info->dcsr &= ~DCSR_HALT;
if (step)
@ -1255,7 +1278,7 @@ static int register_write(struct target *target, unsigned int number,
if (number == S0) {
cache_set_load(target, 0, S0, SLOT0);
cache_set32(target, 1, csrw(S0, CSR_DSCRATCH));
cache_set32(target, 1, csrw(S0, CSR_DSCRATCH0));
cache_set_jump(target, 2);
} else if (number == S1) {
cache_set_load(target, 0, S0, SLOT0);
@ -1384,25 +1407,6 @@ static int halt(struct target *target)
return ERROR_OK;
}
static int init_target(struct command_context *cmd_ctx,
struct target *target)
{
LOG_DEBUG("init");
riscv_info_t *generic_info = (riscv_info_t *) target->arch_info;
generic_info->get_register = get_register;
generic_info->set_register = set_register;
generic_info->version_specific = calloc(1, sizeof(riscv011_info_t));
if (!generic_info->version_specific)
return ERROR_FAIL;
/* Assume 32-bit until we discover the real value in examine(). */
generic_info->xlen[0] = 32;
riscv_init_registers(target);
return ERROR_OK;
}
static void deinit_target(struct target *target)
{
LOG_DEBUG("riscv_deinit_target()");
@ -1413,8 +1417,6 @@ static void deinit_target(struct target *target)
static int strict_step(struct target *target, bool announce)
{
riscv011_info_t *info = get_info(target);
LOG_DEBUG("enter");
struct watchpoint *watchpoint = target->watchpoints;
@ -1433,16 +1435,12 @@ static int strict_step(struct target *target, bool announce)
watchpoint = watchpoint->next;
}
info->need_strict_step = false;
return ERROR_OK;
}
static int step(struct target *target, int current, target_addr_t address,
int handle_breakpoints)
{
riscv011_info_t *info = get_info(target);
jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
if (!current) {
@ -1455,7 +1453,7 @@ static int step(struct target *target, int current, target_addr_t address,
return result;
}
if (info->need_strict_step || handle_breakpoints) {
if (handle_breakpoints) {
int result = strict_step(target, true);
if (result != ERROR_OK)
return result;
@ -1486,7 +1484,6 @@ static int examine(struct target *target)
}
RISCV_INFO(r);
r->hart_count = 1;
riscv011_info_t *info = get_info(target);
info->addrbits = get_field(dtmcontrol, DTMCONTROL_ADDRBITS);
@ -1570,11 +1567,11 @@ static int examine(struct target *target)
}
LOG_DEBUG("Discovered XLEN is %d", riscv_xlen(target));
if (read_csr(target, &r->misa[0], CSR_MISA) != ERROR_OK) {
if (read_remote_csr(target, &r->misa[0], CSR_MISA) != ERROR_OK) {
const unsigned old_csr_misa = 0xf10;
LOG_WARNING("Failed to read misa at 0x%x; trying 0x%x.", CSR_MISA,
old_csr_misa);
if (read_csr(target, &r->misa[0], old_csr_misa) != ERROR_OK) {
if (read_remote_csr(target, &r->misa[0], old_csr_misa) != ERROR_OK) {
/* Maybe this is an old core that still has $misa at the old
* address. */
LOG_ERROR("Failed to read misa at 0x%x.", old_csr_misa);
@ -1606,6 +1603,8 @@ static riscv_error_t handle_halt_routine(struct target *target)
riscv011_info_t *info = get_info(target);
scans_t *scans = scans_new(target, 256);
if (!scans)
return RE_FAIL;
/* Read all GPRs as fast as we can, because gdb is going to ask for them
* anyway. Reading them one at a time is much slower. */
@ -1634,7 +1633,7 @@ static riscv_error_t handle_halt_routine(struct target *target)
scans_add_read(scans, SLOT0, false);
/* Read S0 from dscratch */
unsigned int csr[] = {CSR_DSCRATCH, CSR_DPC, CSR_DCSR};
unsigned int csr[] = {CSR_DSCRATCH0, CSR_DPC, CSR_DCSR};
for (unsigned int i = 0; i < DIM(csr); i++) {
scans_add_write32(scans, 0, csrr(S0, csr[i]), true);
scans_add_read(scans, SLOT0, false);
@ -1848,9 +1847,6 @@ static int handle_halt(struct target *target, bool announce)
break;
case DCSR_CAUSE_HWBP:
target->debug_reason = DBG_REASON_WATCHPOINT;
/* If we halted because of a data trigger, gdb doesn't know to do
* the disable-breakpoints-step-enable-breakpoints dance. */
info->need_strict_step = true;
break;
case DCSR_CAUSE_DEBUGINT:
target->debug_reason = DBG_REASON_DBGRQ;
@ -1935,26 +1931,10 @@ static int riscv011_poll(struct target *target)
static int riscv011_resume(struct target *target, int current,
target_addr_t address, int handle_breakpoints, int debug_execution)
{
riscv011_info_t *info = get_info(target);
RISCV_INFO(r);
jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
if (!current) {
if (riscv_xlen(target) > 32) {
LOG_WARNING("Asked to resume at 32-bit PC on %d-bit target.",
riscv_xlen(target));
}
int result = register_write(target, GDB_REGNO_PC, address);
if (result != ERROR_OK)
return result;
}
if (info->need_strict_step || handle_breakpoints) {
int result = strict_step(target, false);
if (result != ERROR_OK)
return result;
}
r->prepped = false;
return resume(target, debug_execution, false);
}
@ -1973,8 +1953,11 @@ static int assert_reset(struct target *target)
/* Not sure what we should do when there are multiple cores.
* Here just reset the single hart we're talking to. */
info->dcsr |= DCSR_EBREAKM | DCSR_EBREAKH | DCSR_EBREAKS |
DCSR_EBREAKU | DCSR_HALT;
info->dcsr = set_field(info->dcsr, DCSR_EBREAKM, riscv_ebreakm);
info->dcsr = set_field(info->dcsr, DCSR_EBREAKS, riscv_ebreaks);
info->dcsr = set_field(info->dcsr, DCSR_EBREAKU, riscv_ebreaku);
info->dcsr = set_field(info->dcsr, DCSR_EBREAKH, 1);
info->dcsr |= DCSR_HALT;
if (target->reset_halt)
info->dcsr |= DCSR_NDRESET;
else
@ -2001,8 +1984,13 @@ static int deassert_reset(struct target *target)
}
static int read_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, uint8_t *buffer)
uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment)
{
if (increment != size) {
LOG_ERROR("read_memory with custom increment not implemented");
return ERROR_NOT_IMPLEMENTED;
}
jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
cache_set32(target, 0, lw(S0, ZERO, DEBUG_RAM_START + 16));
@ -2029,6 +2017,8 @@ static int read_memory(struct target *target, target_addr_t address,
riscv011_info_t *info = get_info(target);
const unsigned max_batch_size = 256;
scans_t *scans = scans_new(target, max_batch_size);
if (!scans)
return ERROR_FAIL;
uint32_t result_value = 0x777;
uint32_t i = 0;
@ -2185,6 +2175,8 @@ static int write_memory(struct target *target, target_addr_t address,
const unsigned max_batch_size = 256;
scans_t *scans = scans_new(target, max_batch_size);
if (!scans)
return ERROR_FAIL;
uint32_t result_value = 0x777;
uint32_t i = 0;
@ -2304,6 +2296,26 @@ static int arch_state(struct target *target)
return ERROR_OK;
}
static int init_target(struct command_context *cmd_ctx,
struct target *target)
{
LOG_DEBUG("init");
riscv_info_t *generic_info = (riscv_info_t *)target->arch_info;
generic_info->get_register = get_register;
generic_info->set_register = set_register;
generic_info->read_memory = read_memory;
generic_info->version_specific = calloc(1, sizeof(riscv011_info_t));
if (!generic_info->version_specific)
return ERROR_FAIL;
/* Assume 32-bit until we discover the real value in examine(). */
generic_info->xlen[0] = 32;
riscv_init_registers(target);
return ERROR_OK;
}
struct target_type riscv011_target = {
.name = "riscv",
@ -2321,7 +2333,6 @@ struct target_type riscv011_target = {
.assert_reset = assert_reset,
.deassert_reset = deassert_reset,
.read_memory = read_memory,
.write_memory = write_memory,
.arch_state = arch_state,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef RISCV_H
#define RISCV_H
@ -6,9 +8,11 @@ struct riscv_program;
#include <stdint.h>
#include "opcodes.h"
#include "gdb_regs.h"
#include "jtag/jtag.h"
#include "target/register.h"
/* The register cache is statically allocated. */
#define RISCV_MAX_HARTS 32
#define RISCV_MAX_HARTS 1024
#define RISCV_MAX_REGISTERS 5000
#define RISCV_MAX_TRIGGERS 32
#define RISCV_MAX_HWBPS 16
@ -16,6 +20,12 @@ struct riscv_program;
#define DEFAULT_COMMAND_TIMEOUT_SEC 2
#define DEFAULT_RESET_TIMEOUT_SEC 30
#define RISCV_SATP_MODE(xlen) ((xlen) == 32 ? SATP32_MODE : SATP64_MODE)
#define RISCV_SATP_PPN(xlen) ((xlen) == 32 ? SATP32_PPN : SATP64_PPN)
#define RISCV_PGSHIFT 12
# define PG_MAX_LEVEL 4
extern struct target_type riscv011_target;
extern struct target_type riscv013_target;
@ -32,6 +42,7 @@ enum riscv_halt_reason {
RISCV_HALT_SINGLESTEP,
RISCV_HALT_TRIGGER,
RISCV_HALT_UNKNOWN,
RISCV_HALT_GROUP,
RISCV_HALT_ERROR
};
@ -46,9 +57,6 @@ typedef struct {
struct command_context *cmd_ctx;
void *version_specific;
/* The number of harts on this system. */
int hart_count;
/* The hart that the RTOS thinks is currently being debugged. */
int rtos_hartid;
@ -58,11 +66,6 @@ typedef struct {
* every function than an actual */
int current_hartid;
/* Enough space to store all the registers we might need to save. */
/* FIXME: This should probably be a bunch of register caches. */
uint64_t saved_registers[RISCV_MAX_HARTS][RISCV_MAX_REGISTERS];
bool valid_saved_registers[RISCV_MAX_HARTS][RISCV_MAX_REGISTERS];
/* OpenOCD's register cache points into here. This is not per-hart because
* we just invalidate the entire cache when we change which hart is
* selected. */
@ -75,6 +78,8 @@ typedef struct {
/* It's possible that each core has a different supported ISA set. */
int xlen[RISCV_MAX_HARTS];
riscv_reg_t misa[RISCV_MAX_HARTS];
/* Cached value of vlenb. 0 if vlenb is not readable for some reason. */
unsigned vlenb[RISCV_MAX_HARTS];
/* The number of triggers per hart. */
unsigned trigger_count[RISCV_MAX_HARTS];
@ -100,19 +105,33 @@ typedef struct {
* delays, causing them to be relearned. Used for testing. */
int reset_delays_wait;
/* This target has been prepped and is ready to step/resume. */
bool prepped;
/* This target was selected using hasel. */
bool selected;
/* Helper functions that target the various RISC-V debug spec
* implementations. */
int (*get_register)(struct target *target,
riscv_reg_t *value, int hid, int rid);
int (*set_register)(struct target *target, int hartid, int regid,
uint64_t value);
int (*get_register_buf)(struct target *target, uint8_t *buf, int regno);
int (*set_register_buf)(struct target *target, int regno,
const uint8_t *buf);
int (*select_current_hart)(struct target *target);
bool (*is_halted)(struct target *target);
int (*halt_current_hart)(struct target *target);
int (*resume_current_hart)(struct target *target);
/* Resume this target, as well as every other prepped target that can be
* resumed near-simultaneously. Clear the prepped flag on any target that
* was resumed. */
int (*resume_go)(struct target *target);
int (*step_current_hart)(struct target *target);
int (*on_halt)(struct target *target);
int (*on_resume)(struct target *target);
/* Get this target as ready as possible to resume, without actually
* resuming. */
int (*resume_prep)(struct target *target);
int (*halt_prep)(struct target *target);
int (*halt_go)(struct target *target);
int (*on_step)(struct target *target);
enum riscv_halt_reason (*halt_reason)(struct target *target);
int (*write_debug_buffer)(struct target *target, unsigned index,
@ -134,8 +153,52 @@ typedef struct {
uint32_t num_words, target_addr_t illegal_address, bool run_sbbusyerror_test);
int (*test_compliance)(struct target *target);
int (*read_memory)(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment);
/* How many harts are attached to the DM that this target is attached to? */
int (*hart_count)(struct target *target);
unsigned (*data_bits)(struct target *target);
/* Storage for vector register types. */
struct reg_data_type_vector vector_uint8;
struct reg_data_type_vector vector_uint16;
struct reg_data_type_vector vector_uint32;
struct reg_data_type_vector vector_uint64;
struct reg_data_type_vector vector_uint128;
struct reg_data_type type_uint8_vector;
struct reg_data_type type_uint16_vector;
struct reg_data_type type_uint32_vector;
struct reg_data_type type_uint64_vector;
struct reg_data_type type_uint128_vector;
struct reg_data_type_union_field vector_fields[5];
struct reg_data_type_union vector_union;
struct reg_data_type type_vector;
/* Set when trigger registers are changed by the user. This indicates we eed
* to beware that we may hit a trigger that we didn't realize had been set. */
bool manual_hwbp_set;
} riscv_info_t;
typedef struct {
uint8_t tunneled_dr_width;
struct scan_field tunneled_dr[4];
} riscv_bscan_tunneled_scan_context_t;
typedef struct {
const char *name;
int level;
unsigned va_bits;
unsigned pte_shift;
unsigned vpn_shift[PG_MAX_LEVEL];
unsigned vpn_mask[PG_MAX_LEVEL];
unsigned pte_ppn_shift[PG_MAX_LEVEL];
unsigned pte_ppn_mask[PG_MAX_LEVEL];
unsigned pa_ppn_shift[PG_MAX_LEVEL];
unsigned pa_ppn_mask[PG_MAX_LEVEL];
} virt2phys_info_t;
/* Wall-clock timeout for a command/access. Settable via RISC-V Target commands.*/
extern int riscv_command_timeout_sec;
@ -144,6 +207,11 @@ extern int riscv_reset_timeout_sec;
extern bool riscv_prefer_sba;
extern bool riscv_enable_virtual;
extern bool riscv_ebreakm;
extern bool riscv_ebreaks;
extern bool riscv_ebreaku;
/* Everything needs the RISC-V specific info structure, so here's a nice macro
* that provides that. */
static inline riscv_info_t *riscv_info(const struct target *target) __attribute__((unused));
@ -158,17 +226,28 @@ extern struct scan_field select_dbus;
extern uint8_t ir_idcode[4];
extern struct scan_field select_idcode;
extern struct scan_field select_user4;
extern struct scan_field *bscan_tunneled_select_dmi;
extern uint32_t bscan_tunneled_select_dmi_num_fields;
typedef enum { BSCAN_TUNNEL_NESTED_TAP, BSCAN_TUNNEL_DATA_REGISTER } bscan_tunnel_type_t;
extern int bscan_tunnel_ir_width;
extern bscan_tunnel_type_t bscan_tunnel_type;
uint32_t dtmcontrol_scan_via_bscan(struct target *target, uint32_t out);
void select_dmi_via_bscan(struct target *target);
/*** OpenOCD Interface */
int riscv_openocd_poll(struct target *target);
int riscv_openocd_halt(struct target *target);
int riscv_halt(struct target *target);
int riscv_openocd_resume(
int riscv_resume(
struct target *target,
int current,
target_addr_t address,
int handle_breakpoints,
int debug_execution
int debug_execution,
bool single_hart
);
int riscv_openocd_step(
@ -186,14 +265,6 @@ int riscv_openocd_deassert_reset(struct target *target);
/* Initializes the shared RISC-V structure. */
void riscv_info_init(struct target *target, riscv_info_t *r);
/* 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. */
int riscv_halt_all_harts(struct target *target);
int riscv_halt_one_hart(struct target *target, int hartid);
int riscv_resume_all_harts(struct target *target);
int riscv_resume_one_hart(struct target *target, int hartid);
/* Steps the hart that's currently selected in the RTOS, or if there is no RTOS
* then the only hart. */
int riscv_step_rtos_hart(struct target *target);
@ -201,7 +272,7 @@ int riscv_step_rtos_hart(struct target *target);
bool riscv_supports_extension(struct target *target, int hartid, char letter);
/* Returns XLEN for the given (or current) hart. */
int riscv_xlen(const struct target *target);
unsigned riscv_xlen(const struct target *target);
int riscv_xlen_of_hart(const struct target *target, int hartid);
bool riscv_rtos_enabled(const struct target *target);
@ -226,12 +297,14 @@ int riscv_count_harts(struct target *target);
/* Returns TRUE if the target has the given register on the given hart. */
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. */
/** Set register, updating the cache. */
int riscv_set_register(struct target *target, enum gdb_regno i, riscv_reg_t v);
/** Set register, updating the cache. */
int riscv_set_register_on_hart(struct target *target, int hid, enum gdb_regno rid, uint64_t v);
/** Get register, from the cache if it's in there. */
int riscv_get_register(struct target *target, riscv_reg_t *value,
enum gdb_regno r);
/** Get register, from the cache if it's in there. */
int riscv_get_register_on_hart(struct target *target, riscv_reg_t *value,
int hartid, enum gdb_regno regid);
@ -272,6 +345,15 @@ int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_wp_addre
int riscv_init_registers(struct target *target);
void riscv_semihosting_init(struct target *target);
int riscv_semihosting(struct target *target, int *retval);
typedef enum {
SEMI_NONE, /* Not halted for a semihosting call. */
SEMI_HANDLED, /* Call handled, and target was resumed. */
SEMI_WAITING, /* Call handled, target is halted waiting until we can resume. */
SEMI_ERROR /* Something went wrong. */
} semihosting_result_t;
semihosting_result_t riscv_semihosting(struct target *target, int *retval);
void riscv_add_bscan_tunneled_scan(struct target *target, struct scan_field *field,
riscv_bscan_tunneled_scan_context_t *ctxt);
#endif

View File

@ -1,3 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/***************************************************************************
* Copyright (C) 2018 by Liviu Ionescu *
* ilg@livius.net *
@ -60,35 +62,35 @@ void riscv_semihosting_init(struct target *target)
/**
* Check for and process a semihosting request using the ARM protocol). This
* is meant to be called when the target is stopped due to a debug mode entry.
* If the value 0 is returned then there was nothing to process. A non-zero
* return value signifies that a request was processed and the target resumed,
* or an error was encountered, in which case the caller must return
* immediately.
*
* @param target Pointer to the target to process.
* @param retval Pointer to a location where the return code will be stored
* @return non-zero value if a request was processed or an error encountered
*/
int riscv_semihosting(struct target *target, int *retval)
semihosting_result_t riscv_semihosting(struct target *target, int *retval)
{
struct semihosting *semihosting = target->semihosting;
if (!semihosting)
return 0;
if (!semihosting) {
LOG_DEBUG(" -> NONE (!semihosting)");
return SEMI_NONE;
}
if (!semihosting->is_active)
return 0;
if (!semihosting->is_active) {
LOG_DEBUG(" -> NONE (!semihosting->is_active)");
return SEMI_NONE;
}
riscv_reg_t dpc;
int result = riscv_get_register(target, &dpc, GDB_REGNO_DPC);
riscv_reg_t pc;
int result = riscv_get_register(target, &pc, GDB_REGNO_PC);
if (result != ERROR_OK)
return 0;
return SEMI_ERROR;
uint8_t tmp[12];
/* Read the current instruction, including the bracketing */
*retval = target_read_memory(target, dpc - 4, 2, 6, tmp);
*retval = target_read_memory(target, pc - 4, 2, 6, tmp);
if (*retval != ERROR_OK)
return 0;
return SEMI_ERROR;
/*
* The instructions that trigger a semihosting call,
@ -101,12 +103,12 @@ int riscv_semihosting(struct target *target, int *retval)
uint32_t pre = target_buffer_get_u32(target, tmp);
uint32_t ebreak = target_buffer_get_u32(target, tmp + 4);
uint32_t post = target_buffer_get_u32(target, tmp + 8);
LOG_DEBUG("check %08x %08x %08x from 0x%" PRIx64 "-4", pre, ebreak, post, dpc);
LOG_DEBUG("check %08x %08x %08x from 0x%" PRIx64 "-4", pre, ebreak, post, pc);
if (pre != 0x01f01013 || ebreak != 0x00100073 || post != 0x40705013) {
/* Not the magic sequence defining semihosting. */
return 0;
LOG_DEBUG(" -> NONE (no magic)");
return SEMI_NONE;
}
/*
@ -114,18 +116,21 @@ int riscv_semihosting(struct target *target, int *retval)
* operation to complete.
*/
if (!semihosting->hit_fileio) {
/* RISC-V uses A0 and A1 to pass function arguments */
riscv_reg_t r0;
riscv_reg_t r1;
result = riscv_get_register(target, &r0, GDB_REGNO_A0);
if (result != ERROR_OK)
return 0;
if (result != ERROR_OK) {
LOG_DEBUG(" -> ERROR (couldn't read a0)");
return SEMI_ERROR;
}
result = riscv_get_register(target, &r1, GDB_REGNO_A1);
if (result != ERROR_OK)
return 0;
if (result != ERROR_OK) {
LOG_DEBUG(" -> ERROR (couldn't read a1)");
return SEMI_ERROR;
}
semihosting->op = r0;
semihosting->param = r1;
@ -136,11 +141,12 @@ int riscv_semihosting(struct target *target, int *retval)
*retval = semihosting_common(target);
if (*retval != ERROR_OK) {
LOG_ERROR("Failed semihosting operation");
return 0;
return SEMI_ERROR;
}
} else {
/* Unknown operation number, not a semihosting call. */
return 0;
LOG_DEBUG(" -> NONE (unknown operation number)");
return SEMI_NONE;
}
}
@ -150,16 +156,16 @@ int riscv_semihosting(struct target *target, int *retval)
*/
if (semihosting->is_resumable && !semihosting->hit_fileio) {
/* Resume right after the EBREAK 4 bytes instruction. */
*retval = target_resume(target, 0, dpc+4, 0, 0);
if (*retval != ERROR_OK) {
LOG_ERROR("Failed to resume target");
return 0;
}
*retval = riscv_set_register(target, GDB_REGNO_PC, pc + 4);
if (*retval != ERROR_OK)
return SEMI_ERROR;
return 1;
LOG_DEBUG(" -> HANDLED");
return SEMI_HANDLED;
}
return 0;
LOG_DEBUG(" -> WAITING");
return SEMI_WAITING;
}
/* -------------------------------------------------------------------------
@ -171,7 +177,7 @@ int riscv_semihosting(struct target *target, int *retval)
*/
static int riscv_semihosting_setup(struct target *target, int enable)
{
LOG_DEBUG("enable=%d", enable);
LOG_DEBUG("[%s] enable=%d", target_name(target), enable);
struct semihosting *semihosting = target->semihosting;
if (semihosting)