Merge pull request #1152 from fk-sc/translation-drivers

target/riscv: added translation drivers
This commit is contained in:
Evgeniy Naydanov 2024-10-24 15:06:27 +03:00 committed by GitHub
commit 7b4ad6f173
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
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.
@end deffn
@deffn {Command} {riscv set_enable_virtual} on|off
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.
@end deffn
@deffn {Command} {riscv set_enable_virt2phys} on|off
When on (default), memory accesses are performed on physical or virtual memory
depending on the current satp configuration. When off, all memory accessses are
performed on physical memory.
@deffn {Command} {riscv virt2phys_mode} [@option{hw}|@option{sw}|@option{off}]
Configure how OpenOCD translates virtual addresses to physical:
@itemize @bullet
@item @option{sw} - OpenOCD translates virtual addresses explicitly by
traversing the page table entries (by performing physical memory accesses to
read the respective entries). This is the default mode.
@item @option{hw} - Virtual addresses are translated implicitly by hardware.
(Virtual memory access will fail with an error if the hardware doesn't
support the necessary functionality.)
@item @option{off} - Virtual addresses are not translated (identity mapping is assumed).
@end itemize
Returns current translation mode if called without arguments.
@end deffn
@deffn {Command} {riscv resume_order} normal|reversed

View File

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

View File

@ -1995,11 +1995,11 @@ static int examine(struct target *target)
info->impebreak);
}
if (info->progbufsize < 4 && riscv_enable_virtual) {
LOG_TARGET_ERROR(target, "set_enable_virtual is not available on this target. It "
"requires a program buffer size of at least 4. (progbufsize=%d) "
"Use `riscv set_enable_virtual off` to continue."
, info->progbufsize);
if (info->progbufsize < 4 && riscv_virt2phys_mode_is_hw(target)) {
LOG_TARGET_ERROR(target, "software address translation "
"is not available on this target. It requires a "
"program buffer size of at least 4. (progbufsize=%d) "
"Use `riscv set_enable_virtual off` to continue.", info->progbufsize);
}
/* Don't call any riscv_* functions until after we've counted the number of
@ -3075,7 +3075,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)
{
if (riscv_enable_virtual && has_sufficient_progbuf(target, 5)) {
if (riscv_virt2phys_mode_is_hw(target)
&& has_sufficient_progbuf(target, 5)) {
/* Read DCSR */
uint64_t dcsr;
if (register_read_direct(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK)
@ -4280,7 +4281,8 @@ read_memory_progbuf(struct target *target, target_addr_t address,
if (modify_privilege(target, &mstatus, &mstatus_old) != ERROR_OK)
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 = {
.target_address = address,
.increment = increment,
@ -4852,7 +4854,8 @@ write_memory_progbuf(struct target *target, target_addr_t address,
if (modify_privilege(target, &mstatus, &mstatus_old) != ERROR_OK)
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);

View File

@ -139,6 +139,35 @@ struct tdata1_cache {
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.*/
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);
}
static bool riscv_enable_virt2phys = true;
bool riscv_enable_virtual;
static enum {
RO_NORMAL,
RO_REVERSED
@ -2714,7 +2739,7 @@ static int riscv_mmu(struct target *target, int *enabled)
{
*enabled = 0;
if (!riscv_enable_virt2phys)
if (!riscv_virt2phys_mode_is_sw(target))
return ERROR_OK;
/* Don't use MMU in explicit or effective M (machine) mode */
@ -3938,16 +3963,6 @@ COMMAND_HANDLER(riscv_set_mem_access)
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)
{
char *args = strdup(tcl_arg);
@ -4459,16 +4474,6 @@ COMMAND_HANDLER(riscv_set_maskisr)
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)
{
struct target *target = get_current_target(CMD_CTX);
@ -5093,6 +5098,36 @@ COMMAND_HANDLER(handle_reserve_trigger)
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[] = {
{
.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 "
"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",
.handler = riscv_set_expose_csrs,
@ -5277,14 +5303,6 @@ static const struct command_registration riscv_exec_command_handlers[] = {
.help = "mask riscv interrupts",
.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",
.handler = riscv_set_ebreakm,
@ -5353,6 +5371,16 @@ static const struct command_registration riscv_exec_command_handlers[] = {
.usage = "[index ('on'|'off')]",
.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
};
@ -5469,6 +5497,8 @@ static void riscv_info_init(struct target *target, struct riscv_info *r)
r->xlen = -1;
r->virt2phys_mode = RISCV_VIRT2PHYS_MODE_SW;
r->isrmask_mode = RISCV_ISRMASK_OFF;
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_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 {
RISCV_HALT_INTERRUPT,
RISCV_HALT_EBREAK,
@ -165,6 +173,9 @@ struct riscv_info {
* most recent halt was not caused by a trigger, then this is -1. */
int64_t trigger_hit;
/* The configured approach to translate virtual addresses to physical */
riscv_virt2phys_mode_t virt2phys_mode;
bool triggers_enumerated;
/* Decremented every scan, and when it reaches 0 we clear the learned
@ -331,11 +342,12 @@ typedef struct {
unsigned int pa_ppn_mask[PG_MAX_LEVEL];
} 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.*/
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
* that provides that. */
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
}