10c54acb8STomasz Jeznach /* 20c54acb8STomasz Jeznach * QEMU emulation of an RISC-V IOMMU 30c54acb8STomasz Jeznach * 40c54acb8STomasz Jeznach * Copyright (C) 2021-2023, Rivos Inc. 50c54acb8STomasz Jeznach * 60c54acb8STomasz Jeznach * This program is free software; you can redistribute it and/or modify it 70c54acb8STomasz Jeznach * under the terms and conditions of the GNU General Public License, 80c54acb8STomasz Jeznach * version 2 or later, as published by the Free Software Foundation. 90c54acb8STomasz Jeznach * 100c54acb8STomasz Jeznach * This program is distributed in the hope that it will be useful, 110c54acb8STomasz Jeznach * but WITHOUT ANY WARRANTY; without even the implied warranty of 120c54acb8STomasz Jeznach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 130c54acb8STomasz Jeznach * GNU General Public License for more details. 140c54acb8STomasz Jeznach * 150c54acb8STomasz Jeznach * You should have received a copy of the GNU General Public License along 160c54acb8STomasz Jeznach * with this program; if not, see <http://www.gnu.org/licenses/>. 170c54acb8STomasz Jeznach */ 180c54acb8STomasz Jeznach 190c54acb8STomasz Jeznach #include "qemu/osdep.h" 200c54acb8STomasz Jeznach #include "qom/object.h" 210c54acb8STomasz Jeznach #include "hw/pci/pci_bus.h" 220c54acb8STomasz Jeznach #include "hw/pci/pci_device.h" 230c54acb8STomasz Jeznach #include "hw/qdev-properties.h" 240c54acb8STomasz Jeznach #include "hw/riscv/riscv_hart.h" 250c54acb8STomasz Jeznach #include "migration/vmstate.h" 260c54acb8STomasz Jeznach #include "qapi/error.h" 270c54acb8STomasz Jeznach #include "qemu/timer.h" 280c54acb8STomasz Jeznach 290c54acb8STomasz Jeznach #include "cpu_bits.h" 300c54acb8STomasz Jeznach #include "riscv-iommu.h" 310c54acb8STomasz Jeznach #include "riscv-iommu-bits.h" 324faea7e0STomasz Jeznach #include "riscv-iommu-hpm.h" 330c54acb8STomasz Jeznach #include "trace.h" 340c54acb8STomasz Jeznach 350c54acb8STomasz Jeznach #define LIMIT_CACHE_CTX (1U << 7) 360c54acb8STomasz Jeznach #define LIMIT_CACHE_IOT (1U << 20) 370c54acb8STomasz Jeznach 380c54acb8STomasz Jeznach /* Physical page number coversions */ 390c54acb8STomasz Jeznach #define PPN_PHYS(ppn) ((ppn) << TARGET_PAGE_BITS) 400c54acb8STomasz Jeznach #define PPN_DOWN(phy) ((phy) >> TARGET_PAGE_BITS) 410c54acb8STomasz Jeznach 420c54acb8STomasz Jeznach typedef struct RISCVIOMMUEntry RISCVIOMMUEntry; 430c54acb8STomasz Jeznach 440c54acb8STomasz Jeznach /* Device assigned I/O address space */ 450c54acb8STomasz Jeznach struct RISCVIOMMUSpace { 460c54acb8STomasz Jeznach IOMMUMemoryRegion iova_mr; /* IOVA memory region for attached device */ 470c54acb8STomasz Jeznach AddressSpace iova_as; /* IOVA address space for attached device */ 480c54acb8STomasz Jeznach RISCVIOMMUState *iommu; /* Managing IOMMU device state */ 490c54acb8STomasz Jeznach uint32_t devid; /* Requester identifier, AKA device_id */ 500c54acb8STomasz Jeznach bool notifier; /* IOMMU unmap notifier enabled */ 510c54acb8STomasz Jeznach QLIST_ENTRY(RISCVIOMMUSpace) list; 520c54acb8STomasz Jeznach }; 530c54acb8STomasz Jeznach 54fa622855SJason Chien typedef enum RISCVIOMMUTransTag { 55fa622855SJason Chien RISCV_IOMMU_TRANS_TAG_BY, /* Bypass */ 56fa622855SJason Chien RISCV_IOMMU_TRANS_TAG_SS, /* Single Stage */ 57fa622855SJason Chien RISCV_IOMMU_TRANS_TAG_VG, /* G-stage only */ 58fa622855SJason Chien RISCV_IOMMU_TRANS_TAG_VN, /* Nested translation */ 59fa622855SJason Chien } RISCVIOMMUTransTag; 60fa622855SJason Chien 619d085a1cSTomasz Jeznach /* Address translation cache entry */ 629d085a1cSTomasz Jeznach struct RISCVIOMMUEntry { 63fa622855SJason Chien RISCVIOMMUTransTag tag; /* Translation Tag */ 649d085a1cSTomasz Jeznach uint64_t iova:44; /* IOVA Page Number */ 659d085a1cSTomasz Jeznach uint64_t pscid:20; /* Process Soft-Context identifier */ 669d085a1cSTomasz Jeznach uint64_t phys:44; /* Physical Page Number */ 679d085a1cSTomasz Jeznach uint64_t gscid:16; /* Guest Soft-Context identifier */ 689d085a1cSTomasz Jeznach uint64_t perm:2; /* IOMMU_RW flags */ 699d085a1cSTomasz Jeznach }; 709d085a1cSTomasz Jeznach 710c54acb8STomasz Jeznach /* IOMMU index for transactions without process_id specified. */ 720c54acb8STomasz Jeznach #define RISCV_IOMMU_NOPROCID 0 730c54acb8STomasz Jeznach 740c54acb8STomasz Jeznach static uint8_t riscv_iommu_get_icvec_vector(uint32_t icvec, uint32_t vec_type) 750c54acb8STomasz Jeznach { 760c54acb8STomasz Jeznach switch (vec_type) { 770c54acb8STomasz Jeznach case RISCV_IOMMU_INTR_CQ: 780c54acb8STomasz Jeznach return icvec & RISCV_IOMMU_ICVEC_CIV; 790c54acb8STomasz Jeznach case RISCV_IOMMU_INTR_FQ: 800c54acb8STomasz Jeznach return (icvec & RISCV_IOMMU_ICVEC_FIV) >> 4; 810c54acb8STomasz Jeznach case RISCV_IOMMU_INTR_PM: 820c54acb8STomasz Jeznach return (icvec & RISCV_IOMMU_ICVEC_PMIV) >> 8; 830c54acb8STomasz Jeznach case RISCV_IOMMU_INTR_PQ: 840c54acb8STomasz Jeznach return (icvec & RISCV_IOMMU_ICVEC_PIV) >> 12; 850c54acb8STomasz Jeznach default: 860c54acb8STomasz Jeznach g_assert_not_reached(); 870c54acb8STomasz Jeznach } 880c54acb8STomasz Jeznach } 890c54acb8STomasz Jeznach 9011ecf24cSTomasz Jeznach void riscv_iommu_notify(RISCVIOMMUState *s, int vec_type) 910c54acb8STomasz Jeznach { 920c54acb8STomasz Jeznach uint32_t ipsr, icvec, vector; 930c54acb8STomasz Jeznach 945b128435STomasz Jeznach if (!s->notify) { 950c54acb8STomasz Jeznach return; 960c54acb8STomasz Jeznach } 970c54acb8STomasz Jeznach 980c54acb8STomasz Jeznach icvec = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_ICVEC); 990c54acb8STomasz Jeznach ipsr = riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_IPSR, (1 << vec_type), 0); 1000c54acb8STomasz Jeznach 1010c54acb8STomasz Jeznach if (!(ipsr & (1 << vec_type))) { 1020c54acb8STomasz Jeznach vector = riscv_iommu_get_icvec_vector(icvec, vec_type); 1030c54acb8STomasz Jeznach s->notify(s, vector); 1040c54acb8STomasz Jeznach trace_riscv_iommu_notify_int_vector(vec_type, vector); 1050c54acb8STomasz Jeznach } 1060c54acb8STomasz Jeznach } 1070c54acb8STomasz Jeznach 1080c54acb8STomasz Jeznach static void riscv_iommu_fault(RISCVIOMMUState *s, 1090c54acb8STomasz Jeznach struct riscv_iommu_fq_record *ev) 1100c54acb8STomasz Jeznach { 1110c54acb8STomasz Jeznach uint32_t ctrl = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQCSR); 1120c54acb8STomasz Jeznach uint32_t head = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQH) & s->fq_mask; 1130c54acb8STomasz Jeznach uint32_t tail = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQT) & s->fq_mask; 1140c54acb8STomasz Jeznach uint32_t next = (tail + 1) & s->fq_mask; 1150c54acb8STomasz Jeznach uint32_t devid = get_field(ev->hdr, RISCV_IOMMU_FQ_HDR_DID); 1160c54acb8STomasz Jeznach 1170c54acb8STomasz Jeznach trace_riscv_iommu_flt(s->parent_obj.id, PCI_BUS_NUM(devid), PCI_SLOT(devid), 1180c54acb8STomasz Jeznach PCI_FUNC(devid), ev->hdr, ev->iotval); 1190c54acb8STomasz Jeznach 1200c54acb8STomasz Jeznach if (!(ctrl & RISCV_IOMMU_FQCSR_FQON) || 1210c54acb8STomasz Jeznach !!(ctrl & (RISCV_IOMMU_FQCSR_FQOF | RISCV_IOMMU_FQCSR_FQMF))) { 1220c54acb8STomasz Jeznach return; 1230c54acb8STomasz Jeznach } 1240c54acb8STomasz Jeznach 1250c54acb8STomasz Jeznach if (head == next) { 1260c54acb8STomasz Jeznach riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_FQCSR, 1270c54acb8STomasz Jeznach RISCV_IOMMU_FQCSR_FQOF, 0); 1280c54acb8STomasz Jeznach } else { 1290c54acb8STomasz Jeznach dma_addr_t addr = s->fq_addr + tail * sizeof(*ev); 1300c54acb8STomasz Jeznach if (dma_memory_write(s->target_as, addr, ev, sizeof(*ev), 1310c54acb8STomasz Jeznach MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { 1320c54acb8STomasz Jeznach riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_FQCSR, 1330c54acb8STomasz Jeznach RISCV_IOMMU_FQCSR_FQMF, 0); 1340c54acb8STomasz Jeznach } else { 1350c54acb8STomasz Jeznach riscv_iommu_reg_set32(s, RISCV_IOMMU_REG_FQT, next); 1360c54acb8STomasz Jeznach } 1370c54acb8STomasz Jeznach } 1380c54acb8STomasz Jeznach 1390c54acb8STomasz Jeznach if (ctrl & RISCV_IOMMU_FQCSR_FIE) { 1400c54acb8STomasz Jeznach riscv_iommu_notify(s, RISCV_IOMMU_INTR_FQ); 1410c54acb8STomasz Jeznach } 1420c54acb8STomasz Jeznach } 1430c54acb8STomasz Jeznach 1440c54acb8STomasz Jeznach static void riscv_iommu_pri(RISCVIOMMUState *s, 1450c54acb8STomasz Jeznach struct riscv_iommu_pq_record *pr) 1460c54acb8STomasz Jeznach { 1470c54acb8STomasz Jeznach uint32_t ctrl = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_PQCSR); 1480c54acb8STomasz Jeznach uint32_t head = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_PQH) & s->pq_mask; 1490c54acb8STomasz Jeznach uint32_t tail = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_PQT) & s->pq_mask; 1500c54acb8STomasz Jeznach uint32_t next = (tail + 1) & s->pq_mask; 1510c54acb8STomasz Jeznach uint32_t devid = get_field(pr->hdr, RISCV_IOMMU_PREQ_HDR_DID); 1520c54acb8STomasz Jeznach 1530c54acb8STomasz Jeznach trace_riscv_iommu_pri(s->parent_obj.id, PCI_BUS_NUM(devid), PCI_SLOT(devid), 1540c54acb8STomasz Jeznach PCI_FUNC(devid), pr->payload); 1550c54acb8STomasz Jeznach 1560c54acb8STomasz Jeznach if (!(ctrl & RISCV_IOMMU_PQCSR_PQON) || 1570c54acb8STomasz Jeznach !!(ctrl & (RISCV_IOMMU_PQCSR_PQOF | RISCV_IOMMU_PQCSR_PQMF))) { 1580c54acb8STomasz Jeznach return; 1590c54acb8STomasz Jeznach } 1600c54acb8STomasz Jeznach 1610c54acb8STomasz Jeznach if (head == next) { 1620c54acb8STomasz Jeznach riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_PQCSR, 1630c54acb8STomasz Jeznach RISCV_IOMMU_PQCSR_PQOF, 0); 1640c54acb8STomasz Jeznach } else { 1650c54acb8STomasz Jeznach dma_addr_t addr = s->pq_addr + tail * sizeof(*pr); 1660c54acb8STomasz Jeznach if (dma_memory_write(s->target_as, addr, pr, sizeof(*pr), 1670c54acb8STomasz Jeznach MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { 1680c54acb8STomasz Jeznach riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_PQCSR, 1690c54acb8STomasz Jeznach RISCV_IOMMU_PQCSR_PQMF, 0); 1700c54acb8STomasz Jeznach } else { 1710c54acb8STomasz Jeznach riscv_iommu_reg_set32(s, RISCV_IOMMU_REG_PQT, next); 1720c54acb8STomasz Jeznach } 1730c54acb8STomasz Jeznach } 1740c54acb8STomasz Jeznach 1750c54acb8STomasz Jeznach if (ctrl & RISCV_IOMMU_PQCSR_PIE) { 1760c54acb8STomasz Jeznach riscv_iommu_notify(s, RISCV_IOMMU_INTR_PQ); 1770c54acb8STomasz Jeznach } 1780c54acb8STomasz Jeznach } 1790c54acb8STomasz Jeznach 180d37eede7SPierrick Bouvier /* 181d37eede7SPierrick Bouvier * Discards all bits from 'val' whose matching bits in the same 182d37eede7SPierrick Bouvier * positions in the mask 'ext' are zeros, and packs the remaining 183d37eede7SPierrick Bouvier * bits from 'val' contiguously at the least-significant end of the 184d37eede7SPierrick Bouvier * result, keeping the same bit order as 'val' and filling any 185d37eede7SPierrick Bouvier * other bits at the most-significant end of the result with zeros. 186d37eede7SPierrick Bouvier * 187d37eede7SPierrick Bouvier * For example, for the following 'val' and 'ext', the return 'ret' 188d37eede7SPierrick Bouvier * will be: 189d37eede7SPierrick Bouvier * 190d37eede7SPierrick Bouvier * val = a b c d e f g h 191d37eede7SPierrick Bouvier * ext = 1 0 1 0 0 1 1 0 192d37eede7SPierrick Bouvier * ret = 0 0 0 0 a c f g 193d37eede7SPierrick Bouvier * 194d37eede7SPierrick Bouvier * This function, taken from the riscv-iommu 1.0 spec, section 2.3.3 195d37eede7SPierrick Bouvier * "Process to translate addresses of MSIs", is similar to bit manip 196d37eede7SPierrick Bouvier * function PEXT (Parallel bits extract) from x86. 197d37eede7SPierrick Bouvier */ 198d37eede7SPierrick Bouvier static uint64_t riscv_iommu_pext_u64(uint64_t val, uint64_t ext) 1990c54acb8STomasz Jeznach { 2000c54acb8STomasz Jeznach uint64_t ret = 0; 2010c54acb8STomasz Jeznach uint64_t rot = 1; 2020c54acb8STomasz Jeznach 2030c54acb8STomasz Jeznach while (ext) { 2040c54acb8STomasz Jeznach if (ext & 1) { 2050c54acb8STomasz Jeznach if (val & 1) { 2060c54acb8STomasz Jeznach ret |= rot; 2070c54acb8STomasz Jeznach } 2080c54acb8STomasz Jeznach rot <<= 1; 2090c54acb8STomasz Jeznach } 2100c54acb8STomasz Jeznach val >>= 1; 2110c54acb8STomasz Jeznach ext >>= 1; 2120c54acb8STomasz Jeznach } 2130c54acb8STomasz Jeznach 2140c54acb8STomasz Jeznach return ret; 2150c54acb8STomasz Jeznach } 2160c54acb8STomasz Jeznach 2170c54acb8STomasz Jeznach /* Check if GPA matches MSI/MRIF pattern. */ 2180c54acb8STomasz Jeznach static bool riscv_iommu_msi_check(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, 2190c54acb8STomasz Jeznach dma_addr_t gpa) 2200c54acb8STomasz Jeznach { 2210c54acb8STomasz Jeznach if (!s->enable_msi) { 2220c54acb8STomasz Jeznach return false; 2230c54acb8STomasz Jeznach } 2240c54acb8STomasz Jeznach 2250c54acb8STomasz Jeznach if (get_field(ctx->msiptp, RISCV_IOMMU_DC_MSIPTP_MODE) != 2260c54acb8STomasz Jeznach RISCV_IOMMU_DC_MSIPTP_MODE_FLAT) { 2270c54acb8STomasz Jeznach return false; /* Invalid MSI/MRIF mode */ 2280c54acb8STomasz Jeznach } 2290c54acb8STomasz Jeznach 2300c54acb8STomasz Jeznach if ((PPN_DOWN(gpa) ^ ctx->msi_addr_pattern) & ~ctx->msi_addr_mask) { 2310c54acb8STomasz Jeznach return false; /* GPA not in MSI range defined by AIA IMSIC rules. */ 2320c54acb8STomasz Jeznach } 2330c54acb8STomasz Jeznach 2340c54acb8STomasz Jeznach return true; 2350c54acb8STomasz Jeznach } 2360c54acb8STomasz Jeznach 2370c54acb8STomasz Jeznach /* 2380c54acb8STomasz Jeznach * RISCV IOMMU Address Translation Lookup - Page Table Walk 2390c54acb8STomasz Jeznach * 2400c54acb8STomasz Jeznach * Note: Code is based on get_physical_address() from target/riscv/cpu_helper.c 2410c54acb8STomasz Jeznach * Both implementation can be merged into single helper function in future. 2420c54acb8STomasz Jeznach * Keeping them separate for now, as error reporting and flow specifics are 2430c54acb8STomasz Jeznach * sufficiently different for separate implementation. 2440c54acb8STomasz Jeznach * 2450c54acb8STomasz Jeznach * @s : IOMMU Device State 2460c54acb8STomasz Jeznach * @ctx : Translation context for device id and process address space id. 2470c54acb8STomasz Jeznach * @iotlb : translation data: physical address and access mode. 2480c54acb8STomasz Jeznach * @return : success or fault cause code. 2490c54acb8STomasz Jeznach */ 2500c54acb8STomasz Jeznach static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, 2510c54acb8STomasz Jeznach IOMMUTLBEntry *iotlb) 2520c54acb8STomasz Jeznach { 2530c54acb8STomasz Jeznach dma_addr_t addr, base; 2540c54acb8STomasz Jeznach uint64_t satp, gatp, pte; 2550c54acb8STomasz Jeznach bool en_s, en_g; 2560c54acb8STomasz Jeznach struct { 2570c54acb8STomasz Jeznach unsigned char step; 2580c54acb8STomasz Jeznach unsigned char levels; 2590c54acb8STomasz Jeznach unsigned char ptidxbits; 2600c54acb8STomasz Jeznach unsigned char ptesize; 2610c54acb8STomasz Jeznach } sc[2]; 2620c54acb8STomasz Jeznach /* Translation stage phase */ 2630c54acb8STomasz Jeznach enum { 2640c54acb8STomasz Jeznach S_STAGE = 0, 2650c54acb8STomasz Jeznach G_STAGE = 1, 2660c54acb8STomasz Jeznach } pass; 2670c54acb8STomasz Jeznach MemTxResult ret; 2680c54acb8STomasz Jeznach 2690c54acb8STomasz Jeznach satp = get_field(ctx->satp, RISCV_IOMMU_ATP_MODE_FIELD); 2700c54acb8STomasz Jeznach gatp = get_field(ctx->gatp, RISCV_IOMMU_ATP_MODE_FIELD); 2710c54acb8STomasz Jeznach 2720c54acb8STomasz Jeznach en_s = satp != RISCV_IOMMU_DC_FSC_MODE_BARE; 2730c54acb8STomasz Jeznach en_g = gatp != RISCV_IOMMU_DC_IOHGATP_MODE_BARE; 2740c54acb8STomasz Jeznach 2750c54acb8STomasz Jeznach /* 2760c54acb8STomasz Jeznach * Early check for MSI address match when IOVA == GPA. 2770c54acb8STomasz Jeznach * Note that the (!en_s) condition means that the MSI 2780c54acb8STomasz Jeznach * page table may only be used when guest pages are 2790c54acb8STomasz Jeznach * mapped using the g-stage page table, whether single- 2800c54acb8STomasz Jeznach * or two-stage paging is enabled. It's unavoidable though, 2810c54acb8STomasz Jeznach * because the spec mandates that we do a first-stage 2820c54acb8STomasz Jeznach * translation before we check the MSI page table, which 2830c54acb8STomasz Jeznach * means we can't do an early MSI check unless we have 2840c54acb8STomasz Jeznach * strictly !en_s. 2850c54acb8STomasz Jeznach */ 2860c54acb8STomasz Jeznach if (!en_s && (iotlb->perm & IOMMU_WO) && 2870c54acb8STomasz Jeznach riscv_iommu_msi_check(s, ctx, iotlb->iova)) { 2880c54acb8STomasz Jeznach iotlb->target_as = &s->trap_as; 2890c54acb8STomasz Jeznach iotlb->translated_addr = iotlb->iova; 2900c54acb8STomasz Jeznach iotlb->addr_mask = ~TARGET_PAGE_MASK; 2910c54acb8STomasz Jeznach return 0; 2920c54acb8STomasz Jeznach } 2930c54acb8STomasz Jeznach 2940c54acb8STomasz Jeznach /* Exit early for pass-through mode. */ 2950c54acb8STomasz Jeznach if (!(en_s || en_g)) { 2960c54acb8STomasz Jeznach iotlb->translated_addr = iotlb->iova; 2970c54acb8STomasz Jeznach iotlb->addr_mask = ~TARGET_PAGE_MASK; 2980c54acb8STomasz Jeznach /* Allow R/W in pass-through mode */ 2990c54acb8STomasz Jeznach iotlb->perm = IOMMU_RW; 3000c54acb8STomasz Jeznach return 0; 3010c54acb8STomasz Jeznach } 3020c54acb8STomasz Jeznach 3030c54acb8STomasz Jeznach /* S/G translation parameters. */ 3040c54acb8STomasz Jeznach for (pass = 0; pass < 2; pass++) { 3050c54acb8STomasz Jeznach uint32_t sv_mode; 3060c54acb8STomasz Jeznach 3070c54acb8STomasz Jeznach sc[pass].step = 0; 3080c54acb8STomasz Jeznach if (pass ? (s->fctl & RISCV_IOMMU_FCTL_GXL) : 3090c54acb8STomasz Jeznach (ctx->tc & RISCV_IOMMU_DC_TC_SXL)) { 3100c54acb8STomasz Jeznach /* 32bit mode for GXL/SXL == 1 */ 3110c54acb8STomasz Jeznach switch (pass ? gatp : satp) { 3120c54acb8STomasz Jeznach case RISCV_IOMMU_DC_IOHGATP_MODE_BARE: 3130c54acb8STomasz Jeznach sc[pass].levels = 0; 3140c54acb8STomasz Jeznach sc[pass].ptidxbits = 0; 3150c54acb8STomasz Jeznach sc[pass].ptesize = 0; 3160c54acb8STomasz Jeznach break; 3170c54acb8STomasz Jeznach case RISCV_IOMMU_DC_IOHGATP_MODE_SV32X4: 3180c54acb8STomasz Jeznach sv_mode = pass ? RISCV_IOMMU_CAP_SV32X4 : RISCV_IOMMU_CAP_SV32; 3190c54acb8STomasz Jeznach if (!(s->cap & sv_mode)) { 3200c54acb8STomasz Jeznach return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED; 3210c54acb8STomasz Jeznach } 3220c54acb8STomasz Jeznach sc[pass].levels = 2; 3230c54acb8STomasz Jeznach sc[pass].ptidxbits = 10; 3240c54acb8STomasz Jeznach sc[pass].ptesize = 4; 3250c54acb8STomasz Jeznach break; 3260c54acb8STomasz Jeznach default: 3270c54acb8STomasz Jeznach return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED; 3280c54acb8STomasz Jeznach } 3290c54acb8STomasz Jeznach } else { 3300c54acb8STomasz Jeznach /* 64bit mode for GXL/SXL == 0 */ 3310c54acb8STomasz Jeznach switch (pass ? gatp : satp) { 3320c54acb8STomasz Jeznach case RISCV_IOMMU_DC_IOHGATP_MODE_BARE: 3330c54acb8STomasz Jeznach sc[pass].levels = 0; 3340c54acb8STomasz Jeznach sc[pass].ptidxbits = 0; 3350c54acb8STomasz Jeznach sc[pass].ptesize = 0; 3360c54acb8STomasz Jeznach break; 3370c54acb8STomasz Jeznach case RISCV_IOMMU_DC_IOHGATP_MODE_SV39X4: 3380c54acb8STomasz Jeznach sv_mode = pass ? RISCV_IOMMU_CAP_SV39X4 : RISCV_IOMMU_CAP_SV39; 3390c54acb8STomasz Jeznach if (!(s->cap & sv_mode)) { 3400c54acb8STomasz Jeznach return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED; 3410c54acb8STomasz Jeznach } 3420c54acb8STomasz Jeznach sc[pass].levels = 3; 3430c54acb8STomasz Jeznach sc[pass].ptidxbits = 9; 3440c54acb8STomasz Jeznach sc[pass].ptesize = 8; 3450c54acb8STomasz Jeznach break; 3460c54acb8STomasz Jeznach case RISCV_IOMMU_DC_IOHGATP_MODE_SV48X4: 3470c54acb8STomasz Jeznach sv_mode = pass ? RISCV_IOMMU_CAP_SV48X4 : RISCV_IOMMU_CAP_SV48; 3480c54acb8STomasz Jeznach if (!(s->cap & sv_mode)) { 3490c54acb8STomasz Jeznach return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED; 3500c54acb8STomasz Jeznach } 3510c54acb8STomasz Jeznach sc[pass].levels = 4; 3520c54acb8STomasz Jeznach sc[pass].ptidxbits = 9; 3530c54acb8STomasz Jeznach sc[pass].ptesize = 8; 3540c54acb8STomasz Jeznach break; 3550c54acb8STomasz Jeznach case RISCV_IOMMU_DC_IOHGATP_MODE_SV57X4: 3560c54acb8STomasz Jeznach sv_mode = pass ? RISCV_IOMMU_CAP_SV57X4 : RISCV_IOMMU_CAP_SV57; 3570c54acb8STomasz Jeznach if (!(s->cap & sv_mode)) { 3580c54acb8STomasz Jeznach return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED; 3590c54acb8STomasz Jeznach } 3600c54acb8STomasz Jeznach sc[pass].levels = 5; 3610c54acb8STomasz Jeznach sc[pass].ptidxbits = 9; 3620c54acb8STomasz Jeznach sc[pass].ptesize = 8; 3630c54acb8STomasz Jeznach break; 3640c54acb8STomasz Jeznach default: 3650c54acb8STomasz Jeznach return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED; 3660c54acb8STomasz Jeznach } 3670c54acb8STomasz Jeznach } 3680c54acb8STomasz Jeznach }; 3690c54acb8STomasz Jeznach 3700c54acb8STomasz Jeznach /* S/G stages translation tables root pointers */ 3710c54acb8STomasz Jeznach gatp = PPN_PHYS(get_field(ctx->gatp, RISCV_IOMMU_ATP_PPN_FIELD)); 3720c54acb8STomasz Jeznach satp = PPN_PHYS(get_field(ctx->satp, RISCV_IOMMU_ATP_PPN_FIELD)); 3730c54acb8STomasz Jeznach addr = (en_s && en_g) ? satp : iotlb->iova; 3740c54acb8STomasz Jeznach base = en_g ? gatp : satp; 3750c54acb8STomasz Jeznach pass = en_g ? G_STAGE : S_STAGE; 3760c54acb8STomasz Jeznach 3770c54acb8STomasz Jeznach do { 3780c54acb8STomasz Jeznach const unsigned widened = (pass && !sc[pass].step) ? 2 : 0; 3790c54acb8STomasz Jeznach const unsigned va_bits = widened + sc[pass].ptidxbits; 3800c54acb8STomasz Jeznach const unsigned va_skip = TARGET_PAGE_BITS + sc[pass].ptidxbits * 3810c54acb8STomasz Jeznach (sc[pass].levels - 1 - sc[pass].step); 3820c54acb8STomasz Jeznach const unsigned idx = (addr >> va_skip) & ((1 << va_bits) - 1); 3830c54acb8STomasz Jeznach const dma_addr_t pte_addr = base + idx * sc[pass].ptesize; 3840c54acb8STomasz Jeznach const bool ade = 3850c54acb8STomasz Jeznach ctx->tc & (pass ? RISCV_IOMMU_DC_TC_GADE : RISCV_IOMMU_DC_TC_SADE); 3860c54acb8STomasz Jeznach 3870c54acb8STomasz Jeznach /* Address range check before first level lookup */ 3880c54acb8STomasz Jeznach if (!sc[pass].step) { 389e5d28bf2SJason Chien const uint64_t va_len = va_skip + va_bits; 390e5d28bf2SJason Chien const uint64_t va_mask = (1ULL << va_len) - 1; 391e5d28bf2SJason Chien 392e5d28bf2SJason Chien if (pass == S_STAGE && va_len > 32) { 393e5d28bf2SJason Chien target_ulong mask, masked_msbs; 394e5d28bf2SJason Chien 395e5d28bf2SJason Chien mask = (1L << (TARGET_LONG_BITS - (va_len - 1))) - 1; 396e5d28bf2SJason Chien masked_msbs = (addr >> (va_len - 1)) & mask; 397e5d28bf2SJason Chien 398e5d28bf2SJason Chien if (masked_msbs != 0 && masked_msbs != mask) { 399e5d28bf2SJason Chien return (iotlb->perm & IOMMU_WO) ? 400e5d28bf2SJason Chien RISCV_IOMMU_FQ_CAUSE_WR_FAULT_S : 401e5d28bf2SJason Chien RISCV_IOMMU_FQ_CAUSE_RD_FAULT_S; 402e5d28bf2SJason Chien } 403e5d28bf2SJason Chien } else { 4040c54acb8STomasz Jeznach if ((addr & va_mask) != addr) { 405e5d28bf2SJason Chien return (iotlb->perm & IOMMU_WO) ? 406e5d28bf2SJason Chien RISCV_IOMMU_FQ_CAUSE_WR_FAULT_VS : 407e5d28bf2SJason Chien RISCV_IOMMU_FQ_CAUSE_RD_FAULT_VS; 408e5d28bf2SJason Chien } 4090c54acb8STomasz Jeznach } 4100c54acb8STomasz Jeznach } 4110c54acb8STomasz Jeznach 41211ecf24cSTomasz Jeznach 41311ecf24cSTomasz Jeznach if (pass == S_STAGE) { 41411ecf24cSTomasz Jeznach riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_S_VS_WALKS); 41511ecf24cSTomasz Jeznach } else { 41611ecf24cSTomasz Jeznach riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_G_WALKS); 41711ecf24cSTomasz Jeznach } 41811ecf24cSTomasz Jeznach 4190c54acb8STomasz Jeznach /* Read page table entry */ 4200c54acb8STomasz Jeznach if (sc[pass].ptesize == 4) { 4210c54acb8STomasz Jeznach uint32_t pte32 = 0; 4220c54acb8STomasz Jeznach ret = ldl_le_dma(s->target_as, pte_addr, &pte32, 4230c54acb8STomasz Jeznach MEMTXATTRS_UNSPECIFIED); 4240c54acb8STomasz Jeznach pte = pte32; 4250c54acb8STomasz Jeznach } else { 4260c54acb8STomasz Jeznach ret = ldq_le_dma(s->target_as, pte_addr, &pte, 4270c54acb8STomasz Jeznach MEMTXATTRS_UNSPECIFIED); 4280c54acb8STomasz Jeznach } 4290c54acb8STomasz Jeznach if (ret != MEMTX_OK) { 4300c54acb8STomasz Jeznach return (iotlb->perm & IOMMU_WO) ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT 4310c54acb8STomasz Jeznach : RISCV_IOMMU_FQ_CAUSE_RD_FAULT; 4320c54acb8STomasz Jeznach } 4330c54acb8STomasz Jeznach 4340c54acb8STomasz Jeznach sc[pass].step++; 4350c54acb8STomasz Jeznach hwaddr ppn = pte >> PTE_PPN_SHIFT; 4360c54acb8STomasz Jeznach 4370c54acb8STomasz Jeznach if (!(pte & PTE_V)) { 4380c54acb8STomasz Jeznach break; /* Invalid PTE */ 4390c54acb8STomasz Jeznach } else if (!(pte & (PTE_R | PTE_W | PTE_X))) { 4400c54acb8STomasz Jeznach base = PPN_PHYS(ppn); /* Inner PTE, continue walking */ 4410c54acb8STomasz Jeznach } else if ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) { 4420c54acb8STomasz Jeznach break; /* Reserved leaf PTE flags: PTE_W */ 4430c54acb8STomasz Jeznach } else if ((pte & (PTE_R | PTE_W | PTE_X)) == (PTE_W | PTE_X)) { 4440c54acb8STomasz Jeznach break; /* Reserved leaf PTE flags: PTE_W + PTE_X */ 4450c54acb8STomasz Jeznach } else if (ppn & ((1ULL << (va_skip - TARGET_PAGE_BITS)) - 1)) { 4460c54acb8STomasz Jeznach break; /* Misaligned PPN */ 4470c54acb8STomasz Jeznach } else if ((iotlb->perm & IOMMU_RO) && !(pte & PTE_R)) { 4480c54acb8STomasz Jeznach break; /* Read access check failed */ 4490c54acb8STomasz Jeznach } else if ((iotlb->perm & IOMMU_WO) && !(pte & PTE_W)) { 4500c54acb8STomasz Jeznach break; /* Write access check failed */ 4510c54acb8STomasz Jeznach } else if ((iotlb->perm & IOMMU_RO) && !ade && !(pte & PTE_A)) { 4520c54acb8STomasz Jeznach break; /* Access bit not set */ 4530c54acb8STomasz Jeznach } else if ((iotlb->perm & IOMMU_WO) && !ade && !(pte & PTE_D)) { 4540c54acb8STomasz Jeznach break; /* Dirty bit not set */ 4550c54acb8STomasz Jeznach } else { 4560c54acb8STomasz Jeznach /* Leaf PTE, translation completed. */ 4570c54acb8STomasz Jeznach sc[pass].step = sc[pass].levels; 4580c54acb8STomasz Jeznach base = PPN_PHYS(ppn) | (addr & ((1ULL << va_skip) - 1)); 4590c54acb8STomasz Jeznach /* Update address mask based on smallest translation granularity */ 4600c54acb8STomasz Jeznach iotlb->addr_mask &= (1ULL << va_skip) - 1; 4610c54acb8STomasz Jeznach /* Continue with S-Stage translation? */ 4620c54acb8STomasz Jeznach if (pass && sc[0].step != sc[0].levels) { 4630c54acb8STomasz Jeznach pass = S_STAGE; 4640c54acb8STomasz Jeznach addr = iotlb->iova; 4650c54acb8STomasz Jeznach continue; 4660c54acb8STomasz Jeznach } 4670c54acb8STomasz Jeznach /* Translation phase completed (GPA or SPA) */ 4680c54acb8STomasz Jeznach iotlb->translated_addr = base; 4690c54acb8STomasz Jeznach iotlb->perm = (pte & PTE_W) ? ((pte & PTE_R) ? IOMMU_RW : IOMMU_WO) 4700c54acb8STomasz Jeznach : IOMMU_RO; 4710c54acb8STomasz Jeznach 4720c54acb8STomasz Jeznach /* Check MSI GPA address match */ 4730c54acb8STomasz Jeznach if (pass == S_STAGE && (iotlb->perm & IOMMU_WO) && 4740c54acb8STomasz Jeznach riscv_iommu_msi_check(s, ctx, base)) { 4750c54acb8STomasz Jeznach /* Trap MSI writes and return GPA address. */ 4760c54acb8STomasz Jeznach iotlb->target_as = &s->trap_as; 4770c54acb8STomasz Jeznach iotlb->addr_mask = ~TARGET_PAGE_MASK; 4780c54acb8STomasz Jeznach return 0; 4790c54acb8STomasz Jeznach } 4800c54acb8STomasz Jeznach 4810c54acb8STomasz Jeznach /* Continue with G-Stage translation? */ 4820c54acb8STomasz Jeznach if (!pass && en_g) { 4830c54acb8STomasz Jeznach pass = G_STAGE; 4840c54acb8STomasz Jeznach addr = base; 4850c54acb8STomasz Jeznach base = gatp; 4860c54acb8STomasz Jeznach sc[pass].step = 0; 4870c54acb8STomasz Jeznach continue; 4880c54acb8STomasz Jeznach } 4890c54acb8STomasz Jeznach 4900c54acb8STomasz Jeznach return 0; 4910c54acb8STomasz Jeznach } 4920c54acb8STomasz Jeznach 4930c54acb8STomasz Jeznach if (sc[pass].step == sc[pass].levels) { 4940c54acb8STomasz Jeznach break; /* Can't find leaf PTE */ 4950c54acb8STomasz Jeznach } 4960c54acb8STomasz Jeznach 4970c54acb8STomasz Jeznach /* Continue with G-Stage translation? */ 4980c54acb8STomasz Jeznach if (!pass && en_g) { 4990c54acb8STomasz Jeznach pass = G_STAGE; 5000c54acb8STomasz Jeznach addr = base; 5010c54acb8STomasz Jeznach base = gatp; 5020c54acb8STomasz Jeznach sc[pass].step = 0; 5030c54acb8STomasz Jeznach } 5040c54acb8STomasz Jeznach } while (1); 5050c54acb8STomasz Jeznach 5060c54acb8STomasz Jeznach return (iotlb->perm & IOMMU_WO) ? 5070c54acb8STomasz Jeznach (pass ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT_VS : 5080c54acb8STomasz Jeznach RISCV_IOMMU_FQ_CAUSE_WR_FAULT_S) : 5090c54acb8STomasz Jeznach (pass ? RISCV_IOMMU_FQ_CAUSE_RD_FAULT_VS : 5100c54acb8STomasz Jeznach RISCV_IOMMU_FQ_CAUSE_RD_FAULT_S); 5110c54acb8STomasz Jeznach } 5120c54acb8STomasz Jeznach 5130c54acb8STomasz Jeznach static void riscv_iommu_report_fault(RISCVIOMMUState *s, 5140c54acb8STomasz Jeznach RISCVIOMMUContext *ctx, 5150c54acb8STomasz Jeznach uint32_t fault_type, uint32_t cause, 5160c54acb8STomasz Jeznach bool pv, 5170c54acb8STomasz Jeznach uint64_t iotval, uint64_t iotval2) 5180c54acb8STomasz Jeznach { 5190c54acb8STomasz Jeznach struct riscv_iommu_fq_record ev = { 0 }; 5200c54acb8STomasz Jeznach 5210c54acb8STomasz Jeznach if (ctx->tc & RISCV_IOMMU_DC_TC_DTF) { 5220c54acb8STomasz Jeznach switch (cause) { 5230c54acb8STomasz Jeznach case RISCV_IOMMU_FQ_CAUSE_DMA_DISABLED: 5240c54acb8STomasz Jeznach case RISCV_IOMMU_FQ_CAUSE_DDT_LOAD_FAULT: 5250c54acb8STomasz Jeznach case RISCV_IOMMU_FQ_CAUSE_DDT_INVALID: 5260c54acb8STomasz Jeznach case RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED: 5270c54acb8STomasz Jeznach case RISCV_IOMMU_FQ_CAUSE_DDT_CORRUPTED: 5280c54acb8STomasz Jeznach case RISCV_IOMMU_FQ_CAUSE_INTERNAL_DP_ERROR: 5290c54acb8STomasz Jeznach case RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT: 5300c54acb8STomasz Jeznach break; 5310c54acb8STomasz Jeznach default: 5320c54acb8STomasz Jeznach /* DTF prevents reporting a fault for this given cause */ 5330c54acb8STomasz Jeznach return; 5340c54acb8STomasz Jeznach } 5350c54acb8STomasz Jeznach } 5360c54acb8STomasz Jeznach 5370c54acb8STomasz Jeznach ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_CAUSE, cause); 5380c54acb8STomasz Jeznach ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_TTYPE, fault_type); 5390c54acb8STomasz Jeznach ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_DID, ctx->devid); 5400c54acb8STomasz Jeznach ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_PV, true); 5410c54acb8STomasz Jeznach 5420c54acb8STomasz Jeznach if (pv) { 5430c54acb8STomasz Jeznach ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_PID, ctx->process_id); 5440c54acb8STomasz Jeznach } 5450c54acb8STomasz Jeznach 5460c54acb8STomasz Jeznach ev.iotval = iotval; 5470c54acb8STomasz Jeznach ev.iotval2 = iotval2; 5480c54acb8STomasz Jeznach 5490c54acb8STomasz Jeznach riscv_iommu_fault(s, &ev); 5500c54acb8STomasz Jeznach } 5510c54acb8STomasz Jeznach 5520c54acb8STomasz Jeznach /* Redirect MSI write for given GPA. */ 5530c54acb8STomasz Jeznach static MemTxResult riscv_iommu_msi_write(RISCVIOMMUState *s, 5540c54acb8STomasz Jeznach RISCVIOMMUContext *ctx, uint64_t gpa, uint64_t data, 5550c54acb8STomasz Jeznach unsigned size, MemTxAttrs attrs) 5560c54acb8STomasz Jeznach { 5570c54acb8STomasz Jeznach MemTxResult res; 5580c54acb8STomasz Jeznach dma_addr_t addr; 5590c54acb8STomasz Jeznach uint64_t intn; 5600c54acb8STomasz Jeznach uint32_t n190; 5610c54acb8STomasz Jeznach uint64_t pte[2]; 5620c54acb8STomasz Jeznach int fault_type = RISCV_IOMMU_FQ_TTYPE_UADDR_WR; 5630c54acb8STomasz Jeznach int cause; 5640c54acb8STomasz Jeznach 5650c54acb8STomasz Jeznach /* Interrupt File Number */ 566d37eede7SPierrick Bouvier intn = riscv_iommu_pext_u64(PPN_DOWN(gpa), ctx->msi_addr_mask); 5670c54acb8STomasz Jeznach if (intn >= 256) { 5680c54acb8STomasz Jeznach /* Interrupt file number out of range */ 5690c54acb8STomasz Jeznach res = MEMTX_ACCESS_ERROR; 5700c54acb8STomasz Jeznach cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT; 5710c54acb8STomasz Jeznach goto err; 5720c54acb8STomasz Jeznach } 5730c54acb8STomasz Jeznach 5740c54acb8STomasz Jeznach /* fetch MSI PTE */ 5750c54acb8STomasz Jeznach addr = PPN_PHYS(get_field(ctx->msiptp, RISCV_IOMMU_DC_MSIPTP_PPN)); 5760c54acb8STomasz Jeznach addr = addr | (intn * sizeof(pte)); 5770c54acb8STomasz Jeznach res = dma_memory_read(s->target_as, addr, &pte, sizeof(pte), 5780c54acb8STomasz Jeznach MEMTXATTRS_UNSPECIFIED); 5790c54acb8STomasz Jeznach if (res != MEMTX_OK) { 5800c54acb8STomasz Jeznach if (res == MEMTX_DECODE_ERROR) { 5810c54acb8STomasz Jeznach cause = RISCV_IOMMU_FQ_CAUSE_MSI_PT_CORRUPTED; 5820c54acb8STomasz Jeznach } else { 5830c54acb8STomasz Jeznach cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT; 5840c54acb8STomasz Jeznach } 5850c54acb8STomasz Jeznach goto err; 5860c54acb8STomasz Jeznach } 5870c54acb8STomasz Jeznach 5880c54acb8STomasz Jeznach le64_to_cpus(&pte[0]); 5890c54acb8STomasz Jeznach le64_to_cpus(&pte[1]); 5900c54acb8STomasz Jeznach 5910c54acb8STomasz Jeznach if (!(pte[0] & RISCV_IOMMU_MSI_PTE_V) || (pte[0] & RISCV_IOMMU_MSI_PTE_C)) { 5920c54acb8STomasz Jeznach /* 5930c54acb8STomasz Jeznach * The spec mentions that: "If msipte.C == 1, then further 5940c54acb8STomasz Jeznach * processing to interpret the PTE is implementation 5950c54acb8STomasz Jeznach * defined.". We'll abort with cause = 262 for this 5960c54acb8STomasz Jeznach * case too. 5970c54acb8STomasz Jeznach */ 5980c54acb8STomasz Jeznach res = MEMTX_ACCESS_ERROR; 5990c54acb8STomasz Jeznach cause = RISCV_IOMMU_FQ_CAUSE_MSI_INVALID; 6000c54acb8STomasz Jeznach goto err; 6010c54acb8STomasz Jeznach } 6020c54acb8STomasz Jeznach 6030c54acb8STomasz Jeznach switch (get_field(pte[0], RISCV_IOMMU_MSI_PTE_M)) { 6040c54acb8STomasz Jeznach case RISCV_IOMMU_MSI_PTE_M_BASIC: 6050c54acb8STomasz Jeznach /* MSI Pass-through mode */ 6060c54acb8STomasz Jeznach addr = PPN_PHYS(get_field(pte[0], RISCV_IOMMU_MSI_PTE_PPN)); 6070c54acb8STomasz Jeznach 6080c54acb8STomasz Jeznach trace_riscv_iommu_msi(s->parent_obj.id, PCI_BUS_NUM(ctx->devid), 6090c54acb8STomasz Jeznach PCI_SLOT(ctx->devid), PCI_FUNC(ctx->devid), 6100c54acb8STomasz Jeznach gpa, addr); 6110c54acb8STomasz Jeznach 6120c54acb8STomasz Jeznach res = dma_memory_write(s->target_as, addr, &data, size, attrs); 6130c54acb8STomasz Jeznach if (res != MEMTX_OK) { 6140c54acb8STomasz Jeznach cause = RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT; 6150c54acb8STomasz Jeznach goto err; 6160c54acb8STomasz Jeznach } 6170c54acb8STomasz Jeznach 6180c54acb8STomasz Jeznach return MEMTX_OK; 6190c54acb8STomasz Jeznach case RISCV_IOMMU_MSI_PTE_M_MRIF: 6200c54acb8STomasz Jeznach /* MRIF mode, continue. */ 6210c54acb8STomasz Jeznach break; 6220c54acb8STomasz Jeznach default: 6230c54acb8STomasz Jeznach res = MEMTX_ACCESS_ERROR; 6240c54acb8STomasz Jeznach cause = RISCV_IOMMU_FQ_CAUSE_MSI_MISCONFIGURED; 6250c54acb8STomasz Jeznach goto err; 6260c54acb8STomasz Jeznach } 6270c54acb8STomasz Jeznach 6280c54acb8STomasz Jeznach /* 6290c54acb8STomasz Jeznach * Report an error for interrupt identities exceeding the maximum allowed 6300c54acb8STomasz Jeznach * for an IMSIC interrupt file (2047) or destination address is not 32-bit 6310c54acb8STomasz Jeznach * aligned. See IOMMU Specification, Chapter 2.3. MSI page tables. 6320c54acb8STomasz Jeznach */ 6330c54acb8STomasz Jeznach if ((data > 2047) || (gpa & 3)) { 6340c54acb8STomasz Jeznach res = MEMTX_ACCESS_ERROR; 6350c54acb8STomasz Jeznach cause = RISCV_IOMMU_FQ_CAUSE_MSI_MISCONFIGURED; 6360c54acb8STomasz Jeznach goto err; 6370c54acb8STomasz Jeznach } 6380c54acb8STomasz Jeznach 6390c54acb8STomasz Jeznach /* MSI MRIF mode, non atomic pending bit update */ 6400c54acb8STomasz Jeznach 6410c54acb8STomasz Jeznach /* MRIF pending bit address */ 6420c54acb8STomasz Jeznach addr = get_field(pte[0], RISCV_IOMMU_MSI_PTE_MRIF_ADDR) << 9; 6430c54acb8STomasz Jeznach addr = addr | ((data & 0x7c0) >> 3); 6440c54acb8STomasz Jeznach 6450c54acb8STomasz Jeznach trace_riscv_iommu_msi(s->parent_obj.id, PCI_BUS_NUM(ctx->devid), 6460c54acb8STomasz Jeznach PCI_SLOT(ctx->devid), PCI_FUNC(ctx->devid), 6470c54acb8STomasz Jeznach gpa, addr); 6480c54acb8STomasz Jeznach 6490c54acb8STomasz Jeznach /* MRIF pending bit mask */ 6500c54acb8STomasz Jeznach data = 1ULL << (data & 0x03f); 6510c54acb8STomasz Jeznach res = dma_memory_read(s->target_as, addr, &intn, sizeof(intn), attrs); 6520c54acb8STomasz Jeznach if (res != MEMTX_OK) { 6530c54acb8STomasz Jeznach cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT; 6540c54acb8STomasz Jeznach goto err; 6550c54acb8STomasz Jeznach } 6560c54acb8STomasz Jeznach 6570c54acb8STomasz Jeznach intn = intn | data; 6580c54acb8STomasz Jeznach res = dma_memory_write(s->target_as, addr, &intn, sizeof(intn), attrs); 6590c54acb8STomasz Jeznach if (res != MEMTX_OK) { 6600c54acb8STomasz Jeznach cause = RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT; 6610c54acb8STomasz Jeznach goto err; 6620c54acb8STomasz Jeznach } 6630c54acb8STomasz Jeznach 6640c54acb8STomasz Jeznach /* Get MRIF enable bits */ 6650c54acb8STomasz Jeznach addr = addr + sizeof(intn); 6660c54acb8STomasz Jeznach res = dma_memory_read(s->target_as, addr, &intn, sizeof(intn), attrs); 6670c54acb8STomasz Jeznach if (res != MEMTX_OK) { 6680c54acb8STomasz Jeznach cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT; 6690c54acb8STomasz Jeznach goto err; 6700c54acb8STomasz Jeznach } 6710c54acb8STomasz Jeznach 6720c54acb8STomasz Jeznach if (!(intn & data)) { 6730c54acb8STomasz Jeznach /* notification disabled, MRIF update completed. */ 6740c54acb8STomasz Jeznach return MEMTX_OK; 6750c54acb8STomasz Jeznach } 6760c54acb8STomasz Jeznach 6770c54acb8STomasz Jeznach /* Send notification message */ 6780c54acb8STomasz Jeznach addr = PPN_PHYS(get_field(pte[1], RISCV_IOMMU_MSI_MRIF_NPPN)); 6790c54acb8STomasz Jeznach n190 = get_field(pte[1], RISCV_IOMMU_MSI_MRIF_NID) | 6800c54acb8STomasz Jeznach (get_field(pte[1], RISCV_IOMMU_MSI_MRIF_NID_MSB) << 10); 6810c54acb8STomasz Jeznach 6820c54acb8STomasz Jeznach res = dma_memory_write(s->target_as, addr, &n190, sizeof(n190), attrs); 6830c54acb8STomasz Jeznach if (res != MEMTX_OK) { 6840c54acb8STomasz Jeznach cause = RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT; 6850c54acb8STomasz Jeznach goto err; 6860c54acb8STomasz Jeznach } 6870c54acb8STomasz Jeznach 6880c54acb8STomasz Jeznach trace_riscv_iommu_mrif_notification(s->parent_obj.id, n190, addr); 6890c54acb8STomasz Jeznach 6900c54acb8STomasz Jeznach return MEMTX_OK; 6910c54acb8STomasz Jeznach 6920c54acb8STomasz Jeznach err: 6930c54acb8STomasz Jeznach riscv_iommu_report_fault(s, ctx, fault_type, cause, 6940c54acb8STomasz Jeznach !!ctx->process_id, 0, 0); 6950c54acb8STomasz Jeznach return res; 6960c54acb8STomasz Jeznach } 6970c54acb8STomasz Jeznach 6980c54acb8STomasz Jeznach /* 6990c54acb8STomasz Jeznach * Check device context configuration as described by the 7000c54acb8STomasz Jeznach * riscv-iommu spec section "Device-context configuration 7010c54acb8STomasz Jeznach * checks". 7020c54acb8STomasz Jeznach */ 7030c54acb8STomasz Jeznach static bool riscv_iommu_validate_device_ctx(RISCVIOMMUState *s, 7040c54acb8STomasz Jeznach RISCVIOMMUContext *ctx) 7050c54acb8STomasz Jeznach { 7060c54acb8STomasz Jeznach uint32_t fsc_mode, msi_mode; 70769a9ae48STomasz Jeznach uint64_t gatp; 70869a9ae48STomasz Jeznach 70969a9ae48STomasz Jeznach if (!(s->cap & RISCV_IOMMU_CAP_ATS) && 71069a9ae48STomasz Jeznach (ctx->tc & RISCV_IOMMU_DC_TC_EN_ATS || 71169a9ae48STomasz Jeznach ctx->tc & RISCV_IOMMU_DC_TC_EN_PRI || 71269a9ae48STomasz Jeznach ctx->tc & RISCV_IOMMU_DC_TC_PRPR)) { 71369a9ae48STomasz Jeznach return false; 71469a9ae48STomasz Jeznach } 71569a9ae48STomasz Jeznach 71669a9ae48STomasz Jeznach if (!(ctx->tc & RISCV_IOMMU_DC_TC_EN_ATS) && 71769a9ae48STomasz Jeznach (ctx->tc & RISCV_IOMMU_DC_TC_T2GPA || 71869a9ae48STomasz Jeznach ctx->tc & RISCV_IOMMU_DC_TC_EN_PRI)) { 71969a9ae48STomasz Jeznach return false; 72069a9ae48STomasz Jeznach } 7210c54acb8STomasz Jeznach 7220c54acb8STomasz Jeznach if (!(ctx->tc & RISCV_IOMMU_DC_TC_EN_PRI) && 7230c54acb8STomasz Jeznach ctx->tc & RISCV_IOMMU_DC_TC_PRPR) { 7240c54acb8STomasz Jeznach return false; 7250c54acb8STomasz Jeznach } 7260c54acb8STomasz Jeznach 7270c54acb8STomasz Jeznach if (!(s->cap & RISCV_IOMMU_CAP_T2GPA) && 7280c54acb8STomasz Jeznach ctx->tc & RISCV_IOMMU_DC_TC_T2GPA) { 7290c54acb8STomasz Jeznach return false; 7300c54acb8STomasz Jeznach } 7310c54acb8STomasz Jeznach 7320c54acb8STomasz Jeznach if (s->cap & RISCV_IOMMU_CAP_MSI_FLAT) { 7330c54acb8STomasz Jeznach msi_mode = get_field(ctx->msiptp, RISCV_IOMMU_DC_MSIPTP_MODE); 7340c54acb8STomasz Jeznach 7350c54acb8STomasz Jeznach if (msi_mode != RISCV_IOMMU_DC_MSIPTP_MODE_OFF && 7360c54acb8STomasz Jeznach msi_mode != RISCV_IOMMU_DC_MSIPTP_MODE_FLAT) { 7370c54acb8STomasz Jeznach return false; 7380c54acb8STomasz Jeznach } 7390c54acb8STomasz Jeznach } 7400c54acb8STomasz Jeznach 74169a9ae48STomasz Jeznach gatp = get_field(ctx->gatp, RISCV_IOMMU_ATP_MODE_FIELD); 74269a9ae48STomasz Jeznach if (ctx->tc & RISCV_IOMMU_DC_TC_T2GPA && 74369a9ae48STomasz Jeznach gatp == RISCV_IOMMU_DC_IOHGATP_MODE_BARE) { 74469a9ae48STomasz Jeznach return false; 74569a9ae48STomasz Jeznach } 74669a9ae48STomasz Jeznach 7470c54acb8STomasz Jeznach fsc_mode = get_field(ctx->satp, RISCV_IOMMU_DC_FSC_MODE); 7480c54acb8STomasz Jeznach 7490c54acb8STomasz Jeznach if (ctx->tc & RISCV_IOMMU_DC_TC_PDTV) { 7500c54acb8STomasz Jeznach switch (fsc_mode) { 7510c54acb8STomasz Jeznach case RISCV_IOMMU_DC_FSC_PDTP_MODE_PD8: 7520c54acb8STomasz Jeznach if (!(s->cap & RISCV_IOMMU_CAP_PD8)) { 7530c54acb8STomasz Jeznach return false; 7540c54acb8STomasz Jeznach } 7550c54acb8STomasz Jeznach break; 7560c54acb8STomasz Jeznach case RISCV_IOMMU_DC_FSC_PDTP_MODE_PD17: 7570c54acb8STomasz Jeznach if (!(s->cap & RISCV_IOMMU_CAP_PD17)) { 7580c54acb8STomasz Jeznach return false; 7590c54acb8STomasz Jeznach } 7600c54acb8STomasz Jeznach break; 7610c54acb8STomasz Jeznach case RISCV_IOMMU_DC_FSC_PDTP_MODE_PD20: 7620c54acb8STomasz Jeznach if (!(s->cap & RISCV_IOMMU_CAP_PD20)) { 7630c54acb8STomasz Jeznach return false; 7640c54acb8STomasz Jeznach } 7650c54acb8STomasz Jeznach break; 7660c54acb8STomasz Jeznach } 7670c54acb8STomasz Jeznach } else { 7680c54acb8STomasz Jeznach /* DC.tc.PDTV is 0 */ 7690c54acb8STomasz Jeznach if (ctx->tc & RISCV_IOMMU_DC_TC_DPE) { 7700c54acb8STomasz Jeznach return false; 7710c54acb8STomasz Jeznach } 7720c54acb8STomasz Jeznach 7730c54acb8STomasz Jeznach if (ctx->tc & RISCV_IOMMU_DC_TC_SXL) { 7740c54acb8STomasz Jeznach if (fsc_mode == RISCV_IOMMU_CAP_SV32 && 7750c54acb8STomasz Jeznach !(s->cap & RISCV_IOMMU_CAP_SV32)) { 7760c54acb8STomasz Jeznach return false; 7770c54acb8STomasz Jeznach } 7780c54acb8STomasz Jeznach } else { 7790c54acb8STomasz Jeznach switch (fsc_mode) { 7800c54acb8STomasz Jeznach case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV39: 7810c54acb8STomasz Jeznach if (!(s->cap & RISCV_IOMMU_CAP_SV39)) { 7820c54acb8STomasz Jeznach return false; 7830c54acb8STomasz Jeznach } 7840c54acb8STomasz Jeznach break; 7850c54acb8STomasz Jeznach case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV48: 7860c54acb8STomasz Jeznach if (!(s->cap & RISCV_IOMMU_CAP_SV48)) { 7870c54acb8STomasz Jeznach return false; 7880c54acb8STomasz Jeznach } 7890c54acb8STomasz Jeznach break; 7900c54acb8STomasz Jeznach case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57: 7910c54acb8STomasz Jeznach if (!(s->cap & RISCV_IOMMU_CAP_SV57)) { 7920c54acb8STomasz Jeznach return false; 7930c54acb8STomasz Jeznach } 7940c54acb8STomasz Jeznach break; 7950c54acb8STomasz Jeznach } 7960c54acb8STomasz Jeznach } 7970c54acb8STomasz Jeznach } 7980c54acb8STomasz Jeznach 7990c54acb8STomasz Jeznach /* 8000c54acb8STomasz Jeznach * CAP_END is always zero (only one endianess). FCTL_BE is 8010c54acb8STomasz Jeznach * always zero (little-endian accesses). Thus TC_SBE must 8020c54acb8STomasz Jeznach * always be LE, i.e. zero. 8030c54acb8STomasz Jeznach */ 8040c54acb8STomasz Jeznach if (ctx->tc & RISCV_IOMMU_DC_TC_SBE) { 8050c54acb8STomasz Jeznach return false; 8060c54acb8STomasz Jeznach } 8070c54acb8STomasz Jeznach 8080c54acb8STomasz Jeznach return true; 8090c54acb8STomasz Jeznach } 8100c54acb8STomasz Jeznach 8110c54acb8STomasz Jeznach /* 8120c54acb8STomasz Jeznach * Validate process context (PC) according to section 8130c54acb8STomasz Jeznach * "Process-context configuration checks". 8140c54acb8STomasz Jeznach */ 8150c54acb8STomasz Jeznach static bool riscv_iommu_validate_process_ctx(RISCVIOMMUState *s, 8160c54acb8STomasz Jeznach RISCVIOMMUContext *ctx) 8170c54acb8STomasz Jeznach { 8180c54acb8STomasz Jeznach uint32_t mode; 8190c54acb8STomasz Jeznach 8200c54acb8STomasz Jeznach if (get_field(ctx->ta, RISCV_IOMMU_PC_TA_RESERVED)) { 8210c54acb8STomasz Jeznach return false; 8220c54acb8STomasz Jeznach } 8230c54acb8STomasz Jeznach 8240c54acb8STomasz Jeznach if (get_field(ctx->satp, RISCV_IOMMU_PC_FSC_RESERVED)) { 8250c54acb8STomasz Jeznach return false; 8260c54acb8STomasz Jeznach } 8270c54acb8STomasz Jeznach 8280c54acb8STomasz Jeznach mode = get_field(ctx->satp, RISCV_IOMMU_DC_FSC_MODE); 8290c54acb8STomasz Jeznach switch (mode) { 8300c54acb8STomasz Jeznach case RISCV_IOMMU_DC_FSC_MODE_BARE: 8310c54acb8STomasz Jeznach /* sv39 and sv32 modes have the same value (8) */ 8320c54acb8STomasz Jeznach case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV39: 8330c54acb8STomasz Jeznach case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV48: 8340c54acb8STomasz Jeznach case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57: 8350c54acb8STomasz Jeznach break; 8360c54acb8STomasz Jeznach default: 8370c54acb8STomasz Jeznach return false; 8380c54acb8STomasz Jeznach } 8390c54acb8STomasz Jeznach 8400c54acb8STomasz Jeznach if (ctx->tc & RISCV_IOMMU_DC_TC_SXL) { 841d3b96a53SDaniel Henrique Barboza if (mode == RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV32 && 8420c54acb8STomasz Jeznach !(s->cap & RISCV_IOMMU_CAP_SV32)) { 8430c54acb8STomasz Jeznach return false; 8440c54acb8STomasz Jeznach } 8450c54acb8STomasz Jeznach } else { 8460c54acb8STomasz Jeznach switch (mode) { 8470c54acb8STomasz Jeznach case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV39: 8480c54acb8STomasz Jeznach if (!(s->cap & RISCV_IOMMU_CAP_SV39)) { 8490c54acb8STomasz Jeznach return false; 8500c54acb8STomasz Jeznach } 8510c54acb8STomasz Jeznach break; 8520c54acb8STomasz Jeznach case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV48: 8530c54acb8STomasz Jeznach if (!(s->cap & RISCV_IOMMU_CAP_SV48)) { 8540c54acb8STomasz Jeznach return false; 8550c54acb8STomasz Jeznach } 8560c54acb8STomasz Jeznach break; 8570c54acb8STomasz Jeznach case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57: 8580c54acb8STomasz Jeznach if (!(s->cap & RISCV_IOMMU_CAP_SV57)) { 8590c54acb8STomasz Jeznach return false; 8600c54acb8STomasz Jeznach } 8610c54acb8STomasz Jeznach break; 8620c54acb8STomasz Jeznach } 8630c54acb8STomasz Jeznach } 8640c54acb8STomasz Jeznach 8650c54acb8STomasz Jeznach return true; 8660c54acb8STomasz Jeznach } 8670c54acb8STomasz Jeznach 8680c54acb8STomasz Jeznach /* 8690c54acb8STomasz Jeznach * RISC-V IOMMU Device Context Loopkup - Device Directory Tree Walk 8700c54acb8STomasz Jeznach * 8710c54acb8STomasz Jeznach * @s : IOMMU Device State 8720c54acb8STomasz Jeznach * @ctx : Device Translation Context with devid and process_id set. 8730c54acb8STomasz Jeznach * @return : success or fault code. 8740c54acb8STomasz Jeznach */ 8750c54acb8STomasz Jeznach static int riscv_iommu_ctx_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx) 8760c54acb8STomasz Jeznach { 8770c54acb8STomasz Jeznach const uint64_t ddtp = s->ddtp; 8780c54acb8STomasz Jeznach unsigned mode = get_field(ddtp, RISCV_IOMMU_DDTP_MODE); 8790c54acb8STomasz Jeznach dma_addr_t addr = PPN_PHYS(get_field(ddtp, RISCV_IOMMU_DDTP_PPN)); 8800c54acb8STomasz Jeznach struct riscv_iommu_dc dc; 8810c54acb8STomasz Jeznach /* Device Context format: 0: extended (64 bytes) | 1: base (32 bytes) */ 8820c54acb8STomasz Jeznach const int dc_fmt = !s->enable_msi; 8830c54acb8STomasz Jeznach const size_t dc_len = sizeof(dc) >> dc_fmt; 884cd5d265fSDaniel Henrique Barboza int depth; 8850c54acb8STomasz Jeznach uint64_t de; 8860c54acb8STomasz Jeznach 8870c54acb8STomasz Jeznach switch (mode) { 8880c54acb8STomasz Jeznach case RISCV_IOMMU_DDTP_MODE_OFF: 8890c54acb8STomasz Jeznach return RISCV_IOMMU_FQ_CAUSE_DMA_DISABLED; 8900c54acb8STomasz Jeznach 8910c54acb8STomasz Jeznach case RISCV_IOMMU_DDTP_MODE_BARE: 8920c54acb8STomasz Jeznach /* mock up pass-through translation context */ 8930c54acb8STomasz Jeznach ctx->gatp = set_field(0, RISCV_IOMMU_ATP_MODE_FIELD, 8940c54acb8STomasz Jeznach RISCV_IOMMU_DC_IOHGATP_MODE_BARE); 8950c54acb8STomasz Jeznach ctx->satp = set_field(0, RISCV_IOMMU_ATP_MODE_FIELD, 8960c54acb8STomasz Jeznach RISCV_IOMMU_DC_FSC_MODE_BARE); 89769a9ae48STomasz Jeznach 8980c54acb8STomasz Jeznach ctx->tc = RISCV_IOMMU_DC_TC_V; 89969a9ae48STomasz Jeznach if (s->enable_ats) { 90069a9ae48STomasz Jeznach ctx->tc |= RISCV_IOMMU_DC_TC_EN_ATS; 90169a9ae48STomasz Jeznach } 90269a9ae48STomasz Jeznach 9030c54acb8STomasz Jeznach ctx->ta = 0; 9040c54acb8STomasz Jeznach ctx->msiptp = 0; 9050c54acb8STomasz Jeznach return 0; 9060c54acb8STomasz Jeznach 9070c54acb8STomasz Jeznach case RISCV_IOMMU_DDTP_MODE_1LVL: 9080c54acb8STomasz Jeznach depth = 0; 9090c54acb8STomasz Jeznach break; 9100c54acb8STomasz Jeznach 9110c54acb8STomasz Jeznach case RISCV_IOMMU_DDTP_MODE_2LVL: 9120c54acb8STomasz Jeznach depth = 1; 9130c54acb8STomasz Jeznach break; 9140c54acb8STomasz Jeznach 9150c54acb8STomasz Jeznach case RISCV_IOMMU_DDTP_MODE_3LVL: 9160c54acb8STomasz Jeznach depth = 2; 9170c54acb8STomasz Jeznach break; 9180c54acb8STomasz Jeznach 9190c54acb8STomasz Jeznach default: 9200c54acb8STomasz Jeznach return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED; 9210c54acb8STomasz Jeznach } 9220c54acb8STomasz Jeznach 9230c54acb8STomasz Jeznach /* 9240c54acb8STomasz Jeznach * Check supported device id width (in bits). 9250c54acb8STomasz Jeznach * See IOMMU Specification, Chapter 6. Software guidelines. 9260c54acb8STomasz Jeznach * - if extended device-context format is used: 9270c54acb8STomasz Jeznach * 1LVL: 6, 2LVL: 15, 3LVL: 24 9280c54acb8STomasz Jeznach * - if base device-context format is used: 9290c54acb8STomasz Jeznach * 1LVL: 7, 2LVL: 16, 3LVL: 24 9300c54acb8STomasz Jeznach */ 9310c54acb8STomasz Jeznach if (ctx->devid >= (1 << (depth * 9 + 6 + (dc_fmt && depth != 2)))) { 9320c54acb8STomasz Jeznach return RISCV_IOMMU_FQ_CAUSE_TTYPE_BLOCKED; 9330c54acb8STomasz Jeznach } 9340c54acb8STomasz Jeznach 9350c54acb8STomasz Jeznach /* Device directory tree walk */ 9360c54acb8STomasz Jeznach for (; depth-- > 0; ) { 93711ecf24cSTomasz Jeznach riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_DD_WALK); 9380c54acb8STomasz Jeznach /* 9390c54acb8STomasz Jeznach * Select device id index bits based on device directory tree level 9400c54acb8STomasz Jeznach * and device context format. 9410c54acb8STomasz Jeznach * See IOMMU Specification, Chapter 2. Data Structures. 9420c54acb8STomasz Jeznach * - if extended device-context format is used: 9430c54acb8STomasz Jeznach * device index: [23:15][14:6][5:0] 9440c54acb8STomasz Jeznach * - if base device-context format is used: 9450c54acb8STomasz Jeznach * device index: [23:16][15:7][6:0] 9460c54acb8STomasz Jeznach */ 9470c54acb8STomasz Jeznach const int split = depth * 9 + 6 + dc_fmt; 9480c54acb8STomasz Jeznach addr |= ((ctx->devid >> split) << 3) & ~TARGET_PAGE_MASK; 9490c54acb8STomasz Jeznach if (dma_memory_read(s->target_as, addr, &de, sizeof(de), 9500c54acb8STomasz Jeznach MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { 9510c54acb8STomasz Jeznach return RISCV_IOMMU_FQ_CAUSE_DDT_LOAD_FAULT; 9520c54acb8STomasz Jeznach } 9530c54acb8STomasz Jeznach le64_to_cpus(&de); 9540c54acb8STomasz Jeznach if (!(de & RISCV_IOMMU_DDTE_VALID)) { 9550c54acb8STomasz Jeznach /* invalid directory entry */ 9560c54acb8STomasz Jeznach return RISCV_IOMMU_FQ_CAUSE_DDT_INVALID; 9570c54acb8STomasz Jeznach } 9580c54acb8STomasz Jeznach if (de & ~(RISCV_IOMMU_DDTE_PPN | RISCV_IOMMU_DDTE_VALID)) { 9590c54acb8STomasz Jeznach /* reserved bits set */ 9600c54acb8STomasz Jeznach return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED; 9610c54acb8STomasz Jeznach } 9620c54acb8STomasz Jeznach addr = PPN_PHYS(get_field(de, RISCV_IOMMU_DDTE_PPN)); 9630c54acb8STomasz Jeznach } 9640c54acb8STomasz Jeznach 96511ecf24cSTomasz Jeznach riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_DD_WALK); 96611ecf24cSTomasz Jeznach 9670c54acb8STomasz Jeznach /* index into device context entry page */ 9680c54acb8STomasz Jeznach addr |= (ctx->devid * dc_len) & ~TARGET_PAGE_MASK; 9690c54acb8STomasz Jeznach 9700c54acb8STomasz Jeznach memset(&dc, 0, sizeof(dc)); 9710c54acb8STomasz Jeznach if (dma_memory_read(s->target_as, addr, &dc, dc_len, 9720c54acb8STomasz Jeznach MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { 9730c54acb8STomasz Jeznach return RISCV_IOMMU_FQ_CAUSE_DDT_LOAD_FAULT; 9740c54acb8STomasz Jeznach } 9750c54acb8STomasz Jeznach 9760c54acb8STomasz Jeznach /* Set translation context. */ 9770c54acb8STomasz Jeznach ctx->tc = le64_to_cpu(dc.tc); 9780c54acb8STomasz Jeznach ctx->gatp = le64_to_cpu(dc.iohgatp); 9790c54acb8STomasz Jeznach ctx->satp = le64_to_cpu(dc.fsc); 9800c54acb8STomasz Jeznach ctx->ta = le64_to_cpu(dc.ta); 9810c54acb8STomasz Jeznach ctx->msiptp = le64_to_cpu(dc.msiptp); 9820c54acb8STomasz Jeznach ctx->msi_addr_mask = le64_to_cpu(dc.msi_addr_mask); 9830c54acb8STomasz Jeznach ctx->msi_addr_pattern = le64_to_cpu(dc.msi_addr_pattern); 9840c54acb8STomasz Jeznach 9850c54acb8STomasz Jeznach if (!(ctx->tc & RISCV_IOMMU_DC_TC_V)) { 9860c54acb8STomasz Jeznach return RISCV_IOMMU_FQ_CAUSE_DDT_INVALID; 9870c54acb8STomasz Jeznach } 9880c54acb8STomasz Jeznach 9890c54acb8STomasz Jeznach if (!riscv_iommu_validate_device_ctx(s, ctx)) { 9900c54acb8STomasz Jeznach return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED; 9910c54acb8STomasz Jeznach } 9920c54acb8STomasz Jeznach 9930c54acb8STomasz Jeznach /* FSC field checks */ 9940c54acb8STomasz Jeznach mode = get_field(ctx->satp, RISCV_IOMMU_DC_FSC_MODE); 9950c54acb8STomasz Jeznach addr = PPN_PHYS(get_field(ctx->satp, RISCV_IOMMU_DC_FSC_PPN)); 9960c54acb8STomasz Jeznach 9970c54acb8STomasz Jeznach if (!(ctx->tc & RISCV_IOMMU_DC_TC_PDTV)) { 9980c54acb8STomasz Jeznach if (ctx->process_id != RISCV_IOMMU_NOPROCID) { 9990c54acb8STomasz Jeznach /* PID is disabled */ 10000c54acb8STomasz Jeznach return RISCV_IOMMU_FQ_CAUSE_TTYPE_BLOCKED; 10010c54acb8STomasz Jeznach } 10020c54acb8STomasz Jeznach if (mode > RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57) { 10030c54acb8STomasz Jeznach /* Invalid translation mode */ 10040c54acb8STomasz Jeznach return RISCV_IOMMU_FQ_CAUSE_DDT_INVALID; 10050c54acb8STomasz Jeznach } 10060c54acb8STomasz Jeznach return 0; 10070c54acb8STomasz Jeznach } 10080c54acb8STomasz Jeznach 10090c54acb8STomasz Jeznach if (ctx->process_id == RISCV_IOMMU_NOPROCID) { 10100c54acb8STomasz Jeznach if (!(ctx->tc & RISCV_IOMMU_DC_TC_DPE)) { 10110c54acb8STomasz Jeznach /* No default process_id enabled, set BARE mode */ 10120c54acb8STomasz Jeznach ctx->satp = 0ULL; 10130c54acb8STomasz Jeznach return 0; 10140c54acb8STomasz Jeznach } else { 10150c54acb8STomasz Jeznach /* Use default process_id #0 */ 10160c54acb8STomasz Jeznach ctx->process_id = 0; 10170c54acb8STomasz Jeznach } 10180c54acb8STomasz Jeznach } 10190c54acb8STomasz Jeznach 10200c54acb8STomasz Jeznach if (mode == RISCV_IOMMU_DC_FSC_MODE_BARE) { 10210c54acb8STomasz Jeznach /* No S-Stage translation, done. */ 10220c54acb8STomasz Jeznach return 0; 10230c54acb8STomasz Jeznach } 10240c54acb8STomasz Jeznach 10250c54acb8STomasz Jeznach /* FSC.TC.PDTV enabled */ 10260c54acb8STomasz Jeznach if (mode > RISCV_IOMMU_DC_FSC_PDTP_MODE_PD20) { 10270c54acb8STomasz Jeznach /* Invalid PDTP.MODE */ 10280c54acb8STomasz Jeznach return RISCV_IOMMU_FQ_CAUSE_PDT_MISCONFIGURED; 10290c54acb8STomasz Jeznach } 10300c54acb8STomasz Jeznach 10310c54acb8STomasz Jeznach for (depth = mode - RISCV_IOMMU_DC_FSC_PDTP_MODE_PD8; depth-- > 0; ) { 103211ecf24cSTomasz Jeznach riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_PD_WALK); 103311ecf24cSTomasz Jeznach 10340c54acb8STomasz Jeznach /* 10350c54acb8STomasz Jeznach * Select process id index bits based on process directory tree 10360c54acb8STomasz Jeznach * level. See IOMMU Specification, 2.2. Process-Directory-Table. 10370c54acb8STomasz Jeznach */ 10380c54acb8STomasz Jeznach const int split = depth * 9 + 8; 10390c54acb8STomasz Jeznach addr |= ((ctx->process_id >> split) << 3) & ~TARGET_PAGE_MASK; 10400c54acb8STomasz Jeznach if (dma_memory_read(s->target_as, addr, &de, sizeof(de), 10410c54acb8STomasz Jeznach MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { 10420c54acb8STomasz Jeznach return RISCV_IOMMU_FQ_CAUSE_PDT_LOAD_FAULT; 10430c54acb8STomasz Jeznach } 10440c54acb8STomasz Jeznach le64_to_cpus(&de); 10450c54acb8STomasz Jeznach if (!(de & RISCV_IOMMU_PC_TA_V)) { 10460c54acb8STomasz Jeznach return RISCV_IOMMU_FQ_CAUSE_PDT_INVALID; 10470c54acb8STomasz Jeznach } 10480c54acb8STomasz Jeznach addr = PPN_PHYS(get_field(de, RISCV_IOMMU_PC_FSC_PPN)); 10490c54acb8STomasz Jeznach } 10500c54acb8STomasz Jeznach 105111ecf24cSTomasz Jeznach riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_PD_WALK); 105211ecf24cSTomasz Jeznach 10530c54acb8STomasz Jeznach /* Leaf entry in PDT */ 10540c54acb8STomasz Jeznach addr |= (ctx->process_id << 4) & ~TARGET_PAGE_MASK; 10550c54acb8STomasz Jeznach if (dma_memory_read(s->target_as, addr, &dc.ta, sizeof(uint64_t) * 2, 10560c54acb8STomasz Jeznach MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { 10570c54acb8STomasz Jeznach return RISCV_IOMMU_FQ_CAUSE_PDT_LOAD_FAULT; 10580c54acb8STomasz Jeznach } 10590c54acb8STomasz Jeznach 10600c54acb8STomasz Jeznach /* Use FSC and TA from process directory entry. */ 10610c54acb8STomasz Jeznach ctx->ta = le64_to_cpu(dc.ta); 10620c54acb8STomasz Jeznach ctx->satp = le64_to_cpu(dc.fsc); 10630c54acb8STomasz Jeznach 10640c54acb8STomasz Jeznach if (!(ctx->ta & RISCV_IOMMU_PC_TA_V)) { 10650c54acb8STomasz Jeznach return RISCV_IOMMU_FQ_CAUSE_PDT_INVALID; 10660c54acb8STomasz Jeznach } 10670c54acb8STomasz Jeznach 10680c54acb8STomasz Jeznach if (!riscv_iommu_validate_process_ctx(s, ctx)) { 10690c54acb8STomasz Jeznach return RISCV_IOMMU_FQ_CAUSE_PDT_MISCONFIGURED; 10700c54acb8STomasz Jeznach } 10710c54acb8STomasz Jeznach 10720c54acb8STomasz Jeznach return 0; 10730c54acb8STomasz Jeznach } 10740c54acb8STomasz Jeznach 10750c54acb8STomasz Jeznach /* Translation Context cache support */ 10760c54acb8STomasz Jeznach static gboolean riscv_iommu_ctx_equal(gconstpointer v1, gconstpointer v2) 10770c54acb8STomasz Jeznach { 10780c54acb8STomasz Jeznach RISCVIOMMUContext *c1 = (RISCVIOMMUContext *) v1; 10790c54acb8STomasz Jeznach RISCVIOMMUContext *c2 = (RISCVIOMMUContext *) v2; 10800c54acb8STomasz Jeznach return c1->devid == c2->devid && 10810c54acb8STomasz Jeznach c1->process_id == c2->process_id; 10820c54acb8STomasz Jeznach } 10830c54acb8STomasz Jeznach 10840c54acb8STomasz Jeznach static guint riscv_iommu_ctx_hash(gconstpointer v) 10850c54acb8STomasz Jeznach { 10860c54acb8STomasz Jeznach RISCVIOMMUContext *ctx = (RISCVIOMMUContext *) v; 10870c54acb8STomasz Jeznach /* 10880c54acb8STomasz Jeznach * Generate simple hash of (process_id, devid) 10890c54acb8STomasz Jeznach * assuming 24-bit wide devid. 10900c54acb8STomasz Jeznach */ 10910c54acb8STomasz Jeznach return (guint)(ctx->devid) + ((guint)(ctx->process_id) << 24); 10920c54acb8STomasz Jeznach } 10930c54acb8STomasz Jeznach 10940c54acb8STomasz Jeznach static void riscv_iommu_ctx_inval_devid_procid(gpointer key, gpointer value, 10950c54acb8STomasz Jeznach gpointer data) 10960c54acb8STomasz Jeznach { 10970c54acb8STomasz Jeznach RISCVIOMMUContext *ctx = (RISCVIOMMUContext *) value; 10980c54acb8STomasz Jeznach RISCVIOMMUContext *arg = (RISCVIOMMUContext *) data; 10990c54acb8STomasz Jeznach if (ctx->tc & RISCV_IOMMU_DC_TC_V && 11000c54acb8STomasz Jeznach ctx->devid == arg->devid && 11010c54acb8STomasz Jeznach ctx->process_id == arg->process_id) { 11020c54acb8STomasz Jeznach ctx->tc &= ~RISCV_IOMMU_DC_TC_V; 11030c54acb8STomasz Jeznach } 11040c54acb8STomasz Jeznach } 11050c54acb8STomasz Jeznach 11060c54acb8STomasz Jeznach static void riscv_iommu_ctx_inval_devid(gpointer key, gpointer value, 11070c54acb8STomasz Jeznach gpointer data) 11080c54acb8STomasz Jeznach { 11090c54acb8STomasz Jeznach RISCVIOMMUContext *ctx = (RISCVIOMMUContext *) value; 11100c54acb8STomasz Jeznach RISCVIOMMUContext *arg = (RISCVIOMMUContext *) data; 11110c54acb8STomasz Jeznach if (ctx->tc & RISCV_IOMMU_DC_TC_V && 11120c54acb8STomasz Jeznach ctx->devid == arg->devid) { 11130c54acb8STomasz Jeznach ctx->tc &= ~RISCV_IOMMU_DC_TC_V; 11140c54acb8STomasz Jeznach } 11150c54acb8STomasz Jeznach } 11160c54acb8STomasz Jeznach 11170c54acb8STomasz Jeznach static void riscv_iommu_ctx_inval_all(gpointer key, gpointer value, 11180c54acb8STomasz Jeznach gpointer data) 11190c54acb8STomasz Jeznach { 11200c54acb8STomasz Jeznach RISCVIOMMUContext *ctx = (RISCVIOMMUContext *) value; 11210c54acb8STomasz Jeznach if (ctx->tc & RISCV_IOMMU_DC_TC_V) { 11220c54acb8STomasz Jeznach ctx->tc &= ~RISCV_IOMMU_DC_TC_V; 11230c54acb8STomasz Jeznach } 11240c54acb8STomasz Jeznach } 11250c54acb8STomasz Jeznach 11260c54acb8STomasz Jeznach static void riscv_iommu_ctx_inval(RISCVIOMMUState *s, GHFunc func, 11270c54acb8STomasz Jeznach uint32_t devid, uint32_t process_id) 11280c54acb8STomasz Jeznach { 11290c54acb8STomasz Jeznach GHashTable *ctx_cache; 11300c54acb8STomasz Jeznach RISCVIOMMUContext key = { 11310c54acb8STomasz Jeznach .devid = devid, 11320c54acb8STomasz Jeznach .process_id = process_id, 11330c54acb8STomasz Jeznach }; 11340c54acb8STomasz Jeznach ctx_cache = g_hash_table_ref(s->ctx_cache); 11350c54acb8STomasz Jeznach g_hash_table_foreach(ctx_cache, func, &key); 11360c54acb8STomasz Jeznach g_hash_table_unref(ctx_cache); 11370c54acb8STomasz Jeznach } 11380c54acb8STomasz Jeznach 11390c54acb8STomasz Jeznach /* Find or allocate translation context for a given {device_id, process_id} */ 11400c54acb8STomasz Jeznach static RISCVIOMMUContext *riscv_iommu_ctx(RISCVIOMMUState *s, 11410c54acb8STomasz Jeznach unsigned devid, unsigned process_id, 11420c54acb8STomasz Jeznach void **ref) 11430c54acb8STomasz Jeznach { 11440c54acb8STomasz Jeznach GHashTable *ctx_cache; 11450c54acb8STomasz Jeznach RISCVIOMMUContext *ctx; 11460c54acb8STomasz Jeznach RISCVIOMMUContext key = { 11470c54acb8STomasz Jeznach .devid = devid, 11480c54acb8STomasz Jeznach .process_id = process_id, 11490c54acb8STomasz Jeznach }; 11500c54acb8STomasz Jeznach 11510c54acb8STomasz Jeznach ctx_cache = g_hash_table_ref(s->ctx_cache); 11520c54acb8STomasz Jeznach ctx = g_hash_table_lookup(ctx_cache, &key); 11530c54acb8STomasz Jeznach 11540c54acb8STomasz Jeznach if (ctx && (ctx->tc & RISCV_IOMMU_DC_TC_V)) { 11550c54acb8STomasz Jeznach *ref = ctx_cache; 11560c54acb8STomasz Jeznach return ctx; 11570c54acb8STomasz Jeznach } 11580c54acb8STomasz Jeznach 11590c54acb8STomasz Jeznach ctx = g_new0(RISCVIOMMUContext, 1); 11600c54acb8STomasz Jeznach ctx->devid = devid; 11610c54acb8STomasz Jeznach ctx->process_id = process_id; 11620c54acb8STomasz Jeznach 11630c54acb8STomasz Jeznach int fault = riscv_iommu_ctx_fetch(s, ctx); 11640c54acb8STomasz Jeznach if (!fault) { 11650c54acb8STomasz Jeznach if (g_hash_table_size(ctx_cache) >= LIMIT_CACHE_CTX) { 11660c54acb8STomasz Jeznach g_hash_table_unref(ctx_cache); 11670c54acb8STomasz Jeznach ctx_cache = g_hash_table_new_full(riscv_iommu_ctx_hash, 11680c54acb8STomasz Jeznach riscv_iommu_ctx_equal, 11690c54acb8STomasz Jeznach g_free, NULL); 11700c54acb8STomasz Jeznach g_hash_table_ref(ctx_cache); 11710c54acb8STomasz Jeznach g_hash_table_unref(qatomic_xchg(&s->ctx_cache, ctx_cache)); 11720c54acb8STomasz Jeznach } 11730c54acb8STomasz Jeznach g_hash_table_add(ctx_cache, ctx); 11740c54acb8STomasz Jeznach *ref = ctx_cache; 11750c54acb8STomasz Jeznach return ctx; 11760c54acb8STomasz Jeznach } 11770c54acb8STomasz Jeznach 11780c54acb8STomasz Jeznach g_hash_table_unref(ctx_cache); 11790c54acb8STomasz Jeznach *ref = NULL; 11800c54acb8STomasz Jeznach 11810c54acb8STomasz Jeznach riscv_iommu_report_fault(s, ctx, RISCV_IOMMU_FQ_TTYPE_UADDR_RD, 11820c54acb8STomasz Jeznach fault, !!process_id, 0, 0); 11830c54acb8STomasz Jeznach 11840c54acb8STomasz Jeznach g_free(ctx); 11850c54acb8STomasz Jeznach return NULL; 11860c54acb8STomasz Jeznach } 11870c54acb8STomasz Jeznach 11880c54acb8STomasz Jeznach static void riscv_iommu_ctx_put(RISCVIOMMUState *s, void *ref) 11890c54acb8STomasz Jeznach { 11900c54acb8STomasz Jeznach if (ref) { 11910c54acb8STomasz Jeznach g_hash_table_unref((GHashTable *)ref); 11920c54acb8STomasz Jeznach } 11930c54acb8STomasz Jeznach } 11940c54acb8STomasz Jeznach 11950c54acb8STomasz Jeznach /* Find or allocate address space for a given device */ 11960c54acb8STomasz Jeznach static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, uint32_t devid) 11970c54acb8STomasz Jeznach { 11980c54acb8STomasz Jeznach RISCVIOMMUSpace *as; 11990c54acb8STomasz Jeznach 12000c54acb8STomasz Jeznach /* FIXME: PCIe bus remapping for attached endpoints. */ 12010c54acb8STomasz Jeznach devid |= s->bus << 8; 12020c54acb8STomasz Jeznach 12030c54acb8STomasz Jeznach QLIST_FOREACH(as, &s->spaces, list) { 12040c54acb8STomasz Jeznach if (as->devid == devid) { 12050c54acb8STomasz Jeznach break; 12060c54acb8STomasz Jeznach } 12070c54acb8STomasz Jeznach } 12080c54acb8STomasz Jeznach 12090c54acb8STomasz Jeznach if (as == NULL) { 12100c54acb8STomasz Jeznach char name[64]; 12110c54acb8STomasz Jeznach as = g_new0(RISCVIOMMUSpace, 1); 12120c54acb8STomasz Jeznach 12130c54acb8STomasz Jeznach as->iommu = s; 12140c54acb8STomasz Jeznach as->devid = devid; 12150c54acb8STomasz Jeznach 12160c54acb8STomasz Jeznach snprintf(name, sizeof(name), "riscv-iommu-%04x:%02x.%d-iova", 12170c54acb8STomasz Jeznach PCI_BUS_NUM(as->devid), PCI_SLOT(as->devid), PCI_FUNC(as->devid)); 12180c54acb8STomasz Jeznach 12190c54acb8STomasz Jeznach /* IOVA address space, untranslated addresses */ 12200c54acb8STomasz Jeznach memory_region_init_iommu(&as->iova_mr, sizeof(as->iova_mr), 12210c54acb8STomasz Jeznach TYPE_RISCV_IOMMU_MEMORY_REGION, 12220c54acb8STomasz Jeznach OBJECT(as), "riscv_iommu", UINT64_MAX); 12230c54acb8STomasz Jeznach address_space_init(&as->iova_as, MEMORY_REGION(&as->iova_mr), name); 12240c54acb8STomasz Jeznach 12250c54acb8STomasz Jeznach QLIST_INSERT_HEAD(&s->spaces, as, list); 12260c54acb8STomasz Jeznach 12270c54acb8STomasz Jeznach trace_riscv_iommu_new(s->parent_obj.id, PCI_BUS_NUM(as->devid), 12280c54acb8STomasz Jeznach PCI_SLOT(as->devid), PCI_FUNC(as->devid)); 12290c54acb8STomasz Jeznach } 12300c54acb8STomasz Jeznach return &as->iova_as; 12310c54acb8STomasz Jeznach } 12320c54acb8STomasz Jeznach 12339d085a1cSTomasz Jeznach /* Translation Object cache support */ 12349d085a1cSTomasz Jeznach static gboolean riscv_iommu_iot_equal(gconstpointer v1, gconstpointer v2) 12350c54acb8STomasz Jeznach { 12369d085a1cSTomasz Jeznach RISCVIOMMUEntry *t1 = (RISCVIOMMUEntry *) v1; 12379d085a1cSTomasz Jeznach RISCVIOMMUEntry *t2 = (RISCVIOMMUEntry *) v2; 12389d085a1cSTomasz Jeznach return t1->gscid == t2->gscid && t1->pscid == t2->pscid && 1239fa622855SJason Chien t1->iova == t2->iova && t1->tag == t2->tag; 12409d085a1cSTomasz Jeznach } 12419d085a1cSTomasz Jeznach 12429d085a1cSTomasz Jeznach static guint riscv_iommu_iot_hash(gconstpointer v) 12439d085a1cSTomasz Jeznach { 12449d085a1cSTomasz Jeznach RISCVIOMMUEntry *t = (RISCVIOMMUEntry *) v; 12459d085a1cSTomasz Jeznach return (guint)t->iova; 12469d085a1cSTomasz Jeznach } 12479d085a1cSTomasz Jeznach 1248fa622855SJason Chien /* GV: 0 AV: 0 PSCV: 0 GVMA: 0 */ 1249fa622855SJason Chien /* GV: 0 AV: 0 GVMA: 1 */ 1250fa622855SJason Chien static 1251fa622855SJason Chien void riscv_iommu_iot_inval_all(gpointer key, gpointer value, gpointer data) 1252fa622855SJason Chien { 1253fa622855SJason Chien RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; 1254fa622855SJason Chien RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; 1255fa622855SJason Chien if (iot->tag == arg->tag) { 1256fa622855SJason Chien iot->perm = IOMMU_NONE; 1257fa622855SJason Chien } 1258fa622855SJason Chien } 1259fa622855SJason Chien 1260fa622855SJason Chien /* GV: 0 AV: 0 PSCV: 1 GVMA: 0 */ 1261fa622855SJason Chien static 1262fa622855SJason Chien void riscv_iommu_iot_inval_pscid(gpointer key, gpointer value, gpointer data) 1263fa622855SJason Chien { 1264fa622855SJason Chien RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; 1265fa622855SJason Chien RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; 1266fa622855SJason Chien if (iot->tag == arg->tag && 1267fa622855SJason Chien iot->pscid == arg->pscid) { 1268fa622855SJason Chien iot->perm = IOMMU_NONE; 1269fa622855SJason Chien } 1270fa622855SJason Chien } 1271fa622855SJason Chien 1272fa622855SJason Chien /* GV: 0 AV: 1 PSCV: 0 GVMA: 0 */ 1273fa622855SJason Chien static 1274fa622855SJason Chien void riscv_iommu_iot_inval_iova(gpointer key, gpointer value, gpointer data) 1275fa622855SJason Chien { 1276fa622855SJason Chien RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; 1277fa622855SJason Chien RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; 1278fa622855SJason Chien if (iot->tag == arg->tag && 1279fa622855SJason Chien iot->iova == arg->iova) { 1280fa622855SJason Chien iot->perm = IOMMU_NONE; 1281fa622855SJason Chien } 1282fa622855SJason Chien } 1283fa622855SJason Chien 1284fa622855SJason Chien /* GV: 0 AV: 1 PSCV: 1 GVMA: 0 */ 12859d085a1cSTomasz Jeznach static void riscv_iommu_iot_inval_pscid_iova(gpointer key, gpointer value, 12869d085a1cSTomasz Jeznach gpointer data) 12879d085a1cSTomasz Jeznach { 12889d085a1cSTomasz Jeznach RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; 12899d085a1cSTomasz Jeznach RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; 1290fa622855SJason Chien if (iot->tag == arg->tag && 12919d085a1cSTomasz Jeznach iot->pscid == arg->pscid && 12929d085a1cSTomasz Jeznach iot->iova == arg->iova) { 12939d085a1cSTomasz Jeznach iot->perm = IOMMU_NONE; 12949d085a1cSTomasz Jeznach } 12959d085a1cSTomasz Jeznach } 12969d085a1cSTomasz Jeznach 1297fa622855SJason Chien /* GV: 1 AV: 0 PSCV: 0 GVMA: 0 */ 1298fa622855SJason Chien /* GV: 1 AV: 0 GVMA: 1 */ 1299fa622855SJason Chien static 1300fa622855SJason Chien void riscv_iommu_iot_inval_gscid(gpointer key, gpointer value, gpointer data) 1301fa622855SJason Chien { 1302fa622855SJason Chien RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; 1303fa622855SJason Chien RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; 1304fa622855SJason Chien if (iot->tag == arg->tag && 1305fa622855SJason Chien iot->gscid == arg->gscid) { 1306fa622855SJason Chien iot->perm = IOMMU_NONE; 1307fa622855SJason Chien } 1308fa622855SJason Chien } 1309fa622855SJason Chien 1310fa622855SJason Chien /* GV: 1 AV: 0 PSCV: 1 GVMA: 0 */ 1311fa622855SJason Chien static void riscv_iommu_iot_inval_gscid_pscid(gpointer key, gpointer value, 13129d085a1cSTomasz Jeznach gpointer data) 13139d085a1cSTomasz Jeznach { 13149d085a1cSTomasz Jeznach RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; 13159d085a1cSTomasz Jeznach RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; 1316fa622855SJason Chien if (iot->tag == arg->tag && 1317fa622855SJason Chien iot->gscid == arg->gscid && 13189d085a1cSTomasz Jeznach iot->pscid == arg->pscid) { 13199d085a1cSTomasz Jeznach iot->perm = IOMMU_NONE; 13209d085a1cSTomasz Jeznach } 13219d085a1cSTomasz Jeznach } 13229d085a1cSTomasz Jeznach 1323fa622855SJason Chien /* GV: 1 AV: 1 PSCV: 0 GVMA: 0 */ 1324fa622855SJason Chien /* GV: 1 AV: 1 GVMA: 1 */ 1325fa622855SJason Chien static void riscv_iommu_iot_inval_gscid_iova(gpointer key, gpointer value, 13269d085a1cSTomasz Jeznach gpointer data) 13279d085a1cSTomasz Jeznach { 13289d085a1cSTomasz Jeznach RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; 13299d085a1cSTomasz Jeznach RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; 1330fa622855SJason Chien if (iot->tag == arg->tag && 1331fa622855SJason Chien iot->gscid == arg->gscid && 1332fa622855SJason Chien iot->iova == arg->iova) { 13339d085a1cSTomasz Jeznach iot->perm = IOMMU_NONE; 13349d085a1cSTomasz Jeznach } 13359d085a1cSTomasz Jeznach } 13369d085a1cSTomasz Jeznach 1337fa622855SJason Chien /* GV: 1 AV: 1 PSCV: 1 GVMA: 0 */ 1338fa622855SJason Chien static void riscv_iommu_iot_inval_gscid_pscid_iova(gpointer key, gpointer value, 13399d085a1cSTomasz Jeznach gpointer data) 13409d085a1cSTomasz Jeznach { 13419d085a1cSTomasz Jeznach RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; 13429d085a1cSTomasz Jeznach RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; 1343fa622855SJason Chien if (iot->tag == arg->tag && 1344fa622855SJason Chien iot->gscid == arg->gscid && 1345fa622855SJason Chien iot->pscid == arg->pscid && 1346fa622855SJason Chien iot->iova == arg->iova) { 13479d085a1cSTomasz Jeznach iot->perm = IOMMU_NONE; 13489d085a1cSTomasz Jeznach } 13499d085a1cSTomasz Jeznach } 13509d085a1cSTomasz Jeznach 13519d085a1cSTomasz Jeznach /* caller should keep ref-count for iot_cache object */ 13529d085a1cSTomasz Jeznach static RISCVIOMMUEntry *riscv_iommu_iot_lookup(RISCVIOMMUContext *ctx, 1353fa622855SJason Chien GHashTable *iot_cache, hwaddr iova, RISCVIOMMUTransTag transtag) 13549d085a1cSTomasz Jeznach { 13559d085a1cSTomasz Jeznach RISCVIOMMUEntry key = { 1356fa622855SJason Chien .tag = transtag, 13579d085a1cSTomasz Jeznach .gscid = get_field(ctx->gatp, RISCV_IOMMU_DC_IOHGATP_GSCID), 13589d085a1cSTomasz Jeznach .pscid = get_field(ctx->ta, RISCV_IOMMU_DC_TA_PSCID), 13599d085a1cSTomasz Jeznach .iova = PPN_DOWN(iova), 13609d085a1cSTomasz Jeznach }; 13619d085a1cSTomasz Jeznach return g_hash_table_lookup(iot_cache, &key); 13629d085a1cSTomasz Jeznach } 13639d085a1cSTomasz Jeznach 13649d085a1cSTomasz Jeznach /* caller should keep ref-count for iot_cache object */ 13659d085a1cSTomasz Jeznach static void riscv_iommu_iot_update(RISCVIOMMUState *s, 13669d085a1cSTomasz Jeznach GHashTable *iot_cache, RISCVIOMMUEntry *iot) 13679d085a1cSTomasz Jeznach { 13689d085a1cSTomasz Jeznach if (!s->iot_limit) { 13699d085a1cSTomasz Jeznach return; 13709d085a1cSTomasz Jeznach } 13719d085a1cSTomasz Jeznach 13729d085a1cSTomasz Jeznach if (g_hash_table_size(s->iot_cache) >= s->iot_limit) { 13739d085a1cSTomasz Jeznach iot_cache = g_hash_table_new_full(riscv_iommu_iot_hash, 13749d085a1cSTomasz Jeznach riscv_iommu_iot_equal, 13759d085a1cSTomasz Jeznach g_free, NULL); 13769d085a1cSTomasz Jeznach g_hash_table_unref(qatomic_xchg(&s->iot_cache, iot_cache)); 13779d085a1cSTomasz Jeznach } 13789d085a1cSTomasz Jeznach g_hash_table_add(iot_cache, iot); 13799d085a1cSTomasz Jeznach } 13809d085a1cSTomasz Jeznach 13819d085a1cSTomasz Jeznach static void riscv_iommu_iot_inval(RISCVIOMMUState *s, GHFunc func, 1382fa622855SJason Chien uint32_t gscid, uint32_t pscid, hwaddr iova, RISCVIOMMUTransTag transtag) 13839d085a1cSTomasz Jeznach { 13849d085a1cSTomasz Jeznach GHashTable *iot_cache; 13859d085a1cSTomasz Jeznach RISCVIOMMUEntry key = { 1386fa622855SJason Chien .tag = transtag, 13879d085a1cSTomasz Jeznach .gscid = gscid, 13889d085a1cSTomasz Jeznach .pscid = pscid, 13899d085a1cSTomasz Jeznach .iova = PPN_DOWN(iova), 13909d085a1cSTomasz Jeznach }; 13919d085a1cSTomasz Jeznach 13929d085a1cSTomasz Jeznach iot_cache = g_hash_table_ref(s->iot_cache); 13939d085a1cSTomasz Jeznach g_hash_table_foreach(iot_cache, func, &key); 13949d085a1cSTomasz Jeznach g_hash_table_unref(iot_cache); 13959d085a1cSTomasz Jeznach } 13969d085a1cSTomasz Jeznach 1397fa622855SJason Chien static RISCVIOMMUTransTag riscv_iommu_get_transtag(RISCVIOMMUContext *ctx) 1398fa622855SJason Chien { 1399fa622855SJason Chien uint64_t satp = get_field(ctx->satp, RISCV_IOMMU_ATP_MODE_FIELD); 1400fa622855SJason Chien uint64_t gatp = get_field(ctx->gatp, RISCV_IOMMU_ATP_MODE_FIELD); 1401fa622855SJason Chien 1402fa622855SJason Chien if (satp == RISCV_IOMMU_DC_FSC_MODE_BARE) { 1403fa622855SJason Chien return (gatp == RISCV_IOMMU_DC_IOHGATP_MODE_BARE) ? 1404fa622855SJason Chien RISCV_IOMMU_TRANS_TAG_BY : RISCV_IOMMU_TRANS_TAG_VG; 1405fa622855SJason Chien } else { 1406fa622855SJason Chien return (gatp == RISCV_IOMMU_DC_IOHGATP_MODE_BARE) ? 1407fa622855SJason Chien RISCV_IOMMU_TRANS_TAG_SS : RISCV_IOMMU_TRANS_TAG_VN; 1408fa622855SJason Chien } 1409fa622855SJason Chien } 1410fa622855SJason Chien 14119d085a1cSTomasz Jeznach static int riscv_iommu_translate(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, 14129d085a1cSTomasz Jeznach IOMMUTLBEntry *iotlb, bool enable_cache) 14139d085a1cSTomasz Jeznach { 1414fa622855SJason Chien RISCVIOMMUTransTag transtag = riscv_iommu_get_transtag(ctx); 14159d085a1cSTomasz Jeznach RISCVIOMMUEntry *iot; 14169d085a1cSTomasz Jeznach IOMMUAccessFlags perm; 14170c54acb8STomasz Jeznach bool enable_pid; 14180c54acb8STomasz Jeznach bool enable_pri; 14199d085a1cSTomasz Jeznach GHashTable *iot_cache; 14200c54acb8STomasz Jeznach int fault; 14210c54acb8STomasz Jeznach 142211ecf24cSTomasz Jeznach riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_URQ); 142311ecf24cSTomasz Jeznach 14249d085a1cSTomasz Jeznach iot_cache = g_hash_table_ref(s->iot_cache); 14250c54acb8STomasz Jeznach /* 14260c54acb8STomasz Jeznach * TC[32] is reserved for custom extensions, used here to temporarily 14270c54acb8STomasz Jeznach * enable automatic page-request generation for ATS queries. 14280c54acb8STomasz Jeznach */ 14290c54acb8STomasz Jeznach enable_pri = (iotlb->perm == IOMMU_NONE) && (ctx->tc & BIT_ULL(32)); 14300c54acb8STomasz Jeznach enable_pid = (ctx->tc & RISCV_IOMMU_DC_TC_PDTV); 14310c54acb8STomasz Jeznach 143269a9ae48STomasz Jeznach /* Check for ATS request. */ 143369a9ae48STomasz Jeznach if (iotlb->perm == IOMMU_NONE) { 143411ecf24cSTomasz Jeznach riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_ATS_RQ); 143569a9ae48STomasz Jeznach /* Check if ATS is disabled. */ 143669a9ae48STomasz Jeznach if (!(ctx->tc & RISCV_IOMMU_DC_TC_EN_ATS)) { 143769a9ae48STomasz Jeznach enable_pri = false; 143869a9ae48STomasz Jeznach fault = RISCV_IOMMU_FQ_CAUSE_TTYPE_BLOCKED; 143969a9ae48STomasz Jeznach goto done; 144069a9ae48STomasz Jeznach } 144169a9ae48STomasz Jeznach } 144269a9ae48STomasz Jeznach 1443fa622855SJason Chien iot = riscv_iommu_iot_lookup(ctx, iot_cache, iotlb->iova, transtag); 14449d085a1cSTomasz Jeznach perm = iot ? iot->perm : IOMMU_NONE; 14459d085a1cSTomasz Jeznach if (perm != IOMMU_NONE) { 14469d085a1cSTomasz Jeznach iotlb->translated_addr = PPN_PHYS(iot->phys); 14479d085a1cSTomasz Jeznach iotlb->addr_mask = ~TARGET_PAGE_MASK; 14489d085a1cSTomasz Jeznach iotlb->perm = perm; 14499d085a1cSTomasz Jeznach fault = 0; 14509d085a1cSTomasz Jeznach goto done; 14519d085a1cSTomasz Jeznach } 14529d085a1cSTomasz Jeznach 145311ecf24cSTomasz Jeznach riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_TLB_MISS); 145411ecf24cSTomasz Jeznach 14550c54acb8STomasz Jeznach /* Translate using device directory / page table information. */ 14560c54acb8STomasz Jeznach fault = riscv_iommu_spa_fetch(s, ctx, iotlb); 14570c54acb8STomasz Jeznach 14589d085a1cSTomasz Jeznach if (!fault && iotlb->target_as == &s->trap_as) { 14599d085a1cSTomasz Jeznach /* Do not cache trapped MSI translations */ 14609d085a1cSTomasz Jeznach goto done; 14619d085a1cSTomasz Jeznach } 14629d085a1cSTomasz Jeznach 14639d085a1cSTomasz Jeznach /* 14649d085a1cSTomasz Jeznach * We made an implementation choice to not cache identity-mapped 14659d085a1cSTomasz Jeznach * translations, as allowed by the specification, to avoid 14669d085a1cSTomasz Jeznach * translation cache evictions for other devices sharing the 14679d085a1cSTomasz Jeznach * IOMMU hardware model. 14689d085a1cSTomasz Jeznach */ 14699d085a1cSTomasz Jeznach if (!fault && iotlb->translated_addr != iotlb->iova && enable_cache) { 14709d085a1cSTomasz Jeznach iot = g_new0(RISCVIOMMUEntry, 1); 14719d085a1cSTomasz Jeznach iot->iova = PPN_DOWN(iotlb->iova); 14729d085a1cSTomasz Jeznach iot->phys = PPN_DOWN(iotlb->translated_addr); 14739d085a1cSTomasz Jeznach iot->gscid = get_field(ctx->gatp, RISCV_IOMMU_DC_IOHGATP_GSCID); 14749d085a1cSTomasz Jeznach iot->pscid = get_field(ctx->ta, RISCV_IOMMU_DC_TA_PSCID); 14759d085a1cSTomasz Jeznach iot->perm = iotlb->perm; 1476fa622855SJason Chien iot->tag = transtag; 14779d085a1cSTomasz Jeznach riscv_iommu_iot_update(s, iot_cache, iot); 14789d085a1cSTomasz Jeznach } 14799d085a1cSTomasz Jeznach 14809d085a1cSTomasz Jeznach done: 14819d085a1cSTomasz Jeznach g_hash_table_unref(iot_cache); 14829d085a1cSTomasz Jeznach 14830c54acb8STomasz Jeznach if (enable_pri && fault) { 14840c54acb8STomasz Jeznach struct riscv_iommu_pq_record pr = {0}; 14850c54acb8STomasz Jeznach if (enable_pid) { 14860c54acb8STomasz Jeznach pr.hdr = set_field(RISCV_IOMMU_PREQ_HDR_PV, 14870c54acb8STomasz Jeznach RISCV_IOMMU_PREQ_HDR_PID, ctx->process_id); 14880c54acb8STomasz Jeznach } 14890c54acb8STomasz Jeznach pr.hdr = set_field(pr.hdr, RISCV_IOMMU_PREQ_HDR_DID, ctx->devid); 14900c54acb8STomasz Jeznach pr.payload = (iotlb->iova & TARGET_PAGE_MASK) | 14910c54acb8STomasz Jeznach RISCV_IOMMU_PREQ_PAYLOAD_M; 14920c54acb8STomasz Jeznach riscv_iommu_pri(s, &pr); 14930c54acb8STomasz Jeznach return fault; 14940c54acb8STomasz Jeznach } 14950c54acb8STomasz Jeznach 14960c54acb8STomasz Jeznach if (fault) { 149769a9ae48STomasz Jeznach unsigned ttype = RISCV_IOMMU_FQ_TTYPE_PCIE_ATS_REQ; 14980c54acb8STomasz Jeznach 14990c54acb8STomasz Jeznach if (iotlb->perm & IOMMU_RW) { 15000c54acb8STomasz Jeznach ttype = RISCV_IOMMU_FQ_TTYPE_UADDR_WR; 150169a9ae48STomasz Jeznach } else if (iotlb->perm & IOMMU_RO) { 15020c54acb8STomasz Jeznach ttype = RISCV_IOMMU_FQ_TTYPE_UADDR_RD; 15030c54acb8STomasz Jeznach } 15040c54acb8STomasz Jeznach 15050c54acb8STomasz Jeznach riscv_iommu_report_fault(s, ctx, ttype, fault, enable_pid, 15060c54acb8STomasz Jeznach iotlb->iova, iotlb->translated_addr); 15070c54acb8STomasz Jeznach return fault; 15080c54acb8STomasz Jeznach } 15090c54acb8STomasz Jeznach 15100c54acb8STomasz Jeznach return 0; 15110c54acb8STomasz Jeznach } 15120c54acb8STomasz Jeznach 15130c54acb8STomasz Jeznach /* IOMMU Command Interface */ 15140c54acb8STomasz Jeznach static MemTxResult riscv_iommu_iofence(RISCVIOMMUState *s, bool notify, 15150c54acb8STomasz Jeznach uint64_t addr, uint32_t data) 15160c54acb8STomasz Jeznach { 15170c54acb8STomasz Jeznach /* 15180c54acb8STomasz Jeznach * ATS processing in this implementation of the IOMMU is synchronous, 15190c54acb8STomasz Jeznach * no need to wait for completions here. 15200c54acb8STomasz Jeznach */ 15210c54acb8STomasz Jeznach if (!notify) { 15220c54acb8STomasz Jeznach return MEMTX_OK; 15230c54acb8STomasz Jeznach } 15240c54acb8STomasz Jeznach 15250c54acb8STomasz Jeznach return dma_memory_write(s->target_as, addr, &data, sizeof(data), 15260c54acb8STomasz Jeznach MEMTXATTRS_UNSPECIFIED); 15270c54acb8STomasz Jeznach } 15280c54acb8STomasz Jeznach 152969a9ae48STomasz Jeznach static void riscv_iommu_ats(RISCVIOMMUState *s, 153069a9ae48STomasz Jeznach struct riscv_iommu_command *cmd, IOMMUNotifierFlag flag, 153169a9ae48STomasz Jeznach IOMMUAccessFlags perm, 153269a9ae48STomasz Jeznach void (*trace_fn)(const char *id)) 153369a9ae48STomasz Jeznach { 153469a9ae48STomasz Jeznach RISCVIOMMUSpace *as = NULL; 153569a9ae48STomasz Jeznach IOMMUNotifier *n; 153669a9ae48STomasz Jeznach IOMMUTLBEvent event; 153769a9ae48STomasz Jeznach uint32_t pid; 153869a9ae48STomasz Jeznach uint32_t devid; 153969a9ae48STomasz Jeznach const bool pv = cmd->dword0 & RISCV_IOMMU_CMD_ATS_PV; 154069a9ae48STomasz Jeznach 154169a9ae48STomasz Jeznach if (cmd->dword0 & RISCV_IOMMU_CMD_ATS_DSV) { 154269a9ae48STomasz Jeznach /* Use device segment and requester id */ 154369a9ae48STomasz Jeznach devid = get_field(cmd->dword0, 154469a9ae48STomasz Jeznach RISCV_IOMMU_CMD_ATS_DSEG | RISCV_IOMMU_CMD_ATS_RID); 154569a9ae48STomasz Jeznach } else { 154669a9ae48STomasz Jeznach devid = get_field(cmd->dword0, RISCV_IOMMU_CMD_ATS_RID); 154769a9ae48STomasz Jeznach } 154869a9ae48STomasz Jeznach 154969a9ae48STomasz Jeznach pid = get_field(cmd->dword0, RISCV_IOMMU_CMD_ATS_PID); 155069a9ae48STomasz Jeznach 155169a9ae48STomasz Jeznach QLIST_FOREACH(as, &s->spaces, list) { 155269a9ae48STomasz Jeznach if (as->devid == devid) { 155369a9ae48STomasz Jeznach break; 155469a9ae48STomasz Jeznach } 155569a9ae48STomasz Jeznach } 155669a9ae48STomasz Jeznach 155769a9ae48STomasz Jeznach if (!as || !as->notifier) { 155869a9ae48STomasz Jeznach return; 155969a9ae48STomasz Jeznach } 156069a9ae48STomasz Jeznach 156169a9ae48STomasz Jeznach event.type = flag; 156269a9ae48STomasz Jeznach event.entry.perm = perm; 156369a9ae48STomasz Jeznach event.entry.target_as = s->target_as; 156469a9ae48STomasz Jeznach 156569a9ae48STomasz Jeznach IOMMU_NOTIFIER_FOREACH(n, &as->iova_mr) { 156669a9ae48STomasz Jeznach if (!pv || n->iommu_idx == pid) { 156769a9ae48STomasz Jeznach event.entry.iova = n->start; 156869a9ae48STomasz Jeznach event.entry.addr_mask = n->end - n->start; 156969a9ae48STomasz Jeznach trace_fn(as->iova_mr.parent_obj.name); 157069a9ae48STomasz Jeznach memory_region_notify_iommu_one(n, &event); 157169a9ae48STomasz Jeznach } 157269a9ae48STomasz Jeznach } 157369a9ae48STomasz Jeznach } 157469a9ae48STomasz Jeznach 157569a9ae48STomasz Jeznach static void riscv_iommu_ats_inval(RISCVIOMMUState *s, 157669a9ae48STomasz Jeznach struct riscv_iommu_command *cmd) 157769a9ae48STomasz Jeznach { 157869a9ae48STomasz Jeznach return riscv_iommu_ats(s, cmd, IOMMU_NOTIFIER_DEVIOTLB_UNMAP, IOMMU_NONE, 157969a9ae48STomasz Jeznach trace_riscv_iommu_ats_inval); 158069a9ae48STomasz Jeznach } 158169a9ae48STomasz Jeznach 158269a9ae48STomasz Jeznach static void riscv_iommu_ats_prgr(RISCVIOMMUState *s, 158369a9ae48STomasz Jeznach struct riscv_iommu_command *cmd) 158469a9ae48STomasz Jeznach { 158569a9ae48STomasz Jeznach unsigned resp_code = get_field(cmd->dword1, 158669a9ae48STomasz Jeznach RISCV_IOMMU_CMD_ATS_PRGR_RESP_CODE); 158769a9ae48STomasz Jeznach 158869a9ae48STomasz Jeznach /* Using the access flag to carry response code information */ 158969a9ae48STomasz Jeznach IOMMUAccessFlags perm = resp_code ? IOMMU_NONE : IOMMU_RW; 159069a9ae48STomasz Jeznach return riscv_iommu_ats(s, cmd, IOMMU_NOTIFIER_MAP, perm, 159169a9ae48STomasz Jeznach trace_riscv_iommu_ats_prgr); 159269a9ae48STomasz Jeznach } 159369a9ae48STomasz Jeznach 15940c54acb8STomasz Jeznach static void riscv_iommu_process_ddtp(RISCVIOMMUState *s) 15950c54acb8STomasz Jeznach { 15960c54acb8STomasz Jeznach uint64_t old_ddtp = s->ddtp; 15970c54acb8STomasz Jeznach uint64_t new_ddtp = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_DDTP); 15980c54acb8STomasz Jeznach unsigned new_mode = get_field(new_ddtp, RISCV_IOMMU_DDTP_MODE); 15990c54acb8STomasz Jeznach unsigned old_mode = get_field(old_ddtp, RISCV_IOMMU_DDTP_MODE); 16000c54acb8STomasz Jeznach bool ok = false; 16010c54acb8STomasz Jeznach 16020c54acb8STomasz Jeznach /* 16030c54acb8STomasz Jeznach * Check for allowed DDTP.MODE transitions: 16040c54acb8STomasz Jeznach * {OFF, BARE} -> {OFF, BARE, 1LVL, 2LVL, 3LVL} 16050c54acb8STomasz Jeznach * {1LVL, 2LVL, 3LVL} -> {OFF, BARE} 16060c54acb8STomasz Jeznach */ 16070c54acb8STomasz Jeznach if (new_mode == old_mode || 16080c54acb8STomasz Jeznach new_mode == RISCV_IOMMU_DDTP_MODE_OFF || 16090c54acb8STomasz Jeznach new_mode == RISCV_IOMMU_DDTP_MODE_BARE) { 16100c54acb8STomasz Jeznach ok = true; 16110c54acb8STomasz Jeznach } else if (new_mode == RISCV_IOMMU_DDTP_MODE_1LVL || 16120c54acb8STomasz Jeznach new_mode == RISCV_IOMMU_DDTP_MODE_2LVL || 16130c54acb8STomasz Jeznach new_mode == RISCV_IOMMU_DDTP_MODE_3LVL) { 16140c54acb8STomasz Jeznach ok = old_mode == RISCV_IOMMU_DDTP_MODE_OFF || 16150c54acb8STomasz Jeznach old_mode == RISCV_IOMMU_DDTP_MODE_BARE; 16160c54acb8STomasz Jeznach } 16170c54acb8STomasz Jeznach 16180c54acb8STomasz Jeznach if (ok) { 16190c54acb8STomasz Jeznach /* clear reserved and busy bits, report back sanitized version */ 16200c54acb8STomasz Jeznach new_ddtp = set_field(new_ddtp & RISCV_IOMMU_DDTP_PPN, 16210c54acb8STomasz Jeznach RISCV_IOMMU_DDTP_MODE, new_mode); 16220c54acb8STomasz Jeznach } else { 16230c54acb8STomasz Jeznach new_ddtp = old_ddtp; 16240c54acb8STomasz Jeznach } 16250c54acb8STomasz Jeznach s->ddtp = new_ddtp; 16260c54acb8STomasz Jeznach 16270c54acb8STomasz Jeznach riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_DDTP, new_ddtp); 16280c54acb8STomasz Jeznach } 16290c54acb8STomasz Jeznach 16300c54acb8STomasz Jeznach /* Command function and opcode field. */ 16310c54acb8STomasz Jeznach #define RISCV_IOMMU_CMD(func, op) (((func) << 7) | (op)) 16320c54acb8STomasz Jeznach 16330c54acb8STomasz Jeznach static void riscv_iommu_process_cq_tail(RISCVIOMMUState *s) 16340c54acb8STomasz Jeznach { 16350c54acb8STomasz Jeznach struct riscv_iommu_command cmd; 16360c54acb8STomasz Jeznach MemTxResult res; 16370c54acb8STomasz Jeznach dma_addr_t addr; 16380c54acb8STomasz Jeznach uint32_t tail, head, ctrl; 16390c54acb8STomasz Jeznach uint64_t cmd_opcode; 16400c54acb8STomasz Jeznach GHFunc func; 16410c54acb8STomasz Jeznach 16420c54acb8STomasz Jeznach ctrl = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQCSR); 16430c54acb8STomasz Jeznach tail = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQT) & s->cq_mask; 16440c54acb8STomasz Jeznach head = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQH) & s->cq_mask; 16450c54acb8STomasz Jeznach 16460c54acb8STomasz Jeznach /* Check for pending error or queue processing disabled */ 16470c54acb8STomasz Jeznach if (!(ctrl & RISCV_IOMMU_CQCSR_CQON) || 16480c54acb8STomasz Jeznach !!(ctrl & (RISCV_IOMMU_CQCSR_CMD_ILL | RISCV_IOMMU_CQCSR_CQMF))) { 16490c54acb8STomasz Jeznach return; 16500c54acb8STomasz Jeznach } 16510c54acb8STomasz Jeznach 16520c54acb8STomasz Jeznach while (tail != head) { 16530c54acb8STomasz Jeznach addr = s->cq_addr + head * sizeof(cmd); 16540c54acb8STomasz Jeznach res = dma_memory_read(s->target_as, addr, &cmd, sizeof(cmd), 16550c54acb8STomasz Jeznach MEMTXATTRS_UNSPECIFIED); 16560c54acb8STomasz Jeznach 16570c54acb8STomasz Jeznach if (res != MEMTX_OK) { 16580c54acb8STomasz Jeznach riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR, 16590c54acb8STomasz Jeznach RISCV_IOMMU_CQCSR_CQMF, 0); 16600c54acb8STomasz Jeznach goto fault; 16610c54acb8STomasz Jeznach } 16620c54acb8STomasz Jeznach 16630c54acb8STomasz Jeznach trace_riscv_iommu_cmd(s->parent_obj.id, cmd.dword0, cmd.dword1); 16640c54acb8STomasz Jeznach 16650c54acb8STomasz Jeznach cmd_opcode = get_field(cmd.dword0, 16660c54acb8STomasz Jeznach RISCV_IOMMU_CMD_OPCODE | RISCV_IOMMU_CMD_FUNC); 16670c54acb8STomasz Jeznach 16680c54acb8STomasz Jeznach switch (cmd_opcode) { 16690c54acb8STomasz Jeznach case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IOFENCE_FUNC_C, 16700c54acb8STomasz Jeznach RISCV_IOMMU_CMD_IOFENCE_OPCODE): 16710c54acb8STomasz Jeznach res = riscv_iommu_iofence(s, 16720c54acb8STomasz Jeznach cmd.dword0 & RISCV_IOMMU_CMD_IOFENCE_AV, cmd.dword1 << 2, 16730c54acb8STomasz Jeznach get_field(cmd.dword0, RISCV_IOMMU_CMD_IOFENCE_DATA)); 16740c54acb8STomasz Jeznach 16750c54acb8STomasz Jeznach if (res != MEMTX_OK) { 16760c54acb8STomasz Jeznach riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR, 16770c54acb8STomasz Jeznach RISCV_IOMMU_CQCSR_CQMF, 0); 16780c54acb8STomasz Jeznach goto fault; 16790c54acb8STomasz Jeznach } 16800c54acb8STomasz Jeznach break; 16810c54acb8STomasz Jeznach 16820c54acb8STomasz Jeznach case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IOTINVAL_FUNC_GVMA, 16830c54acb8STomasz Jeznach RISCV_IOMMU_CMD_IOTINVAL_OPCODE): 1684fa622855SJason Chien { 1685fa622855SJason Chien bool gv = !!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_GV); 1686fa622855SJason Chien bool av = !!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_AV); 1687fa622855SJason Chien bool pscv = !!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_PSCV); 1688fa622855SJason Chien uint32_t gscid = get_field(cmd.dword0, 1689fa622855SJason Chien RISCV_IOMMU_CMD_IOTINVAL_GSCID); 1690fa622855SJason Chien uint32_t pscid = get_field(cmd.dword0, 1691fa622855SJason Chien RISCV_IOMMU_CMD_IOTINVAL_PSCID); 1692fa622855SJason Chien hwaddr iova = (cmd.dword1 << 2) & TARGET_PAGE_MASK; 1693fa622855SJason Chien 1694fa622855SJason Chien if (pscv) { 16950c54acb8STomasz Jeznach /* illegal command arguments IOTINVAL.GVMA & PSCV == 1 */ 16960c54acb8STomasz Jeznach goto cmd_ill; 16970c54acb8STomasz Jeznach } 1698fa622855SJason Chien 1699fa622855SJason Chien func = riscv_iommu_iot_inval_all; 1700fa622855SJason Chien 1701fa622855SJason Chien if (gv) { 1702fa622855SJason Chien func = (av) ? riscv_iommu_iot_inval_gscid_iova : 1703fa622855SJason Chien riscv_iommu_iot_inval_gscid; 1704fa622855SJason Chien } 1705fa622855SJason Chien 1706fa622855SJason Chien riscv_iommu_iot_inval( 1707fa622855SJason Chien s, func, gscid, pscid, iova, RISCV_IOMMU_TRANS_TAG_VG); 1708fa622855SJason Chien 1709fa622855SJason Chien riscv_iommu_iot_inval( 1710fa622855SJason Chien s, func, gscid, pscid, iova, RISCV_IOMMU_TRANS_TAG_VN); 17110c54acb8STomasz Jeznach break; 1712fa622855SJason Chien } 17130c54acb8STomasz Jeznach 17140c54acb8STomasz Jeznach case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IOTINVAL_FUNC_VMA, 17150c54acb8STomasz Jeznach RISCV_IOMMU_CMD_IOTINVAL_OPCODE): 1716fa622855SJason Chien { 1717fa622855SJason Chien bool gv = !!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_GV); 1718fa622855SJason Chien bool av = !!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_AV); 1719fa622855SJason Chien bool pscv = !!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_PSCV); 1720fa622855SJason Chien uint32_t gscid = get_field(cmd.dword0, 1721fa622855SJason Chien RISCV_IOMMU_CMD_IOTINVAL_GSCID); 1722fa622855SJason Chien uint32_t pscid = get_field(cmd.dword0, 1723fa622855SJason Chien RISCV_IOMMU_CMD_IOTINVAL_PSCID); 1724fa622855SJason Chien hwaddr iova = (cmd.dword1 << 2) & TARGET_PAGE_MASK; 1725fa622855SJason Chien RISCVIOMMUTransTag transtag; 1726fa622855SJason Chien 1727fa622855SJason Chien if (gv) { 1728fa622855SJason Chien transtag = RISCV_IOMMU_TRANS_TAG_VN; 1729fa622855SJason Chien if (pscv) { 1730fa622855SJason Chien func = (av) ? riscv_iommu_iot_inval_gscid_pscid_iova : 1731fa622855SJason Chien riscv_iommu_iot_inval_gscid_pscid; 17329d085a1cSTomasz Jeznach } else { 1733fa622855SJason Chien func = (av) ? riscv_iommu_iot_inval_gscid_iova : 1734fa622855SJason Chien riscv_iommu_iot_inval_gscid; 17359d085a1cSTomasz Jeznach } 1736fa622855SJason Chien } else { 1737fa622855SJason Chien transtag = RISCV_IOMMU_TRANS_TAG_SS; 1738fa622855SJason Chien if (pscv) { 1739fa622855SJason Chien func = (av) ? riscv_iommu_iot_inval_pscid_iova : 1740fa622855SJason Chien riscv_iommu_iot_inval_pscid; 1741fa622855SJason Chien } else { 1742fa622855SJason Chien func = (av) ? riscv_iommu_iot_inval_iova : 1743fa622855SJason Chien riscv_iommu_iot_inval_all; 1744fa622855SJason Chien } 1745fa622855SJason Chien } 1746fa622855SJason Chien 1747fa622855SJason Chien riscv_iommu_iot_inval(s, func, gscid, pscid, iova, transtag); 17480c54acb8STomasz Jeznach break; 1749fa622855SJason Chien } 17500c54acb8STomasz Jeznach 17510c54acb8STomasz Jeznach case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IODIR_FUNC_INVAL_DDT, 17520c54acb8STomasz Jeznach RISCV_IOMMU_CMD_IODIR_OPCODE): 17530c54acb8STomasz Jeznach if (!(cmd.dword0 & RISCV_IOMMU_CMD_IODIR_DV)) { 17540c54acb8STomasz Jeznach /* invalidate all device context cache mappings */ 17550c54acb8STomasz Jeznach func = riscv_iommu_ctx_inval_all; 17560c54acb8STomasz Jeznach } else { 17570c54acb8STomasz Jeznach /* invalidate all device context matching DID */ 17580c54acb8STomasz Jeznach func = riscv_iommu_ctx_inval_devid; 17590c54acb8STomasz Jeznach } 17600c54acb8STomasz Jeznach riscv_iommu_ctx_inval(s, func, 17610c54acb8STomasz Jeznach get_field(cmd.dword0, RISCV_IOMMU_CMD_IODIR_DID), 0); 17620c54acb8STomasz Jeznach break; 17630c54acb8STomasz Jeznach 17640c54acb8STomasz Jeznach case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IODIR_FUNC_INVAL_PDT, 17650c54acb8STomasz Jeznach RISCV_IOMMU_CMD_IODIR_OPCODE): 17660c54acb8STomasz Jeznach if (!(cmd.dword0 & RISCV_IOMMU_CMD_IODIR_DV)) { 17670c54acb8STomasz Jeznach /* illegal command arguments IODIR_PDT & DV == 0 */ 17680c54acb8STomasz Jeznach goto cmd_ill; 17690c54acb8STomasz Jeznach } else { 17700c54acb8STomasz Jeznach func = riscv_iommu_ctx_inval_devid_procid; 17710c54acb8STomasz Jeznach } 17720c54acb8STomasz Jeznach riscv_iommu_ctx_inval(s, func, 17730c54acb8STomasz Jeznach get_field(cmd.dword0, RISCV_IOMMU_CMD_IODIR_DID), 17740c54acb8STomasz Jeznach get_field(cmd.dword0, RISCV_IOMMU_CMD_IODIR_PID)); 17750c54acb8STomasz Jeznach break; 17760c54acb8STomasz Jeznach 177769a9ae48STomasz Jeznach /* ATS commands */ 177869a9ae48STomasz Jeznach case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_ATS_FUNC_INVAL, 177969a9ae48STomasz Jeznach RISCV_IOMMU_CMD_ATS_OPCODE): 178069a9ae48STomasz Jeznach if (!s->enable_ats) { 178169a9ae48STomasz Jeznach goto cmd_ill; 178269a9ae48STomasz Jeznach } 178369a9ae48STomasz Jeznach 178469a9ae48STomasz Jeznach riscv_iommu_ats_inval(s, &cmd); 178569a9ae48STomasz Jeznach break; 178669a9ae48STomasz Jeznach 178769a9ae48STomasz Jeznach case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_ATS_FUNC_PRGR, 178869a9ae48STomasz Jeznach RISCV_IOMMU_CMD_ATS_OPCODE): 178969a9ae48STomasz Jeznach if (!s->enable_ats) { 179069a9ae48STomasz Jeznach goto cmd_ill; 179169a9ae48STomasz Jeznach } 179269a9ae48STomasz Jeznach 179369a9ae48STomasz Jeznach riscv_iommu_ats_prgr(s, &cmd); 179469a9ae48STomasz Jeznach break; 179569a9ae48STomasz Jeznach 17960c54acb8STomasz Jeznach default: 17970c54acb8STomasz Jeznach cmd_ill: 17980c54acb8STomasz Jeznach /* Invalid instruction, do not advance instruction index. */ 17990c54acb8STomasz Jeznach riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR, 18000c54acb8STomasz Jeznach RISCV_IOMMU_CQCSR_CMD_ILL, 0); 18010c54acb8STomasz Jeznach goto fault; 18020c54acb8STomasz Jeznach } 18030c54acb8STomasz Jeznach 18040c54acb8STomasz Jeznach /* Advance and update head pointer after command completes. */ 18050c54acb8STomasz Jeznach head = (head + 1) & s->cq_mask; 18060c54acb8STomasz Jeznach riscv_iommu_reg_set32(s, RISCV_IOMMU_REG_CQH, head); 18070c54acb8STomasz Jeznach } 18080c54acb8STomasz Jeznach return; 18090c54acb8STomasz Jeznach 18100c54acb8STomasz Jeznach fault: 18110c54acb8STomasz Jeznach if (ctrl & RISCV_IOMMU_CQCSR_CIE) { 18120c54acb8STomasz Jeznach riscv_iommu_notify(s, RISCV_IOMMU_INTR_CQ); 18130c54acb8STomasz Jeznach } 18140c54acb8STomasz Jeznach } 18150c54acb8STomasz Jeznach 18160c54acb8STomasz Jeznach static void riscv_iommu_process_cq_control(RISCVIOMMUState *s) 18170c54acb8STomasz Jeznach { 18180c54acb8STomasz Jeznach uint64_t base; 18190c54acb8STomasz Jeznach uint32_t ctrl_set = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQCSR); 18200c54acb8STomasz Jeznach uint32_t ctrl_clr; 18210c54acb8STomasz Jeznach bool enable = !!(ctrl_set & RISCV_IOMMU_CQCSR_CQEN); 18220c54acb8STomasz Jeznach bool active = !!(ctrl_set & RISCV_IOMMU_CQCSR_CQON); 18230c54acb8STomasz Jeznach 18240c54acb8STomasz Jeznach if (enable && !active) { 18250c54acb8STomasz Jeznach base = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_CQB); 18260c54acb8STomasz Jeznach s->cq_mask = (2ULL << get_field(base, RISCV_IOMMU_CQB_LOG2SZ)) - 1; 18270c54acb8STomasz Jeznach s->cq_addr = PPN_PHYS(get_field(base, RISCV_IOMMU_CQB_PPN)); 18280c54acb8STomasz Jeznach stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQT], ~s->cq_mask); 18290c54acb8STomasz Jeznach stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_CQH], 0); 18300c54acb8STomasz Jeznach stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_CQT], 0); 18310c54acb8STomasz Jeznach ctrl_set = RISCV_IOMMU_CQCSR_CQON; 18320c54acb8STomasz Jeznach ctrl_clr = RISCV_IOMMU_CQCSR_BUSY | RISCV_IOMMU_CQCSR_CQMF | 18330c54acb8STomasz Jeznach RISCV_IOMMU_CQCSR_CMD_ILL | RISCV_IOMMU_CQCSR_CMD_TO | 18340c54acb8STomasz Jeznach RISCV_IOMMU_CQCSR_FENCE_W_IP; 18350c54acb8STomasz Jeznach } else if (!enable && active) { 18360c54acb8STomasz Jeznach stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQT], ~0); 18370c54acb8STomasz Jeznach ctrl_set = 0; 18380c54acb8STomasz Jeznach ctrl_clr = RISCV_IOMMU_CQCSR_BUSY | RISCV_IOMMU_CQCSR_CQON; 18390c54acb8STomasz Jeznach } else { 18400c54acb8STomasz Jeznach ctrl_set = 0; 18410c54acb8STomasz Jeznach ctrl_clr = RISCV_IOMMU_CQCSR_BUSY; 18420c54acb8STomasz Jeznach } 18430c54acb8STomasz Jeznach 18440c54acb8STomasz Jeznach riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR, ctrl_set, ctrl_clr); 18450c54acb8STomasz Jeznach } 18460c54acb8STomasz Jeznach 18470c54acb8STomasz Jeznach static void riscv_iommu_process_fq_control(RISCVIOMMUState *s) 18480c54acb8STomasz Jeznach { 18490c54acb8STomasz Jeznach uint64_t base; 18500c54acb8STomasz Jeznach uint32_t ctrl_set = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQCSR); 18510c54acb8STomasz Jeznach uint32_t ctrl_clr; 18520c54acb8STomasz Jeznach bool enable = !!(ctrl_set & RISCV_IOMMU_FQCSR_FQEN); 18530c54acb8STomasz Jeznach bool active = !!(ctrl_set & RISCV_IOMMU_FQCSR_FQON); 18540c54acb8STomasz Jeznach 18550c54acb8STomasz Jeznach if (enable && !active) { 18560c54acb8STomasz Jeznach base = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_FQB); 18570c54acb8STomasz Jeznach s->fq_mask = (2ULL << get_field(base, RISCV_IOMMU_FQB_LOG2SZ)) - 1; 18580c54acb8STomasz Jeznach s->fq_addr = PPN_PHYS(get_field(base, RISCV_IOMMU_FQB_PPN)); 18590c54acb8STomasz Jeznach stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQH], ~s->fq_mask); 18600c54acb8STomasz Jeznach stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_FQH], 0); 18610c54acb8STomasz Jeznach stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_FQT], 0); 18620c54acb8STomasz Jeznach ctrl_set = RISCV_IOMMU_FQCSR_FQON; 18630c54acb8STomasz Jeznach ctrl_clr = RISCV_IOMMU_FQCSR_BUSY | RISCV_IOMMU_FQCSR_FQMF | 18640c54acb8STomasz Jeznach RISCV_IOMMU_FQCSR_FQOF; 18650c54acb8STomasz Jeznach } else if (!enable && active) { 18660c54acb8STomasz Jeznach stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQH], ~0); 18670c54acb8STomasz Jeznach ctrl_set = 0; 18680c54acb8STomasz Jeznach ctrl_clr = RISCV_IOMMU_FQCSR_BUSY | RISCV_IOMMU_FQCSR_FQON; 18690c54acb8STomasz Jeznach } else { 18700c54acb8STomasz Jeznach ctrl_set = 0; 18710c54acb8STomasz Jeznach ctrl_clr = RISCV_IOMMU_FQCSR_BUSY; 18720c54acb8STomasz Jeznach } 18730c54acb8STomasz Jeznach 18740c54acb8STomasz Jeznach riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_FQCSR, ctrl_set, ctrl_clr); 18750c54acb8STomasz Jeznach } 18760c54acb8STomasz Jeznach 18770c54acb8STomasz Jeznach static void riscv_iommu_process_pq_control(RISCVIOMMUState *s) 18780c54acb8STomasz Jeznach { 18790c54acb8STomasz Jeznach uint64_t base; 18800c54acb8STomasz Jeznach uint32_t ctrl_set = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_PQCSR); 18810c54acb8STomasz Jeznach uint32_t ctrl_clr; 18820c54acb8STomasz Jeznach bool enable = !!(ctrl_set & RISCV_IOMMU_PQCSR_PQEN); 18830c54acb8STomasz Jeznach bool active = !!(ctrl_set & RISCV_IOMMU_PQCSR_PQON); 18840c54acb8STomasz Jeznach 18850c54acb8STomasz Jeznach if (enable && !active) { 18860c54acb8STomasz Jeznach base = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_PQB); 18870c54acb8STomasz Jeznach s->pq_mask = (2ULL << get_field(base, RISCV_IOMMU_PQB_LOG2SZ)) - 1; 18880c54acb8STomasz Jeznach s->pq_addr = PPN_PHYS(get_field(base, RISCV_IOMMU_PQB_PPN)); 18890c54acb8STomasz Jeznach stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQH], ~s->pq_mask); 18900c54acb8STomasz Jeznach stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_PQH], 0); 18910c54acb8STomasz Jeznach stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_PQT], 0); 18920c54acb8STomasz Jeznach ctrl_set = RISCV_IOMMU_PQCSR_PQON; 18930c54acb8STomasz Jeznach ctrl_clr = RISCV_IOMMU_PQCSR_BUSY | RISCV_IOMMU_PQCSR_PQMF | 18940c54acb8STomasz Jeznach RISCV_IOMMU_PQCSR_PQOF; 18950c54acb8STomasz Jeznach } else if (!enable && active) { 18960c54acb8STomasz Jeznach stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQH], ~0); 18970c54acb8STomasz Jeznach ctrl_set = 0; 18980c54acb8STomasz Jeznach ctrl_clr = RISCV_IOMMU_PQCSR_BUSY | RISCV_IOMMU_PQCSR_PQON; 18990c54acb8STomasz Jeznach } else { 19000c54acb8STomasz Jeznach ctrl_set = 0; 19010c54acb8STomasz Jeznach ctrl_clr = RISCV_IOMMU_PQCSR_BUSY; 19020c54acb8STomasz Jeznach } 19030c54acb8STomasz Jeznach 19040c54acb8STomasz Jeznach riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_PQCSR, ctrl_set, ctrl_clr); 19050c54acb8STomasz Jeznach } 19060c54acb8STomasz Jeznach 1907a7aa525bSTomasz Jeznach static void riscv_iommu_process_dbg(RISCVIOMMUState *s) 1908a7aa525bSTomasz Jeznach { 1909a7aa525bSTomasz Jeznach uint64_t iova = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_TR_REQ_IOVA); 1910a7aa525bSTomasz Jeznach uint64_t ctrl = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_TR_REQ_CTL); 1911a7aa525bSTomasz Jeznach unsigned devid = get_field(ctrl, RISCV_IOMMU_TR_REQ_CTL_DID); 1912a7aa525bSTomasz Jeznach unsigned pid = get_field(ctrl, RISCV_IOMMU_TR_REQ_CTL_PID); 1913a7aa525bSTomasz Jeznach RISCVIOMMUContext *ctx; 1914a7aa525bSTomasz Jeznach void *ref; 1915a7aa525bSTomasz Jeznach 1916a7aa525bSTomasz Jeznach if (!(ctrl & RISCV_IOMMU_TR_REQ_CTL_GO_BUSY)) { 1917a7aa525bSTomasz Jeznach return; 1918a7aa525bSTomasz Jeznach } 1919a7aa525bSTomasz Jeznach 1920a7aa525bSTomasz Jeznach ctx = riscv_iommu_ctx(s, devid, pid, &ref); 1921a7aa525bSTomasz Jeznach if (ctx == NULL) { 1922a7aa525bSTomasz Jeznach riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_TR_RESPONSE, 1923a7aa525bSTomasz Jeznach RISCV_IOMMU_TR_RESPONSE_FAULT | 1924a7aa525bSTomasz Jeznach (RISCV_IOMMU_FQ_CAUSE_DMA_DISABLED << 10)); 1925a7aa525bSTomasz Jeznach } else { 1926a7aa525bSTomasz Jeznach IOMMUTLBEntry iotlb = { 1927a7aa525bSTomasz Jeznach .iova = iova, 1928a7aa525bSTomasz Jeznach .perm = ctrl & RISCV_IOMMU_TR_REQ_CTL_NW ? IOMMU_RO : IOMMU_RW, 1929a7aa525bSTomasz Jeznach .addr_mask = ~0, 1930a7aa525bSTomasz Jeznach .target_as = NULL, 1931a7aa525bSTomasz Jeznach }; 1932a7aa525bSTomasz Jeznach int fault = riscv_iommu_translate(s, ctx, &iotlb, false); 1933a7aa525bSTomasz Jeznach if (fault) { 1934a7aa525bSTomasz Jeznach iova = RISCV_IOMMU_TR_RESPONSE_FAULT | (((uint64_t) fault) << 10); 1935a7aa525bSTomasz Jeznach } else { 1936a7aa525bSTomasz Jeznach iova = iotlb.translated_addr & ~iotlb.addr_mask; 1937a7aa525bSTomasz Jeznach iova >>= TARGET_PAGE_BITS; 1938a7aa525bSTomasz Jeznach iova &= RISCV_IOMMU_TR_RESPONSE_PPN; 1939a7aa525bSTomasz Jeznach 1940a7aa525bSTomasz Jeznach /* We do not support superpages (> 4kbs) for now */ 1941a7aa525bSTomasz Jeznach iova &= ~RISCV_IOMMU_TR_RESPONSE_S; 1942a7aa525bSTomasz Jeznach } 1943a7aa525bSTomasz Jeznach riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_TR_RESPONSE, iova); 1944a7aa525bSTomasz Jeznach } 1945a7aa525bSTomasz Jeznach 1946a7aa525bSTomasz Jeznach riscv_iommu_reg_mod64(s, RISCV_IOMMU_REG_TR_REQ_CTL, 0, 1947a7aa525bSTomasz Jeznach RISCV_IOMMU_TR_REQ_CTL_GO_BUSY); 1948a7aa525bSTomasz Jeznach riscv_iommu_ctx_put(s, ref); 1949a7aa525bSTomasz Jeznach } 1950a7aa525bSTomasz Jeznach 19510c54acb8STomasz Jeznach typedef void riscv_iommu_process_fn(RISCVIOMMUState *s); 19520c54acb8STomasz Jeznach 19530c54acb8STomasz Jeznach static void riscv_iommu_update_icvec(RISCVIOMMUState *s, uint64_t data) 19540c54acb8STomasz Jeznach { 19550c54acb8STomasz Jeznach uint64_t icvec = 0; 19560c54acb8STomasz Jeznach 19570c54acb8STomasz Jeznach icvec |= MIN(data & RISCV_IOMMU_ICVEC_CIV, 19580c54acb8STomasz Jeznach s->icvec_avail_vectors & RISCV_IOMMU_ICVEC_CIV); 19590c54acb8STomasz Jeznach 19600c54acb8STomasz Jeznach icvec |= MIN(data & RISCV_IOMMU_ICVEC_FIV, 19610c54acb8STomasz Jeznach s->icvec_avail_vectors & RISCV_IOMMU_ICVEC_FIV); 19620c54acb8STomasz Jeznach 19630c54acb8STomasz Jeznach icvec |= MIN(data & RISCV_IOMMU_ICVEC_PMIV, 19640c54acb8STomasz Jeznach s->icvec_avail_vectors & RISCV_IOMMU_ICVEC_PMIV); 19650c54acb8STomasz Jeznach 19660c54acb8STomasz Jeznach icvec |= MIN(data & RISCV_IOMMU_ICVEC_PIV, 19670c54acb8STomasz Jeznach s->icvec_avail_vectors & RISCV_IOMMU_ICVEC_PIV); 19680c54acb8STomasz Jeznach 19690c54acb8STomasz Jeznach trace_riscv_iommu_icvec_write(data, icvec); 19700c54acb8STomasz Jeznach 19710c54acb8STomasz Jeznach riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_ICVEC, icvec); 19720c54acb8STomasz Jeznach } 19730c54acb8STomasz Jeznach 19740c54acb8STomasz Jeznach static void riscv_iommu_update_ipsr(RISCVIOMMUState *s, uint64_t data) 19750c54acb8STomasz Jeznach { 19760c54acb8STomasz Jeznach uint32_t cqcsr, fqcsr, pqcsr; 19770c54acb8STomasz Jeznach uint32_t ipsr_set = 0; 19780c54acb8STomasz Jeznach uint32_t ipsr_clr = 0; 19790c54acb8STomasz Jeznach 19800c54acb8STomasz Jeznach if (data & RISCV_IOMMU_IPSR_CIP) { 19810c54acb8STomasz Jeznach cqcsr = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQCSR); 19820c54acb8STomasz Jeznach 19830c54acb8STomasz Jeznach if (cqcsr & RISCV_IOMMU_CQCSR_CIE && 19840c54acb8STomasz Jeznach (cqcsr & RISCV_IOMMU_CQCSR_FENCE_W_IP || 19850c54acb8STomasz Jeznach cqcsr & RISCV_IOMMU_CQCSR_CMD_ILL || 19860c54acb8STomasz Jeznach cqcsr & RISCV_IOMMU_CQCSR_CMD_TO || 19870c54acb8STomasz Jeznach cqcsr & RISCV_IOMMU_CQCSR_CQMF)) { 19880c54acb8STomasz Jeznach ipsr_set |= RISCV_IOMMU_IPSR_CIP; 19890c54acb8STomasz Jeznach } else { 19900c54acb8STomasz Jeznach ipsr_clr |= RISCV_IOMMU_IPSR_CIP; 19910c54acb8STomasz Jeznach } 19920c54acb8STomasz Jeznach } else { 19930c54acb8STomasz Jeznach ipsr_clr |= RISCV_IOMMU_IPSR_CIP; 19940c54acb8STomasz Jeznach } 19950c54acb8STomasz Jeznach 19960c54acb8STomasz Jeznach if (data & RISCV_IOMMU_IPSR_FIP) { 19970c54acb8STomasz Jeznach fqcsr = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQCSR); 19980c54acb8STomasz Jeznach 19990c54acb8STomasz Jeznach if (fqcsr & RISCV_IOMMU_FQCSR_FIE && 20000c54acb8STomasz Jeznach (fqcsr & RISCV_IOMMU_FQCSR_FQOF || 20010c54acb8STomasz Jeznach fqcsr & RISCV_IOMMU_FQCSR_FQMF)) { 20020c54acb8STomasz Jeznach ipsr_set |= RISCV_IOMMU_IPSR_FIP; 20030c54acb8STomasz Jeznach } else { 20040c54acb8STomasz Jeznach ipsr_clr |= RISCV_IOMMU_IPSR_FIP; 20050c54acb8STomasz Jeznach } 20060c54acb8STomasz Jeznach } else { 20070c54acb8STomasz Jeznach ipsr_clr |= RISCV_IOMMU_IPSR_FIP; 20080c54acb8STomasz Jeznach } 20090c54acb8STomasz Jeznach 20100c54acb8STomasz Jeznach if (data & RISCV_IOMMU_IPSR_PIP) { 20110c54acb8STomasz Jeznach pqcsr = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_PQCSR); 20120c54acb8STomasz Jeznach 20130c54acb8STomasz Jeznach if (pqcsr & RISCV_IOMMU_PQCSR_PIE && 20140c54acb8STomasz Jeznach (pqcsr & RISCV_IOMMU_PQCSR_PQOF || 20150c54acb8STomasz Jeznach pqcsr & RISCV_IOMMU_PQCSR_PQMF)) { 20160c54acb8STomasz Jeznach ipsr_set |= RISCV_IOMMU_IPSR_PIP; 20170c54acb8STomasz Jeznach } else { 20180c54acb8STomasz Jeznach ipsr_clr |= RISCV_IOMMU_IPSR_PIP; 20190c54acb8STomasz Jeznach } 20200c54acb8STomasz Jeznach } else { 20210c54acb8STomasz Jeznach ipsr_clr |= RISCV_IOMMU_IPSR_PIP; 20220c54acb8STomasz Jeznach } 20230c54acb8STomasz Jeznach 20240c54acb8STomasz Jeznach riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_IPSR, ipsr_set, ipsr_clr); 20250c54acb8STomasz Jeznach } 20260c54acb8STomasz Jeznach 20272cf2a6c0STomasz Jeznach static void riscv_iommu_process_hpm_writes(RISCVIOMMUState *s, 20282cf2a6c0STomasz Jeznach uint32_t regb, 20292cf2a6c0STomasz Jeznach bool prev_cy_inh) 20302cf2a6c0STomasz Jeznach { 20312cf2a6c0STomasz Jeznach switch (regb) { 20322cf2a6c0STomasz Jeznach case RISCV_IOMMU_REG_IOCOUNTINH: 20332cf2a6c0STomasz Jeznach riscv_iommu_process_iocntinh_cy(s, prev_cy_inh); 20342cf2a6c0STomasz Jeznach break; 20352cf2a6c0STomasz Jeznach 20362cf2a6c0STomasz Jeznach case RISCV_IOMMU_REG_IOHPMCYCLES: 20372cf2a6c0STomasz Jeznach case RISCV_IOMMU_REG_IOHPMCYCLES + 4: 2038*91dd0bd0STomasz Jeznach riscv_iommu_process_hpmcycle_write(s); 20392cf2a6c0STomasz Jeznach break; 20402cf2a6c0STomasz Jeznach 20412cf2a6c0STomasz Jeznach case RISCV_IOMMU_REG_IOHPMEVT_BASE ... 20422cf2a6c0STomasz Jeznach RISCV_IOMMU_REG_IOHPMEVT(RISCV_IOMMU_IOCOUNT_NUM) + 4: 20432cf2a6c0STomasz Jeznach /* not yet implemented */ 20442cf2a6c0STomasz Jeznach break; 20452cf2a6c0STomasz Jeznach } 20462cf2a6c0STomasz Jeznach } 20472cf2a6c0STomasz Jeznach 20480c54acb8STomasz Jeznach /* 20490c54acb8STomasz Jeznach * Write the resulting value of 'data' for the reg specified 20500c54acb8STomasz Jeznach * by 'reg_addr', after considering read-only/read-write/write-clear 20510c54acb8STomasz Jeznach * bits, in the pointer 'dest'. 20520c54acb8STomasz Jeznach * 20530c54acb8STomasz Jeznach * The result is written in little-endian. 20540c54acb8STomasz Jeznach */ 20550c54acb8STomasz Jeznach static void riscv_iommu_write_reg_val(RISCVIOMMUState *s, 20560c54acb8STomasz Jeznach void *dest, hwaddr reg_addr, 20570c54acb8STomasz Jeznach int size, uint64_t data) 20580c54acb8STomasz Jeznach { 20590c54acb8STomasz Jeznach uint64_t ro = ldn_le_p(&s->regs_ro[reg_addr], size); 20600c54acb8STomasz Jeznach uint64_t wc = ldn_le_p(&s->regs_wc[reg_addr], size); 20610c54acb8STomasz Jeznach uint64_t rw = ldn_le_p(&s->regs_rw[reg_addr], size); 20620c54acb8STomasz Jeznach 20630c54acb8STomasz Jeznach stn_le_p(dest, size, ((rw & ro) | (data & ~ro)) & ~(data & wc)); 20640c54acb8STomasz Jeznach } 20650c54acb8STomasz Jeznach 20660c54acb8STomasz Jeznach static MemTxResult riscv_iommu_mmio_write(void *opaque, hwaddr addr, 20670c54acb8STomasz Jeznach uint64_t data, unsigned size, 20680c54acb8STomasz Jeznach MemTxAttrs attrs) 20690c54acb8STomasz Jeznach { 20700c54acb8STomasz Jeznach riscv_iommu_process_fn *process_fn = NULL; 20710c54acb8STomasz Jeznach RISCVIOMMUState *s = opaque; 20720c54acb8STomasz Jeznach uint32_t regb = addr & ~3; 20730c54acb8STomasz Jeznach uint32_t busy = 0; 20740c54acb8STomasz Jeznach uint64_t val = 0; 20752cf2a6c0STomasz Jeznach bool cy_inh = false; 20760c54acb8STomasz Jeznach 20770c54acb8STomasz Jeznach if ((addr & (size - 1)) != 0) { 20780c54acb8STomasz Jeznach /* Unsupported MMIO alignment or access size */ 20790c54acb8STomasz Jeznach return MEMTX_ERROR; 20800c54acb8STomasz Jeznach } 20810c54acb8STomasz Jeznach 20820c54acb8STomasz Jeznach if (addr + size > RISCV_IOMMU_REG_MSI_CONFIG) { 20830c54acb8STomasz Jeznach /* Unsupported MMIO access location. */ 20840c54acb8STomasz Jeznach return MEMTX_ACCESS_ERROR; 20850c54acb8STomasz Jeznach } 20860c54acb8STomasz Jeznach 20870c54acb8STomasz Jeznach /* Track actionable MMIO write. */ 20880c54acb8STomasz Jeznach switch (regb) { 20890c54acb8STomasz Jeznach case RISCV_IOMMU_REG_DDTP: 20900c54acb8STomasz Jeznach case RISCV_IOMMU_REG_DDTP + 4: 20910c54acb8STomasz Jeznach process_fn = riscv_iommu_process_ddtp; 20920c54acb8STomasz Jeznach regb = RISCV_IOMMU_REG_DDTP; 20930c54acb8STomasz Jeznach busy = RISCV_IOMMU_DDTP_BUSY; 20940c54acb8STomasz Jeznach break; 20950c54acb8STomasz Jeznach 20960c54acb8STomasz Jeznach case RISCV_IOMMU_REG_CQT: 20970c54acb8STomasz Jeznach process_fn = riscv_iommu_process_cq_tail; 20980c54acb8STomasz Jeznach break; 20990c54acb8STomasz Jeznach 21000c54acb8STomasz Jeznach case RISCV_IOMMU_REG_CQCSR: 21010c54acb8STomasz Jeznach process_fn = riscv_iommu_process_cq_control; 21020c54acb8STomasz Jeznach busy = RISCV_IOMMU_CQCSR_BUSY; 21030c54acb8STomasz Jeznach break; 21040c54acb8STomasz Jeznach 21050c54acb8STomasz Jeznach case RISCV_IOMMU_REG_FQCSR: 21060c54acb8STomasz Jeznach process_fn = riscv_iommu_process_fq_control; 21070c54acb8STomasz Jeznach busy = RISCV_IOMMU_FQCSR_BUSY; 21080c54acb8STomasz Jeznach break; 21090c54acb8STomasz Jeznach 21100c54acb8STomasz Jeznach case RISCV_IOMMU_REG_PQCSR: 21110c54acb8STomasz Jeznach process_fn = riscv_iommu_process_pq_control; 21120c54acb8STomasz Jeznach busy = RISCV_IOMMU_PQCSR_BUSY; 21130c54acb8STomasz Jeznach break; 21140c54acb8STomasz Jeznach 21150c54acb8STomasz Jeznach case RISCV_IOMMU_REG_ICVEC: 21160c54acb8STomasz Jeznach case RISCV_IOMMU_REG_IPSR: 21170c54acb8STomasz Jeznach /* 21180c54acb8STomasz Jeznach * ICVEC and IPSR have special read/write procedures. We'll 21190c54acb8STomasz Jeznach * call their respective helpers and exit. 21200c54acb8STomasz Jeznach */ 21210c54acb8STomasz Jeznach riscv_iommu_write_reg_val(s, &val, addr, size, data); 21220c54acb8STomasz Jeznach 21230c54acb8STomasz Jeznach /* 21240c54acb8STomasz Jeznach * 'val' is stored as LE. Switch to host endianess 21250c54acb8STomasz Jeznach * before using it. 21260c54acb8STomasz Jeznach */ 21270c54acb8STomasz Jeznach val = le64_to_cpu(val); 21280c54acb8STomasz Jeznach 21290c54acb8STomasz Jeznach if (regb == RISCV_IOMMU_REG_ICVEC) { 21300c54acb8STomasz Jeznach riscv_iommu_update_icvec(s, val); 21310c54acb8STomasz Jeznach } else { 21320c54acb8STomasz Jeznach riscv_iommu_update_ipsr(s, val); 21330c54acb8STomasz Jeznach } 21340c54acb8STomasz Jeznach 21350c54acb8STomasz Jeznach return MEMTX_OK; 21360c54acb8STomasz Jeznach 2137a7aa525bSTomasz Jeznach case RISCV_IOMMU_REG_TR_REQ_CTL: 2138a7aa525bSTomasz Jeznach process_fn = riscv_iommu_process_dbg; 2139a7aa525bSTomasz Jeznach regb = RISCV_IOMMU_REG_TR_REQ_CTL; 2140a7aa525bSTomasz Jeznach busy = RISCV_IOMMU_TR_REQ_CTL_GO_BUSY; 2141a7aa525bSTomasz Jeznach break; 2142a7aa525bSTomasz Jeznach 21432cf2a6c0STomasz Jeznach case RISCV_IOMMU_REG_IOCOUNTINH: 21442cf2a6c0STomasz Jeznach if (addr != RISCV_IOMMU_REG_IOCOUNTINH) { 21452cf2a6c0STomasz Jeznach break; 21462cf2a6c0STomasz Jeznach } 21472cf2a6c0STomasz Jeznach /* Store previous value of CY bit. */ 21482cf2a6c0STomasz Jeznach cy_inh = !!(riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_IOCOUNTINH) & 21492cf2a6c0STomasz Jeznach RISCV_IOMMU_IOCOUNTINH_CY); 21502cf2a6c0STomasz Jeznach break; 21512cf2a6c0STomasz Jeznach 21522cf2a6c0STomasz Jeznach 21530c54acb8STomasz Jeznach default: 21540c54acb8STomasz Jeznach break; 21550c54acb8STomasz Jeznach } 21560c54acb8STomasz Jeznach 21570c54acb8STomasz Jeznach /* 21580c54acb8STomasz Jeznach * Registers update might be not synchronized with core logic. 21590c54acb8STomasz Jeznach * If system software updates register when relevant BUSY bit 21600c54acb8STomasz Jeznach * is set IOMMU behavior of additional writes to the register 21610c54acb8STomasz Jeznach * is UNSPECIFIED. 21620c54acb8STomasz Jeznach */ 21630c54acb8STomasz Jeznach riscv_iommu_write_reg_val(s, &s->regs_rw[addr], addr, size, data); 21640c54acb8STomasz Jeznach 21650c54acb8STomasz Jeznach /* Busy flag update, MSB 4-byte register. */ 21660c54acb8STomasz Jeznach if (busy) { 21670c54acb8STomasz Jeznach uint32_t rw = ldl_le_p(&s->regs_rw[regb]); 21680c54acb8STomasz Jeznach stl_le_p(&s->regs_rw[regb], rw | busy); 21690c54acb8STomasz Jeznach } 21700c54acb8STomasz Jeznach 21712cf2a6c0STomasz Jeznach /* Process HPM writes and update any internal state if needed. */ 21722cf2a6c0STomasz Jeznach if (regb >= RISCV_IOMMU_REG_IOCOUNTOVF && 21732cf2a6c0STomasz Jeznach regb <= (RISCV_IOMMU_REG_IOHPMEVT(RISCV_IOMMU_IOCOUNT_NUM) + 4)) { 21742cf2a6c0STomasz Jeznach riscv_iommu_process_hpm_writes(s, regb, cy_inh); 21752cf2a6c0STomasz Jeznach } 21762cf2a6c0STomasz Jeznach 21770c54acb8STomasz Jeznach if (process_fn) { 21780c54acb8STomasz Jeznach process_fn(s); 21790c54acb8STomasz Jeznach } 21800c54acb8STomasz Jeznach 21810c54acb8STomasz Jeznach return MEMTX_OK; 21820c54acb8STomasz Jeznach } 21830c54acb8STomasz Jeznach 21840c54acb8STomasz Jeznach static MemTxResult riscv_iommu_mmio_read(void *opaque, hwaddr addr, 21850c54acb8STomasz Jeznach uint64_t *data, unsigned size, MemTxAttrs attrs) 21860c54acb8STomasz Jeznach { 21870c54acb8STomasz Jeznach RISCVIOMMUState *s = opaque; 21880c54acb8STomasz Jeznach uint64_t val = -1; 21890c54acb8STomasz Jeznach uint8_t *ptr; 21900c54acb8STomasz Jeznach 21910c54acb8STomasz Jeznach if ((addr & (size - 1)) != 0) { 21920c54acb8STomasz Jeznach /* Unsupported MMIO alignment. */ 21930c54acb8STomasz Jeznach return MEMTX_ERROR; 21940c54acb8STomasz Jeznach } 21950c54acb8STomasz Jeznach 21960c54acb8STomasz Jeznach if (addr + size > RISCV_IOMMU_REG_MSI_CONFIG) { 21970c54acb8STomasz Jeznach return MEMTX_ACCESS_ERROR; 21980c54acb8STomasz Jeznach } 21990c54acb8STomasz Jeznach 22004faea7e0STomasz Jeznach /* Compute cycle register value. */ 22014faea7e0STomasz Jeznach if ((addr & ~7) == RISCV_IOMMU_REG_IOHPMCYCLES) { 22024faea7e0STomasz Jeznach val = riscv_iommu_hpmcycle_read(s); 22034faea7e0STomasz Jeznach ptr = (uint8_t *)&val + (addr & 7); 22044faea7e0STomasz Jeznach } else if ((addr & ~3) == RISCV_IOMMU_REG_IOCOUNTOVF) { 22054faea7e0STomasz Jeznach /* 22064faea7e0STomasz Jeznach * Software can read RISCV_IOMMU_REG_IOCOUNTOVF before timer 22074faea7e0STomasz Jeznach * callback completes. In which case CY_OF bit in 22084faea7e0STomasz Jeznach * RISCV_IOMMU_IOHPMCYCLES_OVF would be 0. Here we take the 22094faea7e0STomasz Jeznach * CY_OF bit state from RISCV_IOMMU_REG_IOHPMCYCLES register as 22104faea7e0STomasz Jeznach * it's not dependent over the timer callback and is computed 22114faea7e0STomasz Jeznach * from cycle overflow. 22124faea7e0STomasz Jeznach */ 22134faea7e0STomasz Jeznach val = ldq_le_p(&s->regs_rw[addr]); 22144faea7e0STomasz Jeznach val |= (riscv_iommu_hpmcycle_read(s) & RISCV_IOMMU_IOHPMCYCLES_OVF) 22154faea7e0STomasz Jeznach ? RISCV_IOMMU_IOCOUNTOVF_CY 22164faea7e0STomasz Jeznach : 0; 22174faea7e0STomasz Jeznach ptr = (uint8_t *)&val + (addr & 3); 22184faea7e0STomasz Jeznach } else { 22190c54acb8STomasz Jeznach ptr = &s->regs_rw[addr]; 22204faea7e0STomasz Jeznach } 22214faea7e0STomasz Jeznach 22220c54acb8STomasz Jeznach val = ldn_le_p(ptr, size); 22230c54acb8STomasz Jeznach 22240c54acb8STomasz Jeznach *data = val; 22250c54acb8STomasz Jeznach 22260c54acb8STomasz Jeznach return MEMTX_OK; 22270c54acb8STomasz Jeznach } 22280c54acb8STomasz Jeznach 22290c54acb8STomasz Jeznach static const MemoryRegionOps riscv_iommu_mmio_ops = { 22300c54acb8STomasz Jeznach .read_with_attrs = riscv_iommu_mmio_read, 22310c54acb8STomasz Jeznach .write_with_attrs = riscv_iommu_mmio_write, 22320c54acb8STomasz Jeznach .endianness = DEVICE_NATIVE_ENDIAN, 22330c54acb8STomasz Jeznach .impl = { 22340c54acb8STomasz Jeznach .min_access_size = 4, 22350c54acb8STomasz Jeznach .max_access_size = 8, 22360c54acb8STomasz Jeznach .unaligned = false, 22370c54acb8STomasz Jeznach }, 22380c54acb8STomasz Jeznach .valid = { 22390c54acb8STomasz Jeznach .min_access_size = 4, 22400c54acb8STomasz Jeznach .max_access_size = 8, 22410c54acb8STomasz Jeznach } 22420c54acb8STomasz Jeznach }; 22430c54acb8STomasz Jeznach 22440c54acb8STomasz Jeznach /* 22450c54acb8STomasz Jeznach * Translations matching MSI pattern check are redirected to "riscv-iommu-trap" 22460c54acb8STomasz Jeznach * memory region as untranslated address, for additional MSI/MRIF interception 22470c54acb8STomasz Jeznach * by IOMMU interrupt remapping implementation. 22480c54acb8STomasz Jeznach * Note: Device emulation code generating an MSI is expected to provide a valid 22490c54acb8STomasz Jeznach * memory transaction attributes with requested_id set. 22500c54acb8STomasz Jeznach */ 22510c54acb8STomasz Jeznach static MemTxResult riscv_iommu_trap_write(void *opaque, hwaddr addr, 22520c54acb8STomasz Jeznach uint64_t data, unsigned size, MemTxAttrs attrs) 22530c54acb8STomasz Jeznach { 22540c54acb8STomasz Jeznach RISCVIOMMUState* s = (RISCVIOMMUState *)opaque; 22550c54acb8STomasz Jeznach RISCVIOMMUContext *ctx; 22560c54acb8STomasz Jeznach MemTxResult res; 22570c54acb8STomasz Jeznach void *ref; 22580c54acb8STomasz Jeznach uint32_t devid = attrs.requester_id; 22590c54acb8STomasz Jeznach 22600c54acb8STomasz Jeznach if (attrs.unspecified) { 22610c54acb8STomasz Jeznach return MEMTX_ACCESS_ERROR; 22620c54acb8STomasz Jeznach } 22630c54acb8STomasz Jeznach 22640c54acb8STomasz Jeznach /* FIXME: PCIe bus remapping for attached endpoints. */ 22650c54acb8STomasz Jeznach devid |= s->bus << 8; 22660c54acb8STomasz Jeznach 22670c54acb8STomasz Jeznach ctx = riscv_iommu_ctx(s, devid, 0, &ref); 22680c54acb8STomasz Jeznach if (ctx == NULL) { 22690c54acb8STomasz Jeznach res = MEMTX_ACCESS_ERROR; 22700c54acb8STomasz Jeznach } else { 22710c54acb8STomasz Jeznach res = riscv_iommu_msi_write(s, ctx, addr, data, size, attrs); 22720c54acb8STomasz Jeznach } 22730c54acb8STomasz Jeznach riscv_iommu_ctx_put(s, ref); 22740c54acb8STomasz Jeznach return res; 22750c54acb8STomasz Jeznach } 22760c54acb8STomasz Jeznach 22770c54acb8STomasz Jeznach static MemTxResult riscv_iommu_trap_read(void *opaque, hwaddr addr, 22780c54acb8STomasz Jeznach uint64_t *data, unsigned size, MemTxAttrs attrs) 22790c54acb8STomasz Jeznach { 22800c54acb8STomasz Jeznach return MEMTX_ACCESS_ERROR; 22810c54acb8STomasz Jeznach } 22820c54acb8STomasz Jeznach 22830c54acb8STomasz Jeznach static const MemoryRegionOps riscv_iommu_trap_ops = { 22840c54acb8STomasz Jeznach .read_with_attrs = riscv_iommu_trap_read, 22850c54acb8STomasz Jeznach .write_with_attrs = riscv_iommu_trap_write, 22860c54acb8STomasz Jeznach .endianness = DEVICE_LITTLE_ENDIAN, 22870c54acb8STomasz Jeznach .impl = { 22880c54acb8STomasz Jeznach .min_access_size = 4, 22890c54acb8STomasz Jeznach .max_access_size = 8, 22900c54acb8STomasz Jeznach .unaligned = true, 22910c54acb8STomasz Jeznach }, 22920c54acb8STomasz Jeznach .valid = { 22930c54acb8STomasz Jeznach .min_access_size = 4, 22940c54acb8STomasz Jeznach .max_access_size = 8, 22950c54acb8STomasz Jeznach } 22960c54acb8STomasz Jeznach }; 22970c54acb8STomasz Jeznach 2298d13346d1SDaniel Henrique Barboza void riscv_iommu_set_cap_igs(RISCVIOMMUState *s, riscv_iommu_igs_mode mode) 2299d13346d1SDaniel Henrique Barboza { 2300d13346d1SDaniel Henrique Barboza s->cap = set_field(s->cap, RISCV_IOMMU_CAP_IGS, mode); 2301d13346d1SDaniel Henrique Barboza } 2302d13346d1SDaniel Henrique Barboza 23034876e6f7SDaniel Henrique Barboza static void riscv_iommu_instance_init(Object *obj) 23044876e6f7SDaniel Henrique Barboza { 23054876e6f7SDaniel Henrique Barboza RISCVIOMMUState *s = RISCV_IOMMU(obj); 23064876e6f7SDaniel Henrique Barboza 23074876e6f7SDaniel Henrique Barboza /* Enable translation debug interface */ 23084876e6f7SDaniel Henrique Barboza s->cap = RISCV_IOMMU_CAP_DBG; 23094876e6f7SDaniel Henrique Barboza 23104876e6f7SDaniel Henrique Barboza /* Report QEMU target physical address space limits */ 23114876e6f7SDaniel Henrique Barboza s->cap = set_field(s->cap, RISCV_IOMMU_CAP_PAS, 23124876e6f7SDaniel Henrique Barboza TARGET_PHYS_ADDR_SPACE_BITS); 23134876e6f7SDaniel Henrique Barboza 23144876e6f7SDaniel Henrique Barboza /* TODO: method to report supported PID bits */ 23154876e6f7SDaniel Henrique Barboza s->pid_bits = 8; /* restricted to size of MemTxAttrs.pid */ 23164876e6f7SDaniel Henrique Barboza s->cap |= RISCV_IOMMU_CAP_PD8; 23174876e6f7SDaniel Henrique Barboza 23184876e6f7SDaniel Henrique Barboza /* register storage */ 23194876e6f7SDaniel Henrique Barboza s->regs_rw = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE); 23204876e6f7SDaniel Henrique Barboza s->regs_ro = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE); 23214876e6f7SDaniel Henrique Barboza s->regs_wc = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE); 23224876e6f7SDaniel Henrique Barboza 23234876e6f7SDaniel Henrique Barboza /* Mark all registers read-only */ 23244876e6f7SDaniel Henrique Barboza memset(s->regs_ro, 0xff, RISCV_IOMMU_REG_SIZE); 23254876e6f7SDaniel Henrique Barboza 23264876e6f7SDaniel Henrique Barboza /* Device translation context cache */ 23274876e6f7SDaniel Henrique Barboza s->ctx_cache = g_hash_table_new_full(riscv_iommu_ctx_hash, 23284876e6f7SDaniel Henrique Barboza riscv_iommu_ctx_equal, 23294876e6f7SDaniel Henrique Barboza g_free, NULL); 23304876e6f7SDaniel Henrique Barboza 23314876e6f7SDaniel Henrique Barboza s->iot_cache = g_hash_table_new_full(riscv_iommu_iot_hash, 23324876e6f7SDaniel Henrique Barboza riscv_iommu_iot_equal, 23334876e6f7SDaniel Henrique Barboza g_free, NULL); 23344876e6f7SDaniel Henrique Barboza 23354876e6f7SDaniel Henrique Barboza s->iommus.le_next = NULL; 23364876e6f7SDaniel Henrique Barboza s->iommus.le_prev = NULL; 23374876e6f7SDaniel Henrique Barboza QLIST_INIT(&s->spaces); 23384876e6f7SDaniel Henrique Barboza } 23394876e6f7SDaniel Henrique Barboza 23400c54acb8STomasz Jeznach static void riscv_iommu_realize(DeviceState *dev, Error **errp) 23410c54acb8STomasz Jeznach { 23420c54acb8STomasz Jeznach RISCVIOMMUState *s = RISCV_IOMMU(dev); 23430c54acb8STomasz Jeznach 23444876e6f7SDaniel Henrique Barboza s->cap |= s->version & RISCV_IOMMU_CAP_VERSION; 23450c54acb8STomasz Jeznach if (s->enable_msi) { 23460c54acb8STomasz Jeznach s->cap |= RISCV_IOMMU_CAP_MSI_FLAT | RISCV_IOMMU_CAP_MSI_MRIF; 23470c54acb8STomasz Jeznach } 234869a9ae48STomasz Jeznach if (s->enable_ats) { 234969a9ae48STomasz Jeznach s->cap |= RISCV_IOMMU_CAP_ATS; 235069a9ae48STomasz Jeznach } 23510c54acb8STomasz Jeznach if (s->enable_s_stage) { 23520c54acb8STomasz Jeznach s->cap |= RISCV_IOMMU_CAP_SV32 | RISCV_IOMMU_CAP_SV39 | 23530c54acb8STomasz Jeznach RISCV_IOMMU_CAP_SV48 | RISCV_IOMMU_CAP_SV57; 23540c54acb8STomasz Jeznach } 23550c54acb8STomasz Jeznach if (s->enable_g_stage) { 23560c54acb8STomasz Jeznach s->cap |= RISCV_IOMMU_CAP_SV32X4 | RISCV_IOMMU_CAP_SV39X4 | 23570c54acb8STomasz Jeznach RISCV_IOMMU_CAP_SV48X4 | RISCV_IOMMU_CAP_SV57X4; 23580c54acb8STomasz Jeznach } 23590c54acb8STomasz Jeznach 23600c54acb8STomasz Jeznach /* Out-of-reset translation mode: OFF (DMA disabled) BARE (passthrough) */ 23610c54acb8STomasz Jeznach s->ddtp = set_field(0, RISCV_IOMMU_DDTP_MODE, s->enable_off ? 23620c54acb8STomasz Jeznach RISCV_IOMMU_DDTP_MODE_OFF : RISCV_IOMMU_DDTP_MODE_BARE); 23630c54acb8STomasz Jeznach 23640c54acb8STomasz Jeznach /* 23650c54acb8STomasz Jeznach * Register complete MMIO space, including MSI/PBA registers. 23660c54acb8STomasz Jeznach * Note, PCIDevice implementation will add overlapping MR for MSI/PBA, 23670c54acb8STomasz Jeznach * managed directly by the PCIDevice implementation. 23680c54acb8STomasz Jeznach */ 23690c54acb8STomasz Jeznach memory_region_init_io(&s->regs_mr, OBJECT(dev), &riscv_iommu_mmio_ops, s, 23700c54acb8STomasz Jeznach "riscv-iommu-regs", RISCV_IOMMU_REG_SIZE); 23710c54acb8STomasz Jeznach 23720c54acb8STomasz Jeznach /* Set power-on register state */ 23730c54acb8STomasz Jeznach stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_CAP], s->cap); 23740c54acb8STomasz Jeznach stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_FCTL], 0); 23750c54acb8STomasz Jeznach stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_FCTL], 23760c54acb8STomasz Jeznach ~(RISCV_IOMMU_FCTL_BE | RISCV_IOMMU_FCTL_WSI)); 23770c54acb8STomasz Jeznach stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_DDTP], 23780c54acb8STomasz Jeznach ~(RISCV_IOMMU_DDTP_PPN | RISCV_IOMMU_DDTP_MODE)); 23790c54acb8STomasz Jeznach stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQB], 23800c54acb8STomasz Jeznach ~(RISCV_IOMMU_CQB_LOG2SZ | RISCV_IOMMU_CQB_PPN)); 23810c54acb8STomasz Jeznach stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQB], 23820c54acb8STomasz Jeznach ~(RISCV_IOMMU_FQB_LOG2SZ | RISCV_IOMMU_FQB_PPN)); 23830c54acb8STomasz Jeznach stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQB], 23840c54acb8STomasz Jeznach ~(RISCV_IOMMU_PQB_LOG2SZ | RISCV_IOMMU_PQB_PPN)); 23850c54acb8STomasz Jeznach stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_CQCSR], RISCV_IOMMU_CQCSR_CQMF | 23860c54acb8STomasz Jeznach RISCV_IOMMU_CQCSR_CMD_TO | RISCV_IOMMU_CQCSR_CMD_ILL); 23870c54acb8STomasz Jeznach stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQCSR], RISCV_IOMMU_CQCSR_CQON | 23880c54acb8STomasz Jeznach RISCV_IOMMU_CQCSR_BUSY); 23890c54acb8STomasz Jeznach stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_FQCSR], RISCV_IOMMU_FQCSR_FQMF | 23900c54acb8STomasz Jeznach RISCV_IOMMU_FQCSR_FQOF); 23910c54acb8STomasz Jeznach stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQCSR], RISCV_IOMMU_FQCSR_FQON | 23920c54acb8STomasz Jeznach RISCV_IOMMU_FQCSR_BUSY); 23930c54acb8STomasz Jeznach stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_PQCSR], RISCV_IOMMU_PQCSR_PQMF | 23940c54acb8STomasz Jeznach RISCV_IOMMU_PQCSR_PQOF); 23950c54acb8STomasz Jeznach stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQCSR], RISCV_IOMMU_PQCSR_PQON | 23960c54acb8STomasz Jeznach RISCV_IOMMU_PQCSR_BUSY); 23970c54acb8STomasz Jeznach stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_IPSR], ~0); 23980c54acb8STomasz Jeznach stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_ICVEC], 0); 23990c54acb8STomasz Jeznach stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_DDTP], s->ddtp); 2400a7aa525bSTomasz Jeznach /* If debug registers enabled. */ 2401a7aa525bSTomasz Jeznach if (s->cap & RISCV_IOMMU_CAP_DBG) { 2402a7aa525bSTomasz Jeznach stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_TR_REQ_IOVA], 0); 2403a7aa525bSTomasz Jeznach stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_TR_REQ_CTL], 2404a7aa525bSTomasz Jeznach RISCV_IOMMU_TR_REQ_CTL_GO_BUSY); 2405a7aa525bSTomasz Jeznach } 24060c54acb8STomasz Jeznach 24070c54acb8STomasz Jeznach /* Memory region for downstream access, if specified. */ 24080c54acb8STomasz Jeznach if (s->target_mr) { 24090c54acb8STomasz Jeznach s->target_as = g_new0(AddressSpace, 1); 24100c54acb8STomasz Jeznach address_space_init(s->target_as, s->target_mr, 24110c54acb8STomasz Jeznach "riscv-iommu-downstream"); 24120c54acb8STomasz Jeznach } else { 24130c54acb8STomasz Jeznach /* Fallback to global system memory. */ 24140c54acb8STomasz Jeznach s->target_as = &address_space_memory; 24150c54acb8STomasz Jeznach } 24160c54acb8STomasz Jeznach 24170c54acb8STomasz Jeznach /* Memory region for untranslated MRIF/MSI writes */ 24180c54acb8STomasz Jeznach memory_region_init_io(&s->trap_mr, OBJECT(dev), &riscv_iommu_trap_ops, s, 24190c54acb8STomasz Jeznach "riscv-iommu-trap", ~0ULL); 24200c54acb8STomasz Jeznach address_space_init(&s->trap_as, &s->trap_mr, "riscv-iommu-trap-as"); 242111ecf24cSTomasz Jeznach 242211ecf24cSTomasz Jeznach if (s->cap & RISCV_IOMMU_CAP_HPM) { 2423ffb37df0STomasz Jeznach s->hpm_timer = 2424ffb37df0STomasz Jeznach timer_new_ns(QEMU_CLOCK_VIRTUAL, riscv_iommu_hpm_timer_cb, s); 242511ecf24cSTomasz Jeznach s->hpm_event_ctr_map = g_hash_table_new(g_direct_hash, g_direct_equal); 242611ecf24cSTomasz Jeznach } 24270c54acb8STomasz Jeznach } 24280c54acb8STomasz Jeznach 24290c54acb8STomasz Jeznach static void riscv_iommu_unrealize(DeviceState *dev) 24300c54acb8STomasz Jeznach { 24310c54acb8STomasz Jeznach RISCVIOMMUState *s = RISCV_IOMMU(dev); 24320c54acb8STomasz Jeznach 24339d085a1cSTomasz Jeznach g_hash_table_unref(s->iot_cache); 24340c54acb8STomasz Jeznach g_hash_table_unref(s->ctx_cache); 243511ecf24cSTomasz Jeznach 243611ecf24cSTomasz Jeznach if (s->cap & RISCV_IOMMU_CAP_HPM) { 243711ecf24cSTomasz Jeznach g_hash_table_unref(s->hpm_event_ctr_map); 2438ffb37df0STomasz Jeznach timer_free(s->hpm_timer); 243911ecf24cSTomasz Jeznach } 24400c54acb8STomasz Jeznach } 24410c54acb8STomasz Jeznach 24429afd2671SDaniel Henrique Barboza void riscv_iommu_reset(RISCVIOMMUState *s) 24439afd2671SDaniel Henrique Barboza { 24449afd2671SDaniel Henrique Barboza uint32_t reg_clr; 24459afd2671SDaniel Henrique Barboza int ddtp_mode; 24469afd2671SDaniel Henrique Barboza 24479afd2671SDaniel Henrique Barboza /* 24489afd2671SDaniel Henrique Barboza * Clear DDTP while setting DDTP_mode back to user 24499afd2671SDaniel Henrique Barboza * initial setting. 24509afd2671SDaniel Henrique Barboza */ 24519afd2671SDaniel Henrique Barboza ddtp_mode = s->enable_off ? 24529afd2671SDaniel Henrique Barboza RISCV_IOMMU_DDTP_MODE_OFF : RISCV_IOMMU_DDTP_MODE_BARE; 24539afd2671SDaniel Henrique Barboza s->ddtp = set_field(0, RISCV_IOMMU_DDTP_MODE, ddtp_mode); 24549afd2671SDaniel Henrique Barboza riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_DDTP, s->ddtp); 24559afd2671SDaniel Henrique Barboza 24569afd2671SDaniel Henrique Barboza reg_clr = RISCV_IOMMU_CQCSR_CQEN | RISCV_IOMMU_CQCSR_CIE | 24579afd2671SDaniel Henrique Barboza RISCV_IOMMU_CQCSR_CQON | RISCV_IOMMU_CQCSR_BUSY; 24589afd2671SDaniel Henrique Barboza riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR, 0, reg_clr); 24599afd2671SDaniel Henrique Barboza 24609afd2671SDaniel Henrique Barboza reg_clr = RISCV_IOMMU_FQCSR_FQEN | RISCV_IOMMU_FQCSR_FIE | 24619afd2671SDaniel Henrique Barboza RISCV_IOMMU_FQCSR_FQON | RISCV_IOMMU_FQCSR_BUSY; 24629afd2671SDaniel Henrique Barboza riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_FQCSR, 0, reg_clr); 24639afd2671SDaniel Henrique Barboza 24649afd2671SDaniel Henrique Barboza reg_clr = RISCV_IOMMU_PQCSR_PQEN | RISCV_IOMMU_PQCSR_PIE | 24659afd2671SDaniel Henrique Barboza RISCV_IOMMU_PQCSR_PQON | RISCV_IOMMU_PQCSR_BUSY; 24669afd2671SDaniel Henrique Barboza riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_PQCSR, 0, reg_clr); 24679afd2671SDaniel Henrique Barboza 24689afd2671SDaniel Henrique Barboza riscv_iommu_reg_mod64(s, RISCV_IOMMU_REG_TR_REQ_CTL, 0, 24699afd2671SDaniel Henrique Barboza RISCV_IOMMU_TR_REQ_CTL_GO_BUSY); 24709afd2671SDaniel Henrique Barboza 24719afd2671SDaniel Henrique Barboza riscv_iommu_reg_set32(s, RISCV_IOMMU_REG_IPSR, 0); 24729afd2671SDaniel Henrique Barboza 24739afd2671SDaniel Henrique Barboza g_hash_table_remove_all(s->ctx_cache); 24749afd2671SDaniel Henrique Barboza g_hash_table_remove_all(s->iot_cache); 24759afd2671SDaniel Henrique Barboza } 24769afd2671SDaniel Henrique Barboza 2477766bade2SRichard Henderson static const Property riscv_iommu_properties[] = { 24780c54acb8STomasz Jeznach DEFINE_PROP_UINT32("version", RISCVIOMMUState, version, 24790c54acb8STomasz Jeznach RISCV_IOMMU_SPEC_DOT_VER), 24800c54acb8STomasz Jeznach DEFINE_PROP_UINT32("bus", RISCVIOMMUState, bus, 0x0), 24819d085a1cSTomasz Jeznach DEFINE_PROP_UINT32("ioatc-limit", RISCVIOMMUState, iot_limit, 24829d085a1cSTomasz Jeznach LIMIT_CACHE_IOT), 24830c54acb8STomasz Jeznach DEFINE_PROP_BOOL("intremap", RISCVIOMMUState, enable_msi, TRUE), 248469a9ae48STomasz Jeznach DEFINE_PROP_BOOL("ats", RISCVIOMMUState, enable_ats, TRUE), 24850c54acb8STomasz Jeznach DEFINE_PROP_BOOL("off", RISCVIOMMUState, enable_off, TRUE), 24860c54acb8STomasz Jeznach DEFINE_PROP_BOOL("s-stage", RISCVIOMMUState, enable_s_stage, TRUE), 24870c54acb8STomasz Jeznach DEFINE_PROP_BOOL("g-stage", RISCVIOMMUState, enable_g_stage, TRUE), 24880c54acb8STomasz Jeznach DEFINE_PROP_LINK("downstream-mr", RISCVIOMMUState, target_mr, 24890c54acb8STomasz Jeznach TYPE_MEMORY_REGION, MemoryRegion *), 24900c54acb8STomasz Jeznach }; 24910c54acb8STomasz Jeznach 24920c54acb8STomasz Jeznach static void riscv_iommu_class_init(ObjectClass *klass, void* data) 24930c54acb8STomasz Jeznach { 24940c54acb8STomasz Jeznach DeviceClass *dc = DEVICE_CLASS(klass); 24950c54acb8STomasz Jeznach 24960c54acb8STomasz Jeznach /* internal device for riscv-iommu-{pci/sys}, not user-creatable */ 24970c54acb8STomasz Jeznach dc->user_creatable = false; 24980c54acb8STomasz Jeznach dc->realize = riscv_iommu_realize; 24990c54acb8STomasz Jeznach dc->unrealize = riscv_iommu_unrealize; 25000c54acb8STomasz Jeznach device_class_set_props(dc, riscv_iommu_properties); 25010c54acb8STomasz Jeznach } 25020c54acb8STomasz Jeznach 25030c54acb8STomasz Jeznach static const TypeInfo riscv_iommu_info = { 25040c54acb8STomasz Jeznach .name = TYPE_RISCV_IOMMU, 25050c54acb8STomasz Jeznach .parent = TYPE_DEVICE, 25060c54acb8STomasz Jeznach .instance_size = sizeof(RISCVIOMMUState), 25074876e6f7SDaniel Henrique Barboza .instance_init = riscv_iommu_instance_init, 25080c54acb8STomasz Jeznach .class_init = riscv_iommu_class_init, 25090c54acb8STomasz Jeznach }; 25100c54acb8STomasz Jeznach 25110c54acb8STomasz Jeznach static const char *IOMMU_FLAG_STR[] = { 25120c54acb8STomasz Jeznach "NA", 25130c54acb8STomasz Jeznach "RO", 25140c54acb8STomasz Jeznach "WR", 25150c54acb8STomasz Jeznach "RW", 25160c54acb8STomasz Jeznach }; 25170c54acb8STomasz Jeznach 25180c54acb8STomasz Jeznach /* RISC-V IOMMU Memory Region - Address Translation Space */ 25190c54acb8STomasz Jeznach static IOMMUTLBEntry riscv_iommu_memory_region_translate( 25200c54acb8STomasz Jeznach IOMMUMemoryRegion *iommu_mr, hwaddr addr, 25210c54acb8STomasz Jeznach IOMMUAccessFlags flag, int iommu_idx) 25220c54acb8STomasz Jeznach { 25230c54acb8STomasz Jeznach RISCVIOMMUSpace *as = container_of(iommu_mr, RISCVIOMMUSpace, iova_mr); 25240c54acb8STomasz Jeznach RISCVIOMMUContext *ctx; 25250c54acb8STomasz Jeznach void *ref; 25260c54acb8STomasz Jeznach IOMMUTLBEntry iotlb = { 25270c54acb8STomasz Jeznach .iova = addr, 25280c54acb8STomasz Jeznach .target_as = as->iommu->target_as, 25290c54acb8STomasz Jeznach .addr_mask = ~0ULL, 25300c54acb8STomasz Jeznach .perm = flag, 25310c54acb8STomasz Jeznach }; 25320c54acb8STomasz Jeznach 25330c54acb8STomasz Jeznach ctx = riscv_iommu_ctx(as->iommu, as->devid, iommu_idx, &ref); 25340c54acb8STomasz Jeznach if (ctx == NULL) { 25350c54acb8STomasz Jeznach /* Translation disabled or invalid. */ 25360c54acb8STomasz Jeznach iotlb.addr_mask = 0; 25370c54acb8STomasz Jeznach iotlb.perm = IOMMU_NONE; 25389d085a1cSTomasz Jeznach } else if (riscv_iommu_translate(as->iommu, ctx, &iotlb, true)) { 25390c54acb8STomasz Jeznach /* Translation disabled or fault reported. */ 25400c54acb8STomasz Jeznach iotlb.addr_mask = 0; 25410c54acb8STomasz Jeznach iotlb.perm = IOMMU_NONE; 25420c54acb8STomasz Jeznach } 25430c54acb8STomasz Jeznach 25440c54acb8STomasz Jeznach /* Trace all dma translations with original access flags. */ 25450c54acb8STomasz Jeznach trace_riscv_iommu_dma(as->iommu->parent_obj.id, PCI_BUS_NUM(as->devid), 25460c54acb8STomasz Jeznach PCI_SLOT(as->devid), PCI_FUNC(as->devid), iommu_idx, 25470c54acb8STomasz Jeznach IOMMU_FLAG_STR[flag & IOMMU_RW], iotlb.iova, 25480c54acb8STomasz Jeznach iotlb.translated_addr); 25490c54acb8STomasz Jeznach 25500c54acb8STomasz Jeznach riscv_iommu_ctx_put(as->iommu, ref); 25510c54acb8STomasz Jeznach 25520c54acb8STomasz Jeznach return iotlb; 25530c54acb8STomasz Jeznach } 25540c54acb8STomasz Jeznach 25550c54acb8STomasz Jeznach static int riscv_iommu_memory_region_notify( 25560c54acb8STomasz Jeznach IOMMUMemoryRegion *iommu_mr, IOMMUNotifierFlag old, 25570c54acb8STomasz Jeznach IOMMUNotifierFlag new, Error **errp) 25580c54acb8STomasz Jeznach { 25590c54acb8STomasz Jeznach RISCVIOMMUSpace *as = container_of(iommu_mr, RISCVIOMMUSpace, iova_mr); 25600c54acb8STomasz Jeznach 25610c54acb8STomasz Jeznach if (old == IOMMU_NOTIFIER_NONE) { 25620c54acb8STomasz Jeznach as->notifier = true; 25630c54acb8STomasz Jeznach trace_riscv_iommu_notifier_add(iommu_mr->parent_obj.name); 25640c54acb8STomasz Jeznach } else if (new == IOMMU_NOTIFIER_NONE) { 25650c54acb8STomasz Jeznach as->notifier = false; 25660c54acb8STomasz Jeznach trace_riscv_iommu_notifier_del(iommu_mr->parent_obj.name); 25670c54acb8STomasz Jeznach } 25680c54acb8STomasz Jeznach 25690c54acb8STomasz Jeznach return 0; 25700c54acb8STomasz Jeznach } 25710c54acb8STomasz Jeznach 25720c54acb8STomasz Jeznach static inline bool pci_is_iommu(PCIDevice *pdev) 25730c54acb8STomasz Jeznach { 25740c54acb8STomasz Jeznach return pci_get_word(pdev->config + PCI_CLASS_DEVICE) == 0x0806; 25750c54acb8STomasz Jeznach } 25760c54acb8STomasz Jeznach 25770c54acb8STomasz Jeznach static AddressSpace *riscv_iommu_find_as(PCIBus *bus, void *opaque, int devfn) 25780c54acb8STomasz Jeznach { 25790c54acb8STomasz Jeznach RISCVIOMMUState *s = (RISCVIOMMUState *) opaque; 25800c54acb8STomasz Jeznach PCIDevice *pdev = pci_find_device(bus, pci_bus_num(bus), devfn); 25810c54acb8STomasz Jeznach AddressSpace *as = NULL; 25820c54acb8STomasz Jeznach 25830c54acb8STomasz Jeznach if (pdev && pci_is_iommu(pdev)) { 25840c54acb8STomasz Jeznach return s->target_as; 25850c54acb8STomasz Jeznach } 25860c54acb8STomasz Jeznach 25870c54acb8STomasz Jeznach /* Find first registered IOMMU device */ 25880c54acb8STomasz Jeznach while (s->iommus.le_prev) { 25890c54acb8STomasz Jeznach s = *(s->iommus.le_prev); 25900c54acb8STomasz Jeznach } 25910c54acb8STomasz Jeznach 25920c54acb8STomasz Jeznach /* Find first matching IOMMU */ 25930c54acb8STomasz Jeznach while (s != NULL && as == NULL) { 25940c54acb8STomasz Jeznach as = riscv_iommu_space(s, PCI_BUILD_BDF(pci_bus_num(bus), devfn)); 25950c54acb8STomasz Jeznach s = s->iommus.le_next; 25960c54acb8STomasz Jeznach } 25970c54acb8STomasz Jeznach 25980c54acb8STomasz Jeznach return as ? as : &address_space_memory; 25990c54acb8STomasz Jeznach } 26000c54acb8STomasz Jeznach 26010c54acb8STomasz Jeznach static const PCIIOMMUOps riscv_iommu_ops = { 26020c54acb8STomasz Jeznach .get_address_space = riscv_iommu_find_as, 26030c54acb8STomasz Jeznach }; 26040c54acb8STomasz Jeznach 26050c54acb8STomasz Jeznach void riscv_iommu_pci_setup_iommu(RISCVIOMMUState *iommu, PCIBus *bus, 26060c54acb8STomasz Jeznach Error **errp) 26070c54acb8STomasz Jeznach { 26080c54acb8STomasz Jeznach if (bus->iommu_ops && 26090c54acb8STomasz Jeznach bus->iommu_ops->get_address_space == riscv_iommu_find_as) { 26100c54acb8STomasz Jeznach /* Allow multiple IOMMUs on the same PCIe bus, link known devices */ 26110c54acb8STomasz Jeznach RISCVIOMMUState *last = (RISCVIOMMUState *)bus->iommu_opaque; 26120c54acb8STomasz Jeznach QLIST_INSERT_AFTER(last, iommu, iommus); 26130c54acb8STomasz Jeznach } else if (!bus->iommu_ops && !bus->iommu_opaque) { 26140c54acb8STomasz Jeznach pci_setup_iommu(bus, &riscv_iommu_ops, iommu); 26150c54acb8STomasz Jeznach } else { 26160c54acb8STomasz Jeznach error_setg(errp, "can't register secondary IOMMU for PCI bus #%d", 26170c54acb8STomasz Jeznach pci_bus_num(bus)); 26180c54acb8STomasz Jeznach } 26190c54acb8STomasz Jeznach } 26200c54acb8STomasz Jeznach 26210c54acb8STomasz Jeznach static int riscv_iommu_memory_region_index(IOMMUMemoryRegion *iommu_mr, 26220c54acb8STomasz Jeznach MemTxAttrs attrs) 26230c54acb8STomasz Jeznach { 26240c54acb8STomasz Jeznach return attrs.unspecified ? RISCV_IOMMU_NOPROCID : (int)attrs.pid; 26250c54acb8STomasz Jeznach } 26260c54acb8STomasz Jeznach 26270c54acb8STomasz Jeznach static int riscv_iommu_memory_region_index_len(IOMMUMemoryRegion *iommu_mr) 26280c54acb8STomasz Jeznach { 26290c54acb8STomasz Jeznach RISCVIOMMUSpace *as = container_of(iommu_mr, RISCVIOMMUSpace, iova_mr); 26300c54acb8STomasz Jeznach return 1 << as->iommu->pid_bits; 26310c54acb8STomasz Jeznach } 26320c54acb8STomasz Jeznach 26330c54acb8STomasz Jeznach static void riscv_iommu_memory_region_init(ObjectClass *klass, void *data) 26340c54acb8STomasz Jeznach { 26350c54acb8STomasz Jeznach IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); 26360c54acb8STomasz Jeznach 26370c54acb8STomasz Jeznach imrc->translate = riscv_iommu_memory_region_translate; 26380c54acb8STomasz Jeznach imrc->notify_flag_changed = riscv_iommu_memory_region_notify; 26390c54acb8STomasz Jeznach imrc->attrs_to_index = riscv_iommu_memory_region_index; 26400c54acb8STomasz Jeznach imrc->num_indexes = riscv_iommu_memory_region_index_len; 26410c54acb8STomasz Jeznach } 26420c54acb8STomasz Jeznach 26430c54acb8STomasz Jeznach static const TypeInfo riscv_iommu_memory_region_info = { 26440c54acb8STomasz Jeznach .parent = TYPE_IOMMU_MEMORY_REGION, 26450c54acb8STomasz Jeznach .name = TYPE_RISCV_IOMMU_MEMORY_REGION, 26460c54acb8STomasz Jeznach .class_init = riscv_iommu_memory_region_init, 26470c54acb8STomasz Jeznach }; 26480c54acb8STomasz Jeznach 26490c54acb8STomasz Jeznach static void riscv_iommu_register_mr_types(void) 26500c54acb8STomasz Jeznach { 26510c54acb8STomasz Jeznach type_register_static(&riscv_iommu_memory_region_info); 26520c54acb8STomasz Jeznach type_register_static(&riscv_iommu_info); 26530c54acb8STomasz Jeznach } 26540c54acb8STomasz Jeznach 26550c54acb8STomasz Jeznach type_init(riscv_iommu_register_mr_types); 2656