diff --git a/hw/riscv/riscv-iommu-bits.h b/hw/riscv/riscv-iommu-bits.h index 47fe01bee5869..2bd30221505d1 100644 --- a/hw/riscv/riscv-iommu-bits.h +++ b/hw/riscv/riscv-iommu-bits.h @@ -96,6 +96,8 @@ struct riscv_iommu_pq_record { #define RISCV_IOMMU_CAP_PD17 BIT_ULL(39) #define RISCV_IOMMU_CAP_PD20 BIT_ULL(40) +#define RISCV_IOMMU_CAP_GIPC BIT_ULL(56) + enum riscv_iommu_igs_modes { RISCV_IOMMU_CAP_IGS_MSI = 0, RISCV_IOMMU_CAP_IGS_WSI, @@ -302,6 +304,8 @@ struct riscv_iommu_dc { #define RISCV_IOMMU_DC_TC_SBE BIT_ULL(10) #define RISCV_IOMMU_DC_TC_SXL BIT_ULL(11) +#define RISCV_IOMMU_DC_TC_GIPC BIT_ULL(24) + /* Second-stage (aka G-stage) context fields */ #define RISCV_IOMMU_DC_IOHGATP_PPN RISCV_IOMMU_ATP_PPN_FIELD #define RISCV_IOMMU_DC_IOHGATP_GSCID GENMASK_ULL(59, 44) diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index 96a7fbdefcf3b..763c2aa66d9e3 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -866,6 +866,149 @@ static bool riscv_iommu_validate_process_ctx(RISCVIOMMUState *s, return true; } +/** + * pdt_memory_read: PDT wrapper of dma_memory_read. + * + * @s: IOMMU Device State + * @ctx: Device Translation Context with devid and pasid set + * @addr: address within that address space + * @buf: buffer with the data transferred + * @len: length of the data transferred + * @attrs: memory transaction attributes + */ +static MemTxResult pdt_memory_read(RISCVIOMMUState *s, + RISCVIOMMUContext *ctx, + dma_addr_t addr, + void *buf, dma_addr_t len, + MemTxAttrs attrs) +{ + uint64_t gatp_mode, pte; + struct { + unsigned char step; + unsigned char levels; + unsigned char ptidxbits; + unsigned char ptesize; + } sc; + MemTxResult ret; + dma_addr_t base = addr; + + /* GIPC detect */ + if ((ctx->tc & RISCV_IOMMU_DC_TC_GIPC) && (s->cap & RISCV_IOMMU_CAP_GIPC)) { + goto out; + } + + /* G stages translation mode */ + gatp_mode = get_field(ctx->gatp, RISCV_IOMMU_ATP_MODE_FIELD); + if (gatp_mode == RISCV_IOMMU_DC_IOHGATP_MODE_BARE) { + goto out; + } + + /* G stages translation tables root pointer */ + base = PPN_PHYS(get_field(ctx->gatp, RISCV_IOMMU_ATP_PPN_FIELD)); + + /* Start at step 0 */ + sc.step = 0; + + if (s->fctl & RISCV_IOMMU_FCTL_GXL) { + /* 32bit mode for GXL == 1 */ + switch (gatp_mode) { + case RISCV_IOMMU_DC_IOHGATP_MODE_SV32X4: + if (!(s->cap & RISCV_IOMMU_CAP_SV32X4)) { + return MEMTX_ACCESS_ERROR; + } + sc.levels = 2; + sc.ptidxbits = 10; + sc.ptesize = 4; + break; + default: + return MEMTX_ACCESS_ERROR; + } + } else { + /* 64bit mode for GXL == 0 */ + switch (gatp_mode) { + case RISCV_IOMMU_DC_IOHGATP_MODE_SV39X4: + if (!(s->cap & RISCV_IOMMU_CAP_SV39X4)) { + return MEMTX_ACCESS_ERROR; + } + sc.levels = 3; + sc.ptidxbits = 9; + sc.ptesize = 8; + break; + case RISCV_IOMMU_DC_IOHGATP_MODE_SV48X4: + if (!(s->cap & RISCV_IOMMU_CAP_SV48X4)) { + return MEMTX_ACCESS_ERROR; + } + sc.levels = 4; + sc.ptidxbits = 9; + sc.ptesize = 8; + break; + case RISCV_IOMMU_DC_IOHGATP_MODE_SV57X4: + if (!(s->cap & RISCV_IOMMU_CAP_SV57X4)) { + return MEMTX_ACCESS_ERROR; + } + sc.levels = 5; + sc.ptidxbits = 9; + sc.ptesize = 8; + break; + default: + return MEMTX_ACCESS_ERROR; + } + } + + do { + const unsigned va_bits = (sc.step ? 0 : 2) + sc.ptidxbits; + const unsigned va_skip = TARGET_PAGE_BITS + sc.ptidxbits * + (sc.levels - 1 - sc.step); + const unsigned idx = (addr >> va_skip) & ((1 << va_bits) - 1); + const dma_addr_t pte_addr = base + idx * sc.ptesize; + + /* Address range check before first level lookup */ + if (!sc.step) { + const uint64_t va_mask = (1ULL << (va_skip + va_bits)) - 1; + if ((addr & va_mask) != addr) { + return MEMTX_ACCESS_ERROR; + } + } + + /* Read page table entry */ + if (sc.ptesize == 4) { + uint32_t pte32 = 0; + ret = ldl_le_dma(s->target_as, pte_addr, &pte32, attrs); + pte = pte32; + } else { + ret = ldq_le_dma(s->target_as, pte_addr, &pte, attrs); + } + if (ret != MEMTX_OK) + return ret; + + sc.step++; + hwaddr ppn = pte >> PTE_PPN_SHIFT; + + if (!(pte & PTE_V)) { + return MEMTX_ACCESS_ERROR; /* Invalid PTE */ + } 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) { + return MEMTX_ACCESS_ERROR; /* Reserved leaf PTE flags: PTE_W */ + } else if ((pte & (PTE_R | PTE_W | PTE_X)) == (PTE_W | PTE_X)) { + return MEMTX_ACCESS_ERROR; /* Reserved leaf PTE flags: PTE_W + PTE_X */ + } else if (ppn & ((1ULL << (va_skip - TARGET_PAGE_BITS)) - 1)) { + return MEMTX_ACCESS_ERROR; /* Misaligned PPN */ + } else { + /* Leaf PTE, translation completed. */ + base = PPN_PHYS(ppn) | (addr & ((1ULL << va_skip) - 1)); + break; + } + + if (sc.step == sc.levels) { + return MEMTX_ACCESS_ERROR; /* Can't find leaf PTE */ + } + } while (1); + +out: + return dma_memory_read(s->target_as, base, buf, len, attrs); +} + /* * RISC-V IOMMU Device Context Loopkup - Device Directory Tree Walk * @@ -884,6 +1027,7 @@ static int riscv_iommu_ctx_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx) const size_t dc_len = sizeof(dc) >> dc_fmt; int depth; uint64_t de; + bool gipc = false; switch (mode) { case RISCV_IOMMU_DDTP_MODE_OFF: @@ -1029,6 +1173,10 @@ static int riscv_iommu_ctx_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx) return RISCV_IOMMU_FQ_CAUSE_PDT_MISCONFIGURED; } + if ((ctx->tc & RISCV_IOMMU_DC_TC_GIPC) && (s->cap & RISCV_IOMMU_CAP_GIPC)) { + gipc = true; + } + for (depth = mode - RISCV_IOMMU_DC_FSC_PDTP_MODE_PD8; depth-- > 0; ) { riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_PD_WALK); @@ -1036,9 +1184,9 @@ static int riscv_iommu_ctx_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx) * Select process id index bits based on process directory tree * level. See IOMMU Specification, 2.2. Process-Directory-Table. */ - const int split = depth * 9 + 8; + const int split = depth * 9 + (gipc ? 7 : 8); addr |= ((ctx->process_id >> split) << 3) & ~TARGET_PAGE_MASK; - if (dma_memory_read(s->target_as, addr, &de, sizeof(de), + if (pdt_memory_read(s, ctx, addr, &de, sizeof(de), MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { return RISCV_IOMMU_FQ_CAUSE_PDT_LOAD_FAULT; } @@ -1052,8 +1200,9 @@ static int riscv_iommu_ctx_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx) riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_PD_WALK); /* Leaf entry in PDT */ - addr |= (ctx->process_id << 4) & ~TARGET_PAGE_MASK; - if (dma_memory_read(s->target_as, addr, &dc.ta, sizeof(uint64_t) * 2, + addr |= (ctx->process_id << (gipc ? 5 : 4)) & ~TARGET_PAGE_MASK; + if (pdt_memory_read(s, ctx, addr, &dc.ta, + gipc ? sizeof(uint64_t) * 4 : sizeof(uint64_t) * 2, MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { return RISCV_IOMMU_FQ_CAUSE_PDT_LOAD_FAULT; } @@ -1061,6 +1210,9 @@ static int riscv_iommu_ctx_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx) /* Use FSC and TA from process directory entry. */ ctx->ta = le64_to_cpu(dc.ta); ctx->satp = le64_to_cpu(dc.fsc); + if (gipc) { + ctx->gatp = le64_to_cpu(dc.msiptp); + } if (!(ctx->ta & RISCV_IOMMU_PC_TA_V)) { return RISCV_IOMMU_FQ_CAUSE_PDT_INVALID; @@ -2354,6 +2506,9 @@ static void riscv_iommu_realize(DeviceState *dev, Error **errp) RISCV_IOMMU_CAP_SV48X4 | RISCV_IOMMU_CAP_SV57X4 | RISCV_IOMMU_CAP_SVRSW60T59B; } + if (s->enable_gipc) { + s->cap |= RISCV_IOMMU_CAP_GIPC; + } if (s->hpm_cntrs > 0) { /* Clip number of HPM counters to maximum supported (31). */ @@ -2508,6 +2663,7 @@ static const Property riscv_iommu_properties[] = { TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_UINT8("hpm-counters", RISCVIOMMUState, hpm_cntrs, RISCV_IOMMU_IOCOUNT_NUM), + DEFINE_PROP_BOOL("gipc", RISCVIOMMUState, enable_gipc, TRUE), }; static void riscv_iommu_class_init(ObjectClass *klass, const void *data) diff --git a/hw/riscv/riscv-iommu.h b/hw/riscv/riscv-iommu.h index a31aa62144f4b..51a6d31259efa 100644 --- a/hw/riscv/riscv-iommu.h +++ b/hw/riscv/riscv-iommu.h @@ -45,6 +45,7 @@ struct RISCVIOMMUState { bool enable_ats; /* Enable ATS support */ bool enable_s_stage; /* Enable S/VS-Stage translation */ bool enable_g_stage; /* Enable G-Stage translation */ + bool enable_gipc; /* Enable G-Stage table in Process Context */ /* IOMMU Internal State */ uint64_t ddtp; /* Validated Device Directory Tree Root Pointer */