[PATCH] target/riscv/cpu_helper.c: add PMA access fault

Daniel Henrique Barboza posted 1 patch 1 week ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20260522172502.320529-1-daniel.barboza@oss.qualcomm.com
Maintainers: Palmer Dabbelt <palmer@dabbelt.com>, Alistair Francis <alistair.francis@wdc.com>, Weiwei Li <liwei1518@gmail.com>, Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>, Liu Zhiwei <zhiwei_liu@linux.alibaba.com>, Chao Liu <chao.liu.zevorn@gmail.com>
target/riscv/cpu.h        |  3 ++-
target/riscv/cpu_helper.c | 34 +++++++++++++++++++++++++---------
2 files changed, 27 insertions(+), 10 deletions(-)
[PATCH] target/riscv/cpu_helper.c: add PMA access fault
Posted by Daniel Henrique Barboza 1 week ago
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>
---
 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);
-- 
2.43.0


Re: [PATCH] target/riscv/cpu_helper.c: add PMA access fault
Posted by Alistair Francis 2 days, 15 hours ago
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
> <daniel.barboza@oss.qualcomm.com>

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);
Re: [PATCH] target/riscv/cpu_helper.c: add PMA access fault
Posted by Alistair Francis 3 days, 14 hours ago
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
> <daniel.barboza@oss.qualcomm.com>

Reviewed-by: Alistair Francis <alistair.francis@wdc.com>

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);
Re: [PATCH] target/riscv/cpu_helper.c: add PMA access fault
Posted by Chao Liu 5 days, 12 hours ago
On Fri, May 22, 2026 at 02:25:02PM +0800, 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 <daniel.barboza@oss.qualcomm.com>
Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com>

> ---
>  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);
> -- 
> 2.43.0
>