diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index 974042d017..7f94ce152e 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -282,6 +282,7 @@ static hwaddr riscv_iommu_napot_page_mask(hwaddr ppn, hwaddr addr, hwaddr *out) static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, IOMMUTLBEntry *iotlb) { + IOMMUAccessFlags pte_perm; dma_addr_t addr, base; uint64_t satp, gatp, pte; bool en_s, en_g; @@ -509,8 +510,16 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, } /* Translation phase completed (GPA or SPA) */ iotlb->translated_addr = base; - iotlb->perm = (pte & PTE_W) ? ((pte & PTE_R) ? IOMMU_RW : IOMMU_WO) - : IOMMU_RO; + + /* + * Do a bit_and between the PTE bits and the original + * request flags to determine the exact permission we + * need, i.e. if the original request is RO and the + * PTE has RW flags the actual perm is RO. + */ + pte_perm = (pte & PTE_W) ? ((pte & PTE_R) ? IOMMU_RW : IOMMU_WO) + : IOMMU_RO; + iotlb->perm &= pte_perm; /* Check MSI GPA address match */ if (pass == S_STAGE && (iotlb->perm & IOMMU_WO) && @@ -1727,7 +1736,8 @@ done: if (fault) { unsigned ttype = RISCV_IOMMU_FQ_TTYPE_PCIE_ATS_REQ; - if (iotlb->perm & IOMMU_RW) { + if ((iotlb->perm & IOMMU_RW) == IOMMU_RW + || iotlb->perm & IOMMU_WO) { ttype = RISCV_IOMMU_FQ_TTYPE_UADDR_WR; } else if (iotlb->perm & IOMMU_RO) { ttype = RISCV_IOMMU_FQ_TTYPE_UADDR_RD;