On 6/8/2026 6:09 AM, Peter Maydell wrote:
On Tue, 2 Jun 2026 at 20:09, Daniel Henrique Barboza
<[email protected]> wrote:

Hello,

I decided to propose this change after working in a RISC-V gitlab bug
[1].  In my proposed fix [2] I had to make assumptions about
address_space_ld* error return, which is always MEMTX_DECODE_ERROR, to
identify that in that particular scenario this is a permission error.
In RISC-V (and from what I can see other archs like x86) an ACCESS_ERROR
due to mem page permission issues will fire a different fault than other
errors like page not found.

The core logic is on memory_region_access_valid(), which already makes a
distinction between error reasons when we look at the qemu_log_mask()
errors, e.g. this is being characterized as a 'rejected':

if (mr->ops->valid.accepts
         && !mr->ops->valid.accepts(mr->opaque, addr, size, is_write, attrs)) {
         qemu_log_mask(LOG_INVALID_MEM, "Invalid %s at addr 0x%" HWADDR_PRIX
                       ", size %u, region '%s', reason: rejected\n",
                       is_write ? "write" : "read",
                       addr, size, memory_region_name(mr));

valid.accepts returning false isn't a  permissions error, though.
It's for cases like "you tried to do a byte access to this device
that requires 32-bit accesses only". Permissions errors are entirely
in the MMU, and don't appear in the MemTx APIs, which are about
transactions in the fabric outside the MMU. The CPU emulation
catches permissions errors early before it even does the
address_space_ld* call.

What is the equivalent of this in the underlying hardware?
The current error codes are intended to roughly match the AXI
bus SLVERR and DECERR error responses (which are "we were
able to pass this memory transaction to a device, but it
rejected it" and "we weren't able to find the device for
this memory transaction at all").

The valid.accepts callback failing is always "we know the memory
region but it doesn't like this", which is true for all
memory_region_access_valid() failures, so maybe the problem
here is that we return DECODE_ERROR for that case when
we should be returning ACCESS_ERROR ?

This is the end result I'm going for.  In patch 1 I changed 
memory_region_access_valid()
to return ACCESS_ERROR when the valid.accepts fails, keeping all the
other exit code as DECODE_ERROR.  The ACCESS_ERROR will then be propagated
as an error in target/riscv/cpu_helper.c, get_physical_address():

        if (riscv_cpu_mxl(env) == MXL_RV32) {
            pte = be ? address_space_ldl_be(cs->as, pte_addr, attrs, &res)
                     : address_space_ldl_le(cs->as, pte_addr, attrs, &res);
        } else {
            pte = be ? address_space_ldq_be(cs->as, pte_addr, attrs, &res)
                     : address_space_ldq_le(cs->as, pte_addr, attrs, &res);
        }

        if (res != MEMTX_OK) {
            return TRANSLATE_FAIL;
        }

In this particular case 'res' will fail as ACCESS_ERROR if the pte does not have
the right permissions and we can throw the right exception.

Now, considering what you said:


"Permissions errors are entirely in the MMU, and don't appear in the MemTx APIs,
which are about transactions in the fabric outside the MMU. The CPU emulation
catches permissions errors early before it even does the address_space_ld* 
call."


I can say that this isn't happening in the RISC-V backend at least, so maybe the
core issue here is that RISC-V is skipping steps in address 
translation/validation.


Thanks,
Daniel


thanks
-- PMM


Reply via email to