On Wed, 2026-09-16 at 00:10 -1000, Richard Henderson wrote:
> On 9/15/26 17:43, Sebastian Alba Vives wrote:
> > pmp_unlock_entries() bounds its loop with pmp_get_num_rules(),
> > which is
> > the count of entries whose A field is not OFF, not the number of
> > implemented PMP entries. When the active rules are not contiguous
> > from
> > index 0, every entry at an index greater than or equal to num_rules
> > keeps its lock bit across a CPU reset.
> > 
> > Those entries stay locked after the reset has cleared mseccfg, so
> > mseccfg.RLB is no longer set and pmp_is_readonly() silently
> > discards
> > subsequent pmpcfg writes. Firmware started after the reset finds
> > PMP
> > entries it did not configure and cannot clear.
> > 
> > Reproduced on -machine virt -cpu rv64,smepmp=true with a bare-metal
> > payload that sets pmpcfg0 = 0x99990000 (entries 2 and 3 as
> > L | NAPOT | R, entries 0 and 1 left OFF) and then resets through
> > the
> > sifive_test device. On the second boot pmpcfg0 reads back
> > 0x99990000
> > and writing 0 to it leaves the value unchanged. With this patch the
> > same payload reads 0x01010000 on the second boot and clearing
> > pmpcfg0
> > succeeds.
> > 
> > Iterate over the implemented entries instead.
> > 
> > Fixes: 4bf501dc0118 ("target/riscv: pmp: Clear pmp/smepmp bits on
> > reset")
> > Signed-off-by: Sebastian Alba Vives <[email protected]>
> > ---
> >   target/riscv/tcg/pmp.c | 4 ++--
> >   1 file changed, 2 insertions(+), 2 deletions(-)
> > 
> > diff --git a/target/riscv/tcg/pmp.c b/target/riscv/tcg/pmp.c
> > index 41b55519a8..5286c6aa90 100644
> > --- a/target/riscv/tcg/pmp.c
> > +++ b/target/riscv/tcg/pmp.c
> > @@ -201,10 +201,10 @@ static bool pmp_write_cfg(CPURISCVState *env,
> > uint32_t pmp_index, uint8_t val)
> >   
> >   void pmp_unlock_entries(CPURISCVState *env)
> >   {
> > -    uint32_t pmp_num = pmp_get_num_rules(env);
> > +    uint8_t pmp_regions = riscv_cpu_cfg(env)->pmp_regions;
> >       int i;
> >   
> > -    for (i = 0; i < pmp_num; i++) {
> > +    for (i = 0; i < pmp_regions; i++) {
> >           env->pmp_state.pmp[i].cfg_reg &= ~(PMP_LOCK |
> > PMP_AMATCH);
> >       }
> >   }
> 
> Why is the entire env->pmp_state not zeroed instead?

The RISC-V spec specifically states that on reset:

"Writable PMP registers’ A and L fields are set to 0...", so this does
seem to be the correct approach

Reviewed-by: Alistair Francis <[email protected]>

Alistair

> 
> 
> r~

Reply via email to