diff options
Diffstat (limited to 'target/riscv/cpu_helper.c')
-rw-r--r-- | target/riscv/cpu_helper.c | 84 |
1 files changed, 63 insertions, 21 deletions
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 83a6bcfad0..fa385594df 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -280,6 +280,49 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv) env->load_res = -1; } +/* + * get_physical_address_pmp - check PMP permission for this physical address + * + * Match the PMP region and check permission for this physical address and it's + * TLB page. Returns 0 if the permission checking was successful + * + * @env: CPURISCVState + * @prot: The returned protection attributes + * @tlb_size: TLB page size containing addr. It could be modified after PMP + * permission checking. NULL if not set TLB page for addr. + * @addr: The physical address to be checked permission + * @access_type: The type of MMU access + * @mode: Indicates current privilege level. + */ +static int get_physical_address_pmp(CPURISCVState *env, int *prot, + target_ulong *tlb_size, hwaddr addr, + int size, MMUAccessType access_type, + int mode) +{ + pmp_priv_t pmp_priv; + target_ulong tlb_size_pmp = 0; + + if (!riscv_feature(env, RISCV_FEATURE_PMP)) { + *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + return TRANSLATE_SUCCESS; + } + + if (!pmp_hart_has_privs(env, addr, size, 1 << access_type, &pmp_priv, + mode)) { + *prot = 0; + return TRANSLATE_PMP_FAIL; + } + + *prot = pmp_priv_to_page_prot(pmp_priv); + if (tlb_size != NULL) { + if (pmp_is_range_in_tlb(env, addr & ~(*tlb_size - 1), &tlb_size_pmp)) { + *tlb_size = tlb_size_pmp; + } + } + + return TRANSLATE_SUCCESS; +} + /* get_physical_address - get the physical address for this virtual address * * Do a page table walk to obtain the physical address corresponding to a @@ -442,9 +485,11 @@ restart: pte_addr = base + idx * ptesize; } - if (riscv_feature(env, RISCV_FEATURE_PMP) && - !pmp_hart_has_privs(env, pte_addr, sizeof(target_ulong), - 1 << MMU_DATA_LOAD, PRV_S)) { + int pmp_prot; + int pmp_ret = get_physical_address_pmp(env, &pmp_prot, NULL, pte_addr, + sizeof(target_ulong), + MMU_DATA_LOAD, PRV_S); + if (pmp_ret != TRANSLATE_SUCCESS) { return TRANSLATE_PMP_FAIL; } @@ -682,13 +727,14 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, #ifndef CONFIG_USER_ONLY vaddr im_address; hwaddr pa = 0; - int prot, prot2; + int prot, prot2, prot_pmp; bool pmp_violation = false; bool first_stage_error = true; bool two_stage_lookup = false; int ret = TRANSLATE_FAIL; int mode = mmu_idx; - target_ulong tlb_size = 0; + /* default TLB page size */ + target_ulong tlb_size = TARGET_PAGE_SIZE; env->guest_phys_fault_addr = 0; @@ -745,10 +791,10 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, prot &= prot2; - if (riscv_feature(env, RISCV_FEATURE_PMP) && - (ret == TRANSLATE_SUCCESS) && - !pmp_hart_has_privs(env, pa, size, 1 << access_type, mode)) { - ret = TRANSLATE_PMP_FAIL; + if (ret == TRANSLATE_SUCCESS) { + ret = get_physical_address_pmp(env, &prot_pmp, &tlb_size, pa, + size, access_type, mode); + prot &= prot_pmp; } if (ret != TRANSLATE_SUCCESS) { @@ -771,25 +817,21 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, "%s address=%" VADDR_PRIx " ret %d physical " TARGET_FMT_plx " prot %d\n", __func__, address, ret, pa, prot); - } - if (riscv_feature(env, RISCV_FEATURE_PMP) && - (ret == TRANSLATE_SUCCESS) && - !pmp_hart_has_privs(env, pa, size, 1 << access_type, mode)) { - ret = TRANSLATE_PMP_FAIL; + if (ret == TRANSLATE_SUCCESS) { + ret = get_physical_address_pmp(env, &prot_pmp, &tlb_size, pa, + size, access_type, mode); + prot &= prot_pmp; + } } + if (ret == TRANSLATE_PMP_FAIL) { pmp_violation = true; } if (ret == TRANSLATE_SUCCESS) { - if (pmp_is_range_in_tlb(env, pa & TARGET_PAGE_MASK, &tlb_size)) { - tlb_set_page(cs, address & ~(tlb_size - 1), pa & ~(tlb_size - 1), - prot, mmu_idx, tlb_size); - } else { - tlb_set_page(cs, address & TARGET_PAGE_MASK, pa & TARGET_PAGE_MASK, - prot, mmu_idx, TARGET_PAGE_SIZE); - } + tlb_set_page(cs, address & ~(tlb_size - 1), pa & ~(tlb_size - 1), + prot, mmu_idx, tlb_size); return true; } else if (probe) { return false; |