diff options
| author | Andrew Jones <andrew.jones@oss.qualcomm.com> | 2026-07-17 13:23:40 +0200 |
|---|---|---|
| committer | Michael Tokarev <mjt@tls.msk.ru> | 2026-08-25 10:27:16 +0300 |
| commit | a9200e29e3839f947853837ae521f950d106b185 (patch) | |
| tree | cbe6c9817d2d8068fafc3fd42b833d5fb5e8b768 | |
| parent | 3ba73ef1296b6d8662ecd4b170fa7cc666d9483d (diff) | |
| download | qemu-a9200e29e3839f947853837ae521f950d106b185.tar.gz qemu-a9200e29e3839f947853837ae521f950d106b185.zip | |
hw/riscv/riscv-iommu: fix U-bit check to apply only to leaf S/VS-stage PTEs
Commit b795ea0ba471 ("hw/riscv/riscv-iommu.c: fault when !PTE_U and
no priv access") placed its check ahead of the leaf-vs-non-leaf
branch in riscv_iommu_spa_fetch(), so it fires on every PTE walked,
including non-leaf/table entries. Per the RISC-V privileged spec's
address translation algorithm (Sv39/Sv48/etc., the "leaf PTE has
been reached" step, followed separately by the U-bit permission
check), the U bit is only defined and checked for the leaf PTE
reached at the end of the walk -- non-leaf PTEs don't carry a
meaningful U bit at all.
Move the check after the leaf/non-leaf branch, alongside the other
leaf-only checks, mirroring how the G_STAGE U-bit check (added in
9158c900ab30) is already correctly placed.
Fixes: b795ea0ba471 ("hw/riscv/riscv-iommu.c: fault when !PTE_U and no priv access")
Fixes: ca4a88e4ae1e in 11.0.x
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
Reviewed-by: Nutty Liu <nutty.liu@hotmail.com>
Message-ID: <20260717112340.1071148-1-andrew.jones@oss.qualcomm.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
(cherry picked from commit f16fd27b3fe3c305d95c61d3516e458c45c6e8c9)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
| -rw-r--r-- | hw/riscv/riscv-iommu.c | 14 |
1 files changed, 7 insertions, 7 deletions
diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index c59e5d40e5..ba77f4ce47 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -476,13 +476,6 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, break; /* Invalid PTE */ } else if (pte & PTE_RESERVED(false)) { break; /* Reserved PTE bits set */ - } else if (!(pte & PTE_U) && !pv) { - /* - * All accesses are assumed to be User mode unless - * process_id is valid (pv). In case we have a - * non-user mode PTE and !pv we need to fault. - */ - break; } else if (!(pte & (PTE_R | PTE_W | PTE_X))) { base = PPN_PHYS(ppn); /* Inner PTE, continue walking */ } else if ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) { @@ -491,6 +484,13 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, break; /* Reserved leaf PTE flags: PTE_W + PTE_X */ } else if (ppn & ((1ULL << (va_skip - TARGET_PAGE_BITS)) - 1)) { break; /* Misaligned PPN */ + } else if (!(pte & PTE_U) && !pv) { + /* + * All accesses are assumed to be User mode unless + * process_id is valid (pv). In case we have a + * non-user mode leaf PTE and !pv we need to fault. + */ + break; } else if ((iotlb->perm & IOMMU_RO) && !(pte & PTE_R)) { break; /* Read access check failed */ } else if ((iotlb->perm & IOMMU_WO) && !(pte & PTE_W)) { |
