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


Reply via email to