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
Hsiangkai 2019-12-11 04:18:03 +08:00 committed by Tim Newsome
parent aec5cca15b
commit 9886f77374
4 changed files with 246 additions and 1 deletions

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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;