hw/riscv/riscv-iommu.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
All IOMMU accesses are assumed to be user mode unless told otherwise,
i.e. we have a process_id. In case we have a non-user mode leaf PTE
(PTE_U isn't set) and we are running in user mode, we need to throw a
fault.
Fixes: 0c54acb8243d ("hw/riscv: add RISC-V IOMMU base emulation")
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3553
Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
---
hw/riscv/riscv-iommu.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c
index c2c470b5df..2bea770fbc 100644
--- a/hw/riscv/riscv-iommu.c
+++ b/hw/riscv/riscv-iommu.c
@@ -297,6 +297,7 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx,
G_STAGE = 1,
} pass;
MemTxResult ret;
+ bool pv = !!ctx->process_id;
satp = get_field(ctx->satp, RISCV_IOMMU_ATP_MODE_FIELD);
gatp = get_field(ctx->gatp, RISCV_IOMMU_ATP_MODE_FIELD);
@@ -468,6 +469,15 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx,
if (!(pte & PTE_V)) {
break; /* Invalid PTE */
+ } 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.
+ */
+ return (iotlb->perm & IOMMU_WO)
+ ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT
+ : RISCV_IOMMU_FQ_CAUSE_RD_FAULT;
} 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) {
--
2.43.0
Hello,
Please disregard this patch.
First because we don't need to explicitly call the fault code like I'm doing
here - we can just add a 'break' and then we'll have the fault code doing the
work for us outside of the loop:
} while (1);
return (iotlb->perm & IOMMU_WO) ?
(pass ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT_VS :
RISCV_IOMMU_FQ_CAUSE_WR_FAULT_S) :
(pass ? RISCV_IOMMU_FQ_CAUSE_RD_FAULT_VS :
RISCV_IOMMU_FQ_CAUSE_RD_FAULT_S);
Second, I just found another bug in the same code area and the patches ended up
conflicting with one another. I'll send both together in a short series.
Thanks,
Daniel
On 6/29/2026 7:42 AM, Daniel Henrique Barboza wrote:
> All IOMMU accesses are assumed to be user mode unless told otherwise,
> i.e. we have a process_id. In case we have a non-user mode leaf PTE
> (PTE_U isn't set) and we are running in user mode, we need to throw a
> fault.
>
> Fixes: 0c54acb8243d ("hw/riscv: add RISC-V IOMMU base emulation")
> Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3553
> Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
> ---
> hw/riscv/riscv-iommu.c | 10 ++++++++++
> 1 file changed, 10 insertions(+)
>
> diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c
> index c2c470b5df..2bea770fbc 100644
> --- a/hw/riscv/riscv-iommu.c
> +++ b/hw/riscv/riscv-iommu.c
> @@ -297,6 +297,7 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx,
> G_STAGE = 1,
> } pass;
> MemTxResult ret;
> + bool pv = !!ctx->process_id;
>
> satp = get_field(ctx->satp, RISCV_IOMMU_ATP_MODE_FIELD);
> gatp = get_field(ctx->gatp, RISCV_IOMMU_ATP_MODE_FIELD);
> @@ -468,6 +469,15 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx,
>
> if (!(pte & PTE_V)) {
> break; /* Invalid PTE */
> + } 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.
> + */
> + return (iotlb->perm & IOMMU_WO)
> + ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT
> + : RISCV_IOMMU_FQ_CAUSE_RD_FAULT;
> } 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) {
© 2016 - 2026 Red Hat, Inc.