Skip to content

Commit ba8c1ee

Browse files
committed
target/riscv: allow hexadecimal values to expose_csr-like commands
hexadecimal values are often used in the documentation. Forcing user to convert CSRs addresses to decimal is unnecessary.
1 parent f51900b commit ba8c1ee

File tree

1 file changed

+81
-50
lines changed

1 file changed

+81
-50
lines changed

src/target/riscv/riscv.c

Lines changed: 81 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -4008,74 +4008,96 @@ COMMAND_HANDLER(riscv_set_mem_access)
40084008
return ERROR_OK;
40094009
}
40104010

4011-
static int parse_ranges(struct list_head *ranges, const char *tcl_arg, const char *reg_type, unsigned int max_val)
4012-
{
4013-
char *args = strdup(tcl_arg);
4014-
if (!args)
4015-
return ERROR_FAIL;
40164011

4017-
/* For backward compatibility, allow multiple parameters within one TCL argument, separated by ',' */
4018-
char *arg = strtok(args, ",");
4019-
while (arg) {
4012+
static bool parse_csr_address(const char *reg_address_str, unsigned int *reg_addr)
4013+
{
4014+
*reg_addr = -1;
4015+
/* skip initial spaces */
4016+
while (isspace(reg_address_str[0]))
4017+
++reg_address_str;
4018+
/* try to detect if string starts with 0x or 0X */
4019+
bool is_hex_address = strncmp(reg_address_str, "0x", 2) == 0 ||
4020+
strncmp(reg_address_str, "0X", 2) == 0;
4021+
4022+
unsigned int scanned_chars;
4023+
if (is_hex_address) {
4024+
reg_address_str += 2;
4025+
if (sscanf(reg_address_str, "%x%n", reg_addr, &scanned_chars) != 1)
4026+
return false;
4027+
} else {
4028+
/* If we are here and register address string starts with zero, this is
4029+
* an indication that most likely user has an incorrect input because:
4030+
* - decimal numbers typically do not start with "0"
4031+
* - octals are not supported by our interface
4032+
* - hexadecimal numbers should have "0x" prefix
4033+
* Thus such input is rejected. */
4034+
if (reg_address_str[0] == '0' && strlen(reg_address_str) > 1)
4035+
return false;
4036+
if (sscanf(reg_address_str, "%u%n", reg_addr, &scanned_chars) != 1)
4037+
return false;
4038+
}
4039+
return scanned_chars == strlen(reg_address_str);
4040+
}
4041+
4042+
static int parse_reg_ranges_impl(struct list_head *ranges, char *args,
4043+
const char *reg_type, unsigned int max_val, char ** const name_buffer)
4044+
{
4045+
/* For backward compatibility, allow multiple parameters within one TCL
4046+
* argument, separated by ',' */
4047+
for (char *arg = strtok(args, ","); arg; arg = strtok(NULL, ",")) {
40204048
unsigned int low = 0;
40214049
unsigned int high = 0;
40224050
char *name = NULL;
40234051

40244052
char *dash = strchr(arg, '-');
40254053
char *equals = strchr(arg, '=');
4026-
unsigned int pos;
40274054

40284055
if (!dash && !equals) {
40294056
/* Expecting single register number. */
4030-
if (sscanf(arg, "%u%n", &low, &pos) != 1 || pos != strlen(arg)) {
4057+
if (!parse_csr_address(arg, &low)) {
40314058
LOG_ERROR("Failed to parse single register number from '%s'.", arg);
4032-
free(args);
40334059
return ERROR_COMMAND_SYNTAX_ERROR;
40344060
}
40354061
} else if (dash && !equals) {
40364062
/* Expecting register range - two numbers separated by a dash: ##-## */
4037-
*dash = 0;
4038-
dash++;
4039-
if (sscanf(arg, "%u%n", &low, &pos) != 1 || pos != strlen(arg)) {
4040-
LOG_ERROR("Failed to parse single register number from '%s'.", arg);
4041-
free(args);
4063+
*dash = '\0';
4064+
if (!parse_csr_address(arg, &low)) {
4065+
LOG_ERROR("Failed to parse '%s' - not a valid decimal or hexadecimal number.",
4066+
arg);
40424067
return ERROR_COMMAND_SYNTAX_ERROR;
40434068
}
4044-
if (sscanf(dash, "%u%n", &high, &pos) != 1 || pos != strlen(dash)) {
4045-
LOG_ERROR("Failed to parse single register number from '%s'.", dash);
4046-
free(args);
4069+
const char *high_num_in = dash + 1;
4070+
if (!parse_csr_address(high_num_in, &high)) {
4071+
LOG_ERROR("Failed to parse '%s' - not a valid decimal or hexadecimal number.",
4072+
high_num_in);
40474073
return ERROR_COMMAND_SYNTAX_ERROR;
40484074
}
40494075
if (high < low) {
40504076
LOG_ERROR("Incorrect range encountered [%u, %u].", low, high);
4051-
free(args);
40524077
return ERROR_FAIL;
40534078
}
40544079
} else if (!dash && equals) {
40554080
/* Expecting single register number with textual name specified: ##=name */
4056-
*equals = 0;
4057-
equals++;
4058-
if (sscanf(arg, "%u%n", &low, &pos) != 1 || pos != strlen(arg)) {
4059-
LOG_ERROR("Failed to parse single register number from '%s'.", arg);
4060-
free(args);
4081+
*equals = '\0';
4082+
if (!parse_csr_address(arg, &low)) {
4083+
LOG_ERROR("Failed to parse '%s' - not a valid decimal or hexadecimal number.",
4084+
arg);
40614085
return ERROR_COMMAND_SYNTAX_ERROR;
40624086
}
40634087

4064-
name = calloc(1, strlen(equals) + strlen(reg_type) + 2);
4088+
const char * const reg_name_in = equals + 1;
4089+
*name_buffer = calloc(1, strlen(reg_name_in) + strlen(reg_type) + 2);
4090+
name = *name_buffer;
40654091
if (!name) {
4066-
LOG_ERROR("Failed to allocate register name.");
4067-
free(args);
4092+
LOG_ERROR("Out of memory");
40684093
return ERROR_FAIL;
40694094
}
40704095

4071-
/* Register prefix: "csr_" or "custom_" */
4072-
strcpy(name, reg_type);
4073-
name[strlen(reg_type)] = '_';
4074-
4075-
if (sscanf(equals, "%[_a-zA-Z0-9]%n", name + strlen(reg_type) + 1, &pos) != 1 || pos != strlen(equals)) {
4076-
LOG_ERROR("Failed to parse register name from '%s'.", equals);
4077-
free(args);
4078-
free(name);
4096+
unsigned int scanned_chars;
4097+
char *scan_dst = name + strlen(reg_type) + 1;
4098+
if (sscanf(reg_name_in, "%[_a-zA-Z0-9]%n", scan_dst, &scanned_chars) != 1 ||
4099+
scanned_chars != strlen(reg_name_in)) {
4100+
LOG_ERROR("Invalid characters in register name '%s'.", reg_name_in);
40794101
return ERROR_COMMAND_SYNTAX_ERROR;
40804102
}
40814103
} else {
@@ -4087,9 +4109,8 @@ static int parse_ranges(struct list_head *ranges, const char *tcl_arg, const cha
40874109
high = MAX(high, low);
40884110

40894111
if (high > max_val) {
4090-
LOG_ERROR("Cannot expose %s register number %u, maximum allowed value is %u.", reg_type, high, max_val);
4091-
free(name);
4092-
free(args);
4112+
LOG_ERROR("Cannot expose %s register number 0x%x, maximum allowed value is 0x%x.",
4113+
reg_type, high, max_val);
40934114
return ERROR_FAIL;
40944115
}
40954116

@@ -4107,32 +4128,42 @@ static int parse_ranges(struct list_head *ranges, const char *tcl_arg, const cha
41074128

41084129
if (entry->name && name && (strcasecmp(entry->name, name) == 0)) {
41094130
LOG_ERROR("Duplicate register name \"%s\" found.", name);
4110-
free(name);
4111-
free(args);
41124131
return ERROR_FAIL;
41134132
}
41144133
}
41154134

41164135
range_list_t *range = calloc(1, sizeof(range_list_t));
41174136
if (!range) {
4118-
LOG_ERROR("Failed to allocate range list.");
4119-
free(name);
4120-
free(args);
4137+
LOG_ERROR("Out of memory");
41214138
return ERROR_FAIL;
41224139
}
41234140

41244141
range->low = low;
41254142
range->high = high;
41264143
range->name = name;
4144+
/* ownership over name_buffer contents is transferred to list item here */
4145+
*name_buffer = NULL;
41274146
list_add(&range->list, ranges);
4128-
4129-
arg = strtok(NULL, ",");
41304147
}
41314148

4132-
free(args);
41334149
return ERROR_OK;
41344150
}
41354151

4152+
static int parse_reg_ranges(struct list_head *ranges, const char *tcl_arg,
4153+
const char *reg_type, unsigned int max_val)
4154+
{
4155+
char *args = strdup(tcl_arg);
4156+
if (!args) {
4157+
LOG_ERROR("Out of memory");
4158+
return ERROR_FAIL;
4159+
}
4160+
char *name_buffer = NULL;
4161+
int result = parse_reg_ranges_impl(ranges, args, reg_type, max_val, &name_buffer);
4162+
free(name_buffer);
4163+
free(args);
4164+
return result;
4165+
}
4166+
41364167
COMMAND_HANDLER(riscv_set_expose_csrs)
41374168
{
41384169
if (CMD_ARGC == 0)
@@ -4143,7 +4174,7 @@ COMMAND_HANDLER(riscv_set_expose_csrs)
41434174
int ret = ERROR_OK;
41444175

41454176
for (unsigned int i = 0; i < CMD_ARGC; i++) {
4146-
ret = parse_ranges(&info->expose_csr, CMD_ARGV[i], "csr", 0xfff);
4177+
ret = parse_reg_ranges(&info->expose_csr, CMD_ARGV[i], "csr", 0xfff);
41474178
if (ret != ERROR_OK)
41484179
break;
41494180
}
@@ -4161,7 +4192,7 @@ COMMAND_HANDLER(riscv_set_expose_custom)
41614192
int ret = ERROR_OK;
41624193

41634194
for (unsigned int i = 0; i < CMD_ARGC; i++) {
4164-
ret = parse_ranges(&info->expose_custom, CMD_ARGV[i], "custom", 0x3fff);
4195+
ret = parse_reg_ranges(&info->expose_custom, CMD_ARGV[i], "custom", 0x3fff);
41654196
if (ret != ERROR_OK)
41664197
break;
41674198
}
@@ -4179,7 +4210,7 @@ COMMAND_HANDLER(riscv_hide_csrs)
41794210
int ret = ERROR_OK;
41804211

41814212
for (unsigned int i = 0; i < CMD_ARGC; i++) {
4182-
ret = parse_ranges(&info->hide_csr, CMD_ARGV[i], "csr", 0xfff);
4213+
ret = parse_reg_ranges(&info->hide_csr, CMD_ARGV[i], "csr", 0xfff);
41834214
if (ret != ERROR_OK)
41844215
break;
41854216
}

0 commit comments

Comments
 (0)