diff options
author | Jim Shu <cwshu@andestech.com> | 2021-02-21 22:01:20 +0800 |
---|---|---|
committer | Alistair Francis <alistair.francis@wdc.com> | 2021-03-22 21:54:40 -0400 |
commit | b297129ae19e26d3cc0e376d2bfc33d76b06d83b (patch) | |
tree | ab5fd1bf4ebf1dab6b38e9b84ae6d253588495f5 /target/riscv/cpu_helper.c | |
parent | 82a4ed8e5014ee814c63be33987e6783d5eacce2 (diff) |
target/riscv: propagate PMP permission to TLB page
Currently, PMP permission checking of TLB page is bypassed if TLB hits
Fix it by propagating PMP permission to TLB page permission.
PMP permission checking also use MMU-style API to change TLB permission
and size.
Signed-off-by: Jim Shu <cwshu@andestech.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Message-id: 1613916082-19528-2-git-send-email-cwshu@andestech.com
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
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; |