diff options
| -rw-r--r-- | hw/riscv/riscv-iommu.c | 50 |
1 files changed, 29 insertions, 21 deletions
diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index ba77f4ce47..fd11558072 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -281,7 +281,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; + IOMMUAccessFlags trans_perm = IOMMU_NONE; dma_addr_t addr, base; uint64_t satp, gatp, pte; bool en_s, en_g; @@ -298,6 +298,14 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, } pass; MemTxResult ret; bool pv = !!ctx->process_id; + /* + * Keep the request permission separate from iotlb->perm. G-stage + * walks translate S-stage PTE addresses before the real leaf is + * reached, but permission checks and fault types must still use the + * original request. A successful walk leaves iotlb->perm with the + * effective leaf permission for the translation cache. + */ + const IOMMUAccessFlags req_perm = iotlb->perm; satp = get_field(ctx->satp, RISCV_IOMMU_ATP_MODE_FIELD); gatp = get_field(ctx->gatp, RISCV_IOMMU_ATP_MODE_FIELD); @@ -316,7 +324,7 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, * means we can't do an early MSI check unless we have * strictly !en_s. */ - if (!en_s && (iotlb->perm & IOMMU_WO) && + if (!en_s && (req_perm & IOMMU_WO) && riscv_iommu_msi_check(s, ctx, iotlb->iova)) { iotlb->target_as = &s->trap_as; iotlb->translated_addr = iotlb->iova; @@ -434,13 +442,13 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, masked_msbs = (addr >> (va_len - 1)) & mask; if (masked_msbs != 0 && masked_msbs != mask) { - return (iotlb->perm & IOMMU_WO) ? + return (req_perm & IOMMU_WO) ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT_S : RISCV_IOMMU_FQ_CAUSE_RD_FAULT_S; } } else { if ((addr & va_mask) != addr) { - return (iotlb->perm & IOMMU_WO) ? + return (req_perm & IOMMU_WO) ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT_VS : RISCV_IOMMU_FQ_CAUSE_RD_FAULT_VS; } @@ -465,8 +473,8 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, MEMTXATTRS_UNSPECIFIED); } if (ret != MEMTX_OK) { - return (iotlb->perm & IOMMU_WO) ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT - : RISCV_IOMMU_FQ_CAUSE_RD_FAULT; + return (req_perm & IOMMU_WO) ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT + : RISCV_IOMMU_FQ_CAUSE_RD_FAULT; } sc[pass].step++; @@ -491,13 +499,13 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, * non-user mode leaf PTE and !pv we need to fault. */ break; - } else if ((iotlb->perm & IOMMU_RO) && !(pte & PTE_R)) { + } else if ((req_perm & IOMMU_RO) && !(pte & PTE_R)) { break; /* Read access check failed */ - } else if ((iotlb->perm & IOMMU_WO) && !(pte & PTE_W)) { + } else if ((req_perm & IOMMU_WO) && !(pte & PTE_W)) { break; /* Write access check failed */ } else if (!ade && !(pte & PTE_A)) { break; /* Access bit not set */ - } else if ((iotlb->perm & IOMMU_WO) && !ade && !(pte & PTE_D)) { + } else if ((req_perm & IOMMU_WO) && !ade && !(pte & PTE_D)) { break; /* Dirty bit not set */ } else if (pass == G_STAGE && !(pte & PTE_U)) { /* @@ -532,21 +540,20 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, addr = iotlb->iova; continue; } + + /* Cache the effective permission, not this request's subset. */ + IOMMUAccessFlags leaf_perm = (pte & PTE_W) ? + ((pte & PTE_R) ? IOMMU_RW : IOMMU_WO) : + IOMMU_RO; + + trans_perm = trans_perm == IOMMU_NONE ? + leaf_perm : trans_perm & leaf_perm; + /* Translation phase completed (GPA or SPA) */ iotlb->translated_addr = base; - /* - * 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) && + if (pass == S_STAGE && (req_perm & IOMMU_WO) && riscv_iommu_msi_check(s, ctx, base)) { /* Trap MSI writes and return GPA address. */ iotlb->target_as = &s->trap_as; @@ -563,6 +570,7 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, continue; } + iotlb->perm = trans_perm; return 0; } @@ -587,7 +595,7 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, */ iotlb->translated_addr = addr; - return (iotlb->perm & IOMMU_WO) ? + return (req_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 : |
