riscv: translate virtual address to physical address. (#425)
* riscv: translate virtual address to physical address. * riscv: fix formatting errors. * riscv: fix build errors. * riscv: Remove redundant command for virtual address access. * Revert "riscv: Remove redundant command for virtual address access." This reverts commit 990d09eac37d2effcfc5c0d0b5c99678f45e7d7f. * riscv: Change command disable_virt2phys to set_enable_virt2phys 1. Avoid double negative logic to make users easy to use. 2. Add document about new comomand 'riscv set_enable_virt2phys on|off'busy
parent
aec5cca15b
commit
9886f77374
|
@ -9472,12 +9472,18 @@ When on, prefer to use System Bus Access to access memory. When off, prefer to
|
||||||
use the Program Buffer to access memory.
|
use the Program Buffer to access memory.
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
@deffn Command {riscv riscv_enable_virtual} on|off
|
@deffn Command {riscv set_enable_virtual} on|off
|
||||||
When on, memory accesses are performed on physical or virtual memory depending
|
When on, memory accesses are performed on physical or virtual memory depending
|
||||||
on the current system configuration. When off, all memory accessses are performed
|
on the current system configuration. When off, all memory accessses are performed
|
||||||
on physical memory.
|
on physical memory.
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Command {riscv set_enable_virt2phys} on|off
|
||||||
|
When on, 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.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
@deffn Command {riscv resume_order} normal|reversed
|
@deffn Command {riscv resume_order} normal|reversed
|
||||||
Some software assumes all harts are executing nearly continuously. Such
|
Some software assumes all harts are executing nearly continuously. Such
|
||||||
software may be sensitive to the order that harts are resumed in. On harts
|
software may be sensitive to the order that harts are resumed in. On harts
|
||||||
|
|
|
@ -86,6 +86,7 @@ enum gdb_regno {
|
||||||
GDB_REGNO_MSTATUS = CSR_MSTATUS + GDB_REGNO_CSR0,
|
GDB_REGNO_MSTATUS = CSR_MSTATUS + GDB_REGNO_CSR0,
|
||||||
GDB_REGNO_MEPC = CSR_MEPC + GDB_REGNO_CSR0,
|
GDB_REGNO_MEPC = CSR_MEPC + GDB_REGNO_CSR0,
|
||||||
GDB_REGNO_MCAUSE = CSR_MCAUSE + GDB_REGNO_CSR0,
|
GDB_REGNO_MCAUSE = CSR_MCAUSE + GDB_REGNO_CSR0,
|
||||||
|
GDB_REGNO_SATP = CSR_SATP + GDB_REGNO_CSR0,
|
||||||
GDB_REGNO_CSR4095 = GDB_REGNO_CSR0 + 4095,
|
GDB_REGNO_CSR4095 = GDB_REGNO_CSR0 + 4095,
|
||||||
GDB_REGNO_PRIV = 4161,
|
GDB_REGNO_PRIV = 4161,
|
||||||
GDB_REGNO_COUNT
|
GDB_REGNO_COUNT
|
||||||
|
|
|
@ -251,6 +251,7 @@ int riscv_command_timeout_sec = DEFAULT_COMMAND_TIMEOUT_SEC;
|
||||||
int riscv_reset_timeout_sec = DEFAULT_RESET_TIMEOUT_SEC;
|
int riscv_reset_timeout_sec = DEFAULT_RESET_TIMEOUT_SEC;
|
||||||
|
|
||||||
bool riscv_prefer_sba;
|
bool riscv_prefer_sba;
|
||||||
|
bool riscv_enable_virt2phys = true;
|
||||||
|
|
||||||
bool riscv_enable_virtual;
|
bool riscv_enable_virtual;
|
||||||
|
|
||||||
|
@ -271,6 +272,39 @@ static enum {
|
||||||
RO_REVERSED
|
RO_REVERSED
|
||||||
} resume_order;
|
} resume_order;
|
||||||
|
|
||||||
|
virt2phys_info_t sv32 = {
|
||||||
|
.level = 2,
|
||||||
|
.pte_shift = 2,
|
||||||
|
.vpn_shift = {12, 22},
|
||||||
|
.vpn_mask = {0x3ff, 0x3ff},
|
||||||
|
.pte_ppn_shift = {10, 20},
|
||||||
|
.pte_ppn_mask = {0x3ff, 0xfff},
|
||||||
|
.pa_ppn_shift = {12, 22},
|
||||||
|
.pa_ppn_mask = {0x3ff, 0xfff},
|
||||||
|
};
|
||||||
|
|
||||||
|
virt2phys_info_t sv39 = {
|
||||||
|
.level = 3,
|
||||||
|
.pte_shift = 3,
|
||||||
|
.vpn_shift = {12, 21, 30},
|
||||||
|
.vpn_mask = {0x1ff, 0x1ff, 0x1ff},
|
||||||
|
.pte_ppn_shift = {10, 19, 28},
|
||||||
|
.pte_ppn_mask = {0x1ff, 0x1ff, 0x3ffffff},
|
||||||
|
.pa_ppn_shift = {12, 21, 30},
|
||||||
|
.pa_ppn_mask = {0x1ff, 0x1ff, 0x3ffffff},
|
||||||
|
};
|
||||||
|
|
||||||
|
virt2phys_info_t sv48 = {
|
||||||
|
.level = 4,
|
||||||
|
.pte_shift = 3,
|
||||||
|
.vpn_shift = {12, 21, 30, 39},
|
||||||
|
.vpn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ff},
|
||||||
|
.pte_ppn_shift = {10, 19, 28, 37},
|
||||||
|
.pte_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ffff},
|
||||||
|
.pa_ppn_shift = {12, 21, 30, 39},
|
||||||
|
.pa_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ffff},
|
||||||
|
};
|
||||||
|
|
||||||
static int riscv_resume_go_all_harts(struct target *target);
|
static int riscv_resume_go_all_harts(struct target *target);
|
||||||
|
|
||||||
void select_dmi_via_bscan(struct target *target)
|
void select_dmi_via_bscan(struct target *target)
|
||||||
|
@ -1328,20 +1362,184 @@ static int riscv_select_current_hart(struct target *target)
|
||||||
return riscv_set_current_hartid(target, target->coreid);
|
return riscv_set_current_hartid(target, target->coreid);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int riscv_read_memory(struct target *target, target_addr_t address,
|
static int riscv_mmu(struct target *target, int *enabled)
|
||||||
|
{
|
||||||
|
if (riscv_rtos_enabled(target))
|
||||||
|
riscv_set_current_hartid(target, target->rtos->current_thread - 1);
|
||||||
|
|
||||||
|
riscv_reg_t value;
|
||||||
|
int result = riscv_get_register(target, &value, GDB_REGNO_SATP);
|
||||||
|
if (result != ERROR_OK) {
|
||||||
|
LOG_DEBUG("Couldn't read SATP.");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_field(value, RISCV_SATP_MODE(riscv_xlen(target))) == SATP_MODE_OFF) {
|
||||||
|
LOG_DEBUG("MMU is disabled.");
|
||||||
|
*enabled = 0;
|
||||||
|
} else {
|
||||||
|
LOG_DEBUG("MMU is enabled.");
|
||||||
|
*enabled = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int riscv_address_translate(struct target *target,
|
||||||
|
target_addr_t virtual, target_addr_t *physical)
|
||||||
|
{
|
||||||
|
riscv_reg_t satp_value;
|
||||||
|
int mode;
|
||||||
|
uint64_t ppn_value;
|
||||||
|
target_addr_t table_address;
|
||||||
|
virt2phys_info_t *info;
|
||||||
|
struct target_type *tt = get_target_type(target);
|
||||||
|
uint64_t pte;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (riscv_rtos_enabled(target))
|
||||||
|
riscv_set_current_hartid(target, target->rtos->current_thread - 1);
|
||||||
|
|
||||||
|
int result = riscv_get_register(target, &satp_value, GDB_REGNO_SATP);
|
||||||
|
if (result != ERROR_OK)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
mode = get_field(satp_value, RISCV_SATP_MODE(riscv_xlen(target)));
|
||||||
|
switch (mode) {
|
||||||
|
case SATP_MODE_SV32:
|
||||||
|
LOG_DEBUG("Translation mode: SV32");
|
||||||
|
info = &sv32;
|
||||||
|
break;
|
||||||
|
case SATP_MODE_SV39:
|
||||||
|
LOG_DEBUG("Translation mode: SV39");
|
||||||
|
info = &sv39;
|
||||||
|
break;
|
||||||
|
case SATP_MODE_SV48:
|
||||||
|
LOG_DEBUG("Translation mode: SV48");
|
||||||
|
info = &sv48;
|
||||||
|
break;
|
||||||
|
case SATP_MODE_OFF:
|
||||||
|
LOG_ERROR("No translation or protection." \
|
||||||
|
" (satp: 0x%" PRIx64")", satp_value);
|
||||||
|
return ERROR_FAIL;
|
||||||
|
default:
|
||||||
|
LOG_ERROR("The translation mode is not supported." \
|
||||||
|
" (satp: 0x%" PRIx64")", satp_value);
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ppn_value = get_field(satp_value, RISCV_SATP_PPN(riscv_xlen(target)));
|
||||||
|
table_address = ppn_value << RISCV_PGSHIFT;
|
||||||
|
i = info->level - 1;
|
||||||
|
while (i >= 0) {
|
||||||
|
uint64_t vpn = virtual >> info->vpn_shift[i];
|
||||||
|
vpn &= info->vpn_mask[i];
|
||||||
|
target_addr_t pte_address = table_address +
|
||||||
|
(vpn << info->pte_shift);
|
||||||
|
uint8_t buffer[8];
|
||||||
|
assert(info->pte_shift <= 3);
|
||||||
|
int retval = tt->read_memory(target, pte_address,
|
||||||
|
4, (1 << info->pte_shift) / 4, buffer);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
if (info->pte_shift == 2)
|
||||||
|
pte = buf_get_u32(buffer, 0, 32);
|
||||||
|
else
|
||||||
|
pte = buf_get_u64(buffer, 0, 64);
|
||||||
|
|
||||||
|
if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W)))
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
if ((pte & PTE_R) || (pte & PTE_X)) /* Found leaf PTE. */
|
||||||
|
break;
|
||||||
|
|
||||||
|
i--;
|
||||||
|
if (i < 0)
|
||||||
|
break;
|
||||||
|
ppn_value = pte >> PTE_PPN_SHIFT;
|
||||||
|
table_address = ppn_value << RISCV_PGSHIFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < 0) {
|
||||||
|
LOG_ERROR("Couldn't find the PTE.");
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*physical = virtual;
|
||||||
|
|
||||||
|
while (i < info->level) {
|
||||||
|
ppn_value = pte >> info->pte_ppn_shift[i];
|
||||||
|
ppn_value &= info->pte_ppn_mask[i];
|
||||||
|
*physical &= ~(info->pa_ppn_mask[i] << info->pa_ppn_shift[i]);
|
||||||
|
*physical |= (ppn_value << info->pa_ppn_shift[i]);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
LOG_DEBUG("Virtual address: 0x%" TARGET_PRIxADDR, virtual);
|
||||||
|
LOG_DEBUG("Physical address: 0x%" TARGET_PRIxADDR, *physical);
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int riscv_virt2phys(struct target *target, target_addr_t virtual, target_addr_t *physical)
|
||||||
|
{
|
||||||
|
if (!riscv_enable_virt2phys)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
int enabled;
|
||||||
|
if (riscv_mmu(target, &enabled) == ERROR_OK) {
|
||||||
|
if (!enabled)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
if (riscv_address_translate(target, virtual, physical) == ERROR_OK)
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int riscv_read_phys_memory(struct target *target, target_addr_t phys_address,
|
||||||
uint32_t size, uint32_t count, uint8_t *buffer)
|
uint32_t size, uint32_t count, uint8_t *buffer)
|
||||||
{
|
{
|
||||||
if (riscv_select_current_hart(target) != ERROR_OK)
|
if (riscv_select_current_hart(target) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
struct target_type *tt = get_target_type(target);
|
struct target_type *tt = get_target_type(target);
|
||||||
|
return tt->read_memory(target, phys_address, size, count, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int riscv_read_memory(struct target *target, target_addr_t address,
|
||||||
|
uint32_t size, uint32_t count, uint8_t *buffer)
|
||||||
|
{
|
||||||
|
if (riscv_select_current_hart(target) != ERROR_OK)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
target_addr_t physical_addr;
|
||||||
|
if (target->type->virt2phys(target, address, &physical_addr) == ERROR_OK)
|
||||||
|
address = physical_addr;
|
||||||
|
|
||||||
|
struct target_type *tt = get_target_type(target);
|
||||||
return tt->read_memory(target, address, size, count, buffer);
|
return tt->read_memory(target, address, size, count, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int riscv_write_phys_memory(struct target *target, target_addr_t phys_address,
|
||||||
|
uint32_t size, uint32_t count, const uint8_t *buffer)
|
||||||
|
{
|
||||||
|
if (riscv_select_current_hart(target) != ERROR_OK)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
struct target_type *tt = get_target_type(target);
|
||||||
|
return tt->write_memory(target, phys_address, size, count, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
static int riscv_write_memory(struct target *target, target_addr_t address,
|
static int riscv_write_memory(struct target *target, target_addr_t address,
|
||||||
uint32_t size, uint32_t count, const uint8_t *buffer)
|
uint32_t size, uint32_t count, const uint8_t *buffer)
|
||||||
{
|
{
|
||||||
if (riscv_select_current_hart(target) != ERROR_OK)
|
if (riscv_select_current_hart(target) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
target_addr_t physical_addr;
|
||||||
|
if (target->type->virt2phys(target, address, &physical_addr) == ERROR_OK)
|
||||||
|
address = physical_addr;
|
||||||
|
|
||||||
struct target_type *tt = get_target_type(target);
|
struct target_type *tt = get_target_type(target);
|
||||||
return tt->write_memory(target, address, size, count, buffer);
|
return tt->write_memory(target, address, size, count, buffer);
|
||||||
}
|
}
|
||||||
|
@ -2251,6 +2449,16 @@ COMMAND_HANDLER(riscv_use_bscan_tunnel)
|
||||||
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;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct command_registration riscv_exec_command_handlers[] = {
|
static const struct command_registration riscv_exec_command_handlers[] = {
|
||||||
{
|
{
|
||||||
.name = "test_compliance",
|
.name = "test_compliance",
|
||||||
|
@ -2386,6 +2594,13 @@ static const struct command_registration riscv_exec_command_handlers[] = {
|
||||||
"(optional) to indicate Bscan Tunnel Type {0:(default) NESTED_TAP , "
|
"(optional) to indicate Bscan Tunnel Type {0:(default) NESTED_TAP , "
|
||||||
"1: DATA_REGISTER}"
|
"1: DATA_REGISTER}"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = "set_enable_virt2phys",
|
||||||
|
.handler = riscv_set_enable_virt2phys,
|
||||||
|
.mode = COMMAND_ANY,
|
||||||
|
.usage = "riscv set_enable_virt2phys on|off",
|
||||||
|
.help = "Enable translation from virtual address to physical address."
|
||||||
|
},
|
||||||
COMMAND_REGISTRATION_DONE
|
COMMAND_REGISTRATION_DONE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2489,9 +2704,14 @@ struct target_type riscv_target = {
|
||||||
|
|
||||||
.read_memory = riscv_read_memory,
|
.read_memory = riscv_read_memory,
|
||||||
.write_memory = riscv_write_memory,
|
.write_memory = riscv_write_memory,
|
||||||
|
.read_phys_memory = riscv_read_phys_memory,
|
||||||
|
.write_phys_memory = riscv_write_phys_memory,
|
||||||
|
|
||||||
.checksum_memory = riscv_checksum_memory,
|
.checksum_memory = riscv_checksum_memory,
|
||||||
|
|
||||||
|
.mmu = riscv_mmu,
|
||||||
|
.virt2phys = riscv_virt2phys,
|
||||||
|
|
||||||
.get_gdb_reg_list = riscv_get_gdb_reg_list,
|
.get_gdb_reg_list = riscv_get_gdb_reg_list,
|
||||||
.get_gdb_reg_list_noread = riscv_get_gdb_reg_list_noread,
|
.get_gdb_reg_list_noread = riscv_get_gdb_reg_list_noread,
|
||||||
|
|
||||||
|
@ -3001,6 +3221,8 @@ const char *gdb_regno_name(enum gdb_regno regno)
|
||||||
return "mcause";
|
return "mcause";
|
||||||
case GDB_REGNO_PRIV:
|
case GDB_REGNO_PRIV:
|
||||||
return "priv";
|
return "priv";
|
||||||
|
case GDB_REGNO_SATP:
|
||||||
|
return "satp";
|
||||||
default:
|
default:
|
||||||
if (regno <= GDB_REGNO_XPR31)
|
if (regno <= GDB_REGNO_XPR31)
|
||||||
sprintf(buf, "x%d", regno - GDB_REGNO_ZERO);
|
sprintf(buf, "x%d", regno - GDB_REGNO_ZERO);
|
||||||
|
|
|
@ -17,6 +17,12 @@ struct riscv_program;
|
||||||
#define DEFAULT_COMMAND_TIMEOUT_SEC 2
|
#define DEFAULT_COMMAND_TIMEOUT_SEC 2
|
||||||
#define DEFAULT_RESET_TIMEOUT_SEC 30
|
#define DEFAULT_RESET_TIMEOUT_SEC 30
|
||||||
|
|
||||||
|
#define RISCV_SATP_MODE(xlen) ((xlen) == 32 ? SATP32_MODE : SATP64_MODE)
|
||||||
|
#define RISCV_SATP_PPN(xlen) ((xlen) == 32 ? SATP32_PPN : SATP64_PPN)
|
||||||
|
#define RISCV_PGSHIFT 12
|
||||||
|
|
||||||
|
# define PG_MAX_LEVEL 4
|
||||||
|
|
||||||
extern struct target_type riscv011_target;
|
extern struct target_type riscv011_target;
|
||||||
extern struct target_type riscv013_target;
|
extern struct target_type riscv013_target;
|
||||||
|
|
||||||
|
@ -149,6 +155,16 @@ typedef struct {
|
||||||
struct scan_field tunneled_dr[4];
|
struct scan_field tunneled_dr[4];
|
||||||
} riscv_bscan_tunneled_scan_context_t;
|
} riscv_bscan_tunneled_scan_context_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int level;
|
||||||
|
unsigned pte_shift;
|
||||||
|
unsigned vpn_shift[PG_MAX_LEVEL];
|
||||||
|
unsigned vpn_mask[PG_MAX_LEVEL];
|
||||||
|
unsigned pte_ppn_shift[PG_MAX_LEVEL];
|
||||||
|
unsigned pte_ppn_mask[PG_MAX_LEVEL];
|
||||||
|
unsigned pa_ppn_shift[PG_MAX_LEVEL];
|
||||||
|
unsigned pa_ppn_mask[PG_MAX_LEVEL];
|
||||||
|
} virt2phys_info_t;
|
||||||
|
|
||||||
/* 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.*/
|
||||||
extern int riscv_command_timeout_sec;
|
extern int riscv_command_timeout_sec;
|
||||||
|
|
Loading…
Reference in New Issue