Integrate the SPMP implementation into the RISC-V CPU model.Memory access checks are updated to take SPMP configuration into account when the extension is active.
Signed-off-by: Luis Cunha <[email protected]> --- target/riscv/cpu.h | 1 + target/riscv/cpu_helper.c | 139 +++++++++++++++++++++++++++++++------- 2 files changed, 116 insertions(+), 24 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index e19c1216ea..bf05ab66ce 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -130,6 +130,7 @@ enum { TRANSLATE_SUCCESS, TRANSLATE_FAIL, TRANSLATE_PMP_FAIL, + TRANSLATE_SPMP_FAIL, TRANSLATE_G_STAGE_FAIL }; diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index dd6c861a90..de20f6c9f4 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -36,6 +36,7 @@ #include "cpu_bits.h" #include "debug.h" #include "pmp.h" +#include "spmp.h" #include "qemu/plugin.h" int riscv_env_mmu_index(CPURISCVState *env, bool ifetch) @@ -1113,6 +1114,42 @@ static int get_physical_address_pmp(CPURISCVState *env, int *prot, hwaddr addr, return TRANSLATE_SUCCESS; } +/* + * get_physical_address_spmp - check SPMP permission for this physical address + * + * Match the SPMP region and check permission for this physical address. + * Returns 0 if the permission checking was successful. + * The SPMP check is happened before the PMP check. + * + * @env: CPURISCVState + * @prot: The returned protection attributes + * @addr: The physical address to be checked permission + * @access_type: The type of access + * @mode: Indicates current privilege level. + */ +static int get_physical_address_spmp(CPURISCVState *env, int *prot, hwaddr addr, + int size, int access_type, + int mode) +{ + spmp_priv_t spmp_priv; + + if (!riscv_cpu_cfg(env)->spmp) { + *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + return TRANSLATE_SUCCESS; + } + + if (!spmp_hart_has_privs(env, addr, size, 1 << access_type, &spmp_priv, + mode)) { + *prot = 0; + return TRANSLATE_SPMP_FAIL; + } + + *prot = spmp_priv_to_page_prot(spmp_priv); + + return TRANSLATE_SUCCESS; +} + + /* Returns 'true' if a svukte address check is needed */ static bool do_svukte_check(CPURISCVState *env, bool first_stage, int mode, bool virt) @@ -1616,8 +1653,8 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, static void raise_mmu_exception(CPURISCVState *env, target_ulong address, MMUAccessType access_type, bool pmp_violation, - bool first_stage, bool two_stage, - bool two_stage_indirect) + bool spmp_violation, bool first_stage, + bool two_stage, bool two_stage_indirect) { CPUState *cs = env_cpu(env); @@ -1625,7 +1662,7 @@ static void raise_mmu_exception(CPURISCVState *env, target_ulong address, case MMU_INST_FETCH: if (pmp_violation) { cs->exception_index = RISCV_EXCP_INST_ACCESS_FAULT; - } else if (env->virt_enabled && !first_stage) { + } else if (env->virt_enabled && (!first_stage || spmp_violation)) { cs->exception_index = RISCV_EXCP_INST_GUEST_PAGE_FAULT; } else { cs->exception_index = RISCV_EXCP_INST_PAGE_FAULT; @@ -1634,7 +1671,8 @@ static void raise_mmu_exception(CPURISCVState *env, target_ulong address, case MMU_DATA_LOAD: if (pmp_violation) { cs->exception_index = RISCV_EXCP_LOAD_ACCESS_FAULT; - } else if (two_stage && !first_stage) { + } else if ((two_stage && !first_stage) || + (env->virt_enabled && spmp_violation)) { cs->exception_index = RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT; } else { cs->exception_index = RISCV_EXCP_LOAD_PAGE_FAULT; @@ -1643,7 +1681,8 @@ static void raise_mmu_exception(CPURISCVState *env, target_ulong address, case MMU_DATA_STORE: if (pmp_violation) { cs->exception_index = RISCV_EXCP_STORE_AMO_ACCESS_FAULT; - } else if (two_stage && !first_stage) { + } else if ((two_stage && !first_stage) || + (env->virt_enabled && spmp_violation)) { cs->exception_index = RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT; } else { cs->exception_index = RISCV_EXCP_STORE_PAGE_FAULT; @@ -1766,8 +1805,9 @@ 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, prot_spmp; bool pmp_violation = false; + bool spmp_violation = false; bool first_stage_error = true; bool two_stage_lookup = mmuidx_2stage(mmu_idx); bool two_stage_indirect_error = false; @@ -1820,16 +1860,38 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, prot &= prot2; if (ret == TRANSLATE_SUCCESS) { - ret = get_physical_address_pmp(env, &prot_pmp, pa, - size, access_type, mode); - tlb_size = pmp_get_tlb_size(env, pa); + int vm; + if (riscv_cpu_mxl(env) == MXL_RV32) { + vm = get_field(env->hgatp, SATP32_MODE); + } else { + vm = get_field(env->hgatp, SATP64_MODE); + } - qemu_log_mask(CPU_LOG_MMU, - "%s PMP address=" HWADDR_FMT_plx " ret %d prot" - " %d tlb_size %" HWADDR_PRIu "\n", - __func__, pa, ret, prot_pmp, tlb_size); + if (vm == VM_1_10_MBARE && riscv_cpu_cfg(env)->spmp) { + /* S-mode Physical Memory Protection check */ + ret = get_physical_address_spmp(env, &prot_spmp, pa, + size, access_type, mode); - prot &= prot_pmp; + prot &= prot_spmp; + + if (ret == TRANSLATE_SPMP_FAIL) { + spmp_violation = true; + } + } + + /* Only apply checks when the SPMP passed */ + if (ret != TRANSLATE_SPMP_FAIL) { + ret = get_physical_address_pmp(env, &prot_pmp, pa, + size, access_type, mode); + tlb_size = pmp_get_tlb_size(env, pa); + + qemu_log_mask(CPU_LOG_MMU, + "%s PMP address=" HWADDR_FMT_plx " ret %d prot" + " %d tlb_size %" HWADDR_PRIu "\n", + __func__, pa, ret, prot_pmp, tlb_size); + + prot &= prot_pmp; + } } else { /* * Guest physical address translation failed, this is a HS @@ -1855,16 +1917,45 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, __func__, address, ret, pa, prot); if (ret == TRANSLATE_SUCCESS) { - ret = get_physical_address_pmp(env, &prot_pmp, pa, - size, access_type, mode); - tlb_size = pmp_get_tlb_size(env, pa); + /* + * Check both SPMP and PMP if the core is running in bare mode. + */ + int vm; + if (riscv_cpu_mxl(env) == MXL_RV32) { + vm = get_field(env->satp, SATP32_MODE); + } else { + vm = get_field(env->satp, SATP64_MODE); + } - qemu_log_mask(CPU_LOG_MMU, - "%s PMP address=" HWADDR_FMT_plx " ret %d prot" - " %d tlb_size %" HWADDR_PRIu "\n", - __func__, pa, ret, prot_pmp, tlb_size); + if (vm == VM_1_10_MBARE && riscv_cpu_cfg(env)->spmp) { + /* S-mode Physical Memory Protection check */ + ret = get_physical_address_spmp(env, &prot_spmp, pa, + size, access_type, mode); + + qemu_log_mask(CPU_LOG_MMU, + "%s SPMP address=" HWADDR_FMT_plx " ret %d" + " prot %d\n", __func__, pa, ret, prot_spmp); + + prot &= prot_spmp; + + if (ret == TRANSLATE_SPMP_FAIL) { + spmp_violation = true; + } + } - prot &= prot_pmp; + /* Only apply checks when the SPMP passed */ + if (ret != TRANSLATE_SPMP_FAIL) { + ret = get_physical_address_pmp(env, &prot_pmp, pa, + size, access_type, mode); + tlb_size = pmp_get_tlb_size(env, pa); + + qemu_log_mask(CPU_LOG_MMU, + "%s PMP address=" HWADDR_FMT_plx " ret %d prot" + " %d tlb_size %" HWADDR_PRIu "\n", + __func__, pa, ret, prot_pmp, tlb_size); + + prot &= prot_pmp; + } } } @@ -1897,8 +1988,8 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, wp_access, retaddr); raise_mmu_exception(env, address, access_type, pmp_violation, - first_stage_error, two_stage_lookup, - two_stage_indirect_error); + spmp_violation, first_stage_error, + two_stage_lookup, two_stage_indirect_error); cpu_loop_exit_restore(cs, retaddr); } -- 2.43.0
