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 <luisccunha8@gmail.com>
---
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
© 2016 - 2026 Red Hat, Inc.