[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 14/18] target-riscv: softmmu/address translation sup
From: |
Sagar Karandikar |
Subject: |
[Qemu-devel] [PATCH 14/18] target-riscv: softmmu/address translation support |
Date: |
Mon, 26 Sep 2016 03:56:44 -0700 |
Signed-off-by: Sagar Karandikar <address@hidden>
---
target-riscv/helper.c | 211 ++++++++++++++++++++++++++++++++++++++++++++++-
target-riscv/op_helper.c | 29 +++++++
2 files changed, 238 insertions(+), 2 deletions(-)
diff --git a/target-riscv/helper.c b/target-riscv/helper.c
index dfdc7bd..2e02351 100644
--- a/target-riscv/helper.c
+++ b/target-riscv/helper.c
@@ -36,16 +36,223 @@ bool riscv_cpu_exec_interrupt(CPUState *cs, int
interrupt_request)
return false;
}
+/* get_physical_address - get the physical address for this virtual address
+ *
+ * Do a page table walk to obtain the physical address corresponding to a
+ * virtual address. Returns 0 if the translation was successful
+ *
+ * Adapted from Spike's mmu_t::translate and mmu_t::walk
+ *
+ */
+static int get_physical_address(CPURISCVState *env, hwaddr *physical,
+ int *prot, target_ulong address,
+ MMUAccessType access_type, int mmu_idx)
+{
+ /* NOTE: the env->PC value visible here will not be
+ * correct, but the value visible to the exception handler
+ * (riscv_cpu_do_interrupt) is correct */
+
+ *prot = 0;
+ CPUState *cs = CPU(riscv_env_get_cpu(env));
+
+ target_ulong mode = env->priv;
+ if (access_type != MMU_INST_FETCH) {
+ if (get_field(env->csr[CSR_MSTATUS], MSTATUS_MPRV)) {
+ mode = get_field(env->csr[CSR_MSTATUS], MSTATUS_MPP);
+ }
+ }
+ if (get_field(env->csr[CSR_MSTATUS], MSTATUS_VM) == VM_MBARE) {
+ mode = PRV_M;
+ }
+
+ /* check to make sure that mmu_idx and mode that we get matches */
+ if (unlikely(mode != mmu_idx)) {
+ fprintf(stderr, "MODE, mmu_idx mismatch\n");
+ exit(1);
+ }
+
+ if (mode == PRV_M) {
+ target_ulong msb_mask = (2UL << (TARGET_LONG_BITS - 1)) - 1;
+ /*0x7FFFFFFFFFFFFFFF; */
+ *physical = address & msb_mask;
+ *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+ return TRANSLATE_SUCCESS;
+ }
+
+ target_ulong addr = address;
+ int supervisor = mode == PRV_S;
+ int pum = get_field(env->csr[CSR_MSTATUS], MSTATUS_PUM);
+ int mxr = get_field(env->csr[CSR_MSTATUS], MSTATUS_MXR);
+
+ int levels, ptidxbits, ptesize;
+ switch (get_field(env->csr[CSR_MSTATUS], MSTATUS_VM)) {
+ case VM_SV32:
+ levels = 2;
+ ptidxbits = 10;
+ ptesize = 4;
+ break;
+ case VM_SV39:
+ levels = 3;
+ ptidxbits = 9;
+ ptesize = 8;
+ break;
+ case VM_SV48:
+ levels = 4;
+ ptidxbits = 9;
+ ptesize = 8;
+ break;
+ default:
+ printf("unsupported MSTATUS_VM value\n");
+ exit(1);
+ }
+
+ int va_bits = PGSHIFT + levels * ptidxbits;
+ target_ulong mask = (1L << (TARGET_LONG_BITS - (va_bits - 1))) - 1;
+ target_ulong masked_msbs = (addr >> (va_bits - 1)) & mask;
+ if (masked_msbs != 0 && masked_msbs != mask) {
+ return TRANSLATE_FAIL;
+ }
+
+ target_ulong base = env->csr[CSR_SPTBR] << PGSHIFT;
+ int ptshift = (levels - 1) * ptidxbits;
+ int i;
+ for (i = 0; i < levels; i++, ptshift -= ptidxbits) {
+ target_ulong idx = (addr >> (PGSHIFT + ptshift)) &
+ ((1 << ptidxbits) - 1);
+
+ /* check that physical address of PTE is legal */
+ target_ulong pte_addr = base + idx * ptesize;
+
+ /* PTE must reside in memory */
+ if (!(pte_addr >= DRAM_BASE && pte_addr < (DRAM_BASE + env->memsize)))
{
+ printf("PTE was not in DRAM region\n");
+ exit(1);
+ break;
+ }
+
+ target_ulong pte = ldq_phys(cs->as, pte_addr);
+ target_ulong ppn = pte >> PTE_PPN_SHIFT;
+
+ if (PTE_TABLE(pte)) { /* next level of page table */
+ base = ppn << PGSHIFT;
+ } else if ((pte & PTE_U) ? supervisor && pum : !supervisor) {
+ break;
+ } else if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) {
+ break;
+ } else if (access_type == MMU_INST_FETCH ? !(pte & PTE_X) :
+ access_type == MMU_DATA_LOAD ? !(pte & PTE_R) &&
+ !(mxr && (pte & PTE_X)) : !((pte & PTE_R) && (pte & PTE_W)))
{
+ break;
+ } else {
+ /* set accessed and possibly dirty bits.
+ we only put it in the TLB if it has the right stuff */
+ stq_phys(cs->as, pte_addr, ldq_phys(cs->as, pte_addr) | PTE_A |
+ ((access_type == MMU_DATA_STORE) * PTE_D));
+
+ /* for superpage mappings, make a fake leaf PTE for the TLB's
+ benefit. */
+ target_ulong vpn = addr >> PGSHIFT;
+ *physical = (ppn | (vpn & ((1L << ptshift) - 1))) << PGSHIFT;
+
+ /* we do not give all prots indicated by the PTE
+ * this is because future accesses need to do things like set the
+ * dirty bit on the PTE
+ *
+ * at this point, we assume that protection checks have occurred */
+ if (supervisor) {
+ if ((pte & PTE_X) && access_type == MMU_INST_FETCH) {
+ *prot |= PAGE_EXEC;
+ } else if ((pte & PTE_W) && access_type == MMU_DATA_STORE) {
+ *prot |= PAGE_WRITE;
+ } else if ((pte & PTE_R) && access_type == MMU_DATA_LOAD) {
+ *prot |= PAGE_READ;
+ } else {
+ printf("err in translation prots");
+ exit(1);
+ }
+ } else {
+ if ((pte & PTE_X) && access_type == MMU_INST_FETCH) {
+ *prot |= PAGE_EXEC;
+ } else if ((pte & PTE_W) && access_type == MMU_DATA_STORE) {
+ *prot |= PAGE_WRITE;
+ } else if ((pte & PTE_R) && access_type == MMU_DATA_LOAD) {
+ *prot |= PAGE_READ;
+ } else {
+ printf("err in translation prots");
+ exit(1);
+ }
+ }
+ return TRANSLATE_SUCCESS;
+ }
+ }
+ return TRANSLATE_FAIL;
+}
+#endif
+
+static void raise_mmu_exception(CPURISCVState *env, target_ulong address,
+ MMUAccessType access_type)
+{
+ CPUState *cs = CPU(riscv_env_get_cpu(env));
+ int exception = 0;
+ if (access_type == MMU_INST_FETCH) { /* inst access */
+ exception = RISCV_EXCP_INST_ACCESS_FAULT;
+ env->badaddr = address;
+ } else if (access_type == MMU_DATA_STORE) { /* store access */
+ exception = RISCV_EXCP_STORE_AMO_ACCESS_FAULT;
+ env->badaddr = address;
+ } else if (access_type == MMU_DATA_LOAD) { /* load access */
+ exception = RISCV_EXCP_LOAD_ACCESS_FAULT;
+ env->badaddr = address;
+ } else {
+ fprintf(stderr, "FAIL: invalid access_type\n");
+ exit(1);
+ }
+ cs->exception_index = exception;
+}
+
+#if !defined(CONFIG_USER_ONLY)
hwaddr riscv_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
{
- return 0;
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ hwaddr phys_addr;
+ int prot;
+ int mem_idx = cpu_mmu_index(&cpu->env, false);
+
+ if (get_physical_address(&cpu->env, &phys_addr, &prot, addr, 0, mem_idx)) {
+ return -1;
+ }
+ return phys_addr;
}
#endif
int riscv_cpu_handle_mmu_fault(CPUState *cs, vaddr address,
MMUAccessType access_type, int mmu_idx)
{
- return 0;
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ CPURISCVState *env = &cpu->env;
+ hwaddr physical;
+ physical = 0; /* stop gcc complaining */
+ int prot;
+ int ret = 0;
+
+ qemu_log_mask(CPU_LOG_MMU,
+ "%s pc " TARGET_FMT_lx " ad %" VADDR_PRIx " access_type %d mmu_idx
\
+ %d\n", __func__, env->PC, address, access_type, mmu_idx);
+
+ ret = get_physical_address(env, &physical, &prot, address, access_type,
+ mmu_idx);
+ qemu_log_mask(CPU_LOG_MMU,
+ "%s address=%" VADDR_PRIx " ret %d physical " TARGET_FMT_plx
+ " prot %d\n",
+ __func__, address, ret, physical, prot);
+ if (ret == TRANSLATE_SUCCESS) {
+ tlb_set_page(cs, address & TARGET_PAGE_MASK,
+ physical & TARGET_PAGE_MASK,
+ prot, mmu_idx, TARGET_PAGE_SIZE);
+ } else if (ret == TRANSLATE_FAIL) {
+ raise_mmu_exception(env, address, access_type);
+ }
+ return ret;
}
/*
diff --git a/target-riscv/op_helper.c b/target-riscv/op_helper.c
index 8449d1b..1ecdef2 100644
--- a/target-riscv/op_helper.c
+++ b/target-riscv/op_helper.c
@@ -533,16 +533,45 @@ void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr
addr,
MMUAccessType access_type, int mmu_idx,
uintptr_t retaddr)
{
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ CPURISCVState *env = &cpu->env;
+ if (access_type == MMU_INST_FETCH) {
+ fprintf(stderr, "unaligned inst fetch not handled here. should not "
+ "trigger\n");
+ exit(1);
+ } else if (access_type == MMU_DATA_STORE) {
+ cs->exception_index = RISCV_EXCP_STORE_AMO_ADDR_MIS;
+ env->badaddr = addr;
+ } else if (access_type == MMU_DATA_LOAD) {
+ cs->exception_index = RISCV_EXCP_LOAD_ADDR_MIS;
+ env->badaddr = addr;
+ } else {
+ fprintf(stderr, "Invalid MMUAccessType\n");
+ exit(1);
+ }
+ do_raise_exception_err(env, cs->exception_index, retaddr);
}
+/* called by qemu's softmmu to fill the qemu tlb */
void tlb_fill(CPUState *cs, target_ulong addr, MMUAccessType access_type,
int mmu_idx, uintptr_t retaddr)
{
+ int ret;
+ ret = riscv_cpu_handle_mmu_fault(cs, addr, access_type, mmu_idx);
+ if (ret == TRANSLATE_FAIL) {
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ CPURISCVState *env = &cpu->env;
+ do_raise_exception_err(env, cs->exception_index, retaddr);
+ }
}
void riscv_cpu_unassigned_access(CPUState *cs, hwaddr addr, bool is_write,
bool is_exec, int unused, unsigned size)
{
+ printf("unassigned address not implemented for riscv\n");
+ printf("are you trying to fetch instructions from an MMIO page?\n");
+ printf("unassigned Address: %016lX\n", addr);
+ exit(1);
}
#endif /* !CONFIG_USER_ONLY */
--
2.9.3
- Re: [Qemu-devel] [PATCH 06/18] target-riscv: Add JALR, Branch Instructions, (continued)
- [Qemu-devel] [PATCH 01/18] target-riscv: Add RISC-V target stubs and Maintainer, Sagar Karandikar, 2016/09/26
- [Qemu-devel] [PATCH 15/18] target-riscv: Interrupt Handling, Sagar Karandikar, 2016/09/26
- [Qemu-devel] [PATCH 07/18] target-riscv: Add Loads/Stores, FP Loads/Stores, Sagar Karandikar, 2016/09/26
- [Qemu-devel] [PATCH 08/18] target-riscv: Add Atomic Instructions, Sagar Karandikar, 2016/09/26
- [Qemu-devel] [PATCH 03/18] target-riscv: Add initialization for translation, Sagar Karandikar, 2016/09/26
- [Qemu-devel] [PATCH 14/18] target-riscv: softmmu/address translation support,
Sagar Karandikar <=
- [Qemu-devel] [PATCH 16/18] target-riscv: Timer Support, Sagar Karandikar, 2016/09/26
- [Qemu-devel] [PATCH 12/18] target-riscv: Add system instructions, Sagar Karandikar, 2016/09/26
- Re: [Qemu-devel] [PATCH 12/18] target-riscv: Add system instructions, Richard Henderson, 2016/09/26
[Qemu-devel] [PATCH 13/18] target-riscv: Add CSR read/write helpers, Sagar Karandikar, 2016/09/26
[Qemu-devel] [PATCH 09/18] target-riscv: Add FMADD, FMSUB, FNMADD, FNMSUB Instructions,, Sagar Karandikar, 2016/09/26