aboutsummaryrefslogtreecommitdiff
path: root/target/riscv/pmp.c
diff options
context:
space:
mode:
Diffstat (limited to 'target/riscv/pmp.c')
-rw-r--r--target/riscv/pmp.c147
1 files changed, 81 insertions, 66 deletions
diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c
index c13a117..5af295e 100644
--- a/target/riscv/pmp.c
+++ b/target/riscv/pmp.c
@@ -33,6 +33,15 @@ static bool pmp_write_cfg(CPURISCVState *env, uint32_t addr_index,
static uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t addr_index);
/*
+ * Convert the PMP permissions to match the truth table in the Smepmp spec.
+ */
+static inline uint8_t pmp_get_smepmp_operation(uint8_t cfg)
+{
+ return ((cfg & PMP_LOCK) >> 4) | ((cfg & PMP_READ) << 2) |
+ (cfg & PMP_WRITE) | ((cfg & PMP_EXEC) >> 2);
+}
+
+/*
* Accessor method to extract address matching type 'a field' from cfg reg
*/
static inline uint8_t pmp_get_a_field(uint8_t cfg)
@@ -46,21 +55,58 @@ static inline uint8_t pmp_get_a_field(uint8_t cfg)
*/
static inline int pmp_is_locked(CPURISCVState *env, uint32_t pmp_index)
{
- /* mseccfg.RLB is set */
- if (MSECCFG_RLB_ISSET(env)) {
- return 0;
- }
-
if (env->pmp_state.pmp[pmp_index].cfg_reg & PMP_LOCK) {
return 1;
}
- /* Top PMP has no 'next' to check */
- if ((pmp_index + 1u) >= MAX_RISCV_PMPS) {
+ return 0;
+}
+
+/*
+ * Check whether a PMP is locked for writing or not.
+ * (i.e. has LOCK flag and mseccfg.RLB is unset)
+ */
+static int pmp_is_readonly(CPURISCVState *env, uint32_t pmp_index)
+{
+ return pmp_is_locked(env, pmp_index) && !MSECCFG_RLB_ISSET(env);
+}
+
+/*
+ * Check whether `val` is an invalid Smepmp config value
+ */
+static int pmp_is_invalid_smepmp_cfg(CPURISCVState *env, uint8_t val)
+{
+ /* No check if mseccfg.MML is not set or if mseccfg.RLB is set */
+ if (!MSECCFG_MML_ISSET(env) || MSECCFG_RLB_ISSET(env)) {
return 0;
}
- return 0;
+ /*
+ * Adding a rule with executable privileges that either is M-mode-only
+ * or a locked Shared-Region is not possible
+ */
+ switch (pmp_get_smepmp_operation(val)) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 12:
+ case 14:
+ case 15:
+ return 0;
+ case 9:
+ case 10:
+ case 11:
+ case 13:
+ return 1;
+ default:
+ g_assert_not_reached();
+ }
}
/*
@@ -91,45 +137,18 @@ static inline uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t pmp_index)
static bool pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val)
{
if (pmp_index < MAX_RISCV_PMPS) {
- bool locked = true;
-
- if (riscv_cpu_cfg(env)->ext_smepmp) {
- /* mseccfg.RLB is set */
- if (MSECCFG_RLB_ISSET(env)) {
- locked = false;
- }
-
- /* mseccfg.MML is not set */
- if (!MSECCFG_MML_ISSET(env) && !pmp_is_locked(env, pmp_index)) {
- locked = false;
- }
-
- /* mseccfg.MML is set */
- if (MSECCFG_MML_ISSET(env)) {
- /* not adding execute bit */
- if ((val & PMP_LOCK) != 0 && (val & PMP_EXEC) != PMP_EXEC) {
- locked = false;
- }
- /* shared region and not adding X bit */
- if ((val & PMP_LOCK) != PMP_LOCK &&
- (val & 0x7) != (PMP_WRITE | PMP_EXEC)) {
- locked = false;
- }
- }
- } else {
- if (!pmp_is_locked(env, pmp_index)) {
- locked = false;
- }
+ if (env->pmp_state.pmp[pmp_index].cfg_reg == val) {
+ /* no change */
+ return false;
}
- if (locked) {
- qemu_log_mask(LOG_GUEST_ERROR, "ignoring pmpcfg write - locked\n");
- } else if (env->pmp_state.pmp[pmp_index].cfg_reg != val) {
- /* If !mseccfg.MML then ignore writes with encoding RW=01 */
- if ((val & PMP_WRITE) && !(val & PMP_READ) &&
- !MSECCFG_MML_ISSET(env)) {
- return false;
- }
+ if (pmp_is_readonly(env, pmp_index)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "ignoring pmpcfg write - read only\n");
+ } else if (pmp_is_invalid_smepmp_cfg(env, val)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "ignoring pmpcfg write - invalid\n");
+ } else {
env->pmp_state.pmp[pmp_index].cfg_reg = val;
pmp_update_rule_addr(env, pmp_index);
return true;
@@ -353,16 +372,6 @@ bool pmp_hart_has_privs(CPURISCVState *env, hwaddr addr,
const uint8_t a_field =
pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg);
- /*
- * Convert the PMP permissions to match the truth table in the
- * Smepmp spec.
- */
- const uint8_t smepmp_operation =
- ((env->pmp_state.pmp[i].cfg_reg & PMP_LOCK) >> 4) |
- ((env->pmp_state.pmp[i].cfg_reg & PMP_READ) << 2) |
- (env->pmp_state.pmp[i].cfg_reg & PMP_WRITE) |
- ((env->pmp_state.pmp[i].cfg_reg & PMP_EXEC) >> 2);
-
if (((s + e) == 2) && (PMP_AMATCH_OFF != a_field)) {
/*
* If the PMP entry is not off and the address is in range,
@@ -381,6 +390,9 @@ bool pmp_hart_has_privs(CPURISCVState *env, hwaddr addr,
/*
* If mseccfg.MML Bit set, do the enhanced pmp priv check
*/
+ const uint8_t smepmp_operation =
+ pmp_get_smepmp_operation(env->pmp_state.pmp[i].cfg_reg);
+
if (mode == PRV_M) {
switch (smepmp_operation) {
case 0:
@@ -517,6 +529,11 @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index,
bool is_next_cfg_tor = false;
if (addr_index < MAX_RISCV_PMPS) {
+ if (env->pmp_state.pmp[addr_index].addr_reg == val) {
+ /* no change */
+ return;
+ }
+
/*
* In TOR mode, need to check the lock bit of the next pmp
* (if there is a next).
@@ -525,25 +542,23 @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index,
uint8_t pmp_cfg = env->pmp_state.pmp[addr_index + 1].cfg_reg;
is_next_cfg_tor = PMP_AMATCH_TOR == pmp_get_a_field(pmp_cfg);
- if (pmp_is_locked(env, addr_index + 1) && is_next_cfg_tor) {
+ if (pmp_is_readonly(env, addr_index + 1) && is_next_cfg_tor) {
qemu_log_mask(LOG_GUEST_ERROR,
- "ignoring pmpaddr write - pmpcfg + 1 locked\n");
+ "ignoring pmpaddr write - pmpcfg+1 read only\n");
return;
}
}
- if (!pmp_is_locked(env, addr_index)) {
- if (env->pmp_state.pmp[addr_index].addr_reg != val) {
- env->pmp_state.pmp[addr_index].addr_reg = val;
- pmp_update_rule_addr(env, addr_index);
- if (is_next_cfg_tor) {
- pmp_update_rule_addr(env, addr_index + 1);
- }
- tlb_flush(env_cpu(env));
+ if (!pmp_is_readonly(env, addr_index)) {
+ env->pmp_state.pmp[addr_index].addr_reg = val;
+ pmp_update_rule_addr(env, addr_index);
+ if (is_next_cfg_tor) {
+ pmp_update_rule_addr(env, addr_index + 1);
}
+ tlb_flush(env_cpu(env));
} else {
qemu_log_mask(LOG_GUEST_ERROR,
- "ignoring pmpaddr write - locked\n");
+ "ignoring pmpaddr write - read only\n");
}
} else {
qemu_log_mask(LOG_GUEST_ERROR,