@samitouri / QOSamiQemu / commits / b18e3f0e2d

hw/riscv/riscv-iommu.c: fix fault type for spa_fetch() faults

Under certain circunstances, like the one described in [1] and [2], a read operation that faults will be logged as a write fault instead, and vice-versa, if they happen after the translation phase in riscv_iommu_spa_fetch(). The first problem is that we're overwriting iotlb->perm with PTE flags, so an IOMMU_RO access flag can be overwritten by whatever flags the PTE has. This will cause the wrong fault type to be thrown at the end of the function in case a fault happens. To solve the iotlb->perm overwrite we'll bit_and the original iotlb->perm access flags with the PTE access flags, preserving the original access type. So a IOMMU_RO access in a R+W PTE will result in a IOMMU_RO perm. Second, the resulting fault is received by riscv_iommu_translate(), which will then report the fault. To do that we require a transaction type (ttype). We're prioritizing checking "perm & IOMMU_RW" to set a UADDR_WR ttype, and then checking "perm & IOMMU_RO" to set UADDR_RD ttype. The issue with that is IOMMU_RO=1 and IOMMU_RW=3, thus checking "perm & IOMMU_RW" for a write then "perm & IOMMU_RO" for a read will cause the read fault to always be diagnosed as write. Make the iotlb->perm matches more strict: "perm & IOMMU_RW" must be exactly IOMMU_RW, ensuring that 'perm' has both flags. Then we can check perm & IOMMU_WO and perm & IOMMU_RO without worrying about overlapping with the RW flag. [1] https://gitlab.com/qemu-project/qemu/-/work_items/3557 [2] https://gitlab.com/qemu-project/qemu/-/work_items/3577 Fixes: 69a9ae4836 ("hw/riscv/riscv-iommu: add ATS support") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3557 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3577 Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260701124034.552271-1-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com>

Daniel Henrique Barboza committed Jul 1, 2026 at 09:40 UTC b18e3f0e2d0f301952ff3ae4cb73d0e9eb4ee697
1 file changed +13 -3
hw/riscv/riscv-iommu.c
+13 -3
@@ -282,6 +282,7 @@ static hwaddr riscv_iommu_napot_page_mask(hwaddr ppn, hwaddr addr, hwaddr *out)
282 static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx,
283 IOMMUTLBEntry *iotlb)
284 {
285 + IOMMUAccessFlags pte_perm;
286 dma_addr_t addr, base;
287 uint64_t satp, gatp, pte;
288 bool en_s, en_g;
@@ -509,8 +510,16 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx,
510 }
511 /* Translation phase completed (GPA or SPA) */
512 iotlb->translated_addr = base;
512 - iotlb->perm = (pte & PTE_W) ? ((pte & PTE_R) ? IOMMU_RW : IOMMU_WO)
513 - : IOMMU_RO;
513 +
514 + /*
515 + * Do a bit_and between the PTE bits and the original
516 + * request flags to determine the exact permission we
517 + * need, i.e. if the original request is RO and the
518 + * PTE has RW flags the actual perm is RO.
519 + */
520 + pte_perm = (pte & PTE_W) ? ((pte & PTE_R) ? IOMMU_RW : IOMMU_WO)
521 + : IOMMU_RO;
522 + iotlb->perm &= pte_perm;
523
524 /* Check MSI GPA address match */
525 if (pass == S_STAGE && (iotlb->perm & IOMMU_WO) &&
@@ -1727,7 +1736,8 @@ done:
1736 if (fault) {
1737 unsigned ttype = RISCV_IOMMU_FQ_TTYPE_PCIE_ATS_REQ;
1738
1730 - if (iotlb->perm & IOMMU_RW) {
1739 + if ((iotlb->perm & IOMMU_RW) == IOMMU_RW
1740 + || iotlb->perm & IOMMU_WO) {
1741 ttype = RISCV_IOMMU_FQ_TTYPE_UADDR_WR;
1742 } else if (iotlb->perm & IOMMU_RO) {
1743 ttype = RISCV_IOMMU_FQ_TTYPE_UADDR_RD;