xref: /qemu/hw/riscv/riscv-iommu.c (revision a7aa525b93c3f7a847cd2185b71aef97a17ec3d5)
10c54acb8STomasz Jeznach /*
20c54acb8STomasz Jeznach  * QEMU emulation of an RISC-V IOMMU
30c54acb8STomasz Jeznach  *
40c54acb8STomasz Jeznach  * Copyright (C) 2021-2023, Rivos Inc.
50c54acb8STomasz Jeznach  *
60c54acb8STomasz Jeznach  * This program is free software; you can redistribute it and/or modify it
70c54acb8STomasz Jeznach  * under the terms and conditions of the GNU General Public License,
80c54acb8STomasz Jeznach  * version 2 or later, as published by the Free Software Foundation.
90c54acb8STomasz Jeznach  *
100c54acb8STomasz Jeznach  * This program is distributed in the hope that it will be useful,
110c54acb8STomasz Jeznach  * but WITHOUT ANY WARRANTY; without even the implied warranty of
120c54acb8STomasz Jeznach  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
130c54acb8STomasz Jeznach  * GNU General Public License for more details.
140c54acb8STomasz Jeznach  *
150c54acb8STomasz Jeznach  * You should have received a copy of the GNU General Public License along
160c54acb8STomasz Jeznach  * with this program; if not, see <http://www.gnu.org/licenses/>.
170c54acb8STomasz Jeznach  */
180c54acb8STomasz Jeznach 
190c54acb8STomasz Jeznach #include "qemu/osdep.h"
200c54acb8STomasz Jeznach #include "qom/object.h"
210c54acb8STomasz Jeznach #include "hw/pci/pci_bus.h"
220c54acb8STomasz Jeznach #include "hw/pci/pci_device.h"
230c54acb8STomasz Jeznach #include "hw/qdev-properties.h"
240c54acb8STomasz Jeznach #include "hw/riscv/riscv_hart.h"
250c54acb8STomasz Jeznach #include "migration/vmstate.h"
260c54acb8STomasz Jeznach #include "qapi/error.h"
270c54acb8STomasz Jeznach #include "qemu/timer.h"
280c54acb8STomasz Jeznach 
290c54acb8STomasz Jeznach #include "cpu_bits.h"
300c54acb8STomasz Jeznach #include "riscv-iommu.h"
310c54acb8STomasz Jeznach #include "riscv-iommu-bits.h"
320c54acb8STomasz Jeznach #include "trace.h"
330c54acb8STomasz Jeznach 
340c54acb8STomasz Jeznach #define LIMIT_CACHE_CTX               (1U << 7)
350c54acb8STomasz Jeznach #define LIMIT_CACHE_IOT               (1U << 20)
360c54acb8STomasz Jeznach 
370c54acb8STomasz Jeznach /* Physical page number coversions */
380c54acb8STomasz Jeznach #define PPN_PHYS(ppn)                 ((ppn) << TARGET_PAGE_BITS)
390c54acb8STomasz Jeznach #define PPN_DOWN(phy)                 ((phy) >> TARGET_PAGE_BITS)
400c54acb8STomasz Jeznach 
410c54acb8STomasz Jeznach typedef struct RISCVIOMMUContext RISCVIOMMUContext;
420c54acb8STomasz Jeznach typedef struct RISCVIOMMUEntry RISCVIOMMUEntry;
430c54acb8STomasz Jeznach 
440c54acb8STomasz Jeznach /* Device assigned I/O address space */
450c54acb8STomasz Jeznach struct RISCVIOMMUSpace {
460c54acb8STomasz Jeznach     IOMMUMemoryRegion iova_mr;  /* IOVA memory region for attached device */
470c54acb8STomasz Jeznach     AddressSpace iova_as;       /* IOVA address space for attached device */
480c54acb8STomasz Jeznach     RISCVIOMMUState *iommu;     /* Managing IOMMU device state */
490c54acb8STomasz Jeznach     uint32_t devid;             /* Requester identifier, AKA device_id */
500c54acb8STomasz Jeznach     bool notifier;              /* IOMMU unmap notifier enabled */
510c54acb8STomasz Jeznach     QLIST_ENTRY(RISCVIOMMUSpace) list;
520c54acb8STomasz Jeznach };
530c54acb8STomasz Jeznach 
540c54acb8STomasz Jeznach /* Device translation context state. */
550c54acb8STomasz Jeznach struct RISCVIOMMUContext {
560c54acb8STomasz Jeznach     uint64_t devid:24;          /* Requester Id, AKA device_id */
570c54acb8STomasz Jeznach     uint64_t process_id:20;     /* Process ID. PASID for PCIe */
580c54acb8STomasz Jeznach     uint64_t tc;                /* Translation Control */
590c54acb8STomasz Jeznach     uint64_t ta;                /* Translation Attributes */
600c54acb8STomasz Jeznach     uint64_t satp;              /* S-Stage address translation and protection */
610c54acb8STomasz Jeznach     uint64_t gatp;              /* G-Stage address translation and protection */
620c54acb8STomasz Jeznach     uint64_t msi_addr_mask;     /* MSI filtering - address mask */
630c54acb8STomasz Jeznach     uint64_t msi_addr_pattern;  /* MSI filtering - address pattern */
640c54acb8STomasz Jeznach     uint64_t msiptp;            /* MSI redirection page table pointer */
650c54acb8STomasz Jeznach };
660c54acb8STomasz Jeznach 
679d085a1cSTomasz Jeznach /* Address translation cache entry */
689d085a1cSTomasz Jeznach struct RISCVIOMMUEntry {
699d085a1cSTomasz Jeznach     uint64_t iova:44;           /* IOVA Page Number */
709d085a1cSTomasz Jeznach     uint64_t pscid:20;          /* Process Soft-Context identifier */
719d085a1cSTomasz Jeznach     uint64_t phys:44;           /* Physical Page Number */
729d085a1cSTomasz Jeznach     uint64_t gscid:16;          /* Guest Soft-Context identifier */
739d085a1cSTomasz Jeznach     uint64_t perm:2;            /* IOMMU_RW flags */
749d085a1cSTomasz Jeznach };
759d085a1cSTomasz Jeznach 
760c54acb8STomasz Jeznach /* IOMMU index for transactions without process_id specified. */
770c54acb8STomasz Jeznach #define RISCV_IOMMU_NOPROCID 0
780c54acb8STomasz Jeznach 
790c54acb8STomasz Jeznach static uint8_t riscv_iommu_get_icvec_vector(uint32_t icvec, uint32_t vec_type)
800c54acb8STomasz Jeznach {
810c54acb8STomasz Jeznach     switch (vec_type) {
820c54acb8STomasz Jeznach     case RISCV_IOMMU_INTR_CQ:
830c54acb8STomasz Jeznach         return icvec & RISCV_IOMMU_ICVEC_CIV;
840c54acb8STomasz Jeznach     case RISCV_IOMMU_INTR_FQ:
850c54acb8STomasz Jeznach         return (icvec & RISCV_IOMMU_ICVEC_FIV) >> 4;
860c54acb8STomasz Jeznach     case RISCV_IOMMU_INTR_PM:
870c54acb8STomasz Jeznach         return (icvec & RISCV_IOMMU_ICVEC_PMIV) >> 8;
880c54acb8STomasz Jeznach     case RISCV_IOMMU_INTR_PQ:
890c54acb8STomasz Jeznach         return (icvec & RISCV_IOMMU_ICVEC_PIV) >> 12;
900c54acb8STomasz Jeznach     default:
910c54acb8STomasz Jeznach         g_assert_not_reached();
920c54acb8STomasz Jeznach     }
930c54acb8STomasz Jeznach }
940c54acb8STomasz Jeznach 
950c54acb8STomasz Jeznach static void riscv_iommu_notify(RISCVIOMMUState *s, int vec_type)
960c54acb8STomasz Jeznach {
970c54acb8STomasz Jeznach     const uint32_t fctl = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FCTL);
980c54acb8STomasz Jeznach     uint32_t ipsr, icvec, vector;
990c54acb8STomasz Jeznach 
1000c54acb8STomasz Jeznach     if (fctl & RISCV_IOMMU_FCTL_WSI || !s->notify) {
1010c54acb8STomasz Jeznach         return;
1020c54acb8STomasz Jeznach     }
1030c54acb8STomasz Jeznach 
1040c54acb8STomasz Jeznach     icvec = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_ICVEC);
1050c54acb8STomasz Jeznach     ipsr = riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_IPSR, (1 << vec_type), 0);
1060c54acb8STomasz Jeznach 
1070c54acb8STomasz Jeznach     if (!(ipsr & (1 << vec_type))) {
1080c54acb8STomasz Jeznach         vector = riscv_iommu_get_icvec_vector(icvec, vec_type);
1090c54acb8STomasz Jeznach         s->notify(s, vector);
1100c54acb8STomasz Jeznach         trace_riscv_iommu_notify_int_vector(vec_type, vector);
1110c54acb8STomasz Jeznach     }
1120c54acb8STomasz Jeznach }
1130c54acb8STomasz Jeznach 
1140c54acb8STomasz Jeznach static void riscv_iommu_fault(RISCVIOMMUState *s,
1150c54acb8STomasz Jeznach                               struct riscv_iommu_fq_record *ev)
1160c54acb8STomasz Jeznach {
1170c54acb8STomasz Jeznach     uint32_t ctrl = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQCSR);
1180c54acb8STomasz Jeznach     uint32_t head = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQH) & s->fq_mask;
1190c54acb8STomasz Jeznach     uint32_t tail = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQT) & s->fq_mask;
1200c54acb8STomasz Jeznach     uint32_t next = (tail + 1) & s->fq_mask;
1210c54acb8STomasz Jeznach     uint32_t devid = get_field(ev->hdr, RISCV_IOMMU_FQ_HDR_DID);
1220c54acb8STomasz Jeznach 
1230c54acb8STomasz Jeznach     trace_riscv_iommu_flt(s->parent_obj.id, PCI_BUS_NUM(devid), PCI_SLOT(devid),
1240c54acb8STomasz Jeznach                           PCI_FUNC(devid), ev->hdr, ev->iotval);
1250c54acb8STomasz Jeznach 
1260c54acb8STomasz Jeznach     if (!(ctrl & RISCV_IOMMU_FQCSR_FQON) ||
1270c54acb8STomasz Jeznach         !!(ctrl & (RISCV_IOMMU_FQCSR_FQOF | RISCV_IOMMU_FQCSR_FQMF))) {
1280c54acb8STomasz Jeznach         return;
1290c54acb8STomasz Jeznach     }
1300c54acb8STomasz Jeznach 
1310c54acb8STomasz Jeznach     if (head == next) {
1320c54acb8STomasz Jeznach         riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_FQCSR,
1330c54acb8STomasz Jeznach                               RISCV_IOMMU_FQCSR_FQOF, 0);
1340c54acb8STomasz Jeznach     } else {
1350c54acb8STomasz Jeznach         dma_addr_t addr = s->fq_addr + tail * sizeof(*ev);
1360c54acb8STomasz Jeznach         if (dma_memory_write(s->target_as, addr, ev, sizeof(*ev),
1370c54acb8STomasz Jeznach                              MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
1380c54acb8STomasz Jeznach             riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_FQCSR,
1390c54acb8STomasz Jeznach                                   RISCV_IOMMU_FQCSR_FQMF, 0);
1400c54acb8STomasz Jeznach         } else {
1410c54acb8STomasz Jeznach             riscv_iommu_reg_set32(s, RISCV_IOMMU_REG_FQT, next);
1420c54acb8STomasz Jeznach         }
1430c54acb8STomasz Jeznach     }
1440c54acb8STomasz Jeznach 
1450c54acb8STomasz Jeznach     if (ctrl & RISCV_IOMMU_FQCSR_FIE) {
1460c54acb8STomasz Jeznach         riscv_iommu_notify(s, RISCV_IOMMU_INTR_FQ);
1470c54acb8STomasz Jeznach     }
1480c54acb8STomasz Jeznach }
1490c54acb8STomasz Jeznach 
1500c54acb8STomasz Jeznach static void riscv_iommu_pri(RISCVIOMMUState *s,
1510c54acb8STomasz Jeznach     struct riscv_iommu_pq_record *pr)
1520c54acb8STomasz Jeznach {
1530c54acb8STomasz Jeznach     uint32_t ctrl = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_PQCSR);
1540c54acb8STomasz Jeznach     uint32_t head = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_PQH) & s->pq_mask;
1550c54acb8STomasz Jeznach     uint32_t tail = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_PQT) & s->pq_mask;
1560c54acb8STomasz Jeznach     uint32_t next = (tail + 1) & s->pq_mask;
1570c54acb8STomasz Jeznach     uint32_t devid = get_field(pr->hdr, RISCV_IOMMU_PREQ_HDR_DID);
1580c54acb8STomasz Jeznach 
1590c54acb8STomasz Jeznach     trace_riscv_iommu_pri(s->parent_obj.id, PCI_BUS_NUM(devid), PCI_SLOT(devid),
1600c54acb8STomasz Jeznach                           PCI_FUNC(devid), pr->payload);
1610c54acb8STomasz Jeznach 
1620c54acb8STomasz Jeznach     if (!(ctrl & RISCV_IOMMU_PQCSR_PQON) ||
1630c54acb8STomasz Jeznach         !!(ctrl & (RISCV_IOMMU_PQCSR_PQOF | RISCV_IOMMU_PQCSR_PQMF))) {
1640c54acb8STomasz Jeznach         return;
1650c54acb8STomasz Jeznach     }
1660c54acb8STomasz Jeznach 
1670c54acb8STomasz Jeznach     if (head == next) {
1680c54acb8STomasz Jeznach         riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_PQCSR,
1690c54acb8STomasz Jeznach                               RISCV_IOMMU_PQCSR_PQOF, 0);
1700c54acb8STomasz Jeznach     } else {
1710c54acb8STomasz Jeznach         dma_addr_t addr = s->pq_addr + tail * sizeof(*pr);
1720c54acb8STomasz Jeznach         if (dma_memory_write(s->target_as, addr, pr, sizeof(*pr),
1730c54acb8STomasz Jeznach                              MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
1740c54acb8STomasz Jeznach             riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_PQCSR,
1750c54acb8STomasz Jeznach                                   RISCV_IOMMU_PQCSR_PQMF, 0);
1760c54acb8STomasz Jeznach         } else {
1770c54acb8STomasz Jeznach             riscv_iommu_reg_set32(s, RISCV_IOMMU_REG_PQT, next);
1780c54acb8STomasz Jeznach         }
1790c54acb8STomasz Jeznach     }
1800c54acb8STomasz Jeznach 
1810c54acb8STomasz Jeznach     if (ctrl & RISCV_IOMMU_PQCSR_PIE) {
1820c54acb8STomasz Jeznach         riscv_iommu_notify(s, RISCV_IOMMU_INTR_PQ);
1830c54acb8STomasz Jeznach     }
1840c54acb8STomasz Jeznach }
1850c54acb8STomasz Jeznach 
1860c54acb8STomasz Jeznach /* Portable implementation of pext_u64, bit-mask extraction. */
1870c54acb8STomasz Jeznach static uint64_t _pext_u64(uint64_t val, uint64_t ext)
1880c54acb8STomasz Jeznach {
1890c54acb8STomasz Jeznach     uint64_t ret = 0;
1900c54acb8STomasz Jeznach     uint64_t rot = 1;
1910c54acb8STomasz Jeznach 
1920c54acb8STomasz Jeznach     while (ext) {
1930c54acb8STomasz Jeznach         if (ext & 1) {
1940c54acb8STomasz Jeznach             if (val & 1) {
1950c54acb8STomasz Jeznach                 ret |= rot;
1960c54acb8STomasz Jeznach             }
1970c54acb8STomasz Jeznach             rot <<= 1;
1980c54acb8STomasz Jeznach         }
1990c54acb8STomasz Jeznach         val >>= 1;
2000c54acb8STomasz Jeznach         ext >>= 1;
2010c54acb8STomasz Jeznach     }
2020c54acb8STomasz Jeznach 
2030c54acb8STomasz Jeznach     return ret;
2040c54acb8STomasz Jeznach }
2050c54acb8STomasz Jeznach 
2060c54acb8STomasz Jeznach /* Check if GPA matches MSI/MRIF pattern. */
2070c54acb8STomasz Jeznach static bool riscv_iommu_msi_check(RISCVIOMMUState *s, RISCVIOMMUContext *ctx,
2080c54acb8STomasz Jeznach     dma_addr_t gpa)
2090c54acb8STomasz Jeznach {
2100c54acb8STomasz Jeznach     if (!s->enable_msi) {
2110c54acb8STomasz Jeznach         return false;
2120c54acb8STomasz Jeznach     }
2130c54acb8STomasz Jeznach 
2140c54acb8STomasz Jeznach     if (get_field(ctx->msiptp, RISCV_IOMMU_DC_MSIPTP_MODE) !=
2150c54acb8STomasz Jeznach         RISCV_IOMMU_DC_MSIPTP_MODE_FLAT) {
2160c54acb8STomasz Jeznach         return false; /* Invalid MSI/MRIF mode */
2170c54acb8STomasz Jeznach     }
2180c54acb8STomasz Jeznach 
2190c54acb8STomasz Jeznach     if ((PPN_DOWN(gpa) ^ ctx->msi_addr_pattern) & ~ctx->msi_addr_mask) {
2200c54acb8STomasz Jeznach         return false; /* GPA not in MSI range defined by AIA IMSIC rules. */
2210c54acb8STomasz Jeznach     }
2220c54acb8STomasz Jeznach 
2230c54acb8STomasz Jeznach     return true;
2240c54acb8STomasz Jeznach }
2250c54acb8STomasz Jeznach 
2260c54acb8STomasz Jeznach /*
2270c54acb8STomasz Jeznach  * RISCV IOMMU Address Translation Lookup - Page Table Walk
2280c54acb8STomasz Jeznach  *
2290c54acb8STomasz Jeznach  * Note: Code is based on get_physical_address() from target/riscv/cpu_helper.c
2300c54acb8STomasz Jeznach  * Both implementation can be merged into single helper function in future.
2310c54acb8STomasz Jeznach  * Keeping them separate for now, as error reporting and flow specifics are
2320c54acb8STomasz Jeznach  * sufficiently different for separate implementation.
2330c54acb8STomasz Jeznach  *
2340c54acb8STomasz Jeznach  * @s        : IOMMU Device State
2350c54acb8STomasz Jeznach  * @ctx      : Translation context for device id and process address space id.
2360c54acb8STomasz Jeznach  * @iotlb    : translation data: physical address and access mode.
2370c54acb8STomasz Jeznach  * @return   : success or fault cause code.
2380c54acb8STomasz Jeznach  */
2390c54acb8STomasz Jeznach static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx,
2400c54acb8STomasz Jeznach     IOMMUTLBEntry *iotlb)
2410c54acb8STomasz Jeznach {
2420c54acb8STomasz Jeznach     dma_addr_t addr, base;
2430c54acb8STomasz Jeznach     uint64_t satp, gatp, pte;
2440c54acb8STomasz Jeznach     bool en_s, en_g;
2450c54acb8STomasz Jeznach     struct {
2460c54acb8STomasz Jeznach         unsigned char step;
2470c54acb8STomasz Jeznach         unsigned char levels;
2480c54acb8STomasz Jeznach         unsigned char ptidxbits;
2490c54acb8STomasz Jeznach         unsigned char ptesize;
2500c54acb8STomasz Jeznach     } sc[2];
2510c54acb8STomasz Jeznach     /* Translation stage phase */
2520c54acb8STomasz Jeznach     enum {
2530c54acb8STomasz Jeznach         S_STAGE = 0,
2540c54acb8STomasz Jeznach         G_STAGE = 1,
2550c54acb8STomasz Jeznach     } pass;
2560c54acb8STomasz Jeznach     MemTxResult ret;
2570c54acb8STomasz Jeznach 
2580c54acb8STomasz Jeznach     satp = get_field(ctx->satp, RISCV_IOMMU_ATP_MODE_FIELD);
2590c54acb8STomasz Jeznach     gatp = get_field(ctx->gatp, RISCV_IOMMU_ATP_MODE_FIELD);
2600c54acb8STomasz Jeznach 
2610c54acb8STomasz Jeznach     en_s = satp != RISCV_IOMMU_DC_FSC_MODE_BARE;
2620c54acb8STomasz Jeznach     en_g = gatp != RISCV_IOMMU_DC_IOHGATP_MODE_BARE;
2630c54acb8STomasz Jeznach 
2640c54acb8STomasz Jeznach     /*
2650c54acb8STomasz Jeznach      * Early check for MSI address match when IOVA == GPA.
2660c54acb8STomasz Jeznach      * Note that the (!en_s) condition means that the MSI
2670c54acb8STomasz Jeznach      * page table may only be used when guest pages are
2680c54acb8STomasz Jeznach      * mapped using the g-stage page table, whether single-
2690c54acb8STomasz Jeznach      * or two-stage paging is enabled. It's unavoidable though,
2700c54acb8STomasz Jeznach      * because the spec mandates that we do a first-stage
2710c54acb8STomasz Jeznach      * translation before we check the MSI page table, which
2720c54acb8STomasz Jeznach      * means we can't do an early MSI check unless we have
2730c54acb8STomasz Jeznach      * strictly !en_s.
2740c54acb8STomasz Jeznach      */
2750c54acb8STomasz Jeznach     if (!en_s && (iotlb->perm & IOMMU_WO) &&
2760c54acb8STomasz Jeznach         riscv_iommu_msi_check(s, ctx, iotlb->iova)) {
2770c54acb8STomasz Jeznach         iotlb->target_as = &s->trap_as;
2780c54acb8STomasz Jeznach         iotlb->translated_addr = iotlb->iova;
2790c54acb8STomasz Jeznach         iotlb->addr_mask = ~TARGET_PAGE_MASK;
2800c54acb8STomasz Jeznach         return 0;
2810c54acb8STomasz Jeznach     }
2820c54acb8STomasz Jeznach 
2830c54acb8STomasz Jeznach     /* Exit early for pass-through mode. */
2840c54acb8STomasz Jeznach     if (!(en_s || en_g)) {
2850c54acb8STomasz Jeznach         iotlb->translated_addr = iotlb->iova;
2860c54acb8STomasz Jeznach         iotlb->addr_mask = ~TARGET_PAGE_MASK;
2870c54acb8STomasz Jeznach         /* Allow R/W in pass-through mode */
2880c54acb8STomasz Jeznach         iotlb->perm = IOMMU_RW;
2890c54acb8STomasz Jeznach         return 0;
2900c54acb8STomasz Jeznach     }
2910c54acb8STomasz Jeznach 
2920c54acb8STomasz Jeznach     /* S/G translation parameters. */
2930c54acb8STomasz Jeznach     for (pass = 0; pass < 2; pass++) {
2940c54acb8STomasz Jeznach         uint32_t sv_mode;
2950c54acb8STomasz Jeznach 
2960c54acb8STomasz Jeznach         sc[pass].step = 0;
2970c54acb8STomasz Jeznach         if (pass ? (s->fctl & RISCV_IOMMU_FCTL_GXL) :
2980c54acb8STomasz Jeznach             (ctx->tc & RISCV_IOMMU_DC_TC_SXL)) {
2990c54acb8STomasz Jeznach             /* 32bit mode for GXL/SXL == 1 */
3000c54acb8STomasz Jeznach             switch (pass ? gatp : satp) {
3010c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_IOHGATP_MODE_BARE:
3020c54acb8STomasz Jeznach                 sc[pass].levels    = 0;
3030c54acb8STomasz Jeznach                 sc[pass].ptidxbits = 0;
3040c54acb8STomasz Jeznach                 sc[pass].ptesize   = 0;
3050c54acb8STomasz Jeznach                 break;
3060c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_IOHGATP_MODE_SV32X4:
3070c54acb8STomasz Jeznach                 sv_mode = pass ? RISCV_IOMMU_CAP_SV32X4 : RISCV_IOMMU_CAP_SV32;
3080c54acb8STomasz Jeznach                 if (!(s->cap & sv_mode)) {
3090c54acb8STomasz Jeznach                     return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
3100c54acb8STomasz Jeznach                 }
3110c54acb8STomasz Jeznach                 sc[pass].levels    = 2;
3120c54acb8STomasz Jeznach                 sc[pass].ptidxbits = 10;
3130c54acb8STomasz Jeznach                 sc[pass].ptesize   = 4;
3140c54acb8STomasz Jeznach                 break;
3150c54acb8STomasz Jeznach             default:
3160c54acb8STomasz Jeznach                 return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
3170c54acb8STomasz Jeznach             }
3180c54acb8STomasz Jeznach         } else {
3190c54acb8STomasz Jeznach             /* 64bit mode for GXL/SXL == 0 */
3200c54acb8STomasz Jeznach             switch (pass ? gatp : satp) {
3210c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_IOHGATP_MODE_BARE:
3220c54acb8STomasz Jeznach                 sc[pass].levels    = 0;
3230c54acb8STomasz Jeznach                 sc[pass].ptidxbits = 0;
3240c54acb8STomasz Jeznach                 sc[pass].ptesize   = 0;
3250c54acb8STomasz Jeznach                 break;
3260c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_IOHGATP_MODE_SV39X4:
3270c54acb8STomasz Jeznach                 sv_mode = pass ? RISCV_IOMMU_CAP_SV39X4 : RISCV_IOMMU_CAP_SV39;
3280c54acb8STomasz Jeznach                 if (!(s->cap & sv_mode)) {
3290c54acb8STomasz Jeznach                     return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
3300c54acb8STomasz Jeznach                 }
3310c54acb8STomasz Jeznach                 sc[pass].levels    = 3;
3320c54acb8STomasz Jeznach                 sc[pass].ptidxbits = 9;
3330c54acb8STomasz Jeznach                 sc[pass].ptesize   = 8;
3340c54acb8STomasz Jeznach                 break;
3350c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_IOHGATP_MODE_SV48X4:
3360c54acb8STomasz Jeznach                 sv_mode = pass ? RISCV_IOMMU_CAP_SV48X4 : RISCV_IOMMU_CAP_SV48;
3370c54acb8STomasz Jeznach                 if (!(s->cap & sv_mode)) {
3380c54acb8STomasz Jeznach                     return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
3390c54acb8STomasz Jeznach                 }
3400c54acb8STomasz Jeznach                 sc[pass].levels    = 4;
3410c54acb8STomasz Jeznach                 sc[pass].ptidxbits = 9;
3420c54acb8STomasz Jeznach                 sc[pass].ptesize   = 8;
3430c54acb8STomasz Jeznach                 break;
3440c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_IOHGATP_MODE_SV57X4:
3450c54acb8STomasz Jeznach                 sv_mode = pass ? RISCV_IOMMU_CAP_SV57X4 : RISCV_IOMMU_CAP_SV57;
3460c54acb8STomasz Jeznach                 if (!(s->cap & sv_mode)) {
3470c54acb8STomasz Jeznach                     return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
3480c54acb8STomasz Jeznach                 }
3490c54acb8STomasz Jeznach                 sc[pass].levels    = 5;
3500c54acb8STomasz Jeznach                 sc[pass].ptidxbits = 9;
3510c54acb8STomasz Jeznach                 sc[pass].ptesize   = 8;
3520c54acb8STomasz Jeznach                 break;
3530c54acb8STomasz Jeznach             default:
3540c54acb8STomasz Jeznach                 return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
3550c54acb8STomasz Jeznach             }
3560c54acb8STomasz Jeznach         }
3570c54acb8STomasz Jeznach     };
3580c54acb8STomasz Jeznach 
3590c54acb8STomasz Jeznach     /* S/G stages translation tables root pointers */
3600c54acb8STomasz Jeznach     gatp = PPN_PHYS(get_field(ctx->gatp, RISCV_IOMMU_ATP_PPN_FIELD));
3610c54acb8STomasz Jeznach     satp = PPN_PHYS(get_field(ctx->satp, RISCV_IOMMU_ATP_PPN_FIELD));
3620c54acb8STomasz Jeznach     addr = (en_s && en_g) ? satp : iotlb->iova;
3630c54acb8STomasz Jeznach     base = en_g ? gatp : satp;
3640c54acb8STomasz Jeznach     pass = en_g ? G_STAGE : S_STAGE;
3650c54acb8STomasz Jeznach 
3660c54acb8STomasz Jeznach     do {
3670c54acb8STomasz Jeznach         const unsigned widened = (pass && !sc[pass].step) ? 2 : 0;
3680c54acb8STomasz Jeznach         const unsigned va_bits = widened + sc[pass].ptidxbits;
3690c54acb8STomasz Jeznach         const unsigned va_skip = TARGET_PAGE_BITS + sc[pass].ptidxbits *
3700c54acb8STomasz Jeznach                                  (sc[pass].levels - 1 - sc[pass].step);
3710c54acb8STomasz Jeznach         const unsigned idx = (addr >> va_skip) & ((1 << va_bits) - 1);
3720c54acb8STomasz Jeznach         const dma_addr_t pte_addr = base + idx * sc[pass].ptesize;
3730c54acb8STomasz Jeznach         const bool ade =
3740c54acb8STomasz Jeznach             ctx->tc & (pass ? RISCV_IOMMU_DC_TC_GADE : RISCV_IOMMU_DC_TC_SADE);
3750c54acb8STomasz Jeznach 
3760c54acb8STomasz Jeznach         /* Address range check before first level lookup */
3770c54acb8STomasz Jeznach         if (!sc[pass].step) {
3780c54acb8STomasz Jeznach             const uint64_t va_mask = (1ULL << (va_skip + va_bits)) - 1;
3790c54acb8STomasz Jeznach             if ((addr & va_mask) != addr) {
3800c54acb8STomasz Jeznach                 return RISCV_IOMMU_FQ_CAUSE_DMA_DISABLED;
3810c54acb8STomasz Jeznach             }
3820c54acb8STomasz Jeznach         }
3830c54acb8STomasz Jeznach 
3840c54acb8STomasz Jeznach         /* Read page table entry */
3850c54acb8STomasz Jeznach         if (sc[pass].ptesize == 4) {
3860c54acb8STomasz Jeznach             uint32_t pte32 = 0;
3870c54acb8STomasz Jeznach             ret = ldl_le_dma(s->target_as, pte_addr, &pte32,
3880c54acb8STomasz Jeznach                              MEMTXATTRS_UNSPECIFIED);
3890c54acb8STomasz Jeznach             pte = pte32;
3900c54acb8STomasz Jeznach         } else {
3910c54acb8STomasz Jeznach             ret = ldq_le_dma(s->target_as, pte_addr, &pte,
3920c54acb8STomasz Jeznach                              MEMTXATTRS_UNSPECIFIED);
3930c54acb8STomasz Jeznach         }
3940c54acb8STomasz Jeznach         if (ret != MEMTX_OK) {
3950c54acb8STomasz Jeznach             return (iotlb->perm & IOMMU_WO) ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT
3960c54acb8STomasz Jeznach                                             : RISCV_IOMMU_FQ_CAUSE_RD_FAULT;
3970c54acb8STomasz Jeznach         }
3980c54acb8STomasz Jeznach 
3990c54acb8STomasz Jeznach         sc[pass].step++;
4000c54acb8STomasz Jeznach         hwaddr ppn = pte >> PTE_PPN_SHIFT;
4010c54acb8STomasz Jeznach 
4020c54acb8STomasz Jeznach         if (!(pte & PTE_V)) {
4030c54acb8STomasz Jeznach             break;                /* Invalid PTE */
4040c54acb8STomasz Jeznach         } else if (!(pte & (PTE_R | PTE_W | PTE_X))) {
4050c54acb8STomasz Jeznach             base = PPN_PHYS(ppn); /* Inner PTE, continue walking */
4060c54acb8STomasz Jeznach         } else if ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) {
4070c54acb8STomasz Jeznach             break;                /* Reserved leaf PTE flags: PTE_W */
4080c54acb8STomasz Jeznach         } else if ((pte & (PTE_R | PTE_W | PTE_X)) == (PTE_W | PTE_X)) {
4090c54acb8STomasz Jeznach             break;                /* Reserved leaf PTE flags: PTE_W + PTE_X */
4100c54acb8STomasz Jeznach         } else if (ppn & ((1ULL << (va_skip - TARGET_PAGE_BITS)) - 1)) {
4110c54acb8STomasz Jeznach             break;                /* Misaligned PPN */
4120c54acb8STomasz Jeznach         } else if ((iotlb->perm & IOMMU_RO) && !(pte & PTE_R)) {
4130c54acb8STomasz Jeznach             break;                /* Read access check failed */
4140c54acb8STomasz Jeznach         } else if ((iotlb->perm & IOMMU_WO) && !(pte & PTE_W)) {
4150c54acb8STomasz Jeznach             break;                /* Write access check failed */
4160c54acb8STomasz Jeznach         } else if ((iotlb->perm & IOMMU_RO) && !ade && !(pte & PTE_A)) {
4170c54acb8STomasz Jeznach             break;                /* Access bit not set */
4180c54acb8STomasz Jeznach         } else if ((iotlb->perm & IOMMU_WO) && !ade && !(pte & PTE_D)) {
4190c54acb8STomasz Jeznach             break;                /* Dirty bit not set */
4200c54acb8STomasz Jeznach         } else {
4210c54acb8STomasz Jeznach             /* Leaf PTE, translation completed. */
4220c54acb8STomasz Jeznach             sc[pass].step = sc[pass].levels;
4230c54acb8STomasz Jeznach             base = PPN_PHYS(ppn) | (addr & ((1ULL << va_skip) - 1));
4240c54acb8STomasz Jeznach             /* Update address mask based on smallest translation granularity */
4250c54acb8STomasz Jeznach             iotlb->addr_mask &= (1ULL << va_skip) - 1;
4260c54acb8STomasz Jeznach             /* Continue with S-Stage translation? */
4270c54acb8STomasz Jeznach             if (pass && sc[0].step != sc[0].levels) {
4280c54acb8STomasz Jeznach                 pass = S_STAGE;
4290c54acb8STomasz Jeznach                 addr = iotlb->iova;
4300c54acb8STomasz Jeznach                 continue;
4310c54acb8STomasz Jeznach             }
4320c54acb8STomasz Jeznach             /* Translation phase completed (GPA or SPA) */
4330c54acb8STomasz Jeznach             iotlb->translated_addr = base;
4340c54acb8STomasz Jeznach             iotlb->perm = (pte & PTE_W) ? ((pte & PTE_R) ? IOMMU_RW : IOMMU_WO)
4350c54acb8STomasz Jeznach                                                          : IOMMU_RO;
4360c54acb8STomasz Jeznach 
4370c54acb8STomasz Jeznach             /* Check MSI GPA address match */
4380c54acb8STomasz Jeznach             if (pass == S_STAGE && (iotlb->perm & IOMMU_WO) &&
4390c54acb8STomasz Jeznach                 riscv_iommu_msi_check(s, ctx, base)) {
4400c54acb8STomasz Jeznach                 /* Trap MSI writes and return GPA address. */
4410c54acb8STomasz Jeznach                 iotlb->target_as = &s->trap_as;
4420c54acb8STomasz Jeznach                 iotlb->addr_mask = ~TARGET_PAGE_MASK;
4430c54acb8STomasz Jeznach                 return 0;
4440c54acb8STomasz Jeznach             }
4450c54acb8STomasz Jeznach 
4460c54acb8STomasz Jeznach             /* Continue with G-Stage translation? */
4470c54acb8STomasz Jeznach             if (!pass && en_g) {
4480c54acb8STomasz Jeznach                 pass = G_STAGE;
4490c54acb8STomasz Jeznach                 addr = base;
4500c54acb8STomasz Jeznach                 base = gatp;
4510c54acb8STomasz Jeznach                 sc[pass].step = 0;
4520c54acb8STomasz Jeznach                 continue;
4530c54acb8STomasz Jeznach             }
4540c54acb8STomasz Jeznach 
4550c54acb8STomasz Jeznach             return 0;
4560c54acb8STomasz Jeznach         }
4570c54acb8STomasz Jeznach 
4580c54acb8STomasz Jeznach         if (sc[pass].step == sc[pass].levels) {
4590c54acb8STomasz Jeznach             break; /* Can't find leaf PTE */
4600c54acb8STomasz Jeznach         }
4610c54acb8STomasz Jeznach 
4620c54acb8STomasz Jeznach         /* Continue with G-Stage translation? */
4630c54acb8STomasz Jeznach         if (!pass && en_g) {
4640c54acb8STomasz Jeznach             pass = G_STAGE;
4650c54acb8STomasz Jeznach             addr = base;
4660c54acb8STomasz Jeznach             base = gatp;
4670c54acb8STomasz Jeznach             sc[pass].step = 0;
4680c54acb8STomasz Jeznach         }
4690c54acb8STomasz Jeznach     } while (1);
4700c54acb8STomasz Jeznach 
4710c54acb8STomasz Jeznach     return (iotlb->perm & IOMMU_WO) ?
4720c54acb8STomasz Jeznach                 (pass ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT_VS :
4730c54acb8STomasz Jeznach                         RISCV_IOMMU_FQ_CAUSE_WR_FAULT_S) :
4740c54acb8STomasz Jeznach                 (pass ? RISCV_IOMMU_FQ_CAUSE_RD_FAULT_VS :
4750c54acb8STomasz Jeznach                         RISCV_IOMMU_FQ_CAUSE_RD_FAULT_S);
4760c54acb8STomasz Jeznach }
4770c54acb8STomasz Jeznach 
4780c54acb8STomasz Jeznach static void riscv_iommu_report_fault(RISCVIOMMUState *s,
4790c54acb8STomasz Jeznach                                      RISCVIOMMUContext *ctx,
4800c54acb8STomasz Jeznach                                      uint32_t fault_type, uint32_t cause,
4810c54acb8STomasz Jeznach                                      bool pv,
4820c54acb8STomasz Jeznach                                      uint64_t iotval, uint64_t iotval2)
4830c54acb8STomasz Jeznach {
4840c54acb8STomasz Jeznach     struct riscv_iommu_fq_record ev = { 0 };
4850c54acb8STomasz Jeznach 
4860c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_DTF) {
4870c54acb8STomasz Jeznach         switch (cause) {
4880c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_DMA_DISABLED:
4890c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_DDT_LOAD_FAULT:
4900c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_DDT_INVALID:
4910c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED:
4920c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_DDT_CORRUPTED:
4930c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_INTERNAL_DP_ERROR:
4940c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT:
4950c54acb8STomasz Jeznach             break;
4960c54acb8STomasz Jeznach         default:
4970c54acb8STomasz Jeznach             /* DTF prevents reporting a fault for this given cause */
4980c54acb8STomasz Jeznach             return;
4990c54acb8STomasz Jeznach         }
5000c54acb8STomasz Jeznach     }
5010c54acb8STomasz Jeznach 
5020c54acb8STomasz Jeznach     ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_CAUSE, cause);
5030c54acb8STomasz Jeznach     ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_TTYPE, fault_type);
5040c54acb8STomasz Jeznach     ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_DID, ctx->devid);
5050c54acb8STomasz Jeznach     ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_PV, true);
5060c54acb8STomasz Jeznach 
5070c54acb8STomasz Jeznach     if (pv) {
5080c54acb8STomasz Jeznach         ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_PID, ctx->process_id);
5090c54acb8STomasz Jeznach     }
5100c54acb8STomasz Jeznach 
5110c54acb8STomasz Jeznach     ev.iotval = iotval;
5120c54acb8STomasz Jeznach     ev.iotval2 = iotval2;
5130c54acb8STomasz Jeznach 
5140c54acb8STomasz Jeznach     riscv_iommu_fault(s, &ev);
5150c54acb8STomasz Jeznach }
5160c54acb8STomasz Jeznach 
5170c54acb8STomasz Jeznach /* Redirect MSI write for given GPA. */
5180c54acb8STomasz Jeznach static MemTxResult riscv_iommu_msi_write(RISCVIOMMUState *s,
5190c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx, uint64_t gpa, uint64_t data,
5200c54acb8STomasz Jeznach     unsigned size, MemTxAttrs attrs)
5210c54acb8STomasz Jeznach {
5220c54acb8STomasz Jeznach     MemTxResult res;
5230c54acb8STomasz Jeznach     dma_addr_t addr;
5240c54acb8STomasz Jeznach     uint64_t intn;
5250c54acb8STomasz Jeznach     uint32_t n190;
5260c54acb8STomasz Jeznach     uint64_t pte[2];
5270c54acb8STomasz Jeznach     int fault_type = RISCV_IOMMU_FQ_TTYPE_UADDR_WR;
5280c54acb8STomasz Jeznach     int cause;
5290c54acb8STomasz Jeznach 
5300c54acb8STomasz Jeznach     /* Interrupt File Number */
5310c54acb8STomasz Jeznach     intn = _pext_u64(PPN_DOWN(gpa), ctx->msi_addr_mask);
5320c54acb8STomasz Jeznach     if (intn >= 256) {
5330c54acb8STomasz Jeznach         /* Interrupt file number out of range */
5340c54acb8STomasz Jeznach         res = MEMTX_ACCESS_ERROR;
5350c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT;
5360c54acb8STomasz Jeznach         goto err;
5370c54acb8STomasz Jeznach     }
5380c54acb8STomasz Jeznach 
5390c54acb8STomasz Jeznach     /* fetch MSI PTE */
5400c54acb8STomasz Jeznach     addr = PPN_PHYS(get_field(ctx->msiptp, RISCV_IOMMU_DC_MSIPTP_PPN));
5410c54acb8STomasz Jeznach     addr = addr | (intn * sizeof(pte));
5420c54acb8STomasz Jeznach     res = dma_memory_read(s->target_as, addr, &pte, sizeof(pte),
5430c54acb8STomasz Jeznach             MEMTXATTRS_UNSPECIFIED);
5440c54acb8STomasz Jeznach     if (res != MEMTX_OK) {
5450c54acb8STomasz Jeznach         if (res == MEMTX_DECODE_ERROR) {
5460c54acb8STomasz Jeznach             cause = RISCV_IOMMU_FQ_CAUSE_MSI_PT_CORRUPTED;
5470c54acb8STomasz Jeznach         } else {
5480c54acb8STomasz Jeznach             cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT;
5490c54acb8STomasz Jeznach         }
5500c54acb8STomasz Jeznach         goto err;
5510c54acb8STomasz Jeznach     }
5520c54acb8STomasz Jeznach 
5530c54acb8STomasz Jeznach     le64_to_cpus(&pte[0]);
5540c54acb8STomasz Jeznach     le64_to_cpus(&pte[1]);
5550c54acb8STomasz Jeznach 
5560c54acb8STomasz Jeznach     if (!(pte[0] & RISCV_IOMMU_MSI_PTE_V) || (pte[0] & RISCV_IOMMU_MSI_PTE_C)) {
5570c54acb8STomasz Jeznach         /*
5580c54acb8STomasz Jeznach          * The spec mentions that: "If msipte.C == 1, then further
5590c54acb8STomasz Jeznach          * processing to interpret the PTE is implementation
5600c54acb8STomasz Jeznach          * defined.". We'll abort with cause = 262 for this
5610c54acb8STomasz Jeznach          * case too.
5620c54acb8STomasz Jeznach          */
5630c54acb8STomasz Jeznach         res = MEMTX_ACCESS_ERROR;
5640c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_INVALID;
5650c54acb8STomasz Jeznach         goto err;
5660c54acb8STomasz Jeznach     }
5670c54acb8STomasz Jeznach 
5680c54acb8STomasz Jeznach     switch (get_field(pte[0], RISCV_IOMMU_MSI_PTE_M)) {
5690c54acb8STomasz Jeznach     case RISCV_IOMMU_MSI_PTE_M_BASIC:
5700c54acb8STomasz Jeznach         /* MSI Pass-through mode */
5710c54acb8STomasz Jeznach         addr = PPN_PHYS(get_field(pte[0], RISCV_IOMMU_MSI_PTE_PPN));
5720c54acb8STomasz Jeznach 
5730c54acb8STomasz Jeznach         trace_riscv_iommu_msi(s->parent_obj.id, PCI_BUS_NUM(ctx->devid),
5740c54acb8STomasz Jeznach                               PCI_SLOT(ctx->devid), PCI_FUNC(ctx->devid),
5750c54acb8STomasz Jeznach                               gpa, addr);
5760c54acb8STomasz Jeznach 
5770c54acb8STomasz Jeznach         res = dma_memory_write(s->target_as, addr, &data, size, attrs);
5780c54acb8STomasz Jeznach         if (res != MEMTX_OK) {
5790c54acb8STomasz Jeznach             cause = RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT;
5800c54acb8STomasz Jeznach             goto err;
5810c54acb8STomasz Jeznach         }
5820c54acb8STomasz Jeznach 
5830c54acb8STomasz Jeznach         return MEMTX_OK;
5840c54acb8STomasz Jeznach     case RISCV_IOMMU_MSI_PTE_M_MRIF:
5850c54acb8STomasz Jeznach         /* MRIF mode, continue. */
5860c54acb8STomasz Jeznach         break;
5870c54acb8STomasz Jeznach     default:
5880c54acb8STomasz Jeznach         res = MEMTX_ACCESS_ERROR;
5890c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_MISCONFIGURED;
5900c54acb8STomasz Jeznach         goto err;
5910c54acb8STomasz Jeznach     }
5920c54acb8STomasz Jeznach 
5930c54acb8STomasz Jeznach     /*
5940c54acb8STomasz Jeznach      * Report an error for interrupt identities exceeding the maximum allowed
5950c54acb8STomasz Jeznach      * for an IMSIC interrupt file (2047) or destination address is not 32-bit
5960c54acb8STomasz Jeznach      * aligned. See IOMMU Specification, Chapter 2.3. MSI page tables.
5970c54acb8STomasz Jeznach      */
5980c54acb8STomasz Jeznach     if ((data > 2047) || (gpa & 3)) {
5990c54acb8STomasz Jeznach         res = MEMTX_ACCESS_ERROR;
6000c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_MISCONFIGURED;
6010c54acb8STomasz Jeznach         goto err;
6020c54acb8STomasz Jeznach     }
6030c54acb8STomasz Jeznach 
6040c54acb8STomasz Jeznach     /* MSI MRIF mode, non atomic pending bit update */
6050c54acb8STomasz Jeznach 
6060c54acb8STomasz Jeznach     /* MRIF pending bit address */
6070c54acb8STomasz Jeznach     addr = get_field(pte[0], RISCV_IOMMU_MSI_PTE_MRIF_ADDR) << 9;
6080c54acb8STomasz Jeznach     addr = addr | ((data & 0x7c0) >> 3);
6090c54acb8STomasz Jeznach 
6100c54acb8STomasz Jeznach     trace_riscv_iommu_msi(s->parent_obj.id, PCI_BUS_NUM(ctx->devid),
6110c54acb8STomasz Jeznach                           PCI_SLOT(ctx->devid), PCI_FUNC(ctx->devid),
6120c54acb8STomasz Jeznach                           gpa, addr);
6130c54acb8STomasz Jeznach 
6140c54acb8STomasz Jeznach     /* MRIF pending bit mask */
6150c54acb8STomasz Jeznach     data = 1ULL << (data & 0x03f);
6160c54acb8STomasz Jeznach     res = dma_memory_read(s->target_as, addr, &intn, sizeof(intn), attrs);
6170c54acb8STomasz Jeznach     if (res != MEMTX_OK) {
6180c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT;
6190c54acb8STomasz Jeznach         goto err;
6200c54acb8STomasz Jeznach     }
6210c54acb8STomasz Jeznach 
6220c54acb8STomasz Jeznach     intn = intn | data;
6230c54acb8STomasz Jeznach     res = dma_memory_write(s->target_as, addr, &intn, sizeof(intn), attrs);
6240c54acb8STomasz Jeznach     if (res != MEMTX_OK) {
6250c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT;
6260c54acb8STomasz Jeznach         goto err;
6270c54acb8STomasz Jeznach     }
6280c54acb8STomasz Jeznach 
6290c54acb8STomasz Jeznach     /* Get MRIF enable bits */
6300c54acb8STomasz Jeznach     addr = addr + sizeof(intn);
6310c54acb8STomasz Jeznach     res = dma_memory_read(s->target_as, addr, &intn, sizeof(intn), attrs);
6320c54acb8STomasz Jeznach     if (res != MEMTX_OK) {
6330c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT;
6340c54acb8STomasz Jeznach         goto err;
6350c54acb8STomasz Jeznach     }
6360c54acb8STomasz Jeznach 
6370c54acb8STomasz Jeznach     if (!(intn & data)) {
6380c54acb8STomasz Jeznach         /* notification disabled, MRIF update completed. */
6390c54acb8STomasz Jeznach         return MEMTX_OK;
6400c54acb8STomasz Jeznach     }
6410c54acb8STomasz Jeznach 
6420c54acb8STomasz Jeznach     /* Send notification message */
6430c54acb8STomasz Jeznach     addr = PPN_PHYS(get_field(pte[1], RISCV_IOMMU_MSI_MRIF_NPPN));
6440c54acb8STomasz Jeznach     n190 = get_field(pte[1], RISCV_IOMMU_MSI_MRIF_NID) |
6450c54acb8STomasz Jeznach           (get_field(pte[1], RISCV_IOMMU_MSI_MRIF_NID_MSB) << 10);
6460c54acb8STomasz Jeznach 
6470c54acb8STomasz Jeznach     res = dma_memory_write(s->target_as, addr, &n190, sizeof(n190), attrs);
6480c54acb8STomasz Jeznach     if (res != MEMTX_OK) {
6490c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT;
6500c54acb8STomasz Jeznach         goto err;
6510c54acb8STomasz Jeznach     }
6520c54acb8STomasz Jeznach 
6530c54acb8STomasz Jeznach     trace_riscv_iommu_mrif_notification(s->parent_obj.id, n190, addr);
6540c54acb8STomasz Jeznach 
6550c54acb8STomasz Jeznach     return MEMTX_OK;
6560c54acb8STomasz Jeznach 
6570c54acb8STomasz Jeznach err:
6580c54acb8STomasz Jeznach     riscv_iommu_report_fault(s, ctx, fault_type, cause,
6590c54acb8STomasz Jeznach                              !!ctx->process_id, 0, 0);
6600c54acb8STomasz Jeznach     return res;
6610c54acb8STomasz Jeznach }
6620c54acb8STomasz Jeznach 
6630c54acb8STomasz Jeznach /*
6640c54acb8STomasz Jeznach  * Check device context configuration as described by the
6650c54acb8STomasz Jeznach  * riscv-iommu spec section "Device-context configuration
6660c54acb8STomasz Jeznach  * checks".
6670c54acb8STomasz Jeznach  */
6680c54acb8STomasz Jeznach static bool riscv_iommu_validate_device_ctx(RISCVIOMMUState *s,
6690c54acb8STomasz Jeznach                                             RISCVIOMMUContext *ctx)
6700c54acb8STomasz Jeznach {
6710c54acb8STomasz Jeznach     uint32_t fsc_mode, msi_mode;
67269a9ae48STomasz Jeznach     uint64_t gatp;
67369a9ae48STomasz Jeznach 
67469a9ae48STomasz Jeznach     if (!(s->cap & RISCV_IOMMU_CAP_ATS) &&
67569a9ae48STomasz Jeznach         (ctx->tc & RISCV_IOMMU_DC_TC_EN_ATS ||
67669a9ae48STomasz Jeznach          ctx->tc & RISCV_IOMMU_DC_TC_EN_PRI ||
67769a9ae48STomasz Jeznach          ctx->tc & RISCV_IOMMU_DC_TC_PRPR)) {
67869a9ae48STomasz Jeznach         return false;
67969a9ae48STomasz Jeznach     }
68069a9ae48STomasz Jeznach 
68169a9ae48STomasz Jeznach     if (!(ctx->tc & RISCV_IOMMU_DC_TC_EN_ATS) &&
68269a9ae48STomasz Jeznach         (ctx->tc & RISCV_IOMMU_DC_TC_T2GPA ||
68369a9ae48STomasz Jeznach          ctx->tc & RISCV_IOMMU_DC_TC_EN_PRI)) {
68469a9ae48STomasz Jeznach         return false;
68569a9ae48STomasz Jeznach     }
6860c54acb8STomasz Jeznach 
6870c54acb8STomasz Jeznach     if (!(ctx->tc & RISCV_IOMMU_DC_TC_EN_PRI) &&
6880c54acb8STomasz Jeznach         ctx->tc & RISCV_IOMMU_DC_TC_PRPR) {
6890c54acb8STomasz Jeznach         return false;
6900c54acb8STomasz Jeznach     }
6910c54acb8STomasz Jeznach 
6920c54acb8STomasz Jeznach     if (!(s->cap & RISCV_IOMMU_CAP_T2GPA) &&
6930c54acb8STomasz Jeznach         ctx->tc & RISCV_IOMMU_DC_TC_T2GPA) {
6940c54acb8STomasz Jeznach         return false;
6950c54acb8STomasz Jeznach     }
6960c54acb8STomasz Jeznach 
6970c54acb8STomasz Jeznach     if (s->cap & RISCV_IOMMU_CAP_MSI_FLAT) {
6980c54acb8STomasz Jeznach         msi_mode = get_field(ctx->msiptp, RISCV_IOMMU_DC_MSIPTP_MODE);
6990c54acb8STomasz Jeznach 
7000c54acb8STomasz Jeznach         if (msi_mode != RISCV_IOMMU_DC_MSIPTP_MODE_OFF &&
7010c54acb8STomasz Jeznach             msi_mode != RISCV_IOMMU_DC_MSIPTP_MODE_FLAT) {
7020c54acb8STomasz Jeznach             return false;
7030c54acb8STomasz Jeznach         }
7040c54acb8STomasz Jeznach     }
7050c54acb8STomasz Jeznach 
70669a9ae48STomasz Jeznach     gatp = get_field(ctx->gatp, RISCV_IOMMU_ATP_MODE_FIELD);
70769a9ae48STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_T2GPA &&
70869a9ae48STomasz Jeznach         gatp == RISCV_IOMMU_DC_IOHGATP_MODE_BARE) {
70969a9ae48STomasz Jeznach         return false;
71069a9ae48STomasz Jeznach     }
71169a9ae48STomasz Jeznach 
7120c54acb8STomasz Jeznach     fsc_mode = get_field(ctx->satp, RISCV_IOMMU_DC_FSC_MODE);
7130c54acb8STomasz Jeznach 
7140c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_PDTV) {
7150c54acb8STomasz Jeznach         switch (fsc_mode) {
7160c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_PDTP_MODE_PD8:
7170c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_PD8)) {
7180c54acb8STomasz Jeznach                 return false;
7190c54acb8STomasz Jeznach             }
7200c54acb8STomasz Jeznach             break;
7210c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_PDTP_MODE_PD17:
7220c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_PD17)) {
7230c54acb8STomasz Jeznach                 return false;
7240c54acb8STomasz Jeznach             }
7250c54acb8STomasz Jeznach             break;
7260c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_PDTP_MODE_PD20:
7270c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_PD20)) {
7280c54acb8STomasz Jeznach                 return false;
7290c54acb8STomasz Jeznach             }
7300c54acb8STomasz Jeznach             break;
7310c54acb8STomasz Jeznach         }
7320c54acb8STomasz Jeznach     } else {
7330c54acb8STomasz Jeznach         /* DC.tc.PDTV is 0 */
7340c54acb8STomasz Jeznach         if (ctx->tc & RISCV_IOMMU_DC_TC_DPE) {
7350c54acb8STomasz Jeznach             return false;
7360c54acb8STomasz Jeznach         }
7370c54acb8STomasz Jeznach 
7380c54acb8STomasz Jeznach         if (ctx->tc & RISCV_IOMMU_DC_TC_SXL) {
7390c54acb8STomasz Jeznach             if (fsc_mode == RISCV_IOMMU_CAP_SV32 &&
7400c54acb8STomasz Jeznach                 !(s->cap & RISCV_IOMMU_CAP_SV32)) {
7410c54acb8STomasz Jeznach                 return false;
7420c54acb8STomasz Jeznach             }
7430c54acb8STomasz Jeznach         } else {
7440c54acb8STomasz Jeznach             switch (fsc_mode) {
7450c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV39:
7460c54acb8STomasz Jeznach                 if (!(s->cap & RISCV_IOMMU_CAP_SV39)) {
7470c54acb8STomasz Jeznach                     return false;
7480c54acb8STomasz Jeznach                 }
7490c54acb8STomasz Jeznach                 break;
7500c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV48:
7510c54acb8STomasz Jeznach                 if (!(s->cap & RISCV_IOMMU_CAP_SV48)) {
7520c54acb8STomasz Jeznach                     return false;
7530c54acb8STomasz Jeznach                 }
7540c54acb8STomasz Jeznach             break;
7550c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57:
7560c54acb8STomasz Jeznach                 if (!(s->cap & RISCV_IOMMU_CAP_SV57)) {
7570c54acb8STomasz Jeznach                     return false;
7580c54acb8STomasz Jeznach                 }
7590c54acb8STomasz Jeznach                 break;
7600c54acb8STomasz Jeznach             }
7610c54acb8STomasz Jeznach         }
7620c54acb8STomasz Jeznach     }
7630c54acb8STomasz Jeznach 
7640c54acb8STomasz Jeznach     /*
7650c54acb8STomasz Jeznach      * CAP_END is always zero (only one endianess). FCTL_BE is
7660c54acb8STomasz Jeznach      * always zero (little-endian accesses). Thus TC_SBE must
7670c54acb8STomasz Jeznach      * always be LE, i.e. zero.
7680c54acb8STomasz Jeznach      */
7690c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_SBE) {
7700c54acb8STomasz Jeznach         return false;
7710c54acb8STomasz Jeznach     }
7720c54acb8STomasz Jeznach 
7730c54acb8STomasz Jeznach     return true;
7740c54acb8STomasz Jeznach }
7750c54acb8STomasz Jeznach 
7760c54acb8STomasz Jeznach /*
7770c54acb8STomasz Jeznach  * Validate process context (PC) according to section
7780c54acb8STomasz Jeznach  * "Process-context configuration checks".
7790c54acb8STomasz Jeznach  */
7800c54acb8STomasz Jeznach static bool riscv_iommu_validate_process_ctx(RISCVIOMMUState *s,
7810c54acb8STomasz Jeznach                                              RISCVIOMMUContext *ctx)
7820c54acb8STomasz Jeznach {
7830c54acb8STomasz Jeznach     uint32_t mode;
7840c54acb8STomasz Jeznach 
7850c54acb8STomasz Jeznach     if (get_field(ctx->ta, RISCV_IOMMU_PC_TA_RESERVED)) {
7860c54acb8STomasz Jeznach         return false;
7870c54acb8STomasz Jeznach     }
7880c54acb8STomasz Jeznach 
7890c54acb8STomasz Jeznach     if (get_field(ctx->satp, RISCV_IOMMU_PC_FSC_RESERVED)) {
7900c54acb8STomasz Jeznach         return false;
7910c54acb8STomasz Jeznach     }
7920c54acb8STomasz Jeznach 
7930c54acb8STomasz Jeznach     mode = get_field(ctx->satp, RISCV_IOMMU_DC_FSC_MODE);
7940c54acb8STomasz Jeznach     switch (mode) {
7950c54acb8STomasz Jeznach     case RISCV_IOMMU_DC_FSC_MODE_BARE:
7960c54acb8STomasz Jeznach     /* sv39 and sv32 modes have the same value (8) */
7970c54acb8STomasz Jeznach     case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV39:
7980c54acb8STomasz Jeznach     case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV48:
7990c54acb8STomasz Jeznach     case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57:
8000c54acb8STomasz Jeznach         break;
8010c54acb8STomasz Jeznach     default:
8020c54acb8STomasz Jeznach         return false;
8030c54acb8STomasz Jeznach     }
8040c54acb8STomasz Jeznach 
8050c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_SXL) {
8060c54acb8STomasz Jeznach         if (mode == RISCV_IOMMU_CAP_SV32 &&
8070c54acb8STomasz Jeznach             !(s->cap & RISCV_IOMMU_CAP_SV32)) {
8080c54acb8STomasz Jeznach                 return false;
8090c54acb8STomasz Jeznach         }
8100c54acb8STomasz Jeznach     } else {
8110c54acb8STomasz Jeznach         switch (mode) {
8120c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV39:
8130c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_SV39)) {
8140c54acb8STomasz Jeznach                 return false;
8150c54acb8STomasz Jeznach             }
8160c54acb8STomasz Jeznach             break;
8170c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV48:
8180c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_SV48)) {
8190c54acb8STomasz Jeznach                 return false;
8200c54acb8STomasz Jeznach             }
8210c54acb8STomasz Jeznach             break;
8220c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57:
8230c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_SV57)) {
8240c54acb8STomasz Jeznach                 return false;
8250c54acb8STomasz Jeznach             }
8260c54acb8STomasz Jeznach             break;
8270c54acb8STomasz Jeznach         }
8280c54acb8STomasz Jeznach     }
8290c54acb8STomasz Jeznach 
8300c54acb8STomasz Jeznach     return true;
8310c54acb8STomasz Jeznach }
8320c54acb8STomasz Jeznach 
8330c54acb8STomasz Jeznach /*
8340c54acb8STomasz Jeznach  * RISC-V IOMMU Device Context Loopkup - Device Directory Tree Walk
8350c54acb8STomasz Jeznach  *
8360c54acb8STomasz Jeznach  * @s         : IOMMU Device State
8370c54acb8STomasz Jeznach  * @ctx       : Device Translation Context with devid and process_id set.
8380c54acb8STomasz Jeznach  * @return    : success or fault code.
8390c54acb8STomasz Jeznach  */
8400c54acb8STomasz Jeznach static int riscv_iommu_ctx_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx)
8410c54acb8STomasz Jeznach {
8420c54acb8STomasz Jeznach     const uint64_t ddtp = s->ddtp;
8430c54acb8STomasz Jeznach     unsigned mode = get_field(ddtp, RISCV_IOMMU_DDTP_MODE);
8440c54acb8STomasz Jeznach     dma_addr_t addr = PPN_PHYS(get_field(ddtp, RISCV_IOMMU_DDTP_PPN));
8450c54acb8STomasz Jeznach     struct riscv_iommu_dc dc;
8460c54acb8STomasz Jeznach     /* Device Context format: 0: extended (64 bytes) | 1: base (32 bytes) */
8470c54acb8STomasz Jeznach     const int dc_fmt = !s->enable_msi;
8480c54acb8STomasz Jeznach     const size_t dc_len = sizeof(dc) >> dc_fmt;
8490c54acb8STomasz Jeznach     unsigned depth;
8500c54acb8STomasz Jeznach     uint64_t de;
8510c54acb8STomasz Jeznach 
8520c54acb8STomasz Jeznach     switch (mode) {
8530c54acb8STomasz Jeznach     case RISCV_IOMMU_DDTP_MODE_OFF:
8540c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_DMA_DISABLED;
8550c54acb8STomasz Jeznach 
8560c54acb8STomasz Jeznach     case RISCV_IOMMU_DDTP_MODE_BARE:
8570c54acb8STomasz Jeznach         /* mock up pass-through translation context */
8580c54acb8STomasz Jeznach         ctx->gatp = set_field(0, RISCV_IOMMU_ATP_MODE_FIELD,
8590c54acb8STomasz Jeznach             RISCV_IOMMU_DC_IOHGATP_MODE_BARE);
8600c54acb8STomasz Jeznach         ctx->satp = set_field(0, RISCV_IOMMU_ATP_MODE_FIELD,
8610c54acb8STomasz Jeznach             RISCV_IOMMU_DC_FSC_MODE_BARE);
86269a9ae48STomasz Jeznach 
8630c54acb8STomasz Jeznach         ctx->tc = RISCV_IOMMU_DC_TC_V;
86469a9ae48STomasz Jeznach         if (s->enable_ats) {
86569a9ae48STomasz Jeznach             ctx->tc |= RISCV_IOMMU_DC_TC_EN_ATS;
86669a9ae48STomasz Jeznach         }
86769a9ae48STomasz Jeznach 
8680c54acb8STomasz Jeznach         ctx->ta = 0;
8690c54acb8STomasz Jeznach         ctx->msiptp = 0;
8700c54acb8STomasz Jeznach         return 0;
8710c54acb8STomasz Jeznach 
8720c54acb8STomasz Jeznach     case RISCV_IOMMU_DDTP_MODE_1LVL:
8730c54acb8STomasz Jeznach         depth = 0;
8740c54acb8STomasz Jeznach         break;
8750c54acb8STomasz Jeznach 
8760c54acb8STomasz Jeznach     case RISCV_IOMMU_DDTP_MODE_2LVL:
8770c54acb8STomasz Jeznach         depth = 1;
8780c54acb8STomasz Jeznach         break;
8790c54acb8STomasz Jeznach 
8800c54acb8STomasz Jeznach     case RISCV_IOMMU_DDTP_MODE_3LVL:
8810c54acb8STomasz Jeznach         depth = 2;
8820c54acb8STomasz Jeznach         break;
8830c54acb8STomasz Jeznach 
8840c54acb8STomasz Jeznach     default:
8850c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
8860c54acb8STomasz Jeznach     }
8870c54acb8STomasz Jeznach 
8880c54acb8STomasz Jeznach     /*
8890c54acb8STomasz Jeznach      * Check supported device id width (in bits).
8900c54acb8STomasz Jeznach      * See IOMMU Specification, Chapter 6. Software guidelines.
8910c54acb8STomasz Jeznach      * - if extended device-context format is used:
8920c54acb8STomasz Jeznach      *   1LVL: 6, 2LVL: 15, 3LVL: 24
8930c54acb8STomasz Jeznach      * - if base device-context format is used:
8940c54acb8STomasz Jeznach      *   1LVL: 7, 2LVL: 16, 3LVL: 24
8950c54acb8STomasz Jeznach      */
8960c54acb8STomasz Jeznach     if (ctx->devid >= (1 << (depth * 9 + 6 + (dc_fmt && depth != 2)))) {
8970c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_TTYPE_BLOCKED;
8980c54acb8STomasz Jeznach     }
8990c54acb8STomasz Jeznach 
9000c54acb8STomasz Jeznach     /* Device directory tree walk */
9010c54acb8STomasz Jeznach     for (; depth-- > 0; ) {
9020c54acb8STomasz Jeznach         /*
9030c54acb8STomasz Jeznach          * Select device id index bits based on device directory tree level
9040c54acb8STomasz Jeznach          * and device context format.
9050c54acb8STomasz Jeznach          * See IOMMU Specification, Chapter 2. Data Structures.
9060c54acb8STomasz Jeznach          * - if extended device-context format is used:
9070c54acb8STomasz Jeznach          *   device index: [23:15][14:6][5:0]
9080c54acb8STomasz Jeznach          * - if base device-context format is used:
9090c54acb8STomasz Jeznach          *   device index: [23:16][15:7][6:0]
9100c54acb8STomasz Jeznach          */
9110c54acb8STomasz Jeznach         const int split = depth * 9 + 6 + dc_fmt;
9120c54acb8STomasz Jeznach         addr |= ((ctx->devid >> split) << 3) & ~TARGET_PAGE_MASK;
9130c54acb8STomasz Jeznach         if (dma_memory_read(s->target_as, addr, &de, sizeof(de),
9140c54acb8STomasz Jeznach                             MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
9150c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_DDT_LOAD_FAULT;
9160c54acb8STomasz Jeznach         }
9170c54acb8STomasz Jeznach         le64_to_cpus(&de);
9180c54acb8STomasz Jeznach         if (!(de & RISCV_IOMMU_DDTE_VALID)) {
9190c54acb8STomasz Jeznach             /* invalid directory entry */
9200c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_DDT_INVALID;
9210c54acb8STomasz Jeznach         }
9220c54acb8STomasz Jeznach         if (de & ~(RISCV_IOMMU_DDTE_PPN | RISCV_IOMMU_DDTE_VALID)) {
9230c54acb8STomasz Jeznach             /* reserved bits set */
9240c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
9250c54acb8STomasz Jeznach         }
9260c54acb8STomasz Jeznach         addr = PPN_PHYS(get_field(de, RISCV_IOMMU_DDTE_PPN));
9270c54acb8STomasz Jeznach     }
9280c54acb8STomasz Jeznach 
9290c54acb8STomasz Jeznach     /* index into device context entry page */
9300c54acb8STomasz Jeznach     addr |= (ctx->devid * dc_len) & ~TARGET_PAGE_MASK;
9310c54acb8STomasz Jeznach 
9320c54acb8STomasz Jeznach     memset(&dc, 0, sizeof(dc));
9330c54acb8STomasz Jeznach     if (dma_memory_read(s->target_as, addr, &dc, dc_len,
9340c54acb8STomasz Jeznach                         MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
9350c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_DDT_LOAD_FAULT;
9360c54acb8STomasz Jeznach     }
9370c54acb8STomasz Jeznach 
9380c54acb8STomasz Jeznach     /* Set translation context. */
9390c54acb8STomasz Jeznach     ctx->tc = le64_to_cpu(dc.tc);
9400c54acb8STomasz Jeznach     ctx->gatp = le64_to_cpu(dc.iohgatp);
9410c54acb8STomasz Jeznach     ctx->satp = le64_to_cpu(dc.fsc);
9420c54acb8STomasz Jeznach     ctx->ta = le64_to_cpu(dc.ta);
9430c54acb8STomasz Jeznach     ctx->msiptp = le64_to_cpu(dc.msiptp);
9440c54acb8STomasz Jeznach     ctx->msi_addr_mask = le64_to_cpu(dc.msi_addr_mask);
9450c54acb8STomasz Jeznach     ctx->msi_addr_pattern = le64_to_cpu(dc.msi_addr_pattern);
9460c54acb8STomasz Jeznach 
9470c54acb8STomasz Jeznach     if (!(ctx->tc & RISCV_IOMMU_DC_TC_V)) {
9480c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_DDT_INVALID;
9490c54acb8STomasz Jeznach     }
9500c54acb8STomasz Jeznach 
9510c54acb8STomasz Jeznach     if (!riscv_iommu_validate_device_ctx(s, ctx)) {
9520c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
9530c54acb8STomasz Jeznach     }
9540c54acb8STomasz Jeznach 
9550c54acb8STomasz Jeznach     /* FSC field checks */
9560c54acb8STomasz Jeznach     mode = get_field(ctx->satp, RISCV_IOMMU_DC_FSC_MODE);
9570c54acb8STomasz Jeznach     addr = PPN_PHYS(get_field(ctx->satp, RISCV_IOMMU_DC_FSC_PPN));
9580c54acb8STomasz Jeznach 
9590c54acb8STomasz Jeznach     if (!(ctx->tc & RISCV_IOMMU_DC_TC_PDTV)) {
9600c54acb8STomasz Jeznach         if (ctx->process_id != RISCV_IOMMU_NOPROCID) {
9610c54acb8STomasz Jeznach             /* PID is disabled */
9620c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_TTYPE_BLOCKED;
9630c54acb8STomasz Jeznach         }
9640c54acb8STomasz Jeznach         if (mode > RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57) {
9650c54acb8STomasz Jeznach             /* Invalid translation mode */
9660c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_DDT_INVALID;
9670c54acb8STomasz Jeznach         }
9680c54acb8STomasz Jeznach         return 0;
9690c54acb8STomasz Jeznach     }
9700c54acb8STomasz Jeznach 
9710c54acb8STomasz Jeznach     if (ctx->process_id == RISCV_IOMMU_NOPROCID) {
9720c54acb8STomasz Jeznach         if (!(ctx->tc & RISCV_IOMMU_DC_TC_DPE)) {
9730c54acb8STomasz Jeznach             /* No default process_id enabled, set BARE mode */
9740c54acb8STomasz Jeznach             ctx->satp = 0ULL;
9750c54acb8STomasz Jeznach             return 0;
9760c54acb8STomasz Jeznach         } else {
9770c54acb8STomasz Jeznach             /* Use default process_id #0 */
9780c54acb8STomasz Jeznach             ctx->process_id = 0;
9790c54acb8STomasz Jeznach         }
9800c54acb8STomasz Jeznach     }
9810c54acb8STomasz Jeznach 
9820c54acb8STomasz Jeznach     if (mode == RISCV_IOMMU_DC_FSC_MODE_BARE) {
9830c54acb8STomasz Jeznach         /* No S-Stage translation, done. */
9840c54acb8STomasz Jeznach         return 0;
9850c54acb8STomasz Jeznach     }
9860c54acb8STomasz Jeznach 
9870c54acb8STomasz Jeznach     /* FSC.TC.PDTV enabled */
9880c54acb8STomasz Jeznach     if (mode > RISCV_IOMMU_DC_FSC_PDTP_MODE_PD20) {
9890c54acb8STomasz Jeznach         /* Invalid PDTP.MODE */
9900c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_PDT_MISCONFIGURED;
9910c54acb8STomasz Jeznach     }
9920c54acb8STomasz Jeznach 
9930c54acb8STomasz Jeznach     for (depth = mode - RISCV_IOMMU_DC_FSC_PDTP_MODE_PD8; depth-- > 0; ) {
9940c54acb8STomasz Jeznach         /*
9950c54acb8STomasz Jeznach          * Select process id index bits based on process directory tree
9960c54acb8STomasz Jeznach          * level. See IOMMU Specification, 2.2. Process-Directory-Table.
9970c54acb8STomasz Jeznach          */
9980c54acb8STomasz Jeznach         const int split = depth * 9 + 8;
9990c54acb8STomasz Jeznach         addr |= ((ctx->process_id >> split) << 3) & ~TARGET_PAGE_MASK;
10000c54acb8STomasz Jeznach         if (dma_memory_read(s->target_as, addr, &de, sizeof(de),
10010c54acb8STomasz Jeznach                             MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
10020c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_PDT_LOAD_FAULT;
10030c54acb8STomasz Jeznach         }
10040c54acb8STomasz Jeznach         le64_to_cpus(&de);
10050c54acb8STomasz Jeznach         if (!(de & RISCV_IOMMU_PC_TA_V)) {
10060c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_PDT_INVALID;
10070c54acb8STomasz Jeznach         }
10080c54acb8STomasz Jeznach         addr = PPN_PHYS(get_field(de, RISCV_IOMMU_PC_FSC_PPN));
10090c54acb8STomasz Jeznach     }
10100c54acb8STomasz Jeznach 
10110c54acb8STomasz Jeznach     /* Leaf entry in PDT */
10120c54acb8STomasz Jeznach     addr |= (ctx->process_id << 4) & ~TARGET_PAGE_MASK;
10130c54acb8STomasz Jeznach     if (dma_memory_read(s->target_as, addr, &dc.ta, sizeof(uint64_t) * 2,
10140c54acb8STomasz Jeznach                         MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
10150c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_PDT_LOAD_FAULT;
10160c54acb8STomasz Jeznach     }
10170c54acb8STomasz Jeznach 
10180c54acb8STomasz Jeznach     /* Use FSC and TA from process directory entry. */
10190c54acb8STomasz Jeznach     ctx->ta = le64_to_cpu(dc.ta);
10200c54acb8STomasz Jeznach     ctx->satp = le64_to_cpu(dc.fsc);
10210c54acb8STomasz Jeznach 
10220c54acb8STomasz Jeznach     if (!(ctx->ta & RISCV_IOMMU_PC_TA_V)) {
10230c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_PDT_INVALID;
10240c54acb8STomasz Jeznach     }
10250c54acb8STomasz Jeznach 
10260c54acb8STomasz Jeznach     if (!riscv_iommu_validate_process_ctx(s, ctx)) {
10270c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_PDT_MISCONFIGURED;
10280c54acb8STomasz Jeznach     }
10290c54acb8STomasz Jeznach 
10300c54acb8STomasz Jeznach     return 0;
10310c54acb8STomasz Jeznach }
10320c54acb8STomasz Jeznach 
10330c54acb8STomasz Jeznach /* Translation Context cache support */
10340c54acb8STomasz Jeznach static gboolean riscv_iommu_ctx_equal(gconstpointer v1, gconstpointer v2)
10350c54acb8STomasz Jeznach {
10360c54acb8STomasz Jeznach     RISCVIOMMUContext *c1 = (RISCVIOMMUContext *) v1;
10370c54acb8STomasz Jeznach     RISCVIOMMUContext *c2 = (RISCVIOMMUContext *) v2;
10380c54acb8STomasz Jeznach     return c1->devid == c2->devid &&
10390c54acb8STomasz Jeznach            c1->process_id == c2->process_id;
10400c54acb8STomasz Jeznach }
10410c54acb8STomasz Jeznach 
10420c54acb8STomasz Jeznach static guint riscv_iommu_ctx_hash(gconstpointer v)
10430c54acb8STomasz Jeznach {
10440c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx = (RISCVIOMMUContext *) v;
10450c54acb8STomasz Jeznach     /*
10460c54acb8STomasz Jeznach      * Generate simple hash of (process_id, devid)
10470c54acb8STomasz Jeznach      * assuming 24-bit wide devid.
10480c54acb8STomasz Jeznach      */
10490c54acb8STomasz Jeznach     return (guint)(ctx->devid) + ((guint)(ctx->process_id) << 24);
10500c54acb8STomasz Jeznach }
10510c54acb8STomasz Jeznach 
10520c54acb8STomasz Jeznach static void riscv_iommu_ctx_inval_devid_procid(gpointer key, gpointer value,
10530c54acb8STomasz Jeznach                                                gpointer data)
10540c54acb8STomasz Jeznach {
10550c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx = (RISCVIOMMUContext *) value;
10560c54acb8STomasz Jeznach     RISCVIOMMUContext *arg = (RISCVIOMMUContext *) data;
10570c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_V &&
10580c54acb8STomasz Jeznach         ctx->devid == arg->devid &&
10590c54acb8STomasz Jeznach         ctx->process_id == arg->process_id) {
10600c54acb8STomasz Jeznach         ctx->tc &= ~RISCV_IOMMU_DC_TC_V;
10610c54acb8STomasz Jeznach     }
10620c54acb8STomasz Jeznach }
10630c54acb8STomasz Jeznach 
10640c54acb8STomasz Jeznach static void riscv_iommu_ctx_inval_devid(gpointer key, gpointer value,
10650c54acb8STomasz Jeznach                                         gpointer data)
10660c54acb8STomasz Jeznach {
10670c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx = (RISCVIOMMUContext *) value;
10680c54acb8STomasz Jeznach     RISCVIOMMUContext *arg = (RISCVIOMMUContext *) data;
10690c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_V &&
10700c54acb8STomasz Jeznach         ctx->devid == arg->devid) {
10710c54acb8STomasz Jeznach         ctx->tc &= ~RISCV_IOMMU_DC_TC_V;
10720c54acb8STomasz Jeznach     }
10730c54acb8STomasz Jeznach }
10740c54acb8STomasz Jeznach 
10750c54acb8STomasz Jeznach static void riscv_iommu_ctx_inval_all(gpointer key, gpointer value,
10760c54acb8STomasz Jeznach                                       gpointer data)
10770c54acb8STomasz Jeznach {
10780c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx = (RISCVIOMMUContext *) value;
10790c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_V) {
10800c54acb8STomasz Jeznach         ctx->tc &= ~RISCV_IOMMU_DC_TC_V;
10810c54acb8STomasz Jeznach     }
10820c54acb8STomasz Jeznach }
10830c54acb8STomasz Jeznach 
10840c54acb8STomasz Jeznach static void riscv_iommu_ctx_inval(RISCVIOMMUState *s, GHFunc func,
10850c54acb8STomasz Jeznach                                   uint32_t devid, uint32_t process_id)
10860c54acb8STomasz Jeznach {
10870c54acb8STomasz Jeznach     GHashTable *ctx_cache;
10880c54acb8STomasz Jeznach     RISCVIOMMUContext key = {
10890c54acb8STomasz Jeznach         .devid = devid,
10900c54acb8STomasz Jeznach         .process_id = process_id,
10910c54acb8STomasz Jeznach     };
10920c54acb8STomasz Jeznach     ctx_cache = g_hash_table_ref(s->ctx_cache);
10930c54acb8STomasz Jeznach     g_hash_table_foreach(ctx_cache, func, &key);
10940c54acb8STomasz Jeznach     g_hash_table_unref(ctx_cache);
10950c54acb8STomasz Jeznach }
10960c54acb8STomasz Jeznach 
10970c54acb8STomasz Jeznach /* Find or allocate translation context for a given {device_id, process_id} */
10980c54acb8STomasz Jeznach static RISCVIOMMUContext *riscv_iommu_ctx(RISCVIOMMUState *s,
10990c54acb8STomasz Jeznach                                           unsigned devid, unsigned process_id,
11000c54acb8STomasz Jeznach                                           void **ref)
11010c54acb8STomasz Jeznach {
11020c54acb8STomasz Jeznach     GHashTable *ctx_cache;
11030c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx;
11040c54acb8STomasz Jeznach     RISCVIOMMUContext key = {
11050c54acb8STomasz Jeznach         .devid = devid,
11060c54acb8STomasz Jeznach         .process_id = process_id,
11070c54acb8STomasz Jeznach     };
11080c54acb8STomasz Jeznach 
11090c54acb8STomasz Jeznach     ctx_cache = g_hash_table_ref(s->ctx_cache);
11100c54acb8STomasz Jeznach     ctx = g_hash_table_lookup(ctx_cache, &key);
11110c54acb8STomasz Jeznach 
11120c54acb8STomasz Jeznach     if (ctx && (ctx->tc & RISCV_IOMMU_DC_TC_V)) {
11130c54acb8STomasz Jeznach         *ref = ctx_cache;
11140c54acb8STomasz Jeznach         return ctx;
11150c54acb8STomasz Jeznach     }
11160c54acb8STomasz Jeznach 
11170c54acb8STomasz Jeznach     ctx = g_new0(RISCVIOMMUContext, 1);
11180c54acb8STomasz Jeznach     ctx->devid = devid;
11190c54acb8STomasz Jeznach     ctx->process_id = process_id;
11200c54acb8STomasz Jeznach 
11210c54acb8STomasz Jeznach     int fault = riscv_iommu_ctx_fetch(s, ctx);
11220c54acb8STomasz Jeznach     if (!fault) {
11230c54acb8STomasz Jeznach         if (g_hash_table_size(ctx_cache) >= LIMIT_CACHE_CTX) {
11240c54acb8STomasz Jeznach             g_hash_table_unref(ctx_cache);
11250c54acb8STomasz Jeznach             ctx_cache = g_hash_table_new_full(riscv_iommu_ctx_hash,
11260c54acb8STomasz Jeznach                                               riscv_iommu_ctx_equal,
11270c54acb8STomasz Jeznach                                               g_free, NULL);
11280c54acb8STomasz Jeznach             g_hash_table_ref(ctx_cache);
11290c54acb8STomasz Jeznach             g_hash_table_unref(qatomic_xchg(&s->ctx_cache, ctx_cache));
11300c54acb8STomasz Jeznach         }
11310c54acb8STomasz Jeznach         g_hash_table_add(ctx_cache, ctx);
11320c54acb8STomasz Jeznach         *ref = ctx_cache;
11330c54acb8STomasz Jeznach         return ctx;
11340c54acb8STomasz Jeznach     }
11350c54acb8STomasz Jeznach 
11360c54acb8STomasz Jeznach     g_hash_table_unref(ctx_cache);
11370c54acb8STomasz Jeznach     *ref = NULL;
11380c54acb8STomasz Jeznach 
11390c54acb8STomasz Jeznach     riscv_iommu_report_fault(s, ctx, RISCV_IOMMU_FQ_TTYPE_UADDR_RD,
11400c54acb8STomasz Jeznach                              fault, !!process_id, 0, 0);
11410c54acb8STomasz Jeznach 
11420c54acb8STomasz Jeznach     g_free(ctx);
11430c54acb8STomasz Jeznach     return NULL;
11440c54acb8STomasz Jeznach }
11450c54acb8STomasz Jeznach 
11460c54acb8STomasz Jeznach static void riscv_iommu_ctx_put(RISCVIOMMUState *s, void *ref)
11470c54acb8STomasz Jeznach {
11480c54acb8STomasz Jeznach     if (ref) {
11490c54acb8STomasz Jeznach         g_hash_table_unref((GHashTable *)ref);
11500c54acb8STomasz Jeznach     }
11510c54acb8STomasz Jeznach }
11520c54acb8STomasz Jeznach 
11530c54acb8STomasz Jeznach /* Find or allocate address space for a given device */
11540c54acb8STomasz Jeznach static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, uint32_t devid)
11550c54acb8STomasz Jeznach {
11560c54acb8STomasz Jeznach     RISCVIOMMUSpace *as;
11570c54acb8STomasz Jeznach 
11580c54acb8STomasz Jeznach     /* FIXME: PCIe bus remapping for attached endpoints. */
11590c54acb8STomasz Jeznach     devid |= s->bus << 8;
11600c54acb8STomasz Jeznach 
11610c54acb8STomasz Jeznach     QLIST_FOREACH(as, &s->spaces, list) {
11620c54acb8STomasz Jeznach         if (as->devid == devid) {
11630c54acb8STomasz Jeznach             break;
11640c54acb8STomasz Jeznach         }
11650c54acb8STomasz Jeznach     }
11660c54acb8STomasz Jeznach 
11670c54acb8STomasz Jeznach     if (as == NULL) {
11680c54acb8STomasz Jeznach         char name[64];
11690c54acb8STomasz Jeznach         as = g_new0(RISCVIOMMUSpace, 1);
11700c54acb8STomasz Jeznach 
11710c54acb8STomasz Jeznach         as->iommu = s;
11720c54acb8STomasz Jeznach         as->devid = devid;
11730c54acb8STomasz Jeznach 
11740c54acb8STomasz Jeznach         snprintf(name, sizeof(name), "riscv-iommu-%04x:%02x.%d-iova",
11750c54acb8STomasz Jeznach             PCI_BUS_NUM(as->devid), PCI_SLOT(as->devid), PCI_FUNC(as->devid));
11760c54acb8STomasz Jeznach 
11770c54acb8STomasz Jeznach         /* IOVA address space, untranslated addresses */
11780c54acb8STomasz Jeznach         memory_region_init_iommu(&as->iova_mr, sizeof(as->iova_mr),
11790c54acb8STomasz Jeznach             TYPE_RISCV_IOMMU_MEMORY_REGION,
11800c54acb8STomasz Jeznach             OBJECT(as), "riscv_iommu", UINT64_MAX);
11810c54acb8STomasz Jeznach         address_space_init(&as->iova_as, MEMORY_REGION(&as->iova_mr), name);
11820c54acb8STomasz Jeznach 
11830c54acb8STomasz Jeznach         QLIST_INSERT_HEAD(&s->spaces, as, list);
11840c54acb8STomasz Jeznach 
11850c54acb8STomasz Jeznach         trace_riscv_iommu_new(s->parent_obj.id, PCI_BUS_NUM(as->devid),
11860c54acb8STomasz Jeznach                 PCI_SLOT(as->devid), PCI_FUNC(as->devid));
11870c54acb8STomasz Jeznach     }
11880c54acb8STomasz Jeznach     return &as->iova_as;
11890c54acb8STomasz Jeznach }
11900c54acb8STomasz Jeznach 
11919d085a1cSTomasz Jeznach /* Translation Object cache support */
11929d085a1cSTomasz Jeznach static gboolean riscv_iommu_iot_equal(gconstpointer v1, gconstpointer v2)
11930c54acb8STomasz Jeznach {
11949d085a1cSTomasz Jeznach     RISCVIOMMUEntry *t1 = (RISCVIOMMUEntry *) v1;
11959d085a1cSTomasz Jeznach     RISCVIOMMUEntry *t2 = (RISCVIOMMUEntry *) v2;
11969d085a1cSTomasz Jeznach     return t1->gscid == t2->gscid && t1->pscid == t2->pscid &&
11979d085a1cSTomasz Jeznach            t1->iova == t2->iova;
11989d085a1cSTomasz Jeznach }
11999d085a1cSTomasz Jeznach 
12009d085a1cSTomasz Jeznach static guint riscv_iommu_iot_hash(gconstpointer v)
12019d085a1cSTomasz Jeznach {
12029d085a1cSTomasz Jeznach     RISCVIOMMUEntry *t = (RISCVIOMMUEntry *) v;
12039d085a1cSTomasz Jeznach     return (guint)t->iova;
12049d085a1cSTomasz Jeznach }
12059d085a1cSTomasz Jeznach 
12069d085a1cSTomasz Jeznach /* GV: 1 PSCV: 1 AV: 1 */
12079d085a1cSTomasz Jeznach static void riscv_iommu_iot_inval_pscid_iova(gpointer key, gpointer value,
12089d085a1cSTomasz Jeznach                                              gpointer data)
12099d085a1cSTomasz Jeznach {
12109d085a1cSTomasz Jeznach     RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value;
12119d085a1cSTomasz Jeznach     RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data;
12129d085a1cSTomasz Jeznach     if (iot->gscid == arg->gscid &&
12139d085a1cSTomasz Jeznach         iot->pscid == arg->pscid &&
12149d085a1cSTomasz Jeznach         iot->iova == arg->iova) {
12159d085a1cSTomasz Jeznach         iot->perm = IOMMU_NONE;
12169d085a1cSTomasz Jeznach     }
12179d085a1cSTomasz Jeznach }
12189d085a1cSTomasz Jeznach 
12199d085a1cSTomasz Jeznach /* GV: 1 PSCV: 1 AV: 0 */
12209d085a1cSTomasz Jeznach static void riscv_iommu_iot_inval_pscid(gpointer key, gpointer value,
12219d085a1cSTomasz Jeznach                                         gpointer data)
12229d085a1cSTomasz Jeznach {
12239d085a1cSTomasz Jeznach     RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value;
12249d085a1cSTomasz Jeznach     RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data;
12259d085a1cSTomasz Jeznach     if (iot->gscid == arg->gscid &&
12269d085a1cSTomasz Jeznach         iot->pscid == arg->pscid) {
12279d085a1cSTomasz Jeznach         iot->perm = IOMMU_NONE;
12289d085a1cSTomasz Jeznach     }
12299d085a1cSTomasz Jeznach }
12309d085a1cSTomasz Jeznach 
12319d085a1cSTomasz Jeznach /* GV: 1 GVMA: 1 */
12329d085a1cSTomasz Jeznach static void riscv_iommu_iot_inval_gscid_gpa(gpointer key, gpointer value,
12339d085a1cSTomasz Jeznach                                             gpointer data)
12349d085a1cSTomasz Jeznach {
12359d085a1cSTomasz Jeznach     RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value;
12369d085a1cSTomasz Jeznach     RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data;
12379d085a1cSTomasz Jeznach     if (iot->gscid == arg->gscid) {
12389d085a1cSTomasz Jeznach         /* simplified cache, no GPA matching */
12399d085a1cSTomasz Jeznach         iot->perm = IOMMU_NONE;
12409d085a1cSTomasz Jeznach     }
12419d085a1cSTomasz Jeznach }
12429d085a1cSTomasz Jeznach 
12439d085a1cSTomasz Jeznach /* GV: 1 GVMA: 0 */
12449d085a1cSTomasz Jeznach static void riscv_iommu_iot_inval_gscid(gpointer key, gpointer value,
12459d085a1cSTomasz Jeznach                                         gpointer data)
12469d085a1cSTomasz Jeznach {
12479d085a1cSTomasz Jeznach     RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value;
12489d085a1cSTomasz Jeznach     RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data;
12499d085a1cSTomasz Jeznach     if (iot->gscid == arg->gscid) {
12509d085a1cSTomasz Jeznach         iot->perm = IOMMU_NONE;
12519d085a1cSTomasz Jeznach     }
12529d085a1cSTomasz Jeznach }
12539d085a1cSTomasz Jeznach 
12549d085a1cSTomasz Jeznach /* GV: 0 */
12559d085a1cSTomasz Jeznach static void riscv_iommu_iot_inval_all(gpointer key, gpointer value,
12569d085a1cSTomasz Jeznach                                       gpointer data)
12579d085a1cSTomasz Jeznach {
12589d085a1cSTomasz Jeznach     RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value;
12599d085a1cSTomasz Jeznach     iot->perm = IOMMU_NONE;
12609d085a1cSTomasz Jeznach }
12619d085a1cSTomasz Jeznach 
12629d085a1cSTomasz Jeznach /* caller should keep ref-count for iot_cache object */
12639d085a1cSTomasz Jeznach static RISCVIOMMUEntry *riscv_iommu_iot_lookup(RISCVIOMMUContext *ctx,
12649d085a1cSTomasz Jeznach     GHashTable *iot_cache, hwaddr iova)
12659d085a1cSTomasz Jeznach {
12669d085a1cSTomasz Jeznach     RISCVIOMMUEntry key = {
12679d085a1cSTomasz Jeznach         .gscid = get_field(ctx->gatp, RISCV_IOMMU_DC_IOHGATP_GSCID),
12689d085a1cSTomasz Jeznach         .pscid = get_field(ctx->ta, RISCV_IOMMU_DC_TA_PSCID),
12699d085a1cSTomasz Jeznach         .iova  = PPN_DOWN(iova),
12709d085a1cSTomasz Jeznach     };
12719d085a1cSTomasz Jeznach     return g_hash_table_lookup(iot_cache, &key);
12729d085a1cSTomasz Jeznach }
12739d085a1cSTomasz Jeznach 
12749d085a1cSTomasz Jeznach /* caller should keep ref-count for iot_cache object */
12759d085a1cSTomasz Jeznach static void riscv_iommu_iot_update(RISCVIOMMUState *s,
12769d085a1cSTomasz Jeznach     GHashTable *iot_cache, RISCVIOMMUEntry *iot)
12779d085a1cSTomasz Jeznach {
12789d085a1cSTomasz Jeznach     if (!s->iot_limit) {
12799d085a1cSTomasz Jeznach         return;
12809d085a1cSTomasz Jeznach     }
12819d085a1cSTomasz Jeznach 
12829d085a1cSTomasz Jeznach     if (g_hash_table_size(s->iot_cache) >= s->iot_limit) {
12839d085a1cSTomasz Jeznach         iot_cache = g_hash_table_new_full(riscv_iommu_iot_hash,
12849d085a1cSTomasz Jeznach                                           riscv_iommu_iot_equal,
12859d085a1cSTomasz Jeznach                                           g_free, NULL);
12869d085a1cSTomasz Jeznach         g_hash_table_unref(qatomic_xchg(&s->iot_cache, iot_cache));
12879d085a1cSTomasz Jeznach     }
12889d085a1cSTomasz Jeznach     g_hash_table_add(iot_cache, iot);
12899d085a1cSTomasz Jeznach }
12909d085a1cSTomasz Jeznach 
12919d085a1cSTomasz Jeznach static void riscv_iommu_iot_inval(RISCVIOMMUState *s, GHFunc func,
12929d085a1cSTomasz Jeznach     uint32_t gscid, uint32_t pscid, hwaddr iova)
12939d085a1cSTomasz Jeznach {
12949d085a1cSTomasz Jeznach     GHashTable *iot_cache;
12959d085a1cSTomasz Jeznach     RISCVIOMMUEntry key = {
12969d085a1cSTomasz Jeznach         .gscid = gscid,
12979d085a1cSTomasz Jeznach         .pscid = pscid,
12989d085a1cSTomasz Jeznach         .iova  = PPN_DOWN(iova),
12999d085a1cSTomasz Jeznach     };
13009d085a1cSTomasz Jeznach 
13019d085a1cSTomasz Jeznach     iot_cache = g_hash_table_ref(s->iot_cache);
13029d085a1cSTomasz Jeznach     g_hash_table_foreach(iot_cache, func, &key);
13039d085a1cSTomasz Jeznach     g_hash_table_unref(iot_cache);
13049d085a1cSTomasz Jeznach }
13059d085a1cSTomasz Jeznach 
13069d085a1cSTomasz Jeznach static int riscv_iommu_translate(RISCVIOMMUState *s, RISCVIOMMUContext *ctx,
13079d085a1cSTomasz Jeznach     IOMMUTLBEntry *iotlb, bool enable_cache)
13089d085a1cSTomasz Jeznach {
13099d085a1cSTomasz Jeznach     RISCVIOMMUEntry *iot;
13109d085a1cSTomasz Jeznach     IOMMUAccessFlags perm;
13110c54acb8STomasz Jeznach     bool enable_pid;
13120c54acb8STomasz Jeznach     bool enable_pri;
13139d085a1cSTomasz Jeznach     GHashTable *iot_cache;
13140c54acb8STomasz Jeznach     int fault;
13150c54acb8STomasz Jeznach 
13169d085a1cSTomasz Jeznach     iot_cache = g_hash_table_ref(s->iot_cache);
13170c54acb8STomasz Jeznach     /*
13180c54acb8STomasz Jeznach      * TC[32] is reserved for custom extensions, used here to temporarily
13190c54acb8STomasz Jeznach      * enable automatic page-request generation for ATS queries.
13200c54acb8STomasz Jeznach      */
13210c54acb8STomasz Jeznach     enable_pri = (iotlb->perm == IOMMU_NONE) && (ctx->tc & BIT_ULL(32));
13220c54acb8STomasz Jeznach     enable_pid = (ctx->tc & RISCV_IOMMU_DC_TC_PDTV);
13230c54acb8STomasz Jeznach 
132469a9ae48STomasz Jeznach     /* Check for ATS request. */
132569a9ae48STomasz Jeznach     if (iotlb->perm == IOMMU_NONE) {
132669a9ae48STomasz Jeznach         /* Check if ATS is disabled. */
132769a9ae48STomasz Jeznach         if (!(ctx->tc & RISCV_IOMMU_DC_TC_EN_ATS)) {
132869a9ae48STomasz Jeznach             enable_pri = false;
132969a9ae48STomasz Jeznach             fault = RISCV_IOMMU_FQ_CAUSE_TTYPE_BLOCKED;
133069a9ae48STomasz Jeznach             goto done;
133169a9ae48STomasz Jeznach         }
133269a9ae48STomasz Jeznach     }
133369a9ae48STomasz Jeznach 
13349d085a1cSTomasz Jeznach     iot = riscv_iommu_iot_lookup(ctx, iot_cache, iotlb->iova);
13359d085a1cSTomasz Jeznach     perm = iot ? iot->perm : IOMMU_NONE;
13369d085a1cSTomasz Jeznach     if (perm != IOMMU_NONE) {
13379d085a1cSTomasz Jeznach         iotlb->translated_addr = PPN_PHYS(iot->phys);
13389d085a1cSTomasz Jeznach         iotlb->addr_mask = ~TARGET_PAGE_MASK;
13399d085a1cSTomasz Jeznach         iotlb->perm = perm;
13409d085a1cSTomasz Jeznach         fault = 0;
13419d085a1cSTomasz Jeznach         goto done;
13429d085a1cSTomasz Jeznach     }
13439d085a1cSTomasz Jeznach 
13440c54acb8STomasz Jeznach     /* Translate using device directory / page table information. */
13450c54acb8STomasz Jeznach     fault = riscv_iommu_spa_fetch(s, ctx, iotlb);
13460c54acb8STomasz Jeznach 
13479d085a1cSTomasz Jeznach     if (!fault && iotlb->target_as == &s->trap_as) {
13489d085a1cSTomasz Jeznach         /* Do not cache trapped MSI translations */
13499d085a1cSTomasz Jeznach         goto done;
13509d085a1cSTomasz Jeznach     }
13519d085a1cSTomasz Jeznach 
13529d085a1cSTomasz Jeznach     /*
13539d085a1cSTomasz Jeznach      * We made an implementation choice to not cache identity-mapped
13549d085a1cSTomasz Jeznach      * translations, as allowed by the specification, to avoid
13559d085a1cSTomasz Jeznach      * translation cache evictions for other devices sharing the
13569d085a1cSTomasz Jeznach      * IOMMU hardware model.
13579d085a1cSTomasz Jeznach      */
13589d085a1cSTomasz Jeznach     if (!fault && iotlb->translated_addr != iotlb->iova && enable_cache) {
13599d085a1cSTomasz Jeznach         iot = g_new0(RISCVIOMMUEntry, 1);
13609d085a1cSTomasz Jeznach         iot->iova = PPN_DOWN(iotlb->iova);
13619d085a1cSTomasz Jeznach         iot->phys = PPN_DOWN(iotlb->translated_addr);
13629d085a1cSTomasz Jeznach         iot->gscid = get_field(ctx->gatp, RISCV_IOMMU_DC_IOHGATP_GSCID);
13639d085a1cSTomasz Jeznach         iot->pscid = get_field(ctx->ta, RISCV_IOMMU_DC_TA_PSCID);
13649d085a1cSTomasz Jeznach         iot->perm = iotlb->perm;
13659d085a1cSTomasz Jeznach         riscv_iommu_iot_update(s, iot_cache, iot);
13669d085a1cSTomasz Jeznach     }
13679d085a1cSTomasz Jeznach 
13689d085a1cSTomasz Jeznach done:
13699d085a1cSTomasz Jeznach     g_hash_table_unref(iot_cache);
13709d085a1cSTomasz Jeznach 
13710c54acb8STomasz Jeznach     if (enable_pri && fault) {
13720c54acb8STomasz Jeznach         struct riscv_iommu_pq_record pr = {0};
13730c54acb8STomasz Jeznach         if (enable_pid) {
13740c54acb8STomasz Jeznach             pr.hdr = set_field(RISCV_IOMMU_PREQ_HDR_PV,
13750c54acb8STomasz Jeznach                                RISCV_IOMMU_PREQ_HDR_PID, ctx->process_id);
13760c54acb8STomasz Jeznach         }
13770c54acb8STomasz Jeznach         pr.hdr = set_field(pr.hdr, RISCV_IOMMU_PREQ_HDR_DID, ctx->devid);
13780c54acb8STomasz Jeznach         pr.payload = (iotlb->iova & TARGET_PAGE_MASK) |
13790c54acb8STomasz Jeznach                      RISCV_IOMMU_PREQ_PAYLOAD_M;
13800c54acb8STomasz Jeznach         riscv_iommu_pri(s, &pr);
13810c54acb8STomasz Jeznach         return fault;
13820c54acb8STomasz Jeznach     }
13830c54acb8STomasz Jeznach 
13840c54acb8STomasz Jeznach     if (fault) {
138569a9ae48STomasz Jeznach         unsigned ttype = RISCV_IOMMU_FQ_TTYPE_PCIE_ATS_REQ;
13860c54acb8STomasz Jeznach 
13870c54acb8STomasz Jeznach         if (iotlb->perm & IOMMU_RW) {
13880c54acb8STomasz Jeznach             ttype = RISCV_IOMMU_FQ_TTYPE_UADDR_WR;
138969a9ae48STomasz Jeznach         } else if (iotlb->perm & IOMMU_RO) {
13900c54acb8STomasz Jeznach             ttype = RISCV_IOMMU_FQ_TTYPE_UADDR_RD;
13910c54acb8STomasz Jeznach         }
13920c54acb8STomasz Jeznach 
13930c54acb8STomasz Jeznach         riscv_iommu_report_fault(s, ctx, ttype, fault, enable_pid,
13940c54acb8STomasz Jeznach                                  iotlb->iova, iotlb->translated_addr);
13950c54acb8STomasz Jeznach         return fault;
13960c54acb8STomasz Jeznach     }
13970c54acb8STomasz Jeznach 
13980c54acb8STomasz Jeznach     return 0;
13990c54acb8STomasz Jeznach }
14000c54acb8STomasz Jeznach 
14010c54acb8STomasz Jeznach /* IOMMU Command Interface */
14020c54acb8STomasz Jeznach static MemTxResult riscv_iommu_iofence(RISCVIOMMUState *s, bool notify,
14030c54acb8STomasz Jeznach     uint64_t addr, uint32_t data)
14040c54acb8STomasz Jeznach {
14050c54acb8STomasz Jeznach     /*
14060c54acb8STomasz Jeznach      * ATS processing in this implementation of the IOMMU is synchronous,
14070c54acb8STomasz Jeznach      * no need to wait for completions here.
14080c54acb8STomasz Jeznach      */
14090c54acb8STomasz Jeznach     if (!notify) {
14100c54acb8STomasz Jeznach         return MEMTX_OK;
14110c54acb8STomasz Jeznach     }
14120c54acb8STomasz Jeznach 
14130c54acb8STomasz Jeznach     return dma_memory_write(s->target_as, addr, &data, sizeof(data),
14140c54acb8STomasz Jeznach         MEMTXATTRS_UNSPECIFIED);
14150c54acb8STomasz Jeznach }
14160c54acb8STomasz Jeznach 
141769a9ae48STomasz Jeznach static void riscv_iommu_ats(RISCVIOMMUState *s,
141869a9ae48STomasz Jeznach     struct riscv_iommu_command *cmd, IOMMUNotifierFlag flag,
141969a9ae48STomasz Jeznach     IOMMUAccessFlags perm,
142069a9ae48STomasz Jeznach     void (*trace_fn)(const char *id))
142169a9ae48STomasz Jeznach {
142269a9ae48STomasz Jeznach     RISCVIOMMUSpace *as = NULL;
142369a9ae48STomasz Jeznach     IOMMUNotifier *n;
142469a9ae48STomasz Jeznach     IOMMUTLBEvent event;
142569a9ae48STomasz Jeznach     uint32_t pid;
142669a9ae48STomasz Jeznach     uint32_t devid;
142769a9ae48STomasz Jeznach     const bool pv = cmd->dword0 & RISCV_IOMMU_CMD_ATS_PV;
142869a9ae48STomasz Jeznach 
142969a9ae48STomasz Jeznach     if (cmd->dword0 & RISCV_IOMMU_CMD_ATS_DSV) {
143069a9ae48STomasz Jeznach         /* Use device segment and requester id */
143169a9ae48STomasz Jeznach         devid = get_field(cmd->dword0,
143269a9ae48STomasz Jeznach             RISCV_IOMMU_CMD_ATS_DSEG | RISCV_IOMMU_CMD_ATS_RID);
143369a9ae48STomasz Jeznach     } else {
143469a9ae48STomasz Jeznach         devid = get_field(cmd->dword0, RISCV_IOMMU_CMD_ATS_RID);
143569a9ae48STomasz Jeznach     }
143669a9ae48STomasz Jeznach 
143769a9ae48STomasz Jeznach     pid = get_field(cmd->dword0, RISCV_IOMMU_CMD_ATS_PID);
143869a9ae48STomasz Jeznach 
143969a9ae48STomasz Jeznach     QLIST_FOREACH(as, &s->spaces, list) {
144069a9ae48STomasz Jeznach         if (as->devid == devid) {
144169a9ae48STomasz Jeznach             break;
144269a9ae48STomasz Jeznach         }
144369a9ae48STomasz Jeznach     }
144469a9ae48STomasz Jeznach 
144569a9ae48STomasz Jeznach     if (!as || !as->notifier) {
144669a9ae48STomasz Jeznach         return;
144769a9ae48STomasz Jeznach     }
144869a9ae48STomasz Jeznach 
144969a9ae48STomasz Jeznach     event.type = flag;
145069a9ae48STomasz Jeznach     event.entry.perm = perm;
145169a9ae48STomasz Jeznach     event.entry.target_as = s->target_as;
145269a9ae48STomasz Jeznach 
145369a9ae48STomasz Jeznach     IOMMU_NOTIFIER_FOREACH(n, &as->iova_mr) {
145469a9ae48STomasz Jeznach         if (!pv || n->iommu_idx == pid) {
145569a9ae48STomasz Jeznach             event.entry.iova = n->start;
145669a9ae48STomasz Jeznach             event.entry.addr_mask = n->end - n->start;
145769a9ae48STomasz Jeznach             trace_fn(as->iova_mr.parent_obj.name);
145869a9ae48STomasz Jeznach             memory_region_notify_iommu_one(n, &event);
145969a9ae48STomasz Jeznach         }
146069a9ae48STomasz Jeznach     }
146169a9ae48STomasz Jeznach }
146269a9ae48STomasz Jeznach 
146369a9ae48STomasz Jeznach static void riscv_iommu_ats_inval(RISCVIOMMUState *s,
146469a9ae48STomasz Jeznach     struct riscv_iommu_command *cmd)
146569a9ae48STomasz Jeznach {
146669a9ae48STomasz Jeznach     return riscv_iommu_ats(s, cmd, IOMMU_NOTIFIER_DEVIOTLB_UNMAP, IOMMU_NONE,
146769a9ae48STomasz Jeznach                            trace_riscv_iommu_ats_inval);
146869a9ae48STomasz Jeznach }
146969a9ae48STomasz Jeznach 
147069a9ae48STomasz Jeznach static void riscv_iommu_ats_prgr(RISCVIOMMUState *s,
147169a9ae48STomasz Jeznach     struct riscv_iommu_command *cmd)
147269a9ae48STomasz Jeznach {
147369a9ae48STomasz Jeznach     unsigned resp_code = get_field(cmd->dword1,
147469a9ae48STomasz Jeznach                                    RISCV_IOMMU_CMD_ATS_PRGR_RESP_CODE);
147569a9ae48STomasz Jeznach 
147669a9ae48STomasz Jeznach     /* Using the access flag to carry response code information */
147769a9ae48STomasz Jeznach     IOMMUAccessFlags perm = resp_code ? IOMMU_NONE : IOMMU_RW;
147869a9ae48STomasz Jeznach     return riscv_iommu_ats(s, cmd, IOMMU_NOTIFIER_MAP, perm,
147969a9ae48STomasz Jeznach                            trace_riscv_iommu_ats_prgr);
148069a9ae48STomasz Jeznach }
148169a9ae48STomasz Jeznach 
14820c54acb8STomasz Jeznach static void riscv_iommu_process_ddtp(RISCVIOMMUState *s)
14830c54acb8STomasz Jeznach {
14840c54acb8STomasz Jeznach     uint64_t old_ddtp = s->ddtp;
14850c54acb8STomasz Jeznach     uint64_t new_ddtp = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_DDTP);
14860c54acb8STomasz Jeznach     unsigned new_mode = get_field(new_ddtp, RISCV_IOMMU_DDTP_MODE);
14870c54acb8STomasz Jeznach     unsigned old_mode = get_field(old_ddtp, RISCV_IOMMU_DDTP_MODE);
14880c54acb8STomasz Jeznach     bool ok = false;
14890c54acb8STomasz Jeznach 
14900c54acb8STomasz Jeznach     /*
14910c54acb8STomasz Jeznach      * Check for allowed DDTP.MODE transitions:
14920c54acb8STomasz Jeznach      * {OFF, BARE}        -> {OFF, BARE, 1LVL, 2LVL, 3LVL}
14930c54acb8STomasz Jeznach      * {1LVL, 2LVL, 3LVL} -> {OFF, BARE}
14940c54acb8STomasz Jeznach      */
14950c54acb8STomasz Jeznach     if (new_mode == old_mode ||
14960c54acb8STomasz Jeznach         new_mode == RISCV_IOMMU_DDTP_MODE_OFF ||
14970c54acb8STomasz Jeznach         new_mode == RISCV_IOMMU_DDTP_MODE_BARE) {
14980c54acb8STomasz Jeznach         ok = true;
14990c54acb8STomasz Jeznach     } else if (new_mode == RISCV_IOMMU_DDTP_MODE_1LVL ||
15000c54acb8STomasz Jeznach                new_mode == RISCV_IOMMU_DDTP_MODE_2LVL ||
15010c54acb8STomasz Jeznach                new_mode == RISCV_IOMMU_DDTP_MODE_3LVL) {
15020c54acb8STomasz Jeznach         ok = old_mode == RISCV_IOMMU_DDTP_MODE_OFF ||
15030c54acb8STomasz Jeznach              old_mode == RISCV_IOMMU_DDTP_MODE_BARE;
15040c54acb8STomasz Jeznach     }
15050c54acb8STomasz Jeznach 
15060c54acb8STomasz Jeznach     if (ok) {
15070c54acb8STomasz Jeznach         /* clear reserved and busy bits, report back sanitized version */
15080c54acb8STomasz Jeznach         new_ddtp = set_field(new_ddtp & RISCV_IOMMU_DDTP_PPN,
15090c54acb8STomasz Jeznach                              RISCV_IOMMU_DDTP_MODE, new_mode);
15100c54acb8STomasz Jeznach     } else {
15110c54acb8STomasz Jeznach         new_ddtp = old_ddtp;
15120c54acb8STomasz Jeznach     }
15130c54acb8STomasz Jeznach     s->ddtp = new_ddtp;
15140c54acb8STomasz Jeznach 
15150c54acb8STomasz Jeznach     riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_DDTP, new_ddtp);
15160c54acb8STomasz Jeznach }
15170c54acb8STomasz Jeznach 
15180c54acb8STomasz Jeznach /* Command function and opcode field. */
15190c54acb8STomasz Jeznach #define RISCV_IOMMU_CMD(func, op) (((func) << 7) | (op))
15200c54acb8STomasz Jeznach 
15210c54acb8STomasz Jeznach static void riscv_iommu_process_cq_tail(RISCVIOMMUState *s)
15220c54acb8STomasz Jeznach {
15230c54acb8STomasz Jeznach     struct riscv_iommu_command cmd;
15240c54acb8STomasz Jeznach     MemTxResult res;
15250c54acb8STomasz Jeznach     dma_addr_t addr;
15260c54acb8STomasz Jeznach     uint32_t tail, head, ctrl;
15270c54acb8STomasz Jeznach     uint64_t cmd_opcode;
15280c54acb8STomasz Jeznach     GHFunc func;
15290c54acb8STomasz Jeznach 
15300c54acb8STomasz Jeznach     ctrl = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQCSR);
15310c54acb8STomasz Jeznach     tail = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQT) & s->cq_mask;
15320c54acb8STomasz Jeznach     head = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQH) & s->cq_mask;
15330c54acb8STomasz Jeznach 
15340c54acb8STomasz Jeznach     /* Check for pending error or queue processing disabled */
15350c54acb8STomasz Jeznach     if (!(ctrl & RISCV_IOMMU_CQCSR_CQON) ||
15360c54acb8STomasz Jeznach         !!(ctrl & (RISCV_IOMMU_CQCSR_CMD_ILL | RISCV_IOMMU_CQCSR_CQMF))) {
15370c54acb8STomasz Jeznach         return;
15380c54acb8STomasz Jeznach     }
15390c54acb8STomasz Jeznach 
15400c54acb8STomasz Jeznach     while (tail != head) {
15410c54acb8STomasz Jeznach         addr = s->cq_addr  + head * sizeof(cmd);
15420c54acb8STomasz Jeznach         res = dma_memory_read(s->target_as, addr, &cmd, sizeof(cmd),
15430c54acb8STomasz Jeznach                               MEMTXATTRS_UNSPECIFIED);
15440c54acb8STomasz Jeznach 
15450c54acb8STomasz Jeznach         if (res != MEMTX_OK) {
15460c54acb8STomasz Jeznach             riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR,
15470c54acb8STomasz Jeznach                                   RISCV_IOMMU_CQCSR_CQMF, 0);
15480c54acb8STomasz Jeznach             goto fault;
15490c54acb8STomasz Jeznach         }
15500c54acb8STomasz Jeznach 
15510c54acb8STomasz Jeznach         trace_riscv_iommu_cmd(s->parent_obj.id, cmd.dword0, cmd.dword1);
15520c54acb8STomasz Jeznach 
15530c54acb8STomasz Jeznach         cmd_opcode = get_field(cmd.dword0,
15540c54acb8STomasz Jeznach                                RISCV_IOMMU_CMD_OPCODE | RISCV_IOMMU_CMD_FUNC);
15550c54acb8STomasz Jeznach 
15560c54acb8STomasz Jeznach         switch (cmd_opcode) {
15570c54acb8STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IOFENCE_FUNC_C,
15580c54acb8STomasz Jeznach                              RISCV_IOMMU_CMD_IOFENCE_OPCODE):
15590c54acb8STomasz Jeznach             res = riscv_iommu_iofence(s,
15600c54acb8STomasz Jeznach                 cmd.dword0 & RISCV_IOMMU_CMD_IOFENCE_AV, cmd.dword1 << 2,
15610c54acb8STomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IOFENCE_DATA));
15620c54acb8STomasz Jeznach 
15630c54acb8STomasz Jeznach             if (res != MEMTX_OK) {
15640c54acb8STomasz Jeznach                 riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR,
15650c54acb8STomasz Jeznach                                       RISCV_IOMMU_CQCSR_CQMF, 0);
15660c54acb8STomasz Jeznach                 goto fault;
15670c54acb8STomasz Jeznach             }
15680c54acb8STomasz Jeznach             break;
15690c54acb8STomasz Jeznach 
15700c54acb8STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IOTINVAL_FUNC_GVMA,
15710c54acb8STomasz Jeznach                              RISCV_IOMMU_CMD_IOTINVAL_OPCODE):
15720c54acb8STomasz Jeznach             if (cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_PSCV) {
15730c54acb8STomasz Jeznach                 /* illegal command arguments IOTINVAL.GVMA & PSCV == 1 */
15740c54acb8STomasz Jeznach                 goto cmd_ill;
15759d085a1cSTomasz Jeznach             } else if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_GV)) {
15769d085a1cSTomasz Jeznach                 /* invalidate all cache mappings */
15779d085a1cSTomasz Jeznach                 func = riscv_iommu_iot_inval_all;
15789d085a1cSTomasz Jeznach             } else if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_AV)) {
15799d085a1cSTomasz Jeznach                 /* invalidate cache matching GSCID */
15809d085a1cSTomasz Jeznach                 func = riscv_iommu_iot_inval_gscid;
15819d085a1cSTomasz Jeznach             } else {
15829d085a1cSTomasz Jeznach                 /* invalidate cache matching GSCID and ADDR (GPA) */
15839d085a1cSTomasz Jeznach                 func = riscv_iommu_iot_inval_gscid_gpa;
15840c54acb8STomasz Jeznach             }
15859d085a1cSTomasz Jeznach             riscv_iommu_iot_inval(s, func,
15869d085a1cSTomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IOTINVAL_GSCID), 0,
15879d085a1cSTomasz Jeznach                 cmd.dword1 << 2 & TARGET_PAGE_MASK);
15880c54acb8STomasz Jeznach             break;
15890c54acb8STomasz Jeznach 
15900c54acb8STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IOTINVAL_FUNC_VMA,
15910c54acb8STomasz Jeznach                              RISCV_IOMMU_CMD_IOTINVAL_OPCODE):
15929d085a1cSTomasz Jeznach             if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_GV)) {
15939d085a1cSTomasz Jeznach                 /* invalidate all cache mappings, simplified model */
15949d085a1cSTomasz Jeznach                 func = riscv_iommu_iot_inval_all;
15959d085a1cSTomasz Jeznach             } else if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_PSCV)) {
15969d085a1cSTomasz Jeznach                 /* invalidate cache matching GSCID, simplified model */
15979d085a1cSTomasz Jeznach                 func = riscv_iommu_iot_inval_gscid;
15989d085a1cSTomasz Jeznach             } else if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_AV)) {
15999d085a1cSTomasz Jeznach                 /* invalidate cache matching GSCID and PSCID */
16009d085a1cSTomasz Jeznach                 func = riscv_iommu_iot_inval_pscid;
16019d085a1cSTomasz Jeznach             } else {
16029d085a1cSTomasz Jeznach                 /* invalidate cache matching GSCID and PSCID and ADDR (IOVA) */
16039d085a1cSTomasz Jeznach                 func = riscv_iommu_iot_inval_pscid_iova;
16049d085a1cSTomasz Jeznach             }
16059d085a1cSTomasz Jeznach             riscv_iommu_iot_inval(s, func,
16069d085a1cSTomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IOTINVAL_GSCID),
16079d085a1cSTomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IOTINVAL_PSCID),
16089d085a1cSTomasz Jeznach                 cmd.dword1 << 2 & TARGET_PAGE_MASK);
16090c54acb8STomasz Jeznach             break;
16100c54acb8STomasz Jeznach 
16110c54acb8STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IODIR_FUNC_INVAL_DDT,
16120c54acb8STomasz Jeznach                              RISCV_IOMMU_CMD_IODIR_OPCODE):
16130c54acb8STomasz Jeznach             if (!(cmd.dword0 & RISCV_IOMMU_CMD_IODIR_DV)) {
16140c54acb8STomasz Jeznach                 /* invalidate all device context cache mappings */
16150c54acb8STomasz Jeznach                 func = riscv_iommu_ctx_inval_all;
16160c54acb8STomasz Jeznach             } else {
16170c54acb8STomasz Jeznach                 /* invalidate all device context matching DID */
16180c54acb8STomasz Jeznach                 func = riscv_iommu_ctx_inval_devid;
16190c54acb8STomasz Jeznach             }
16200c54acb8STomasz Jeznach             riscv_iommu_ctx_inval(s, func,
16210c54acb8STomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IODIR_DID), 0);
16220c54acb8STomasz Jeznach             break;
16230c54acb8STomasz Jeznach 
16240c54acb8STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IODIR_FUNC_INVAL_PDT,
16250c54acb8STomasz Jeznach                              RISCV_IOMMU_CMD_IODIR_OPCODE):
16260c54acb8STomasz Jeznach             if (!(cmd.dword0 & RISCV_IOMMU_CMD_IODIR_DV)) {
16270c54acb8STomasz Jeznach                 /* illegal command arguments IODIR_PDT & DV == 0 */
16280c54acb8STomasz Jeznach                 goto cmd_ill;
16290c54acb8STomasz Jeznach             } else {
16300c54acb8STomasz Jeznach                 func = riscv_iommu_ctx_inval_devid_procid;
16310c54acb8STomasz Jeznach             }
16320c54acb8STomasz Jeznach             riscv_iommu_ctx_inval(s, func,
16330c54acb8STomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IODIR_DID),
16340c54acb8STomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IODIR_PID));
16350c54acb8STomasz Jeznach             break;
16360c54acb8STomasz Jeznach 
163769a9ae48STomasz Jeznach         /* ATS commands */
163869a9ae48STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_ATS_FUNC_INVAL,
163969a9ae48STomasz Jeznach                              RISCV_IOMMU_CMD_ATS_OPCODE):
164069a9ae48STomasz Jeznach             if (!s->enable_ats) {
164169a9ae48STomasz Jeznach                 goto cmd_ill;
164269a9ae48STomasz Jeznach             }
164369a9ae48STomasz Jeznach 
164469a9ae48STomasz Jeznach             riscv_iommu_ats_inval(s, &cmd);
164569a9ae48STomasz Jeznach             break;
164669a9ae48STomasz Jeznach 
164769a9ae48STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_ATS_FUNC_PRGR,
164869a9ae48STomasz Jeznach                              RISCV_IOMMU_CMD_ATS_OPCODE):
164969a9ae48STomasz Jeznach             if (!s->enable_ats) {
165069a9ae48STomasz Jeznach                 goto cmd_ill;
165169a9ae48STomasz Jeznach             }
165269a9ae48STomasz Jeznach 
165369a9ae48STomasz Jeznach             riscv_iommu_ats_prgr(s, &cmd);
165469a9ae48STomasz Jeznach             break;
165569a9ae48STomasz Jeznach 
16560c54acb8STomasz Jeznach         default:
16570c54acb8STomasz Jeznach         cmd_ill:
16580c54acb8STomasz Jeznach             /* Invalid instruction, do not advance instruction index. */
16590c54acb8STomasz Jeznach             riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR,
16600c54acb8STomasz Jeznach                 RISCV_IOMMU_CQCSR_CMD_ILL, 0);
16610c54acb8STomasz Jeznach             goto fault;
16620c54acb8STomasz Jeznach         }
16630c54acb8STomasz Jeznach 
16640c54acb8STomasz Jeznach         /* Advance and update head pointer after command completes. */
16650c54acb8STomasz Jeznach         head = (head + 1) & s->cq_mask;
16660c54acb8STomasz Jeznach         riscv_iommu_reg_set32(s, RISCV_IOMMU_REG_CQH, head);
16670c54acb8STomasz Jeznach     }
16680c54acb8STomasz Jeznach     return;
16690c54acb8STomasz Jeznach 
16700c54acb8STomasz Jeznach fault:
16710c54acb8STomasz Jeznach     if (ctrl & RISCV_IOMMU_CQCSR_CIE) {
16720c54acb8STomasz Jeznach         riscv_iommu_notify(s, RISCV_IOMMU_INTR_CQ);
16730c54acb8STomasz Jeznach     }
16740c54acb8STomasz Jeznach }
16750c54acb8STomasz Jeznach 
16760c54acb8STomasz Jeznach static void riscv_iommu_process_cq_control(RISCVIOMMUState *s)
16770c54acb8STomasz Jeznach {
16780c54acb8STomasz Jeznach     uint64_t base;
16790c54acb8STomasz Jeznach     uint32_t ctrl_set = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQCSR);
16800c54acb8STomasz Jeznach     uint32_t ctrl_clr;
16810c54acb8STomasz Jeznach     bool enable = !!(ctrl_set & RISCV_IOMMU_CQCSR_CQEN);
16820c54acb8STomasz Jeznach     bool active = !!(ctrl_set & RISCV_IOMMU_CQCSR_CQON);
16830c54acb8STomasz Jeznach 
16840c54acb8STomasz Jeznach     if (enable && !active) {
16850c54acb8STomasz Jeznach         base = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_CQB);
16860c54acb8STomasz Jeznach         s->cq_mask = (2ULL << get_field(base, RISCV_IOMMU_CQB_LOG2SZ)) - 1;
16870c54acb8STomasz Jeznach         s->cq_addr = PPN_PHYS(get_field(base, RISCV_IOMMU_CQB_PPN));
16880c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQT], ~s->cq_mask);
16890c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_CQH], 0);
16900c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_CQT], 0);
16910c54acb8STomasz Jeznach         ctrl_set = RISCV_IOMMU_CQCSR_CQON;
16920c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_CQCSR_BUSY | RISCV_IOMMU_CQCSR_CQMF |
16930c54acb8STomasz Jeznach                    RISCV_IOMMU_CQCSR_CMD_ILL | RISCV_IOMMU_CQCSR_CMD_TO |
16940c54acb8STomasz Jeznach                    RISCV_IOMMU_CQCSR_FENCE_W_IP;
16950c54acb8STomasz Jeznach     } else if (!enable && active) {
16960c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQT], ~0);
16970c54acb8STomasz Jeznach         ctrl_set = 0;
16980c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_CQCSR_BUSY | RISCV_IOMMU_CQCSR_CQON;
16990c54acb8STomasz Jeznach     } else {
17000c54acb8STomasz Jeznach         ctrl_set = 0;
17010c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_CQCSR_BUSY;
17020c54acb8STomasz Jeznach     }
17030c54acb8STomasz Jeznach 
17040c54acb8STomasz Jeznach     riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR, ctrl_set, ctrl_clr);
17050c54acb8STomasz Jeznach }
17060c54acb8STomasz Jeznach 
17070c54acb8STomasz Jeznach static void riscv_iommu_process_fq_control(RISCVIOMMUState *s)
17080c54acb8STomasz Jeznach {
17090c54acb8STomasz Jeznach     uint64_t base;
17100c54acb8STomasz Jeznach     uint32_t ctrl_set = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQCSR);
17110c54acb8STomasz Jeznach     uint32_t ctrl_clr;
17120c54acb8STomasz Jeznach     bool enable = !!(ctrl_set & RISCV_IOMMU_FQCSR_FQEN);
17130c54acb8STomasz Jeznach     bool active = !!(ctrl_set & RISCV_IOMMU_FQCSR_FQON);
17140c54acb8STomasz Jeznach 
17150c54acb8STomasz Jeznach     if (enable && !active) {
17160c54acb8STomasz Jeznach         base = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_FQB);
17170c54acb8STomasz Jeznach         s->fq_mask = (2ULL << get_field(base, RISCV_IOMMU_FQB_LOG2SZ)) - 1;
17180c54acb8STomasz Jeznach         s->fq_addr = PPN_PHYS(get_field(base, RISCV_IOMMU_FQB_PPN));
17190c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQH], ~s->fq_mask);
17200c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_FQH], 0);
17210c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_FQT], 0);
17220c54acb8STomasz Jeznach         ctrl_set = RISCV_IOMMU_FQCSR_FQON;
17230c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_FQCSR_BUSY | RISCV_IOMMU_FQCSR_FQMF |
17240c54acb8STomasz Jeznach             RISCV_IOMMU_FQCSR_FQOF;
17250c54acb8STomasz Jeznach     } else if (!enable && active) {
17260c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQH], ~0);
17270c54acb8STomasz Jeznach         ctrl_set = 0;
17280c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_FQCSR_BUSY | RISCV_IOMMU_FQCSR_FQON;
17290c54acb8STomasz Jeznach     } else {
17300c54acb8STomasz Jeznach         ctrl_set = 0;
17310c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_FQCSR_BUSY;
17320c54acb8STomasz Jeznach     }
17330c54acb8STomasz Jeznach 
17340c54acb8STomasz Jeznach     riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_FQCSR, ctrl_set, ctrl_clr);
17350c54acb8STomasz Jeznach }
17360c54acb8STomasz Jeznach 
17370c54acb8STomasz Jeznach static void riscv_iommu_process_pq_control(RISCVIOMMUState *s)
17380c54acb8STomasz Jeznach {
17390c54acb8STomasz Jeznach     uint64_t base;
17400c54acb8STomasz Jeznach     uint32_t ctrl_set = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_PQCSR);
17410c54acb8STomasz Jeznach     uint32_t ctrl_clr;
17420c54acb8STomasz Jeznach     bool enable = !!(ctrl_set & RISCV_IOMMU_PQCSR_PQEN);
17430c54acb8STomasz Jeznach     bool active = !!(ctrl_set & RISCV_IOMMU_PQCSR_PQON);
17440c54acb8STomasz Jeznach 
17450c54acb8STomasz Jeznach     if (enable && !active) {
17460c54acb8STomasz Jeznach         base = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_PQB);
17470c54acb8STomasz Jeznach         s->pq_mask = (2ULL << get_field(base, RISCV_IOMMU_PQB_LOG2SZ)) - 1;
17480c54acb8STomasz Jeznach         s->pq_addr = PPN_PHYS(get_field(base, RISCV_IOMMU_PQB_PPN));
17490c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQH], ~s->pq_mask);
17500c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_PQH], 0);
17510c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_PQT], 0);
17520c54acb8STomasz Jeznach         ctrl_set = RISCV_IOMMU_PQCSR_PQON;
17530c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_PQCSR_BUSY | RISCV_IOMMU_PQCSR_PQMF |
17540c54acb8STomasz Jeznach             RISCV_IOMMU_PQCSR_PQOF;
17550c54acb8STomasz Jeznach     } else if (!enable && active) {
17560c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQH], ~0);
17570c54acb8STomasz Jeznach         ctrl_set = 0;
17580c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_PQCSR_BUSY | RISCV_IOMMU_PQCSR_PQON;
17590c54acb8STomasz Jeznach     } else {
17600c54acb8STomasz Jeznach         ctrl_set = 0;
17610c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_PQCSR_BUSY;
17620c54acb8STomasz Jeznach     }
17630c54acb8STomasz Jeznach 
17640c54acb8STomasz Jeznach     riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_PQCSR, ctrl_set, ctrl_clr);
17650c54acb8STomasz Jeznach }
17660c54acb8STomasz Jeznach 
1767*a7aa525bSTomasz Jeznach static void riscv_iommu_process_dbg(RISCVIOMMUState *s)
1768*a7aa525bSTomasz Jeznach {
1769*a7aa525bSTomasz Jeznach     uint64_t iova = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_TR_REQ_IOVA);
1770*a7aa525bSTomasz Jeznach     uint64_t ctrl = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_TR_REQ_CTL);
1771*a7aa525bSTomasz Jeznach     unsigned devid = get_field(ctrl, RISCV_IOMMU_TR_REQ_CTL_DID);
1772*a7aa525bSTomasz Jeznach     unsigned pid = get_field(ctrl, RISCV_IOMMU_TR_REQ_CTL_PID);
1773*a7aa525bSTomasz Jeznach     RISCVIOMMUContext *ctx;
1774*a7aa525bSTomasz Jeznach     void *ref;
1775*a7aa525bSTomasz Jeznach 
1776*a7aa525bSTomasz Jeznach     if (!(ctrl & RISCV_IOMMU_TR_REQ_CTL_GO_BUSY)) {
1777*a7aa525bSTomasz Jeznach         return;
1778*a7aa525bSTomasz Jeznach     }
1779*a7aa525bSTomasz Jeznach 
1780*a7aa525bSTomasz Jeznach     ctx = riscv_iommu_ctx(s, devid, pid, &ref);
1781*a7aa525bSTomasz Jeznach     if (ctx == NULL) {
1782*a7aa525bSTomasz Jeznach         riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_TR_RESPONSE,
1783*a7aa525bSTomasz Jeznach                                  RISCV_IOMMU_TR_RESPONSE_FAULT |
1784*a7aa525bSTomasz Jeznach                                  (RISCV_IOMMU_FQ_CAUSE_DMA_DISABLED << 10));
1785*a7aa525bSTomasz Jeznach     } else {
1786*a7aa525bSTomasz Jeznach         IOMMUTLBEntry iotlb = {
1787*a7aa525bSTomasz Jeznach             .iova = iova,
1788*a7aa525bSTomasz Jeznach             .perm = ctrl & RISCV_IOMMU_TR_REQ_CTL_NW ? IOMMU_RO : IOMMU_RW,
1789*a7aa525bSTomasz Jeznach             .addr_mask = ~0,
1790*a7aa525bSTomasz Jeznach             .target_as = NULL,
1791*a7aa525bSTomasz Jeznach         };
1792*a7aa525bSTomasz Jeznach         int fault = riscv_iommu_translate(s, ctx, &iotlb, false);
1793*a7aa525bSTomasz Jeznach         if (fault) {
1794*a7aa525bSTomasz Jeznach             iova = RISCV_IOMMU_TR_RESPONSE_FAULT | (((uint64_t) fault) << 10);
1795*a7aa525bSTomasz Jeznach         } else {
1796*a7aa525bSTomasz Jeznach             iova = iotlb.translated_addr & ~iotlb.addr_mask;
1797*a7aa525bSTomasz Jeznach             iova >>= TARGET_PAGE_BITS;
1798*a7aa525bSTomasz Jeznach             iova &= RISCV_IOMMU_TR_RESPONSE_PPN;
1799*a7aa525bSTomasz Jeznach 
1800*a7aa525bSTomasz Jeznach             /* We do not support superpages (> 4kbs) for now */
1801*a7aa525bSTomasz Jeznach             iova &= ~RISCV_IOMMU_TR_RESPONSE_S;
1802*a7aa525bSTomasz Jeznach         }
1803*a7aa525bSTomasz Jeznach         riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_TR_RESPONSE, iova);
1804*a7aa525bSTomasz Jeznach     }
1805*a7aa525bSTomasz Jeznach 
1806*a7aa525bSTomasz Jeznach     riscv_iommu_reg_mod64(s, RISCV_IOMMU_REG_TR_REQ_CTL, 0,
1807*a7aa525bSTomasz Jeznach         RISCV_IOMMU_TR_REQ_CTL_GO_BUSY);
1808*a7aa525bSTomasz Jeznach     riscv_iommu_ctx_put(s, ref);
1809*a7aa525bSTomasz Jeznach }
1810*a7aa525bSTomasz Jeznach 
18110c54acb8STomasz Jeznach typedef void riscv_iommu_process_fn(RISCVIOMMUState *s);
18120c54acb8STomasz Jeznach 
18130c54acb8STomasz Jeznach static void riscv_iommu_update_icvec(RISCVIOMMUState *s, uint64_t data)
18140c54acb8STomasz Jeznach {
18150c54acb8STomasz Jeznach     uint64_t icvec = 0;
18160c54acb8STomasz Jeznach 
18170c54acb8STomasz Jeznach     icvec |= MIN(data & RISCV_IOMMU_ICVEC_CIV,
18180c54acb8STomasz Jeznach                  s->icvec_avail_vectors & RISCV_IOMMU_ICVEC_CIV);
18190c54acb8STomasz Jeznach 
18200c54acb8STomasz Jeznach     icvec |= MIN(data & RISCV_IOMMU_ICVEC_FIV,
18210c54acb8STomasz Jeznach                  s->icvec_avail_vectors & RISCV_IOMMU_ICVEC_FIV);
18220c54acb8STomasz Jeznach 
18230c54acb8STomasz Jeznach     icvec |= MIN(data & RISCV_IOMMU_ICVEC_PMIV,
18240c54acb8STomasz Jeznach                  s->icvec_avail_vectors & RISCV_IOMMU_ICVEC_PMIV);
18250c54acb8STomasz Jeznach 
18260c54acb8STomasz Jeznach     icvec |= MIN(data & RISCV_IOMMU_ICVEC_PIV,
18270c54acb8STomasz Jeznach                  s->icvec_avail_vectors & RISCV_IOMMU_ICVEC_PIV);
18280c54acb8STomasz Jeznach 
18290c54acb8STomasz Jeznach     trace_riscv_iommu_icvec_write(data, icvec);
18300c54acb8STomasz Jeznach 
18310c54acb8STomasz Jeznach     riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_ICVEC, icvec);
18320c54acb8STomasz Jeznach }
18330c54acb8STomasz Jeznach 
18340c54acb8STomasz Jeznach static void riscv_iommu_update_ipsr(RISCVIOMMUState *s, uint64_t data)
18350c54acb8STomasz Jeznach {
18360c54acb8STomasz Jeznach     uint32_t cqcsr, fqcsr, pqcsr;
18370c54acb8STomasz Jeznach     uint32_t ipsr_set = 0;
18380c54acb8STomasz Jeznach     uint32_t ipsr_clr = 0;
18390c54acb8STomasz Jeznach 
18400c54acb8STomasz Jeznach     if (data & RISCV_IOMMU_IPSR_CIP) {
18410c54acb8STomasz Jeznach         cqcsr = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQCSR);
18420c54acb8STomasz Jeznach 
18430c54acb8STomasz Jeznach         if (cqcsr & RISCV_IOMMU_CQCSR_CIE &&
18440c54acb8STomasz Jeznach             (cqcsr & RISCV_IOMMU_CQCSR_FENCE_W_IP ||
18450c54acb8STomasz Jeznach              cqcsr & RISCV_IOMMU_CQCSR_CMD_ILL ||
18460c54acb8STomasz Jeznach              cqcsr & RISCV_IOMMU_CQCSR_CMD_TO ||
18470c54acb8STomasz Jeznach              cqcsr & RISCV_IOMMU_CQCSR_CQMF)) {
18480c54acb8STomasz Jeznach             ipsr_set |= RISCV_IOMMU_IPSR_CIP;
18490c54acb8STomasz Jeznach         } else {
18500c54acb8STomasz Jeznach             ipsr_clr |= RISCV_IOMMU_IPSR_CIP;
18510c54acb8STomasz Jeznach         }
18520c54acb8STomasz Jeznach     } else {
18530c54acb8STomasz Jeznach         ipsr_clr |= RISCV_IOMMU_IPSR_CIP;
18540c54acb8STomasz Jeznach     }
18550c54acb8STomasz Jeznach 
18560c54acb8STomasz Jeznach     if (data & RISCV_IOMMU_IPSR_FIP) {
18570c54acb8STomasz Jeznach         fqcsr = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQCSR);
18580c54acb8STomasz Jeznach 
18590c54acb8STomasz Jeznach         if (fqcsr & RISCV_IOMMU_FQCSR_FIE &&
18600c54acb8STomasz Jeznach             (fqcsr & RISCV_IOMMU_FQCSR_FQOF ||
18610c54acb8STomasz Jeznach              fqcsr & RISCV_IOMMU_FQCSR_FQMF)) {
18620c54acb8STomasz Jeznach             ipsr_set |= RISCV_IOMMU_IPSR_FIP;
18630c54acb8STomasz Jeznach         } else {
18640c54acb8STomasz Jeznach             ipsr_clr |= RISCV_IOMMU_IPSR_FIP;
18650c54acb8STomasz Jeznach         }
18660c54acb8STomasz Jeznach     } else {
18670c54acb8STomasz Jeznach         ipsr_clr |= RISCV_IOMMU_IPSR_FIP;
18680c54acb8STomasz Jeznach     }
18690c54acb8STomasz Jeznach 
18700c54acb8STomasz Jeznach     if (data & RISCV_IOMMU_IPSR_PIP) {
18710c54acb8STomasz Jeznach         pqcsr = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_PQCSR);
18720c54acb8STomasz Jeznach 
18730c54acb8STomasz Jeznach         if (pqcsr & RISCV_IOMMU_PQCSR_PIE &&
18740c54acb8STomasz Jeznach             (pqcsr & RISCV_IOMMU_PQCSR_PQOF ||
18750c54acb8STomasz Jeznach              pqcsr & RISCV_IOMMU_PQCSR_PQMF)) {
18760c54acb8STomasz Jeznach             ipsr_set |= RISCV_IOMMU_IPSR_PIP;
18770c54acb8STomasz Jeznach         } else {
18780c54acb8STomasz Jeznach             ipsr_clr |= RISCV_IOMMU_IPSR_PIP;
18790c54acb8STomasz Jeznach         }
18800c54acb8STomasz Jeznach     } else {
18810c54acb8STomasz Jeznach         ipsr_clr |= RISCV_IOMMU_IPSR_PIP;
18820c54acb8STomasz Jeznach     }
18830c54acb8STomasz Jeznach 
18840c54acb8STomasz Jeznach     riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_IPSR, ipsr_set, ipsr_clr);
18850c54acb8STomasz Jeznach }
18860c54acb8STomasz Jeznach 
18870c54acb8STomasz Jeznach /*
18880c54acb8STomasz Jeznach  * Write the resulting value of 'data' for the reg specified
18890c54acb8STomasz Jeznach  * by 'reg_addr', after considering read-only/read-write/write-clear
18900c54acb8STomasz Jeznach  * bits, in the pointer 'dest'.
18910c54acb8STomasz Jeznach  *
18920c54acb8STomasz Jeznach  * The result is written in little-endian.
18930c54acb8STomasz Jeznach  */
18940c54acb8STomasz Jeznach static void riscv_iommu_write_reg_val(RISCVIOMMUState *s,
18950c54acb8STomasz Jeznach                                       void *dest, hwaddr reg_addr,
18960c54acb8STomasz Jeznach                                       int size, uint64_t data)
18970c54acb8STomasz Jeznach {
18980c54acb8STomasz Jeznach     uint64_t ro = ldn_le_p(&s->regs_ro[reg_addr], size);
18990c54acb8STomasz Jeznach     uint64_t wc = ldn_le_p(&s->regs_wc[reg_addr], size);
19000c54acb8STomasz Jeznach     uint64_t rw = ldn_le_p(&s->regs_rw[reg_addr], size);
19010c54acb8STomasz Jeznach 
19020c54acb8STomasz Jeznach     stn_le_p(dest, size, ((rw & ro) | (data & ~ro)) & ~(data & wc));
19030c54acb8STomasz Jeznach }
19040c54acb8STomasz Jeznach 
19050c54acb8STomasz Jeznach static MemTxResult riscv_iommu_mmio_write(void *opaque, hwaddr addr,
19060c54acb8STomasz Jeznach                                           uint64_t data, unsigned size,
19070c54acb8STomasz Jeznach                                           MemTxAttrs attrs)
19080c54acb8STomasz Jeznach {
19090c54acb8STomasz Jeznach     riscv_iommu_process_fn *process_fn = NULL;
19100c54acb8STomasz Jeznach     RISCVIOMMUState *s = opaque;
19110c54acb8STomasz Jeznach     uint32_t regb = addr & ~3;
19120c54acb8STomasz Jeznach     uint32_t busy = 0;
19130c54acb8STomasz Jeznach     uint64_t val = 0;
19140c54acb8STomasz Jeznach 
19150c54acb8STomasz Jeznach     if ((addr & (size - 1)) != 0) {
19160c54acb8STomasz Jeznach         /* Unsupported MMIO alignment or access size */
19170c54acb8STomasz Jeznach         return MEMTX_ERROR;
19180c54acb8STomasz Jeznach     }
19190c54acb8STomasz Jeznach 
19200c54acb8STomasz Jeznach     if (addr + size > RISCV_IOMMU_REG_MSI_CONFIG) {
19210c54acb8STomasz Jeznach         /* Unsupported MMIO access location. */
19220c54acb8STomasz Jeznach         return MEMTX_ACCESS_ERROR;
19230c54acb8STomasz Jeznach     }
19240c54acb8STomasz Jeznach 
19250c54acb8STomasz Jeznach     /* Track actionable MMIO write. */
19260c54acb8STomasz Jeznach     switch (regb) {
19270c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_DDTP:
19280c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_DDTP + 4:
19290c54acb8STomasz Jeznach         process_fn = riscv_iommu_process_ddtp;
19300c54acb8STomasz Jeznach         regb = RISCV_IOMMU_REG_DDTP;
19310c54acb8STomasz Jeznach         busy = RISCV_IOMMU_DDTP_BUSY;
19320c54acb8STomasz Jeznach         break;
19330c54acb8STomasz Jeznach 
19340c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_CQT:
19350c54acb8STomasz Jeznach         process_fn = riscv_iommu_process_cq_tail;
19360c54acb8STomasz Jeznach         break;
19370c54acb8STomasz Jeznach 
19380c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_CQCSR:
19390c54acb8STomasz Jeznach         process_fn = riscv_iommu_process_cq_control;
19400c54acb8STomasz Jeznach         busy = RISCV_IOMMU_CQCSR_BUSY;
19410c54acb8STomasz Jeznach         break;
19420c54acb8STomasz Jeznach 
19430c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_FQCSR:
19440c54acb8STomasz Jeznach         process_fn = riscv_iommu_process_fq_control;
19450c54acb8STomasz Jeznach         busy = RISCV_IOMMU_FQCSR_BUSY;
19460c54acb8STomasz Jeznach         break;
19470c54acb8STomasz Jeznach 
19480c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_PQCSR:
19490c54acb8STomasz Jeznach         process_fn = riscv_iommu_process_pq_control;
19500c54acb8STomasz Jeznach         busy = RISCV_IOMMU_PQCSR_BUSY;
19510c54acb8STomasz Jeznach         break;
19520c54acb8STomasz Jeznach 
19530c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_ICVEC:
19540c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_IPSR:
19550c54acb8STomasz Jeznach         /*
19560c54acb8STomasz Jeznach          * ICVEC and IPSR have special read/write procedures. We'll
19570c54acb8STomasz Jeznach          * call their respective helpers and exit.
19580c54acb8STomasz Jeznach          */
19590c54acb8STomasz Jeznach         riscv_iommu_write_reg_val(s, &val, addr, size, data);
19600c54acb8STomasz Jeznach 
19610c54acb8STomasz Jeznach         /*
19620c54acb8STomasz Jeznach          * 'val' is stored as LE. Switch to host endianess
19630c54acb8STomasz Jeznach          * before using it.
19640c54acb8STomasz Jeznach          */
19650c54acb8STomasz Jeznach         val = le64_to_cpu(val);
19660c54acb8STomasz Jeznach 
19670c54acb8STomasz Jeznach         if (regb == RISCV_IOMMU_REG_ICVEC) {
19680c54acb8STomasz Jeznach             riscv_iommu_update_icvec(s, val);
19690c54acb8STomasz Jeznach         } else {
19700c54acb8STomasz Jeznach             riscv_iommu_update_ipsr(s, val);
19710c54acb8STomasz Jeznach         }
19720c54acb8STomasz Jeznach 
19730c54acb8STomasz Jeznach         return MEMTX_OK;
19740c54acb8STomasz Jeznach 
1975*a7aa525bSTomasz Jeznach     case RISCV_IOMMU_REG_TR_REQ_CTL:
1976*a7aa525bSTomasz Jeznach         process_fn = riscv_iommu_process_dbg;
1977*a7aa525bSTomasz Jeznach         regb = RISCV_IOMMU_REG_TR_REQ_CTL;
1978*a7aa525bSTomasz Jeznach         busy = RISCV_IOMMU_TR_REQ_CTL_GO_BUSY;
1979*a7aa525bSTomasz Jeznach         break;
1980*a7aa525bSTomasz Jeznach 
19810c54acb8STomasz Jeznach     default:
19820c54acb8STomasz Jeznach         break;
19830c54acb8STomasz Jeznach     }
19840c54acb8STomasz Jeznach 
19850c54acb8STomasz Jeznach     /*
19860c54acb8STomasz Jeznach      * Registers update might be not synchronized with core logic.
19870c54acb8STomasz Jeznach      * If system software updates register when relevant BUSY bit
19880c54acb8STomasz Jeznach      * is set IOMMU behavior of additional writes to the register
19890c54acb8STomasz Jeznach      * is UNSPECIFIED.
19900c54acb8STomasz Jeznach      */
19910c54acb8STomasz Jeznach     riscv_iommu_write_reg_val(s, &s->regs_rw[addr], addr, size, data);
19920c54acb8STomasz Jeznach 
19930c54acb8STomasz Jeznach     /* Busy flag update, MSB 4-byte register. */
19940c54acb8STomasz Jeznach     if (busy) {
19950c54acb8STomasz Jeznach         uint32_t rw = ldl_le_p(&s->regs_rw[regb]);
19960c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[regb], rw | busy);
19970c54acb8STomasz Jeznach     }
19980c54acb8STomasz Jeznach 
19990c54acb8STomasz Jeznach     if (process_fn) {
20000c54acb8STomasz Jeznach         process_fn(s);
20010c54acb8STomasz Jeznach     }
20020c54acb8STomasz Jeznach 
20030c54acb8STomasz Jeznach     return MEMTX_OK;
20040c54acb8STomasz Jeznach }
20050c54acb8STomasz Jeznach 
20060c54acb8STomasz Jeznach static MemTxResult riscv_iommu_mmio_read(void *opaque, hwaddr addr,
20070c54acb8STomasz Jeznach     uint64_t *data, unsigned size, MemTxAttrs attrs)
20080c54acb8STomasz Jeznach {
20090c54acb8STomasz Jeznach     RISCVIOMMUState *s = opaque;
20100c54acb8STomasz Jeznach     uint64_t val = -1;
20110c54acb8STomasz Jeznach     uint8_t *ptr;
20120c54acb8STomasz Jeznach 
20130c54acb8STomasz Jeznach     if ((addr & (size - 1)) != 0) {
20140c54acb8STomasz Jeznach         /* Unsupported MMIO alignment. */
20150c54acb8STomasz Jeznach         return MEMTX_ERROR;
20160c54acb8STomasz Jeznach     }
20170c54acb8STomasz Jeznach 
20180c54acb8STomasz Jeznach     if (addr + size > RISCV_IOMMU_REG_MSI_CONFIG) {
20190c54acb8STomasz Jeznach         return MEMTX_ACCESS_ERROR;
20200c54acb8STomasz Jeznach     }
20210c54acb8STomasz Jeznach 
20220c54acb8STomasz Jeznach     ptr = &s->regs_rw[addr];
20230c54acb8STomasz Jeznach     val = ldn_le_p(ptr, size);
20240c54acb8STomasz Jeznach 
20250c54acb8STomasz Jeznach     *data = val;
20260c54acb8STomasz Jeznach 
20270c54acb8STomasz Jeznach     return MEMTX_OK;
20280c54acb8STomasz Jeznach }
20290c54acb8STomasz Jeznach 
20300c54acb8STomasz Jeznach static const MemoryRegionOps riscv_iommu_mmio_ops = {
20310c54acb8STomasz Jeznach     .read_with_attrs = riscv_iommu_mmio_read,
20320c54acb8STomasz Jeznach     .write_with_attrs = riscv_iommu_mmio_write,
20330c54acb8STomasz Jeznach     .endianness = DEVICE_NATIVE_ENDIAN,
20340c54acb8STomasz Jeznach     .impl = {
20350c54acb8STomasz Jeznach         .min_access_size = 4,
20360c54acb8STomasz Jeznach         .max_access_size = 8,
20370c54acb8STomasz Jeznach         .unaligned = false,
20380c54acb8STomasz Jeznach     },
20390c54acb8STomasz Jeznach     .valid = {
20400c54acb8STomasz Jeznach         .min_access_size = 4,
20410c54acb8STomasz Jeznach         .max_access_size = 8,
20420c54acb8STomasz Jeznach     }
20430c54acb8STomasz Jeznach };
20440c54acb8STomasz Jeznach 
20450c54acb8STomasz Jeznach /*
20460c54acb8STomasz Jeznach  * Translations matching MSI pattern check are redirected to "riscv-iommu-trap"
20470c54acb8STomasz Jeznach  * memory region as untranslated address, for additional MSI/MRIF interception
20480c54acb8STomasz Jeznach  * by IOMMU interrupt remapping implementation.
20490c54acb8STomasz Jeznach  * Note: Device emulation code generating an MSI is expected to provide a valid
20500c54acb8STomasz Jeznach  * memory transaction attributes with requested_id set.
20510c54acb8STomasz Jeznach  */
20520c54acb8STomasz Jeznach static MemTxResult riscv_iommu_trap_write(void *opaque, hwaddr addr,
20530c54acb8STomasz Jeznach     uint64_t data, unsigned size, MemTxAttrs attrs)
20540c54acb8STomasz Jeznach {
20550c54acb8STomasz Jeznach     RISCVIOMMUState* s = (RISCVIOMMUState *)opaque;
20560c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx;
20570c54acb8STomasz Jeznach     MemTxResult res;
20580c54acb8STomasz Jeznach     void *ref;
20590c54acb8STomasz Jeznach     uint32_t devid = attrs.requester_id;
20600c54acb8STomasz Jeznach 
20610c54acb8STomasz Jeznach     if (attrs.unspecified) {
20620c54acb8STomasz Jeznach         return MEMTX_ACCESS_ERROR;
20630c54acb8STomasz Jeznach     }
20640c54acb8STomasz Jeznach 
20650c54acb8STomasz Jeznach     /* FIXME: PCIe bus remapping for attached endpoints. */
20660c54acb8STomasz Jeznach     devid |= s->bus << 8;
20670c54acb8STomasz Jeznach 
20680c54acb8STomasz Jeznach     ctx = riscv_iommu_ctx(s, devid, 0, &ref);
20690c54acb8STomasz Jeznach     if (ctx == NULL) {
20700c54acb8STomasz Jeznach         res = MEMTX_ACCESS_ERROR;
20710c54acb8STomasz Jeznach     } else {
20720c54acb8STomasz Jeznach         res = riscv_iommu_msi_write(s, ctx, addr, data, size, attrs);
20730c54acb8STomasz Jeznach     }
20740c54acb8STomasz Jeznach     riscv_iommu_ctx_put(s, ref);
20750c54acb8STomasz Jeznach     return res;
20760c54acb8STomasz Jeznach }
20770c54acb8STomasz Jeznach 
20780c54acb8STomasz Jeznach static MemTxResult riscv_iommu_trap_read(void *opaque, hwaddr addr,
20790c54acb8STomasz Jeznach     uint64_t *data, unsigned size, MemTxAttrs attrs)
20800c54acb8STomasz Jeznach {
20810c54acb8STomasz Jeznach     return MEMTX_ACCESS_ERROR;
20820c54acb8STomasz Jeznach }
20830c54acb8STomasz Jeznach 
20840c54acb8STomasz Jeznach static const MemoryRegionOps riscv_iommu_trap_ops = {
20850c54acb8STomasz Jeznach     .read_with_attrs = riscv_iommu_trap_read,
20860c54acb8STomasz Jeznach     .write_with_attrs = riscv_iommu_trap_write,
20870c54acb8STomasz Jeznach     .endianness = DEVICE_LITTLE_ENDIAN,
20880c54acb8STomasz Jeznach     .impl = {
20890c54acb8STomasz Jeznach         .min_access_size = 4,
20900c54acb8STomasz Jeznach         .max_access_size = 8,
20910c54acb8STomasz Jeznach         .unaligned = true,
20920c54acb8STomasz Jeznach     },
20930c54acb8STomasz Jeznach     .valid = {
20940c54acb8STomasz Jeznach         .min_access_size = 4,
20950c54acb8STomasz Jeznach         .max_access_size = 8,
20960c54acb8STomasz Jeznach     }
20970c54acb8STomasz Jeznach };
20980c54acb8STomasz Jeznach 
20990c54acb8STomasz Jeznach static void riscv_iommu_realize(DeviceState *dev, Error **errp)
21000c54acb8STomasz Jeznach {
21010c54acb8STomasz Jeznach     RISCVIOMMUState *s = RISCV_IOMMU(dev);
21020c54acb8STomasz Jeznach 
21030c54acb8STomasz Jeznach     s->cap = s->version & RISCV_IOMMU_CAP_VERSION;
21040c54acb8STomasz Jeznach     if (s->enable_msi) {
21050c54acb8STomasz Jeznach         s->cap |= RISCV_IOMMU_CAP_MSI_FLAT | RISCV_IOMMU_CAP_MSI_MRIF;
21060c54acb8STomasz Jeznach     }
210769a9ae48STomasz Jeznach     if (s->enable_ats) {
210869a9ae48STomasz Jeznach         s->cap |= RISCV_IOMMU_CAP_ATS;
210969a9ae48STomasz Jeznach     }
21100c54acb8STomasz Jeznach     if (s->enable_s_stage) {
21110c54acb8STomasz Jeznach         s->cap |= RISCV_IOMMU_CAP_SV32 | RISCV_IOMMU_CAP_SV39 |
21120c54acb8STomasz Jeznach                   RISCV_IOMMU_CAP_SV48 | RISCV_IOMMU_CAP_SV57;
21130c54acb8STomasz Jeznach     }
21140c54acb8STomasz Jeznach     if (s->enable_g_stage) {
21150c54acb8STomasz Jeznach         s->cap |= RISCV_IOMMU_CAP_SV32X4 | RISCV_IOMMU_CAP_SV39X4 |
21160c54acb8STomasz Jeznach                   RISCV_IOMMU_CAP_SV48X4 | RISCV_IOMMU_CAP_SV57X4;
21170c54acb8STomasz Jeznach     }
2118*a7aa525bSTomasz Jeznach     /* Enable translation debug interface */
2119*a7aa525bSTomasz Jeznach     s->cap |= RISCV_IOMMU_CAP_DBG;
2120*a7aa525bSTomasz Jeznach 
21210c54acb8STomasz Jeznach     /* Report QEMU target physical address space limits */
21220c54acb8STomasz Jeznach     s->cap = set_field(s->cap, RISCV_IOMMU_CAP_PAS,
21230c54acb8STomasz Jeznach                        TARGET_PHYS_ADDR_SPACE_BITS);
21240c54acb8STomasz Jeznach 
21250c54acb8STomasz Jeznach     /* TODO: method to report supported PID bits */
21260c54acb8STomasz Jeznach     s->pid_bits = 8; /* restricted to size of MemTxAttrs.pid */
21270c54acb8STomasz Jeznach     s->cap |= RISCV_IOMMU_CAP_PD8;
21280c54acb8STomasz Jeznach 
21290c54acb8STomasz Jeznach     /* Out-of-reset translation mode: OFF (DMA disabled) BARE (passthrough) */
21300c54acb8STomasz Jeznach     s->ddtp = set_field(0, RISCV_IOMMU_DDTP_MODE, s->enable_off ?
21310c54acb8STomasz Jeznach                         RISCV_IOMMU_DDTP_MODE_OFF : RISCV_IOMMU_DDTP_MODE_BARE);
21320c54acb8STomasz Jeznach 
21330c54acb8STomasz Jeznach     /* register storage */
21340c54acb8STomasz Jeznach     s->regs_rw = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE);
21350c54acb8STomasz Jeznach     s->regs_ro = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE);
21360c54acb8STomasz Jeznach     s->regs_wc = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE);
21370c54acb8STomasz Jeznach 
21380c54acb8STomasz Jeznach      /* Mark all registers read-only */
21390c54acb8STomasz Jeznach     memset(s->regs_ro, 0xff, RISCV_IOMMU_REG_SIZE);
21400c54acb8STomasz Jeznach 
21410c54acb8STomasz Jeznach     /*
21420c54acb8STomasz Jeznach      * Register complete MMIO space, including MSI/PBA registers.
21430c54acb8STomasz Jeznach      * Note, PCIDevice implementation will add overlapping MR for MSI/PBA,
21440c54acb8STomasz Jeznach      * managed directly by the PCIDevice implementation.
21450c54acb8STomasz Jeznach      */
21460c54acb8STomasz Jeznach     memory_region_init_io(&s->regs_mr, OBJECT(dev), &riscv_iommu_mmio_ops, s,
21470c54acb8STomasz Jeznach         "riscv-iommu-regs", RISCV_IOMMU_REG_SIZE);
21480c54acb8STomasz Jeznach 
21490c54acb8STomasz Jeznach     /* Set power-on register state */
21500c54acb8STomasz Jeznach     stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_CAP], s->cap);
21510c54acb8STomasz Jeznach     stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_FCTL], 0);
21520c54acb8STomasz Jeznach     stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_FCTL],
21530c54acb8STomasz Jeznach              ~(RISCV_IOMMU_FCTL_BE | RISCV_IOMMU_FCTL_WSI));
21540c54acb8STomasz Jeznach     stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_DDTP],
21550c54acb8STomasz Jeznach         ~(RISCV_IOMMU_DDTP_PPN | RISCV_IOMMU_DDTP_MODE));
21560c54acb8STomasz Jeznach     stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQB],
21570c54acb8STomasz Jeznach         ~(RISCV_IOMMU_CQB_LOG2SZ | RISCV_IOMMU_CQB_PPN));
21580c54acb8STomasz Jeznach     stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQB],
21590c54acb8STomasz Jeznach         ~(RISCV_IOMMU_FQB_LOG2SZ | RISCV_IOMMU_FQB_PPN));
21600c54acb8STomasz Jeznach     stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQB],
21610c54acb8STomasz Jeznach         ~(RISCV_IOMMU_PQB_LOG2SZ | RISCV_IOMMU_PQB_PPN));
21620c54acb8STomasz Jeznach     stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_CQCSR], RISCV_IOMMU_CQCSR_CQMF |
21630c54acb8STomasz Jeznach         RISCV_IOMMU_CQCSR_CMD_TO | RISCV_IOMMU_CQCSR_CMD_ILL);
21640c54acb8STomasz Jeznach     stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQCSR], RISCV_IOMMU_CQCSR_CQON |
21650c54acb8STomasz Jeznach         RISCV_IOMMU_CQCSR_BUSY);
21660c54acb8STomasz Jeznach     stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_FQCSR], RISCV_IOMMU_FQCSR_FQMF |
21670c54acb8STomasz Jeznach         RISCV_IOMMU_FQCSR_FQOF);
21680c54acb8STomasz Jeznach     stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQCSR], RISCV_IOMMU_FQCSR_FQON |
21690c54acb8STomasz Jeznach         RISCV_IOMMU_FQCSR_BUSY);
21700c54acb8STomasz Jeznach     stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_PQCSR], RISCV_IOMMU_PQCSR_PQMF |
21710c54acb8STomasz Jeznach         RISCV_IOMMU_PQCSR_PQOF);
21720c54acb8STomasz Jeznach     stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQCSR], RISCV_IOMMU_PQCSR_PQON |
21730c54acb8STomasz Jeznach         RISCV_IOMMU_PQCSR_BUSY);
21740c54acb8STomasz Jeznach     stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_IPSR], ~0);
21750c54acb8STomasz Jeznach     stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_ICVEC], 0);
21760c54acb8STomasz Jeznach     stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_DDTP], s->ddtp);
2177*a7aa525bSTomasz Jeznach     /* If debug registers enabled. */
2178*a7aa525bSTomasz Jeznach     if (s->cap & RISCV_IOMMU_CAP_DBG) {
2179*a7aa525bSTomasz Jeznach         stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_TR_REQ_IOVA], 0);
2180*a7aa525bSTomasz Jeznach         stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_TR_REQ_CTL],
2181*a7aa525bSTomasz Jeznach             RISCV_IOMMU_TR_REQ_CTL_GO_BUSY);
2182*a7aa525bSTomasz Jeznach     }
21830c54acb8STomasz Jeznach 
21840c54acb8STomasz Jeznach     /* Memory region for downstream access, if specified. */
21850c54acb8STomasz Jeznach     if (s->target_mr) {
21860c54acb8STomasz Jeznach         s->target_as = g_new0(AddressSpace, 1);
21870c54acb8STomasz Jeznach         address_space_init(s->target_as, s->target_mr,
21880c54acb8STomasz Jeznach             "riscv-iommu-downstream");
21890c54acb8STomasz Jeznach     } else {
21900c54acb8STomasz Jeznach         /* Fallback to global system memory. */
21910c54acb8STomasz Jeznach         s->target_as = &address_space_memory;
21920c54acb8STomasz Jeznach     }
21930c54acb8STomasz Jeznach 
21940c54acb8STomasz Jeznach     /* Memory region for untranslated MRIF/MSI writes */
21950c54acb8STomasz Jeznach     memory_region_init_io(&s->trap_mr, OBJECT(dev), &riscv_iommu_trap_ops, s,
21960c54acb8STomasz Jeznach             "riscv-iommu-trap", ~0ULL);
21970c54acb8STomasz Jeznach     address_space_init(&s->trap_as, &s->trap_mr, "riscv-iommu-trap-as");
21980c54acb8STomasz Jeznach 
21990c54acb8STomasz Jeznach     /* Device translation context cache */
22000c54acb8STomasz Jeznach     s->ctx_cache = g_hash_table_new_full(riscv_iommu_ctx_hash,
22010c54acb8STomasz Jeznach                                          riscv_iommu_ctx_equal,
22020c54acb8STomasz Jeznach                                          g_free, NULL);
22030c54acb8STomasz Jeznach 
22049d085a1cSTomasz Jeznach     s->iot_cache = g_hash_table_new_full(riscv_iommu_iot_hash,
22059d085a1cSTomasz Jeznach                                          riscv_iommu_iot_equal,
22069d085a1cSTomasz Jeznach                                          g_free, NULL);
22079d085a1cSTomasz Jeznach 
22080c54acb8STomasz Jeznach     s->iommus.le_next = NULL;
22090c54acb8STomasz Jeznach     s->iommus.le_prev = NULL;
22100c54acb8STomasz Jeznach     QLIST_INIT(&s->spaces);
22110c54acb8STomasz Jeznach }
22120c54acb8STomasz Jeznach 
22130c54acb8STomasz Jeznach static void riscv_iommu_unrealize(DeviceState *dev)
22140c54acb8STomasz Jeznach {
22150c54acb8STomasz Jeznach     RISCVIOMMUState *s = RISCV_IOMMU(dev);
22160c54acb8STomasz Jeznach 
22179d085a1cSTomasz Jeznach     g_hash_table_unref(s->iot_cache);
22180c54acb8STomasz Jeznach     g_hash_table_unref(s->ctx_cache);
22190c54acb8STomasz Jeznach }
22200c54acb8STomasz Jeznach 
22210c54acb8STomasz Jeznach static Property riscv_iommu_properties[] = {
22220c54acb8STomasz Jeznach     DEFINE_PROP_UINT32("version", RISCVIOMMUState, version,
22230c54acb8STomasz Jeznach         RISCV_IOMMU_SPEC_DOT_VER),
22240c54acb8STomasz Jeznach     DEFINE_PROP_UINT32("bus", RISCVIOMMUState, bus, 0x0),
22259d085a1cSTomasz Jeznach     DEFINE_PROP_UINT32("ioatc-limit", RISCVIOMMUState, iot_limit,
22269d085a1cSTomasz Jeznach         LIMIT_CACHE_IOT),
22270c54acb8STomasz Jeznach     DEFINE_PROP_BOOL("intremap", RISCVIOMMUState, enable_msi, TRUE),
222869a9ae48STomasz Jeznach     DEFINE_PROP_BOOL("ats", RISCVIOMMUState, enable_ats, TRUE),
22290c54acb8STomasz Jeznach     DEFINE_PROP_BOOL("off", RISCVIOMMUState, enable_off, TRUE),
22300c54acb8STomasz Jeznach     DEFINE_PROP_BOOL("s-stage", RISCVIOMMUState, enable_s_stage, TRUE),
22310c54acb8STomasz Jeznach     DEFINE_PROP_BOOL("g-stage", RISCVIOMMUState, enable_g_stage, TRUE),
22320c54acb8STomasz Jeznach     DEFINE_PROP_LINK("downstream-mr", RISCVIOMMUState, target_mr,
22330c54acb8STomasz Jeznach         TYPE_MEMORY_REGION, MemoryRegion *),
22340c54acb8STomasz Jeznach     DEFINE_PROP_END_OF_LIST(),
22350c54acb8STomasz Jeznach };
22360c54acb8STomasz Jeznach 
22370c54acb8STomasz Jeznach static void riscv_iommu_class_init(ObjectClass *klass, void* data)
22380c54acb8STomasz Jeznach {
22390c54acb8STomasz Jeznach     DeviceClass *dc = DEVICE_CLASS(klass);
22400c54acb8STomasz Jeznach 
22410c54acb8STomasz Jeznach     /* internal device for riscv-iommu-{pci/sys}, not user-creatable */
22420c54acb8STomasz Jeznach     dc->user_creatable = false;
22430c54acb8STomasz Jeznach     dc->realize = riscv_iommu_realize;
22440c54acb8STomasz Jeznach     dc->unrealize = riscv_iommu_unrealize;
22450c54acb8STomasz Jeznach     device_class_set_props(dc, riscv_iommu_properties);
22460c54acb8STomasz Jeznach }
22470c54acb8STomasz Jeznach 
22480c54acb8STomasz Jeznach static const TypeInfo riscv_iommu_info = {
22490c54acb8STomasz Jeznach     .name = TYPE_RISCV_IOMMU,
22500c54acb8STomasz Jeznach     .parent = TYPE_DEVICE,
22510c54acb8STomasz Jeznach     .instance_size = sizeof(RISCVIOMMUState),
22520c54acb8STomasz Jeznach     .class_init = riscv_iommu_class_init,
22530c54acb8STomasz Jeznach };
22540c54acb8STomasz Jeznach 
22550c54acb8STomasz Jeznach static const char *IOMMU_FLAG_STR[] = {
22560c54acb8STomasz Jeznach     "NA",
22570c54acb8STomasz Jeznach     "RO",
22580c54acb8STomasz Jeznach     "WR",
22590c54acb8STomasz Jeznach     "RW",
22600c54acb8STomasz Jeznach };
22610c54acb8STomasz Jeznach 
22620c54acb8STomasz Jeznach /* RISC-V IOMMU Memory Region - Address Translation Space */
22630c54acb8STomasz Jeznach static IOMMUTLBEntry riscv_iommu_memory_region_translate(
22640c54acb8STomasz Jeznach     IOMMUMemoryRegion *iommu_mr, hwaddr addr,
22650c54acb8STomasz Jeznach     IOMMUAccessFlags flag, int iommu_idx)
22660c54acb8STomasz Jeznach {
22670c54acb8STomasz Jeznach     RISCVIOMMUSpace *as = container_of(iommu_mr, RISCVIOMMUSpace, iova_mr);
22680c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx;
22690c54acb8STomasz Jeznach     void *ref;
22700c54acb8STomasz Jeznach     IOMMUTLBEntry iotlb = {
22710c54acb8STomasz Jeznach         .iova = addr,
22720c54acb8STomasz Jeznach         .target_as = as->iommu->target_as,
22730c54acb8STomasz Jeznach         .addr_mask = ~0ULL,
22740c54acb8STomasz Jeznach         .perm = flag,
22750c54acb8STomasz Jeznach     };
22760c54acb8STomasz Jeznach 
22770c54acb8STomasz Jeznach     ctx = riscv_iommu_ctx(as->iommu, as->devid, iommu_idx, &ref);
22780c54acb8STomasz Jeznach     if (ctx == NULL) {
22790c54acb8STomasz Jeznach         /* Translation disabled or invalid. */
22800c54acb8STomasz Jeznach         iotlb.addr_mask = 0;
22810c54acb8STomasz Jeznach         iotlb.perm = IOMMU_NONE;
22829d085a1cSTomasz Jeznach     } else if (riscv_iommu_translate(as->iommu, ctx, &iotlb, true)) {
22830c54acb8STomasz Jeznach         /* Translation disabled or fault reported. */
22840c54acb8STomasz Jeznach         iotlb.addr_mask = 0;
22850c54acb8STomasz Jeznach         iotlb.perm = IOMMU_NONE;
22860c54acb8STomasz Jeznach     }
22870c54acb8STomasz Jeznach 
22880c54acb8STomasz Jeznach     /* Trace all dma translations with original access flags. */
22890c54acb8STomasz Jeznach     trace_riscv_iommu_dma(as->iommu->parent_obj.id, PCI_BUS_NUM(as->devid),
22900c54acb8STomasz Jeznach                           PCI_SLOT(as->devid), PCI_FUNC(as->devid), iommu_idx,
22910c54acb8STomasz Jeznach                           IOMMU_FLAG_STR[flag & IOMMU_RW], iotlb.iova,
22920c54acb8STomasz Jeznach                           iotlb.translated_addr);
22930c54acb8STomasz Jeznach 
22940c54acb8STomasz Jeznach     riscv_iommu_ctx_put(as->iommu, ref);
22950c54acb8STomasz Jeznach 
22960c54acb8STomasz Jeznach     return iotlb;
22970c54acb8STomasz Jeznach }
22980c54acb8STomasz Jeznach 
22990c54acb8STomasz Jeznach static int riscv_iommu_memory_region_notify(
23000c54acb8STomasz Jeznach     IOMMUMemoryRegion *iommu_mr, IOMMUNotifierFlag old,
23010c54acb8STomasz Jeznach     IOMMUNotifierFlag new, Error **errp)
23020c54acb8STomasz Jeznach {
23030c54acb8STomasz Jeznach     RISCVIOMMUSpace *as = container_of(iommu_mr, RISCVIOMMUSpace, iova_mr);
23040c54acb8STomasz Jeznach 
23050c54acb8STomasz Jeznach     if (old == IOMMU_NOTIFIER_NONE) {
23060c54acb8STomasz Jeznach         as->notifier = true;
23070c54acb8STomasz Jeznach         trace_riscv_iommu_notifier_add(iommu_mr->parent_obj.name);
23080c54acb8STomasz Jeznach     } else if (new == IOMMU_NOTIFIER_NONE) {
23090c54acb8STomasz Jeznach         as->notifier = false;
23100c54acb8STomasz Jeznach         trace_riscv_iommu_notifier_del(iommu_mr->parent_obj.name);
23110c54acb8STomasz Jeznach     }
23120c54acb8STomasz Jeznach 
23130c54acb8STomasz Jeznach     return 0;
23140c54acb8STomasz Jeznach }
23150c54acb8STomasz Jeznach 
23160c54acb8STomasz Jeznach static inline bool pci_is_iommu(PCIDevice *pdev)
23170c54acb8STomasz Jeznach {
23180c54acb8STomasz Jeznach     return pci_get_word(pdev->config + PCI_CLASS_DEVICE) == 0x0806;
23190c54acb8STomasz Jeznach }
23200c54acb8STomasz Jeznach 
23210c54acb8STomasz Jeznach static AddressSpace *riscv_iommu_find_as(PCIBus *bus, void *opaque, int devfn)
23220c54acb8STomasz Jeznach {
23230c54acb8STomasz Jeznach     RISCVIOMMUState *s = (RISCVIOMMUState *) opaque;
23240c54acb8STomasz Jeznach     PCIDevice *pdev = pci_find_device(bus, pci_bus_num(bus), devfn);
23250c54acb8STomasz Jeznach     AddressSpace *as = NULL;
23260c54acb8STomasz Jeznach 
23270c54acb8STomasz Jeznach     if (pdev && pci_is_iommu(pdev)) {
23280c54acb8STomasz Jeznach         return s->target_as;
23290c54acb8STomasz Jeznach     }
23300c54acb8STomasz Jeznach 
23310c54acb8STomasz Jeznach     /* Find first registered IOMMU device */
23320c54acb8STomasz Jeznach     while (s->iommus.le_prev) {
23330c54acb8STomasz Jeznach         s = *(s->iommus.le_prev);
23340c54acb8STomasz Jeznach     }
23350c54acb8STomasz Jeznach 
23360c54acb8STomasz Jeznach     /* Find first matching IOMMU */
23370c54acb8STomasz Jeznach     while (s != NULL && as == NULL) {
23380c54acb8STomasz Jeznach         as = riscv_iommu_space(s, PCI_BUILD_BDF(pci_bus_num(bus), devfn));
23390c54acb8STomasz Jeznach         s = s->iommus.le_next;
23400c54acb8STomasz Jeznach     }
23410c54acb8STomasz Jeznach 
23420c54acb8STomasz Jeznach     return as ? as : &address_space_memory;
23430c54acb8STomasz Jeznach }
23440c54acb8STomasz Jeznach 
23450c54acb8STomasz Jeznach static const PCIIOMMUOps riscv_iommu_ops = {
23460c54acb8STomasz Jeznach     .get_address_space = riscv_iommu_find_as,
23470c54acb8STomasz Jeznach };
23480c54acb8STomasz Jeznach 
23490c54acb8STomasz Jeznach void riscv_iommu_pci_setup_iommu(RISCVIOMMUState *iommu, PCIBus *bus,
23500c54acb8STomasz Jeznach         Error **errp)
23510c54acb8STomasz Jeznach {
23520c54acb8STomasz Jeznach     if (bus->iommu_ops &&
23530c54acb8STomasz Jeznach         bus->iommu_ops->get_address_space == riscv_iommu_find_as) {
23540c54acb8STomasz Jeznach         /* Allow multiple IOMMUs on the same PCIe bus, link known devices */
23550c54acb8STomasz Jeznach         RISCVIOMMUState *last = (RISCVIOMMUState *)bus->iommu_opaque;
23560c54acb8STomasz Jeznach         QLIST_INSERT_AFTER(last, iommu, iommus);
23570c54acb8STomasz Jeznach     } else if (!bus->iommu_ops && !bus->iommu_opaque) {
23580c54acb8STomasz Jeznach         pci_setup_iommu(bus, &riscv_iommu_ops, iommu);
23590c54acb8STomasz Jeznach     } else {
23600c54acb8STomasz Jeznach         error_setg(errp, "can't register secondary IOMMU for PCI bus #%d",
23610c54acb8STomasz Jeznach             pci_bus_num(bus));
23620c54acb8STomasz Jeznach     }
23630c54acb8STomasz Jeznach }
23640c54acb8STomasz Jeznach 
23650c54acb8STomasz Jeznach static int riscv_iommu_memory_region_index(IOMMUMemoryRegion *iommu_mr,
23660c54acb8STomasz Jeznach     MemTxAttrs attrs)
23670c54acb8STomasz Jeznach {
23680c54acb8STomasz Jeznach     return attrs.unspecified ? RISCV_IOMMU_NOPROCID : (int)attrs.pid;
23690c54acb8STomasz Jeznach }
23700c54acb8STomasz Jeznach 
23710c54acb8STomasz Jeznach static int riscv_iommu_memory_region_index_len(IOMMUMemoryRegion *iommu_mr)
23720c54acb8STomasz Jeznach {
23730c54acb8STomasz Jeznach     RISCVIOMMUSpace *as = container_of(iommu_mr, RISCVIOMMUSpace, iova_mr);
23740c54acb8STomasz Jeznach     return 1 << as->iommu->pid_bits;
23750c54acb8STomasz Jeznach }
23760c54acb8STomasz Jeznach 
23770c54acb8STomasz Jeznach static void riscv_iommu_memory_region_init(ObjectClass *klass, void *data)
23780c54acb8STomasz Jeznach {
23790c54acb8STomasz Jeznach     IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass);
23800c54acb8STomasz Jeznach 
23810c54acb8STomasz Jeznach     imrc->translate = riscv_iommu_memory_region_translate;
23820c54acb8STomasz Jeznach     imrc->notify_flag_changed = riscv_iommu_memory_region_notify;
23830c54acb8STomasz Jeznach     imrc->attrs_to_index = riscv_iommu_memory_region_index;
23840c54acb8STomasz Jeznach     imrc->num_indexes = riscv_iommu_memory_region_index_len;
23850c54acb8STomasz Jeznach }
23860c54acb8STomasz Jeznach 
23870c54acb8STomasz Jeznach static const TypeInfo riscv_iommu_memory_region_info = {
23880c54acb8STomasz Jeznach     .parent = TYPE_IOMMU_MEMORY_REGION,
23890c54acb8STomasz Jeznach     .name = TYPE_RISCV_IOMMU_MEMORY_REGION,
23900c54acb8STomasz Jeznach     .class_init = riscv_iommu_memory_region_init,
23910c54acb8STomasz Jeznach };
23920c54acb8STomasz Jeznach 
23930c54acb8STomasz Jeznach static void riscv_iommu_register_mr_types(void)
23940c54acb8STomasz Jeznach {
23950c54acb8STomasz Jeznach     type_register_static(&riscv_iommu_memory_region_info);
23960c54acb8STomasz Jeznach     type_register_static(&riscv_iommu_info);
23970c54acb8STomasz Jeznach }
23980c54acb8STomasz Jeznach 
23990c54acb8STomasz Jeznach type_init(riscv_iommu_register_mr_types);
2400