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,