From 6a27d9fbc0aca57b3c4790f5e0bcfcc597a3b5cb Mon Sep 17 00:00:00 2001 From: Farid Khaydari Date: Wed, 23 Oct 2024 12:36:29 +0300 Subject: [PATCH] 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 --- doc/openocd.texi | 22 ++++--- src/target/riscv/Makefile.am | 2 + src/target/riscv/riscv-013.c | 19 +++--- src/target/riscv/riscv.c | 114 ++++++++++++++++++++++------------- src/target/riscv/riscv.h | 16 ++++- src/target/riscv/startup.tcl | 51 ++++++++++++++++ 6 files changed, 162 insertions(+), 62 deletions(-) create mode 100644 src/target/riscv/startup.tcl diff --git a/doc/openocd.texi b/doc/openocd.texi index 0e4c5f6e9..b2236dd32 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -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 diff --git a/src/target/riscv/Makefile.am b/src/target/riscv/Makefile.am index aa82f5811..328a317b1 100644 --- a/src/target/riscv/Makefile.am +++ b/src/target/riscv/Makefile.am @@ -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 diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index f3dda0287..9cc879f1e 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -1974,11 +1974,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 @@ -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) { - 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) @@ -4259,7 +4260,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, @@ -4831,7 +4833,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); diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index 59717fd14..fe8994458 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -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; diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h index 4ac10fa76..d5ca6ad00 100644 --- a/src/target/riscv/riscv.h +++ b/src/target/riscv/riscv.h @@ -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 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)); diff --git a/src/target/riscv/startup.tcl b/src/target/riscv/startup.tcl new file mode 100644 index 000000000..31e5059e2 --- /dev/null +++ b/src/target/riscv/startup.tcl @@ -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 +}