summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/riscv/riscv-iommu.c50
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 :