gdb_server: support gdb target description
* Add a parameter in .get_gdb_reg_list() to return different register lists as generating target description. * Modify STRUCT REG to let gdb generate target description according to register information. The modified structure of register is struct reg { const char *name; uint32_t number; /* for regnum="num" */ struct reg_feature *feature; /* for register group feature name */ bool caller_save; /* for save-restore="yes|no" */ void *value; bool dirty; bool valid; bool exist; uint32_t size; struct reg_data_type *reg_data_type; /* for type="type" */ const char *group; /* for group="general|float|vector" */ void *arch_info; const struct reg_arch_type *type; }; Change-Id: I2096b67adf94518ba0b8b23d8c6a9f64ad7932b8 Signed-off-by: Hsiangkai Wang <hsiangkai@gmail.com> Reviewed-on: http://openocd.zylin.com/1382 Tested-by: jenkins Reviewed-by: Franck Jullien <franck.jullien@gmail.com> Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk> Reviewed-by: Andreas Fritiofson <andreas.fritiofson@gmail.com>
This commit is contained in:
parent
9f2922aa7a
commit
d979d78e97
|
@ -226,7 +226,8 @@ static int linux_os_thread_reg_list(struct rtos *rtos,
|
|||
/*LOG_INFO("thread %lx current on core %x",thread_id,
|
||||
* target->coreid);*/
|
||||
retval =
|
||||
target_get_gdb_reg_list(target, ®_list, ®_list_size);
|
||||
target_get_gdb_reg_list(target, ®_list, ®_list_size,
|
||||
REG_CLASS_GENERAL);
|
||||
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
@ -498,7 +499,7 @@ int get_current(struct target *target, int create)
|
|||
int retval;
|
||||
|
||||
if (target_get_gdb_reg_list(head->target, ®_list,
|
||||
®_list_size) != ERROR_OK) {
|
||||
®_list_size, REG_CLASS_GENERAL) != ERROR_OK) {
|
||||
free(buffer);
|
||||
return ERROR_TARGET_FAILURE;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,12 @@
|
|||
* Copyright (C) ST-Ericsson SA 2011 *
|
||||
* michel.jaouen@stericsson.com : smp minimum support *
|
||||
* *
|
||||
* Copyright (C) 2013 Andes Technology *
|
||||
* Hsiangkai Wang <hkwang@andestech.com> *
|
||||
* *
|
||||
* Copyright (C) 2013 Franck Jullien *
|
||||
* elec4fun@gmail.com *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
|
@ -114,6 +120,11 @@ static int gdb_flash_program = 1;
|
|||
*/
|
||||
static int gdb_report_data_abort;
|
||||
|
||||
/* set if we are sending target descriptions to gdb
|
||||
* via qXfer:features:read packet */
|
||||
/* disabled by default */
|
||||
static int gdb_use_target_description;
|
||||
|
||||
static int gdb_last_signal(struct target *target)
|
||||
{
|
||||
switch (target->debug_reason) {
|
||||
|
@ -968,7 +979,8 @@ static int gdb_get_registers_packet(struct connection *connection,
|
|||
if ((target->rtos != NULL) && (ERROR_OK == rtos_get_gdb_reg_list(connection)))
|
||||
return ERROR_OK;
|
||||
|
||||
retval = target_get_gdb_reg_list(target, ®_list, ®_list_size);
|
||||
retval = target_get_gdb_reg_list(target, ®_list, ®_list_size,
|
||||
REG_CLASS_GENERAL);
|
||||
if (retval != ERROR_OK)
|
||||
return gdb_error(connection, retval);
|
||||
|
||||
|
@ -1027,7 +1039,8 @@ static int gdb_set_registers_packet(struct connection *connection,
|
|||
return ERROR_SERVER_REMOTE_CLOSED;
|
||||
}
|
||||
|
||||
retval = target_get_gdb_reg_list(target, ®_list, ®_list_size);
|
||||
retval = target_get_gdb_reg_list(target, ®_list, ®_list_size,
|
||||
REG_CLASS_GENERAL);
|
||||
if (retval != ERROR_OK)
|
||||
return gdb_error(connection, retval);
|
||||
|
||||
|
@ -1072,7 +1085,8 @@ static int gdb_get_register_packet(struct connection *connection,
|
|||
LOG_DEBUG("-");
|
||||
#endif
|
||||
|
||||
retval = target_get_gdb_reg_list(target, ®_list, ®_list_size);
|
||||
retval = target_get_gdb_reg_list(target, ®_list, ®_list_size,
|
||||
REG_CLASS_ALL);
|
||||
if (retval != ERROR_OK)
|
||||
return gdb_error(connection, retval);
|
||||
|
||||
|
@ -1109,7 +1123,8 @@ static int gdb_set_register_packet(struct connection *connection,
|
|||
|
||||
LOG_DEBUG("-");
|
||||
|
||||
retval = target_get_gdb_reg_list(target, ®_list, ®_list_size);
|
||||
retval = target_get_gdb_reg_list(target, ®_list, ®_list_size,
|
||||
REG_CLASS_ALL);
|
||||
if (retval != ERROR_OK)
|
||||
return gdb_error(connection, retval);
|
||||
|
||||
|
@ -1670,6 +1685,331 @@ static int gdb_memory_map(struct connection *connection,
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static const char *gdb_get_reg_type_name(enum reg_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case REG_TYPE_INT8:
|
||||
return "int8";
|
||||
case REG_TYPE_INT16:
|
||||
return "int16";
|
||||
case REG_TYPE_INT32:
|
||||
return "int32";
|
||||
case REG_TYPE_INT64:
|
||||
return "int64";
|
||||
case REG_TYPE_INT128:
|
||||
return "int128";
|
||||
case REG_TYPE_UINT8:
|
||||
return "uint8";
|
||||
case REG_TYPE_UINT16:
|
||||
return "uint16";
|
||||
case REG_TYPE_UINT32:
|
||||
return "uint32";
|
||||
case REG_TYPE_UINT64:
|
||||
return "uint64";
|
||||
case REG_TYPE_UINT128:
|
||||
return "uint128";
|
||||
case REG_TYPE_CODE_PTR:
|
||||
return "code_ptr";
|
||||
case REG_TYPE_DATA_PTR:
|
||||
return "data_ptr";
|
||||
case REG_TYPE_IEEE_SINGLE:
|
||||
return "ieee_single";
|
||||
case REG_TYPE_IEEE_DOUBLE:
|
||||
return "ieee_double";
|
||||
case REG_TYPE_ARCH_DEFINED:
|
||||
return "int"; /* return arbitrary string to avoid compile warning. */
|
||||
}
|
||||
|
||||
return "int"; /* "int" as default value */
|
||||
}
|
||||
|
||||
static int gdb_generate_reg_type_description(struct target *target,
|
||||
char **tdesc, int *pos, int *size, struct reg_data_type *type)
|
||||
{
|
||||
int retval = ERROR_OK;
|
||||
|
||||
if (type->type_class == REG_TYPE_CLASS_VECTOR) {
|
||||
/* <vector id="id" type="type" count="count"/> */
|
||||
xml_printf(&retval, tdesc, pos, size,
|
||||
"<vector id=\"%s\" type=\"%s\" count=\"%d\"/>\n",
|
||||
type->id, type->reg_type_vector->type->id,
|
||||
type->reg_type_vector->count);
|
||||
|
||||
} else if (type->type_class == REG_TYPE_CLASS_UNION) {
|
||||
/* <union id="id">
|
||||
* <field name="name" type="type"/> ...
|
||||
* </union> */
|
||||
xml_printf(&retval, tdesc, pos, size,
|
||||
"<union id=\"%s\">\n",
|
||||
type->id);
|
||||
|
||||
struct reg_data_type_union_field *field;
|
||||
field = type->reg_type_union->fields;
|
||||
while (field != NULL) {
|
||||
xml_printf(&retval, tdesc, pos, size,
|
||||
"<field name=\"%s\" type=\"%s\"/>\n",
|
||||
field->name, field->type->id);
|
||||
|
||||
field = field->next;
|
||||
}
|
||||
|
||||
xml_printf(&retval, tdesc, pos, size,
|
||||
"</union>\n");
|
||||
|
||||
} else if (type->type_class == REG_TYPE_CLASS_STRUCT) {
|
||||
struct reg_data_type_struct_field *field;
|
||||
field = type->reg_type_struct->fields;
|
||||
|
||||
if (field->use_bitfields) {
|
||||
/* <struct id="id" size="size">
|
||||
* <field name="name" start="start" end="end"/> ...
|
||||
* </struct> */
|
||||
xml_printf(&retval, tdesc, pos, size,
|
||||
"<struct id=\"%s\" size=\"%d\">\n",
|
||||
type->id, type->reg_type_struct->size);
|
||||
while (field != NULL) {
|
||||
xml_printf(&retval, tdesc, pos, size,
|
||||
"<field name=\"%s\" start=\"%d\" end=\"%d\"/>\n",
|
||||
field->name, field->bitfield->start,
|
||||
field->bitfield->end);
|
||||
|
||||
field = field->next;
|
||||
}
|
||||
} else {
|
||||
/* <struct id="id">
|
||||
* <field name="name" type="type"/> ...
|
||||
* </struct> */
|
||||
xml_printf(&retval, tdesc, pos, size,
|
||||
"<struct id=\"%s\">\n",
|
||||
type->id);
|
||||
while (field != NULL) {
|
||||
xml_printf(&retval, tdesc, pos, size,
|
||||
"<field name=\"%s\" type=\"%s\"/>\n",
|
||||
field->name, field->type->id);
|
||||
|
||||
field = field->next;
|
||||
}
|
||||
}
|
||||
|
||||
xml_printf(&retval, tdesc, pos, size,
|
||||
"</struct>\n");
|
||||
|
||||
} else if (type->type_class == REG_TYPE_CLASS_FLAGS) {
|
||||
/* <flags id="id" size="size">
|
||||
* <field name="name" start="start" end="end"/> ...
|
||||
* </flags> */
|
||||
xml_printf(&retval, tdesc, pos, size,
|
||||
"<flags id=\"%s\" size=\"%d\">\n",
|
||||
type->id, type->reg_type_flags->size);
|
||||
|
||||
struct reg_data_type_flags_field *field;
|
||||
field = type->reg_type_flags->fields;
|
||||
while (field != NULL) {
|
||||
xml_printf(&retval, tdesc, pos, size,
|
||||
"<field name=\"%s\" start=\"%d\" end=\"%d\"/>\n",
|
||||
field->name, field->bitfield->start, field->bitfield->end);
|
||||
|
||||
field = field->next;
|
||||
}
|
||||
|
||||
xml_printf(&retval, tdesc, pos, size,
|
||||
"</flags>\n");
|
||||
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
/* Get a list of available target registers features. feature_list must
|
||||
* be freed by caller.
|
||||
*/
|
||||
int get_reg_features_list(struct target *target, char **feature_list[], int *feature_list_size,
|
||||
struct reg **reg_list, int reg_list_size)
|
||||
{
|
||||
int tbl_sz = 0;
|
||||
|
||||
/* Start with only one element */
|
||||
*feature_list = calloc(1, sizeof(char *));
|
||||
|
||||
for (int i = 0; i < reg_list_size; i++) {
|
||||
if (reg_list[i]->exist == false)
|
||||
continue;
|
||||
|
||||
if ((reg_list[i]->feature->name != NULL)
|
||||
&& (strcmp(reg_list[i]->feature->name, ""))) {
|
||||
/* We found a feature, check if the feature is already in the
|
||||
* table. If not, allocate a new entry for the table and
|
||||
* put the new feature in it.
|
||||
*/
|
||||
for (int j = 0; j < (tbl_sz + 1); j++) {
|
||||
if (!((*feature_list)[j])) {
|
||||
(*feature_list)[tbl_sz++] = strdup(reg_list[i]->feature->name);
|
||||
*feature_list = realloc(*feature_list, sizeof(char *) * (tbl_sz + 1));
|
||||
(*feature_list)[tbl_sz] = NULL;
|
||||
break;
|
||||
} else {
|
||||
if (!strcmp((*feature_list)[j], reg_list[i]->feature->name))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (feature_list_size)
|
||||
*feature_list_size = tbl_sz;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int gdb_generate_target_description(struct target *target, char **tdesc)
|
||||
{
|
||||
int retval = ERROR_OK;
|
||||
struct reg **reg_list;
|
||||
int reg_list_size;
|
||||
int pos = 0;
|
||||
int size = 0;
|
||||
|
||||
xml_printf(&retval, tdesc, &pos, &size,
|
||||
"<?xml version=\"1.0\"?>\n"
|
||||
"<!DOCTYPE target SYSTEM \"gdb-target.dtd\">\n"
|
||||
"<target version=\"1.0\">\n");
|
||||
|
||||
retval = target_get_gdb_reg_list(target, ®_list,
|
||||
®_list_size, REG_CLASS_ALL);
|
||||
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("get register list failed");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (reg_list_size <= 0)
|
||||
return ERROR_FAIL;
|
||||
|
||||
char **features = NULL;
|
||||
/* Get a list of available target registers features */
|
||||
retval = get_reg_features_list(target, &features, NULL, reg_list, reg_list_size);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Can't get the registers feature list");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
/* If we found some features associated with registers, create sections */
|
||||
int current_feature = 0;
|
||||
|
||||
/* generate target description according to register list */
|
||||
if (features != NULL) {
|
||||
while (features[current_feature]) {
|
||||
|
||||
xml_printf(&retval, tdesc, &pos, &size,
|
||||
"<feature name=\"%s\">\n",
|
||||
features[current_feature]);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < reg_list_size; i++) {
|
||||
|
||||
if (reg_list[i]->exist == false)
|
||||
continue;
|
||||
|
||||
if (strcmp(reg_list[i]->feature->name, features[current_feature]))
|
||||
continue;
|
||||
|
||||
const char *type_str;
|
||||
if (reg_list[i]->reg_data_type != NULL) {
|
||||
if (reg_list[i]->reg_data_type->type == REG_TYPE_ARCH_DEFINED) {
|
||||
/* generate <type... first, if there are architecture-defined types. */
|
||||
gdb_generate_reg_type_description(target, tdesc, &pos, &size,
|
||||
reg_list[i]->reg_data_type);
|
||||
|
||||
type_str = reg_list[i]->reg_data_type->id;
|
||||
} else {
|
||||
/* predefined type */
|
||||
type_str = gdb_get_reg_type_name(
|
||||
reg_list[i]->reg_data_type->type);
|
||||
}
|
||||
} else {
|
||||
/* Default type is "int" */
|
||||
type_str = "int";
|
||||
}
|
||||
|
||||
xml_printf(&retval, tdesc, &pos, &size,
|
||||
"<reg name=\"%s\"", reg_list[i]->name);
|
||||
xml_printf(&retval, tdesc, &pos, &size,
|
||||
" bitsize=\"%d\"", reg_list[i]->size);
|
||||
xml_printf(&retval, tdesc, &pos, &size,
|
||||
" regnum=\"%d\"", reg_list[i]->number);
|
||||
if (reg_list[i]->caller_save)
|
||||
xml_printf(&retval, tdesc, &pos, &size,
|
||||
" save-restore=\"yes\"");
|
||||
else
|
||||
xml_printf(&retval, tdesc, &pos, &size,
|
||||
" save-restore=\"no\"");
|
||||
|
||||
xml_printf(&retval, tdesc, &pos, &size,
|
||||
" type=\"%s\"", type_str);
|
||||
|
||||
if (reg_list[i]->group != NULL)
|
||||
xml_printf(&retval, tdesc, &pos, &size,
|
||||
" group=\"%s\"", reg_list[i]->group);
|
||||
|
||||
xml_printf(&retval, tdesc, &pos, &size,
|
||||
"/>\n");
|
||||
}
|
||||
|
||||
xml_printf(&retval, tdesc, &pos, &size,
|
||||
"</feature>\n");
|
||||
|
||||
current_feature++;
|
||||
}
|
||||
}
|
||||
|
||||
xml_printf(&retval, tdesc, &pos, &size,
|
||||
"</target>\n");
|
||||
|
||||
if (reg_list != NULL)
|
||||
free(reg_list);
|
||||
|
||||
if (features != NULL)
|
||||
free(features);
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int gdb_get_target_description_chunk(struct target *target, char **chunk,
|
||||
int32_t offset, uint32_t length)
|
||||
{
|
||||
static char *tdesc;
|
||||
static uint32_t tdesc_length;
|
||||
|
||||
if (tdesc == NULL) {
|
||||
gdb_generate_target_description(target, &tdesc);
|
||||
tdesc_length = strlen(tdesc);
|
||||
}
|
||||
|
||||
char transfer_type;
|
||||
|
||||
if (length < (tdesc_length - offset))
|
||||
transfer_type = 'm';
|
||||
else
|
||||
transfer_type = 'l';
|
||||
|
||||
*chunk = malloc(length + 2);
|
||||
(*chunk)[0] = transfer_type;
|
||||
if (transfer_type == 'm') {
|
||||
strncpy((*chunk) + 1, tdesc + offset, length);
|
||||
(*chunk)[1 + length] = '\0';
|
||||
} else {
|
||||
strncpy((*chunk) + 1, tdesc + offset, tdesc_length - offset);
|
||||
(*chunk)[1 + (tdesc_length - offset)] = '\0';
|
||||
|
||||
/* After gdb-server sends out last chunk, invalidate tdesc. */
|
||||
free(tdesc);
|
||||
tdesc = NULL;
|
||||
tdesc_length = 0;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int gdb_query_packet(struct connection *connection,
|
||||
char *packet, int packet_size)
|
||||
{
|
||||
|
@ -1744,9 +2084,10 @@ static int gdb_query_packet(struct connection *connection,
|
|||
&buffer,
|
||||
&pos,
|
||||
&size,
|
||||
"PacketSize=%x;qXfer:memory-map:read%c;qXfer:features:read-;QStartNoAckMode+",
|
||||
"PacketSize=%x;qXfer:memory-map:read%c;qXfer:features:read%c;QStartNoAckMode+",
|
||||
(GDB_BUFFER_SIZE - 1),
|
||||
((gdb_use_memory_map == 1) && (flash_get_bank_count() > 0)) ? '+' : '-');
|
||||
((gdb_use_memory_map == 1) && (flash_get_bank_count() > 0)) ? '+' : '-',
|
||||
(gdb_use_target_description == 1) ? '+' : '-');
|
||||
|
||||
if (retval != ERROR_OK) {
|
||||
gdb_send_error(connection, 01);
|
||||
|
@ -1762,8 +2103,6 @@ static int gdb_query_packet(struct connection *connection,
|
|||
return gdb_memory_map(connection, packet, packet_size);
|
||||
else if (strncmp(packet, "qXfer:features:read:", 20) == 0) {
|
||||
char *xml = NULL;
|
||||
int size = 0;
|
||||
int pos = 0;
|
||||
int retval = ERROR_OK;
|
||||
|
||||
int offset;
|
||||
|
@ -1778,17 +2117,12 @@ static int gdb_query_packet(struct connection *connection,
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
if (strcmp(annex, "target.xml") != 0) {
|
||||
gdb_send_error(connection, 01);
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
xml_printf(&retval,
|
||||
&xml,
|
||||
&pos,
|
||||
&size, \
|
||||
"l < target version=\"1.0\">\n < architecture > arm</architecture>\n</target>\n");
|
||||
|
||||
/* Target should prepare correct target description for annex.
|
||||
* The first character of returned xml is 'm' or 'l'. 'm' for
|
||||
* there are *more* chunks to transfer. 'l' for it is the *last*
|
||||
* chunk of target description.
|
||||
*/
|
||||
retval = gdb_get_target_description_chunk(target, &xml, offset, length);
|
||||
if (retval != ERROR_OK) {
|
||||
gdb_error(connection, retval);
|
||||
return retval;
|
||||
|
@ -2372,6 +2706,54 @@ COMMAND_HANDLER(handle_gdb_breakpoint_override_command)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(handle_gdb_target_description_command)
|
||||
{
|
||||
if (CMD_ARGC != 1)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
|
||||
COMMAND_PARSE_ENABLE(CMD_ARGV[0], gdb_use_target_description);
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(handle_gdb_save_tdesc_command)
|
||||
{
|
||||
static char *tdesc;
|
||||
static uint32_t tdesc_length;
|
||||
struct target *target = get_current_target(CMD_CTX);
|
||||
char *tdesc_filename;
|
||||
|
||||
if (tdesc == NULL) {
|
||||
gdb_generate_target_description(target, &tdesc);
|
||||
tdesc_length = strlen(tdesc);
|
||||
}
|
||||
|
||||
struct fileio fileio;
|
||||
size_t size_written;
|
||||
|
||||
tdesc_filename = malloc(strlen(target_type_name(target)) + 5);
|
||||
sprintf(tdesc_filename, "%s.xml", target_type_name(target));
|
||||
|
||||
int retval = fileio_open(&fileio, tdesc_filename, FILEIO_WRITE, FILEIO_TEXT);
|
||||
|
||||
free(tdesc_filename);
|
||||
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_WARNING("Can't open %s for writing", tdesc_filename);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
retval = fileio_write(&fileio, tdesc_length, tdesc, &size_written);
|
||||
|
||||
fileio_close(&fileio);
|
||||
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_WARNING("Error while writing the tdesc file");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static const struct command_registration gdb_command_handlers[] = {
|
||||
{
|
||||
.name = "gdb_sync",
|
||||
|
@ -2424,6 +2806,19 @@ static const struct command_registration gdb_command_handlers[] = {
|
|||
"to be used by gdb 'break' commands.",
|
||||
.usage = "('hard'|'soft'|'disable')"
|
||||
},
|
||||
{
|
||||
.name = "gdb_target_description",
|
||||
.handler = handle_gdb_target_description_command,
|
||||
.mode = COMMAND_CONFIG,
|
||||
.help = "enable or disable target description",
|
||||
.usage = "('enable'|'disable')"
|
||||
},
|
||||
{
|
||||
.name = "gdb_save_tdesc",
|
||||
.handler = handle_gdb_save_tdesc_command,
|
||||
.mode = COMMAND_EXEC,
|
||||
.help = "Save the target description file",
|
||||
},
|
||||
COMMAND_REGISTRATION_DONE
|
||||
};
|
||||
|
||||
|
|
|
@ -211,7 +211,8 @@ extern const struct command_registration arm_command_handlers[];
|
|||
|
||||
int arm_arch_state(struct target *target);
|
||||
int arm_get_gdb_reg_list(struct target *target,
|
||||
struct reg **reg_list[], int *reg_list_size);
|
||||
struct reg **reg_list[], int *reg_list_size,
|
||||
enum target_register_class reg_class);
|
||||
|
||||
int arm_init_arch_info(struct target *target, struct arm *arm);
|
||||
|
||||
|
|
|
@ -1051,7 +1051,8 @@ const struct command_registration arm_command_handlers[] = {
|
|||
};
|
||||
|
||||
int arm_get_gdb_reg_list(struct target *target,
|
||||
struct reg **reg_list[], int *reg_list_size)
|
||||
struct reg **reg_list[], int *reg_list_size,
|
||||
enum target_register_class reg_class)
|
||||
{
|
||||
struct arm *arm = target_to_arm(target);
|
||||
int i;
|
||||
|
|
|
@ -259,7 +259,8 @@ static int armv7m_write_core_reg(struct target *target, struct reg *r,
|
|||
* hardware, so this also fakes a set of long-obsolete FPA registers that
|
||||
* are not used in EABI based software stacks.
|
||||
*/
|
||||
int armv7m_get_gdb_reg_list(struct target *target, struct reg **reg_list[], int *reg_list_size)
|
||||
int armv7m_get_gdb_reg_list(struct target *target, struct reg **reg_list[],
|
||||
int *reg_list_size, enum target_register_class reg_class)
|
||||
{
|
||||
struct armv7m_common *armv7m = target_to_armv7m(target);
|
||||
int i;
|
||||
|
|
|
@ -195,7 +195,8 @@ int armv7m_mode_to_number(enum armv7m_mode mode);
|
|||
|
||||
int armv7m_arch_state(struct target *target);
|
||||
int armv7m_get_gdb_reg_list(struct target *target,
|
||||
struct reg **reg_list[], int *reg_list_size);
|
||||
struct reg **reg_list[], int *reg_list_size,
|
||||
enum target_register_class reg_class);
|
||||
|
||||
int armv7m_init_arch_info(struct target *target, struct armv7m_common *armv7m);
|
||||
|
||||
|
|
|
@ -576,7 +576,8 @@ int avr32_ap7k_arch_state(struct target *target)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int avr32_ap7k_get_gdb_reg_list(struct target *target, struct reg **reg_list[], int *reg_list_size)
|
||||
int avr32_ap7k_get_gdb_reg_list(struct target *target, struct reg **reg_list[],
|
||||
int *reg_list_size, enum target_register_class reg_class)
|
||||
{
|
||||
#if 0
|
||||
/* get pointers to arch-specific information */
|
||||
|
|
|
@ -354,7 +354,8 @@ static uint8_t gdb_reg_list_idx[] = {
|
|||
|
||||
static int dsp563xx_get_gdb_reg_list(struct target *target,
|
||||
struct reg **reg_list[],
|
||||
int *reg_list_size)
|
||||
int *reg_list_size,
|
||||
enum target_register_class reg_class)
|
||||
{
|
||||
int i;
|
||||
struct dsp563xx_common *dsp563xx = target_to_dsp563xx(target);
|
||||
|
|
|
@ -173,7 +173,8 @@ static int mips32_write_core_reg(struct target *target, int num)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int mips32_get_gdb_reg_list(struct target *target, struct reg **reg_list[], int *reg_list_size)
|
||||
int mips32_get_gdb_reg_list(struct target *target, struct reg **reg_list[],
|
||||
int *reg_list_size, enum target_register_class reg_class)
|
||||
{
|
||||
/* get pointers to arch-specific information */
|
||||
struct mips32_common *mips32 = target_to_mips32(target);
|
||||
|
|
|
@ -243,7 +243,8 @@ int mips32_examine(struct target *target);
|
|||
int mips32_register_commands(struct command_context *cmd_ctx);
|
||||
|
||||
int mips32_get_gdb_reg_list(struct target *target,
|
||||
struct reg **reg_list[], int *reg_list_size);
|
||||
struct reg **reg_list[], int *reg_list_size,
|
||||
enum target_register_class reg_class);
|
||||
int mips32_checksum_memory(struct target *target, uint32_t address,
|
||||
uint32_t count, uint32_t *checksum);
|
||||
int mips32_blank_check_memory(struct target *target,
|
||||
|
|
|
@ -91,21 +91,23 @@ static int nds32_get_core_reg(struct reg *reg)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int mapped_regnum = nds32->register_map(nds32, reg_arch_info->num);
|
||||
|
||||
if (reg_arch_info->enable == false) {
|
||||
reg_arch_info->value = NDS32_REGISTER_DISABLE;
|
||||
retval = ERROR_FAIL;
|
||||
} else {
|
||||
if ((nds32->fpu_enable == false) &&
|
||||
(NDS32_REG_TYPE_FPU == nds32_reg_type(reg_arch_info->num))) {
|
||||
(NDS32_REG_TYPE_FPU == nds32_reg_type(mapped_regnum))) {
|
||||
reg_arch_info->value = 0;
|
||||
retval = ERROR_OK;
|
||||
} else if ((nds32->audio_enable == false) &&
|
||||
(NDS32_REG_TYPE_AUMR == nds32_reg_type(reg_arch_info->num))) {
|
||||
(NDS32_REG_TYPE_AUMR == nds32_reg_type(mapped_regnum))) {
|
||||
reg_arch_info->value = 0;
|
||||
retval = ERROR_OK;
|
||||
} else {
|
||||
retval = aice_read_register(aice,
|
||||
reg_arch_info->num, &(reg_arch_info->value));
|
||||
mapped_regnum, &(reg_arch_info->value));
|
||||
}
|
||||
|
||||
LOG_DEBUG("reading register %i(%s), value: 0x%8.8" PRIx32,
|
||||
|
@ -301,44 +303,46 @@ static int nds32_set_core_reg(struct reg *reg, uint8_t *buf)
|
|||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
|
||||
int mapped_regnum = nds32->register_map(nds32, reg_arch_info->num);
|
||||
|
||||
/* ignore values that will generate exception */
|
||||
if (nds32_reg_exception(reg_arch_info->num, value))
|
||||
if (nds32_reg_exception(mapped_regnum, value))
|
||||
return ERROR_OK;
|
||||
|
||||
LOG_DEBUG("writing register %i(%s) with value 0x%8.8" PRIx32,
|
||||
reg_arch_info->num, reg->name, value);
|
||||
|
||||
if ((nds32->fpu_enable == false) &&
|
||||
(NDS32_REG_TYPE_FPU == nds32_reg_type(reg_arch_info->num))) {
|
||||
(NDS32_REG_TYPE_FPU == nds32_reg_type(mapped_regnum))) {
|
||||
|
||||
buf_set_u32(reg->value, 0, 32, 0);
|
||||
} else if ((nds32->audio_enable == false) &&
|
||||
(NDS32_REG_TYPE_AUMR == nds32_reg_type(reg_arch_info->num))) {
|
||||
(NDS32_REG_TYPE_AUMR == nds32_reg_type(mapped_regnum))) {
|
||||
|
||||
buf_set_u32(reg->value, 0, 32, 0);
|
||||
} else {
|
||||
buf_set_u32(reg->value, 0, 32, value);
|
||||
aice_write_register(aice, reg_arch_info->num, reg_arch_info->value);
|
||||
aice_write_register(aice, mapped_regnum, reg_arch_info->value);
|
||||
|
||||
/* After set value to registers, read the value from target
|
||||
* to avoid W1C inconsistency. */
|
||||
aice_read_register(aice, reg_arch_info->num, &(reg_arch_info->value));
|
||||
aice_read_register(aice, mapped_regnum, &(reg_arch_info->value));
|
||||
}
|
||||
|
||||
reg->valid = true;
|
||||
reg->dirty = false;
|
||||
|
||||
/* update registers to take effect right now */
|
||||
if (IR0 == reg_arch_info->num) {
|
||||
if (IR0 == mapped_regnum) {
|
||||
nds32_update_psw(nds32);
|
||||
} else if (MR0 == reg_arch_info->num) {
|
||||
} else if (MR0 == mapped_regnum) {
|
||||
nds32_update_mmu_info(nds32);
|
||||
} else if ((MR6 == reg_arch_info->num) || (MR7 == reg_arch_info->num)) {
|
||||
} else if ((MR6 == mapped_regnum) || (MR7 == mapped_regnum)) {
|
||||
/* update lm information */
|
||||
nds32_update_lm_info(nds32);
|
||||
} else if (MR8 == reg_arch_info->num) {
|
||||
} else if (MR8 == mapped_regnum) {
|
||||
nds32_update_cache_info(nds32);
|
||||
} else if (FUCPR == reg_arch_info->num) {
|
||||
} else if (FUCPR == mapped_regnum) {
|
||||
/* update audio/fpu setting */
|
||||
nds32_check_extension(nds32);
|
||||
}
|
||||
|
@ -415,16 +419,61 @@ static struct reg_cache *nds32_build_reg_cache(struct target *target,
|
|||
reg_arch_info[i].enable = false;
|
||||
|
||||
reg_list[i].name = nds32_reg_simple_name(i);
|
||||
reg_list[i].number = reg_arch_info[i].num;
|
||||
reg_list[i].size = nds32_reg_size(i);
|
||||
reg_list[i].arch_info = ®_arch_info[i];
|
||||
|
||||
reg_list[i].reg_data_type = malloc(sizeof(struct reg_data_type));
|
||||
|
||||
if (FD0 <= reg_arch_info[i].num && reg_arch_info[i].num <= FD31) {
|
||||
reg_list[i].value = &(reg_arch_info[i].value_64);
|
||||
reg_list[i].type = &nds32_reg_access_type_64;
|
||||
|
||||
reg_list[i].reg_data_type->type = REG_TYPE_IEEE_DOUBLE;
|
||||
reg_list[i].reg_data_type->id = "ieee_double";
|
||||
reg_list[i].group = "float";
|
||||
} else {
|
||||
reg_list[i].value = &(reg_arch_info[i].value);
|
||||
reg_list[i].type = &nds32_reg_access_type;
|
||||
reg_list[i].group = "general";
|
||||
|
||||
if ((FS0 <= reg_arch_info[i].num) && (reg_arch_info[i].num <= FS31)) {
|
||||
reg_list[i].reg_data_type->type = REG_TYPE_IEEE_SINGLE;
|
||||
reg_list[i].reg_data_type->id = "ieee_single";
|
||||
reg_list[i].group = "float";
|
||||
} else if ((reg_arch_info[i].num == FPCSR) ||
|
||||
(reg_arch_info[i].num == FPCFG)) {
|
||||
reg_list[i].group = "float";
|
||||
} else if ((reg_arch_info[i].num == R28) ||
|
||||
(reg_arch_info[i].num == R29) ||
|
||||
(reg_arch_info[i].num == R31)) {
|
||||
reg_list[i].reg_data_type->type = REG_TYPE_DATA_PTR;
|
||||
reg_list[i].reg_data_type->id = "data_ptr";
|
||||
} else if ((reg_arch_info[i].num == R30) ||
|
||||
(reg_arch_info[i].num == PC)) {
|
||||
reg_list[i].reg_data_type->type = REG_TYPE_CODE_PTR;
|
||||
reg_list[i].reg_data_type->id = "code_ptr";
|
||||
} else {
|
||||
reg_list[i].reg_data_type->type = REG_TYPE_UINT32;
|
||||
reg_list[i].reg_data_type->id = "uint32";
|
||||
}
|
||||
}
|
||||
|
||||
if (R16 <= reg_arch_info[i].num && reg_arch_info[i].num <= R25)
|
||||
reg_list[i].caller_save = true;
|
||||
else
|
||||
reg_list[i].caller_save = false;
|
||||
|
||||
reg_list[i].feature = malloc(sizeof(struct reg_feature));
|
||||
|
||||
if (R0 <= reg_arch_info[i].num && reg_arch_info[i].num <= IFC_LP)
|
||||
reg_list[i].feature->name = "org.gnu.gdb.nds32.core";
|
||||
else if (CR0 <= reg_arch_info[i].num && reg_arch_info[i].num <= SECUR0)
|
||||
reg_list[i].feature->name = "org.gnu.gdb.nds32.system";
|
||||
else if (D0L24 <= reg_arch_info[i].num && reg_arch_info[i].num <= CBE3)
|
||||
reg_list[i].feature->name = "org.gnu.gdb.nds32.audio";
|
||||
else if (FPCSR <= reg_arch_info[i].num && reg_arch_info[i].num <= FD31)
|
||||
reg_list[i].feature->name = "org.gnu.gdb.nds32.fpu";
|
||||
|
||||
cache->num_regs++;
|
||||
}
|
||||
|
@ -451,9 +500,7 @@ static struct reg *nds32_reg_current(struct nds32 *nds32, unsigned regnum)
|
|||
{
|
||||
struct reg *r;
|
||||
|
||||
/* Register mapping, pass user-view registers to gdb */
|
||||
int mapped_regnum = nds32->register_map(nds32, regnum);
|
||||
r = nds32->core_cache->reg_list + mapped_regnum;
|
||||
r = nds32->core_cache->reg_list + regnum;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
@ -512,12 +559,36 @@ int nds32_set_mapped_reg(struct nds32 *nds32, unsigned regnum, uint32_t value)
|
|||
return r->type->set(r, set_value);
|
||||
}
|
||||
|
||||
/** get all register list */
|
||||
int nds32_get_gdb_reg_list(struct target *target,
|
||||
/** get general register list */
|
||||
static int nds32_get_general_reg_list(struct nds32 *nds32,
|
||||
struct reg **reg_list[], int *reg_list_size)
|
||||
{
|
||||
struct reg *reg_current;
|
||||
int i;
|
||||
int current_idx;
|
||||
|
||||
/** freed in gdb_server.c */
|
||||
*reg_list = malloc(sizeof(struct reg *) * (IFC_LP - R0 + 1));
|
||||
current_idx = 0;
|
||||
|
||||
for (i = R0; i < IFC_LP + 1; i++) {
|
||||
reg_current = nds32_reg_current(nds32, i);
|
||||
if (((struct nds32_reg *)reg_current->arch_info)->enable) {
|
||||
(*reg_list)[current_idx] = reg_current;
|
||||
current_idx++;
|
||||
}
|
||||
}
|
||||
*reg_list_size = current_idx;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
/** get all register list */
|
||||
static int nds32_get_all_reg_list(struct nds32 *nds32,
|
||||
struct reg **reg_list[], int *reg_list_size)
|
||||
{
|
||||
struct nds32 *nds32 = target_to_nds32(target);
|
||||
struct reg_cache *reg_cache = nds32->core_cache;
|
||||
struct reg *reg_current;
|
||||
unsigned int i;
|
||||
|
||||
*reg_list_size = reg_cache->num_regs;
|
||||
|
@ -525,12 +596,35 @@ int nds32_get_gdb_reg_list(struct target *target,
|
|||
/** freed in gdb_server.c */
|
||||
*reg_list = malloc(sizeof(struct reg *) * (*reg_list_size));
|
||||
|
||||
for (i = 0; i < reg_cache->num_regs; i++)
|
||||
(*reg_list)[i] = nds32_reg_current(nds32, i);
|
||||
for (i = 0; i < reg_cache->num_regs; i++) {
|
||||
reg_current = nds32_reg_current(nds32, i);
|
||||
reg_current->exist = ((struct nds32_reg *)
|
||||
reg_current->arch_info)->enable;
|
||||
(*reg_list)[i] = reg_current;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
/** get all register list */
|
||||
int nds32_get_gdb_reg_list(struct target *target,
|
||||
struct reg **reg_list[], int *reg_list_size,
|
||||
enum target_register_class reg_class)
|
||||
{
|
||||
struct nds32 *nds32 = target_to_nds32(target);
|
||||
|
||||
switch (reg_class) {
|
||||
case REG_CLASS_ALL:
|
||||
return nds32_get_all_reg_list(nds32, reg_list, reg_list_size);
|
||||
case REG_CLASS_GENERAL:
|
||||
return nds32_get_general_reg_list(nds32, reg_list, reg_list_size);
|
||||
default:
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
static int nds32_select_memory_mode(struct target *target, uint32_t address,
|
||||
uint32_t length, uint32_t *end_address)
|
||||
{
|
||||
|
|
|
@ -341,7 +341,7 @@ struct nds32 {
|
|||
};
|
||||
|
||||
struct nds32_reg {
|
||||
uint32_t num;
|
||||
int32_t num;
|
||||
uint32_t value;
|
||||
uint64_t value_64;
|
||||
struct target *target;
|
||||
|
@ -364,7 +364,8 @@ extern int nds32_remove_software_breakpoint(struct target *target,
|
|||
struct breakpoint *breakpoint);
|
||||
|
||||
extern int nds32_get_gdb_reg_list(struct target *target,
|
||||
struct reg **reg_list[], int *reg_list_size);
|
||||
struct reg **reg_list[], int *reg_list_size,
|
||||
enum target_register_class reg_class);
|
||||
|
||||
extern int nds32_write_buffer(struct target *target, uint32_t address,
|
||||
uint32_t size, const uint8_t *buffer);
|
||||
|
|
|
@ -26,12 +26,105 @@
|
|||
|
||||
struct target;
|
||||
|
||||
enum reg_type {
|
||||
REG_TYPE_INT8,
|
||||
REG_TYPE_INT16,
|
||||
REG_TYPE_INT32,
|
||||
REG_TYPE_INT64,
|
||||
REG_TYPE_INT128,
|
||||
REG_TYPE_UINT8,
|
||||
REG_TYPE_UINT16,
|
||||
REG_TYPE_UINT32,
|
||||
REG_TYPE_UINT64,
|
||||
REG_TYPE_UINT128,
|
||||
REG_TYPE_CODE_PTR,
|
||||
REG_TYPE_DATA_PTR,
|
||||
REG_TYPE_IEEE_SINGLE,
|
||||
REG_TYPE_IEEE_DOUBLE,
|
||||
REG_TYPE_ARCH_DEFINED,
|
||||
};
|
||||
|
||||
struct reg_feature {
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct reg_data_type_vector {
|
||||
struct reg_data_type *type;
|
||||
uint32_t count;
|
||||
};
|
||||
|
||||
struct reg_data_type_union_field {
|
||||
const char *name;
|
||||
struct reg_data_type *type;
|
||||
struct reg_data_type_union_field *next;
|
||||
};
|
||||
|
||||
struct reg_data_type_union {
|
||||
struct reg_data_type_union_field *fields;
|
||||
};
|
||||
|
||||
struct reg_data_type_bitfield {
|
||||
uint32_t start;
|
||||
uint32_t end;
|
||||
};
|
||||
|
||||
struct reg_data_type_struct_field {
|
||||
const char *name;
|
||||
bool use_bitfields;
|
||||
union {
|
||||
struct reg_data_type_bitfield *bitfield;
|
||||
struct reg_data_type *type;
|
||||
};
|
||||
struct reg_data_type_struct_field *next;
|
||||
};
|
||||
|
||||
struct reg_data_type_struct {
|
||||
uint32_t size;
|
||||
struct reg_data_type_struct_field *fields;
|
||||
};
|
||||
|
||||
struct reg_data_type_flags_field {
|
||||
const char *name;
|
||||
struct reg_data_type_bitfield *bitfield;
|
||||
struct reg_data_type_flags_field *next;
|
||||
};
|
||||
|
||||
struct reg_data_type_flags {
|
||||
uint32_t size;
|
||||
struct reg_data_type_flags_field *fields;
|
||||
};
|
||||
|
||||
enum reg_data_type_class {
|
||||
REG_TYPE_CLASS_VECTOR,
|
||||
REG_TYPE_CLASS_UNION,
|
||||
REG_TYPE_CLASS_STRUCT,
|
||||
REG_TYPE_CLASS_FLAGS,
|
||||
};
|
||||
|
||||
struct reg_data_type {
|
||||
enum reg_type type;
|
||||
const char *id;
|
||||
enum reg_data_type_class type_class;
|
||||
union {
|
||||
struct reg_data_type_vector *reg_type_vector;
|
||||
struct reg_data_type_union *reg_type_union;
|
||||
struct reg_data_type_struct *reg_type_struct;
|
||||
struct reg_data_type_flags *reg_type_flags;
|
||||
};
|
||||
};
|
||||
|
||||
struct reg {
|
||||
const char *name;
|
||||
uint32_t number;
|
||||
struct reg_feature *feature;
|
||||
bool caller_save;
|
||||
void *value;
|
||||
bool dirty;
|
||||
bool valid;
|
||||
bool exist;
|
||||
uint32_t size;
|
||||
struct reg_data_type *reg_data_type;
|
||||
const char *group;
|
||||
void *arch_info;
|
||||
const struct reg_arch_type *type;
|
||||
};
|
||||
|
|
|
@ -1037,9 +1037,10 @@ int target_remove_watchpoint(struct target *target,
|
|||
}
|
||||
|
||||
int target_get_gdb_reg_list(struct target *target,
|
||||
struct reg **reg_list[], int *reg_list_size)
|
||||
struct reg **reg_list[], int *reg_list_size,
|
||||
enum target_register_class reg_class)
|
||||
{
|
||||
return target->type->get_gdb_reg_list(target, reg_list, reg_list_size);
|
||||
return target->type->get_gdb_reg_list(target, reg_list, reg_list_size, reg_class);
|
||||
}
|
||||
int target_step(struct target *target,
|
||||
int current, uint32_t address, int handle_breakpoints)
|
||||
|
|
|
@ -114,6 +114,12 @@ struct backoff_timer {
|
|||
int count;
|
||||
};
|
||||
|
||||
/* split target registers into multiple class */
|
||||
enum target_register_class {
|
||||
REG_CLASS_ALL,
|
||||
REG_CLASS_GENERAL,
|
||||
};
|
||||
|
||||
/* target_type.h contains the full definition of struct target_type */
|
||||
struct target {
|
||||
struct target_type *type; /* target type definition (name, access functions) */
|
||||
|
@ -399,7 +405,8 @@ int target_remove_watchpoint(struct target *target,
|
|||
* This routine is a wrapper for target->type->get_gdb_reg_list.
|
||||
*/
|
||||
int target_get_gdb_reg_list(struct target *target,
|
||||
struct reg **reg_list[], int *reg_list_size);
|
||||
struct reg **reg_list[], int *reg_list_size,
|
||||
enum target_register_class reg_class);
|
||||
|
||||
/**
|
||||
* Step the target.
|
||||
|
|
|
@ -101,7 +101,8 @@ struct target_type {
|
|||
* list, however it is after GDB is connected that monitor commands can
|
||||
* be run to properly initialize the target
|
||||
*/
|
||||
int (*get_gdb_reg_list)(struct target *target, struct reg **reg_list[], int *reg_list_size);
|
||||
int (*get_gdb_reg_list)(struct target *target, struct reg **reg_list[],
|
||||
int *reg_list_size, enum target_register_class reg_class);
|
||||
|
||||
/* target memory access
|
||||
* size: 1 = byte (8bit), 2 = half-word (16bit), 4 = word (32bit)
|
||||
|
|
Loading…
Reference in New Issue