Hi Drew,

On Mon, May 11, 2026 at 2:07 PM Andrew Jones
<[email protected]> wrote:
>
> On Mon, May 11, 2026 at 10:20:32AM -0700, Tomasz Jeznach wrote:
> > Hi Drew,
> >
> > On Fri, May 8, 2026 at 2:29 PM Andrew Jones
> > <[email protected]> wrote:
> > >
> > > On Fri, May 08, 2026 at 03:51:29PM -0500, Andrew Jones wrote:
> > > > The Svnapot extension encodes a 64KB leaf PTE by setting PTE_N and
> > > > storing bits [3:0] of the PPN as a NAPOT size indicator. The IOMMU
> > > > model wasn't checking PTE_N and therefore was using the raw (NAPOT-
> > > > encoded) PPN directly in the physical address, yielding an address
> > > > 32 KB above the correct base.
> > > >
> > > > Fix both riscv_iommu_spa_fetch() and pdt_memory_read() by mirroring
> > > > the Svnapot handling already present in target/riscv/cpu_helper.c:
> > > >
> > > >   napot_bits  = ctz64(ppn) + 1          /* 4 for 64KB */
> > > >   napot_mask  = (1 << napot_bits) - 1   /* 0xF */
> > > >   phys_base   = PPN_PHYS(ppn & ~napot_mask)
> > > >   page_offset = addr & (PPN_PHYS(napot_mask) | (TARGET_PAGE_SIZE - 1))
> > > >
> > > > The spec only defines napot_bits == 4 (64KB); any other value is
> > > > treated as a reserved encoding.
> > > >
> > > > This is a fix, rather than new feature support, because the spec
> > > > says "IOMMU implementations must support the Svnapot standard
> > > > extension for NAPOT Translation Contiguity."
> > > >
> > > > Fixes: 0c54acb8243d ("hw/riscv: add RISC-V IOMMU base emulation")
> > > > Signed-off-by: Andrew Jones <[email protected]>
> > >
> > > I should have added
> > >
> > > Cc: [email protected]
> > >
> > > because as soon as [1] is merged into Linux Linux will no longer boot
> > > on any QEMU build without this patch.
> > >
> > > [1] 
> > > https://lore.kernel.org/all/[email protected]/
> > >
> > > Thanks,
> > > drew
> > >
> > > > ---
> > > >  hw/riscv/riscv-iommu.c | 44 ++++++++++++++++++++++++++++++++++++++----
> > > >  1 file changed, 40 insertions(+), 4 deletions(-)
> > > >
> > > > diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c
> > > > index 25a356d1d366..eb09cc974812 100644
> > > > --- a/hw/riscv/riscv-iommu.c
> > > > +++ b/hw/riscv/riscv-iommu.c
> > > > @@ -237,6 +237,25 @@ static bool riscv_iommu_msi_check(RISCVIOMMUState 
> > > > *s, RISCVIOMMUContext *ctx,
> > > >      return true;
> > > >  }
> > > >
> > > > +/* Returns the NAPOT page mask, or 0 for reserved encodings. */
> > > > +static hwaddr riscv_iommu_napot_page_mask(hwaddr ppn, hwaddr addr, 
> > > > hwaddr *out)
> > > > +{
> > > > +    int napot_bits = ctz64(ppn) + 1;
> > > > +    hwaddr napot_mask, page_mask;
> > > > +
> > > > +    /* The spec only defines 64KB (napot_bits == 4) */
> > > > +    if (napot_bits != 4) {
> > > > +        return 0;
> > > > +    }
> > > > +
> > > > +    napot_mask = (1ULL << napot_bits) - 1;
> > > > +    page_mask = PPN_PHYS(napot_mask) | (TARGET_PAGE_SIZE - 1);
> > > > +
> > > > +    *out = PPN_PHYS(ppn & ~napot_mask) | (addr & page_mask);
> > > > +
> > > > +    return page_mask;
> > > > +}
> > > > +
> > > >  /*
> > > >   * RISCV IOMMU Address Translation Lookup - Page Table Walk
> > > >   *
> > > > @@ -458,9 +477,20 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState 
> > > > *s, RISCVIOMMUContext *ctx,
> > > >          } else {
> > > >              /* Leaf PTE, translation completed. */
> > > >              sc[pass].step = sc[pass].levels;
> > > > -            base = PPN_PHYS(ppn) | (addr & ((1ULL << va_skip) - 1));
> > > > -            /* Update address mask based on smallest translation 
> > > > granularity */
> > > > -            iotlb->addr_mask &= (1ULL << va_skip) - 1;
> > > > +
> > > > +            if (pte & PTE_N) {
> >
> > Would it be worth checking PTE_N only on the last level page table
> > entry (sc[pass].step == sc[pass].levels at the start of the block)?
> >
>
> Hi Tomasz,
>
> I don't think we need it since we can't get here with non-zero napot bits
> on a superpage due to the "Misaligned PPN" check above. And, if we get
> past that and PTE_N is erroneously set, then we'll break from the loop
> when riscv_iommu_napot_page_mask() returns zero, allowing us to fault on
> the bad napot entry.
>
> Thanks,
> drew

Good point.

Reviewed-by: Tomasz Jeznach <[email protected]>

Thanks,
- Tomasz

Reply via email to