On Mon, 8 Jun 2026 at 10:47, Daniel Henrique Barboza
<[email protected]> wrote:
>
>
>
> 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.

But what's the "right exception" here? This is, I think, the code
for "the MMU is loading the page table entry from external memory".
I'm not clear what this has to do with permissions. Generally
"permissions" is a concept that applies to virtual addresses, i.e.
"does the page table for this vaddr say we can access it?", and there's
no concept of permissions for physical addresses. The exception to
that is that you might have something like Arm's "granule protection
table" which does apply permissions to physical accesses, so an
in-guest-memory data structure defines what physical addresses can be
accessed in particular ways. In QEMU we model this as effectively
another level of page tables, so again the check happens before we
do the address_space_ld* call. For risc-v a quick look at the architecture
spec suggests that the Smepmp extension is a bit like this (but
handled via registers rather than guest-memory data structures).

The other kind of check that might need to be done in the MMU before
the load call is "is this physaddr out of range?" (e.g. CPU has
48 bit physaddrs and the L1 PTE specifies a physaddr for the L2 PTE
that is outside the 48 bit range).

Do you have a reference in the architecture spec for the check that
you're trying to implement here ?

I would be a bit surprised if the riscv architecture mandated different
MMU errors for "page table entry load failed because the physaddr is not
pointing at any device" vs "page table entry load failed because the
physaddr points at a device and the device rejected it", because it's
really up to the bus specification how clearly it distinguishes these
various kinds of external abort (or even whether it distinguishes them
synchronously vs asynchronously, given the existence of caches), and
usually architecture specifications try not to impose too much on
the implementation.

thanks
-- PMM

Reply via email to