xref: /qemu/hw/riscv/riscv-iommu.c (revision d37eede7a8e6ff33d21aacb41a68e63e8ffa1d60) !
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 
186*d37eede7SPierrick Bouvier /*
187*d37eede7SPierrick Bouvier  * Discards all bits from 'val' whose matching bits in the same
188*d37eede7SPierrick Bouvier  * positions in the mask 'ext' are zeros, and packs the remaining
189*d37eede7SPierrick Bouvier  * bits from 'val' contiguously at the least-significant end of the
190*d37eede7SPierrick Bouvier  * result, keeping the same bit order as 'val' and filling any
191*d37eede7SPierrick Bouvier  * other bits at the most-significant end of the result with zeros.
192*d37eede7SPierrick Bouvier  *
193*d37eede7SPierrick Bouvier  * For example, for the following 'val' and 'ext', the return 'ret'
194*d37eede7SPierrick Bouvier  * will be:
195*d37eede7SPierrick Bouvier  *
196*d37eede7SPierrick Bouvier  * val = a b c d e f g h
197*d37eede7SPierrick Bouvier  * ext = 1 0 1 0 0 1 1 0
198*d37eede7SPierrick Bouvier  * ret = 0 0 0 0 a c f g
199*d37eede7SPierrick Bouvier  *
200*d37eede7SPierrick Bouvier  * This function, taken from the riscv-iommu 1.0 spec, section 2.3.3
201*d37eede7SPierrick Bouvier  * "Process to translate addresses of MSIs", is similar to bit manip
202*d37eede7SPierrick Bouvier  * function PEXT (Parallel bits extract) from x86.
203*d37eede7SPierrick Bouvier  */
204*d37eede7SPierrick Bouvier static uint64_t riscv_iommu_pext_u64(uint64_t val, uint64_t ext)
2050c54acb8STomasz Jeznach {
2060c54acb8STomasz Jeznach     uint64_t ret = 0;
2070c54acb8STomasz Jeznach     uint64_t rot = 1;
2080c54acb8STomasz Jeznach 
2090c54acb8STomasz Jeznach     while (ext) {
2100c54acb8STomasz Jeznach         if (ext & 1) {
2110c54acb8STomasz Jeznach             if (val & 1) {
2120c54acb8STomasz Jeznach                 ret |= rot;
2130c54acb8STomasz Jeznach             }
2140c54acb8STomasz Jeznach             rot <<= 1;
2150c54acb8STomasz Jeznach         }
2160c54acb8STomasz Jeznach         val >>= 1;
2170c54acb8STomasz Jeznach         ext >>= 1;
2180c54acb8STomasz Jeznach     }
2190c54acb8STomasz Jeznach 
2200c54acb8STomasz Jeznach     return ret;
2210c54acb8STomasz Jeznach }
2220c54acb8STomasz Jeznach 
2230c54acb8STomasz Jeznach /* Check if GPA matches MSI/MRIF pattern. */
2240c54acb8STomasz Jeznach static bool riscv_iommu_msi_check(RISCVIOMMUState *s, RISCVIOMMUContext *ctx,
2250c54acb8STomasz Jeznach     dma_addr_t gpa)
2260c54acb8STomasz Jeznach {
2270c54acb8STomasz Jeznach     if (!s->enable_msi) {
2280c54acb8STomasz Jeznach         return false;
2290c54acb8STomasz Jeznach     }
2300c54acb8STomasz Jeznach 
2310c54acb8STomasz Jeznach     if (get_field(ctx->msiptp, RISCV_IOMMU_DC_MSIPTP_MODE) !=
2320c54acb8STomasz Jeznach         RISCV_IOMMU_DC_MSIPTP_MODE_FLAT) {
2330c54acb8STomasz Jeznach         return false; /* Invalid MSI/MRIF mode */
2340c54acb8STomasz Jeznach     }
2350c54acb8STomasz Jeznach 
2360c54acb8STomasz Jeznach     if ((PPN_DOWN(gpa) ^ ctx->msi_addr_pattern) & ~ctx->msi_addr_mask) {
2370c54acb8STomasz Jeznach         return false; /* GPA not in MSI range defined by AIA IMSIC rules. */
2380c54acb8STomasz Jeznach     }
2390c54acb8STomasz Jeznach 
2400c54acb8STomasz Jeznach     return true;
2410c54acb8STomasz Jeznach }
2420c54acb8STomasz Jeznach 
2430c54acb8STomasz Jeznach /*
2440c54acb8STomasz Jeznach  * RISCV IOMMU Address Translation Lookup - Page Table Walk
2450c54acb8STomasz Jeznach  *
2460c54acb8STomasz Jeznach  * Note: Code is based on get_physical_address() from target/riscv/cpu_helper.c
2470c54acb8STomasz Jeznach  * Both implementation can be merged into single helper function in future.
2480c54acb8STomasz Jeznach  * Keeping them separate for now, as error reporting and flow specifics are
2490c54acb8STomasz Jeznach  * sufficiently different for separate implementation.
2500c54acb8STomasz Jeznach  *
2510c54acb8STomasz Jeznach  * @s        : IOMMU Device State
2520c54acb8STomasz Jeznach  * @ctx      : Translation context for device id and process address space id.
2530c54acb8STomasz Jeznach  * @iotlb    : translation data: physical address and access mode.
2540c54acb8STomasz Jeznach  * @return   : success or fault cause code.
2550c54acb8STomasz Jeznach  */
2560c54acb8STomasz Jeznach static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx,
2570c54acb8STomasz Jeznach     IOMMUTLBEntry *iotlb)
2580c54acb8STomasz Jeznach {
2590c54acb8STomasz Jeznach     dma_addr_t addr, base;
2600c54acb8STomasz Jeznach     uint64_t satp, gatp, pte;
2610c54acb8STomasz Jeznach     bool en_s, en_g;
2620c54acb8STomasz Jeznach     struct {
2630c54acb8STomasz Jeznach         unsigned char step;
2640c54acb8STomasz Jeznach         unsigned char levels;
2650c54acb8STomasz Jeznach         unsigned char ptidxbits;
2660c54acb8STomasz Jeznach         unsigned char ptesize;
2670c54acb8STomasz Jeznach     } sc[2];
2680c54acb8STomasz Jeznach     /* Translation stage phase */
2690c54acb8STomasz Jeznach     enum {
2700c54acb8STomasz Jeznach         S_STAGE = 0,
2710c54acb8STomasz Jeznach         G_STAGE = 1,
2720c54acb8STomasz Jeznach     } pass;
2730c54acb8STomasz Jeznach     MemTxResult ret;
2740c54acb8STomasz Jeznach 
2750c54acb8STomasz Jeznach     satp = get_field(ctx->satp, RISCV_IOMMU_ATP_MODE_FIELD);
2760c54acb8STomasz Jeznach     gatp = get_field(ctx->gatp, RISCV_IOMMU_ATP_MODE_FIELD);
2770c54acb8STomasz Jeznach 
2780c54acb8STomasz Jeznach     en_s = satp != RISCV_IOMMU_DC_FSC_MODE_BARE;
2790c54acb8STomasz Jeznach     en_g = gatp != RISCV_IOMMU_DC_IOHGATP_MODE_BARE;
2800c54acb8STomasz Jeznach 
2810c54acb8STomasz Jeznach     /*
2820c54acb8STomasz Jeznach      * Early check for MSI address match when IOVA == GPA.
2830c54acb8STomasz Jeznach      * Note that the (!en_s) condition means that the MSI
2840c54acb8STomasz Jeznach      * page table may only be used when guest pages are
2850c54acb8STomasz Jeznach      * mapped using the g-stage page table, whether single-
2860c54acb8STomasz Jeznach      * or two-stage paging is enabled. It's unavoidable though,
2870c54acb8STomasz Jeznach      * because the spec mandates that we do a first-stage
2880c54acb8STomasz Jeznach      * translation before we check the MSI page table, which
2890c54acb8STomasz Jeznach      * means we can't do an early MSI check unless we have
2900c54acb8STomasz Jeznach      * strictly !en_s.
2910c54acb8STomasz Jeznach      */
2920c54acb8STomasz Jeznach     if (!en_s && (iotlb->perm & IOMMU_WO) &&
2930c54acb8STomasz Jeznach         riscv_iommu_msi_check(s, ctx, iotlb->iova)) {
2940c54acb8STomasz Jeznach         iotlb->target_as = &s->trap_as;
2950c54acb8STomasz Jeznach         iotlb->translated_addr = iotlb->iova;
2960c54acb8STomasz Jeznach         iotlb->addr_mask = ~TARGET_PAGE_MASK;
2970c54acb8STomasz Jeznach         return 0;
2980c54acb8STomasz Jeznach     }
2990c54acb8STomasz Jeznach 
3000c54acb8STomasz Jeznach     /* Exit early for pass-through mode. */
3010c54acb8STomasz Jeznach     if (!(en_s || en_g)) {
3020c54acb8STomasz Jeznach         iotlb->translated_addr = iotlb->iova;
3030c54acb8STomasz Jeznach         iotlb->addr_mask = ~TARGET_PAGE_MASK;
3040c54acb8STomasz Jeznach         /* Allow R/W in pass-through mode */
3050c54acb8STomasz Jeznach         iotlb->perm = IOMMU_RW;
3060c54acb8STomasz Jeznach         return 0;
3070c54acb8STomasz Jeznach     }
3080c54acb8STomasz Jeznach 
3090c54acb8STomasz Jeznach     /* S/G translation parameters. */
3100c54acb8STomasz Jeznach     for (pass = 0; pass < 2; pass++) {
3110c54acb8STomasz Jeznach         uint32_t sv_mode;
3120c54acb8STomasz Jeznach 
3130c54acb8STomasz Jeznach         sc[pass].step = 0;
3140c54acb8STomasz Jeznach         if (pass ? (s->fctl & RISCV_IOMMU_FCTL_GXL) :
3150c54acb8STomasz Jeznach             (ctx->tc & RISCV_IOMMU_DC_TC_SXL)) {
3160c54acb8STomasz Jeznach             /* 32bit mode for GXL/SXL == 1 */
3170c54acb8STomasz Jeznach             switch (pass ? gatp : satp) {
3180c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_IOHGATP_MODE_BARE:
3190c54acb8STomasz Jeznach                 sc[pass].levels    = 0;
3200c54acb8STomasz Jeznach                 sc[pass].ptidxbits = 0;
3210c54acb8STomasz Jeznach                 sc[pass].ptesize   = 0;
3220c54acb8STomasz Jeznach                 break;
3230c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_IOHGATP_MODE_SV32X4:
3240c54acb8STomasz Jeznach                 sv_mode = pass ? RISCV_IOMMU_CAP_SV32X4 : RISCV_IOMMU_CAP_SV32;
3250c54acb8STomasz Jeznach                 if (!(s->cap & sv_mode)) {
3260c54acb8STomasz Jeznach                     return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
3270c54acb8STomasz Jeznach                 }
3280c54acb8STomasz Jeznach                 sc[pass].levels    = 2;
3290c54acb8STomasz Jeznach                 sc[pass].ptidxbits = 10;
3300c54acb8STomasz Jeznach                 sc[pass].ptesize   = 4;
3310c54acb8STomasz Jeznach                 break;
3320c54acb8STomasz Jeznach             default:
3330c54acb8STomasz Jeznach                 return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
3340c54acb8STomasz Jeznach             }
3350c54acb8STomasz Jeznach         } else {
3360c54acb8STomasz Jeznach             /* 64bit mode for GXL/SXL == 0 */
3370c54acb8STomasz Jeznach             switch (pass ? gatp : satp) {
3380c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_IOHGATP_MODE_BARE:
3390c54acb8STomasz Jeznach                 sc[pass].levels    = 0;
3400c54acb8STomasz Jeznach                 sc[pass].ptidxbits = 0;
3410c54acb8STomasz Jeznach                 sc[pass].ptesize   = 0;
3420c54acb8STomasz Jeznach                 break;
3430c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_IOHGATP_MODE_SV39X4:
3440c54acb8STomasz Jeznach                 sv_mode = pass ? RISCV_IOMMU_CAP_SV39X4 : RISCV_IOMMU_CAP_SV39;
3450c54acb8STomasz Jeznach                 if (!(s->cap & sv_mode)) {
3460c54acb8STomasz Jeznach                     return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
3470c54acb8STomasz Jeznach                 }
3480c54acb8STomasz Jeznach                 sc[pass].levels    = 3;
3490c54acb8STomasz Jeznach                 sc[pass].ptidxbits = 9;
3500c54acb8STomasz Jeznach                 sc[pass].ptesize   = 8;
3510c54acb8STomasz Jeznach                 break;
3520c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_IOHGATP_MODE_SV48X4:
3530c54acb8STomasz Jeznach                 sv_mode = pass ? RISCV_IOMMU_CAP_SV48X4 : RISCV_IOMMU_CAP_SV48;
3540c54acb8STomasz Jeznach                 if (!(s->cap & sv_mode)) {
3550c54acb8STomasz Jeznach                     return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
3560c54acb8STomasz Jeznach                 }
3570c54acb8STomasz Jeznach                 sc[pass].levels    = 4;
3580c54acb8STomasz Jeznach                 sc[pass].ptidxbits = 9;
3590c54acb8STomasz Jeznach                 sc[pass].ptesize   = 8;
3600c54acb8STomasz Jeznach                 break;
3610c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_IOHGATP_MODE_SV57X4:
3620c54acb8STomasz Jeznach                 sv_mode = pass ? RISCV_IOMMU_CAP_SV57X4 : RISCV_IOMMU_CAP_SV57;
3630c54acb8STomasz Jeznach                 if (!(s->cap & sv_mode)) {
3640c54acb8STomasz Jeznach                     return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
3650c54acb8STomasz Jeznach                 }
3660c54acb8STomasz Jeznach                 sc[pass].levels    = 5;
3670c54acb8STomasz Jeznach                 sc[pass].ptidxbits = 9;
3680c54acb8STomasz Jeznach                 sc[pass].ptesize   = 8;
3690c54acb8STomasz Jeznach                 break;
3700c54acb8STomasz Jeznach             default:
3710c54acb8STomasz Jeznach                 return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
3720c54acb8STomasz Jeznach             }
3730c54acb8STomasz Jeznach         }
3740c54acb8STomasz Jeznach     };
3750c54acb8STomasz Jeznach 
3760c54acb8STomasz Jeznach     /* S/G stages translation tables root pointers */
3770c54acb8STomasz Jeznach     gatp = PPN_PHYS(get_field(ctx->gatp, RISCV_IOMMU_ATP_PPN_FIELD));
3780c54acb8STomasz Jeznach     satp = PPN_PHYS(get_field(ctx->satp, RISCV_IOMMU_ATP_PPN_FIELD));
3790c54acb8STomasz Jeznach     addr = (en_s && en_g) ? satp : iotlb->iova;
3800c54acb8STomasz Jeznach     base = en_g ? gatp : satp;
3810c54acb8STomasz Jeznach     pass = en_g ? G_STAGE : S_STAGE;
3820c54acb8STomasz Jeznach 
3830c54acb8STomasz Jeznach     do {
3840c54acb8STomasz Jeznach         const unsigned widened = (pass && !sc[pass].step) ? 2 : 0;
3850c54acb8STomasz Jeznach         const unsigned va_bits = widened + sc[pass].ptidxbits;
3860c54acb8STomasz Jeznach         const unsigned va_skip = TARGET_PAGE_BITS + sc[pass].ptidxbits *
3870c54acb8STomasz Jeznach                                  (sc[pass].levels - 1 - sc[pass].step);
3880c54acb8STomasz Jeznach         const unsigned idx = (addr >> va_skip) & ((1 << va_bits) - 1);
3890c54acb8STomasz Jeznach         const dma_addr_t pte_addr = base + idx * sc[pass].ptesize;
3900c54acb8STomasz Jeznach         const bool ade =
3910c54acb8STomasz Jeznach             ctx->tc & (pass ? RISCV_IOMMU_DC_TC_GADE : RISCV_IOMMU_DC_TC_SADE);
3920c54acb8STomasz Jeznach 
3930c54acb8STomasz Jeznach         /* Address range check before first level lookup */
3940c54acb8STomasz Jeznach         if (!sc[pass].step) {
3950c54acb8STomasz Jeznach             const uint64_t va_mask = (1ULL << (va_skip + va_bits)) - 1;
3960c54acb8STomasz Jeznach             if ((addr & va_mask) != addr) {
3970c54acb8STomasz Jeznach                 return RISCV_IOMMU_FQ_CAUSE_DMA_DISABLED;
3980c54acb8STomasz Jeznach             }
3990c54acb8STomasz Jeznach         }
4000c54acb8STomasz Jeznach 
4010c54acb8STomasz Jeznach         /* Read page table entry */
4020c54acb8STomasz Jeznach         if (sc[pass].ptesize == 4) {
4030c54acb8STomasz Jeznach             uint32_t pte32 = 0;
4040c54acb8STomasz Jeznach             ret = ldl_le_dma(s->target_as, pte_addr, &pte32,
4050c54acb8STomasz Jeznach                              MEMTXATTRS_UNSPECIFIED);
4060c54acb8STomasz Jeznach             pte = pte32;
4070c54acb8STomasz Jeznach         } else {
4080c54acb8STomasz Jeznach             ret = ldq_le_dma(s->target_as, pte_addr, &pte,
4090c54acb8STomasz Jeznach                              MEMTXATTRS_UNSPECIFIED);
4100c54acb8STomasz Jeznach         }
4110c54acb8STomasz Jeznach         if (ret != MEMTX_OK) {
4120c54acb8STomasz Jeznach             return (iotlb->perm & IOMMU_WO) ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT
4130c54acb8STomasz Jeznach                                             : RISCV_IOMMU_FQ_CAUSE_RD_FAULT;
4140c54acb8STomasz Jeznach         }
4150c54acb8STomasz Jeznach 
4160c54acb8STomasz Jeznach         sc[pass].step++;
4170c54acb8STomasz Jeznach         hwaddr ppn = pte >> PTE_PPN_SHIFT;
4180c54acb8STomasz Jeznach 
4190c54acb8STomasz Jeznach         if (!(pte & PTE_V)) {
4200c54acb8STomasz Jeznach             break;                /* Invalid PTE */
4210c54acb8STomasz Jeznach         } else if (!(pte & (PTE_R | PTE_W | PTE_X))) {
4220c54acb8STomasz Jeznach             base = PPN_PHYS(ppn); /* Inner PTE, continue walking */
4230c54acb8STomasz Jeznach         } else if ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) {
4240c54acb8STomasz Jeznach             break;                /* Reserved leaf PTE flags: PTE_W */
4250c54acb8STomasz Jeznach         } else if ((pte & (PTE_R | PTE_W | PTE_X)) == (PTE_W | PTE_X)) {
4260c54acb8STomasz Jeznach             break;                /* Reserved leaf PTE flags: PTE_W + PTE_X */
4270c54acb8STomasz Jeznach         } else if (ppn & ((1ULL << (va_skip - TARGET_PAGE_BITS)) - 1)) {
4280c54acb8STomasz Jeznach             break;                /* Misaligned PPN */
4290c54acb8STomasz Jeznach         } else if ((iotlb->perm & IOMMU_RO) && !(pte & PTE_R)) {
4300c54acb8STomasz Jeznach             break;                /* Read access check failed */
4310c54acb8STomasz Jeznach         } else if ((iotlb->perm & IOMMU_WO) && !(pte & PTE_W)) {
4320c54acb8STomasz Jeznach             break;                /* Write access check failed */
4330c54acb8STomasz Jeznach         } else if ((iotlb->perm & IOMMU_RO) && !ade && !(pte & PTE_A)) {
4340c54acb8STomasz Jeznach             break;                /* Access bit not set */
4350c54acb8STomasz Jeznach         } else if ((iotlb->perm & IOMMU_WO) && !ade && !(pte & PTE_D)) {
4360c54acb8STomasz Jeznach             break;                /* Dirty bit not set */
4370c54acb8STomasz Jeznach         } else {
4380c54acb8STomasz Jeznach             /* Leaf PTE, translation completed. */
4390c54acb8STomasz Jeznach             sc[pass].step = sc[pass].levels;
4400c54acb8STomasz Jeznach             base = PPN_PHYS(ppn) | (addr & ((1ULL << va_skip) - 1));
4410c54acb8STomasz Jeznach             /* Update address mask based on smallest translation granularity */
4420c54acb8STomasz Jeznach             iotlb->addr_mask &= (1ULL << va_skip) - 1;
4430c54acb8STomasz Jeznach             /* Continue with S-Stage translation? */
4440c54acb8STomasz Jeznach             if (pass && sc[0].step != sc[0].levels) {
4450c54acb8STomasz Jeznach                 pass = S_STAGE;
4460c54acb8STomasz Jeznach                 addr = iotlb->iova;
4470c54acb8STomasz Jeznach                 continue;
4480c54acb8STomasz Jeznach             }
4490c54acb8STomasz Jeznach             /* Translation phase completed (GPA or SPA) */
4500c54acb8STomasz Jeznach             iotlb->translated_addr = base;
4510c54acb8STomasz Jeznach             iotlb->perm = (pte & PTE_W) ? ((pte & PTE_R) ? IOMMU_RW : IOMMU_WO)
4520c54acb8STomasz Jeznach                                                          : IOMMU_RO;
4530c54acb8STomasz Jeznach 
4540c54acb8STomasz Jeznach             /* Check MSI GPA address match */
4550c54acb8STomasz Jeznach             if (pass == S_STAGE && (iotlb->perm & IOMMU_WO) &&
4560c54acb8STomasz Jeznach                 riscv_iommu_msi_check(s, ctx, base)) {
4570c54acb8STomasz Jeznach                 /* Trap MSI writes and return GPA address. */
4580c54acb8STomasz Jeznach                 iotlb->target_as = &s->trap_as;
4590c54acb8STomasz Jeznach                 iotlb->addr_mask = ~TARGET_PAGE_MASK;
4600c54acb8STomasz Jeznach                 return 0;
4610c54acb8STomasz Jeznach             }
4620c54acb8STomasz Jeznach 
4630c54acb8STomasz Jeznach             /* Continue with G-Stage translation? */
4640c54acb8STomasz Jeznach             if (!pass && en_g) {
4650c54acb8STomasz Jeznach                 pass = G_STAGE;
4660c54acb8STomasz Jeznach                 addr = base;
4670c54acb8STomasz Jeznach                 base = gatp;
4680c54acb8STomasz Jeznach                 sc[pass].step = 0;
4690c54acb8STomasz Jeznach                 continue;
4700c54acb8STomasz Jeznach             }
4710c54acb8STomasz Jeznach 
4720c54acb8STomasz Jeznach             return 0;
4730c54acb8STomasz Jeznach         }
4740c54acb8STomasz Jeznach 
4750c54acb8STomasz Jeznach         if (sc[pass].step == sc[pass].levels) {
4760c54acb8STomasz Jeznach             break; /* Can't find leaf PTE */
4770c54acb8STomasz Jeznach         }
4780c54acb8STomasz Jeznach 
4790c54acb8STomasz Jeznach         /* Continue with G-Stage translation? */
4800c54acb8STomasz Jeznach         if (!pass && en_g) {
4810c54acb8STomasz Jeznach             pass = G_STAGE;
4820c54acb8STomasz Jeznach             addr = base;
4830c54acb8STomasz Jeznach             base = gatp;
4840c54acb8STomasz Jeznach             sc[pass].step = 0;
4850c54acb8STomasz Jeznach         }
4860c54acb8STomasz Jeznach     } while (1);
4870c54acb8STomasz Jeznach 
4880c54acb8STomasz Jeznach     return (iotlb->perm & IOMMU_WO) ?
4890c54acb8STomasz Jeznach                 (pass ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT_VS :
4900c54acb8STomasz Jeznach                         RISCV_IOMMU_FQ_CAUSE_WR_FAULT_S) :
4910c54acb8STomasz Jeznach                 (pass ? RISCV_IOMMU_FQ_CAUSE_RD_FAULT_VS :
4920c54acb8STomasz Jeznach                         RISCV_IOMMU_FQ_CAUSE_RD_FAULT_S);
4930c54acb8STomasz Jeznach }
4940c54acb8STomasz Jeznach 
4950c54acb8STomasz Jeznach static void riscv_iommu_report_fault(RISCVIOMMUState *s,
4960c54acb8STomasz Jeznach                                      RISCVIOMMUContext *ctx,
4970c54acb8STomasz Jeznach                                      uint32_t fault_type, uint32_t cause,
4980c54acb8STomasz Jeznach                                      bool pv,
4990c54acb8STomasz Jeznach                                      uint64_t iotval, uint64_t iotval2)
5000c54acb8STomasz Jeznach {
5010c54acb8STomasz Jeznach     struct riscv_iommu_fq_record ev = { 0 };
5020c54acb8STomasz Jeznach 
5030c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_DTF) {
5040c54acb8STomasz Jeznach         switch (cause) {
5050c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_DMA_DISABLED:
5060c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_DDT_LOAD_FAULT:
5070c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_DDT_INVALID:
5080c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED:
5090c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_DDT_CORRUPTED:
5100c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_INTERNAL_DP_ERROR:
5110c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT:
5120c54acb8STomasz Jeznach             break;
5130c54acb8STomasz Jeznach         default:
5140c54acb8STomasz Jeznach             /* DTF prevents reporting a fault for this given cause */
5150c54acb8STomasz Jeznach             return;
5160c54acb8STomasz Jeznach         }
5170c54acb8STomasz Jeznach     }
5180c54acb8STomasz Jeznach 
5190c54acb8STomasz Jeznach     ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_CAUSE, cause);
5200c54acb8STomasz Jeznach     ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_TTYPE, fault_type);
5210c54acb8STomasz Jeznach     ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_DID, ctx->devid);
5220c54acb8STomasz Jeznach     ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_PV, true);
5230c54acb8STomasz Jeznach 
5240c54acb8STomasz Jeznach     if (pv) {
5250c54acb8STomasz Jeznach         ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_PID, ctx->process_id);
5260c54acb8STomasz Jeznach     }
5270c54acb8STomasz Jeznach 
5280c54acb8STomasz Jeznach     ev.iotval = iotval;
5290c54acb8STomasz Jeznach     ev.iotval2 = iotval2;
5300c54acb8STomasz Jeznach 
5310c54acb8STomasz Jeznach     riscv_iommu_fault(s, &ev);
5320c54acb8STomasz Jeznach }
5330c54acb8STomasz Jeznach 
5340c54acb8STomasz Jeznach /* Redirect MSI write for given GPA. */
5350c54acb8STomasz Jeznach static MemTxResult riscv_iommu_msi_write(RISCVIOMMUState *s,
5360c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx, uint64_t gpa, uint64_t data,
5370c54acb8STomasz Jeznach     unsigned size, MemTxAttrs attrs)
5380c54acb8STomasz Jeznach {
5390c54acb8STomasz Jeznach     MemTxResult res;
5400c54acb8STomasz Jeznach     dma_addr_t addr;
5410c54acb8STomasz Jeznach     uint64_t intn;
5420c54acb8STomasz Jeznach     uint32_t n190;
5430c54acb8STomasz Jeznach     uint64_t pte[2];
5440c54acb8STomasz Jeznach     int fault_type = RISCV_IOMMU_FQ_TTYPE_UADDR_WR;
5450c54acb8STomasz Jeznach     int cause;
5460c54acb8STomasz Jeznach 
5470c54acb8STomasz Jeznach     /* Interrupt File Number */
548*d37eede7SPierrick Bouvier     intn = riscv_iommu_pext_u64(PPN_DOWN(gpa), ctx->msi_addr_mask);
5490c54acb8STomasz Jeznach     if (intn >= 256) {
5500c54acb8STomasz Jeznach         /* Interrupt file number out of range */
5510c54acb8STomasz Jeznach         res = MEMTX_ACCESS_ERROR;
5520c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT;
5530c54acb8STomasz Jeznach         goto err;
5540c54acb8STomasz Jeznach     }
5550c54acb8STomasz Jeznach 
5560c54acb8STomasz Jeznach     /* fetch MSI PTE */
5570c54acb8STomasz Jeznach     addr = PPN_PHYS(get_field(ctx->msiptp, RISCV_IOMMU_DC_MSIPTP_PPN));
5580c54acb8STomasz Jeznach     addr = addr | (intn * sizeof(pte));
5590c54acb8STomasz Jeznach     res = dma_memory_read(s->target_as, addr, &pte, sizeof(pte),
5600c54acb8STomasz Jeznach             MEMTXATTRS_UNSPECIFIED);
5610c54acb8STomasz Jeznach     if (res != MEMTX_OK) {
5620c54acb8STomasz Jeznach         if (res == MEMTX_DECODE_ERROR) {
5630c54acb8STomasz Jeznach             cause = RISCV_IOMMU_FQ_CAUSE_MSI_PT_CORRUPTED;
5640c54acb8STomasz Jeznach         } else {
5650c54acb8STomasz Jeznach             cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT;
5660c54acb8STomasz Jeznach         }
5670c54acb8STomasz Jeznach         goto err;
5680c54acb8STomasz Jeznach     }
5690c54acb8STomasz Jeznach 
5700c54acb8STomasz Jeznach     le64_to_cpus(&pte[0]);
5710c54acb8STomasz Jeznach     le64_to_cpus(&pte[1]);
5720c54acb8STomasz Jeznach 
5730c54acb8STomasz Jeznach     if (!(pte[0] & RISCV_IOMMU_MSI_PTE_V) || (pte[0] & RISCV_IOMMU_MSI_PTE_C)) {
5740c54acb8STomasz Jeznach         /*
5750c54acb8STomasz Jeznach          * The spec mentions that: "If msipte.C == 1, then further
5760c54acb8STomasz Jeznach          * processing to interpret the PTE is implementation
5770c54acb8STomasz Jeznach          * defined.". We'll abort with cause = 262 for this
5780c54acb8STomasz Jeznach          * case too.
5790c54acb8STomasz Jeznach          */
5800c54acb8STomasz Jeznach         res = MEMTX_ACCESS_ERROR;
5810c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_INVALID;
5820c54acb8STomasz Jeznach         goto err;
5830c54acb8STomasz Jeznach     }
5840c54acb8STomasz Jeznach 
5850c54acb8STomasz Jeznach     switch (get_field(pte[0], RISCV_IOMMU_MSI_PTE_M)) {
5860c54acb8STomasz Jeznach     case RISCV_IOMMU_MSI_PTE_M_BASIC:
5870c54acb8STomasz Jeznach         /* MSI Pass-through mode */
5880c54acb8STomasz Jeznach         addr = PPN_PHYS(get_field(pte[0], RISCV_IOMMU_MSI_PTE_PPN));
5890c54acb8STomasz Jeznach 
5900c54acb8STomasz Jeznach         trace_riscv_iommu_msi(s->parent_obj.id, PCI_BUS_NUM(ctx->devid),
5910c54acb8STomasz Jeznach                               PCI_SLOT(ctx->devid), PCI_FUNC(ctx->devid),
5920c54acb8STomasz Jeznach                               gpa, addr);
5930c54acb8STomasz Jeznach 
5940c54acb8STomasz Jeznach         res = dma_memory_write(s->target_as, addr, &data, size, attrs);
5950c54acb8STomasz Jeznach         if (res != MEMTX_OK) {
5960c54acb8STomasz Jeznach             cause = RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT;
5970c54acb8STomasz Jeznach             goto err;
5980c54acb8STomasz Jeznach         }
5990c54acb8STomasz Jeznach 
6000c54acb8STomasz Jeznach         return MEMTX_OK;
6010c54acb8STomasz Jeznach     case RISCV_IOMMU_MSI_PTE_M_MRIF:
6020c54acb8STomasz Jeznach         /* MRIF mode, continue. */
6030c54acb8STomasz Jeznach         break;
6040c54acb8STomasz Jeznach     default:
6050c54acb8STomasz Jeznach         res = MEMTX_ACCESS_ERROR;
6060c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_MISCONFIGURED;
6070c54acb8STomasz Jeznach         goto err;
6080c54acb8STomasz Jeznach     }
6090c54acb8STomasz Jeznach 
6100c54acb8STomasz Jeznach     /*
6110c54acb8STomasz Jeznach      * Report an error for interrupt identities exceeding the maximum allowed
6120c54acb8STomasz Jeznach      * for an IMSIC interrupt file (2047) or destination address is not 32-bit
6130c54acb8STomasz Jeznach      * aligned. See IOMMU Specification, Chapter 2.3. MSI page tables.
6140c54acb8STomasz Jeznach      */
6150c54acb8STomasz Jeznach     if ((data > 2047) || (gpa & 3)) {
6160c54acb8STomasz Jeznach         res = MEMTX_ACCESS_ERROR;
6170c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_MISCONFIGURED;
6180c54acb8STomasz Jeznach         goto err;
6190c54acb8STomasz Jeznach     }
6200c54acb8STomasz Jeznach 
6210c54acb8STomasz Jeznach     /* MSI MRIF mode, non atomic pending bit update */
6220c54acb8STomasz Jeznach 
6230c54acb8STomasz Jeznach     /* MRIF pending bit address */
6240c54acb8STomasz Jeznach     addr = get_field(pte[0], RISCV_IOMMU_MSI_PTE_MRIF_ADDR) << 9;
6250c54acb8STomasz Jeznach     addr = addr | ((data & 0x7c0) >> 3);
6260c54acb8STomasz Jeznach 
6270c54acb8STomasz Jeznach     trace_riscv_iommu_msi(s->parent_obj.id, PCI_BUS_NUM(ctx->devid),
6280c54acb8STomasz Jeznach                           PCI_SLOT(ctx->devid), PCI_FUNC(ctx->devid),
6290c54acb8STomasz Jeznach                           gpa, addr);
6300c54acb8STomasz Jeznach 
6310c54acb8STomasz Jeznach     /* MRIF pending bit mask */
6320c54acb8STomasz Jeznach     data = 1ULL << (data & 0x03f);
6330c54acb8STomasz Jeznach     res = dma_memory_read(s->target_as, addr, &intn, sizeof(intn), attrs);
6340c54acb8STomasz Jeznach     if (res != MEMTX_OK) {
6350c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT;
6360c54acb8STomasz Jeznach         goto err;
6370c54acb8STomasz Jeznach     }
6380c54acb8STomasz Jeznach 
6390c54acb8STomasz Jeznach     intn = intn | data;
6400c54acb8STomasz Jeznach     res = dma_memory_write(s->target_as, addr, &intn, sizeof(intn), attrs);
6410c54acb8STomasz Jeznach     if (res != MEMTX_OK) {
6420c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT;
6430c54acb8STomasz Jeznach         goto err;
6440c54acb8STomasz Jeznach     }
6450c54acb8STomasz Jeznach 
6460c54acb8STomasz Jeznach     /* Get MRIF enable bits */
6470c54acb8STomasz Jeznach     addr = addr + sizeof(intn);
6480c54acb8STomasz Jeznach     res = dma_memory_read(s->target_as, addr, &intn, sizeof(intn), attrs);
6490c54acb8STomasz Jeznach     if (res != MEMTX_OK) {
6500c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT;
6510c54acb8STomasz Jeznach         goto err;
6520c54acb8STomasz Jeznach     }
6530c54acb8STomasz Jeznach 
6540c54acb8STomasz Jeznach     if (!(intn & data)) {
6550c54acb8STomasz Jeznach         /* notification disabled, MRIF update completed. */
6560c54acb8STomasz Jeznach         return MEMTX_OK;
6570c54acb8STomasz Jeznach     }
6580c54acb8STomasz Jeznach 
6590c54acb8STomasz Jeznach     /* Send notification message */
6600c54acb8STomasz Jeznach     addr = PPN_PHYS(get_field(pte[1], RISCV_IOMMU_MSI_MRIF_NPPN));
6610c54acb8STomasz Jeznach     n190 = get_field(pte[1], RISCV_IOMMU_MSI_MRIF_NID) |
6620c54acb8STomasz Jeznach           (get_field(pte[1], RISCV_IOMMU_MSI_MRIF_NID_MSB) << 10);
6630c54acb8STomasz Jeznach 
6640c54acb8STomasz Jeznach     res = dma_memory_write(s->target_as, addr, &n190, sizeof(n190), attrs);
6650c54acb8STomasz Jeznach     if (res != MEMTX_OK) {
6660c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT;
6670c54acb8STomasz Jeznach         goto err;
6680c54acb8STomasz Jeznach     }
6690c54acb8STomasz Jeznach 
6700c54acb8STomasz Jeznach     trace_riscv_iommu_mrif_notification(s->parent_obj.id, n190, addr);
6710c54acb8STomasz Jeznach 
6720c54acb8STomasz Jeznach     return MEMTX_OK;
6730c54acb8STomasz Jeznach 
6740c54acb8STomasz Jeznach err:
6750c54acb8STomasz Jeznach     riscv_iommu_report_fault(s, ctx, fault_type, cause,
6760c54acb8STomasz Jeznach                              !!ctx->process_id, 0, 0);
6770c54acb8STomasz Jeznach     return res;
6780c54acb8STomasz Jeznach }
6790c54acb8STomasz Jeznach 
6800c54acb8STomasz Jeznach /*
6810c54acb8STomasz Jeznach  * Check device context configuration as described by the
6820c54acb8STomasz Jeznach  * riscv-iommu spec section "Device-context configuration
6830c54acb8STomasz Jeznach  * checks".
6840c54acb8STomasz Jeznach  */
6850c54acb8STomasz Jeznach static bool riscv_iommu_validate_device_ctx(RISCVIOMMUState *s,
6860c54acb8STomasz Jeznach                                             RISCVIOMMUContext *ctx)
6870c54acb8STomasz Jeznach {
6880c54acb8STomasz Jeznach     uint32_t fsc_mode, msi_mode;
68969a9ae48STomasz Jeznach     uint64_t gatp;
69069a9ae48STomasz Jeznach 
69169a9ae48STomasz Jeznach     if (!(s->cap & RISCV_IOMMU_CAP_ATS) &&
69269a9ae48STomasz Jeznach         (ctx->tc & RISCV_IOMMU_DC_TC_EN_ATS ||
69369a9ae48STomasz Jeznach          ctx->tc & RISCV_IOMMU_DC_TC_EN_PRI ||
69469a9ae48STomasz Jeznach          ctx->tc & RISCV_IOMMU_DC_TC_PRPR)) {
69569a9ae48STomasz Jeznach         return false;
69669a9ae48STomasz Jeznach     }
69769a9ae48STomasz Jeznach 
69869a9ae48STomasz Jeznach     if (!(ctx->tc & RISCV_IOMMU_DC_TC_EN_ATS) &&
69969a9ae48STomasz Jeznach         (ctx->tc & RISCV_IOMMU_DC_TC_T2GPA ||
70069a9ae48STomasz Jeznach          ctx->tc & RISCV_IOMMU_DC_TC_EN_PRI)) {
70169a9ae48STomasz Jeznach         return false;
70269a9ae48STomasz Jeznach     }
7030c54acb8STomasz Jeznach 
7040c54acb8STomasz Jeznach     if (!(ctx->tc & RISCV_IOMMU_DC_TC_EN_PRI) &&
7050c54acb8STomasz Jeznach         ctx->tc & RISCV_IOMMU_DC_TC_PRPR) {
7060c54acb8STomasz Jeznach         return false;
7070c54acb8STomasz Jeznach     }
7080c54acb8STomasz Jeznach 
7090c54acb8STomasz Jeznach     if (!(s->cap & RISCV_IOMMU_CAP_T2GPA) &&
7100c54acb8STomasz Jeznach         ctx->tc & RISCV_IOMMU_DC_TC_T2GPA) {
7110c54acb8STomasz Jeznach         return false;
7120c54acb8STomasz Jeznach     }
7130c54acb8STomasz Jeznach 
7140c54acb8STomasz Jeznach     if (s->cap & RISCV_IOMMU_CAP_MSI_FLAT) {
7150c54acb8STomasz Jeznach         msi_mode = get_field(ctx->msiptp, RISCV_IOMMU_DC_MSIPTP_MODE);
7160c54acb8STomasz Jeznach 
7170c54acb8STomasz Jeznach         if (msi_mode != RISCV_IOMMU_DC_MSIPTP_MODE_OFF &&
7180c54acb8STomasz Jeznach             msi_mode != RISCV_IOMMU_DC_MSIPTP_MODE_FLAT) {
7190c54acb8STomasz Jeznach             return false;
7200c54acb8STomasz Jeznach         }
7210c54acb8STomasz Jeznach     }
7220c54acb8STomasz Jeznach 
72369a9ae48STomasz Jeznach     gatp = get_field(ctx->gatp, RISCV_IOMMU_ATP_MODE_FIELD);
72469a9ae48STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_T2GPA &&
72569a9ae48STomasz Jeznach         gatp == RISCV_IOMMU_DC_IOHGATP_MODE_BARE) {
72669a9ae48STomasz Jeznach         return false;
72769a9ae48STomasz Jeznach     }
72869a9ae48STomasz Jeznach 
7290c54acb8STomasz Jeznach     fsc_mode = get_field(ctx->satp, RISCV_IOMMU_DC_FSC_MODE);
7300c54acb8STomasz Jeznach 
7310c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_PDTV) {
7320c54acb8STomasz Jeznach         switch (fsc_mode) {
7330c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_PDTP_MODE_PD8:
7340c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_PD8)) {
7350c54acb8STomasz Jeznach                 return false;
7360c54acb8STomasz Jeznach             }
7370c54acb8STomasz Jeznach             break;
7380c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_PDTP_MODE_PD17:
7390c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_PD17)) {
7400c54acb8STomasz Jeznach                 return false;
7410c54acb8STomasz Jeznach             }
7420c54acb8STomasz Jeznach             break;
7430c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_PDTP_MODE_PD20:
7440c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_PD20)) {
7450c54acb8STomasz Jeznach                 return false;
7460c54acb8STomasz Jeznach             }
7470c54acb8STomasz Jeznach             break;
7480c54acb8STomasz Jeznach         }
7490c54acb8STomasz Jeznach     } else {
7500c54acb8STomasz Jeznach         /* DC.tc.PDTV is 0 */
7510c54acb8STomasz Jeznach         if (ctx->tc & RISCV_IOMMU_DC_TC_DPE) {
7520c54acb8STomasz Jeznach             return false;
7530c54acb8STomasz Jeznach         }
7540c54acb8STomasz Jeznach 
7550c54acb8STomasz Jeznach         if (ctx->tc & RISCV_IOMMU_DC_TC_SXL) {
7560c54acb8STomasz Jeznach             if (fsc_mode == RISCV_IOMMU_CAP_SV32 &&
7570c54acb8STomasz Jeznach                 !(s->cap & RISCV_IOMMU_CAP_SV32)) {
7580c54acb8STomasz Jeznach                 return false;
7590c54acb8STomasz Jeznach             }
7600c54acb8STomasz Jeznach         } else {
7610c54acb8STomasz Jeznach             switch (fsc_mode) {
7620c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV39:
7630c54acb8STomasz Jeznach                 if (!(s->cap & RISCV_IOMMU_CAP_SV39)) {
7640c54acb8STomasz Jeznach                     return false;
7650c54acb8STomasz Jeznach                 }
7660c54acb8STomasz Jeznach                 break;
7670c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV48:
7680c54acb8STomasz Jeznach                 if (!(s->cap & RISCV_IOMMU_CAP_SV48)) {
7690c54acb8STomasz Jeznach                     return false;
7700c54acb8STomasz Jeznach                 }
7710c54acb8STomasz Jeznach             break;
7720c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57:
7730c54acb8STomasz Jeznach                 if (!(s->cap & RISCV_IOMMU_CAP_SV57)) {
7740c54acb8STomasz Jeznach                     return false;
7750c54acb8STomasz Jeznach                 }
7760c54acb8STomasz Jeznach                 break;
7770c54acb8STomasz Jeznach             }
7780c54acb8STomasz Jeznach         }
7790c54acb8STomasz Jeznach     }
7800c54acb8STomasz Jeznach 
7810c54acb8STomasz Jeznach     /*
7820c54acb8STomasz Jeznach      * CAP_END is always zero (only one endianess). FCTL_BE is
7830c54acb8STomasz Jeznach      * always zero (little-endian accesses). Thus TC_SBE must
7840c54acb8STomasz Jeznach      * always be LE, i.e. zero.
7850c54acb8STomasz Jeznach      */
7860c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_SBE) {
7870c54acb8STomasz Jeznach         return false;
7880c54acb8STomasz Jeznach     }
7890c54acb8STomasz Jeznach 
7900c54acb8STomasz Jeznach     return true;
7910c54acb8STomasz Jeznach }
7920c54acb8STomasz Jeznach 
7930c54acb8STomasz Jeznach /*
7940c54acb8STomasz Jeznach  * Validate process context (PC) according to section
7950c54acb8STomasz Jeznach  * "Process-context configuration checks".
7960c54acb8STomasz Jeznach  */
7970c54acb8STomasz Jeznach static bool riscv_iommu_validate_process_ctx(RISCVIOMMUState *s,
7980c54acb8STomasz Jeznach                                              RISCVIOMMUContext *ctx)
7990c54acb8STomasz Jeznach {
8000c54acb8STomasz Jeznach     uint32_t mode;
8010c54acb8STomasz Jeznach 
8020c54acb8STomasz Jeznach     if (get_field(ctx->ta, RISCV_IOMMU_PC_TA_RESERVED)) {
8030c54acb8STomasz Jeznach         return false;
8040c54acb8STomasz Jeznach     }
8050c54acb8STomasz Jeznach 
8060c54acb8STomasz Jeznach     if (get_field(ctx->satp, RISCV_IOMMU_PC_FSC_RESERVED)) {
8070c54acb8STomasz Jeznach         return false;
8080c54acb8STomasz Jeznach     }
8090c54acb8STomasz Jeznach 
8100c54acb8STomasz Jeznach     mode = get_field(ctx->satp, RISCV_IOMMU_DC_FSC_MODE);
8110c54acb8STomasz Jeznach     switch (mode) {
8120c54acb8STomasz Jeznach     case RISCV_IOMMU_DC_FSC_MODE_BARE:
8130c54acb8STomasz Jeznach     /* sv39 and sv32 modes have the same value (8) */
8140c54acb8STomasz Jeznach     case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV39:
8150c54acb8STomasz Jeznach     case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV48:
8160c54acb8STomasz Jeznach     case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57:
8170c54acb8STomasz Jeznach         break;
8180c54acb8STomasz Jeznach     default:
8190c54acb8STomasz Jeznach         return false;
8200c54acb8STomasz Jeznach     }
8210c54acb8STomasz Jeznach 
8220c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_SXL) {
8230c54acb8STomasz Jeznach         if (mode == RISCV_IOMMU_CAP_SV32 &&
8240c54acb8STomasz Jeznach             !(s->cap & RISCV_IOMMU_CAP_SV32)) {
8250c54acb8STomasz Jeznach                 return false;
8260c54acb8STomasz Jeznach         }
8270c54acb8STomasz Jeznach     } else {
8280c54acb8STomasz Jeznach         switch (mode) {
8290c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV39:
8300c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_SV39)) {
8310c54acb8STomasz Jeznach                 return false;
8320c54acb8STomasz Jeznach             }
8330c54acb8STomasz Jeznach             break;
8340c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV48:
8350c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_SV48)) {
8360c54acb8STomasz Jeznach                 return false;
8370c54acb8STomasz Jeznach             }
8380c54acb8STomasz Jeznach             break;
8390c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57:
8400c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_SV57)) {
8410c54acb8STomasz Jeznach                 return false;
8420c54acb8STomasz Jeznach             }
8430c54acb8STomasz Jeznach             break;
8440c54acb8STomasz Jeznach         }
8450c54acb8STomasz Jeznach     }
8460c54acb8STomasz Jeznach 
8470c54acb8STomasz Jeznach     return true;
8480c54acb8STomasz Jeznach }
8490c54acb8STomasz Jeznach 
8500c54acb8STomasz Jeznach /*
8510c54acb8STomasz Jeznach  * RISC-V IOMMU Device Context Loopkup - Device Directory Tree Walk
8520c54acb8STomasz Jeznach  *
8530c54acb8STomasz Jeznach  * @s         : IOMMU Device State
8540c54acb8STomasz Jeznach  * @ctx       : Device Translation Context with devid and process_id set.
8550c54acb8STomasz Jeznach  * @return    : success or fault code.
8560c54acb8STomasz Jeznach  */
8570c54acb8STomasz Jeznach static int riscv_iommu_ctx_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx)
8580c54acb8STomasz Jeznach {
8590c54acb8STomasz Jeznach     const uint64_t ddtp = s->ddtp;
8600c54acb8STomasz Jeznach     unsigned mode = get_field(ddtp, RISCV_IOMMU_DDTP_MODE);
8610c54acb8STomasz Jeznach     dma_addr_t addr = PPN_PHYS(get_field(ddtp, RISCV_IOMMU_DDTP_PPN));
8620c54acb8STomasz Jeznach     struct riscv_iommu_dc dc;
8630c54acb8STomasz Jeznach     /* Device Context format: 0: extended (64 bytes) | 1: base (32 bytes) */
8640c54acb8STomasz Jeznach     const int dc_fmt = !s->enable_msi;
8650c54acb8STomasz Jeznach     const size_t dc_len = sizeof(dc) >> dc_fmt;
8660c54acb8STomasz Jeznach     unsigned depth;
8670c54acb8STomasz Jeznach     uint64_t de;
8680c54acb8STomasz Jeznach 
8690c54acb8STomasz Jeznach     switch (mode) {
8700c54acb8STomasz Jeznach     case RISCV_IOMMU_DDTP_MODE_OFF:
8710c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_DMA_DISABLED;
8720c54acb8STomasz Jeznach 
8730c54acb8STomasz Jeznach     case RISCV_IOMMU_DDTP_MODE_BARE:
8740c54acb8STomasz Jeznach         /* mock up pass-through translation context */
8750c54acb8STomasz Jeznach         ctx->gatp = set_field(0, RISCV_IOMMU_ATP_MODE_FIELD,
8760c54acb8STomasz Jeznach             RISCV_IOMMU_DC_IOHGATP_MODE_BARE);
8770c54acb8STomasz Jeznach         ctx->satp = set_field(0, RISCV_IOMMU_ATP_MODE_FIELD,
8780c54acb8STomasz Jeznach             RISCV_IOMMU_DC_FSC_MODE_BARE);
87969a9ae48STomasz Jeznach 
8800c54acb8STomasz Jeznach         ctx->tc = RISCV_IOMMU_DC_TC_V;
88169a9ae48STomasz Jeznach         if (s->enable_ats) {
88269a9ae48STomasz Jeznach             ctx->tc |= RISCV_IOMMU_DC_TC_EN_ATS;
88369a9ae48STomasz Jeznach         }
88469a9ae48STomasz Jeznach 
8850c54acb8STomasz Jeznach         ctx->ta = 0;
8860c54acb8STomasz Jeznach         ctx->msiptp = 0;
8870c54acb8STomasz Jeznach         return 0;
8880c54acb8STomasz Jeznach 
8890c54acb8STomasz Jeznach     case RISCV_IOMMU_DDTP_MODE_1LVL:
8900c54acb8STomasz Jeznach         depth = 0;
8910c54acb8STomasz Jeznach         break;
8920c54acb8STomasz Jeznach 
8930c54acb8STomasz Jeznach     case RISCV_IOMMU_DDTP_MODE_2LVL:
8940c54acb8STomasz Jeznach         depth = 1;
8950c54acb8STomasz Jeznach         break;
8960c54acb8STomasz Jeznach 
8970c54acb8STomasz Jeznach     case RISCV_IOMMU_DDTP_MODE_3LVL:
8980c54acb8STomasz Jeznach         depth = 2;
8990c54acb8STomasz Jeznach         break;
9000c54acb8STomasz Jeznach 
9010c54acb8STomasz Jeznach     default:
9020c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
9030c54acb8STomasz Jeznach     }
9040c54acb8STomasz Jeznach 
9050c54acb8STomasz Jeznach     /*
9060c54acb8STomasz Jeznach      * Check supported device id width (in bits).
9070c54acb8STomasz Jeznach      * See IOMMU Specification, Chapter 6. Software guidelines.
9080c54acb8STomasz Jeznach      * - if extended device-context format is used:
9090c54acb8STomasz Jeznach      *   1LVL: 6, 2LVL: 15, 3LVL: 24
9100c54acb8STomasz Jeznach      * - if base device-context format is used:
9110c54acb8STomasz Jeznach      *   1LVL: 7, 2LVL: 16, 3LVL: 24
9120c54acb8STomasz Jeznach      */
9130c54acb8STomasz Jeznach     if (ctx->devid >= (1 << (depth * 9 + 6 + (dc_fmt && depth != 2)))) {
9140c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_TTYPE_BLOCKED;
9150c54acb8STomasz Jeznach     }
9160c54acb8STomasz Jeznach 
9170c54acb8STomasz Jeznach     /* Device directory tree walk */
9180c54acb8STomasz Jeznach     for (; depth-- > 0; ) {
9190c54acb8STomasz Jeznach         /*
9200c54acb8STomasz Jeznach          * Select device id index bits based on device directory tree level
9210c54acb8STomasz Jeznach          * and device context format.
9220c54acb8STomasz Jeznach          * See IOMMU Specification, Chapter 2. Data Structures.
9230c54acb8STomasz Jeznach          * - if extended device-context format is used:
9240c54acb8STomasz Jeznach          *   device index: [23:15][14:6][5:0]
9250c54acb8STomasz Jeznach          * - if base device-context format is used:
9260c54acb8STomasz Jeznach          *   device index: [23:16][15:7][6:0]
9270c54acb8STomasz Jeznach          */
9280c54acb8STomasz Jeznach         const int split = depth * 9 + 6 + dc_fmt;
9290c54acb8STomasz Jeznach         addr |= ((ctx->devid >> split) << 3) & ~TARGET_PAGE_MASK;
9300c54acb8STomasz Jeznach         if (dma_memory_read(s->target_as, addr, &de, sizeof(de),
9310c54acb8STomasz Jeznach                             MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
9320c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_DDT_LOAD_FAULT;
9330c54acb8STomasz Jeznach         }
9340c54acb8STomasz Jeznach         le64_to_cpus(&de);
9350c54acb8STomasz Jeznach         if (!(de & RISCV_IOMMU_DDTE_VALID)) {
9360c54acb8STomasz Jeznach             /* invalid directory entry */
9370c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_DDT_INVALID;
9380c54acb8STomasz Jeznach         }
9390c54acb8STomasz Jeznach         if (de & ~(RISCV_IOMMU_DDTE_PPN | RISCV_IOMMU_DDTE_VALID)) {
9400c54acb8STomasz Jeznach             /* reserved bits set */
9410c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
9420c54acb8STomasz Jeznach         }
9430c54acb8STomasz Jeznach         addr = PPN_PHYS(get_field(de, RISCV_IOMMU_DDTE_PPN));
9440c54acb8STomasz Jeznach     }
9450c54acb8STomasz Jeznach 
9460c54acb8STomasz Jeznach     /* index into device context entry page */
9470c54acb8STomasz Jeznach     addr |= (ctx->devid * dc_len) & ~TARGET_PAGE_MASK;
9480c54acb8STomasz Jeznach 
9490c54acb8STomasz Jeznach     memset(&dc, 0, sizeof(dc));
9500c54acb8STomasz Jeznach     if (dma_memory_read(s->target_as, addr, &dc, dc_len,
9510c54acb8STomasz Jeznach                         MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
9520c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_DDT_LOAD_FAULT;
9530c54acb8STomasz Jeznach     }
9540c54acb8STomasz Jeznach 
9550c54acb8STomasz Jeznach     /* Set translation context. */
9560c54acb8STomasz Jeznach     ctx->tc = le64_to_cpu(dc.tc);
9570c54acb8STomasz Jeznach     ctx->gatp = le64_to_cpu(dc.iohgatp);
9580c54acb8STomasz Jeznach     ctx->satp = le64_to_cpu(dc.fsc);
9590c54acb8STomasz Jeznach     ctx->ta = le64_to_cpu(dc.ta);
9600c54acb8STomasz Jeznach     ctx->msiptp = le64_to_cpu(dc.msiptp);
9610c54acb8STomasz Jeznach     ctx->msi_addr_mask = le64_to_cpu(dc.msi_addr_mask);
9620c54acb8STomasz Jeznach     ctx->msi_addr_pattern = le64_to_cpu(dc.msi_addr_pattern);
9630c54acb8STomasz Jeznach 
9640c54acb8STomasz Jeznach     if (!(ctx->tc & RISCV_IOMMU_DC_TC_V)) {
9650c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_DDT_INVALID;
9660c54acb8STomasz Jeznach     }
9670c54acb8STomasz Jeznach 
9680c54acb8STomasz Jeznach     if (!riscv_iommu_validate_device_ctx(s, ctx)) {
9690c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
9700c54acb8STomasz Jeznach     }
9710c54acb8STomasz Jeznach 
9720c54acb8STomasz Jeznach     /* FSC field checks */
9730c54acb8STomasz Jeznach     mode = get_field(ctx->satp, RISCV_IOMMU_DC_FSC_MODE);
9740c54acb8STomasz Jeznach     addr = PPN_PHYS(get_field(ctx->satp, RISCV_IOMMU_DC_FSC_PPN));
9750c54acb8STomasz Jeznach 
9760c54acb8STomasz Jeznach     if (!(ctx->tc & RISCV_IOMMU_DC_TC_PDTV)) {
9770c54acb8STomasz Jeznach         if (ctx->process_id != RISCV_IOMMU_NOPROCID) {
9780c54acb8STomasz Jeznach             /* PID is disabled */
9790c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_TTYPE_BLOCKED;
9800c54acb8STomasz Jeznach         }
9810c54acb8STomasz Jeznach         if (mode > RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57) {
9820c54acb8STomasz Jeznach             /* Invalid translation mode */
9830c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_DDT_INVALID;
9840c54acb8STomasz Jeznach         }
9850c54acb8STomasz Jeznach         return 0;
9860c54acb8STomasz Jeznach     }
9870c54acb8STomasz Jeznach 
9880c54acb8STomasz Jeznach     if (ctx->process_id == RISCV_IOMMU_NOPROCID) {
9890c54acb8STomasz Jeznach         if (!(ctx->tc & RISCV_IOMMU_DC_TC_DPE)) {
9900c54acb8STomasz Jeznach             /* No default process_id enabled, set BARE mode */
9910c54acb8STomasz Jeznach             ctx->satp = 0ULL;
9920c54acb8STomasz Jeznach             return 0;
9930c54acb8STomasz Jeznach         } else {
9940c54acb8STomasz Jeznach             /* Use default process_id #0 */
9950c54acb8STomasz Jeznach             ctx->process_id = 0;
9960c54acb8STomasz Jeznach         }
9970c54acb8STomasz Jeznach     }
9980c54acb8STomasz Jeznach 
9990c54acb8STomasz Jeznach     if (mode == RISCV_IOMMU_DC_FSC_MODE_BARE) {
10000c54acb8STomasz Jeznach         /* No S-Stage translation, done. */
10010c54acb8STomasz Jeznach         return 0;
10020c54acb8STomasz Jeznach     }
10030c54acb8STomasz Jeznach 
10040c54acb8STomasz Jeznach     /* FSC.TC.PDTV enabled */
10050c54acb8STomasz Jeznach     if (mode > RISCV_IOMMU_DC_FSC_PDTP_MODE_PD20) {
10060c54acb8STomasz Jeznach         /* Invalid PDTP.MODE */
10070c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_PDT_MISCONFIGURED;
10080c54acb8STomasz Jeznach     }
10090c54acb8STomasz Jeznach 
10100c54acb8STomasz Jeznach     for (depth = mode - RISCV_IOMMU_DC_FSC_PDTP_MODE_PD8; depth-- > 0; ) {
10110c54acb8STomasz Jeznach         /*
10120c54acb8STomasz Jeznach          * Select process id index bits based on process directory tree
10130c54acb8STomasz Jeznach          * level. See IOMMU Specification, 2.2. Process-Directory-Table.
10140c54acb8STomasz Jeznach          */
10150c54acb8STomasz Jeznach         const int split = depth * 9 + 8;
10160c54acb8STomasz Jeznach         addr |= ((ctx->process_id >> split) << 3) & ~TARGET_PAGE_MASK;
10170c54acb8STomasz Jeznach         if (dma_memory_read(s->target_as, addr, &de, sizeof(de),
10180c54acb8STomasz Jeznach                             MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
10190c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_PDT_LOAD_FAULT;
10200c54acb8STomasz Jeznach         }
10210c54acb8STomasz Jeznach         le64_to_cpus(&de);
10220c54acb8STomasz Jeznach         if (!(de & RISCV_IOMMU_PC_TA_V)) {
10230c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_PDT_INVALID;
10240c54acb8STomasz Jeznach         }
10250c54acb8STomasz Jeznach         addr = PPN_PHYS(get_field(de, RISCV_IOMMU_PC_FSC_PPN));
10260c54acb8STomasz Jeznach     }
10270c54acb8STomasz Jeznach 
10280c54acb8STomasz Jeznach     /* Leaf entry in PDT */
10290c54acb8STomasz Jeznach     addr |= (ctx->process_id << 4) & ~TARGET_PAGE_MASK;
10300c54acb8STomasz Jeznach     if (dma_memory_read(s->target_as, addr, &dc.ta, sizeof(uint64_t) * 2,
10310c54acb8STomasz Jeznach                         MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
10320c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_PDT_LOAD_FAULT;
10330c54acb8STomasz Jeznach     }
10340c54acb8STomasz Jeznach 
10350c54acb8STomasz Jeznach     /* Use FSC and TA from process directory entry. */
10360c54acb8STomasz Jeznach     ctx->ta = le64_to_cpu(dc.ta);
10370c54acb8STomasz Jeznach     ctx->satp = le64_to_cpu(dc.fsc);
10380c54acb8STomasz Jeznach 
10390c54acb8STomasz Jeznach     if (!(ctx->ta & RISCV_IOMMU_PC_TA_V)) {
10400c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_PDT_INVALID;
10410c54acb8STomasz Jeznach     }
10420c54acb8STomasz Jeznach 
10430c54acb8STomasz Jeznach     if (!riscv_iommu_validate_process_ctx(s, ctx)) {
10440c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_PDT_MISCONFIGURED;
10450c54acb8STomasz Jeznach     }
10460c54acb8STomasz Jeznach 
10470c54acb8STomasz Jeznach     return 0;
10480c54acb8STomasz Jeznach }
10490c54acb8STomasz Jeznach 
10500c54acb8STomasz Jeznach /* Translation Context cache support */
10510c54acb8STomasz Jeznach static gboolean riscv_iommu_ctx_equal(gconstpointer v1, gconstpointer v2)
10520c54acb8STomasz Jeznach {
10530c54acb8STomasz Jeznach     RISCVIOMMUContext *c1 = (RISCVIOMMUContext *) v1;
10540c54acb8STomasz Jeznach     RISCVIOMMUContext *c2 = (RISCVIOMMUContext *) v2;
10550c54acb8STomasz Jeznach     return c1->devid == c2->devid &&
10560c54acb8STomasz Jeznach            c1->process_id == c2->process_id;
10570c54acb8STomasz Jeznach }
10580c54acb8STomasz Jeznach 
10590c54acb8STomasz Jeznach static guint riscv_iommu_ctx_hash(gconstpointer v)
10600c54acb8STomasz Jeznach {
10610c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx = (RISCVIOMMUContext *) v;
10620c54acb8STomasz Jeznach     /*
10630c54acb8STomasz Jeznach      * Generate simple hash of (process_id, devid)
10640c54acb8STomasz Jeznach      * assuming 24-bit wide devid.
10650c54acb8STomasz Jeznach      */
10660c54acb8STomasz Jeznach     return (guint)(ctx->devid) + ((guint)(ctx->process_id) << 24);
10670c54acb8STomasz Jeznach }
10680c54acb8STomasz Jeznach 
10690c54acb8STomasz Jeznach static void riscv_iommu_ctx_inval_devid_procid(gpointer key, gpointer value,
10700c54acb8STomasz Jeznach                                                gpointer data)
10710c54acb8STomasz Jeznach {
10720c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx = (RISCVIOMMUContext *) value;
10730c54acb8STomasz Jeznach     RISCVIOMMUContext *arg = (RISCVIOMMUContext *) data;
10740c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_V &&
10750c54acb8STomasz Jeznach         ctx->devid == arg->devid &&
10760c54acb8STomasz Jeznach         ctx->process_id == arg->process_id) {
10770c54acb8STomasz Jeznach         ctx->tc &= ~RISCV_IOMMU_DC_TC_V;
10780c54acb8STomasz Jeznach     }
10790c54acb8STomasz Jeznach }
10800c54acb8STomasz Jeznach 
10810c54acb8STomasz Jeznach static void riscv_iommu_ctx_inval_devid(gpointer key, gpointer value,
10820c54acb8STomasz Jeznach                                         gpointer data)
10830c54acb8STomasz Jeznach {
10840c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx = (RISCVIOMMUContext *) value;
10850c54acb8STomasz Jeznach     RISCVIOMMUContext *arg = (RISCVIOMMUContext *) data;
10860c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_V &&
10870c54acb8STomasz Jeznach         ctx->devid == arg->devid) {
10880c54acb8STomasz Jeznach         ctx->tc &= ~RISCV_IOMMU_DC_TC_V;
10890c54acb8STomasz Jeznach     }
10900c54acb8STomasz Jeznach }
10910c54acb8STomasz Jeznach 
10920c54acb8STomasz Jeznach static void riscv_iommu_ctx_inval_all(gpointer key, gpointer value,
10930c54acb8STomasz Jeznach                                       gpointer data)
10940c54acb8STomasz Jeznach {
10950c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx = (RISCVIOMMUContext *) value;
10960c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_V) {
10970c54acb8STomasz Jeznach         ctx->tc &= ~RISCV_IOMMU_DC_TC_V;
10980c54acb8STomasz Jeznach     }
10990c54acb8STomasz Jeznach }
11000c54acb8STomasz Jeznach 
11010c54acb8STomasz Jeznach static void riscv_iommu_ctx_inval(RISCVIOMMUState *s, GHFunc func,
11020c54acb8STomasz Jeznach                                   uint32_t devid, uint32_t process_id)
11030c54acb8STomasz Jeznach {
11040c54acb8STomasz Jeznach     GHashTable *ctx_cache;
11050c54acb8STomasz Jeznach     RISCVIOMMUContext key = {
11060c54acb8STomasz Jeznach         .devid = devid,
11070c54acb8STomasz Jeznach         .process_id = process_id,
11080c54acb8STomasz Jeznach     };
11090c54acb8STomasz Jeznach     ctx_cache = g_hash_table_ref(s->ctx_cache);
11100c54acb8STomasz Jeznach     g_hash_table_foreach(ctx_cache, func, &key);
11110c54acb8STomasz Jeznach     g_hash_table_unref(ctx_cache);
11120c54acb8STomasz Jeznach }
11130c54acb8STomasz Jeznach 
11140c54acb8STomasz Jeznach /* Find or allocate translation context for a given {device_id, process_id} */
11150c54acb8STomasz Jeznach static RISCVIOMMUContext *riscv_iommu_ctx(RISCVIOMMUState *s,
11160c54acb8STomasz Jeznach                                           unsigned devid, unsigned process_id,
11170c54acb8STomasz Jeznach                                           void **ref)
11180c54acb8STomasz Jeznach {
11190c54acb8STomasz Jeznach     GHashTable *ctx_cache;
11200c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx;
11210c54acb8STomasz Jeznach     RISCVIOMMUContext key = {
11220c54acb8STomasz Jeznach         .devid = devid,
11230c54acb8STomasz Jeznach         .process_id = process_id,
11240c54acb8STomasz Jeznach     };
11250c54acb8STomasz Jeznach 
11260c54acb8STomasz Jeznach     ctx_cache = g_hash_table_ref(s->ctx_cache);
11270c54acb8STomasz Jeznach     ctx = g_hash_table_lookup(ctx_cache, &key);
11280c54acb8STomasz Jeznach 
11290c54acb8STomasz Jeznach     if (ctx && (ctx->tc & RISCV_IOMMU_DC_TC_V)) {
11300c54acb8STomasz Jeznach         *ref = ctx_cache;
11310c54acb8STomasz Jeznach         return ctx;
11320c54acb8STomasz Jeznach     }
11330c54acb8STomasz Jeznach 
11340c54acb8STomasz Jeznach     ctx = g_new0(RISCVIOMMUContext, 1);
11350c54acb8STomasz Jeznach     ctx->devid = devid;
11360c54acb8STomasz Jeznach     ctx->process_id = process_id;
11370c54acb8STomasz Jeznach 
11380c54acb8STomasz Jeznach     int fault = riscv_iommu_ctx_fetch(s, ctx);
11390c54acb8STomasz Jeznach     if (!fault) {
11400c54acb8STomasz Jeznach         if (g_hash_table_size(ctx_cache) >= LIMIT_CACHE_CTX) {
11410c54acb8STomasz Jeznach             g_hash_table_unref(ctx_cache);
11420c54acb8STomasz Jeznach             ctx_cache = g_hash_table_new_full(riscv_iommu_ctx_hash,
11430c54acb8STomasz Jeznach                                               riscv_iommu_ctx_equal,
11440c54acb8STomasz Jeznach                                               g_free, NULL);
11450c54acb8STomasz Jeznach             g_hash_table_ref(ctx_cache);
11460c54acb8STomasz Jeznach             g_hash_table_unref(qatomic_xchg(&s->ctx_cache, ctx_cache));
11470c54acb8STomasz Jeznach         }
11480c54acb8STomasz Jeznach         g_hash_table_add(ctx_cache, ctx);
11490c54acb8STomasz Jeznach         *ref = ctx_cache;
11500c54acb8STomasz Jeznach         return ctx;
11510c54acb8STomasz Jeznach     }
11520c54acb8STomasz Jeznach 
11530c54acb8STomasz Jeznach     g_hash_table_unref(ctx_cache);
11540c54acb8STomasz Jeznach     *ref = NULL;
11550c54acb8STomasz Jeznach 
11560c54acb8STomasz Jeznach     riscv_iommu_report_fault(s, ctx, RISCV_IOMMU_FQ_TTYPE_UADDR_RD,
11570c54acb8STomasz Jeznach                              fault, !!process_id, 0, 0);
11580c54acb8STomasz Jeznach 
11590c54acb8STomasz Jeznach     g_free(ctx);
11600c54acb8STomasz Jeznach     return NULL;
11610c54acb8STomasz Jeznach }
11620c54acb8STomasz Jeznach 
11630c54acb8STomasz Jeznach static void riscv_iommu_ctx_put(RISCVIOMMUState *s, void *ref)
11640c54acb8STomasz Jeznach {
11650c54acb8STomasz Jeznach     if (ref) {
11660c54acb8STomasz Jeznach         g_hash_table_unref((GHashTable *)ref);
11670c54acb8STomasz Jeznach     }
11680c54acb8STomasz Jeznach }
11690c54acb8STomasz Jeznach 
11700c54acb8STomasz Jeznach /* Find or allocate address space for a given device */
11710c54acb8STomasz Jeznach static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, uint32_t devid)
11720c54acb8STomasz Jeznach {
11730c54acb8STomasz Jeznach     RISCVIOMMUSpace *as;
11740c54acb8STomasz Jeznach 
11750c54acb8STomasz Jeznach     /* FIXME: PCIe bus remapping for attached endpoints. */
11760c54acb8STomasz Jeznach     devid |= s->bus << 8;
11770c54acb8STomasz Jeznach 
11780c54acb8STomasz Jeznach     QLIST_FOREACH(as, &s->spaces, list) {
11790c54acb8STomasz Jeznach         if (as->devid == devid) {
11800c54acb8STomasz Jeznach             break;
11810c54acb8STomasz Jeznach         }
11820c54acb8STomasz Jeznach     }
11830c54acb8STomasz Jeznach 
11840c54acb8STomasz Jeznach     if (as == NULL) {
11850c54acb8STomasz Jeznach         char name[64];
11860c54acb8STomasz Jeznach         as = g_new0(RISCVIOMMUSpace, 1);
11870c54acb8STomasz Jeznach 
11880c54acb8STomasz Jeznach         as->iommu = s;
11890c54acb8STomasz Jeznach         as->devid = devid;
11900c54acb8STomasz Jeznach 
11910c54acb8STomasz Jeznach         snprintf(name, sizeof(name), "riscv-iommu-%04x:%02x.%d-iova",
11920c54acb8STomasz Jeznach             PCI_BUS_NUM(as->devid), PCI_SLOT(as->devid), PCI_FUNC(as->devid));
11930c54acb8STomasz Jeznach 
11940c54acb8STomasz Jeznach         /* IOVA address space, untranslated addresses */
11950c54acb8STomasz Jeznach         memory_region_init_iommu(&as->iova_mr, sizeof(as->iova_mr),
11960c54acb8STomasz Jeznach             TYPE_RISCV_IOMMU_MEMORY_REGION,
11970c54acb8STomasz Jeznach             OBJECT(as), "riscv_iommu", UINT64_MAX);
11980c54acb8STomasz Jeznach         address_space_init(&as->iova_as, MEMORY_REGION(&as->iova_mr), name);
11990c54acb8STomasz Jeznach 
12000c54acb8STomasz Jeznach         QLIST_INSERT_HEAD(&s->spaces, as, list);
12010c54acb8STomasz Jeznach 
12020c54acb8STomasz Jeznach         trace_riscv_iommu_new(s->parent_obj.id, PCI_BUS_NUM(as->devid),
12030c54acb8STomasz Jeznach                 PCI_SLOT(as->devid), PCI_FUNC(as->devid));
12040c54acb8STomasz Jeznach     }
12050c54acb8STomasz Jeznach     return &as->iova_as;
12060c54acb8STomasz Jeznach }
12070c54acb8STomasz Jeznach 
12089d085a1cSTomasz Jeznach /* Translation Object cache support */
12099d085a1cSTomasz Jeznach static gboolean riscv_iommu_iot_equal(gconstpointer v1, gconstpointer v2)
12100c54acb8STomasz Jeznach {
12119d085a1cSTomasz Jeznach     RISCVIOMMUEntry *t1 = (RISCVIOMMUEntry *) v1;
12129d085a1cSTomasz Jeznach     RISCVIOMMUEntry *t2 = (RISCVIOMMUEntry *) v2;
12139d085a1cSTomasz Jeznach     return t1->gscid == t2->gscid && t1->pscid == t2->pscid &&
12149d085a1cSTomasz Jeznach            t1->iova == t2->iova;
12159d085a1cSTomasz Jeznach }
12169d085a1cSTomasz Jeznach 
12179d085a1cSTomasz Jeznach static guint riscv_iommu_iot_hash(gconstpointer v)
12189d085a1cSTomasz Jeznach {
12199d085a1cSTomasz Jeznach     RISCVIOMMUEntry *t = (RISCVIOMMUEntry *) v;
12209d085a1cSTomasz Jeznach     return (guint)t->iova;
12219d085a1cSTomasz Jeznach }
12229d085a1cSTomasz Jeznach 
12239d085a1cSTomasz Jeznach /* GV: 1 PSCV: 1 AV: 1 */
12249d085a1cSTomasz Jeznach static void riscv_iommu_iot_inval_pscid_iova(gpointer key, gpointer value,
12259d085a1cSTomasz Jeznach                                              gpointer data)
12269d085a1cSTomasz Jeznach {
12279d085a1cSTomasz Jeznach     RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value;
12289d085a1cSTomasz Jeznach     RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data;
12299d085a1cSTomasz Jeznach     if (iot->gscid == arg->gscid &&
12309d085a1cSTomasz Jeznach         iot->pscid == arg->pscid &&
12319d085a1cSTomasz Jeznach         iot->iova == arg->iova) {
12329d085a1cSTomasz Jeznach         iot->perm = IOMMU_NONE;
12339d085a1cSTomasz Jeznach     }
12349d085a1cSTomasz Jeznach }
12359d085a1cSTomasz Jeznach 
12369d085a1cSTomasz Jeznach /* GV: 1 PSCV: 1 AV: 0 */
12379d085a1cSTomasz Jeznach static void riscv_iommu_iot_inval_pscid(gpointer key, gpointer value,
12389d085a1cSTomasz Jeznach                                         gpointer data)
12399d085a1cSTomasz Jeznach {
12409d085a1cSTomasz Jeznach     RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value;
12419d085a1cSTomasz Jeznach     RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data;
12429d085a1cSTomasz Jeznach     if (iot->gscid == arg->gscid &&
12439d085a1cSTomasz Jeznach         iot->pscid == arg->pscid) {
12449d085a1cSTomasz Jeznach         iot->perm = IOMMU_NONE;
12459d085a1cSTomasz Jeznach     }
12469d085a1cSTomasz Jeznach }
12479d085a1cSTomasz Jeznach 
12489d085a1cSTomasz Jeznach /* GV: 1 GVMA: 1 */
12499d085a1cSTomasz Jeznach static void riscv_iommu_iot_inval_gscid_gpa(gpointer key, gpointer value,
12509d085a1cSTomasz Jeznach                                             gpointer data)
12519d085a1cSTomasz Jeznach {
12529d085a1cSTomasz Jeznach     RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value;
12539d085a1cSTomasz Jeznach     RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data;
12549d085a1cSTomasz Jeznach     if (iot->gscid == arg->gscid) {
12559d085a1cSTomasz Jeznach         /* simplified cache, no GPA matching */
12569d085a1cSTomasz Jeznach         iot->perm = IOMMU_NONE;
12579d085a1cSTomasz Jeznach     }
12589d085a1cSTomasz Jeznach }
12599d085a1cSTomasz Jeznach 
12609d085a1cSTomasz Jeznach /* GV: 1 GVMA: 0 */
12619d085a1cSTomasz Jeznach static void riscv_iommu_iot_inval_gscid(gpointer key, gpointer value,
12629d085a1cSTomasz Jeznach                                         gpointer data)
12639d085a1cSTomasz Jeznach {
12649d085a1cSTomasz Jeznach     RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value;
12659d085a1cSTomasz Jeznach     RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data;
12669d085a1cSTomasz Jeznach     if (iot->gscid == arg->gscid) {
12679d085a1cSTomasz Jeznach         iot->perm = IOMMU_NONE;
12689d085a1cSTomasz Jeznach     }
12699d085a1cSTomasz Jeznach }
12709d085a1cSTomasz Jeznach 
12719d085a1cSTomasz Jeznach /* GV: 0 */
12729d085a1cSTomasz Jeznach static void riscv_iommu_iot_inval_all(gpointer key, gpointer value,
12739d085a1cSTomasz Jeznach                                       gpointer data)
12749d085a1cSTomasz Jeznach {
12759d085a1cSTomasz Jeznach     RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value;
12769d085a1cSTomasz Jeznach     iot->perm = IOMMU_NONE;
12779d085a1cSTomasz Jeznach }
12789d085a1cSTomasz Jeznach 
12799d085a1cSTomasz Jeznach /* caller should keep ref-count for iot_cache object */
12809d085a1cSTomasz Jeznach static RISCVIOMMUEntry *riscv_iommu_iot_lookup(RISCVIOMMUContext *ctx,
12819d085a1cSTomasz Jeznach     GHashTable *iot_cache, hwaddr iova)
12829d085a1cSTomasz Jeznach {
12839d085a1cSTomasz Jeznach     RISCVIOMMUEntry key = {
12849d085a1cSTomasz Jeznach         .gscid = get_field(ctx->gatp, RISCV_IOMMU_DC_IOHGATP_GSCID),
12859d085a1cSTomasz Jeznach         .pscid = get_field(ctx->ta, RISCV_IOMMU_DC_TA_PSCID),
12869d085a1cSTomasz Jeznach         .iova  = PPN_DOWN(iova),
12879d085a1cSTomasz Jeznach     };
12889d085a1cSTomasz Jeznach     return g_hash_table_lookup(iot_cache, &key);
12899d085a1cSTomasz Jeznach }
12909d085a1cSTomasz Jeznach 
12919d085a1cSTomasz Jeznach /* caller should keep ref-count for iot_cache object */
12929d085a1cSTomasz Jeznach static void riscv_iommu_iot_update(RISCVIOMMUState *s,
12939d085a1cSTomasz Jeznach     GHashTable *iot_cache, RISCVIOMMUEntry *iot)
12949d085a1cSTomasz Jeznach {
12959d085a1cSTomasz Jeznach     if (!s->iot_limit) {
12969d085a1cSTomasz Jeznach         return;
12979d085a1cSTomasz Jeznach     }
12989d085a1cSTomasz Jeznach 
12999d085a1cSTomasz Jeznach     if (g_hash_table_size(s->iot_cache) >= s->iot_limit) {
13009d085a1cSTomasz Jeznach         iot_cache = g_hash_table_new_full(riscv_iommu_iot_hash,
13019d085a1cSTomasz Jeznach                                           riscv_iommu_iot_equal,
13029d085a1cSTomasz Jeznach                                           g_free, NULL);
13039d085a1cSTomasz Jeznach         g_hash_table_unref(qatomic_xchg(&s->iot_cache, iot_cache));
13049d085a1cSTomasz Jeznach     }
13059d085a1cSTomasz Jeznach     g_hash_table_add(iot_cache, iot);
13069d085a1cSTomasz Jeznach }
13079d085a1cSTomasz Jeznach 
13089d085a1cSTomasz Jeznach static void riscv_iommu_iot_inval(RISCVIOMMUState *s, GHFunc func,
13099d085a1cSTomasz Jeznach     uint32_t gscid, uint32_t pscid, hwaddr iova)
13109d085a1cSTomasz Jeznach {
13119d085a1cSTomasz Jeznach     GHashTable *iot_cache;
13129d085a1cSTomasz Jeznach     RISCVIOMMUEntry key = {
13139d085a1cSTomasz Jeznach         .gscid = gscid,
13149d085a1cSTomasz Jeznach         .pscid = pscid,
13159d085a1cSTomasz Jeznach         .iova  = PPN_DOWN(iova),
13169d085a1cSTomasz Jeznach     };
13179d085a1cSTomasz Jeznach 
13189d085a1cSTomasz Jeznach     iot_cache = g_hash_table_ref(s->iot_cache);
13199d085a1cSTomasz Jeznach     g_hash_table_foreach(iot_cache, func, &key);
13209d085a1cSTomasz Jeznach     g_hash_table_unref(iot_cache);
13219d085a1cSTomasz Jeznach }
13229d085a1cSTomasz Jeznach 
13239d085a1cSTomasz Jeznach static int riscv_iommu_translate(RISCVIOMMUState *s, RISCVIOMMUContext *ctx,
13249d085a1cSTomasz Jeznach     IOMMUTLBEntry *iotlb, bool enable_cache)
13259d085a1cSTomasz Jeznach {
13269d085a1cSTomasz Jeznach     RISCVIOMMUEntry *iot;
13279d085a1cSTomasz Jeznach     IOMMUAccessFlags perm;
13280c54acb8STomasz Jeznach     bool enable_pid;
13290c54acb8STomasz Jeznach     bool enable_pri;
13309d085a1cSTomasz Jeznach     GHashTable *iot_cache;
13310c54acb8STomasz Jeznach     int fault;
13320c54acb8STomasz Jeznach 
13339d085a1cSTomasz Jeznach     iot_cache = g_hash_table_ref(s->iot_cache);
13340c54acb8STomasz Jeznach     /*
13350c54acb8STomasz Jeznach      * TC[32] is reserved for custom extensions, used here to temporarily
13360c54acb8STomasz Jeznach      * enable automatic page-request generation for ATS queries.
13370c54acb8STomasz Jeznach      */
13380c54acb8STomasz Jeznach     enable_pri = (iotlb->perm == IOMMU_NONE) && (ctx->tc & BIT_ULL(32));
13390c54acb8STomasz Jeznach     enable_pid = (ctx->tc & RISCV_IOMMU_DC_TC_PDTV);
13400c54acb8STomasz Jeznach 
134169a9ae48STomasz Jeznach     /* Check for ATS request. */
134269a9ae48STomasz Jeznach     if (iotlb->perm == IOMMU_NONE) {
134369a9ae48STomasz Jeznach         /* Check if ATS is disabled. */
134469a9ae48STomasz Jeznach         if (!(ctx->tc & RISCV_IOMMU_DC_TC_EN_ATS)) {
134569a9ae48STomasz Jeznach             enable_pri = false;
134669a9ae48STomasz Jeznach             fault = RISCV_IOMMU_FQ_CAUSE_TTYPE_BLOCKED;
134769a9ae48STomasz Jeznach             goto done;
134869a9ae48STomasz Jeznach         }
134969a9ae48STomasz Jeznach     }
135069a9ae48STomasz Jeznach 
13519d085a1cSTomasz Jeznach     iot = riscv_iommu_iot_lookup(ctx, iot_cache, iotlb->iova);
13529d085a1cSTomasz Jeznach     perm = iot ? iot->perm : IOMMU_NONE;
13539d085a1cSTomasz Jeznach     if (perm != IOMMU_NONE) {
13549d085a1cSTomasz Jeznach         iotlb->translated_addr = PPN_PHYS(iot->phys);
13559d085a1cSTomasz Jeznach         iotlb->addr_mask = ~TARGET_PAGE_MASK;
13569d085a1cSTomasz Jeznach         iotlb->perm = perm;
13579d085a1cSTomasz Jeznach         fault = 0;
13589d085a1cSTomasz Jeznach         goto done;
13599d085a1cSTomasz Jeznach     }
13609d085a1cSTomasz Jeznach 
13610c54acb8STomasz Jeznach     /* Translate using device directory / page table information. */
13620c54acb8STomasz Jeznach     fault = riscv_iommu_spa_fetch(s, ctx, iotlb);
13630c54acb8STomasz Jeznach 
13649d085a1cSTomasz Jeznach     if (!fault && iotlb->target_as == &s->trap_as) {
13659d085a1cSTomasz Jeznach         /* Do not cache trapped MSI translations */
13669d085a1cSTomasz Jeznach         goto done;
13679d085a1cSTomasz Jeznach     }
13689d085a1cSTomasz Jeznach 
13699d085a1cSTomasz Jeznach     /*
13709d085a1cSTomasz Jeznach      * We made an implementation choice to not cache identity-mapped
13719d085a1cSTomasz Jeznach      * translations, as allowed by the specification, to avoid
13729d085a1cSTomasz Jeznach      * translation cache evictions for other devices sharing the
13739d085a1cSTomasz Jeznach      * IOMMU hardware model.
13749d085a1cSTomasz Jeznach      */
13759d085a1cSTomasz Jeznach     if (!fault && iotlb->translated_addr != iotlb->iova && enable_cache) {
13769d085a1cSTomasz Jeznach         iot = g_new0(RISCVIOMMUEntry, 1);
13779d085a1cSTomasz Jeznach         iot->iova = PPN_DOWN(iotlb->iova);
13789d085a1cSTomasz Jeznach         iot->phys = PPN_DOWN(iotlb->translated_addr);
13799d085a1cSTomasz Jeznach         iot->gscid = get_field(ctx->gatp, RISCV_IOMMU_DC_IOHGATP_GSCID);
13809d085a1cSTomasz Jeznach         iot->pscid = get_field(ctx->ta, RISCV_IOMMU_DC_TA_PSCID);
13819d085a1cSTomasz Jeznach         iot->perm = iotlb->perm;
13829d085a1cSTomasz Jeznach         riscv_iommu_iot_update(s, iot_cache, iot);
13839d085a1cSTomasz Jeznach     }
13849d085a1cSTomasz Jeznach 
13859d085a1cSTomasz Jeznach done:
13869d085a1cSTomasz Jeznach     g_hash_table_unref(iot_cache);
13879d085a1cSTomasz Jeznach 
13880c54acb8STomasz Jeznach     if (enable_pri && fault) {
13890c54acb8STomasz Jeznach         struct riscv_iommu_pq_record pr = {0};
13900c54acb8STomasz Jeznach         if (enable_pid) {
13910c54acb8STomasz Jeznach             pr.hdr = set_field(RISCV_IOMMU_PREQ_HDR_PV,
13920c54acb8STomasz Jeznach                                RISCV_IOMMU_PREQ_HDR_PID, ctx->process_id);
13930c54acb8STomasz Jeznach         }
13940c54acb8STomasz Jeznach         pr.hdr = set_field(pr.hdr, RISCV_IOMMU_PREQ_HDR_DID, ctx->devid);
13950c54acb8STomasz Jeznach         pr.payload = (iotlb->iova & TARGET_PAGE_MASK) |
13960c54acb8STomasz Jeznach                      RISCV_IOMMU_PREQ_PAYLOAD_M;
13970c54acb8STomasz Jeznach         riscv_iommu_pri(s, &pr);
13980c54acb8STomasz Jeznach         return fault;
13990c54acb8STomasz Jeznach     }
14000c54acb8STomasz Jeznach 
14010c54acb8STomasz Jeznach     if (fault) {
140269a9ae48STomasz Jeznach         unsigned ttype = RISCV_IOMMU_FQ_TTYPE_PCIE_ATS_REQ;
14030c54acb8STomasz Jeznach 
14040c54acb8STomasz Jeznach         if (iotlb->perm & IOMMU_RW) {
14050c54acb8STomasz Jeznach             ttype = RISCV_IOMMU_FQ_TTYPE_UADDR_WR;
140669a9ae48STomasz Jeznach         } else if (iotlb->perm & IOMMU_RO) {
14070c54acb8STomasz Jeznach             ttype = RISCV_IOMMU_FQ_TTYPE_UADDR_RD;
14080c54acb8STomasz Jeznach         }
14090c54acb8STomasz Jeznach 
14100c54acb8STomasz Jeznach         riscv_iommu_report_fault(s, ctx, ttype, fault, enable_pid,
14110c54acb8STomasz Jeznach                                  iotlb->iova, iotlb->translated_addr);
14120c54acb8STomasz Jeznach         return fault;
14130c54acb8STomasz Jeznach     }
14140c54acb8STomasz Jeznach 
14150c54acb8STomasz Jeznach     return 0;
14160c54acb8STomasz Jeznach }
14170c54acb8STomasz Jeznach 
14180c54acb8STomasz Jeznach /* IOMMU Command Interface */
14190c54acb8STomasz Jeznach static MemTxResult riscv_iommu_iofence(RISCVIOMMUState *s, bool notify,
14200c54acb8STomasz Jeznach     uint64_t addr, uint32_t data)
14210c54acb8STomasz Jeznach {
14220c54acb8STomasz Jeznach     /*
14230c54acb8STomasz Jeznach      * ATS processing in this implementation of the IOMMU is synchronous,
14240c54acb8STomasz Jeznach      * no need to wait for completions here.
14250c54acb8STomasz Jeznach      */
14260c54acb8STomasz Jeznach     if (!notify) {
14270c54acb8STomasz Jeznach         return MEMTX_OK;
14280c54acb8STomasz Jeznach     }
14290c54acb8STomasz Jeznach 
14300c54acb8STomasz Jeznach     return dma_memory_write(s->target_as, addr, &data, sizeof(data),
14310c54acb8STomasz Jeznach         MEMTXATTRS_UNSPECIFIED);
14320c54acb8STomasz Jeznach }
14330c54acb8STomasz Jeznach 
143469a9ae48STomasz Jeznach static void riscv_iommu_ats(RISCVIOMMUState *s,
143569a9ae48STomasz Jeznach     struct riscv_iommu_command *cmd, IOMMUNotifierFlag flag,
143669a9ae48STomasz Jeznach     IOMMUAccessFlags perm,
143769a9ae48STomasz Jeznach     void (*trace_fn)(const char *id))
143869a9ae48STomasz Jeznach {
143969a9ae48STomasz Jeznach     RISCVIOMMUSpace *as = NULL;
144069a9ae48STomasz Jeznach     IOMMUNotifier *n;
144169a9ae48STomasz Jeznach     IOMMUTLBEvent event;
144269a9ae48STomasz Jeznach     uint32_t pid;
144369a9ae48STomasz Jeznach     uint32_t devid;
144469a9ae48STomasz Jeznach     const bool pv = cmd->dword0 & RISCV_IOMMU_CMD_ATS_PV;
144569a9ae48STomasz Jeznach 
144669a9ae48STomasz Jeznach     if (cmd->dword0 & RISCV_IOMMU_CMD_ATS_DSV) {
144769a9ae48STomasz Jeznach         /* Use device segment and requester id */
144869a9ae48STomasz Jeznach         devid = get_field(cmd->dword0,
144969a9ae48STomasz Jeznach             RISCV_IOMMU_CMD_ATS_DSEG | RISCV_IOMMU_CMD_ATS_RID);
145069a9ae48STomasz Jeznach     } else {
145169a9ae48STomasz Jeznach         devid = get_field(cmd->dword0, RISCV_IOMMU_CMD_ATS_RID);
145269a9ae48STomasz Jeznach     }
145369a9ae48STomasz Jeznach 
145469a9ae48STomasz Jeznach     pid = get_field(cmd->dword0, RISCV_IOMMU_CMD_ATS_PID);
145569a9ae48STomasz Jeznach 
145669a9ae48STomasz Jeznach     QLIST_FOREACH(as, &s->spaces, list) {
145769a9ae48STomasz Jeznach         if (as->devid == devid) {
145869a9ae48STomasz Jeznach             break;
145969a9ae48STomasz Jeznach         }
146069a9ae48STomasz Jeznach     }
146169a9ae48STomasz Jeznach 
146269a9ae48STomasz Jeznach     if (!as || !as->notifier) {
146369a9ae48STomasz Jeznach         return;
146469a9ae48STomasz Jeznach     }
146569a9ae48STomasz Jeznach 
146669a9ae48STomasz Jeznach     event.type = flag;
146769a9ae48STomasz Jeznach     event.entry.perm = perm;
146869a9ae48STomasz Jeznach     event.entry.target_as = s->target_as;
146969a9ae48STomasz Jeznach 
147069a9ae48STomasz Jeznach     IOMMU_NOTIFIER_FOREACH(n, &as->iova_mr) {
147169a9ae48STomasz Jeznach         if (!pv || n->iommu_idx == pid) {
147269a9ae48STomasz Jeznach             event.entry.iova = n->start;
147369a9ae48STomasz Jeznach             event.entry.addr_mask = n->end - n->start;
147469a9ae48STomasz Jeznach             trace_fn(as->iova_mr.parent_obj.name);
147569a9ae48STomasz Jeznach             memory_region_notify_iommu_one(n, &event);
147669a9ae48STomasz Jeznach         }
147769a9ae48STomasz Jeznach     }
147869a9ae48STomasz Jeznach }
147969a9ae48STomasz Jeznach 
148069a9ae48STomasz Jeznach static void riscv_iommu_ats_inval(RISCVIOMMUState *s,
148169a9ae48STomasz Jeznach     struct riscv_iommu_command *cmd)
148269a9ae48STomasz Jeznach {
148369a9ae48STomasz Jeznach     return riscv_iommu_ats(s, cmd, IOMMU_NOTIFIER_DEVIOTLB_UNMAP, IOMMU_NONE,
148469a9ae48STomasz Jeznach                            trace_riscv_iommu_ats_inval);
148569a9ae48STomasz Jeznach }
148669a9ae48STomasz Jeznach 
148769a9ae48STomasz Jeznach static void riscv_iommu_ats_prgr(RISCVIOMMUState *s,
148869a9ae48STomasz Jeznach     struct riscv_iommu_command *cmd)
148969a9ae48STomasz Jeznach {
149069a9ae48STomasz Jeznach     unsigned resp_code = get_field(cmd->dword1,
149169a9ae48STomasz Jeznach                                    RISCV_IOMMU_CMD_ATS_PRGR_RESP_CODE);
149269a9ae48STomasz Jeznach 
149369a9ae48STomasz Jeznach     /* Using the access flag to carry response code information */
149469a9ae48STomasz Jeznach     IOMMUAccessFlags perm = resp_code ? IOMMU_NONE : IOMMU_RW;
149569a9ae48STomasz Jeznach     return riscv_iommu_ats(s, cmd, IOMMU_NOTIFIER_MAP, perm,
149669a9ae48STomasz Jeznach                            trace_riscv_iommu_ats_prgr);
149769a9ae48STomasz Jeznach }
149869a9ae48STomasz Jeznach 
14990c54acb8STomasz Jeznach static void riscv_iommu_process_ddtp(RISCVIOMMUState *s)
15000c54acb8STomasz Jeznach {
15010c54acb8STomasz Jeznach     uint64_t old_ddtp = s->ddtp;
15020c54acb8STomasz Jeznach     uint64_t new_ddtp = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_DDTP);
15030c54acb8STomasz Jeznach     unsigned new_mode = get_field(new_ddtp, RISCV_IOMMU_DDTP_MODE);
15040c54acb8STomasz Jeznach     unsigned old_mode = get_field(old_ddtp, RISCV_IOMMU_DDTP_MODE);
15050c54acb8STomasz Jeznach     bool ok = false;
15060c54acb8STomasz Jeznach 
15070c54acb8STomasz Jeznach     /*
15080c54acb8STomasz Jeznach      * Check for allowed DDTP.MODE transitions:
15090c54acb8STomasz Jeznach      * {OFF, BARE}        -> {OFF, BARE, 1LVL, 2LVL, 3LVL}
15100c54acb8STomasz Jeznach      * {1LVL, 2LVL, 3LVL} -> {OFF, BARE}
15110c54acb8STomasz Jeznach      */
15120c54acb8STomasz Jeznach     if (new_mode == old_mode ||
15130c54acb8STomasz Jeznach         new_mode == RISCV_IOMMU_DDTP_MODE_OFF ||
15140c54acb8STomasz Jeznach         new_mode == RISCV_IOMMU_DDTP_MODE_BARE) {
15150c54acb8STomasz Jeznach         ok = true;
15160c54acb8STomasz Jeznach     } else if (new_mode == RISCV_IOMMU_DDTP_MODE_1LVL ||
15170c54acb8STomasz Jeznach                new_mode == RISCV_IOMMU_DDTP_MODE_2LVL ||
15180c54acb8STomasz Jeznach                new_mode == RISCV_IOMMU_DDTP_MODE_3LVL) {
15190c54acb8STomasz Jeznach         ok = old_mode == RISCV_IOMMU_DDTP_MODE_OFF ||
15200c54acb8STomasz Jeznach              old_mode == RISCV_IOMMU_DDTP_MODE_BARE;
15210c54acb8STomasz Jeznach     }
15220c54acb8STomasz Jeznach 
15230c54acb8STomasz Jeznach     if (ok) {
15240c54acb8STomasz Jeznach         /* clear reserved and busy bits, report back sanitized version */
15250c54acb8STomasz Jeznach         new_ddtp = set_field(new_ddtp & RISCV_IOMMU_DDTP_PPN,
15260c54acb8STomasz Jeznach                              RISCV_IOMMU_DDTP_MODE, new_mode);
15270c54acb8STomasz Jeznach     } else {
15280c54acb8STomasz Jeznach         new_ddtp = old_ddtp;
15290c54acb8STomasz Jeznach     }
15300c54acb8STomasz Jeznach     s->ddtp = new_ddtp;
15310c54acb8STomasz Jeznach 
15320c54acb8STomasz Jeznach     riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_DDTP, new_ddtp);
15330c54acb8STomasz Jeznach }
15340c54acb8STomasz Jeznach 
15350c54acb8STomasz Jeznach /* Command function and opcode field. */
15360c54acb8STomasz Jeznach #define RISCV_IOMMU_CMD(func, op) (((func) << 7) | (op))
15370c54acb8STomasz Jeznach 
15380c54acb8STomasz Jeznach static void riscv_iommu_process_cq_tail(RISCVIOMMUState *s)
15390c54acb8STomasz Jeznach {
15400c54acb8STomasz Jeznach     struct riscv_iommu_command cmd;
15410c54acb8STomasz Jeznach     MemTxResult res;
15420c54acb8STomasz Jeznach     dma_addr_t addr;
15430c54acb8STomasz Jeznach     uint32_t tail, head, ctrl;
15440c54acb8STomasz Jeznach     uint64_t cmd_opcode;
15450c54acb8STomasz Jeznach     GHFunc func;
15460c54acb8STomasz Jeznach 
15470c54acb8STomasz Jeznach     ctrl = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQCSR);
15480c54acb8STomasz Jeznach     tail = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQT) & s->cq_mask;
15490c54acb8STomasz Jeznach     head = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQH) & s->cq_mask;
15500c54acb8STomasz Jeznach 
15510c54acb8STomasz Jeznach     /* Check for pending error or queue processing disabled */
15520c54acb8STomasz Jeznach     if (!(ctrl & RISCV_IOMMU_CQCSR_CQON) ||
15530c54acb8STomasz Jeznach         !!(ctrl & (RISCV_IOMMU_CQCSR_CMD_ILL | RISCV_IOMMU_CQCSR_CQMF))) {
15540c54acb8STomasz Jeznach         return;
15550c54acb8STomasz Jeznach     }
15560c54acb8STomasz Jeznach 
15570c54acb8STomasz Jeznach     while (tail != head) {
15580c54acb8STomasz Jeznach         addr = s->cq_addr  + head * sizeof(cmd);
15590c54acb8STomasz Jeznach         res = dma_memory_read(s->target_as, addr, &cmd, sizeof(cmd),
15600c54acb8STomasz Jeznach                               MEMTXATTRS_UNSPECIFIED);
15610c54acb8STomasz Jeznach 
15620c54acb8STomasz Jeznach         if (res != MEMTX_OK) {
15630c54acb8STomasz Jeznach             riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR,
15640c54acb8STomasz Jeznach                                   RISCV_IOMMU_CQCSR_CQMF, 0);
15650c54acb8STomasz Jeznach             goto fault;
15660c54acb8STomasz Jeznach         }
15670c54acb8STomasz Jeznach 
15680c54acb8STomasz Jeznach         trace_riscv_iommu_cmd(s->parent_obj.id, cmd.dword0, cmd.dword1);
15690c54acb8STomasz Jeznach 
15700c54acb8STomasz Jeznach         cmd_opcode = get_field(cmd.dword0,
15710c54acb8STomasz Jeznach                                RISCV_IOMMU_CMD_OPCODE | RISCV_IOMMU_CMD_FUNC);
15720c54acb8STomasz Jeznach 
15730c54acb8STomasz Jeznach         switch (cmd_opcode) {
15740c54acb8STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IOFENCE_FUNC_C,
15750c54acb8STomasz Jeznach                              RISCV_IOMMU_CMD_IOFENCE_OPCODE):
15760c54acb8STomasz Jeznach             res = riscv_iommu_iofence(s,
15770c54acb8STomasz Jeznach                 cmd.dword0 & RISCV_IOMMU_CMD_IOFENCE_AV, cmd.dword1 << 2,
15780c54acb8STomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IOFENCE_DATA));
15790c54acb8STomasz Jeznach 
15800c54acb8STomasz Jeznach             if (res != MEMTX_OK) {
15810c54acb8STomasz Jeznach                 riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR,
15820c54acb8STomasz Jeznach                                       RISCV_IOMMU_CQCSR_CQMF, 0);
15830c54acb8STomasz Jeznach                 goto fault;
15840c54acb8STomasz Jeznach             }
15850c54acb8STomasz Jeznach             break;
15860c54acb8STomasz Jeznach 
15870c54acb8STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IOTINVAL_FUNC_GVMA,
15880c54acb8STomasz Jeznach                              RISCV_IOMMU_CMD_IOTINVAL_OPCODE):
15890c54acb8STomasz Jeznach             if (cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_PSCV) {
15900c54acb8STomasz Jeznach                 /* illegal command arguments IOTINVAL.GVMA & PSCV == 1 */
15910c54acb8STomasz Jeznach                 goto cmd_ill;
15929d085a1cSTomasz Jeznach             } else if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_GV)) {
15939d085a1cSTomasz Jeznach                 /* invalidate all cache mappings */
15949d085a1cSTomasz Jeznach                 func = riscv_iommu_iot_inval_all;
15959d085a1cSTomasz Jeznach             } else if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_AV)) {
15969d085a1cSTomasz Jeznach                 /* invalidate cache matching GSCID */
15979d085a1cSTomasz Jeznach                 func = riscv_iommu_iot_inval_gscid;
15989d085a1cSTomasz Jeznach             } else {
15999d085a1cSTomasz Jeznach                 /* invalidate cache matching GSCID and ADDR (GPA) */
16009d085a1cSTomasz Jeznach                 func = riscv_iommu_iot_inval_gscid_gpa;
16010c54acb8STomasz Jeznach             }
16029d085a1cSTomasz Jeznach             riscv_iommu_iot_inval(s, func,
16039d085a1cSTomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IOTINVAL_GSCID), 0,
16049d085a1cSTomasz Jeznach                 cmd.dword1 << 2 & TARGET_PAGE_MASK);
16050c54acb8STomasz Jeznach             break;
16060c54acb8STomasz Jeznach 
16070c54acb8STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IOTINVAL_FUNC_VMA,
16080c54acb8STomasz Jeznach                              RISCV_IOMMU_CMD_IOTINVAL_OPCODE):
16099d085a1cSTomasz Jeznach             if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_GV)) {
16109d085a1cSTomasz Jeznach                 /* invalidate all cache mappings, simplified model */
16119d085a1cSTomasz Jeznach                 func = riscv_iommu_iot_inval_all;
16129d085a1cSTomasz Jeznach             } else if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_PSCV)) {
16139d085a1cSTomasz Jeznach                 /* invalidate cache matching GSCID, simplified model */
16149d085a1cSTomasz Jeznach                 func = riscv_iommu_iot_inval_gscid;
16159d085a1cSTomasz Jeznach             } else if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_AV)) {
16169d085a1cSTomasz Jeznach                 /* invalidate cache matching GSCID and PSCID */
16179d085a1cSTomasz Jeznach                 func = riscv_iommu_iot_inval_pscid;
16189d085a1cSTomasz Jeznach             } else {
16199d085a1cSTomasz Jeznach                 /* invalidate cache matching GSCID and PSCID and ADDR (IOVA) */
16209d085a1cSTomasz Jeznach                 func = riscv_iommu_iot_inval_pscid_iova;
16219d085a1cSTomasz Jeznach             }
16229d085a1cSTomasz Jeznach             riscv_iommu_iot_inval(s, func,
16239d085a1cSTomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IOTINVAL_GSCID),
16249d085a1cSTomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IOTINVAL_PSCID),
16259d085a1cSTomasz Jeznach                 cmd.dword1 << 2 & TARGET_PAGE_MASK);
16260c54acb8STomasz Jeznach             break;
16270c54acb8STomasz Jeznach 
16280c54acb8STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IODIR_FUNC_INVAL_DDT,
16290c54acb8STomasz Jeznach                              RISCV_IOMMU_CMD_IODIR_OPCODE):
16300c54acb8STomasz Jeznach             if (!(cmd.dword0 & RISCV_IOMMU_CMD_IODIR_DV)) {
16310c54acb8STomasz Jeznach                 /* invalidate all device context cache mappings */
16320c54acb8STomasz Jeznach                 func = riscv_iommu_ctx_inval_all;
16330c54acb8STomasz Jeznach             } else {
16340c54acb8STomasz Jeznach                 /* invalidate all device context matching DID */
16350c54acb8STomasz Jeznach                 func = riscv_iommu_ctx_inval_devid;
16360c54acb8STomasz Jeznach             }
16370c54acb8STomasz Jeznach             riscv_iommu_ctx_inval(s, func,
16380c54acb8STomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IODIR_DID), 0);
16390c54acb8STomasz Jeznach             break;
16400c54acb8STomasz Jeznach 
16410c54acb8STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IODIR_FUNC_INVAL_PDT,
16420c54acb8STomasz Jeznach                              RISCV_IOMMU_CMD_IODIR_OPCODE):
16430c54acb8STomasz Jeznach             if (!(cmd.dword0 & RISCV_IOMMU_CMD_IODIR_DV)) {
16440c54acb8STomasz Jeznach                 /* illegal command arguments IODIR_PDT & DV == 0 */
16450c54acb8STomasz Jeznach                 goto cmd_ill;
16460c54acb8STomasz Jeznach             } else {
16470c54acb8STomasz Jeznach                 func = riscv_iommu_ctx_inval_devid_procid;
16480c54acb8STomasz Jeznach             }
16490c54acb8STomasz Jeznach             riscv_iommu_ctx_inval(s, func,
16500c54acb8STomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IODIR_DID),
16510c54acb8STomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IODIR_PID));
16520c54acb8STomasz Jeznach             break;
16530c54acb8STomasz Jeznach 
165469a9ae48STomasz Jeznach         /* ATS commands */
165569a9ae48STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_ATS_FUNC_INVAL,
165669a9ae48STomasz Jeznach                              RISCV_IOMMU_CMD_ATS_OPCODE):
165769a9ae48STomasz Jeznach             if (!s->enable_ats) {
165869a9ae48STomasz Jeznach                 goto cmd_ill;
165969a9ae48STomasz Jeznach             }
166069a9ae48STomasz Jeznach 
166169a9ae48STomasz Jeznach             riscv_iommu_ats_inval(s, &cmd);
166269a9ae48STomasz Jeznach             break;
166369a9ae48STomasz Jeznach 
166469a9ae48STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_ATS_FUNC_PRGR,
166569a9ae48STomasz Jeznach                              RISCV_IOMMU_CMD_ATS_OPCODE):
166669a9ae48STomasz Jeznach             if (!s->enable_ats) {
166769a9ae48STomasz Jeznach                 goto cmd_ill;
166869a9ae48STomasz Jeznach             }
166969a9ae48STomasz Jeznach 
167069a9ae48STomasz Jeznach             riscv_iommu_ats_prgr(s, &cmd);
167169a9ae48STomasz Jeznach             break;
167269a9ae48STomasz Jeznach 
16730c54acb8STomasz Jeznach         default:
16740c54acb8STomasz Jeznach         cmd_ill:
16750c54acb8STomasz Jeznach             /* Invalid instruction, do not advance instruction index. */
16760c54acb8STomasz Jeznach             riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR,
16770c54acb8STomasz Jeznach                 RISCV_IOMMU_CQCSR_CMD_ILL, 0);
16780c54acb8STomasz Jeznach             goto fault;
16790c54acb8STomasz Jeznach         }
16800c54acb8STomasz Jeznach 
16810c54acb8STomasz Jeznach         /* Advance and update head pointer after command completes. */
16820c54acb8STomasz Jeznach         head = (head + 1) & s->cq_mask;
16830c54acb8STomasz Jeznach         riscv_iommu_reg_set32(s, RISCV_IOMMU_REG_CQH, head);
16840c54acb8STomasz Jeznach     }
16850c54acb8STomasz Jeznach     return;
16860c54acb8STomasz Jeznach 
16870c54acb8STomasz Jeznach fault:
16880c54acb8STomasz Jeznach     if (ctrl & RISCV_IOMMU_CQCSR_CIE) {
16890c54acb8STomasz Jeznach         riscv_iommu_notify(s, RISCV_IOMMU_INTR_CQ);
16900c54acb8STomasz Jeznach     }
16910c54acb8STomasz Jeznach }
16920c54acb8STomasz Jeznach 
16930c54acb8STomasz Jeznach static void riscv_iommu_process_cq_control(RISCVIOMMUState *s)
16940c54acb8STomasz Jeznach {
16950c54acb8STomasz Jeznach     uint64_t base;
16960c54acb8STomasz Jeznach     uint32_t ctrl_set = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQCSR);
16970c54acb8STomasz Jeznach     uint32_t ctrl_clr;
16980c54acb8STomasz Jeznach     bool enable = !!(ctrl_set & RISCV_IOMMU_CQCSR_CQEN);
16990c54acb8STomasz Jeznach     bool active = !!(ctrl_set & RISCV_IOMMU_CQCSR_CQON);
17000c54acb8STomasz Jeznach 
17010c54acb8STomasz Jeznach     if (enable && !active) {
17020c54acb8STomasz Jeznach         base = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_CQB);
17030c54acb8STomasz Jeznach         s->cq_mask = (2ULL << get_field(base, RISCV_IOMMU_CQB_LOG2SZ)) - 1;
17040c54acb8STomasz Jeznach         s->cq_addr = PPN_PHYS(get_field(base, RISCV_IOMMU_CQB_PPN));
17050c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQT], ~s->cq_mask);
17060c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_CQH], 0);
17070c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_CQT], 0);
17080c54acb8STomasz Jeznach         ctrl_set = RISCV_IOMMU_CQCSR_CQON;
17090c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_CQCSR_BUSY | RISCV_IOMMU_CQCSR_CQMF |
17100c54acb8STomasz Jeznach                    RISCV_IOMMU_CQCSR_CMD_ILL | RISCV_IOMMU_CQCSR_CMD_TO |
17110c54acb8STomasz Jeznach                    RISCV_IOMMU_CQCSR_FENCE_W_IP;
17120c54acb8STomasz Jeznach     } else if (!enable && active) {
17130c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQT], ~0);
17140c54acb8STomasz Jeznach         ctrl_set = 0;
17150c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_CQCSR_BUSY | RISCV_IOMMU_CQCSR_CQON;
17160c54acb8STomasz Jeznach     } else {
17170c54acb8STomasz Jeznach         ctrl_set = 0;
17180c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_CQCSR_BUSY;
17190c54acb8STomasz Jeznach     }
17200c54acb8STomasz Jeznach 
17210c54acb8STomasz Jeznach     riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR, ctrl_set, ctrl_clr);
17220c54acb8STomasz Jeznach }
17230c54acb8STomasz Jeznach 
17240c54acb8STomasz Jeznach static void riscv_iommu_process_fq_control(RISCVIOMMUState *s)
17250c54acb8STomasz Jeznach {
17260c54acb8STomasz Jeznach     uint64_t base;
17270c54acb8STomasz Jeznach     uint32_t ctrl_set = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQCSR);
17280c54acb8STomasz Jeznach     uint32_t ctrl_clr;
17290c54acb8STomasz Jeznach     bool enable = !!(ctrl_set & RISCV_IOMMU_FQCSR_FQEN);
17300c54acb8STomasz Jeznach     bool active = !!(ctrl_set & RISCV_IOMMU_FQCSR_FQON);
17310c54acb8STomasz Jeznach 
17320c54acb8STomasz Jeznach     if (enable && !active) {
17330c54acb8STomasz Jeznach         base = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_FQB);
17340c54acb8STomasz Jeznach         s->fq_mask = (2ULL << get_field(base, RISCV_IOMMU_FQB_LOG2SZ)) - 1;
17350c54acb8STomasz Jeznach         s->fq_addr = PPN_PHYS(get_field(base, RISCV_IOMMU_FQB_PPN));
17360c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQH], ~s->fq_mask);
17370c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_FQH], 0);
17380c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_FQT], 0);
17390c54acb8STomasz Jeznach         ctrl_set = RISCV_IOMMU_FQCSR_FQON;
17400c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_FQCSR_BUSY | RISCV_IOMMU_FQCSR_FQMF |
17410c54acb8STomasz Jeznach             RISCV_IOMMU_FQCSR_FQOF;
17420c54acb8STomasz Jeznach     } else if (!enable && active) {
17430c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQH], ~0);
17440c54acb8STomasz Jeznach         ctrl_set = 0;
17450c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_FQCSR_BUSY | RISCV_IOMMU_FQCSR_FQON;
17460c54acb8STomasz Jeznach     } else {
17470c54acb8STomasz Jeznach         ctrl_set = 0;
17480c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_FQCSR_BUSY;
17490c54acb8STomasz Jeznach     }
17500c54acb8STomasz Jeznach 
17510c54acb8STomasz Jeznach     riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_FQCSR, ctrl_set, ctrl_clr);
17520c54acb8STomasz Jeznach }
17530c54acb8STomasz Jeznach 
17540c54acb8STomasz Jeznach static void riscv_iommu_process_pq_control(RISCVIOMMUState *s)
17550c54acb8STomasz Jeznach {
17560c54acb8STomasz Jeznach     uint64_t base;
17570c54acb8STomasz Jeznach     uint32_t ctrl_set = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_PQCSR);
17580c54acb8STomasz Jeznach     uint32_t ctrl_clr;
17590c54acb8STomasz Jeznach     bool enable = !!(ctrl_set & RISCV_IOMMU_PQCSR_PQEN);
17600c54acb8STomasz Jeznach     bool active = !!(ctrl_set & RISCV_IOMMU_PQCSR_PQON);
17610c54acb8STomasz Jeznach 
17620c54acb8STomasz Jeznach     if (enable && !active) {
17630c54acb8STomasz Jeznach         base = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_PQB);
17640c54acb8STomasz Jeznach         s->pq_mask = (2ULL << get_field(base, RISCV_IOMMU_PQB_LOG2SZ)) - 1;
17650c54acb8STomasz Jeznach         s->pq_addr = PPN_PHYS(get_field(base, RISCV_IOMMU_PQB_PPN));
17660c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQH], ~s->pq_mask);
17670c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_PQH], 0);
17680c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_PQT], 0);
17690c54acb8STomasz Jeznach         ctrl_set = RISCV_IOMMU_PQCSR_PQON;
17700c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_PQCSR_BUSY | RISCV_IOMMU_PQCSR_PQMF |
17710c54acb8STomasz Jeznach             RISCV_IOMMU_PQCSR_PQOF;
17720c54acb8STomasz Jeznach     } else if (!enable && active) {
17730c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQH], ~0);
17740c54acb8STomasz Jeznach         ctrl_set = 0;
17750c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_PQCSR_BUSY | RISCV_IOMMU_PQCSR_PQON;
17760c54acb8STomasz Jeznach     } else {
17770c54acb8STomasz Jeznach         ctrl_set = 0;
17780c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_PQCSR_BUSY;
17790c54acb8STomasz Jeznach     }
17800c54acb8STomasz Jeznach 
17810c54acb8STomasz Jeznach     riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_PQCSR, ctrl_set, ctrl_clr);
17820c54acb8STomasz Jeznach }
17830c54acb8STomasz Jeznach 
1784a7aa525bSTomasz Jeznach static void riscv_iommu_process_dbg(RISCVIOMMUState *s)
1785a7aa525bSTomasz Jeznach {
1786a7aa525bSTomasz Jeznach     uint64_t iova = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_TR_REQ_IOVA);
1787a7aa525bSTomasz Jeznach     uint64_t ctrl = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_TR_REQ_CTL);
1788a7aa525bSTomasz Jeznach     unsigned devid = get_field(ctrl, RISCV_IOMMU_TR_REQ_CTL_DID);
1789a7aa525bSTomasz Jeznach     unsigned pid = get_field(ctrl, RISCV_IOMMU_TR_REQ_CTL_PID);
1790a7aa525bSTomasz Jeznach     RISCVIOMMUContext *ctx;
1791a7aa525bSTomasz Jeznach     void *ref;
1792a7aa525bSTomasz Jeznach 
1793a7aa525bSTomasz Jeznach     if (!(ctrl & RISCV_IOMMU_TR_REQ_CTL_GO_BUSY)) {
1794a7aa525bSTomasz Jeznach         return;
1795a7aa525bSTomasz Jeznach     }
1796a7aa525bSTomasz Jeznach 
1797a7aa525bSTomasz Jeznach     ctx = riscv_iommu_ctx(s, devid, pid, &ref);
1798a7aa525bSTomasz Jeznach     if (ctx == NULL) {
1799a7aa525bSTomasz Jeznach         riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_TR_RESPONSE,
1800a7aa525bSTomasz Jeznach                                  RISCV_IOMMU_TR_RESPONSE_FAULT |
1801a7aa525bSTomasz Jeznach                                  (RISCV_IOMMU_FQ_CAUSE_DMA_DISABLED << 10));
1802a7aa525bSTomasz Jeznach     } else {
1803a7aa525bSTomasz Jeznach         IOMMUTLBEntry iotlb = {
1804a7aa525bSTomasz Jeznach             .iova = iova,
1805a7aa525bSTomasz Jeznach             .perm = ctrl & RISCV_IOMMU_TR_REQ_CTL_NW ? IOMMU_RO : IOMMU_RW,
1806a7aa525bSTomasz Jeznach             .addr_mask = ~0,
1807a7aa525bSTomasz Jeznach             .target_as = NULL,
1808a7aa525bSTomasz Jeznach         };
1809a7aa525bSTomasz Jeznach         int fault = riscv_iommu_translate(s, ctx, &iotlb, false);
1810a7aa525bSTomasz Jeznach         if (fault) {
1811a7aa525bSTomasz Jeznach             iova = RISCV_IOMMU_TR_RESPONSE_FAULT | (((uint64_t) fault) << 10);
1812a7aa525bSTomasz Jeznach         } else {
1813a7aa525bSTomasz Jeznach             iova = iotlb.translated_addr & ~iotlb.addr_mask;
1814a7aa525bSTomasz Jeznach             iova >>= TARGET_PAGE_BITS;
1815a7aa525bSTomasz Jeznach             iova &= RISCV_IOMMU_TR_RESPONSE_PPN;
1816a7aa525bSTomasz Jeznach 
1817a7aa525bSTomasz Jeznach             /* We do not support superpages (> 4kbs) for now */
1818a7aa525bSTomasz Jeznach             iova &= ~RISCV_IOMMU_TR_RESPONSE_S;
1819a7aa525bSTomasz Jeznach         }
1820a7aa525bSTomasz Jeznach         riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_TR_RESPONSE, iova);
1821a7aa525bSTomasz Jeznach     }
1822a7aa525bSTomasz Jeznach 
1823a7aa525bSTomasz Jeznach     riscv_iommu_reg_mod64(s, RISCV_IOMMU_REG_TR_REQ_CTL, 0,
1824a7aa525bSTomasz Jeznach         RISCV_IOMMU_TR_REQ_CTL_GO_BUSY);
1825a7aa525bSTomasz Jeznach     riscv_iommu_ctx_put(s, ref);
1826a7aa525bSTomasz Jeznach }
1827a7aa525bSTomasz Jeznach 
18280c54acb8STomasz Jeznach typedef void riscv_iommu_process_fn(RISCVIOMMUState *s);
18290c54acb8STomasz Jeznach 
18300c54acb8STomasz Jeznach static void riscv_iommu_update_icvec(RISCVIOMMUState *s, uint64_t data)
18310c54acb8STomasz Jeznach {
18320c54acb8STomasz Jeznach     uint64_t icvec = 0;
18330c54acb8STomasz Jeznach 
18340c54acb8STomasz Jeznach     icvec |= MIN(data & RISCV_IOMMU_ICVEC_CIV,
18350c54acb8STomasz Jeznach                  s->icvec_avail_vectors & RISCV_IOMMU_ICVEC_CIV);
18360c54acb8STomasz Jeznach 
18370c54acb8STomasz Jeznach     icvec |= MIN(data & RISCV_IOMMU_ICVEC_FIV,
18380c54acb8STomasz Jeznach                  s->icvec_avail_vectors & RISCV_IOMMU_ICVEC_FIV);
18390c54acb8STomasz Jeznach 
18400c54acb8STomasz Jeznach     icvec |= MIN(data & RISCV_IOMMU_ICVEC_PMIV,
18410c54acb8STomasz Jeznach                  s->icvec_avail_vectors & RISCV_IOMMU_ICVEC_PMIV);
18420c54acb8STomasz Jeznach 
18430c54acb8STomasz Jeznach     icvec |= MIN(data & RISCV_IOMMU_ICVEC_PIV,
18440c54acb8STomasz Jeznach                  s->icvec_avail_vectors & RISCV_IOMMU_ICVEC_PIV);
18450c54acb8STomasz Jeznach 
18460c54acb8STomasz Jeznach     trace_riscv_iommu_icvec_write(data, icvec);
18470c54acb8STomasz Jeznach 
18480c54acb8STomasz Jeznach     riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_ICVEC, icvec);
18490c54acb8STomasz Jeznach }
18500c54acb8STomasz Jeznach 
18510c54acb8STomasz Jeznach static void riscv_iommu_update_ipsr(RISCVIOMMUState *s, uint64_t data)
18520c54acb8STomasz Jeznach {
18530c54acb8STomasz Jeznach     uint32_t cqcsr, fqcsr, pqcsr;
18540c54acb8STomasz Jeznach     uint32_t ipsr_set = 0;
18550c54acb8STomasz Jeznach     uint32_t ipsr_clr = 0;
18560c54acb8STomasz Jeznach 
18570c54acb8STomasz Jeznach     if (data & RISCV_IOMMU_IPSR_CIP) {
18580c54acb8STomasz Jeznach         cqcsr = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQCSR);
18590c54acb8STomasz Jeznach 
18600c54acb8STomasz Jeznach         if (cqcsr & RISCV_IOMMU_CQCSR_CIE &&
18610c54acb8STomasz Jeznach             (cqcsr & RISCV_IOMMU_CQCSR_FENCE_W_IP ||
18620c54acb8STomasz Jeznach              cqcsr & RISCV_IOMMU_CQCSR_CMD_ILL ||
18630c54acb8STomasz Jeznach              cqcsr & RISCV_IOMMU_CQCSR_CMD_TO ||
18640c54acb8STomasz Jeznach              cqcsr & RISCV_IOMMU_CQCSR_CQMF)) {
18650c54acb8STomasz Jeznach             ipsr_set |= RISCV_IOMMU_IPSR_CIP;
18660c54acb8STomasz Jeznach         } else {
18670c54acb8STomasz Jeznach             ipsr_clr |= RISCV_IOMMU_IPSR_CIP;
18680c54acb8STomasz Jeznach         }
18690c54acb8STomasz Jeznach     } else {
18700c54acb8STomasz Jeznach         ipsr_clr |= RISCV_IOMMU_IPSR_CIP;
18710c54acb8STomasz Jeznach     }
18720c54acb8STomasz Jeznach 
18730c54acb8STomasz Jeznach     if (data & RISCV_IOMMU_IPSR_FIP) {
18740c54acb8STomasz Jeznach         fqcsr = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQCSR);
18750c54acb8STomasz Jeznach 
18760c54acb8STomasz Jeznach         if (fqcsr & RISCV_IOMMU_FQCSR_FIE &&
18770c54acb8STomasz Jeznach             (fqcsr & RISCV_IOMMU_FQCSR_FQOF ||
18780c54acb8STomasz Jeznach              fqcsr & RISCV_IOMMU_FQCSR_FQMF)) {
18790c54acb8STomasz Jeznach             ipsr_set |= RISCV_IOMMU_IPSR_FIP;
18800c54acb8STomasz Jeznach         } else {
18810c54acb8STomasz Jeznach             ipsr_clr |= RISCV_IOMMU_IPSR_FIP;
18820c54acb8STomasz Jeznach         }
18830c54acb8STomasz Jeznach     } else {
18840c54acb8STomasz Jeznach         ipsr_clr |= RISCV_IOMMU_IPSR_FIP;
18850c54acb8STomasz Jeznach     }
18860c54acb8STomasz Jeznach 
18870c54acb8STomasz Jeznach     if (data & RISCV_IOMMU_IPSR_PIP) {
18880c54acb8STomasz Jeznach         pqcsr = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_PQCSR);
18890c54acb8STomasz Jeznach 
18900c54acb8STomasz Jeznach         if (pqcsr & RISCV_IOMMU_PQCSR_PIE &&
18910c54acb8STomasz Jeznach             (pqcsr & RISCV_IOMMU_PQCSR_PQOF ||
18920c54acb8STomasz Jeznach              pqcsr & RISCV_IOMMU_PQCSR_PQMF)) {
18930c54acb8STomasz Jeznach             ipsr_set |= RISCV_IOMMU_IPSR_PIP;
18940c54acb8STomasz Jeznach         } else {
18950c54acb8STomasz Jeznach             ipsr_clr |= RISCV_IOMMU_IPSR_PIP;
18960c54acb8STomasz Jeznach         }
18970c54acb8STomasz Jeznach     } else {
18980c54acb8STomasz Jeznach         ipsr_clr |= RISCV_IOMMU_IPSR_PIP;
18990c54acb8STomasz Jeznach     }
19000c54acb8STomasz Jeznach 
19010c54acb8STomasz Jeznach     riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_IPSR, ipsr_set, ipsr_clr);
19020c54acb8STomasz Jeznach }
19030c54acb8STomasz Jeznach 
19040c54acb8STomasz Jeznach /*
19050c54acb8STomasz Jeznach  * Write the resulting value of 'data' for the reg specified
19060c54acb8STomasz Jeznach  * by 'reg_addr', after considering read-only/read-write/write-clear
19070c54acb8STomasz Jeznach  * bits, in the pointer 'dest'.
19080c54acb8STomasz Jeznach  *
19090c54acb8STomasz Jeznach  * The result is written in little-endian.
19100c54acb8STomasz Jeznach  */
19110c54acb8STomasz Jeznach static void riscv_iommu_write_reg_val(RISCVIOMMUState *s,
19120c54acb8STomasz Jeznach                                       void *dest, hwaddr reg_addr,
19130c54acb8STomasz Jeznach                                       int size, uint64_t data)
19140c54acb8STomasz Jeznach {
19150c54acb8STomasz Jeznach     uint64_t ro = ldn_le_p(&s->regs_ro[reg_addr], size);
19160c54acb8STomasz Jeznach     uint64_t wc = ldn_le_p(&s->regs_wc[reg_addr], size);
19170c54acb8STomasz Jeznach     uint64_t rw = ldn_le_p(&s->regs_rw[reg_addr], size);
19180c54acb8STomasz Jeznach 
19190c54acb8STomasz Jeznach     stn_le_p(dest, size, ((rw & ro) | (data & ~ro)) & ~(data & wc));
19200c54acb8STomasz Jeznach }
19210c54acb8STomasz Jeznach 
19220c54acb8STomasz Jeznach static MemTxResult riscv_iommu_mmio_write(void *opaque, hwaddr addr,
19230c54acb8STomasz Jeznach                                           uint64_t data, unsigned size,
19240c54acb8STomasz Jeznach                                           MemTxAttrs attrs)
19250c54acb8STomasz Jeznach {
19260c54acb8STomasz Jeznach     riscv_iommu_process_fn *process_fn = NULL;
19270c54acb8STomasz Jeznach     RISCVIOMMUState *s = opaque;
19280c54acb8STomasz Jeznach     uint32_t regb = addr & ~3;
19290c54acb8STomasz Jeznach     uint32_t busy = 0;
19300c54acb8STomasz Jeznach     uint64_t val = 0;
19310c54acb8STomasz Jeznach 
19320c54acb8STomasz Jeznach     if ((addr & (size - 1)) != 0) {
19330c54acb8STomasz Jeznach         /* Unsupported MMIO alignment or access size */
19340c54acb8STomasz Jeznach         return MEMTX_ERROR;
19350c54acb8STomasz Jeznach     }
19360c54acb8STomasz Jeznach 
19370c54acb8STomasz Jeznach     if (addr + size > RISCV_IOMMU_REG_MSI_CONFIG) {
19380c54acb8STomasz Jeznach         /* Unsupported MMIO access location. */
19390c54acb8STomasz Jeznach         return MEMTX_ACCESS_ERROR;
19400c54acb8STomasz Jeznach     }
19410c54acb8STomasz Jeznach 
19420c54acb8STomasz Jeznach     /* Track actionable MMIO write. */
19430c54acb8STomasz Jeznach     switch (regb) {
19440c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_DDTP:
19450c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_DDTP + 4:
19460c54acb8STomasz Jeznach         process_fn = riscv_iommu_process_ddtp;
19470c54acb8STomasz Jeznach         regb = RISCV_IOMMU_REG_DDTP;
19480c54acb8STomasz Jeznach         busy = RISCV_IOMMU_DDTP_BUSY;
19490c54acb8STomasz Jeznach         break;
19500c54acb8STomasz Jeznach 
19510c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_CQT:
19520c54acb8STomasz Jeznach         process_fn = riscv_iommu_process_cq_tail;
19530c54acb8STomasz Jeznach         break;
19540c54acb8STomasz Jeznach 
19550c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_CQCSR:
19560c54acb8STomasz Jeznach         process_fn = riscv_iommu_process_cq_control;
19570c54acb8STomasz Jeznach         busy = RISCV_IOMMU_CQCSR_BUSY;
19580c54acb8STomasz Jeznach         break;
19590c54acb8STomasz Jeznach 
19600c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_FQCSR:
19610c54acb8STomasz Jeznach         process_fn = riscv_iommu_process_fq_control;
19620c54acb8STomasz Jeznach         busy = RISCV_IOMMU_FQCSR_BUSY;
19630c54acb8STomasz Jeznach         break;
19640c54acb8STomasz Jeznach 
19650c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_PQCSR:
19660c54acb8STomasz Jeznach         process_fn = riscv_iommu_process_pq_control;
19670c54acb8STomasz Jeznach         busy = RISCV_IOMMU_PQCSR_BUSY;
19680c54acb8STomasz Jeznach         break;
19690c54acb8STomasz Jeznach 
19700c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_ICVEC:
19710c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_IPSR:
19720c54acb8STomasz Jeznach         /*
19730c54acb8STomasz Jeznach          * ICVEC and IPSR have special read/write procedures. We'll
19740c54acb8STomasz Jeznach          * call their respective helpers and exit.
19750c54acb8STomasz Jeznach          */
19760c54acb8STomasz Jeznach         riscv_iommu_write_reg_val(s, &val, addr, size, data);
19770c54acb8STomasz Jeznach 
19780c54acb8STomasz Jeznach         /*
19790c54acb8STomasz Jeznach          * 'val' is stored as LE. Switch to host endianess
19800c54acb8STomasz Jeznach          * before using it.
19810c54acb8STomasz Jeznach          */
19820c54acb8STomasz Jeznach         val = le64_to_cpu(val);
19830c54acb8STomasz Jeznach 
19840c54acb8STomasz Jeznach         if (regb == RISCV_IOMMU_REG_ICVEC) {
19850c54acb8STomasz Jeznach             riscv_iommu_update_icvec(s, val);
19860c54acb8STomasz Jeznach         } else {
19870c54acb8STomasz Jeznach             riscv_iommu_update_ipsr(s, val);
19880c54acb8STomasz Jeznach         }
19890c54acb8STomasz Jeznach 
19900c54acb8STomasz Jeznach         return MEMTX_OK;
19910c54acb8STomasz Jeznach 
1992a7aa525bSTomasz Jeznach     case RISCV_IOMMU_REG_TR_REQ_CTL:
1993a7aa525bSTomasz Jeznach         process_fn = riscv_iommu_process_dbg;
1994a7aa525bSTomasz Jeznach         regb = RISCV_IOMMU_REG_TR_REQ_CTL;
1995a7aa525bSTomasz Jeznach         busy = RISCV_IOMMU_TR_REQ_CTL_GO_BUSY;
1996a7aa525bSTomasz Jeznach         break;
1997a7aa525bSTomasz Jeznach 
19980c54acb8STomasz Jeznach     default:
19990c54acb8STomasz Jeznach         break;
20000c54acb8STomasz Jeznach     }
20010c54acb8STomasz Jeznach 
20020c54acb8STomasz Jeznach     /*
20030c54acb8STomasz Jeznach      * Registers update might be not synchronized with core logic.
20040c54acb8STomasz Jeznach      * If system software updates register when relevant BUSY bit
20050c54acb8STomasz Jeznach      * is set IOMMU behavior of additional writes to the register
20060c54acb8STomasz Jeznach      * is UNSPECIFIED.
20070c54acb8STomasz Jeznach      */
20080c54acb8STomasz Jeznach     riscv_iommu_write_reg_val(s, &s->regs_rw[addr], addr, size, data);
20090c54acb8STomasz Jeznach 
20100c54acb8STomasz Jeznach     /* Busy flag update, MSB 4-byte register. */
20110c54acb8STomasz Jeznach     if (busy) {
20120c54acb8STomasz Jeznach         uint32_t rw = ldl_le_p(&s->regs_rw[regb]);
20130c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[regb], rw | busy);
20140c54acb8STomasz Jeznach     }
20150c54acb8STomasz Jeznach 
20160c54acb8STomasz Jeznach     if (process_fn) {
20170c54acb8STomasz Jeznach         process_fn(s);
20180c54acb8STomasz Jeznach     }
20190c54acb8STomasz Jeznach 
20200c54acb8STomasz Jeznach     return MEMTX_OK;
20210c54acb8STomasz Jeznach }
20220c54acb8STomasz Jeznach 
20230c54acb8STomasz Jeznach static MemTxResult riscv_iommu_mmio_read(void *opaque, hwaddr addr,
20240c54acb8STomasz Jeznach     uint64_t *data, unsigned size, MemTxAttrs attrs)
20250c54acb8STomasz Jeznach {
20260c54acb8STomasz Jeznach     RISCVIOMMUState *s = opaque;
20270c54acb8STomasz Jeznach     uint64_t val = -1;
20280c54acb8STomasz Jeznach     uint8_t *ptr;
20290c54acb8STomasz Jeznach 
20300c54acb8STomasz Jeznach     if ((addr & (size - 1)) != 0) {
20310c54acb8STomasz Jeznach         /* Unsupported MMIO alignment. */
20320c54acb8STomasz Jeznach         return MEMTX_ERROR;
20330c54acb8STomasz Jeznach     }
20340c54acb8STomasz Jeznach 
20350c54acb8STomasz Jeznach     if (addr + size > RISCV_IOMMU_REG_MSI_CONFIG) {
20360c54acb8STomasz Jeznach         return MEMTX_ACCESS_ERROR;
20370c54acb8STomasz Jeznach     }
20380c54acb8STomasz Jeznach 
20390c54acb8STomasz Jeznach     ptr = &s->regs_rw[addr];
20400c54acb8STomasz Jeznach     val = ldn_le_p(ptr, size);
20410c54acb8STomasz Jeznach 
20420c54acb8STomasz Jeznach     *data = val;
20430c54acb8STomasz Jeznach 
20440c54acb8STomasz Jeznach     return MEMTX_OK;
20450c54acb8STomasz Jeznach }
20460c54acb8STomasz Jeznach 
20470c54acb8STomasz Jeznach static const MemoryRegionOps riscv_iommu_mmio_ops = {
20480c54acb8STomasz Jeznach     .read_with_attrs = riscv_iommu_mmio_read,
20490c54acb8STomasz Jeznach     .write_with_attrs = riscv_iommu_mmio_write,
20500c54acb8STomasz Jeznach     .endianness = DEVICE_NATIVE_ENDIAN,
20510c54acb8STomasz Jeznach     .impl = {
20520c54acb8STomasz Jeznach         .min_access_size = 4,
20530c54acb8STomasz Jeznach         .max_access_size = 8,
20540c54acb8STomasz Jeznach         .unaligned = false,
20550c54acb8STomasz Jeznach     },
20560c54acb8STomasz Jeznach     .valid = {
20570c54acb8STomasz Jeznach         .min_access_size = 4,
20580c54acb8STomasz Jeznach         .max_access_size = 8,
20590c54acb8STomasz Jeznach     }
20600c54acb8STomasz Jeznach };
20610c54acb8STomasz Jeznach 
20620c54acb8STomasz Jeznach /*
20630c54acb8STomasz Jeznach  * Translations matching MSI pattern check are redirected to "riscv-iommu-trap"
20640c54acb8STomasz Jeznach  * memory region as untranslated address, for additional MSI/MRIF interception
20650c54acb8STomasz Jeznach  * by IOMMU interrupt remapping implementation.
20660c54acb8STomasz Jeznach  * Note: Device emulation code generating an MSI is expected to provide a valid
20670c54acb8STomasz Jeznach  * memory transaction attributes with requested_id set.
20680c54acb8STomasz Jeznach  */
20690c54acb8STomasz Jeznach static MemTxResult riscv_iommu_trap_write(void *opaque, hwaddr addr,
20700c54acb8STomasz Jeznach     uint64_t data, unsigned size, MemTxAttrs attrs)
20710c54acb8STomasz Jeznach {
20720c54acb8STomasz Jeznach     RISCVIOMMUState* s = (RISCVIOMMUState *)opaque;
20730c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx;
20740c54acb8STomasz Jeznach     MemTxResult res;
20750c54acb8STomasz Jeznach     void *ref;
20760c54acb8STomasz Jeznach     uint32_t devid = attrs.requester_id;
20770c54acb8STomasz Jeznach 
20780c54acb8STomasz Jeznach     if (attrs.unspecified) {
20790c54acb8STomasz Jeznach         return MEMTX_ACCESS_ERROR;
20800c54acb8STomasz Jeznach     }
20810c54acb8STomasz Jeznach 
20820c54acb8STomasz Jeznach     /* FIXME: PCIe bus remapping for attached endpoints. */
20830c54acb8STomasz Jeznach     devid |= s->bus << 8;
20840c54acb8STomasz Jeznach 
20850c54acb8STomasz Jeznach     ctx = riscv_iommu_ctx(s, devid, 0, &ref);
20860c54acb8STomasz Jeznach     if (ctx == NULL) {
20870c54acb8STomasz Jeznach         res = MEMTX_ACCESS_ERROR;
20880c54acb8STomasz Jeznach     } else {
20890c54acb8STomasz Jeznach         res = riscv_iommu_msi_write(s, ctx, addr, data, size, attrs);
20900c54acb8STomasz Jeznach     }
20910c54acb8STomasz Jeznach     riscv_iommu_ctx_put(s, ref);
20920c54acb8STomasz Jeznach     return res;
20930c54acb8STomasz Jeznach }
20940c54acb8STomasz Jeznach 
20950c54acb8STomasz Jeznach static MemTxResult riscv_iommu_trap_read(void *opaque, hwaddr addr,
20960c54acb8STomasz Jeznach     uint64_t *data, unsigned size, MemTxAttrs attrs)
20970c54acb8STomasz Jeznach {
20980c54acb8STomasz Jeznach     return MEMTX_ACCESS_ERROR;
20990c54acb8STomasz Jeznach }
21000c54acb8STomasz Jeznach 
21010c54acb8STomasz Jeznach static const MemoryRegionOps riscv_iommu_trap_ops = {
21020c54acb8STomasz Jeznach     .read_with_attrs = riscv_iommu_trap_read,
21030c54acb8STomasz Jeznach     .write_with_attrs = riscv_iommu_trap_write,
21040c54acb8STomasz Jeznach     .endianness = DEVICE_LITTLE_ENDIAN,
21050c54acb8STomasz Jeznach     .impl = {
21060c54acb8STomasz Jeznach         .min_access_size = 4,
21070c54acb8STomasz Jeznach         .max_access_size = 8,
21080c54acb8STomasz Jeznach         .unaligned = true,
21090c54acb8STomasz Jeznach     },
21100c54acb8STomasz Jeznach     .valid = {
21110c54acb8STomasz Jeznach         .min_access_size = 4,
21120c54acb8STomasz Jeznach         .max_access_size = 8,
21130c54acb8STomasz Jeznach     }
21140c54acb8STomasz Jeznach };
21150c54acb8STomasz Jeznach 
21160c54acb8STomasz Jeznach static void riscv_iommu_realize(DeviceState *dev, Error **errp)
21170c54acb8STomasz Jeznach {
21180c54acb8STomasz Jeznach     RISCVIOMMUState *s = RISCV_IOMMU(dev);
21190c54acb8STomasz Jeznach 
21200c54acb8STomasz Jeznach     s->cap = s->version & RISCV_IOMMU_CAP_VERSION;
21210c54acb8STomasz Jeznach     if (s->enable_msi) {
21220c54acb8STomasz Jeznach         s->cap |= RISCV_IOMMU_CAP_MSI_FLAT | RISCV_IOMMU_CAP_MSI_MRIF;
21230c54acb8STomasz Jeznach     }
212469a9ae48STomasz Jeznach     if (s->enable_ats) {
212569a9ae48STomasz Jeznach         s->cap |= RISCV_IOMMU_CAP_ATS;
212669a9ae48STomasz Jeznach     }
21270c54acb8STomasz Jeznach     if (s->enable_s_stage) {
21280c54acb8STomasz Jeznach         s->cap |= RISCV_IOMMU_CAP_SV32 | RISCV_IOMMU_CAP_SV39 |
21290c54acb8STomasz Jeznach                   RISCV_IOMMU_CAP_SV48 | RISCV_IOMMU_CAP_SV57;
21300c54acb8STomasz Jeznach     }
21310c54acb8STomasz Jeznach     if (s->enable_g_stage) {
21320c54acb8STomasz Jeznach         s->cap |= RISCV_IOMMU_CAP_SV32X4 | RISCV_IOMMU_CAP_SV39X4 |
21330c54acb8STomasz Jeznach                   RISCV_IOMMU_CAP_SV48X4 | RISCV_IOMMU_CAP_SV57X4;
21340c54acb8STomasz Jeznach     }
2135a7aa525bSTomasz Jeznach     /* Enable translation debug interface */
2136a7aa525bSTomasz Jeznach     s->cap |= RISCV_IOMMU_CAP_DBG;
2137a7aa525bSTomasz Jeznach 
21380c54acb8STomasz Jeznach     /* Report QEMU target physical address space limits */
21390c54acb8STomasz Jeznach     s->cap = set_field(s->cap, RISCV_IOMMU_CAP_PAS,
21400c54acb8STomasz Jeznach                        TARGET_PHYS_ADDR_SPACE_BITS);
21410c54acb8STomasz Jeznach 
21420c54acb8STomasz Jeznach     /* TODO: method to report supported PID bits */
21430c54acb8STomasz Jeznach     s->pid_bits = 8; /* restricted to size of MemTxAttrs.pid */
21440c54acb8STomasz Jeznach     s->cap |= RISCV_IOMMU_CAP_PD8;
21450c54acb8STomasz Jeznach 
21460c54acb8STomasz Jeznach     /* Out-of-reset translation mode: OFF (DMA disabled) BARE (passthrough) */
21470c54acb8STomasz Jeznach     s->ddtp = set_field(0, RISCV_IOMMU_DDTP_MODE, s->enable_off ?
21480c54acb8STomasz Jeznach                         RISCV_IOMMU_DDTP_MODE_OFF : RISCV_IOMMU_DDTP_MODE_BARE);
21490c54acb8STomasz Jeznach 
21500c54acb8STomasz Jeznach     /* register storage */
21510c54acb8STomasz Jeznach     s->regs_rw = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE);
21520c54acb8STomasz Jeznach     s->regs_ro = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE);
21530c54acb8STomasz Jeznach     s->regs_wc = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE);
21540c54acb8STomasz Jeznach 
21550c54acb8STomasz Jeznach      /* Mark all registers read-only */
21560c54acb8STomasz Jeznach     memset(s->regs_ro, 0xff, RISCV_IOMMU_REG_SIZE);
21570c54acb8STomasz Jeznach 
21580c54acb8STomasz Jeznach     /*
21590c54acb8STomasz Jeznach      * Register complete MMIO space, including MSI/PBA registers.
21600c54acb8STomasz Jeznach      * Note, PCIDevice implementation will add overlapping MR for MSI/PBA,
21610c54acb8STomasz Jeznach      * managed directly by the PCIDevice implementation.
21620c54acb8STomasz Jeznach      */
21630c54acb8STomasz Jeznach     memory_region_init_io(&s->regs_mr, OBJECT(dev), &riscv_iommu_mmio_ops, s,
21640c54acb8STomasz Jeznach         "riscv-iommu-regs", RISCV_IOMMU_REG_SIZE);
21650c54acb8STomasz Jeznach 
21660c54acb8STomasz Jeznach     /* Set power-on register state */
21670c54acb8STomasz Jeznach     stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_CAP], s->cap);
21680c54acb8STomasz Jeznach     stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_FCTL], 0);
21690c54acb8STomasz Jeznach     stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_FCTL],
21700c54acb8STomasz Jeznach              ~(RISCV_IOMMU_FCTL_BE | RISCV_IOMMU_FCTL_WSI));
21710c54acb8STomasz Jeznach     stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_DDTP],
21720c54acb8STomasz Jeznach         ~(RISCV_IOMMU_DDTP_PPN | RISCV_IOMMU_DDTP_MODE));
21730c54acb8STomasz Jeznach     stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQB],
21740c54acb8STomasz Jeznach         ~(RISCV_IOMMU_CQB_LOG2SZ | RISCV_IOMMU_CQB_PPN));
21750c54acb8STomasz Jeznach     stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQB],
21760c54acb8STomasz Jeznach         ~(RISCV_IOMMU_FQB_LOG2SZ | RISCV_IOMMU_FQB_PPN));
21770c54acb8STomasz Jeznach     stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQB],
21780c54acb8STomasz Jeznach         ~(RISCV_IOMMU_PQB_LOG2SZ | RISCV_IOMMU_PQB_PPN));
21790c54acb8STomasz Jeznach     stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_CQCSR], RISCV_IOMMU_CQCSR_CQMF |
21800c54acb8STomasz Jeznach         RISCV_IOMMU_CQCSR_CMD_TO | RISCV_IOMMU_CQCSR_CMD_ILL);
21810c54acb8STomasz Jeznach     stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQCSR], RISCV_IOMMU_CQCSR_CQON |
21820c54acb8STomasz Jeznach         RISCV_IOMMU_CQCSR_BUSY);
21830c54acb8STomasz Jeznach     stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_FQCSR], RISCV_IOMMU_FQCSR_FQMF |
21840c54acb8STomasz Jeznach         RISCV_IOMMU_FQCSR_FQOF);
21850c54acb8STomasz Jeznach     stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQCSR], RISCV_IOMMU_FQCSR_FQON |
21860c54acb8STomasz Jeznach         RISCV_IOMMU_FQCSR_BUSY);
21870c54acb8STomasz Jeznach     stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_PQCSR], RISCV_IOMMU_PQCSR_PQMF |
21880c54acb8STomasz Jeznach         RISCV_IOMMU_PQCSR_PQOF);
21890c54acb8STomasz Jeznach     stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQCSR], RISCV_IOMMU_PQCSR_PQON |
21900c54acb8STomasz Jeznach         RISCV_IOMMU_PQCSR_BUSY);
21910c54acb8STomasz Jeznach     stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_IPSR], ~0);
21920c54acb8STomasz Jeznach     stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_ICVEC], 0);
21930c54acb8STomasz Jeznach     stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_DDTP], s->ddtp);
2194a7aa525bSTomasz Jeznach     /* If debug registers enabled. */
2195a7aa525bSTomasz Jeznach     if (s->cap & RISCV_IOMMU_CAP_DBG) {
2196a7aa525bSTomasz Jeznach         stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_TR_REQ_IOVA], 0);
2197a7aa525bSTomasz Jeznach         stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_TR_REQ_CTL],
2198a7aa525bSTomasz Jeznach             RISCV_IOMMU_TR_REQ_CTL_GO_BUSY);
2199a7aa525bSTomasz Jeznach     }
22000c54acb8STomasz Jeznach 
22010c54acb8STomasz Jeznach     /* Memory region for downstream access, if specified. */
22020c54acb8STomasz Jeznach     if (s->target_mr) {
22030c54acb8STomasz Jeznach         s->target_as = g_new0(AddressSpace, 1);
22040c54acb8STomasz Jeznach         address_space_init(s->target_as, s->target_mr,
22050c54acb8STomasz Jeznach             "riscv-iommu-downstream");
22060c54acb8STomasz Jeznach     } else {
22070c54acb8STomasz Jeznach         /* Fallback to global system memory. */
22080c54acb8STomasz Jeznach         s->target_as = &address_space_memory;
22090c54acb8STomasz Jeznach     }
22100c54acb8STomasz Jeznach 
22110c54acb8STomasz Jeznach     /* Memory region for untranslated MRIF/MSI writes */
22120c54acb8STomasz Jeznach     memory_region_init_io(&s->trap_mr, OBJECT(dev), &riscv_iommu_trap_ops, s,
22130c54acb8STomasz Jeznach             "riscv-iommu-trap", ~0ULL);
22140c54acb8STomasz Jeznach     address_space_init(&s->trap_as, &s->trap_mr, "riscv-iommu-trap-as");
22150c54acb8STomasz Jeznach 
22160c54acb8STomasz Jeznach     /* Device translation context cache */
22170c54acb8STomasz Jeznach     s->ctx_cache = g_hash_table_new_full(riscv_iommu_ctx_hash,
22180c54acb8STomasz Jeznach                                          riscv_iommu_ctx_equal,
22190c54acb8STomasz Jeznach                                          g_free, NULL);
22200c54acb8STomasz Jeznach 
22219d085a1cSTomasz Jeznach     s->iot_cache = g_hash_table_new_full(riscv_iommu_iot_hash,
22229d085a1cSTomasz Jeznach                                          riscv_iommu_iot_equal,
22239d085a1cSTomasz Jeznach                                          g_free, NULL);
22249d085a1cSTomasz Jeznach 
22250c54acb8STomasz Jeznach     s->iommus.le_next = NULL;
22260c54acb8STomasz Jeznach     s->iommus.le_prev = NULL;
22270c54acb8STomasz Jeznach     QLIST_INIT(&s->spaces);
22280c54acb8STomasz Jeznach }
22290c54acb8STomasz Jeznach 
22300c54acb8STomasz Jeznach static void riscv_iommu_unrealize(DeviceState *dev)
22310c54acb8STomasz Jeznach {
22320c54acb8STomasz Jeznach     RISCVIOMMUState *s = RISCV_IOMMU(dev);
22330c54acb8STomasz Jeznach 
22349d085a1cSTomasz Jeznach     g_hash_table_unref(s->iot_cache);
22350c54acb8STomasz Jeznach     g_hash_table_unref(s->ctx_cache);
22360c54acb8STomasz Jeznach }
22370c54acb8STomasz Jeznach 
22380c54acb8STomasz Jeznach static Property riscv_iommu_properties[] = {
22390c54acb8STomasz Jeznach     DEFINE_PROP_UINT32("version", RISCVIOMMUState, version,
22400c54acb8STomasz Jeznach         RISCV_IOMMU_SPEC_DOT_VER),
22410c54acb8STomasz Jeznach     DEFINE_PROP_UINT32("bus", RISCVIOMMUState, bus, 0x0),
22429d085a1cSTomasz Jeznach     DEFINE_PROP_UINT32("ioatc-limit", RISCVIOMMUState, iot_limit,
22439d085a1cSTomasz Jeznach         LIMIT_CACHE_IOT),
22440c54acb8STomasz Jeznach     DEFINE_PROP_BOOL("intremap", RISCVIOMMUState, enable_msi, TRUE),
224569a9ae48STomasz Jeznach     DEFINE_PROP_BOOL("ats", RISCVIOMMUState, enable_ats, TRUE),
22460c54acb8STomasz Jeznach     DEFINE_PROP_BOOL("off", RISCVIOMMUState, enable_off, TRUE),
22470c54acb8STomasz Jeznach     DEFINE_PROP_BOOL("s-stage", RISCVIOMMUState, enable_s_stage, TRUE),
22480c54acb8STomasz Jeznach     DEFINE_PROP_BOOL("g-stage", RISCVIOMMUState, enable_g_stage, TRUE),
22490c54acb8STomasz Jeznach     DEFINE_PROP_LINK("downstream-mr", RISCVIOMMUState, target_mr,
22500c54acb8STomasz Jeznach         TYPE_MEMORY_REGION, MemoryRegion *),
22510c54acb8STomasz Jeznach     DEFINE_PROP_END_OF_LIST(),
22520c54acb8STomasz Jeznach };
22530c54acb8STomasz Jeznach 
22540c54acb8STomasz Jeznach static void riscv_iommu_class_init(ObjectClass *klass, void* data)
22550c54acb8STomasz Jeznach {
22560c54acb8STomasz Jeznach     DeviceClass *dc = DEVICE_CLASS(klass);
22570c54acb8STomasz Jeznach 
22580c54acb8STomasz Jeznach     /* internal device for riscv-iommu-{pci/sys}, not user-creatable */
22590c54acb8STomasz Jeznach     dc->user_creatable = false;
22600c54acb8STomasz Jeznach     dc->realize = riscv_iommu_realize;
22610c54acb8STomasz Jeznach     dc->unrealize = riscv_iommu_unrealize;
22620c54acb8STomasz Jeznach     device_class_set_props(dc, riscv_iommu_properties);
22630c54acb8STomasz Jeznach }
22640c54acb8STomasz Jeznach 
22650c54acb8STomasz Jeznach static const TypeInfo riscv_iommu_info = {
22660c54acb8STomasz Jeznach     .name = TYPE_RISCV_IOMMU,
22670c54acb8STomasz Jeznach     .parent = TYPE_DEVICE,
22680c54acb8STomasz Jeznach     .instance_size = sizeof(RISCVIOMMUState),
22690c54acb8STomasz Jeznach     .class_init = riscv_iommu_class_init,
22700c54acb8STomasz Jeznach };
22710c54acb8STomasz Jeznach 
22720c54acb8STomasz Jeznach static const char *IOMMU_FLAG_STR[] = {
22730c54acb8STomasz Jeznach     "NA",
22740c54acb8STomasz Jeznach     "RO",
22750c54acb8STomasz Jeznach     "WR",
22760c54acb8STomasz Jeznach     "RW",
22770c54acb8STomasz Jeznach };
22780c54acb8STomasz Jeznach 
22790c54acb8STomasz Jeznach /* RISC-V IOMMU Memory Region - Address Translation Space */
22800c54acb8STomasz Jeznach static IOMMUTLBEntry riscv_iommu_memory_region_translate(
22810c54acb8STomasz Jeznach     IOMMUMemoryRegion *iommu_mr, hwaddr addr,
22820c54acb8STomasz Jeznach     IOMMUAccessFlags flag, int iommu_idx)
22830c54acb8STomasz Jeznach {
22840c54acb8STomasz Jeznach     RISCVIOMMUSpace *as = container_of(iommu_mr, RISCVIOMMUSpace, iova_mr);
22850c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx;
22860c54acb8STomasz Jeznach     void *ref;
22870c54acb8STomasz Jeznach     IOMMUTLBEntry iotlb = {
22880c54acb8STomasz Jeznach         .iova = addr,
22890c54acb8STomasz Jeznach         .target_as = as->iommu->target_as,
22900c54acb8STomasz Jeznach         .addr_mask = ~0ULL,
22910c54acb8STomasz Jeznach         .perm = flag,
22920c54acb8STomasz Jeznach     };
22930c54acb8STomasz Jeznach 
22940c54acb8STomasz Jeznach     ctx = riscv_iommu_ctx(as->iommu, as->devid, iommu_idx, &ref);
22950c54acb8STomasz Jeznach     if (ctx == NULL) {
22960c54acb8STomasz Jeznach         /* Translation disabled or invalid. */
22970c54acb8STomasz Jeznach         iotlb.addr_mask = 0;
22980c54acb8STomasz Jeznach         iotlb.perm = IOMMU_NONE;
22999d085a1cSTomasz Jeznach     } else if (riscv_iommu_translate(as->iommu, ctx, &iotlb, true)) {
23000c54acb8STomasz Jeznach         /* Translation disabled or fault reported. */
23010c54acb8STomasz Jeznach         iotlb.addr_mask = 0;
23020c54acb8STomasz Jeznach         iotlb.perm = IOMMU_NONE;
23030c54acb8STomasz Jeznach     }
23040c54acb8STomasz Jeznach 
23050c54acb8STomasz Jeznach     /* Trace all dma translations with original access flags. */
23060c54acb8STomasz Jeznach     trace_riscv_iommu_dma(as->iommu->parent_obj.id, PCI_BUS_NUM(as->devid),
23070c54acb8STomasz Jeznach                           PCI_SLOT(as->devid), PCI_FUNC(as->devid), iommu_idx,
23080c54acb8STomasz Jeznach                           IOMMU_FLAG_STR[flag & IOMMU_RW], iotlb.iova,
23090c54acb8STomasz Jeznach                           iotlb.translated_addr);
23100c54acb8STomasz Jeznach 
23110c54acb8STomasz Jeznach     riscv_iommu_ctx_put(as->iommu, ref);
23120c54acb8STomasz Jeznach 
23130c54acb8STomasz Jeznach     return iotlb;
23140c54acb8STomasz Jeznach }
23150c54acb8STomasz Jeznach 
23160c54acb8STomasz Jeznach static int riscv_iommu_memory_region_notify(
23170c54acb8STomasz Jeznach     IOMMUMemoryRegion *iommu_mr, IOMMUNotifierFlag old,
23180c54acb8STomasz Jeznach     IOMMUNotifierFlag new, Error **errp)
23190c54acb8STomasz Jeznach {
23200c54acb8STomasz Jeznach     RISCVIOMMUSpace *as = container_of(iommu_mr, RISCVIOMMUSpace, iova_mr);
23210c54acb8STomasz Jeznach 
23220c54acb8STomasz Jeznach     if (old == IOMMU_NOTIFIER_NONE) {
23230c54acb8STomasz Jeznach         as->notifier = true;
23240c54acb8STomasz Jeznach         trace_riscv_iommu_notifier_add(iommu_mr->parent_obj.name);
23250c54acb8STomasz Jeznach     } else if (new == IOMMU_NOTIFIER_NONE) {
23260c54acb8STomasz Jeznach         as->notifier = false;
23270c54acb8STomasz Jeznach         trace_riscv_iommu_notifier_del(iommu_mr->parent_obj.name);
23280c54acb8STomasz Jeznach     }
23290c54acb8STomasz Jeznach 
23300c54acb8STomasz Jeznach     return 0;
23310c54acb8STomasz Jeznach }
23320c54acb8STomasz Jeznach 
23330c54acb8STomasz Jeznach static inline bool pci_is_iommu(PCIDevice *pdev)
23340c54acb8STomasz Jeznach {
23350c54acb8STomasz Jeznach     return pci_get_word(pdev->config + PCI_CLASS_DEVICE) == 0x0806;
23360c54acb8STomasz Jeznach }
23370c54acb8STomasz Jeznach 
23380c54acb8STomasz Jeznach static AddressSpace *riscv_iommu_find_as(PCIBus *bus, void *opaque, int devfn)
23390c54acb8STomasz Jeznach {
23400c54acb8STomasz Jeznach     RISCVIOMMUState *s = (RISCVIOMMUState *) opaque;
23410c54acb8STomasz Jeznach     PCIDevice *pdev = pci_find_device(bus, pci_bus_num(bus), devfn);
23420c54acb8STomasz Jeznach     AddressSpace *as = NULL;
23430c54acb8STomasz Jeznach 
23440c54acb8STomasz Jeznach     if (pdev && pci_is_iommu(pdev)) {
23450c54acb8STomasz Jeznach         return s->target_as;
23460c54acb8STomasz Jeznach     }
23470c54acb8STomasz Jeznach 
23480c54acb8STomasz Jeznach     /* Find first registered IOMMU device */
23490c54acb8STomasz Jeznach     while (s->iommus.le_prev) {
23500c54acb8STomasz Jeznach         s = *(s->iommus.le_prev);
23510c54acb8STomasz Jeznach     }
23520c54acb8STomasz Jeznach 
23530c54acb8STomasz Jeznach     /* Find first matching IOMMU */
23540c54acb8STomasz Jeznach     while (s != NULL && as == NULL) {
23550c54acb8STomasz Jeznach         as = riscv_iommu_space(s, PCI_BUILD_BDF(pci_bus_num(bus), devfn));
23560c54acb8STomasz Jeznach         s = s->iommus.le_next;
23570c54acb8STomasz Jeznach     }
23580c54acb8STomasz Jeznach 
23590c54acb8STomasz Jeznach     return as ? as : &address_space_memory;
23600c54acb8STomasz Jeznach }
23610c54acb8STomasz Jeznach 
23620c54acb8STomasz Jeznach static const PCIIOMMUOps riscv_iommu_ops = {
23630c54acb8STomasz Jeznach     .get_address_space = riscv_iommu_find_as,
23640c54acb8STomasz Jeznach };
23650c54acb8STomasz Jeznach 
23660c54acb8STomasz Jeznach void riscv_iommu_pci_setup_iommu(RISCVIOMMUState *iommu, PCIBus *bus,
23670c54acb8STomasz Jeznach         Error **errp)
23680c54acb8STomasz Jeznach {
23690c54acb8STomasz Jeznach     if (bus->iommu_ops &&
23700c54acb8STomasz Jeznach         bus->iommu_ops->get_address_space == riscv_iommu_find_as) {
23710c54acb8STomasz Jeznach         /* Allow multiple IOMMUs on the same PCIe bus, link known devices */
23720c54acb8STomasz Jeznach         RISCVIOMMUState *last = (RISCVIOMMUState *)bus->iommu_opaque;
23730c54acb8STomasz Jeznach         QLIST_INSERT_AFTER(last, iommu, iommus);
23740c54acb8STomasz Jeznach     } else if (!bus->iommu_ops && !bus->iommu_opaque) {
23750c54acb8STomasz Jeznach         pci_setup_iommu(bus, &riscv_iommu_ops, iommu);
23760c54acb8STomasz Jeznach     } else {
23770c54acb8STomasz Jeznach         error_setg(errp, "can't register secondary IOMMU for PCI bus #%d",
23780c54acb8STomasz Jeznach             pci_bus_num(bus));
23790c54acb8STomasz Jeznach     }
23800c54acb8STomasz Jeznach }
23810c54acb8STomasz Jeznach 
23820c54acb8STomasz Jeznach static int riscv_iommu_memory_region_index(IOMMUMemoryRegion *iommu_mr,
23830c54acb8STomasz Jeznach     MemTxAttrs attrs)
23840c54acb8STomasz Jeznach {
23850c54acb8STomasz Jeznach     return attrs.unspecified ? RISCV_IOMMU_NOPROCID : (int)attrs.pid;
23860c54acb8STomasz Jeznach }
23870c54acb8STomasz Jeznach 
23880c54acb8STomasz Jeznach static int riscv_iommu_memory_region_index_len(IOMMUMemoryRegion *iommu_mr)
23890c54acb8STomasz Jeznach {
23900c54acb8STomasz Jeznach     RISCVIOMMUSpace *as = container_of(iommu_mr, RISCVIOMMUSpace, iova_mr);
23910c54acb8STomasz Jeznach     return 1 << as->iommu->pid_bits;
23920c54acb8STomasz Jeznach }
23930c54acb8STomasz Jeznach 
23940c54acb8STomasz Jeznach static void riscv_iommu_memory_region_init(ObjectClass *klass, void *data)
23950c54acb8STomasz Jeznach {
23960c54acb8STomasz Jeznach     IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass);
23970c54acb8STomasz Jeznach 
23980c54acb8STomasz Jeznach     imrc->translate = riscv_iommu_memory_region_translate;
23990c54acb8STomasz Jeznach     imrc->notify_flag_changed = riscv_iommu_memory_region_notify;
24000c54acb8STomasz Jeznach     imrc->attrs_to_index = riscv_iommu_memory_region_index;
24010c54acb8STomasz Jeznach     imrc->num_indexes = riscv_iommu_memory_region_index_len;
24020c54acb8STomasz Jeznach }
24030c54acb8STomasz Jeznach 
24040c54acb8STomasz Jeznach static const TypeInfo riscv_iommu_memory_region_info = {
24050c54acb8STomasz Jeznach     .parent = TYPE_IOMMU_MEMORY_REGION,
24060c54acb8STomasz Jeznach     .name = TYPE_RISCV_IOMMU_MEMORY_REGION,
24070c54acb8STomasz Jeznach     .class_init = riscv_iommu_memory_region_init,
24080c54acb8STomasz Jeznach };
24090c54acb8STomasz Jeznach 
24100c54acb8STomasz Jeznach static void riscv_iommu_register_mr_types(void)
24110c54acb8STomasz Jeznach {
24120c54acb8STomasz Jeznach     type_register_static(&riscv_iommu_memory_region_info);
24130c54acb8STomasz Jeznach     type_register_static(&riscv_iommu_info);
24140c54acb8STomasz Jeznach }
24150c54acb8STomasz Jeznach 
24160c54acb8STomasz Jeznach type_init(riscv_iommu_register_mr_types);
2417