target/riscv: added translation drivers

Existing flags: 'enable_virtual' and 'enable_virt2phys' were
replaced with explicit translation drivers. Motivation:

(1) Having 'enable_virtual' and 'enable_virt2phys' flags set simultaneously
may cause double address translation which is unacceptable

(2) Flags were global for all targets which is wrong too

Signed-off-by: Farid Khaydari <f.khaydari@syntacore.com>
This commit is contained in:
Farid Khaydari 2024-10-23 12:36:29 +03:00
parent a4020f1a02
commit 6a27d9fbc0
6 changed files with 162 additions and 62 deletions

View File

@ -11356,16 +11356,18 @@ This command can be used to change the memory access methods if the default
behavior is not suitable for a particular target. behavior is not suitable for a particular target.
@end deffn @end deffn
@deffn {Command} {riscv set_enable_virtual} on|off @deffn {Command} {riscv virt2phys_mode} [@option{hw}|@option{sw}|@option{off}]
When on, memory accesses are performed on physical or virtual memory depending Configure how OpenOCD translates virtual addresses to physical:
on the current system configuration. When off (default), all memory accessses are performed @itemize @bullet
on physical memory. @item @option{sw} - OpenOCD translates virtual addresses explicitly by
@end deffn traversing the page table entries (by performing physical memory accesses to
read the respective entries). This is the default mode.
@deffn {Command} {riscv set_enable_virt2phys} on|off @item @option{hw} - Virtual addresses are translated implicitly by hardware.
When on (default), memory accesses are performed on physical or virtual memory (Virtual memory access will fail with an error if the hardware doesn't
depending on the current satp configuration. When off, all memory accessses are support the necessary functionality.)
performed on physical memory. @item @option{off} - Virtual addresses are not translated (identity mapping is assumed).
@end itemize
Returns current translation mode if called without arguments.
@end deffn @end deffn
@deffn {Command} {riscv resume_order} normal|reversed @deffn {Command} {riscv resume_order} normal|reversed

View File

@ -26,3 +26,5 @@ noinst_LTLIBRARIES += %D%/libriscv.la
%D%/riscv_semihosting.c \ %D%/riscv_semihosting.c \
%D%/debug_defines.c \ %D%/debug_defines.c \
%D%/debug_reg_printer.c %D%/debug_reg_printer.c
STARTUP_TCL_SRCS += %D%/startup.tcl

View File

@ -1974,11 +1974,11 @@ static int examine(struct target *target)
info->impebreak); info->impebreak);
} }
if (info->progbufsize < 4 && riscv_enable_virtual) { if (info->progbufsize < 4 && riscv_virt2phys_mode_is_hw(target)) {
LOG_TARGET_ERROR(target, "set_enable_virtual is not available on this target. It " LOG_TARGET_ERROR(target, "software address translation "
"requires a program buffer size of at least 4. (progbufsize=%d) " "is not available on this target. It requires a "
"Use `riscv set_enable_virtual off` to continue." "program buffer size of at least 4. (progbufsize=%d) "
, info->progbufsize); "Use `riscv set_enable_virtual off` to continue.", info->progbufsize);
} }
/* Don't call any riscv_* functions until after we've counted the number of /* Don't call any riscv_* functions until after we've counted the number of
@ -3054,7 +3054,8 @@ static int read_sbcs_nonbusy(struct target *target, uint32_t *sbcs)
static int modify_privilege(struct target *target, uint64_t *mstatus, uint64_t *mstatus_old) static int modify_privilege(struct target *target, uint64_t *mstatus, uint64_t *mstatus_old)
{ {
if (riscv_enable_virtual && has_sufficient_progbuf(target, 5)) { if (riscv_virt2phys_mode_is_hw(target)
&& has_sufficient_progbuf(target, 5)) {
/* Read DCSR */ /* Read DCSR */
uint64_t dcsr; uint64_t dcsr;
if (register_read_direct(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK) if (register_read_direct(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK)
@ -4259,7 +4260,8 @@ read_memory_progbuf(struct target *target, target_addr_t address,
if (modify_privilege(target, &mstatus, &mstatus_old) != ERROR_OK) if (modify_privilege(target, &mstatus, &mstatus_old) != ERROR_OK)
return MEM_ACCESS_FAILED_PRIV_MOD_FAILED; return MEM_ACCESS_FAILED_PRIV_MOD_FAILED;
const bool mprven = riscv_enable_virtual && get_field(mstatus, MSTATUS_MPRV); const bool mprven = riscv_virt2phys_mode_is_hw(target)
&& get_field(mstatus, MSTATUS_MPRV);
const struct memory_access_info access = { const struct memory_access_info access = {
.target_address = address, .target_address = address,
.increment = increment, .increment = increment,
@ -4831,7 +4833,8 @@ write_memory_progbuf(struct target *target, target_addr_t address,
if (modify_privilege(target, &mstatus, &mstatus_old) != ERROR_OK) if (modify_privilege(target, &mstatus, &mstatus_old) != ERROR_OK)
return MEM_ACCESS_FAILED_PRIV_MOD_FAILED; return MEM_ACCESS_FAILED_PRIV_MOD_FAILED;
const bool mprven = riscv_enable_virtual && get_field(mstatus, MSTATUS_MPRV); const bool mprven = riscv_virt2phys_mode_is_hw(target)
&& get_field(mstatus, MSTATUS_MPRV);
int result = write_memory_progbuf_inner(target, address, size, count, buffer, mprven); int result = write_memory_progbuf_inner(target, address, size, count, buffer, mprven);

View File

@ -139,6 +139,35 @@ struct tdata1_cache {
struct list_head elem_tdata1; struct list_head elem_tdata1;
}; };
bool riscv_virt2phys_mode_is_hw(const struct target *target)
{
assert(target);
RISCV_INFO(r);
return r->virt2phys_mode == RISCV_VIRT2PHYS_MODE_HW;
}
bool riscv_virt2phys_mode_is_sw(const struct target *target)
{
assert(target);
RISCV_INFO(r);
return r->virt2phys_mode == RISCV_VIRT2PHYS_MODE_SW;
}
const char *riscv_virt2phys_mode_to_str(riscv_virt2phys_mode_t mode)
{
assert(mode == RISCV_VIRT2PHYS_MODE_OFF
|| mode == RISCV_VIRT2PHYS_MODE_SW
|| mode == RISCV_VIRT2PHYS_MODE_HW);
static const char *const names[] = {
[RISCV_VIRT2PHYS_MODE_HW] = "hw",
[RISCV_VIRT2PHYS_MODE_SW] = "sw",
[RISCV_VIRT2PHYS_MODE_OFF] = "off",
};
return names[mode];
}
/* Wall-clock timeout for a command/access. Settable via RISC-V Target commands.*/ /* Wall-clock timeout for a command/access. Settable via RISC-V Target commands.*/
static int riscv_command_timeout_sec_value = DEFAULT_COMMAND_TIMEOUT_SEC; static int riscv_command_timeout_sec_value = DEFAULT_COMMAND_TIMEOUT_SEC;
@ -150,10 +179,6 @@ int riscv_get_command_timeout_sec(void)
return MAX(riscv_command_timeout_sec_value, riscv_reset_timeout_sec); return MAX(riscv_command_timeout_sec_value, riscv_reset_timeout_sec);
} }
static bool riscv_enable_virt2phys = true;
bool riscv_enable_virtual;
static enum { static enum {
RO_NORMAL, RO_NORMAL,
RO_REVERSED RO_REVERSED
@ -2714,7 +2739,7 @@ static int riscv_mmu(struct target *target, int *enabled)
{ {
*enabled = 0; *enabled = 0;
if (!riscv_enable_virt2phys) if (!riscv_virt2phys_mode_is_sw(target))
return ERROR_OK; return ERROR_OK;
/* Don't use MMU in explicit or effective M (machine) mode */ /* Don't use MMU in explicit or effective M (machine) mode */
@ -3938,16 +3963,6 @@ COMMAND_HANDLER(riscv_set_mem_access)
return ERROR_OK; return ERROR_OK;
} }
COMMAND_HANDLER(riscv_set_enable_virtual)
{
if (CMD_ARGC != 1) {
LOG_ERROR("Command takes exactly 1 parameter");
return ERROR_COMMAND_SYNTAX_ERROR;
}
COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_enable_virtual);
return ERROR_OK;
}
static int parse_ranges(struct list_head *ranges, const char *tcl_arg, const char *reg_type, unsigned int max_val) static int parse_ranges(struct list_head *ranges, const char *tcl_arg, const char *reg_type, unsigned int max_val)
{ {
char *args = strdup(tcl_arg); char *args = strdup(tcl_arg);
@ -4459,16 +4474,6 @@ COMMAND_HANDLER(riscv_set_maskisr)
return ERROR_OK; return ERROR_OK;
} }
COMMAND_HANDLER(riscv_set_enable_virt2phys)
{
if (CMD_ARGC != 1) {
LOG_ERROR("Command takes exactly 1 parameter");
return ERROR_COMMAND_SYNTAX_ERROR;
}
COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_enable_virt2phys);
return ERROR_OK;
}
COMMAND_HANDLER(riscv_set_ebreakm) COMMAND_HANDLER(riscv_set_ebreakm)
{ {
struct target *target = get_current_target(CMD_CTX); struct target *target = get_current_target(CMD_CTX);
@ -5093,6 +5098,36 @@ COMMAND_HANDLER(handle_reserve_trigger)
return ERROR_OK; return ERROR_OK;
} }
COMMAND_HANDLER(handle_riscv_virt2phys_mode)
{
struct riscv_info *info = riscv_info(get_current_target(CMD_CTX));
if (CMD_ARGC == 0) {
riscv_virt2phys_mode_t mode = info->virt2phys_mode;
command_print(CMD, "%s", riscv_virt2phys_mode_to_str(mode));
return ERROR_OK;
}
if (CMD_ARGC != 1)
return ERROR_COMMAND_SYNTAX_ERROR;
// TODO: add auto mode to allow OpenOCD choose translation mode
if (!strcmp(CMD_ARGV[0],
riscv_virt2phys_mode_to_str(RISCV_VIRT2PHYS_MODE_SW))) {
info->virt2phys_mode = RISCV_VIRT2PHYS_MODE_SW;
} else if (!strcmp(CMD_ARGV[0],
riscv_virt2phys_mode_to_str(RISCV_VIRT2PHYS_MODE_HW))) {
info->virt2phys_mode = RISCV_VIRT2PHYS_MODE_HW;
} else if (!strcmp(CMD_ARGV[0],
riscv_virt2phys_mode_to_str(RISCV_VIRT2PHYS_MODE_OFF))) {
info->virt2phys_mode = RISCV_VIRT2PHYS_MODE_OFF;
} else {
command_print(CMD, "Unsupported address translation mode: %s", CMD_ARGV[0]);
return ERROR_COMMAND_ARGUMENT_INVALID;
}
return ERROR_OK;
}
static const struct command_registration riscv_exec_command_handlers[] = { static const struct command_registration riscv_exec_command_handlers[] = {
{ {
.name = "dump_sample_buf", .name = "dump_sample_buf",
@ -5144,15 +5179,6 @@ static const struct command_registration riscv_exec_command_handlers[] = {
.help = "Set which memory access methods shall be used and in which order " .help = "Set which memory access methods shall be used and in which order "
"of priority. Method can be one of: 'progbuf', 'sysbus' or 'abstract'." "of priority. Method can be one of: 'progbuf', 'sysbus' or 'abstract'."
}, },
{
.name = "set_enable_virtual",
.handler = riscv_set_enable_virtual,
.mode = COMMAND_ANY,
.usage = "on|off",
.help = "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."
},
{ {
.name = "expose_csrs", .name = "expose_csrs",
.handler = riscv_set_expose_csrs, .handler = riscv_set_expose_csrs,
@ -5277,14 +5303,6 @@ static const struct command_registration riscv_exec_command_handlers[] = {
.help = "mask riscv interrupts", .help = "mask riscv interrupts",
.usage = "['off'|'steponly']", .usage = "['off'|'steponly']",
}, },
{
.name = "set_enable_virt2phys",
.handler = riscv_set_enable_virt2phys,
.mode = COMMAND_ANY,
.usage = "on|off",
.help = "When on (default), enable translation from virtual address to "
"physical address."
},
{ {
.name = "set_ebreakm", .name = "set_ebreakm",
.handler = riscv_set_ebreakm, .handler = riscv_set_ebreakm,
@ -5353,6 +5371,16 @@ static const struct command_registration riscv_exec_command_handlers[] = {
.usage = "[index ('on'|'off')]", .usage = "[index ('on'|'off')]",
.help = "Controls which RISC-V triggers shall not be touched by OpenOCD.", .help = "Controls which RISC-V triggers shall not be touched by OpenOCD.",
}, },
{
.name = "virt2phys_mode",
.handler = handle_riscv_virt2phys_mode,
.mode = COMMAND_ANY,
.usage = "['sw'|'hw'|'off']",
.help = "Configure the virtual address translation mode: "
"sw - translate vaddr to paddr by manually traversing page tables, "
"hw - translate vaddr to paddr by hardware, "
"off - no address translation."
},
COMMAND_REGISTRATION_DONE COMMAND_REGISTRATION_DONE
}; };
@ -5469,6 +5497,8 @@ static void riscv_info_init(struct target *target, struct riscv_info *r)
r->xlen = -1; r->xlen = -1;
r->virt2phys_mode = RISCV_VIRT2PHYS_MODE_SW;
r->isrmask_mode = RISCV_ISRMASK_OFF; r->isrmask_mode = RISCV_ISRMASK_OFF;
r->mem_access_methods[0] = RISCV_MEM_ACCESS_PROGBUF; r->mem_access_methods[0] = RISCV_MEM_ACCESS_PROGBUF;

View File

@ -57,6 +57,14 @@ typedef enum riscv_mem_access_method {
RISCV_MEM_ACCESS_MAX_METHODS_NUM RISCV_MEM_ACCESS_MAX_METHODS_NUM
} riscv_mem_access_method_t; } riscv_mem_access_method_t;
typedef enum riscv_virt2phys_mode {
RISCV_VIRT2PHYS_MODE_HW,
RISCV_VIRT2PHYS_MODE_SW,
RISCV_VIRT2PHYS_MODE_OFF
} riscv_virt2phys_mode_t;
const char *riscv_virt2phys_mode_to_str(riscv_virt2phys_mode_t mode);
enum riscv_halt_reason { enum riscv_halt_reason {
RISCV_HALT_INTERRUPT, RISCV_HALT_INTERRUPT,
RISCV_HALT_EBREAK, RISCV_HALT_EBREAK,
@ -165,6 +173,9 @@ struct riscv_info {
* most recent halt was not caused by a trigger, then this is -1. */ * most recent halt was not caused by a trigger, then this is -1. */
int64_t trigger_hit; int64_t trigger_hit;
/* The configured approach to translate virtual addresses to physical */
riscv_virt2phys_mode_t virt2phys_mode;
bool triggers_enumerated; bool triggers_enumerated;
/* Decremented every scan, and when it reaches 0 we clear the learned /* Decremented every scan, and when it reaches 0 we clear the learned
@ -331,11 +342,12 @@ typedef struct {
unsigned pa_ppn_mask[PG_MAX_LEVEL]; unsigned pa_ppn_mask[PG_MAX_LEVEL];
} virt2phys_info_t; } virt2phys_info_t;
bool riscv_virt2phys_mode_is_hw(const struct target *target);
bool riscv_virt2phys_mode_is_sw(const struct target *target);
/* Wall-clock timeout for a command/access. Settable via RISC-V Target commands.*/ /* Wall-clock timeout for a command/access. Settable via RISC-V Target commands.*/
int riscv_get_command_timeout_sec(void); int riscv_get_command_timeout_sec(void);
extern bool riscv_enable_virtual;
/* Everything needs the RISC-V specific info structure, so here's a nice macro /* Everything needs the RISC-V specific info structure, so here's a nice macro
* that provides that. */ * that provides that. */
static inline struct riscv_info *riscv_info(const struct target *target) __attribute__((unused)); static inline struct riscv_info *riscv_info(const struct target *target) __attribute__((unused));

View File

@ -0,0 +1,51 @@
# SPDX-License-Identifier: GPL-2.0-or-later
lappend _telnet_autocomplete_skip "riscv set_enable_virtual"
proc {riscv set_enable_virtual} on_off {
echo {DEPRECATED! use 'riscv virt2phys_mode' not 'riscv set_enable_virtual'}
foreach t [target names] {
if {[$t cget -type] ne "riscv"} { continue }
switch -- [$t riscv virt2phys_mode] {
off -
hw {
switch -- $on_off {
on {$t riscv virt2phys_mode hw}
off {$t riscv virt2phys_mode off}
}
}
sw {
if {$on_off eq "on"} {
error {Can't enable virtual while translation mode is SW}
}
}
}
}
return {}
}
lappend _telnet_autocomplete_skip "riscv set_enable_virt2phys"
proc {riscv set_enable_virt2phys} on_off {
echo {DEPRECATED! use 'riscv virt2phys_mode' not 'riscv set_enable_virt2phys'}
foreach t [target names] {
if {[$t cget -type] ne "riscv"} { continue }
switch -- [riscv virt2phys_mode] {
off -
sw {
switch -- $on_off {
on {riscv virt2phys_mode sw}
off {riscv virt2phys_mode off}
}
}
hw {
if {$on_off eq "on"} {
error {Can't enable virt2phys while translation mode is HW}
}
}
}
}
return {}
}
proc riscv {cmd args} {
tailcall "riscv $cmd" {*}$args
}