On Fri, 2026-05-22 at 14:25 -0300, Daniel Henrique Barboza wrote:
> We're not doing anything special w.r.t PMA (Physical Memory Access)
> related faults, handling them like regular faults that will
> eventually
> turn to be regular page faults.
>
> Turns out we can't do that. Priv spec section "Virtual Address
> Translation Process" mentions:
>
> "If a store to the PTE at address a+va.vpn[i]×PTESIZE would violate a
> PMA or PMP check, raise an access-fault exception corresponding to
> the
> original access type."
>
> This means that we should handle PMA violations with access faults,
> like
> we're already doing with PMP. One clear code path where we should
> throw
> a PMA failure, exposed by [1], is the error return from
> address_space_ld* call.
>
> There's a separated issue with the error code being returned by them
> (it
> always return DECODE_ERROR even with 'rejected' reads) that we're
> going
> to work around it by assuming that we did a good job with the PTE
> address sanitization beforehand, and interpret that the error here is
> related to PMA. This is of course not ideal but fixing this QEMU API
> is
> out of scope for this work.
>
> All this said, we'll set the new pmp_pma_violation flag when we have
> either a PMP or a PMA fault, and everything else shall fall into
> place.
>
> [1] https://gitlab.com/qemu-project/qemu/-/work_items/3502
>
> Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3502
> Signed-off-by: Daniel Henrique Barboza
> <[email protected]>
Thanks!
Applied to riscv-to-apply.next
Alistair
> ---
> target/riscv/cpu.h | 3 ++-
> target/riscv/cpu_helper.c | 34 +++++++++++++++++++++++++---------
> 2 files changed, 27 insertions(+), 10 deletions(-)
>
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index 7d79c7a5a7..e5e1687177 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -131,7 +131,8 @@ enum {
> TRANSLATE_SUCCESS,
> TRANSLATE_FAIL,
> TRANSLATE_PMP_FAIL,
> - TRANSLATE_G_STAGE_FAIL
> + TRANSLATE_G_STAGE_FAIL,
> + TRANSLATE_PMA_FAIL,
> };
>
> /* Extension context status */
> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> index 17305e1bb7..5303d9f7f4 100644
> --- a/target/riscv/cpu_helper.c
> +++ b/target/riscv/cpu_helper.c
> @@ -1424,7 +1424,22 @@ static int get_physical_address(CPURISCVState
> *env, hwaddr *physical,
> }
>
> if (res != MEMTX_OK) {
> - return TRANSLATE_FAIL;
> + /*
> + * The result of address_space_* APIs above does not
> take into
> + * consideration reject reads, putting all errors in the
> same
> + * cathegory (DECODE_ERROR), although there's a clear
> + * distinction between a rejected read versus other
> errors
> + * (see memory_region_dispatch_read() ->
> + * memory_region_access_valid()). This is something
> that
> + * we might have to deal with core QEMU logic some other
> + * day.
> + *
> + * For this particular error path, given that we made
> checks
> + * w.r.t legal PTE address before calling those APIs,
> we'll
> + * assume that anything != MEMTX_OK means a rejected
> read,
> + * i.e. a PMA error.
> + */
> + return TRANSLATE_PMA_FAIL;
> }
>
> if (riscv_cpu_sxl(env) == MXL_RV32) {
> @@ -1668,7 +1683,8 @@ static int get_physical_address(CPURISCVState
> *env, hwaddr *physical,
> }
>
> static void raise_mmu_exception(CPURISCVState *env, target_ulong
> address,
> - MMUAccessType access_type, bool
> pmp_violation,
> + MMUAccessType access_type,
> + bool pmp_pma_violation,
> bool first_stage, bool two_stage,
> bool two_stage_indirect)
> {
> @@ -1676,7 +1692,7 @@ static void raise_mmu_exception(CPURISCVState
> *env, target_ulong address,
>
> switch (access_type) {
> case MMU_INST_FETCH:
> - if (pmp_violation) {
> + if (pmp_pma_violation) {
> cs->exception_index = RISCV_EXCP_INST_ACCESS_FAULT;
> } else if (env->virt_enabled && !first_stage) {
> cs->exception_index = RISCV_EXCP_INST_GUEST_PAGE_FAULT;
> @@ -1685,7 +1701,7 @@ static void raise_mmu_exception(CPURISCVState
> *env, target_ulong address,
> }
> break;
> case MMU_DATA_LOAD:
> - if (pmp_violation) {
> + if (pmp_pma_violation) {
> cs->exception_index = RISCV_EXCP_LOAD_ACCESS_FAULT;
> } else if (two_stage && !first_stage) {
> cs->exception_index =
> RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT;
> @@ -1694,7 +1710,7 @@ static void raise_mmu_exception(CPURISCVState
> *env, target_ulong address,
> }
> break;
> case MMU_DATA_STORE:
> - if (pmp_violation) {
> + if (pmp_pma_violation) {
> cs->exception_index = RISCV_EXCP_STORE_AMO_ACCESS_FAULT;
> } else if (two_stage && !first_stage) {
> cs->exception_index =
> RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT;
> @@ -1820,7 +1836,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr
> address, int size,
> vaddr im_address;
> hwaddr pa = 0;
> int prot, prot2, prot_pmp;
> - bool pmp_violation = false;
> + bool pmp_pma_violation = false;
> bool first_stage_error = true;
> bool two_stage_lookup = mmuidx_2stage(mmu_idx);
> bool two_stage_indirect_error = false;
> @@ -1921,8 +1937,8 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr
> address, int size,
> }
> }
>
> - if (ret == TRANSLATE_PMP_FAIL) {
> - pmp_violation = true;
> + if (ret == TRANSLATE_PMP_FAIL || ret == TRANSLATE_PMA_FAIL) {
> + pmp_pma_violation = true;
> }
>
> if (ret == TRANSLATE_SUCCESS) {
> @@ -1949,7 +1965,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr
> address, int size,
> cpu_check_watchpoint(cs, address, size,
> MEMTXATTRS_UNSPECIFIED,
> wp_access, retaddr);
>
> - raise_mmu_exception(env, address, access_type,
> pmp_violation,
> + raise_mmu_exception(env, address, access_type,
> pmp_pma_violation,
> first_stage_error, two_stage_lookup,
> two_stage_indirect_error);
> cpu_loop_exit_restore(cs, retaddr);