@samitouri / QOSamiQemu / commits / 8e090ecfa9

target/riscv/cpu_helper.c: add PMA access fault

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 <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com> Message-ID: <20260522172502.320529-1-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com>

Daniel Henrique Barboza committed May 22, 2026 at 14:25 UTC 8e090ecfa9e5d9c225a9f6aae1e39586c182afa7
2 files changed +27 -10
target/riscv/cpu.h
+2 -1
@@ -137,7 +137,8 @@ enum {
137 TRANSLATE_SUCCESS,
138 TRANSLATE_FAIL,
139 TRANSLATE_PMP_FAIL,
140 - TRANSLATE_G_STAGE_FAIL
140 + TRANSLATE_G_STAGE_FAIL,
141 + TRANSLATE_PMA_FAIL,
142 };
143
144 /* Extension context status */
target/riscv/cpu_helper.c
+25 -9
@@ -1424,7 +1424,22 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical,
1424 }
1425
1426 if (res != MEMTX_OK) {
1427 - return TRANSLATE_FAIL;
1427 + /*
1428 + * The result of address_space_* APIs above does not take into
1429 + * consideration reject reads, putting all errors in the same
1430 + * cathegory (DECODE_ERROR), although there's a clear
1431 + * distinction between a rejected read versus other errors
1432 + * (see memory_region_dispatch_read() ->
1433 + * memory_region_access_valid()). This is something that
1434 + * we might have to deal with core QEMU logic some other
1435 + * day.
1436 + *
1437 + * For this particular error path, given that we made checks
1438 + * w.r.t legal PTE address before calling those APIs, we'll
1439 + * assume that anything != MEMTX_OK means a rejected read,
1440 + * i.e. a PMA error.
1441 + */
1442 + return TRANSLATE_PMA_FAIL;
1443 }
1444
1445 if (riscv_cpu_sxl(env) == MXL_RV32) {
@@ -1704,7 +1719,8 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical,
1719 }
1720
1721 static void raise_mmu_exception(CPURISCVState *env, target_ulong address,
1707 - MMUAccessType access_type, bool pmp_violation,
1722 + MMUAccessType access_type,
1723 + bool pmp_pma_violation,
1724 bool first_stage, bool two_stage,
1725 bool two_stage_indirect)
1726 {
@@ -1712,7 +1728,7 @@ static void raise_mmu_exception(CPURISCVState *env, target_ulong address,
1728
1729 switch (access_type) {
1730 case MMU_INST_FETCH:
1715 - if (pmp_violation) {
1731 + if (pmp_pma_violation) {
1732 cs->exception_index = RISCV_EXCP_INST_ACCESS_FAULT;
1733 } else if (env->virt_enabled && !first_stage) {
1734 cs->exception_index = RISCV_EXCP_INST_GUEST_PAGE_FAULT;
@@ -1721,7 +1737,7 @@ static void raise_mmu_exception(CPURISCVState *env, target_ulong address,
1737 }
1738 break;
1739 case MMU_DATA_LOAD:
1724 - if (pmp_violation) {
1740 + if (pmp_pma_violation) {
1741 cs->exception_index = RISCV_EXCP_LOAD_ACCESS_FAULT;
1742 } else if (two_stage && !first_stage) {
1743 cs->exception_index = RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT;
@@ -1730,7 +1746,7 @@ static void raise_mmu_exception(CPURISCVState *env, target_ulong address,
1746 }
1747 break;
1748 case MMU_DATA_STORE:
1733 - if (pmp_violation) {
1749 + if (pmp_pma_violation) {
1750 cs->exception_index = RISCV_EXCP_STORE_AMO_ACCESS_FAULT;
1751 } else if (two_stage && !first_stage) {
1752 cs->exception_index = RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT;
@@ -1856,7 +1872,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
1872 vaddr im_address;
1873 hwaddr pa = 0;
1874 int prot, prot2, prot_pmp;
1859 - bool pmp_violation = false;
1875 + bool pmp_pma_violation = false;
1876 bool first_stage_error = true;
1877 bool two_stage_lookup = mmuidx_2stage(mmu_idx);
1878 bool two_stage_indirect_error = false;
@@ -1956,8 +1972,8 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
1972 }
1973 }
1974
1959 - if (ret == TRANSLATE_PMP_FAIL) {
1960 - pmp_violation = true;
1975 + if (ret == TRANSLATE_PMP_FAIL || ret == TRANSLATE_PMA_FAIL) {
1976 + pmp_pma_violation = true;
1977 }
1978
1979 if (ret == TRANSLATE_SUCCESS) {
@@ -1984,7 +2000,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
2000 cpu_check_watchpoint(cs, address, size, MEMTXATTRS_UNSPECIFIED,
2001 wp_access, retaddr);
2002
1987 - raise_mmu_exception(env, address, access_type, pmp_violation,
2003 + raise_mmu_exception(env, address, access_type, pmp_pma_violation,
2004 first_stage_error, two_stage_lookup,
2005 two_stage_indirect_error);
2006 cpu_loop_exit_restore(cs, retaddr);