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
