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