target/riscv/cpu_helper.c | 81 +++++++++++++++++++++++++++++++++++++--
1 file changed, 77 insertions(+), 4 deletions(-)
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index 3479a62cc7..f8ca74ef61 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -1089,9 +1089,8 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong
newpriv, bool virt_en)
* @access_type: The type of MMU access
* @mode: Indicates current privilege level.
*/
-static int get_physical_address_pmp(CPURISCVState *env, int *prot, hwaddr addr,
- int size, MMUAccessType access_type,
- int mode)
+int get_physical_address_pmp(CPURISCVState *env, int *prot, hwaddr addr,
+ int size, MMUAccessType access_type, int mode)
{
pmp_priv_t pmp_priv;
bool pmp_has_privs;
@@ -1162,6 +1161,60 @@ static bool check_svukte_addr(CPURISCVState *env, vaddr
addr)
return !high_bit;
}
+/*
+ * get_physical_address_mpt - check mpt permission for this physical address
+ *
+ * Lookup the Memory Protection Table and check permission for this
+ * physical address. Returns 0 if the permission checking was successful
+ *
+ * @env: CPURISCVState
+ * @prot: The returned protection attributes
+ * @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_mpt(CPURISCVState *env, int *prot, hwaddr addr,
+ MMUAccessType access_type, int mode)
+{
+ mpt_access_t mpt_access;
+ bool mpt_has_access;
+
+ /*
+ * If the extension is not supported or the mmpt.mode is Bare,
+ * there is no protection, return success.
+ */
+ if (!riscv_cpu_cfg(env)->ext_smmpt || env->mptmode == 0) {
+ *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+ return TRANSLATE_SUCCESS;
+ }
+
+ /*
+ * MPT is checked for all accesses to physical memory, unless the
+ * effective privilege mode is M.
+ *
+ * Data accesses in M-mode when the MPRV bit in mstatus is set and
+ * the MPP field in mstatus contains S or U are subject to MPT checks.
+ *
+ * In riscv_env_mmu_index, The MPRV and MPP bits are already checked and
+ * encoded to mmu_idx, So we do not need to check it here.
+ */
+ if (mode == PRV_M) {
+ *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+ return TRANSLATE_SUCCESS;
+ }
+
+ mpt_has_access = smmpt_check_access(env, addr,
+ &mpt_access, access_type);
+ if (!mpt_has_access) {
+ *prot = 0;
+ return TRANSLATE_MPT_FAIL;
+ }
+
+ *prot = smmpt_access_to_page_prot(mpt_access);
+
+ return TRANSLATE_SUCCESS;
+}
+
/*
* get_physical_address - get the physical address for this virtual address
*
@@ -1356,6 +1409,13 @@ static int get_physical_address(CPURISCVState *env,
hwaddr *physical,
pte_addr = base + idx * ptesize;
}
+ int mpt_prot;
+ int mpt_ret = get_physical_address_mpt(env, &mpt_prot, pte_addr,
+ MMU_DATA_LOAD, PRV_S);
+ if (mpt_ret != TRANSLATE_SUCCESS) {
+ return TRANSLATE_MPT_FAIL;
+ }
+
int pmp_prot;
int pmp_ret = get_physical_address_pmp(env, &pmp_prot, pte_addr,
sxlen_bytes,
@@ -1766,7 +1826,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int
size,
CPURISCVState *env = &cpu->env;
vaddr im_address;
hwaddr pa = 0;
- int prot, prot2, prot_pmp;
+ int prot, prot2, prot_pmp, mpt_prot;
bool pmp_violation = false;
bool first_stage_error = true;
bool two_stage_lookup = mmuidx_2stage(mmu_idx);
@@ -1820,6 +1880,13 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int
size,
prot &= prot2;
if (ret == TRANSLATE_SUCCESS) {
+ ret = get_physical_address_mpt(env, &mpt_prot, pa,
+ access_type, mode);
+ qemu_log_mask(CPU_LOG_MMU,
+ "%s MPT address=" HWADDR_FMT_plx " ret %d prot"
+ " %d\n",
+ __func__, pa, ret, mpt_prot);
+ prot &= mpt_prot;
ret = get_physical_address_pmp(env, &prot_pmp, pa,
size, access_type, mode);
tlb_size = pmp_get_tlb_size(env, pa);
@@ -1855,6 +1922,12 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int
size,
__func__, address, ret, pa, prot);
if (ret == TRANSLATE_SUCCESS) {
+ ret = get_physical_address_mpt(env, &mpt_prot, pa,
+ access_type, mode);
+ qemu_log_mask(CPU_LOG_MMU,
+ "%s MPT address=" HWADDR_FMT_plx " ret %d prot %d\n",
+ __func__, pa, ret, mpt_prot);
+ prot &= mpt_prot;
ret = get_physical_address_pmp(env, &prot_pmp, pa,
size, access_type, mode);
tlb_size = pmp_get_tlb_size(env, pa);