Merge pull request #1152 from fk-sc/translation-drivers
target/riscv: added translation drivers
This commit is contained in:
commit
7b4ad6f173
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue