#include "trace.h"
#include "exec/exec-all.h"
-static void pmp_write_cfg(CPURISCVState *env, uint32_t addr_index,
+static bool pmp_write_cfg(CPURISCVState *env, uint32_t addr_index,
uint8_t val);
static uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t addr_index);
-static void pmp_update_rule(CPURISCVState *env, uint32_t pmp_index);
/*
* Accessor method to extract address matching type 'a field' from cfg reg
*/
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;
* Accessor to set the cfg reg for a specific PMP/HART
* Bounds checks and relevant lock bit.
*/
-static void pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val)
+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)->epmp) {
+ if (riscv_cpu_cfg(env)->ext_smepmp) {
/* mseccfg.RLB is set */
if (MSECCFG_RLB_ISSET(env)) {
locked = false;
if (locked) {
qemu_log_mask(LOG_GUEST_ERROR, "ignoring pmpcfg write - locked\n");
- } else {
+ } 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;
+ }
env->pmp_state.pmp[pmp_index].cfg_reg = val;
- pmp_update_rule(env, pmp_index);
+ pmp_update_rule_addr(env, pmp_index);
+ return true;
}
} else {
qemu_log_mask(LOG_GUEST_ERROR,
"ignoring pmpcfg write - out of bounds\n");
}
+
+ return false;
+}
+
+void pmp_unlock_entries(CPURISCVState *env)
+{
+ uint32_t pmp_num = pmp_get_num_rules(env);
+ int i;
+
+ for (i = 0; i < pmp_num; i++) {
+ env->pmp_state.pmp[i].cfg_reg &= ~(PMP_LOCK | PMP_AMATCH);
+ }
}
-static void pmp_decode_napot(target_ulong a, target_ulong *sa,
- target_ulong *ea)
+static void pmp_decode_napot(hwaddr a, hwaddr *sa, hwaddr *ea)
{
/*
* aaaa...aaa0 8-byte NAPOT range
uint8_t this_cfg = env->pmp_state.pmp[pmp_index].cfg_reg;
target_ulong this_addr = env->pmp_state.pmp[pmp_index].addr_reg;
target_ulong prev_addr = 0u;
- target_ulong sa = 0u;
- target_ulong ea = 0u;
+ hwaddr sa = 0u;
+ hwaddr ea = 0u;
if (pmp_index >= 1u) {
prev_addr = env->pmp_state.pmp[pmp_index - 1].addr_reg;
}
}
-/*
- * Convert cfg/addr reg values here into simple 'sa' --> start address and 'ea'
- * end address values.
- * This function is called relatively infrequently whereas the check that
- * an address is within a pmp rule is called often, so optimise that one
- */
-static void pmp_update_rule(CPURISCVState *env, uint32_t pmp_index)
-{
- pmp_update_rule_addr(env, pmp_index);
- pmp_update_rule_nums(env);
-}
-
-static int pmp_is_in_range(CPURISCVState *env, int pmp_index,
- target_ulong addr)
+static int pmp_is_in_range(CPURISCVState *env, int pmp_index, hwaddr addr)
{
int result = 0;
/*
* Check if the address has required RWX privs when no PMP entry is matched.
*/
-static bool pmp_hart_has_privs_default(CPURISCVState *env, target_ulong addr,
- target_ulong size, pmp_priv_t privs,
+static bool pmp_hart_has_privs_default(CPURISCVState *env, pmp_priv_t privs,
pmp_priv_t *allowed_privs,
target_ulong mode)
{
bool ret;
- if (riscv_cpu_cfg(env)->epmp) {
- if (MSECCFG_MMWP_ISSET(env)) {
- /*
- * The Machine Mode Whitelist Policy (mseccfg.MMWP) is set
- * so we default to deny all, even for M-mode.
- */
+ if (MSECCFG_MMWP_ISSET(env)) {
+ /*
+ * The Machine Mode Whitelist Policy (mseccfg.MMWP) is set
+ * so we default to deny all, even for M-mode.
+ */
+ *allowed_privs = 0;
+ return false;
+ } else if (MSECCFG_MML_ISSET(env)) {
+ /*
+ * The Machine Mode Lockdown (mseccfg.MML) bit is set
+ * so we can only execute code in M-mode with an applicable
+ * rule. Other modes are disabled.
+ */
+ if (mode == PRV_M && !(privs & PMP_EXEC)) {
+ ret = true;
+ *allowed_privs = PMP_READ | PMP_WRITE;
+ } else {
+ ret = false;
*allowed_privs = 0;
- return false;
- } else if (MSECCFG_MML_ISSET(env)) {
- /*
- * The Machine Mode Lockdown (mseccfg.MML) bit is set
- * so we can only execute code in M-mode with an applicable
- * rule. Other modes are disabled.
- */
- if (mode == PRV_M && !(privs & PMP_EXEC)) {
- ret = true;
- *allowed_privs = PMP_READ | PMP_WRITE;
- } else {
- ret = false;
- *allowed_privs = 0;
- }
-
- return ret;
}
+
+ return ret;
}
if (!riscv_cpu_cfg(env)->pmp || (mode == PRV_M)) {
/*
* Check if the address has required RWX privs to complete desired operation
- * Return PMP rule index if a pmp rule match
- * Return MAX_RISCV_PMPS if default match
- * Return negtive value if no match
+ * Return true if a pmp rule match or default match
+ * Return false if no match
*/
-int pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
- target_ulong size, pmp_priv_t privs,
- pmp_priv_t *allowed_privs, target_ulong mode)
+bool pmp_hart_has_privs(CPURISCVState *env, hwaddr addr,
+ target_ulong size, pmp_priv_t privs,
+ pmp_priv_t *allowed_privs, target_ulong mode)
{
int i = 0;
- int ret = -1;
int pmp_size = 0;
- target_ulong s = 0;
- target_ulong e = 0;
+ hwaddr s = 0;
+ hwaddr e = 0;
/* Short cut if no rules */
if (0 == pmp_get_num_rules(env)) {
- if (pmp_hart_has_privs_default(env, addr, size, privs,
- allowed_privs, mode)) {
- ret = MAX_RISCV_PMPS;
- }
+ return pmp_hart_has_privs_default(env, privs, allowed_privs, mode);
}
if (size == 0) {
if ((s + e) == 1) {
qemu_log_mask(LOG_GUEST_ERROR,
"pmp violation - access is partially inside\n");
- ret = -1;
- break;
+ *allowed_privs = 0;
+ return false;
}
/* fully inside */
/*
* Convert the PMP permissions to match the truth table in the
- * ePMP spec.
+ * Smepmp spec.
*/
- const uint8_t epmp_operation =
+ 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) |
* If mseccfg.MML Bit set, do the enhanced pmp priv check
*/
if (mode == PRV_M) {
- switch (epmp_operation) {
+ switch (smepmp_operation) {
case 0:
case 1:
case 4:
g_assert_not_reached();
}
} else {
- switch (epmp_operation) {
+ switch (smepmp_operation) {
case 0:
case 8:
case 9:
* defined with PMP must be used. We shouldn't fallback on
* finding default privileges.
*/
- ret = i;
- break;
+ return (privs & *allowed_privs) == privs;
}
}
/* No rule matched */
- if (ret == -1) {
- if (pmp_hart_has_privs_default(env, addr, size, privs,
- allowed_privs, mode)) {
- ret = MAX_RISCV_PMPS;
- }
- }
-
- return ret;
+ return pmp_hart_has_privs_default(env, privs, allowed_privs, mode);
}
/*
int i;
uint8_t cfg_val;
int pmpcfg_nums = 2 << riscv_cpu_mxl(env);
+ bool modified = false;
trace_pmpcfg_csr_write(env->mhartid, reg_index, val);
for (i = 0; i < pmpcfg_nums; i++) {
cfg_val = (val >> 8 * i) & 0xff;
- pmp_write_cfg(env, (reg_index * 4) + i, cfg_val);
+ modified |= pmp_write_cfg(env, (reg_index * 4) + i, cfg_val);
}
/* If PMP permission of any addr has been changed, flush TLB pages. */
- tlb_flush(env_cpu(env));
+ if (modified) {
+ pmp_update_rule_nums(env);
+ tlb_flush(env_cpu(env));
+ }
}
target_ulong val)
{
trace_pmpaddr_csr_write(env->mhartid, addr_index, val);
+ bool is_next_cfg_tor = false;
if (addr_index < MAX_RISCV_PMPS) {
/*
*/
if (addr_index + 1 < MAX_RISCV_PMPS) {
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_cfg & PMP_LOCK &&
- PMP_AMATCH_TOR == pmp_get_a_field(pmp_cfg)) {
+ if (pmp_cfg & PMP_LOCK && is_next_cfg_tor) {
qemu_log_mask(LOG_GUEST_ERROR,
"ignoring pmpaddr write - pmpcfg + 1 locked\n");
return;
}
if (!pmp_is_locked(env, addr_index)) {
- env->pmp_state.pmp[addr_index].addr_reg = val;
- pmp_update_rule(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));
+ }
} else {
qemu_log_mask(LOG_GUEST_ERROR,
"ignoring pmpaddr write - locked\n");
}
}
- /* Sticky bits */
- val |= (env->mseccfg & (MSECCFG_MMWP | MSECCFG_MML));
+ if (riscv_cpu_cfg(env)->ext_smepmp) {
+ /* Sticky bits */
+ val |= (env->mseccfg & (MSECCFG_MMWP | MSECCFG_MML));
+ if ((val ^ env->mseccfg) & (MSECCFG_MMWP | MSECCFG_MML)) {
+ tlb_flush(env_cpu(env));
+ }
+ } else {
+ val &= ~(MSECCFG_MMWP | MSECCFG_MML | MSECCFG_RLB);
+ }
env->mseccfg = val;
}
* To avoid this we return a size of 1 (which means no caching) if the PMP
* region only covers partial of the TLB page.
*/
-target_ulong pmp_get_tlb_size(CPURISCVState *env, target_ulong addr)
+target_ulong pmp_get_tlb_size(CPURISCVState *env, hwaddr addr)
{
- target_ulong pmp_sa;
- target_ulong pmp_ea;
- target_ulong tlb_sa = addr & ~(TARGET_PAGE_SIZE - 1);
- target_ulong tlb_ea = tlb_sa + TARGET_PAGE_SIZE - 1;
+ hwaddr pmp_sa;
+ hwaddr pmp_ea;
+ hwaddr tlb_sa = addr & ~(TARGET_PAGE_SIZE - 1);
+ hwaddr tlb_ea = tlb_sa + TARGET_PAGE_SIZE - 1;
int i;
/*