Add support for v1 system bus access.
This is functional, but doesn't handle errors. Change-Id: Ifb46af1b0b567f3c2a6135b2ad5eb7ba63a3f595
This commit is contained in:
parent
beac00149c
commit
b67379700b
|
@ -1041,17 +1041,47 @@
|
|||
#define DMI_AUTHDATA_DATA_OFFSET 0
|
||||
#define DMI_AUTHDATA_DATA_LENGTH 32
|
||||
#define DMI_AUTHDATA_DATA (0xffffffffU << DMI_AUTHDATA_DATA_OFFSET)
|
||||
#define DMI_SBADDRESS3 0x37
|
||||
/*
|
||||
* Accesses bits 127:96 of the physical address in {\tt sbaddress} (if
|
||||
* the system address bus is that wide).
|
||||
*/
|
||||
#define DMI_SBADDRESS3_ADDRESS_OFFSET 0
|
||||
#define DMI_SBADDRESS3_ADDRESS_LENGTH 32
|
||||
#define DMI_SBADDRESS3_ADDRESS (0xffffffffU << DMI_SBADDRESS3_ADDRESS_OFFSET)
|
||||
#define DMI_SBCS 0x38
|
||||
/*
|
||||
* When a 1, every write to \Rsbaddresszero automatically triggers a
|
||||
* 0: The System Bus interface conforms to mainline drafts of this
|
||||
* spec older than 1 January, 2018.
|
||||
*
|
||||
* 1: The System Bus interface conforms to this version of the spec.
|
||||
*
|
||||
* Other values are reserved for future versions.
|
||||
*/
|
||||
#define DMI_SBCS_SBVERSION_OFFSET 29
|
||||
#define DMI_SBCS_SBVERSION_LENGTH 3
|
||||
#define DMI_SBCS_SBVERSION (0x7U << DMI_SBCS_SBVERSION_OFFSET)
|
||||
/*
|
||||
* When 1, indicates the system bus master is busy. (Whether the
|
||||
* system bus itself is busy is related, but not the same thing.) This
|
||||
* bit goes high immediately when a read or write is requested for any
|
||||
* reason, and does not go low until the access is fully completed.
|
||||
*
|
||||
* To avoid race conditions, debuggers must not try to clear \Fsberror
|
||||
* until they read \Fsbbusy as 0.
|
||||
*/
|
||||
#define DMI_SBCS_SBBUSY_OFFSET 21
|
||||
#define DMI_SBCS_SBBUSY_LENGTH 1
|
||||
#define DMI_SBCS_SBBUSY (0x1U << DMI_SBCS_SBBUSY_OFFSET)
|
||||
/*
|
||||
* When 1, every write to \Rsbaddresszero automatically triggers a
|
||||
* system bus read at the new address.
|
||||
*/
|
||||
#define DMI_SBCS_SBREADONADDR_OFFSET 20
|
||||
#define DMI_SBCS_SBREADONADDR_LENGTH 1
|
||||
#define DMI_SBCS_SBREADONADDR (0x1U << DMI_SBCS_SBREADONADDR_OFFSET)
|
||||
/*
|
||||
* Select the access size to use for system bus accesses triggered by
|
||||
* writes to \Rsbaddresszero or \Rsbdatazero.
|
||||
* Select the access size to use for system bus accesses.
|
||||
*
|
||||
* 0: 8-bit
|
||||
*
|
||||
|
@ -1063,8 +1093,8 @@
|
|||
*
|
||||
* 4: 128-bit
|
||||
*
|
||||
* If an unsupported system bus access size is written here, the DM
|
||||
* does not perform the access and sberror is set to 3.
|
||||
* If \Fsbaccess has an unsupported value when the DM starts a bus
|
||||
* access, the access is not performed and \Fsberror is set to 3.
|
||||
*/
|
||||
#define DMI_SBCS_SBACCESS_OFFSET 17
|
||||
#define DMI_SBCS_SBACCESS_LENGTH 3
|
||||
|
@ -1098,9 +1128,8 @@
|
|||
*
|
||||
* 3: There was some other error (eg. alignment).
|
||||
*
|
||||
* 4: The system bus master was busy when one of the
|
||||
* {\tt sbaddress} or {\tt sbdata} registers was written,
|
||||
* or \Rsbdatazero was read when it had stale data.
|
||||
* 4: The system bus master was busy when one of the {\tt sbaddress}
|
||||
* was written, or one of the {\tt sbdata} registers was accessed.
|
||||
*/
|
||||
#define DMI_SBCS_SBERROR_OFFSET 12
|
||||
#define DMI_SBCS_SBERROR_LENGTH 3
|
||||
|
|
|
@ -143,6 +143,9 @@ typedef struct {
|
|||
/* Number of words in the Program Buffer. */
|
||||
unsigned progbufsize;
|
||||
|
||||
/* We cache the read-only bits of sbcs here. */
|
||||
uint32_t sbcs;
|
||||
|
||||
yes_no_maybe_t progbuf_writable;
|
||||
/* We only need the address so that we know the alignment of the buffer. */
|
||||
riscv_addr_t progbuf_address;
|
||||
|
@ -218,6 +221,18 @@ static void decode_dmi(char *text, unsigned address, unsigned data)
|
|||
{ DMI_ABSTRACTCS, DMI_ABSTRACTCS_DATACOUNT, "datacount" },
|
||||
|
||||
{ DMI_COMMAND, DMI_COMMAND_CMDTYPE, "cmdtype" },
|
||||
|
||||
{ DMI_SBCS, DMI_SBCS_SBREADONADDR, "sbreadonaddr" },
|
||||
{ DMI_SBCS, DMI_SBCS_SBACCESS, "sbaccess" },
|
||||
{ DMI_SBCS, DMI_SBCS_SBAUTOINCREMENT, "sbautoincrement" },
|
||||
{ DMI_SBCS, DMI_SBCS_SBREADONDATA, "sbreadondata" },
|
||||
{ DMI_SBCS, DMI_SBCS_SBERROR, "sberror" },
|
||||
{ DMI_SBCS, DMI_SBCS_SBASIZE, "sbasize" },
|
||||
{ DMI_SBCS, DMI_SBCS_SBACCESS128, "sbaccess128" },
|
||||
{ DMI_SBCS, DMI_SBCS_SBACCESS64, "sbaccess64" },
|
||||
{ DMI_SBCS, DMI_SBCS_SBACCESS32, "sbaccess32" },
|
||||
{ DMI_SBCS, DMI_SBCS_SBACCESS16, "sbaccess16" },
|
||||
{ DMI_SBCS, DMI_SBCS_SBACCESS8, "sbaccess8" },
|
||||
};
|
||||
|
||||
text[0] = 0;
|
||||
|
@ -1148,6 +1163,7 @@ static int examine(struct target *target)
|
|||
info->dtmcontrol_idle = get_field(dtmcontrol, DTM_DTMCS_IDLE);
|
||||
|
||||
uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS);
|
||||
LOG_DEBUG("dmstatus: 0x%08x", dmstatus);
|
||||
if (get_field(dmstatus, DMI_DMSTATUS_VERSION) != 2) {
|
||||
LOG_ERROR("OpenOCD only supports Debug Module version 2, not %d "
|
||||
"(dmstatus=0x%x)", get_field(dmstatus, DMI_DMSTATUS_VERSION), dmstatus);
|
||||
|
@ -1158,16 +1174,7 @@ static int examine(struct target *target)
|
|||
dmi_write(target, DMI_DMCONTROL, 0);
|
||||
dmi_write(target, DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE);
|
||||
uint32_t dmcontrol = dmi_read(target, DMI_DMCONTROL);
|
||||
|
||||
uint32_t hartinfo = dmi_read(target, DMI_HARTINFO);
|
||||
|
||||
LOG_DEBUG("dmcontrol: 0x%08x", dmcontrol);
|
||||
LOG_DEBUG("dmstatus: 0x%08x", dmstatus);
|
||||
LOG_DEBUG("hartinfo: 0x%08x", hartinfo);
|
||||
|
||||
info->datasize = get_field(hartinfo, DMI_HARTINFO_DATASIZE);
|
||||
info->dataaccess = get_field(hartinfo, DMI_HARTINFO_DATAACCESS);
|
||||
info->dataaddr = get_field(hartinfo, DMI_HARTINFO_DATAADDR);
|
||||
|
||||
if (!get_field(dmcontrol, DMI_DMCONTROL_DMACTIVE)) {
|
||||
LOG_ERROR("Debug Module did not become active. dmcontrol=0x%x",
|
||||
|
@ -1175,6 +1182,13 @@ static int examine(struct target *target)
|
|||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
uint32_t hartinfo = dmi_read(target, DMI_HARTINFO);
|
||||
LOG_DEBUG("hartinfo: 0x%08x", hartinfo);
|
||||
|
||||
info->datasize = get_field(hartinfo, DMI_HARTINFO_DATASIZE);
|
||||
info->dataaccess = get_field(hartinfo, DMI_HARTINFO_DATAACCESS);
|
||||
info->dataaddr = get_field(hartinfo, DMI_HARTINFO_DATAADDR);
|
||||
|
||||
if (!get_field(dmstatus, DMI_DMSTATUS_AUTHENTICATED)) {
|
||||
LOG_ERROR("Authentication required by RISC-V core but not "
|
||||
"supported by OpenOCD. dmcontrol=0x%x", dmcontrol);
|
||||
|
@ -1191,6 +1205,8 @@ static int examine(struct target *target)
|
|||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
info->sbcs = dmi_read(target, DMI_SBCS);
|
||||
|
||||
/* Check that abstract data registers are accessible. */
|
||||
uint32_t abstractcs = dmi_read(target, DMI_ABSTRACTCS);
|
||||
info->datacount = get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT);
|
||||
|
@ -1439,11 +1455,102 @@ static void log_memory_access(target_addr_t address, uint64_t value,
|
|||
LOG_DEBUG(fmt, value);
|
||||
}
|
||||
|
||||
/* Read the relevant sbdata regs depending on size, and put the results into
|
||||
* buffer. */
|
||||
static int read_memory_bus_word(struct target *target, uint32_t size,
|
||||
uint8_t *buffer)
|
||||
{
|
||||
if (size > 12)
|
||||
write_to_buf(buffer + 12, dmi_read(target, DMI_SBDATA3), 4);
|
||||
if (size > 8)
|
||||
write_to_buf(buffer + 8, dmi_read(target, DMI_SBDATA2), 4);
|
||||
if (size > 4)
|
||||
write_to_buf(buffer + 4, dmi_read(target, DMI_SBDATA1), 4);
|
||||
write_to_buf(buffer, dmi_read(target, DMI_SBDATA0), MIN(size, 4));
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static uint32_t sb_sbaccess(unsigned size_bytes)
|
||||
{
|
||||
switch (size_bytes) {
|
||||
case 1:
|
||||
return set_field(0, DMI_SBCS_SBACCESS, 0);
|
||||
case 2:
|
||||
return set_field(0, DMI_SBCS_SBACCESS, 1);
|
||||
case 4:
|
||||
return set_field(0, DMI_SBCS_SBACCESS, 2);
|
||||
case 8:
|
||||
return set_field(0, DMI_SBCS_SBACCESS, 3);
|
||||
case 16:
|
||||
return set_field(0, DMI_SBCS_SBACCESS, 4);
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
|
||||
static int sb_write_address(struct target *target, target_addr_t address)
|
||||
{
|
||||
RISCV013_INFO(info);
|
||||
unsigned sbasize = get_field(info->sbcs, DMI_SBCS_SBASIZE);
|
||||
/* There currently is no support for >64-bit addresses in OpenOCD. */
|
||||
if (sbasize > 96) {
|
||||
dmi_write(target, DMI_SBADDRESS3, 0);
|
||||
}
|
||||
if (sbasize > 64) {
|
||||
dmi_write(target, DMI_SBADDRESS2, 0);
|
||||
}
|
||||
if (sbasize > 32) {
|
||||
#if BUILD_TARGET64
|
||||
dmi_write(target, DMI_SBADDRESS1, address >> 32);
|
||||
#else
|
||||
dmi_write(target, DMI_SBADDRESS1, 0);
|
||||
#endif
|
||||
}
|
||||
return dmi_write(target, DMI_SBADDRESS0, address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the requested memory using the system bus interface.
|
||||
*/
|
||||
static int read_memory_bus(struct target *target, target_addr_t address,
|
||||
uint32_t size, uint32_t count, uint8_t *buffer)
|
||||
{
|
||||
uint32_t sbcs = set_field(0, DMI_SBCS_SBREADONADDR, 1);
|
||||
sbcs |= sb_sbaccess(size);
|
||||
sbcs = set_field(sbcs, DMI_SBCS_SBAUTOINCREMENT, 1);
|
||||
sbcs = set_field(sbcs, DMI_SBCS_SBREADONDATA, count > 1);
|
||||
dmi_write(target, DMI_SBCS, sbcs);
|
||||
|
||||
/* This address write will trigger the first read. */
|
||||
sb_write_address(target, address);
|
||||
|
||||
for (uint32_t i = 0; i < count - 1; i++) {
|
||||
read_memory_bus_word(target, size, buffer + i * size);
|
||||
}
|
||||
|
||||
sbcs = set_field(sbcs, DMI_SBCS_SBREADONDATA, 0);
|
||||
dmi_write(target, DMI_SBCS, sbcs);
|
||||
|
||||
read_memory_bus_word(target, size, buffer + (count - 1) * size);
|
||||
|
||||
sbcs = dmi_read(target, DMI_SBCS);
|
||||
unsigned error = get_field(sbcs, DMI_SBCS_SBERROR);
|
||||
if (error == 0) {
|
||||
return ERROR_OK;
|
||||
} else if (error == 1 || error == 2 || error == 3) {
|
||||
/* Bus timeout, bus error, other bus error. */
|
||||
return ERROR_FAIL;
|
||||
} else if (error == 4) {
|
||||
assert(0);
|
||||
/* TODO: Reading too fast. We should deal with this properly. */
|
||||
}
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the requested memory, taking care to execute every read exactly once,
|
||||
* even if cmderr=busy is encountered.
|
||||
*/
|
||||
static int read_memory(struct target *target, target_addr_t address,
|
||||
static int read_memory_progbuf(struct target *target, target_addr_t address,
|
||||
uint32_t size, uint32_t count, uint8_t *buffer)
|
||||
{
|
||||
RISCV013_INFO(info);
|
||||
|
@ -1679,7 +1786,76 @@ error:
|
|||
return result;
|
||||
}
|
||||
|
||||
static int write_memory(struct target *target, target_addr_t address,
|
||||
static int read_memory(struct target *target, target_addr_t address,
|
||||
uint32_t size, uint32_t count, uint8_t *buffer)
|
||||
{
|
||||
RISCV013_INFO(info);
|
||||
if ((get_field(info->sbcs, DMI_SBCS_SBVERSION) == 1) && (
|
||||
(get_field(info->sbcs, DMI_SBCS_SBACCESS8) && size == 1) ||
|
||||
(get_field(info->sbcs, DMI_SBCS_SBACCESS16) && size == 2) ||
|
||||
(get_field(info->sbcs, DMI_SBCS_SBACCESS32) && size == 4) ||
|
||||
(get_field(info->sbcs, DMI_SBCS_SBACCESS64) && size == 8) ||
|
||||
(get_field(info->sbcs, DMI_SBCS_SBACCESS128) && size == 16))) {
|
||||
return read_memory_bus(target, address, size, count, buffer);
|
||||
} else {
|
||||
return read_memory_progbuf(target, address, size, count, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static int write_memory_bus(struct target *target, target_addr_t address,
|
||||
uint32_t size, uint32_t count, const uint8_t *buffer)
|
||||
{
|
||||
uint32_t sbcs = sb_sbaccess(size);
|
||||
sbcs = set_field(sbcs, DMI_SBCS_SBAUTOINCREMENT, 1);
|
||||
dmi_write(target, DMI_SBCS, sbcs);
|
||||
|
||||
sb_write_address(target, address);
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
const uint8_t *p = buffer + i * size;
|
||||
if (size > 12)
|
||||
dmi_write(target, DMI_SBDATA3,
|
||||
((uint32_t) p[12]) |
|
||||
(((uint32_t) p[13]) << 8) |
|
||||
(((uint32_t) p[14]) << 16) |
|
||||
(((uint32_t) p[15]) << 24));
|
||||
if (size > 8)
|
||||
dmi_write(target, DMI_SBDATA2,
|
||||
((uint32_t) p[8]) |
|
||||
(((uint32_t) p[9]) << 8) |
|
||||
(((uint32_t) p[10]) << 16) |
|
||||
(((uint32_t) p[11]) << 24));
|
||||
if (size > 4)
|
||||
dmi_write(target, DMI_SBDATA1,
|
||||
((uint32_t) p[4]) |
|
||||
(((uint32_t) p[5]) << 8) |
|
||||
(((uint32_t) p[6]) << 16) |
|
||||
(((uint32_t) p[7]) << 24));
|
||||
uint32_t value = p[0];
|
||||
if (size > 2) {
|
||||
value |= ((uint32_t) p[2]) << 16;
|
||||
value |= ((uint32_t) p[3]) << 24;
|
||||
}
|
||||
if (size > 1)
|
||||
value |= ((uint32_t) p[1]) << 8;
|
||||
dmi_write(target, DMI_SBDATA0, value);
|
||||
}
|
||||
|
||||
sbcs = dmi_read(target, DMI_SBCS);
|
||||
unsigned error = get_field(sbcs, DMI_SBCS_SBERROR);
|
||||
if (error == 0) {
|
||||
return ERROR_OK;
|
||||
} else if (error == 1 || error == 2 || error == 3) {
|
||||
/* Bus timeout, bus error, other bus error. */
|
||||
return ERROR_FAIL;
|
||||
} else if (error == 4) {
|
||||
assert(0);
|
||||
/* TODO: Reading too fast. We should deal with this properly. */
|
||||
}
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
static int write_memory_progbuf(struct target *target, target_addr_t address,
|
||||
uint32_t size, uint32_t count, const uint8_t *buffer)
|
||||
{
|
||||
RISCV013_INFO(info);
|
||||
|
@ -1856,6 +2032,22 @@ error:
|
|||
return result;
|
||||
}
|
||||
|
||||
static int write_memory(struct target *target, target_addr_t address,
|
||||
uint32_t size, uint32_t count, const uint8_t *buffer)
|
||||
{
|
||||
RISCV013_INFO(info);
|
||||
if ((get_field(info->sbcs, DMI_SBCS_SBVERSION) == 1) && (
|
||||
(get_field(info->sbcs, DMI_SBCS_SBACCESS8) && size == 1) ||
|
||||
(get_field(info->sbcs, DMI_SBCS_SBACCESS16) && size == 2) ||
|
||||
(get_field(info->sbcs, DMI_SBCS_SBACCESS32) && size == 4) ||
|
||||
(get_field(info->sbcs, DMI_SBCS_SBACCESS64) && size == 8) ||
|
||||
(get_field(info->sbcs, DMI_SBCS_SBACCESS128) && size == 16))) {
|
||||
return write_memory_bus(target, address, size, count, buffer);
|
||||
} else {
|
||||
return write_memory_progbuf(target, address, size, count, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static int arch_state(struct target *target)
|
||||
{
|
||||
return ERROR_OK;
|
||||
|
|
Loading…
Reference in New Issue