The priv spec only requires the A and L fields to be reset, which the
previous patch now does for every implemented entry. QEMU additionally
keeps derived state next to the architectural registers:
pmp_state.addr[] holds the decoded start and end address of each entry
and pmp_state.num_rules caches the active rule count. Both are
recomputed from the pmpaddr and pmpcfg write paths only, never at
reset.

pmp_is_in_range() compares against addr[].sa and addr[].ea without
consulting the A field, and the partially-inside check in
pmp_hart_has_privs() runs before the A field is tested, so a range
left over from before the reset can still deny an access that
straddles its boundary once the guest configures any entry again.

Clear the derived state alongside the architectural fields so the two
cannot disagree.

Signed-off-by: Sebastian Alba Vives <[email protected]>
---
 target/riscv/tcg/pmp.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/target/riscv/tcg/pmp.c b/target/riscv/tcg/pmp.c
index 061f0e078d..04df53c234 100644
--- a/target/riscv/tcg/pmp.c
+++ b/target/riscv/tcg/pmp.c
@@ -207,6 +207,14 @@ void pmp_unlock_entries(CPURISCVState *env)
     for (i = 0; i < pmp_num; i++) {
         env->pmp_state.pmp[i].cfg_reg &= ~(PMP_LOCK | PMP_AMATCH);
     }
+
+    /*
+     * addr[] and num_rules are derived from the pmpaddr and pmpcfg write
+     * paths and are not recomputed at reset. Clear them here so they
+     * cannot disagree with the architectural fields cleared above.
+     */
+    memset(env->pmp_state.addr, 0, sizeof(env->pmp_state.addr));
+    env->pmp_state.num_rules = 0;
 }
 
 static void pmp_decode_napot(hwaddr a, hwaddr *sa, hwaddr *ea)
-- 
2.43.0


Reply via email to