Signed-off-by: Luis Cunha <[email protected]>
---
 target/riscv/cpu.c                |  8 ++++++++
 target/riscv/cpu.h                |  2 ++
 target/riscv/cpu_bits.h           |  2 ++
 target/riscv/cpu_cfg_fields.h.inc |  1 +
 target/riscv/csr.c                | 27 +++++++++++++++++++++++++++
 target/riscv/pmp.c                | 14 +++++++++++++-
 target/riscv/pmp.h                |  1 +
 7 files changed, 54 insertions(+), 1 deletion(-)

diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index e56470a374..5383e07dbd 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -760,6 +760,14 @@ static void riscv_cpu_reset_hold(Object *obj, ResetType 
type)
     }
 
     pmp_unlock_entries(env);
+
+    /* Is SPMP enabled? */
+    if (riscv_cpu_cfg(env)->spmp) {
+        env->mpmpdeleg = MPMP_DELEG_DEFAULT;
+        env->spmp_state.num_deleg_rules = 64 - MPMP_DELEG_DEFAULT;
+
+        spmp_unlock_entries(env);
+    }
 #else
     env->priv = PRV_U;
     env->senvcfg = 0;
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index c265098324..e19c1216ea 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -179,6 +179,7 @@ extern RISCVCPUImpliedExtsRule 
*riscv_multi_ext_implied_rules[];
 #define MIN_RISCV_PMP_GRANULARITY 4
 
 #define MAX_RISCV_SPMPS (64)
+#define MPMP_DELEG_DEFAULT (64)
 
 #if !defined(CONFIG_USER_ONLY)
 #include "pmp.h"
@@ -447,6 +448,7 @@ struct CPUArchState {
 
     /* S-mode Physical Memory Protection */
     spmp_table_t spmp_state;
+    uint16_t     mpmpdeleg;
 
     /* trigger module */
     target_ulong trigger_cur;
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index 9b7ab28bba..36f7c1e873 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -449,6 +449,8 @@
 #define CSR_PMPADDR62       0x3ee
 #define CSR_PMPADDR63       0x3ef
 
+#define CSR_MPMPDELEG       0x316
+
 /* RNMI */
 #define CSR_MNSCRATCH       0x740
 #define CSR_MNEPC           0x741
diff --git a/target/riscv/cpu_cfg_fields.h.inc 
b/target/riscv/cpu_cfg_fields.h.inc
index 72d417c241..772cc176d0 100644
--- a/target/riscv/cpu_cfg_fields.h.inc
+++ b/target/riscv/cpu_cfg_fields.h.inc
@@ -119,6 +119,7 @@ BOOL_FIELD(rvv_ta_all_1s)
 BOOL_FIELD(rvv_ma_all_1s)
 BOOL_FIELD(rvv_vl_half_avl)
 BOOL_FIELD(rvv_vsetvl_x0_vill)
+BOOL_FIELD(ext_smpmpdeleg)
 /* Named features  */
 BOOL_FIELD(ext_svade)
 BOOL_FIELD(ext_zic64b)
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 167d84d92a..4b9fa11540 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -5365,6 +5365,30 @@ static int rmw_xireg_spmp(CPURISCVState *env, int csrno,
     return 0;
 }
 
+/* S-mode Physical Memory Protection */
+static RISCVException rmw_mpmpdeleg(CPURISCVState *env, int csrno,
+                                    target_ulong *ret_val,
+                                    target_ulong new_val, target_ulong wr_mask)
+{
+    uint16_t new_mpmpdeleg = (env->mpmpdeleg & ~wr_mask) | (new_val & wr_mask);
+
+    if (ret_val) {
+        *ret_val = env->mpmpdeleg;
+    }
+
+    /*
+     * pmpnum is locked if MSECCFG_MML is set
+     * and if new_mpmpdeleg is higher than last locked rule
+     */
+    if (!(env->mseccfg & MSECCFG_MML) &&
+        (new_mpmpdeleg & 0x7F) > env->pmp_state.last_locked_rule) {
+        env->mpmpdeleg = new_mpmpdeleg & 0x7F;
+    }
+
+    env->spmp_state.num_deleg_rules = MPMP_DELEG_DEFAULT - env->mpmpdeleg;
+    return RISCV_EXCP_NONE;
+}
+
 static RISCVException read_tselect(CPURISCVState *env, int csrno,
                                    target_ulong *val)
 {
@@ -6362,6 +6386,9 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
     [CSR_PMPADDR63]  = { "pmpaddr63", pmp, read_pmpaddr, write_pmpaddr,
                          .min_priv_ver = PRIV_VERSION_1_12_0           },
 
+    /* S-mode Physical Memory Protection */
+    [CSR_MPMPDELEG]   = { "mpmpdeleg", spmp, NULL, NULL, rmw_mpmpdeleg },
+
     /* Debug CSRs */
     [CSR_TSELECT]   =  { "tselect",  debug, read_tselect,  write_tselect  },
     [CSR_TDATA1]    =  { "tdata1",   debug, read_tdata,    write_tdata    },
diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c
index 3ef62d26ad..22d6f1f5b3 100644
--- a/target/riscv/pmp.c
+++ b/target/riscv/pmp.c
@@ -174,6 +174,12 @@ static bool pmp_write_cfg(CPURISCVState *env, uint32_t 
pmp_index, uint8_t val)
                     return false;
             }
             env->pmp_state.pmp[pmp_index].cfg_reg = val;
+
+            if ((val & PMP_LOCK) &&
+                env->pmp_state.last_locked_rule < (signed int)pmp_index) {
+                env->pmp_state.last_locked_rule = pmp_index;
+            }
+
             pmp_update_rule_addr(env, pmp_index);
             return true;
         }
@@ -193,6 +199,8 @@ void pmp_unlock_entries(CPURISCVState *env)
     for (i = 0; i < pmp_num; i++) {
         env->pmp_state.pmp[i].cfg_reg &= ~(PMP_LOCK | PMP_AMATCH);
     }
+
+    env->pmp_state.last_locked_rule = -1;
 }
 
 static void pmp_decode_napot(hwaddr a, hwaddr *sa, hwaddr *ea)
@@ -385,11 +393,15 @@ bool pmp_hart_has_privs(CPURISCVState *env, hwaddr addr,
         pmp_size = size;
     }
 
+    /* If SPMP is enabled, use the MPMP delegation */
+    uint8_t max_pmp_index =
+        (riscv_cpu_cfg(env)->spmp) ? (env->mpmpdeleg & 0x7F) : pmp_regions;
+
     /*
      * 1.10 draft priv spec states there is an implicit order
      * from low to high
      */
-    for (i = 0; i < pmp_regions; i++) {
+    for (i = 0; i < max_pmp_index; i++) {
         s = pmp_is_in_range(env, i, addr);
         e = pmp_is_in_range(env, i, addr + pmp_size - 1);
 
diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h
index 271cf24169..3194c6b531 100644
--- a/target/riscv/pmp.h
+++ b/target/riscv/pmp.h
@@ -63,6 +63,7 @@ typedef struct {
     pmp_entry_t pmp[MAX_RISCV_PMPS];
     pmp_addr_t  addr[MAX_RISCV_PMPS];
     uint32_t num_rules;
+    signed char last_locked_rule;
 } pmp_table_t;
 
 void pmpcfg_csr_write(CPURISCVState *env, uint32_t reg_index,
-- 
2.43.0


Reply via email to