On Wed, Oct 22, 2025 at 12:43 PM Jay Chang <[email protected]> wrote: > > This patch ensure pmpcfg and pmpaddr comply with WARL constraints. > > When the PMP granularity is greater than 4 bytes, NA4 mode is not valid > per the spec and will be silently ignored. > > According to the spec, changing pmpcfg.A only affects the "read" value > of pmpaddr. When G > 2 and pmpcfg.A is NAPOT, bits pmpaddr[G-2:0] read > as all ones. When G > 1 and pmpcfg.A is OFF or TOR, bits pmpaddr[G-1:0] > read as all zeros. This allows software to read back the correct > granularity value. > > In addition, when updating the PMP address rule in TOR mode, > the start and end addresses of the PMP region should be aligned > to the PMP granularity. (The current SPEC only state in TOR mode > that bits pmpaddr[G-1:0] do not affect the TOR address-matching logic.) > > Signed-off-by: Jay Chang <[email protected]> > Reviewed-by: Frank Chang <[email protected]> > Reviewed-by: Jim Shu <[email protected]>
Reviewed-by: Alistair Francis <[email protected]> Alistair > --- > target/riscv/pmp.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 46 insertions(+) > > diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c > index 72f1372a49..3ef62d26ad 100644 > --- a/target/riscv/pmp.c > +++ b/target/riscv/pmp.c > @@ -108,6 +108,17 @@ static int pmp_is_invalid_smepmp_cfg(CPURISCVState *env, > uint8_t val) > g_assert_not_reached(); > } > } > +/* > + * Calculate PMP granularity value 'g' > + * > + * The granularity value 'g' is defined as log2(granularity) - 2, where > + * granularity is the minimum alignment requirement for PMP regions in bytes. > + */ > +static inline int pmp_get_granularity_g(CPURISCVState *env) > +{ > + return __builtin_ctz(riscv_cpu_cfg(env)->pmp_granularity >> 2); > +} > + > > /* > * Count the number of active rules. > @@ -153,6 +164,15 @@ static bool pmp_write_cfg(CPURISCVState *env, uint32_t > pmp_index, uint8_t val) > qemu_log_mask(LOG_GUEST_ERROR, > "ignoring pmpcfg write - invalid\n"); > } else { > + uint8_t a_field = pmp_get_a_field(val); > + /* > + * When granularity g >= 1 (i.e., granularity > 4 bytes), > + * the NA4 (Naturally Aligned 4-byte) mode is not selectable > + */ > + if ((riscv_cpu_cfg(env)->pmp_granularity > > + MIN_RISCV_PMP_GRANULARITY) && (a_field == PMP_AMATCH_NA4)) { > + return false; > + } > env->pmp_state.pmp[pmp_index].cfg_reg = val; > pmp_update_rule_addr(env, pmp_index); > return true; > @@ -199,6 +219,7 @@ void pmp_update_rule_addr(CPURISCVState *env, uint32_t > pmp_index) > target_ulong prev_addr = 0u; > hwaddr sa = 0u; > hwaddr ea = 0u; > + int g = pmp_get_granularity_g(env); > > if (pmp_index >= 1u) { > prev_addr = env->pmp_state.pmp[pmp_index - 1].addr_reg; > @@ -211,6 +232,11 @@ void pmp_update_rule_addr(CPURISCVState *env, uint32_t > pmp_index) > break; > > case PMP_AMATCH_TOR: > + /* Bits pmpaddr[G-1:0] do not affect the TOR address-matching logic. > */ > + if (g >= 1) { > + prev_addr &= ~((1ULL << g) - 1ULL); > + this_addr &= ~((1ULL << g) - 1ULL); > + } > if (prev_addr >= this_addr) { > sa = ea = 0u; > break; > @@ -577,6 +603,7 @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t > addr_index, > > /* > * Handle a read from a pmpaddr CSR > + * Change A field of pmpcfg affects the read value of pmpaddr > */ > target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index) > { > @@ -585,6 +612,25 @@ target_ulong pmpaddr_csr_read(CPURISCVState *env, > uint32_t addr_index) > > if (addr_index < pmp_regions) { > val = env->pmp_state.pmp[addr_index].addr_reg; > + int g = pmp_get_granularity_g(env); > + switch (pmp_get_a_field(env->pmp_state.pmp[addr_index].cfg_reg)) { > + case PMP_AMATCH_OFF: > + /* fallthrough */ > + case PMP_AMATCH_TOR: > + /* Bit [g-1:0] read all zero */ > + if (g >= 1 && g < TARGET_LONG_BITS) { > + val &= ~((1ULL << g) - 1ULL); > + } > + break; > + case PMP_AMATCH_NAPOT: > + /* Bit [g-2:0] read all one */ > + if (g >= 2 && g < TARGET_LONG_BITS) { > + val |= ((1ULL << (g - 1)) - 1ULL); > + } > + break; > + default: > + break; > + } > trace_pmpaddr_csr_read(env->mhartid, addr_index, val); > } else { > qemu_log_mask(LOG_GUEST_ERROR, > -- > 2.48.1 > >
