Merge pull request #218 from riscv/auth

Add `riscv authdata_read` and `riscv authdata_write` commands to support arbitrary authentication through TCL scripts
This commit is contained in:
Tim Newsome 2018-02-28 09:20:31 -08:00 committed by GitHub
commit d388f1cbb2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 289 additions and 60 deletions

View File

@ -19,6 +19,7 @@
#include "target/register.h"
#include "target/breakpoints.h"
#include "helper/time_support.h"
#include "helper/list.h"
#include "riscv.h"
#include "rtos/riscv_debug.h"
#include "debug_defines.h"
@ -135,6 +136,20 @@ typedef enum {
YNM_NO
} yes_no_maybe_t;
typedef struct {
struct list_head list;
int abs_chain_position;
/* Indicates we already reset this DM, so don't need to do it again. */
bool was_reset;
/* Targets that are connected to this DM. */
struct list_head target_list;
} dm013_info_t;
typedef struct {
struct list_head list;
struct target *target;
} target_list_t;
typedef struct {
/* Number of address bits in the dbus register. */
unsigned abits;
@ -189,14 +204,62 @@ typedef struct {
/* The width of the hartsel field. */
unsigned hartsellen;
/* DM that provides access to this target. */
dm013_info_t *dm;
} riscv013_info_t;
LIST_HEAD(dm_list);
static riscv013_info_t *get_info(const struct target *target)
{
riscv_info_t *info = (riscv_info_t *) target->arch_info;
return (riscv013_info_t *) info->version_specific;
}
/**
* Return the DM structure for this target. If there isn't one, find it in the
* global list of DMs. If it's not in there, then create one and initialize it
* to 0.
*/
static dm013_info_t *get_dm(struct target *target)
{
RISCV013_INFO(info);
if (info->dm)
return info->dm;
int abs_chain_position = target->tap->abs_chain_position;
dm013_info_t *entry;
dm013_info_t *dm = NULL;
list_for_each_entry(entry, &dm_list, list) {
if (entry->abs_chain_position == abs_chain_position) {
dm = entry;
break;
}
}
if (!dm) {
dm = calloc(1, sizeof(dm013_info_t));
dm->abs_chain_position = abs_chain_position;
INIT_LIST_HEAD(&dm->target_list);
list_add(&dm->list, &dm_list);
}
info->dm = dm;
target_list_t *target_entry;
list_for_each_entry(target_entry, &dm->target_list, list) {
if (target_entry->target == target) {
return dm;
}
}
target_entry = calloc(1, sizeof(*target_entry));
target_entry->target = target;
list_add(&target_entry->list, &dm->target_list);
return dm;
}
static uint32_t hartsel_mask(const struct target *target)
{
RISCV013_INFO(info);
@ -525,6 +588,19 @@ static int dmi_write(struct target *target, uint16_t address, uint64_t value)
return ERROR_OK;
}
int dmstatus_read(struct target *target, uint32_t *dmstatus,
bool authenticated)
{
*dmstatus = dmi_read(target, DMI_DMSTATUS);
if (authenticated && !get_field(*dmstatus, DMI_DMSTATUS_AUTHENTICATED)) {
LOG_ERROR("Debugger is not authenticated to target Debug Module. "
"(dmstatus=0x%x). Use `riscv authdata_read` and "
"`riscv_authdata_write` commands to authenticate.", *dmstatus);
return ERROR_FAIL;
}
return ERROR_OK;
}
static void increase_ac_busy_delay(struct target *target)
{
riscv013_info_t *info = get_info(target);
@ -1111,57 +1187,31 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t
return result;
}
/*** OpenOCD target functions. ***/
static int init_target(struct command_context *cmd_ctx,
struct target *target)
int wait_for_authbusy(struct target *target, uint32_t *dmstatus)
{
LOG_DEBUG("init");
riscv_info_t *generic_info = (riscv_info_t *) target->arch_info;
generic_info->get_register = &riscv013_get_register;
generic_info->set_register = &riscv013_set_register;
generic_info->select_current_hart = &riscv013_select_current_hart;
generic_info->is_halted = &riscv013_is_halted;
generic_info->halt_current_hart = &riscv013_halt_current_hart;
generic_info->resume_current_hart = &riscv013_resume_current_hart;
generic_info->step_current_hart = &riscv013_step_current_hart;
generic_info->on_halt = &riscv013_on_halt;
generic_info->on_resume = &riscv013_on_resume;
generic_info->on_step = &riscv013_on_step;
generic_info->halt_reason = &riscv013_halt_reason;
generic_info->read_debug_buffer = &riscv013_read_debug_buffer;
generic_info->write_debug_buffer = &riscv013_write_debug_buffer;
generic_info->execute_debug_buffer = &riscv013_execute_debug_buffer;
generic_info->fill_dmi_write_u64 = &riscv013_fill_dmi_write_u64;
generic_info->fill_dmi_read_u64 = &riscv013_fill_dmi_read_u64;
generic_info->fill_dmi_nop_u64 = &riscv013_fill_dmi_nop_u64;
generic_info->dmi_write_u64_bits = &riscv013_dmi_write_u64_bits;
generic_info->version_specific = calloc(1, sizeof(riscv013_info_t));
if (!generic_info->version_specific)
return ERROR_FAIL;
riscv013_info_t *info = get_info(target);
info->progbufsize = -1;
info->dmi_busy_delay = 0;
info->bus_master_read_delay = 0;
info->bus_master_write_delay = 0;
info->ac_busy_delay = 0;
/* Assume all these abstract commands are supported until we learn
* otherwise.
* TODO: The spec allows eg. one CSR to be able to be accessed abstractly
* while another one isn't. We don't track that this closely here, but in
* the future we probably should. */
info->abstract_read_csr_supported = true;
info->abstract_write_csr_supported = true;
info->abstract_read_fpr_supported = true;
info->abstract_write_fpr_supported = true;
time_t start = time(NULL);
while (1) {
uint32_t value;
if (dmstatus_read(target, &value, false) != ERROR_OK)
return ERROR_FAIL;
if (dmstatus)
*dmstatus = value;
if (!get_field(value, DMI_DMSTATUS_AUTHBUSY))
break;
if (time(NULL) - start > riscv_command_timeout_sec) {
LOG_ERROR("Timed out after %ds waiting for authbusy to go low (dmstatus=0x%x). "
"Increase the timeout with riscv set_command_timeout_sec.",
riscv_command_timeout_sec,
value);
return ERROR_FAIL;
}
}
return ERROR_OK;
}
/*** OpenOCD target functions. ***/
static void deinit_target(struct target *target)
{
LOG_DEBUG("riscv_deinit_target()");
@ -1195,7 +1245,9 @@ static int examine(struct target *target)
info->abits = get_field(dtmcontrol, DTM_DTMCS_ABITS);
info->dtmcontrol_idle = get_field(dtmcontrol, DTM_DTMCS_IDLE);
uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS);
uint32_t dmstatus;
if (dmstatus_read(target, &dmstatus, false) != ERROR_OK)
return ERROR_FAIL;
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 "
@ -1204,8 +1256,12 @@ static int examine(struct target *target)
}
/* Reset the Debug Module. */
dmi_write(target, DMI_DMCONTROL, 0);
dmi_write(target, DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE);
dm013_info_t *dm = get_dm(target);
if (!dm->was_reset) {
dmi_write(target, DMI_DMCONTROL, 0);
dmi_write(target, DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE);
dm->was_reset = true;
}
uint32_t max_hartsel_mask = ((1L<<10)-1) << DMI_DMCONTROL_HARTSEL_OFFSET;
dmi_write(target, DMI_DMCONTROL, max_hartsel_mask | DMI_DMCONTROL_DMACTIVE);
@ -1232,9 +1288,14 @@ static int examine(struct target *target)
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);
return ERROR_FAIL;
LOG_ERROR("Debugger is not authenticated to target Debug Module. "
"(dmstatus=0x%x). Use `riscv authdata_read` and "
"`riscv_authdata_write` commands to authenticate.", dmstatus);
/* If we return ERROR_FAIL here, then in a multicore setup the next
* core won't be examined, which means we won't set up the
* authentication commands for them, which means the config script
* needs to be a lot more complex. */
return ERROR_OK;
}
info->sbcs = dmi_read(target, DMI_SBCS);
@ -1267,7 +1328,9 @@ static int examine(struct target *target)
r->current_hartid = i;
riscv013_select_current_hart(target);
uint32_t s = dmi_read(target, DMI_DMSTATUS);
uint32_t s;
if (dmstatus_read(target, &s, true) != ERROR_OK)
return ERROR_FAIL;
if (get_field(s, DMI_DMSTATUS_ANYNONEXISTENT))
break;
r->hart_count = i + 1;
@ -1340,6 +1403,93 @@ static int examine(struct target *target)
return ERROR_OK;
}
int riscv013_authdata_read(struct target *target, uint32_t *value)
{
if (wait_for_authbusy(target, NULL) != ERROR_OK)
return ERROR_FAIL;
*value = dmi_read(target, DMI_AUTHDATA);
return ERROR_OK;
}
int riscv013_authdata_write(struct target *target, uint32_t value)
{
uint32_t before, after;
if (wait_for_authbusy(target, &before) != ERROR_OK)
return ERROR_FAIL;
dmi_write(target, DMI_AUTHDATA, value);
if (wait_for_authbusy(target, &after) != ERROR_OK)
return ERROR_FAIL;
if (!get_field(before, DMI_DMSTATUS_AUTHENTICATED) &&
get_field(after, DMI_DMSTATUS_AUTHENTICATED)) {
LOG_INFO("authdata_write resulted in successful authentication");
int result = ERROR_OK;
dm013_info_t *dm = get_dm(target);
target_list_t *entry;
list_for_each_entry(entry, &dm->target_list, list) {
if (examine(entry->target) != ERROR_OK)
result = ERROR_FAIL;
}
return result;
}
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 = &riscv013_get_register;
generic_info->set_register = &riscv013_set_register;
generic_info->select_current_hart = &riscv013_select_current_hart;
generic_info->is_halted = &riscv013_is_halted;
generic_info->halt_current_hart = &riscv013_halt_current_hart;
generic_info->resume_current_hart = &riscv013_resume_current_hart;
generic_info->step_current_hart = &riscv013_step_current_hart;
generic_info->on_halt = &riscv013_on_halt;
generic_info->on_resume = &riscv013_on_resume;
generic_info->on_step = &riscv013_on_step;
generic_info->halt_reason = &riscv013_halt_reason;
generic_info->read_debug_buffer = &riscv013_read_debug_buffer;
generic_info->write_debug_buffer = &riscv013_write_debug_buffer;
generic_info->execute_debug_buffer = &riscv013_execute_debug_buffer;
generic_info->fill_dmi_write_u64 = &riscv013_fill_dmi_write_u64;
generic_info->fill_dmi_read_u64 = &riscv013_fill_dmi_read_u64;
generic_info->fill_dmi_nop_u64 = &riscv013_fill_dmi_nop_u64;
generic_info->dmi_write_u64_bits = &riscv013_dmi_write_u64_bits;
generic_info->authdata_read = &riscv013_authdata_read;
generic_info->authdata_write = &riscv013_authdata_write;
generic_info->version_specific = calloc(1, sizeof(riscv013_info_t));
if (!generic_info->version_specific)
return ERROR_FAIL;
riscv013_info_t *info = get_info(target);
info->progbufsize = -1;
info->dmi_busy_delay = 0;
info->bus_master_read_delay = 0;
info->bus_master_write_delay = 0;
info->ac_busy_delay = 0;
/* Assume all these abstract commands are supported until we learn
* otherwise.
* TODO: The spec allows eg. one CSR to be able to be accessed abstractly
* while another one isn't. We don't track that this closely here, but in
* the future we probably should. */
info->abstract_read_csr_supported = true;
info->abstract_write_csr_supported = true;
info->abstract_read_fpr_supported = true;
info->abstract_write_fpr_supported = true;
return ERROR_OK;
}
static int assert_reset(struct target *target)
{
RISCV_INFO(r);
@ -1416,7 +1566,8 @@ static int deassert_reset(struct target *target)
if (target->reset_halt) {
LOG_DEBUG("Waiting for hart to be halted.");
do {
dmstatus = dmi_read(target, DMI_DMSTATUS);
if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
return ERROR_FAIL;
if (time(NULL) - start > riscv_reset_timeout_sec) {
LOG_ERROR("Hart didn't halt coming out of reset in %ds; "
"dmstatus=0x%x; "
@ -1433,7 +1584,8 @@ static int deassert_reset(struct target *target)
} else {
LOG_DEBUG("Waiting for hart to be running.");
do {
dmstatus = dmi_read(target, DMI_DMSTATUS);
if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
return ERROR_FAIL;
if (get_field(dmstatus, DMI_DMSTATUS_ANYHALTED) ||
get_field(dmstatus, DMI_DMSTATUS_ANYUNAVAIL)) {
LOG_ERROR("Unexpected hart status during reset. dmstatus=0x%x",
@ -2306,7 +2458,9 @@ static int riscv013_halt_current_hart(struct target *target)
break;
if (!riscv_is_halted(target)) {
uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS);
uint32_t dmstatus;
if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
return ERROR_FAIL;
dmcontrol = dmi_read(target, DMI_DMCONTROL);
LOG_ERROR("unable to halt hart %d", r->current_hartid);
@ -2348,7 +2502,9 @@ static int riscv013_on_halt(struct target *target)
static bool riscv013_is_halted(struct target *target)
{
uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS);
uint32_t dmstatus;
if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
return false;
if (get_field(dmstatus, DMI_DMSTATUS_ANYUNAVAIL))
LOG_ERROR("hart %d is unavailiable", riscv_current_hartid(target));
if (get_field(dmstatus, DMI_DMSTATUS_ANYNONEXISTENT))
@ -2481,9 +2637,11 @@ static int riscv013_step_or_resume_current_hart(struct target *target, bool step
dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_RESUMEREQ, 1);
dmi_write(target, DMI_DMCONTROL, dmcontrol);
uint32_t dmstatus;
for (size_t i = 0; i < 256; ++i) {
usleep(10);
uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS);
if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
return ERROR_FAIL;
if (get_field(dmstatus, DMI_DMSTATUS_ALLRESUMEACK) == 0)
continue;
if (step && get_field(dmstatus, DMI_DMSTATUS_ALLHALTED) == 0)
@ -2494,7 +2652,8 @@ static int riscv013_step_or_resume_current_hart(struct target *target, bool step
return ERROR_OK;
}
uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS);
if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
return ERROR_FAIL;
dmcontrol = dmi_read(target, DMI_DMCONTROL);
LOG_ERROR("unable to resume hart %d", r->current_hartid);
LOG_ERROR(" dmcontrol=0x%08x", dmcontrol);

View File

@ -1212,6 +1212,7 @@ COMMAND_HANDLER(riscv_set_scratch_ram)
return ERROR_OK;
}
// TODO: use COMMAND_PARSE_NUMBER
long long unsigned int address;
int result = sscanf(CMD_ARGV[0], "%llx", &address);
if (result != (int) strlen(CMD_ARGV[0])) {
@ -1307,6 +1308,58 @@ COMMAND_HANDLER(riscv_set_expose_csrs)
return ERROR_OK;
}
COMMAND_HANDLER(riscv_authdata_read)
{
if (CMD_ARGC != 0) {
LOG_ERROR("Command takes no parameters");
return ERROR_COMMAND_SYNTAX_ERROR;
}
struct target *target = get_current_target(CMD_CTX);
if (!target) {
LOG_ERROR("target is NULL!");
return ERROR_FAIL;
}
RISCV_INFO(r);
if (!r) {
LOG_ERROR("riscv_info is NULL!");
return ERROR_FAIL;
}
if (r->authdata_read) {
uint32_t value;
if (r->authdata_read(target, &value) != ERROR_OK)
return ERROR_FAIL;
command_print(CMD_CTX, "0x%" PRIx32, value);
return ERROR_OK;
} else {
LOG_ERROR("authdata_read is not implemented for this target.");
return ERROR_FAIL;
}
}
COMMAND_HANDLER(riscv_authdata_write)
{
if (CMD_ARGC != 1) {
LOG_ERROR("Command takes exactly 1 argument");
return ERROR_COMMAND_SYNTAX_ERROR;
}
struct target *target = get_current_target(CMD_CTX);
RISCV_INFO(r);
uint32_t value;
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], value);
if (r->authdata_write) {
return r->authdata_write(target, value);
} else {
LOG_ERROR("authdata_write is not implemented for this target.");
return ERROR_FAIL;
}
}
static const struct command_registration riscv_exec_command_handlers[] = {
{
.name = "set_command_timeout_sec",
@ -1333,11 +1386,25 @@ static const struct command_registration riscv_exec_command_handlers[] = {
.name = "expose_csrs",
.handler = riscv_set_expose_csrs,
.mode = COMMAND_ANY,
.usage = "riscv expose_csrs n0[-m0][,n0[-m0]]...",
.usage = "riscv expose_csrs n0[-m0][,n1[-m1]]...",
.help = "Configure a list of inclusive ranges for CSRs to expose in "
"addition to the standard ones. This must be executed before "
"`init`."
},
{
.name = "authdata_read",
.handler = riscv_authdata_read,
.mode = COMMAND_ANY,
.usage = "riscv authdata_read",
.help = "Return the 32-bit value read from authdata."
},
{
.name = "authdata_write",
.handler = riscv_authdata_write,
.mode = COMMAND_ANY,
.usage = "riscv authdata_write value",
.help = "Write the 32-bit value to authdata."
},
COMMAND_REGISTRATION_DONE
};

View File

@ -110,6 +110,9 @@ typedef struct {
void (*fill_dmi_write_u64)(struct target *target, char *buf, int a, uint64_t d);
void (*fill_dmi_read_u64)(struct target *target, char *buf, int a);
void (*fill_dmi_nop_u64)(struct target *target, char *buf);
int (*authdata_read)(struct target *target, uint32_t *value);
int (*authdata_write)(struct target *target, uint32_t value);
} riscv_info_t;
/* Wall-clock timeout for a command/access. Settable via RISC-V Target commands.*/