xref: /qemu/hw/riscv/riscv-iommu.c (revision 4876e6f7b51cf7dcbfaa43ae323e51ce9ebfcf79)
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 
186d37eede7SPierrick Bouvier /*
187d37eede7SPierrick Bouvier  * Discards all bits from 'val' whose matching bits in the same
188d37eede7SPierrick Bouvier  * positions in the mask 'ext' are zeros, and packs the remaining
189d37eede7SPierrick Bouvier  * bits from 'val' contiguously at the least-significant end of the
190d37eede7SPierrick Bouvier  * result, keeping the same bit order as 'val' and filling any
191d37eede7SPierrick Bouvier  * other bits at the most-significant end of the result with zeros.
192d37eede7SPierrick Bouvier  *
193d37eede7SPierrick Bouvier  * For example, for the following 'val' and 'ext', the return 'ret'
194d37eede7SPierrick Bouvier  * will be:
195d37eede7SPierrick Bouvier  *
196d37eede7SPierrick Bouvier  * val = a b c d e f g h
197d37eede7SPierrick Bouvier  * ext = 1 0 1 0 0 1 1 0
198d37eede7SPierrick Bouvier  * ret = 0 0 0 0 a c f g
199d37eede7SPierrick Bouvier  *
200d37eede7SPierrick Bouvier  * This function, taken from the riscv-iommu 1.0 spec, section 2.3.3
201d37eede7SPierrick Bouvier  * "Process to translate addresses of MSIs", is similar to bit manip
202d37eede7SPierrick Bouvier  * function PEXT (Parallel bits extract) from x86.
203d37eede7SPierrick Bouvier  */
204d37eede7SPierrick 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) {
395e5d28bf2SJason Chien             const uint64_t va_len = va_skip + va_bits;
396e5d28bf2SJason Chien             const uint64_t va_mask = (1ULL << va_len) - 1;
397e5d28bf2SJason Chien 
398e5d28bf2SJason Chien             if (pass == S_STAGE && va_len > 32) {
399e5d28bf2SJason Chien                 target_ulong mask, masked_msbs;
400e5d28bf2SJason Chien 
401e5d28bf2SJason Chien                 mask = (1L << (TARGET_LONG_BITS - (va_len - 1))) - 1;
402e5d28bf2SJason Chien                 masked_msbs = (addr >> (va_len - 1)) & mask;
403e5d28bf2SJason Chien 
404e5d28bf2SJason Chien                 if (masked_msbs != 0 && masked_msbs != mask) {
405e5d28bf2SJason Chien                     return (iotlb->perm & IOMMU_WO) ?
406e5d28bf2SJason Chien                                 RISCV_IOMMU_FQ_CAUSE_WR_FAULT_S :
407e5d28bf2SJason Chien                                 RISCV_IOMMU_FQ_CAUSE_RD_FAULT_S;
408e5d28bf2SJason Chien                 }
409e5d28bf2SJason Chien             } else {
4100c54acb8STomasz Jeznach                 if ((addr & va_mask) != addr) {
411e5d28bf2SJason Chien                     return (iotlb->perm & IOMMU_WO) ?
412e5d28bf2SJason Chien                                 RISCV_IOMMU_FQ_CAUSE_WR_FAULT_VS :
413e5d28bf2SJason Chien                                 RISCV_IOMMU_FQ_CAUSE_RD_FAULT_VS;
414e5d28bf2SJason Chien                 }
4150c54acb8STomasz Jeznach             }
4160c54acb8STomasz Jeznach         }
4170c54acb8STomasz Jeznach 
4180c54acb8STomasz Jeznach         /* Read page table entry */
4190c54acb8STomasz Jeznach         if (sc[pass].ptesize == 4) {
4200c54acb8STomasz Jeznach             uint32_t pte32 = 0;
4210c54acb8STomasz Jeznach             ret = ldl_le_dma(s->target_as, pte_addr, &pte32,
4220c54acb8STomasz Jeznach                              MEMTXATTRS_UNSPECIFIED);
4230c54acb8STomasz Jeznach             pte = pte32;
4240c54acb8STomasz Jeznach         } else {
4250c54acb8STomasz Jeznach             ret = ldq_le_dma(s->target_as, pte_addr, &pte,
4260c54acb8STomasz Jeznach                              MEMTXATTRS_UNSPECIFIED);
4270c54acb8STomasz Jeznach         }
4280c54acb8STomasz Jeznach         if (ret != MEMTX_OK) {
4290c54acb8STomasz Jeznach             return (iotlb->perm & IOMMU_WO) ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT
4300c54acb8STomasz Jeznach                                             : RISCV_IOMMU_FQ_CAUSE_RD_FAULT;
4310c54acb8STomasz Jeznach         }
4320c54acb8STomasz Jeznach 
4330c54acb8STomasz Jeznach         sc[pass].step++;
4340c54acb8STomasz Jeznach         hwaddr ppn = pte >> PTE_PPN_SHIFT;
4350c54acb8STomasz Jeznach 
4360c54acb8STomasz Jeznach         if (!(pte & PTE_V)) {
4370c54acb8STomasz Jeznach             break;                /* Invalid PTE */
4380c54acb8STomasz Jeznach         } else if (!(pte & (PTE_R | PTE_W | PTE_X))) {
4390c54acb8STomasz Jeznach             base = PPN_PHYS(ppn); /* Inner PTE, continue walking */
4400c54acb8STomasz Jeznach         } else if ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) {
4410c54acb8STomasz Jeznach             break;                /* Reserved leaf PTE flags: PTE_W */
4420c54acb8STomasz Jeznach         } else if ((pte & (PTE_R | PTE_W | PTE_X)) == (PTE_W | PTE_X)) {
4430c54acb8STomasz Jeznach             break;                /* Reserved leaf PTE flags: PTE_W + PTE_X */
4440c54acb8STomasz Jeznach         } else if (ppn & ((1ULL << (va_skip - TARGET_PAGE_BITS)) - 1)) {
4450c54acb8STomasz Jeznach             break;                /* Misaligned PPN */
4460c54acb8STomasz Jeznach         } else if ((iotlb->perm & IOMMU_RO) && !(pte & PTE_R)) {
4470c54acb8STomasz Jeznach             break;                /* Read access check failed */
4480c54acb8STomasz Jeznach         } else if ((iotlb->perm & IOMMU_WO) && !(pte & PTE_W)) {
4490c54acb8STomasz Jeznach             break;                /* Write access check failed */
4500c54acb8STomasz Jeznach         } else if ((iotlb->perm & IOMMU_RO) && !ade && !(pte & PTE_A)) {
4510c54acb8STomasz Jeznach             break;                /* Access bit not set */
4520c54acb8STomasz Jeznach         } else if ((iotlb->perm & IOMMU_WO) && !ade && !(pte & PTE_D)) {
4530c54acb8STomasz Jeznach             break;                /* Dirty bit not set */
4540c54acb8STomasz Jeznach         } else {
4550c54acb8STomasz Jeznach             /* Leaf PTE, translation completed. */
4560c54acb8STomasz Jeznach             sc[pass].step = sc[pass].levels;
4570c54acb8STomasz Jeznach             base = PPN_PHYS(ppn) | (addr & ((1ULL << va_skip) - 1));
4580c54acb8STomasz Jeznach             /* Update address mask based on smallest translation granularity */
4590c54acb8STomasz Jeznach             iotlb->addr_mask &= (1ULL << va_skip) - 1;
4600c54acb8STomasz Jeznach             /* Continue with S-Stage translation? */
4610c54acb8STomasz Jeznach             if (pass && sc[0].step != sc[0].levels) {
4620c54acb8STomasz Jeznach                 pass = S_STAGE;
4630c54acb8STomasz Jeznach                 addr = iotlb->iova;
4640c54acb8STomasz Jeznach                 continue;
4650c54acb8STomasz Jeznach             }
4660c54acb8STomasz Jeznach             /* Translation phase completed (GPA or SPA) */
4670c54acb8STomasz Jeznach             iotlb->translated_addr = base;
4680c54acb8STomasz Jeznach             iotlb->perm = (pte & PTE_W) ? ((pte & PTE_R) ? IOMMU_RW : IOMMU_WO)
4690c54acb8STomasz Jeznach                                                          : IOMMU_RO;
4700c54acb8STomasz Jeznach 
4710c54acb8STomasz Jeznach             /* Check MSI GPA address match */
4720c54acb8STomasz Jeznach             if (pass == S_STAGE && (iotlb->perm & IOMMU_WO) &&
4730c54acb8STomasz Jeznach                 riscv_iommu_msi_check(s, ctx, base)) {
4740c54acb8STomasz Jeznach                 /* Trap MSI writes and return GPA address. */
4750c54acb8STomasz Jeznach                 iotlb->target_as = &s->trap_as;
4760c54acb8STomasz Jeznach                 iotlb->addr_mask = ~TARGET_PAGE_MASK;
4770c54acb8STomasz Jeznach                 return 0;
4780c54acb8STomasz Jeznach             }
4790c54acb8STomasz Jeznach 
4800c54acb8STomasz Jeznach             /* Continue with G-Stage translation? */
4810c54acb8STomasz Jeznach             if (!pass && en_g) {
4820c54acb8STomasz Jeznach                 pass = G_STAGE;
4830c54acb8STomasz Jeznach                 addr = base;
4840c54acb8STomasz Jeznach                 base = gatp;
4850c54acb8STomasz Jeznach                 sc[pass].step = 0;
4860c54acb8STomasz Jeznach                 continue;
4870c54acb8STomasz Jeznach             }
4880c54acb8STomasz Jeznach 
4890c54acb8STomasz Jeznach             return 0;
4900c54acb8STomasz Jeznach         }
4910c54acb8STomasz Jeznach 
4920c54acb8STomasz Jeznach         if (sc[pass].step == sc[pass].levels) {
4930c54acb8STomasz Jeznach             break; /* Can't find leaf PTE */
4940c54acb8STomasz Jeznach         }
4950c54acb8STomasz Jeznach 
4960c54acb8STomasz Jeznach         /* Continue with G-Stage translation? */
4970c54acb8STomasz Jeznach         if (!pass && en_g) {
4980c54acb8STomasz Jeznach             pass = G_STAGE;
4990c54acb8STomasz Jeznach             addr = base;
5000c54acb8STomasz Jeznach             base = gatp;
5010c54acb8STomasz Jeznach             sc[pass].step = 0;
5020c54acb8STomasz Jeznach         }
5030c54acb8STomasz Jeznach     } while (1);
5040c54acb8STomasz Jeznach 
5050c54acb8STomasz Jeznach     return (iotlb->perm & IOMMU_WO) ?
5060c54acb8STomasz Jeznach                 (pass ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT_VS :
5070c54acb8STomasz Jeznach                         RISCV_IOMMU_FQ_CAUSE_WR_FAULT_S) :
5080c54acb8STomasz Jeznach                 (pass ? RISCV_IOMMU_FQ_CAUSE_RD_FAULT_VS :
5090c54acb8STomasz Jeznach                         RISCV_IOMMU_FQ_CAUSE_RD_FAULT_S);
5100c54acb8STomasz Jeznach }
5110c54acb8STomasz Jeznach 
5120c54acb8STomasz Jeznach static void riscv_iommu_report_fault(RISCVIOMMUState *s,
5130c54acb8STomasz Jeznach                                      RISCVIOMMUContext *ctx,
5140c54acb8STomasz Jeznach                                      uint32_t fault_type, uint32_t cause,
5150c54acb8STomasz Jeznach                                      bool pv,
5160c54acb8STomasz Jeznach                                      uint64_t iotval, uint64_t iotval2)
5170c54acb8STomasz Jeznach {
5180c54acb8STomasz Jeznach     struct riscv_iommu_fq_record ev = { 0 };
5190c54acb8STomasz Jeznach 
5200c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_DTF) {
5210c54acb8STomasz Jeznach         switch (cause) {
5220c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_DMA_DISABLED:
5230c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_DDT_LOAD_FAULT:
5240c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_DDT_INVALID:
5250c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED:
5260c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_DDT_CORRUPTED:
5270c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_INTERNAL_DP_ERROR:
5280c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT:
5290c54acb8STomasz Jeznach             break;
5300c54acb8STomasz Jeznach         default:
5310c54acb8STomasz Jeznach             /* DTF prevents reporting a fault for this given cause */
5320c54acb8STomasz Jeznach             return;
5330c54acb8STomasz Jeznach         }
5340c54acb8STomasz Jeznach     }
5350c54acb8STomasz Jeznach 
5360c54acb8STomasz Jeznach     ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_CAUSE, cause);
5370c54acb8STomasz Jeznach     ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_TTYPE, fault_type);
5380c54acb8STomasz Jeznach     ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_DID, ctx->devid);
5390c54acb8STomasz Jeznach     ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_PV, true);
5400c54acb8STomasz Jeznach 
5410c54acb8STomasz Jeznach     if (pv) {
5420c54acb8STomasz Jeznach         ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_PID, ctx->process_id);
5430c54acb8STomasz Jeznach     }
5440c54acb8STomasz Jeznach 
5450c54acb8STomasz Jeznach     ev.iotval = iotval;
5460c54acb8STomasz Jeznach     ev.iotval2 = iotval2;
5470c54acb8STomasz Jeznach 
5480c54acb8STomasz Jeznach     riscv_iommu_fault(s, &ev);
5490c54acb8STomasz Jeznach }
5500c54acb8STomasz Jeznach 
5510c54acb8STomasz Jeznach /* Redirect MSI write for given GPA. */
5520c54acb8STomasz Jeznach static MemTxResult riscv_iommu_msi_write(RISCVIOMMUState *s,
5530c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx, uint64_t gpa, uint64_t data,
5540c54acb8STomasz Jeznach     unsigned size, MemTxAttrs attrs)
5550c54acb8STomasz Jeznach {
5560c54acb8STomasz Jeznach     MemTxResult res;
5570c54acb8STomasz Jeznach     dma_addr_t addr;
5580c54acb8STomasz Jeznach     uint64_t intn;
5590c54acb8STomasz Jeznach     uint32_t n190;
5600c54acb8STomasz Jeznach     uint64_t pte[2];
5610c54acb8STomasz Jeznach     int fault_type = RISCV_IOMMU_FQ_TTYPE_UADDR_WR;
5620c54acb8STomasz Jeznach     int cause;
5630c54acb8STomasz Jeznach 
5640c54acb8STomasz Jeznach     /* Interrupt File Number */
565d37eede7SPierrick Bouvier     intn = riscv_iommu_pext_u64(PPN_DOWN(gpa), ctx->msi_addr_mask);
5660c54acb8STomasz Jeznach     if (intn >= 256) {
5670c54acb8STomasz Jeznach         /* Interrupt file number out of range */
5680c54acb8STomasz Jeznach         res = MEMTX_ACCESS_ERROR;
5690c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT;
5700c54acb8STomasz Jeznach         goto err;
5710c54acb8STomasz Jeznach     }
5720c54acb8STomasz Jeznach 
5730c54acb8STomasz Jeznach     /* fetch MSI PTE */
5740c54acb8STomasz Jeznach     addr = PPN_PHYS(get_field(ctx->msiptp, RISCV_IOMMU_DC_MSIPTP_PPN));
5750c54acb8STomasz Jeznach     addr = addr | (intn * sizeof(pte));
5760c54acb8STomasz Jeznach     res = dma_memory_read(s->target_as, addr, &pte, sizeof(pte),
5770c54acb8STomasz Jeznach             MEMTXATTRS_UNSPECIFIED);
5780c54acb8STomasz Jeznach     if (res != MEMTX_OK) {
5790c54acb8STomasz Jeznach         if (res == MEMTX_DECODE_ERROR) {
5800c54acb8STomasz Jeznach             cause = RISCV_IOMMU_FQ_CAUSE_MSI_PT_CORRUPTED;
5810c54acb8STomasz Jeznach         } else {
5820c54acb8STomasz Jeznach             cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT;
5830c54acb8STomasz Jeznach         }
5840c54acb8STomasz Jeznach         goto err;
5850c54acb8STomasz Jeznach     }
5860c54acb8STomasz Jeznach 
5870c54acb8STomasz Jeznach     le64_to_cpus(&pte[0]);
5880c54acb8STomasz Jeznach     le64_to_cpus(&pte[1]);
5890c54acb8STomasz Jeznach 
5900c54acb8STomasz Jeznach     if (!(pte[0] & RISCV_IOMMU_MSI_PTE_V) || (pte[0] & RISCV_IOMMU_MSI_PTE_C)) {
5910c54acb8STomasz Jeznach         /*
5920c54acb8STomasz Jeznach          * The spec mentions that: "If msipte.C == 1, then further
5930c54acb8STomasz Jeznach          * processing to interpret the PTE is implementation
5940c54acb8STomasz Jeznach          * defined.". We'll abort with cause = 262 for this
5950c54acb8STomasz Jeznach          * case too.
5960c54acb8STomasz Jeznach          */
5970c54acb8STomasz Jeznach         res = MEMTX_ACCESS_ERROR;
5980c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_INVALID;
5990c54acb8STomasz Jeznach         goto err;
6000c54acb8STomasz Jeznach     }
6010c54acb8STomasz Jeznach 
6020c54acb8STomasz Jeznach     switch (get_field(pte[0], RISCV_IOMMU_MSI_PTE_M)) {
6030c54acb8STomasz Jeznach     case RISCV_IOMMU_MSI_PTE_M_BASIC:
6040c54acb8STomasz Jeznach         /* MSI Pass-through mode */
6050c54acb8STomasz Jeznach         addr = PPN_PHYS(get_field(pte[0], RISCV_IOMMU_MSI_PTE_PPN));
6060c54acb8STomasz Jeznach 
6070c54acb8STomasz Jeznach         trace_riscv_iommu_msi(s->parent_obj.id, PCI_BUS_NUM(ctx->devid),
6080c54acb8STomasz Jeznach                               PCI_SLOT(ctx->devid), PCI_FUNC(ctx->devid),
6090c54acb8STomasz Jeznach                               gpa, addr);
6100c54acb8STomasz Jeznach 
6110c54acb8STomasz Jeznach         res = dma_memory_write(s->target_as, addr, &data, size, attrs);
6120c54acb8STomasz Jeznach         if (res != MEMTX_OK) {
6130c54acb8STomasz Jeznach             cause = RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT;
6140c54acb8STomasz Jeznach             goto err;
6150c54acb8STomasz Jeznach         }
6160c54acb8STomasz Jeznach 
6170c54acb8STomasz Jeznach         return MEMTX_OK;
6180c54acb8STomasz Jeznach     case RISCV_IOMMU_MSI_PTE_M_MRIF:
6190c54acb8STomasz Jeznach         /* MRIF mode, continue. */
6200c54acb8STomasz Jeznach         break;
6210c54acb8STomasz Jeznach     default:
6220c54acb8STomasz Jeznach         res = MEMTX_ACCESS_ERROR;
6230c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_MISCONFIGURED;
6240c54acb8STomasz Jeznach         goto err;
6250c54acb8STomasz Jeznach     }
6260c54acb8STomasz Jeznach 
6270c54acb8STomasz Jeznach     /*
6280c54acb8STomasz Jeznach      * Report an error for interrupt identities exceeding the maximum allowed
6290c54acb8STomasz Jeznach      * for an IMSIC interrupt file (2047) or destination address is not 32-bit
6300c54acb8STomasz Jeznach      * aligned. See IOMMU Specification, Chapter 2.3. MSI page tables.
6310c54acb8STomasz Jeznach      */
6320c54acb8STomasz Jeznach     if ((data > 2047) || (gpa & 3)) {
6330c54acb8STomasz Jeznach         res = MEMTX_ACCESS_ERROR;
6340c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_MISCONFIGURED;
6350c54acb8STomasz Jeznach         goto err;
6360c54acb8STomasz Jeznach     }
6370c54acb8STomasz Jeznach 
6380c54acb8STomasz Jeznach     /* MSI MRIF mode, non atomic pending bit update */
6390c54acb8STomasz Jeznach 
6400c54acb8STomasz Jeznach     /* MRIF pending bit address */
6410c54acb8STomasz Jeznach     addr = get_field(pte[0], RISCV_IOMMU_MSI_PTE_MRIF_ADDR) << 9;
6420c54acb8STomasz Jeznach     addr = addr | ((data & 0x7c0) >> 3);
6430c54acb8STomasz Jeznach 
6440c54acb8STomasz Jeznach     trace_riscv_iommu_msi(s->parent_obj.id, PCI_BUS_NUM(ctx->devid),
6450c54acb8STomasz Jeznach                           PCI_SLOT(ctx->devid), PCI_FUNC(ctx->devid),
6460c54acb8STomasz Jeznach                           gpa, addr);
6470c54acb8STomasz Jeznach 
6480c54acb8STomasz Jeznach     /* MRIF pending bit mask */
6490c54acb8STomasz Jeznach     data = 1ULL << (data & 0x03f);
6500c54acb8STomasz Jeznach     res = dma_memory_read(s->target_as, addr, &intn, sizeof(intn), attrs);
6510c54acb8STomasz Jeznach     if (res != MEMTX_OK) {
6520c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT;
6530c54acb8STomasz Jeznach         goto err;
6540c54acb8STomasz Jeznach     }
6550c54acb8STomasz Jeznach 
6560c54acb8STomasz Jeznach     intn = intn | data;
6570c54acb8STomasz Jeznach     res = dma_memory_write(s->target_as, addr, &intn, sizeof(intn), attrs);
6580c54acb8STomasz Jeznach     if (res != MEMTX_OK) {
6590c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT;
6600c54acb8STomasz Jeznach         goto err;
6610c54acb8STomasz Jeznach     }
6620c54acb8STomasz Jeznach 
6630c54acb8STomasz Jeznach     /* Get MRIF enable bits */
6640c54acb8STomasz Jeznach     addr = addr + sizeof(intn);
6650c54acb8STomasz Jeznach     res = dma_memory_read(s->target_as, addr, &intn, sizeof(intn), attrs);
6660c54acb8STomasz Jeznach     if (res != MEMTX_OK) {
6670c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT;
6680c54acb8STomasz Jeznach         goto err;
6690c54acb8STomasz Jeznach     }
6700c54acb8STomasz Jeznach 
6710c54acb8STomasz Jeznach     if (!(intn & data)) {
6720c54acb8STomasz Jeznach         /* notification disabled, MRIF update completed. */
6730c54acb8STomasz Jeznach         return MEMTX_OK;
6740c54acb8STomasz Jeznach     }
6750c54acb8STomasz Jeznach 
6760c54acb8STomasz Jeznach     /* Send notification message */
6770c54acb8STomasz Jeznach     addr = PPN_PHYS(get_field(pte[1], RISCV_IOMMU_MSI_MRIF_NPPN));
6780c54acb8STomasz Jeznach     n190 = get_field(pte[1], RISCV_IOMMU_MSI_MRIF_NID) |
6790c54acb8STomasz Jeznach           (get_field(pte[1], RISCV_IOMMU_MSI_MRIF_NID_MSB) << 10);
6800c54acb8STomasz Jeznach 
6810c54acb8STomasz Jeznach     res = dma_memory_write(s->target_as, addr, &n190, sizeof(n190), attrs);
6820c54acb8STomasz Jeznach     if (res != MEMTX_OK) {
6830c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT;
6840c54acb8STomasz Jeznach         goto err;
6850c54acb8STomasz Jeznach     }
6860c54acb8STomasz Jeznach 
6870c54acb8STomasz Jeznach     trace_riscv_iommu_mrif_notification(s->parent_obj.id, n190, addr);
6880c54acb8STomasz Jeznach 
6890c54acb8STomasz Jeznach     return MEMTX_OK;
6900c54acb8STomasz Jeznach 
6910c54acb8STomasz Jeznach err:
6920c54acb8STomasz Jeznach     riscv_iommu_report_fault(s, ctx, fault_type, cause,
6930c54acb8STomasz Jeznach                              !!ctx->process_id, 0, 0);
6940c54acb8STomasz Jeznach     return res;
6950c54acb8STomasz Jeznach }
6960c54acb8STomasz Jeznach 
6970c54acb8STomasz Jeznach /*
6980c54acb8STomasz Jeznach  * Check device context configuration as described by the
6990c54acb8STomasz Jeznach  * riscv-iommu spec section "Device-context configuration
7000c54acb8STomasz Jeznach  * checks".
7010c54acb8STomasz Jeznach  */
7020c54acb8STomasz Jeznach static bool riscv_iommu_validate_device_ctx(RISCVIOMMUState *s,
7030c54acb8STomasz Jeznach                                             RISCVIOMMUContext *ctx)
7040c54acb8STomasz Jeznach {
7050c54acb8STomasz Jeznach     uint32_t fsc_mode, msi_mode;
70669a9ae48STomasz Jeznach     uint64_t gatp;
70769a9ae48STomasz Jeznach 
70869a9ae48STomasz Jeznach     if (!(s->cap & RISCV_IOMMU_CAP_ATS) &&
70969a9ae48STomasz Jeznach         (ctx->tc & RISCV_IOMMU_DC_TC_EN_ATS ||
71069a9ae48STomasz Jeznach          ctx->tc & RISCV_IOMMU_DC_TC_EN_PRI ||
71169a9ae48STomasz Jeznach          ctx->tc & RISCV_IOMMU_DC_TC_PRPR)) {
71269a9ae48STomasz Jeznach         return false;
71369a9ae48STomasz Jeznach     }
71469a9ae48STomasz Jeznach 
71569a9ae48STomasz Jeznach     if (!(ctx->tc & RISCV_IOMMU_DC_TC_EN_ATS) &&
71669a9ae48STomasz Jeznach         (ctx->tc & RISCV_IOMMU_DC_TC_T2GPA ||
71769a9ae48STomasz Jeznach          ctx->tc & RISCV_IOMMU_DC_TC_EN_PRI)) {
71869a9ae48STomasz Jeznach         return false;
71969a9ae48STomasz Jeznach     }
7200c54acb8STomasz Jeznach 
7210c54acb8STomasz Jeznach     if (!(ctx->tc & RISCV_IOMMU_DC_TC_EN_PRI) &&
7220c54acb8STomasz Jeznach         ctx->tc & RISCV_IOMMU_DC_TC_PRPR) {
7230c54acb8STomasz Jeznach         return false;
7240c54acb8STomasz Jeznach     }
7250c54acb8STomasz Jeznach 
7260c54acb8STomasz Jeznach     if (!(s->cap & RISCV_IOMMU_CAP_T2GPA) &&
7270c54acb8STomasz Jeznach         ctx->tc & RISCV_IOMMU_DC_TC_T2GPA) {
7280c54acb8STomasz Jeznach         return false;
7290c54acb8STomasz Jeznach     }
7300c54acb8STomasz Jeznach 
7310c54acb8STomasz Jeznach     if (s->cap & RISCV_IOMMU_CAP_MSI_FLAT) {
7320c54acb8STomasz Jeznach         msi_mode = get_field(ctx->msiptp, RISCV_IOMMU_DC_MSIPTP_MODE);
7330c54acb8STomasz Jeznach 
7340c54acb8STomasz Jeznach         if (msi_mode != RISCV_IOMMU_DC_MSIPTP_MODE_OFF &&
7350c54acb8STomasz Jeznach             msi_mode != RISCV_IOMMU_DC_MSIPTP_MODE_FLAT) {
7360c54acb8STomasz Jeznach             return false;
7370c54acb8STomasz Jeznach         }
7380c54acb8STomasz Jeznach     }
7390c54acb8STomasz Jeznach 
74069a9ae48STomasz Jeznach     gatp = get_field(ctx->gatp, RISCV_IOMMU_ATP_MODE_FIELD);
74169a9ae48STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_T2GPA &&
74269a9ae48STomasz Jeznach         gatp == RISCV_IOMMU_DC_IOHGATP_MODE_BARE) {
74369a9ae48STomasz Jeznach         return false;
74469a9ae48STomasz Jeznach     }
74569a9ae48STomasz Jeznach 
7460c54acb8STomasz Jeznach     fsc_mode = get_field(ctx->satp, RISCV_IOMMU_DC_FSC_MODE);
7470c54acb8STomasz Jeznach 
7480c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_PDTV) {
7490c54acb8STomasz Jeznach         switch (fsc_mode) {
7500c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_PDTP_MODE_PD8:
7510c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_PD8)) {
7520c54acb8STomasz Jeznach                 return false;
7530c54acb8STomasz Jeznach             }
7540c54acb8STomasz Jeznach             break;
7550c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_PDTP_MODE_PD17:
7560c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_PD17)) {
7570c54acb8STomasz Jeznach                 return false;
7580c54acb8STomasz Jeznach             }
7590c54acb8STomasz Jeznach             break;
7600c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_PDTP_MODE_PD20:
7610c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_PD20)) {
7620c54acb8STomasz Jeznach                 return false;
7630c54acb8STomasz Jeznach             }
7640c54acb8STomasz Jeznach             break;
7650c54acb8STomasz Jeznach         }
7660c54acb8STomasz Jeznach     } else {
7670c54acb8STomasz Jeznach         /* DC.tc.PDTV is 0 */
7680c54acb8STomasz Jeznach         if (ctx->tc & RISCV_IOMMU_DC_TC_DPE) {
7690c54acb8STomasz Jeznach             return false;
7700c54acb8STomasz Jeznach         }
7710c54acb8STomasz Jeznach 
7720c54acb8STomasz Jeznach         if (ctx->tc & RISCV_IOMMU_DC_TC_SXL) {
7730c54acb8STomasz Jeznach             if (fsc_mode == RISCV_IOMMU_CAP_SV32 &&
7740c54acb8STomasz Jeznach                 !(s->cap & RISCV_IOMMU_CAP_SV32)) {
7750c54acb8STomasz Jeznach                 return false;
7760c54acb8STomasz Jeznach             }
7770c54acb8STomasz Jeznach         } else {
7780c54acb8STomasz Jeznach             switch (fsc_mode) {
7790c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV39:
7800c54acb8STomasz Jeznach                 if (!(s->cap & RISCV_IOMMU_CAP_SV39)) {
7810c54acb8STomasz Jeznach                     return false;
7820c54acb8STomasz Jeznach                 }
7830c54acb8STomasz Jeznach                 break;
7840c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV48:
7850c54acb8STomasz Jeznach                 if (!(s->cap & RISCV_IOMMU_CAP_SV48)) {
7860c54acb8STomasz Jeznach                     return false;
7870c54acb8STomasz Jeznach                 }
7880c54acb8STomasz Jeznach             break;
7890c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57:
7900c54acb8STomasz Jeznach                 if (!(s->cap & RISCV_IOMMU_CAP_SV57)) {
7910c54acb8STomasz Jeznach                     return false;
7920c54acb8STomasz Jeznach                 }
7930c54acb8STomasz Jeznach                 break;
7940c54acb8STomasz Jeznach             }
7950c54acb8STomasz Jeznach         }
7960c54acb8STomasz Jeznach     }
7970c54acb8STomasz Jeznach 
7980c54acb8STomasz Jeznach     /*
7990c54acb8STomasz Jeznach      * CAP_END is always zero (only one endianess). FCTL_BE is
8000c54acb8STomasz Jeznach      * always zero (little-endian accesses). Thus TC_SBE must
8010c54acb8STomasz Jeznach      * always be LE, i.e. zero.
8020c54acb8STomasz Jeznach      */
8030c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_SBE) {
8040c54acb8STomasz Jeznach         return false;
8050c54acb8STomasz Jeznach     }
8060c54acb8STomasz Jeznach 
8070c54acb8STomasz Jeznach     return true;
8080c54acb8STomasz Jeznach }
8090c54acb8STomasz Jeznach 
8100c54acb8STomasz Jeznach /*
8110c54acb8STomasz Jeznach  * Validate process context (PC) according to section
8120c54acb8STomasz Jeznach  * "Process-context configuration checks".
8130c54acb8STomasz Jeznach  */
8140c54acb8STomasz Jeznach static bool riscv_iommu_validate_process_ctx(RISCVIOMMUState *s,
8150c54acb8STomasz Jeznach                                              RISCVIOMMUContext *ctx)
8160c54acb8STomasz Jeznach {
8170c54acb8STomasz Jeznach     uint32_t mode;
8180c54acb8STomasz Jeznach 
8190c54acb8STomasz Jeznach     if (get_field(ctx->ta, RISCV_IOMMU_PC_TA_RESERVED)) {
8200c54acb8STomasz Jeznach         return false;
8210c54acb8STomasz Jeznach     }
8220c54acb8STomasz Jeznach 
8230c54acb8STomasz Jeznach     if (get_field(ctx->satp, RISCV_IOMMU_PC_FSC_RESERVED)) {
8240c54acb8STomasz Jeznach         return false;
8250c54acb8STomasz Jeznach     }
8260c54acb8STomasz Jeznach 
8270c54acb8STomasz Jeznach     mode = get_field(ctx->satp, RISCV_IOMMU_DC_FSC_MODE);
8280c54acb8STomasz Jeznach     switch (mode) {
8290c54acb8STomasz Jeznach     case RISCV_IOMMU_DC_FSC_MODE_BARE:
8300c54acb8STomasz Jeznach     /* sv39 and sv32 modes have the same value (8) */
8310c54acb8STomasz Jeznach     case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV39:
8320c54acb8STomasz Jeznach     case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV48:
8330c54acb8STomasz Jeznach     case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57:
8340c54acb8STomasz Jeznach         break;
8350c54acb8STomasz Jeznach     default:
8360c54acb8STomasz Jeznach         return false;
8370c54acb8STomasz Jeznach     }
8380c54acb8STomasz Jeznach 
8390c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_SXL) {
840d3b96a53SDaniel Henrique Barboza         if (mode == RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV32 &&
8410c54acb8STomasz Jeznach             !(s->cap & RISCV_IOMMU_CAP_SV32)) {
8420c54acb8STomasz Jeznach                 return false;
8430c54acb8STomasz Jeznach         }
8440c54acb8STomasz Jeznach     } else {
8450c54acb8STomasz Jeznach         switch (mode) {
8460c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV39:
8470c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_SV39)) {
8480c54acb8STomasz Jeznach                 return false;
8490c54acb8STomasz Jeznach             }
8500c54acb8STomasz Jeznach             break;
8510c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV48:
8520c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_SV48)) {
8530c54acb8STomasz Jeznach                 return false;
8540c54acb8STomasz Jeznach             }
8550c54acb8STomasz Jeznach             break;
8560c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57:
8570c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_SV57)) {
8580c54acb8STomasz Jeznach                 return false;
8590c54acb8STomasz Jeznach             }
8600c54acb8STomasz Jeznach             break;
8610c54acb8STomasz Jeznach         }
8620c54acb8STomasz Jeznach     }
8630c54acb8STomasz Jeznach 
8640c54acb8STomasz Jeznach     return true;
8650c54acb8STomasz Jeznach }
8660c54acb8STomasz Jeznach 
8670c54acb8STomasz Jeznach /*
8680c54acb8STomasz Jeznach  * RISC-V IOMMU Device Context Loopkup - Device Directory Tree Walk
8690c54acb8STomasz Jeznach  *
8700c54acb8STomasz Jeznach  * @s         : IOMMU Device State
8710c54acb8STomasz Jeznach  * @ctx       : Device Translation Context with devid and process_id set.
8720c54acb8STomasz Jeznach  * @return    : success or fault code.
8730c54acb8STomasz Jeznach  */
8740c54acb8STomasz Jeznach static int riscv_iommu_ctx_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx)
8750c54acb8STomasz Jeznach {
8760c54acb8STomasz Jeznach     const uint64_t ddtp = s->ddtp;
8770c54acb8STomasz Jeznach     unsigned mode = get_field(ddtp, RISCV_IOMMU_DDTP_MODE);
8780c54acb8STomasz Jeznach     dma_addr_t addr = PPN_PHYS(get_field(ddtp, RISCV_IOMMU_DDTP_PPN));
8790c54acb8STomasz Jeznach     struct riscv_iommu_dc dc;
8800c54acb8STomasz Jeznach     /* Device Context format: 0: extended (64 bytes) | 1: base (32 bytes) */
8810c54acb8STomasz Jeznach     const int dc_fmt = !s->enable_msi;
8820c54acb8STomasz Jeznach     const size_t dc_len = sizeof(dc) >> dc_fmt;
883cd5d265fSDaniel Henrique Barboza     int depth;
8840c54acb8STomasz Jeznach     uint64_t de;
8850c54acb8STomasz Jeznach 
8860c54acb8STomasz Jeznach     switch (mode) {
8870c54acb8STomasz Jeznach     case RISCV_IOMMU_DDTP_MODE_OFF:
8880c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_DMA_DISABLED;
8890c54acb8STomasz Jeznach 
8900c54acb8STomasz Jeznach     case RISCV_IOMMU_DDTP_MODE_BARE:
8910c54acb8STomasz Jeznach         /* mock up pass-through translation context */
8920c54acb8STomasz Jeznach         ctx->gatp = set_field(0, RISCV_IOMMU_ATP_MODE_FIELD,
8930c54acb8STomasz Jeznach             RISCV_IOMMU_DC_IOHGATP_MODE_BARE);
8940c54acb8STomasz Jeznach         ctx->satp = set_field(0, RISCV_IOMMU_ATP_MODE_FIELD,
8950c54acb8STomasz Jeznach             RISCV_IOMMU_DC_FSC_MODE_BARE);
89669a9ae48STomasz Jeznach 
8970c54acb8STomasz Jeznach         ctx->tc = RISCV_IOMMU_DC_TC_V;
89869a9ae48STomasz Jeznach         if (s->enable_ats) {
89969a9ae48STomasz Jeznach             ctx->tc |= RISCV_IOMMU_DC_TC_EN_ATS;
90069a9ae48STomasz Jeznach         }
90169a9ae48STomasz Jeznach 
9020c54acb8STomasz Jeznach         ctx->ta = 0;
9030c54acb8STomasz Jeznach         ctx->msiptp = 0;
9040c54acb8STomasz Jeznach         return 0;
9050c54acb8STomasz Jeznach 
9060c54acb8STomasz Jeznach     case RISCV_IOMMU_DDTP_MODE_1LVL:
9070c54acb8STomasz Jeznach         depth = 0;
9080c54acb8STomasz Jeznach         break;
9090c54acb8STomasz Jeznach 
9100c54acb8STomasz Jeznach     case RISCV_IOMMU_DDTP_MODE_2LVL:
9110c54acb8STomasz Jeznach         depth = 1;
9120c54acb8STomasz Jeznach         break;
9130c54acb8STomasz Jeznach 
9140c54acb8STomasz Jeznach     case RISCV_IOMMU_DDTP_MODE_3LVL:
9150c54acb8STomasz Jeznach         depth = 2;
9160c54acb8STomasz Jeznach         break;
9170c54acb8STomasz Jeznach 
9180c54acb8STomasz Jeznach     default:
9190c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
9200c54acb8STomasz Jeznach     }
9210c54acb8STomasz Jeznach 
9220c54acb8STomasz Jeznach     /*
9230c54acb8STomasz Jeznach      * Check supported device id width (in bits).
9240c54acb8STomasz Jeznach      * See IOMMU Specification, Chapter 6. Software guidelines.
9250c54acb8STomasz Jeznach      * - if extended device-context format is used:
9260c54acb8STomasz Jeznach      *   1LVL: 6, 2LVL: 15, 3LVL: 24
9270c54acb8STomasz Jeznach      * - if base device-context format is used:
9280c54acb8STomasz Jeznach      *   1LVL: 7, 2LVL: 16, 3LVL: 24
9290c54acb8STomasz Jeznach      */
9300c54acb8STomasz Jeznach     if (ctx->devid >= (1 << (depth * 9 + 6 + (dc_fmt && depth != 2)))) {
9310c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_TTYPE_BLOCKED;
9320c54acb8STomasz Jeznach     }
9330c54acb8STomasz Jeznach 
9340c54acb8STomasz Jeznach     /* Device directory tree walk */
9350c54acb8STomasz Jeznach     for (; depth-- > 0; ) {
9360c54acb8STomasz Jeznach         /*
9370c54acb8STomasz Jeznach          * Select device id index bits based on device directory tree level
9380c54acb8STomasz Jeznach          * and device context format.
9390c54acb8STomasz Jeznach          * See IOMMU Specification, Chapter 2. Data Structures.
9400c54acb8STomasz Jeznach          * - if extended device-context format is used:
9410c54acb8STomasz Jeznach          *   device index: [23:15][14:6][5:0]
9420c54acb8STomasz Jeznach          * - if base device-context format is used:
9430c54acb8STomasz Jeznach          *   device index: [23:16][15:7][6:0]
9440c54acb8STomasz Jeznach          */
9450c54acb8STomasz Jeznach         const int split = depth * 9 + 6 + dc_fmt;
9460c54acb8STomasz Jeznach         addr |= ((ctx->devid >> split) << 3) & ~TARGET_PAGE_MASK;
9470c54acb8STomasz Jeznach         if (dma_memory_read(s->target_as, addr, &de, sizeof(de),
9480c54acb8STomasz Jeznach                             MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
9490c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_DDT_LOAD_FAULT;
9500c54acb8STomasz Jeznach         }
9510c54acb8STomasz Jeznach         le64_to_cpus(&de);
9520c54acb8STomasz Jeznach         if (!(de & RISCV_IOMMU_DDTE_VALID)) {
9530c54acb8STomasz Jeznach             /* invalid directory entry */
9540c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_DDT_INVALID;
9550c54acb8STomasz Jeznach         }
9560c54acb8STomasz Jeznach         if (de & ~(RISCV_IOMMU_DDTE_PPN | RISCV_IOMMU_DDTE_VALID)) {
9570c54acb8STomasz Jeznach             /* reserved bits set */
9580c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
9590c54acb8STomasz Jeznach         }
9600c54acb8STomasz Jeznach         addr = PPN_PHYS(get_field(de, RISCV_IOMMU_DDTE_PPN));
9610c54acb8STomasz Jeznach     }
9620c54acb8STomasz Jeznach 
9630c54acb8STomasz Jeznach     /* index into device context entry page */
9640c54acb8STomasz Jeznach     addr |= (ctx->devid * dc_len) & ~TARGET_PAGE_MASK;
9650c54acb8STomasz Jeznach 
9660c54acb8STomasz Jeznach     memset(&dc, 0, sizeof(dc));
9670c54acb8STomasz Jeznach     if (dma_memory_read(s->target_as, addr, &dc, dc_len,
9680c54acb8STomasz Jeznach                         MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
9690c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_DDT_LOAD_FAULT;
9700c54acb8STomasz Jeznach     }
9710c54acb8STomasz Jeznach 
9720c54acb8STomasz Jeznach     /* Set translation context. */
9730c54acb8STomasz Jeznach     ctx->tc = le64_to_cpu(dc.tc);
9740c54acb8STomasz Jeznach     ctx->gatp = le64_to_cpu(dc.iohgatp);
9750c54acb8STomasz Jeznach     ctx->satp = le64_to_cpu(dc.fsc);
9760c54acb8STomasz Jeznach     ctx->ta = le64_to_cpu(dc.ta);
9770c54acb8STomasz Jeznach     ctx->msiptp = le64_to_cpu(dc.msiptp);
9780c54acb8STomasz Jeznach     ctx->msi_addr_mask = le64_to_cpu(dc.msi_addr_mask);
9790c54acb8STomasz Jeznach     ctx->msi_addr_pattern = le64_to_cpu(dc.msi_addr_pattern);
9800c54acb8STomasz Jeznach 
9810c54acb8STomasz Jeznach     if (!(ctx->tc & RISCV_IOMMU_DC_TC_V)) {
9820c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_DDT_INVALID;
9830c54acb8STomasz Jeznach     }
9840c54acb8STomasz Jeznach 
9850c54acb8STomasz Jeznach     if (!riscv_iommu_validate_device_ctx(s, ctx)) {
9860c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
9870c54acb8STomasz Jeznach     }
9880c54acb8STomasz Jeznach 
9890c54acb8STomasz Jeznach     /* FSC field checks */
9900c54acb8STomasz Jeznach     mode = get_field(ctx->satp, RISCV_IOMMU_DC_FSC_MODE);
9910c54acb8STomasz Jeznach     addr = PPN_PHYS(get_field(ctx->satp, RISCV_IOMMU_DC_FSC_PPN));
9920c54acb8STomasz Jeznach 
9930c54acb8STomasz Jeznach     if (!(ctx->tc & RISCV_IOMMU_DC_TC_PDTV)) {
9940c54acb8STomasz Jeznach         if (ctx->process_id != RISCV_IOMMU_NOPROCID) {
9950c54acb8STomasz Jeznach             /* PID is disabled */
9960c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_TTYPE_BLOCKED;
9970c54acb8STomasz Jeznach         }
9980c54acb8STomasz Jeznach         if (mode > RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57) {
9990c54acb8STomasz Jeznach             /* Invalid translation mode */
10000c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_DDT_INVALID;
10010c54acb8STomasz Jeznach         }
10020c54acb8STomasz Jeznach         return 0;
10030c54acb8STomasz Jeznach     }
10040c54acb8STomasz Jeznach 
10050c54acb8STomasz Jeznach     if (ctx->process_id == RISCV_IOMMU_NOPROCID) {
10060c54acb8STomasz Jeznach         if (!(ctx->tc & RISCV_IOMMU_DC_TC_DPE)) {
10070c54acb8STomasz Jeznach             /* No default process_id enabled, set BARE mode */
10080c54acb8STomasz Jeznach             ctx->satp = 0ULL;
10090c54acb8STomasz Jeznach             return 0;
10100c54acb8STomasz Jeznach         } else {
10110c54acb8STomasz Jeznach             /* Use default process_id #0 */
10120c54acb8STomasz Jeznach             ctx->process_id = 0;
10130c54acb8STomasz Jeznach         }
10140c54acb8STomasz Jeznach     }
10150c54acb8STomasz Jeznach 
10160c54acb8STomasz Jeznach     if (mode == RISCV_IOMMU_DC_FSC_MODE_BARE) {
10170c54acb8STomasz Jeznach         /* No S-Stage translation, done. */
10180c54acb8STomasz Jeznach         return 0;
10190c54acb8STomasz Jeznach     }
10200c54acb8STomasz Jeznach 
10210c54acb8STomasz Jeznach     /* FSC.TC.PDTV enabled */
10220c54acb8STomasz Jeznach     if (mode > RISCV_IOMMU_DC_FSC_PDTP_MODE_PD20) {
10230c54acb8STomasz Jeznach         /* Invalid PDTP.MODE */
10240c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_PDT_MISCONFIGURED;
10250c54acb8STomasz Jeznach     }
10260c54acb8STomasz Jeznach 
10270c54acb8STomasz Jeznach     for (depth = mode - RISCV_IOMMU_DC_FSC_PDTP_MODE_PD8; depth-- > 0; ) {
10280c54acb8STomasz Jeznach         /*
10290c54acb8STomasz Jeznach          * Select process id index bits based on process directory tree
10300c54acb8STomasz Jeznach          * level. See IOMMU Specification, 2.2. Process-Directory-Table.
10310c54acb8STomasz Jeznach          */
10320c54acb8STomasz Jeznach         const int split = depth * 9 + 8;
10330c54acb8STomasz Jeznach         addr |= ((ctx->process_id >> split) << 3) & ~TARGET_PAGE_MASK;
10340c54acb8STomasz Jeznach         if (dma_memory_read(s->target_as, addr, &de, sizeof(de),
10350c54acb8STomasz Jeznach                             MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
10360c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_PDT_LOAD_FAULT;
10370c54acb8STomasz Jeznach         }
10380c54acb8STomasz Jeznach         le64_to_cpus(&de);
10390c54acb8STomasz Jeznach         if (!(de & RISCV_IOMMU_PC_TA_V)) {
10400c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_PDT_INVALID;
10410c54acb8STomasz Jeznach         }
10420c54acb8STomasz Jeznach         addr = PPN_PHYS(get_field(de, RISCV_IOMMU_PC_FSC_PPN));
10430c54acb8STomasz Jeznach     }
10440c54acb8STomasz Jeznach 
10450c54acb8STomasz Jeznach     /* Leaf entry in PDT */
10460c54acb8STomasz Jeznach     addr |= (ctx->process_id << 4) & ~TARGET_PAGE_MASK;
10470c54acb8STomasz Jeznach     if (dma_memory_read(s->target_as, addr, &dc.ta, sizeof(uint64_t) * 2,
10480c54acb8STomasz Jeznach                         MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
10490c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_PDT_LOAD_FAULT;
10500c54acb8STomasz Jeznach     }
10510c54acb8STomasz Jeznach 
10520c54acb8STomasz Jeznach     /* Use FSC and TA from process directory entry. */
10530c54acb8STomasz Jeznach     ctx->ta = le64_to_cpu(dc.ta);
10540c54acb8STomasz Jeznach     ctx->satp = le64_to_cpu(dc.fsc);
10550c54acb8STomasz Jeznach 
10560c54acb8STomasz Jeznach     if (!(ctx->ta & RISCV_IOMMU_PC_TA_V)) {
10570c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_PDT_INVALID;
10580c54acb8STomasz Jeznach     }
10590c54acb8STomasz Jeznach 
10600c54acb8STomasz Jeznach     if (!riscv_iommu_validate_process_ctx(s, ctx)) {
10610c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_PDT_MISCONFIGURED;
10620c54acb8STomasz Jeznach     }
10630c54acb8STomasz Jeznach 
10640c54acb8STomasz Jeznach     return 0;
10650c54acb8STomasz Jeznach }
10660c54acb8STomasz Jeznach 
10670c54acb8STomasz Jeznach /* Translation Context cache support */
10680c54acb8STomasz Jeznach static gboolean riscv_iommu_ctx_equal(gconstpointer v1, gconstpointer v2)
10690c54acb8STomasz Jeznach {
10700c54acb8STomasz Jeznach     RISCVIOMMUContext *c1 = (RISCVIOMMUContext *) v1;
10710c54acb8STomasz Jeznach     RISCVIOMMUContext *c2 = (RISCVIOMMUContext *) v2;
10720c54acb8STomasz Jeznach     return c1->devid == c2->devid &&
10730c54acb8STomasz Jeznach            c1->process_id == c2->process_id;
10740c54acb8STomasz Jeznach }
10750c54acb8STomasz Jeznach 
10760c54acb8STomasz Jeznach static guint riscv_iommu_ctx_hash(gconstpointer v)
10770c54acb8STomasz Jeznach {
10780c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx = (RISCVIOMMUContext *) v;
10790c54acb8STomasz Jeznach     /*
10800c54acb8STomasz Jeznach      * Generate simple hash of (process_id, devid)
10810c54acb8STomasz Jeznach      * assuming 24-bit wide devid.
10820c54acb8STomasz Jeznach      */
10830c54acb8STomasz Jeznach     return (guint)(ctx->devid) + ((guint)(ctx->process_id) << 24);
10840c54acb8STomasz Jeznach }
10850c54acb8STomasz Jeznach 
10860c54acb8STomasz Jeznach static void riscv_iommu_ctx_inval_devid_procid(gpointer key, gpointer value,
10870c54acb8STomasz Jeznach                                                gpointer data)
10880c54acb8STomasz Jeznach {
10890c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx = (RISCVIOMMUContext *) value;
10900c54acb8STomasz Jeznach     RISCVIOMMUContext *arg = (RISCVIOMMUContext *) data;
10910c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_V &&
10920c54acb8STomasz Jeznach         ctx->devid == arg->devid &&
10930c54acb8STomasz Jeznach         ctx->process_id == arg->process_id) {
10940c54acb8STomasz Jeznach         ctx->tc &= ~RISCV_IOMMU_DC_TC_V;
10950c54acb8STomasz Jeznach     }
10960c54acb8STomasz Jeznach }
10970c54acb8STomasz Jeznach 
10980c54acb8STomasz Jeznach static void riscv_iommu_ctx_inval_devid(gpointer key, gpointer value,
10990c54acb8STomasz Jeznach                                         gpointer data)
11000c54acb8STomasz Jeznach {
11010c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx = (RISCVIOMMUContext *) value;
11020c54acb8STomasz Jeznach     RISCVIOMMUContext *arg = (RISCVIOMMUContext *) data;
11030c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_V &&
11040c54acb8STomasz Jeznach         ctx->devid == arg->devid) {
11050c54acb8STomasz Jeznach         ctx->tc &= ~RISCV_IOMMU_DC_TC_V;
11060c54acb8STomasz Jeznach     }
11070c54acb8STomasz Jeznach }
11080c54acb8STomasz Jeznach 
11090c54acb8STomasz Jeznach static void riscv_iommu_ctx_inval_all(gpointer key, gpointer value,
11100c54acb8STomasz Jeznach                                       gpointer data)
11110c54acb8STomasz Jeznach {
11120c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx = (RISCVIOMMUContext *) value;
11130c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_V) {
11140c54acb8STomasz Jeznach         ctx->tc &= ~RISCV_IOMMU_DC_TC_V;
11150c54acb8STomasz Jeznach     }
11160c54acb8STomasz Jeznach }
11170c54acb8STomasz Jeznach 
11180c54acb8STomasz Jeznach static void riscv_iommu_ctx_inval(RISCVIOMMUState *s, GHFunc func,
11190c54acb8STomasz Jeznach                                   uint32_t devid, uint32_t process_id)
11200c54acb8STomasz Jeznach {
11210c54acb8STomasz Jeznach     GHashTable *ctx_cache;
11220c54acb8STomasz Jeznach     RISCVIOMMUContext key = {
11230c54acb8STomasz Jeznach         .devid = devid,
11240c54acb8STomasz Jeznach         .process_id = process_id,
11250c54acb8STomasz Jeznach     };
11260c54acb8STomasz Jeznach     ctx_cache = g_hash_table_ref(s->ctx_cache);
11270c54acb8STomasz Jeznach     g_hash_table_foreach(ctx_cache, func, &key);
11280c54acb8STomasz Jeznach     g_hash_table_unref(ctx_cache);
11290c54acb8STomasz Jeznach }
11300c54acb8STomasz Jeznach 
11310c54acb8STomasz Jeznach /* Find or allocate translation context for a given {device_id, process_id} */
11320c54acb8STomasz Jeznach static RISCVIOMMUContext *riscv_iommu_ctx(RISCVIOMMUState *s,
11330c54acb8STomasz Jeznach                                           unsigned devid, unsigned process_id,
11340c54acb8STomasz Jeznach                                           void **ref)
11350c54acb8STomasz Jeznach {
11360c54acb8STomasz Jeznach     GHashTable *ctx_cache;
11370c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx;
11380c54acb8STomasz Jeznach     RISCVIOMMUContext key = {
11390c54acb8STomasz Jeznach         .devid = devid,
11400c54acb8STomasz Jeznach         .process_id = process_id,
11410c54acb8STomasz Jeznach     };
11420c54acb8STomasz Jeznach 
11430c54acb8STomasz Jeznach     ctx_cache = g_hash_table_ref(s->ctx_cache);
11440c54acb8STomasz Jeznach     ctx = g_hash_table_lookup(ctx_cache, &key);
11450c54acb8STomasz Jeznach 
11460c54acb8STomasz Jeznach     if (ctx && (ctx->tc & RISCV_IOMMU_DC_TC_V)) {
11470c54acb8STomasz Jeznach         *ref = ctx_cache;
11480c54acb8STomasz Jeznach         return ctx;
11490c54acb8STomasz Jeznach     }
11500c54acb8STomasz Jeznach 
11510c54acb8STomasz Jeznach     ctx = g_new0(RISCVIOMMUContext, 1);
11520c54acb8STomasz Jeznach     ctx->devid = devid;
11530c54acb8STomasz Jeznach     ctx->process_id = process_id;
11540c54acb8STomasz Jeznach 
11550c54acb8STomasz Jeznach     int fault = riscv_iommu_ctx_fetch(s, ctx);
11560c54acb8STomasz Jeznach     if (!fault) {
11570c54acb8STomasz Jeznach         if (g_hash_table_size(ctx_cache) >= LIMIT_CACHE_CTX) {
11580c54acb8STomasz Jeznach             g_hash_table_unref(ctx_cache);
11590c54acb8STomasz Jeznach             ctx_cache = g_hash_table_new_full(riscv_iommu_ctx_hash,
11600c54acb8STomasz Jeznach                                               riscv_iommu_ctx_equal,
11610c54acb8STomasz Jeznach                                               g_free, NULL);
11620c54acb8STomasz Jeznach             g_hash_table_ref(ctx_cache);
11630c54acb8STomasz Jeznach             g_hash_table_unref(qatomic_xchg(&s->ctx_cache, ctx_cache));
11640c54acb8STomasz Jeznach         }
11650c54acb8STomasz Jeznach         g_hash_table_add(ctx_cache, ctx);
11660c54acb8STomasz Jeznach         *ref = ctx_cache;
11670c54acb8STomasz Jeznach         return ctx;
11680c54acb8STomasz Jeznach     }
11690c54acb8STomasz Jeznach 
11700c54acb8STomasz Jeznach     g_hash_table_unref(ctx_cache);
11710c54acb8STomasz Jeznach     *ref = NULL;
11720c54acb8STomasz Jeznach 
11730c54acb8STomasz Jeznach     riscv_iommu_report_fault(s, ctx, RISCV_IOMMU_FQ_TTYPE_UADDR_RD,
11740c54acb8STomasz Jeznach                              fault, !!process_id, 0, 0);
11750c54acb8STomasz Jeznach 
11760c54acb8STomasz Jeznach     g_free(ctx);
11770c54acb8STomasz Jeznach     return NULL;
11780c54acb8STomasz Jeznach }
11790c54acb8STomasz Jeznach 
11800c54acb8STomasz Jeznach static void riscv_iommu_ctx_put(RISCVIOMMUState *s, void *ref)
11810c54acb8STomasz Jeznach {
11820c54acb8STomasz Jeznach     if (ref) {
11830c54acb8STomasz Jeznach         g_hash_table_unref((GHashTable *)ref);
11840c54acb8STomasz Jeznach     }
11850c54acb8STomasz Jeznach }
11860c54acb8STomasz Jeznach 
11870c54acb8STomasz Jeznach /* Find or allocate address space for a given device */
11880c54acb8STomasz Jeznach static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, uint32_t devid)
11890c54acb8STomasz Jeznach {
11900c54acb8STomasz Jeznach     RISCVIOMMUSpace *as;
11910c54acb8STomasz Jeznach 
11920c54acb8STomasz Jeznach     /* FIXME: PCIe bus remapping for attached endpoints. */
11930c54acb8STomasz Jeznach     devid |= s->bus << 8;
11940c54acb8STomasz Jeznach 
11950c54acb8STomasz Jeznach     QLIST_FOREACH(as, &s->spaces, list) {
11960c54acb8STomasz Jeznach         if (as->devid == devid) {
11970c54acb8STomasz Jeznach             break;
11980c54acb8STomasz Jeznach         }
11990c54acb8STomasz Jeznach     }
12000c54acb8STomasz Jeznach 
12010c54acb8STomasz Jeznach     if (as == NULL) {
12020c54acb8STomasz Jeznach         char name[64];
12030c54acb8STomasz Jeznach         as = g_new0(RISCVIOMMUSpace, 1);
12040c54acb8STomasz Jeznach 
12050c54acb8STomasz Jeznach         as->iommu = s;
12060c54acb8STomasz Jeznach         as->devid = devid;
12070c54acb8STomasz Jeznach 
12080c54acb8STomasz Jeznach         snprintf(name, sizeof(name), "riscv-iommu-%04x:%02x.%d-iova",
12090c54acb8STomasz Jeznach             PCI_BUS_NUM(as->devid), PCI_SLOT(as->devid), PCI_FUNC(as->devid));
12100c54acb8STomasz Jeznach 
12110c54acb8STomasz Jeznach         /* IOVA address space, untranslated addresses */
12120c54acb8STomasz Jeznach         memory_region_init_iommu(&as->iova_mr, sizeof(as->iova_mr),
12130c54acb8STomasz Jeznach             TYPE_RISCV_IOMMU_MEMORY_REGION,
12140c54acb8STomasz Jeznach             OBJECT(as), "riscv_iommu", UINT64_MAX);
12150c54acb8STomasz Jeznach         address_space_init(&as->iova_as, MEMORY_REGION(&as->iova_mr), name);
12160c54acb8STomasz Jeznach 
12170c54acb8STomasz Jeznach         QLIST_INSERT_HEAD(&s->spaces, as, list);
12180c54acb8STomasz Jeznach 
12190c54acb8STomasz Jeznach         trace_riscv_iommu_new(s->parent_obj.id, PCI_BUS_NUM(as->devid),
12200c54acb8STomasz Jeznach                 PCI_SLOT(as->devid), PCI_FUNC(as->devid));
12210c54acb8STomasz Jeznach     }
12220c54acb8STomasz Jeznach     return &as->iova_as;
12230c54acb8STomasz Jeznach }
12240c54acb8STomasz Jeznach 
12259d085a1cSTomasz Jeznach /* Translation Object cache support */
12269d085a1cSTomasz Jeznach static gboolean riscv_iommu_iot_equal(gconstpointer v1, gconstpointer v2)
12270c54acb8STomasz Jeznach {
12289d085a1cSTomasz Jeznach     RISCVIOMMUEntry *t1 = (RISCVIOMMUEntry *) v1;
12299d085a1cSTomasz Jeznach     RISCVIOMMUEntry *t2 = (RISCVIOMMUEntry *) v2;
12309d085a1cSTomasz Jeznach     return t1->gscid == t2->gscid && t1->pscid == t2->pscid &&
12319d085a1cSTomasz Jeznach            t1->iova == t2->iova;
12329d085a1cSTomasz Jeznach }
12339d085a1cSTomasz Jeznach 
12349d085a1cSTomasz Jeznach static guint riscv_iommu_iot_hash(gconstpointer v)
12359d085a1cSTomasz Jeznach {
12369d085a1cSTomasz Jeznach     RISCVIOMMUEntry *t = (RISCVIOMMUEntry *) v;
12379d085a1cSTomasz Jeznach     return (guint)t->iova;
12389d085a1cSTomasz Jeznach }
12399d085a1cSTomasz Jeznach 
12409d085a1cSTomasz Jeznach /* GV: 1 PSCV: 1 AV: 1 */
12419d085a1cSTomasz Jeznach static void riscv_iommu_iot_inval_pscid_iova(gpointer key, gpointer value,
12429d085a1cSTomasz Jeznach                                              gpointer data)
12439d085a1cSTomasz Jeznach {
12449d085a1cSTomasz Jeznach     RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value;
12459d085a1cSTomasz Jeznach     RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data;
12469d085a1cSTomasz Jeznach     if (iot->gscid == arg->gscid &&
12479d085a1cSTomasz Jeznach         iot->pscid == arg->pscid &&
12489d085a1cSTomasz Jeznach         iot->iova == arg->iova) {
12499d085a1cSTomasz Jeznach         iot->perm = IOMMU_NONE;
12509d085a1cSTomasz Jeznach     }
12519d085a1cSTomasz Jeznach }
12529d085a1cSTomasz Jeznach 
12539d085a1cSTomasz Jeznach /* GV: 1 PSCV: 1 AV: 0 */
12549d085a1cSTomasz Jeznach static void riscv_iommu_iot_inval_pscid(gpointer key, gpointer value,
12559d085a1cSTomasz Jeznach                                         gpointer data)
12569d085a1cSTomasz Jeznach {
12579d085a1cSTomasz Jeznach     RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value;
12589d085a1cSTomasz Jeznach     RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data;
12599d085a1cSTomasz Jeznach     if (iot->gscid == arg->gscid &&
12609d085a1cSTomasz Jeznach         iot->pscid == arg->pscid) {
12619d085a1cSTomasz Jeznach         iot->perm = IOMMU_NONE;
12629d085a1cSTomasz Jeznach     }
12639d085a1cSTomasz Jeznach }
12649d085a1cSTomasz Jeznach 
12659d085a1cSTomasz Jeznach /* GV: 1 GVMA: 1 */
12669d085a1cSTomasz Jeznach static void riscv_iommu_iot_inval_gscid_gpa(gpointer key, gpointer value,
12679d085a1cSTomasz Jeznach                                             gpointer data)
12689d085a1cSTomasz Jeznach {
12699d085a1cSTomasz Jeznach     RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value;
12709d085a1cSTomasz Jeznach     RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data;
12719d085a1cSTomasz Jeznach     if (iot->gscid == arg->gscid) {
12729d085a1cSTomasz Jeznach         /* simplified cache, no GPA matching */
12739d085a1cSTomasz Jeznach         iot->perm = IOMMU_NONE;
12749d085a1cSTomasz Jeznach     }
12759d085a1cSTomasz Jeznach }
12769d085a1cSTomasz Jeznach 
12779d085a1cSTomasz Jeznach /* GV: 1 GVMA: 0 */
12789d085a1cSTomasz Jeznach static void riscv_iommu_iot_inval_gscid(gpointer key, gpointer value,
12799d085a1cSTomasz Jeznach                                         gpointer data)
12809d085a1cSTomasz Jeznach {
12819d085a1cSTomasz Jeznach     RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value;
12829d085a1cSTomasz Jeznach     RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data;
12839d085a1cSTomasz Jeznach     if (iot->gscid == arg->gscid) {
12849d085a1cSTomasz Jeznach         iot->perm = IOMMU_NONE;
12859d085a1cSTomasz Jeznach     }
12869d085a1cSTomasz Jeznach }
12879d085a1cSTomasz Jeznach 
12889d085a1cSTomasz Jeznach /* GV: 0 */
12899d085a1cSTomasz Jeznach static void riscv_iommu_iot_inval_all(gpointer key, gpointer value,
12909d085a1cSTomasz Jeznach                                       gpointer data)
12919d085a1cSTomasz Jeznach {
12929d085a1cSTomasz Jeznach     RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value;
12939d085a1cSTomasz Jeznach     iot->perm = IOMMU_NONE;
12949d085a1cSTomasz Jeznach }
12959d085a1cSTomasz Jeznach 
12969d085a1cSTomasz Jeznach /* caller should keep ref-count for iot_cache object */
12979d085a1cSTomasz Jeznach static RISCVIOMMUEntry *riscv_iommu_iot_lookup(RISCVIOMMUContext *ctx,
12989d085a1cSTomasz Jeznach     GHashTable *iot_cache, hwaddr iova)
12999d085a1cSTomasz Jeznach {
13009d085a1cSTomasz Jeznach     RISCVIOMMUEntry key = {
13019d085a1cSTomasz Jeznach         .gscid = get_field(ctx->gatp, RISCV_IOMMU_DC_IOHGATP_GSCID),
13029d085a1cSTomasz Jeznach         .pscid = get_field(ctx->ta, RISCV_IOMMU_DC_TA_PSCID),
13039d085a1cSTomasz Jeznach         .iova  = PPN_DOWN(iova),
13049d085a1cSTomasz Jeznach     };
13059d085a1cSTomasz Jeznach     return g_hash_table_lookup(iot_cache, &key);
13069d085a1cSTomasz Jeznach }
13079d085a1cSTomasz Jeznach 
13089d085a1cSTomasz Jeznach /* caller should keep ref-count for iot_cache object */
13099d085a1cSTomasz Jeznach static void riscv_iommu_iot_update(RISCVIOMMUState *s,
13109d085a1cSTomasz Jeznach     GHashTable *iot_cache, RISCVIOMMUEntry *iot)
13119d085a1cSTomasz Jeznach {
13129d085a1cSTomasz Jeznach     if (!s->iot_limit) {
13139d085a1cSTomasz Jeznach         return;
13149d085a1cSTomasz Jeznach     }
13159d085a1cSTomasz Jeznach 
13169d085a1cSTomasz Jeznach     if (g_hash_table_size(s->iot_cache) >= s->iot_limit) {
13179d085a1cSTomasz Jeznach         iot_cache = g_hash_table_new_full(riscv_iommu_iot_hash,
13189d085a1cSTomasz Jeznach                                           riscv_iommu_iot_equal,
13199d085a1cSTomasz Jeznach                                           g_free, NULL);
13209d085a1cSTomasz Jeznach         g_hash_table_unref(qatomic_xchg(&s->iot_cache, iot_cache));
13219d085a1cSTomasz Jeznach     }
13229d085a1cSTomasz Jeznach     g_hash_table_add(iot_cache, iot);
13239d085a1cSTomasz Jeznach }
13249d085a1cSTomasz Jeznach 
13259d085a1cSTomasz Jeznach static void riscv_iommu_iot_inval(RISCVIOMMUState *s, GHFunc func,
13269d085a1cSTomasz Jeznach     uint32_t gscid, uint32_t pscid, hwaddr iova)
13279d085a1cSTomasz Jeznach {
13289d085a1cSTomasz Jeznach     GHashTable *iot_cache;
13299d085a1cSTomasz Jeznach     RISCVIOMMUEntry key = {
13309d085a1cSTomasz Jeznach         .gscid = gscid,
13319d085a1cSTomasz Jeznach         .pscid = pscid,
13329d085a1cSTomasz Jeznach         .iova  = PPN_DOWN(iova),
13339d085a1cSTomasz Jeznach     };
13349d085a1cSTomasz Jeznach 
13359d085a1cSTomasz Jeznach     iot_cache = g_hash_table_ref(s->iot_cache);
13369d085a1cSTomasz Jeznach     g_hash_table_foreach(iot_cache, func, &key);
13379d085a1cSTomasz Jeznach     g_hash_table_unref(iot_cache);
13389d085a1cSTomasz Jeznach }
13399d085a1cSTomasz Jeznach 
13409d085a1cSTomasz Jeznach static int riscv_iommu_translate(RISCVIOMMUState *s, RISCVIOMMUContext *ctx,
13419d085a1cSTomasz Jeznach     IOMMUTLBEntry *iotlb, bool enable_cache)
13429d085a1cSTomasz Jeznach {
13439d085a1cSTomasz Jeznach     RISCVIOMMUEntry *iot;
13449d085a1cSTomasz Jeznach     IOMMUAccessFlags perm;
13450c54acb8STomasz Jeznach     bool enable_pid;
13460c54acb8STomasz Jeznach     bool enable_pri;
13479d085a1cSTomasz Jeznach     GHashTable *iot_cache;
13480c54acb8STomasz Jeznach     int fault;
13490c54acb8STomasz Jeznach 
13509d085a1cSTomasz Jeznach     iot_cache = g_hash_table_ref(s->iot_cache);
13510c54acb8STomasz Jeznach     /*
13520c54acb8STomasz Jeznach      * TC[32] is reserved for custom extensions, used here to temporarily
13530c54acb8STomasz Jeznach      * enable automatic page-request generation for ATS queries.
13540c54acb8STomasz Jeznach      */
13550c54acb8STomasz Jeznach     enable_pri = (iotlb->perm == IOMMU_NONE) && (ctx->tc & BIT_ULL(32));
13560c54acb8STomasz Jeznach     enable_pid = (ctx->tc & RISCV_IOMMU_DC_TC_PDTV);
13570c54acb8STomasz Jeznach 
135869a9ae48STomasz Jeznach     /* Check for ATS request. */
135969a9ae48STomasz Jeznach     if (iotlb->perm == IOMMU_NONE) {
136069a9ae48STomasz Jeznach         /* Check if ATS is disabled. */
136169a9ae48STomasz Jeznach         if (!(ctx->tc & RISCV_IOMMU_DC_TC_EN_ATS)) {
136269a9ae48STomasz Jeznach             enable_pri = false;
136369a9ae48STomasz Jeznach             fault = RISCV_IOMMU_FQ_CAUSE_TTYPE_BLOCKED;
136469a9ae48STomasz Jeznach             goto done;
136569a9ae48STomasz Jeznach         }
136669a9ae48STomasz Jeznach     }
136769a9ae48STomasz Jeznach 
13689d085a1cSTomasz Jeznach     iot = riscv_iommu_iot_lookup(ctx, iot_cache, iotlb->iova);
13699d085a1cSTomasz Jeznach     perm = iot ? iot->perm : IOMMU_NONE;
13709d085a1cSTomasz Jeznach     if (perm != IOMMU_NONE) {
13719d085a1cSTomasz Jeznach         iotlb->translated_addr = PPN_PHYS(iot->phys);
13729d085a1cSTomasz Jeznach         iotlb->addr_mask = ~TARGET_PAGE_MASK;
13739d085a1cSTomasz Jeznach         iotlb->perm = perm;
13749d085a1cSTomasz Jeznach         fault = 0;
13759d085a1cSTomasz Jeznach         goto done;
13769d085a1cSTomasz Jeznach     }
13779d085a1cSTomasz Jeznach 
13780c54acb8STomasz Jeznach     /* Translate using device directory / page table information. */
13790c54acb8STomasz Jeznach     fault = riscv_iommu_spa_fetch(s, ctx, iotlb);
13800c54acb8STomasz Jeznach 
13819d085a1cSTomasz Jeznach     if (!fault && iotlb->target_as == &s->trap_as) {
13829d085a1cSTomasz Jeznach         /* Do not cache trapped MSI translations */
13839d085a1cSTomasz Jeznach         goto done;
13849d085a1cSTomasz Jeznach     }
13859d085a1cSTomasz Jeznach 
13869d085a1cSTomasz Jeznach     /*
13879d085a1cSTomasz Jeznach      * We made an implementation choice to not cache identity-mapped
13889d085a1cSTomasz Jeznach      * translations, as allowed by the specification, to avoid
13899d085a1cSTomasz Jeznach      * translation cache evictions for other devices sharing the
13909d085a1cSTomasz Jeznach      * IOMMU hardware model.
13919d085a1cSTomasz Jeznach      */
13929d085a1cSTomasz Jeznach     if (!fault && iotlb->translated_addr != iotlb->iova && enable_cache) {
13939d085a1cSTomasz Jeznach         iot = g_new0(RISCVIOMMUEntry, 1);
13949d085a1cSTomasz Jeznach         iot->iova = PPN_DOWN(iotlb->iova);
13959d085a1cSTomasz Jeznach         iot->phys = PPN_DOWN(iotlb->translated_addr);
13969d085a1cSTomasz Jeznach         iot->gscid = get_field(ctx->gatp, RISCV_IOMMU_DC_IOHGATP_GSCID);
13979d085a1cSTomasz Jeznach         iot->pscid = get_field(ctx->ta, RISCV_IOMMU_DC_TA_PSCID);
13989d085a1cSTomasz Jeznach         iot->perm = iotlb->perm;
13999d085a1cSTomasz Jeznach         riscv_iommu_iot_update(s, iot_cache, iot);
14009d085a1cSTomasz Jeznach     }
14019d085a1cSTomasz Jeznach 
14029d085a1cSTomasz Jeznach done:
14039d085a1cSTomasz Jeznach     g_hash_table_unref(iot_cache);
14049d085a1cSTomasz Jeznach 
14050c54acb8STomasz Jeznach     if (enable_pri && fault) {
14060c54acb8STomasz Jeznach         struct riscv_iommu_pq_record pr = {0};
14070c54acb8STomasz Jeznach         if (enable_pid) {
14080c54acb8STomasz Jeznach             pr.hdr = set_field(RISCV_IOMMU_PREQ_HDR_PV,
14090c54acb8STomasz Jeznach                                RISCV_IOMMU_PREQ_HDR_PID, ctx->process_id);
14100c54acb8STomasz Jeznach         }
14110c54acb8STomasz Jeznach         pr.hdr = set_field(pr.hdr, RISCV_IOMMU_PREQ_HDR_DID, ctx->devid);
14120c54acb8STomasz Jeznach         pr.payload = (iotlb->iova & TARGET_PAGE_MASK) |
14130c54acb8STomasz Jeznach                      RISCV_IOMMU_PREQ_PAYLOAD_M;
14140c54acb8STomasz Jeznach         riscv_iommu_pri(s, &pr);
14150c54acb8STomasz Jeznach         return fault;
14160c54acb8STomasz Jeznach     }
14170c54acb8STomasz Jeznach 
14180c54acb8STomasz Jeznach     if (fault) {
141969a9ae48STomasz Jeznach         unsigned ttype = RISCV_IOMMU_FQ_TTYPE_PCIE_ATS_REQ;
14200c54acb8STomasz Jeznach 
14210c54acb8STomasz Jeznach         if (iotlb->perm & IOMMU_RW) {
14220c54acb8STomasz Jeznach             ttype = RISCV_IOMMU_FQ_TTYPE_UADDR_WR;
142369a9ae48STomasz Jeznach         } else if (iotlb->perm & IOMMU_RO) {
14240c54acb8STomasz Jeznach             ttype = RISCV_IOMMU_FQ_TTYPE_UADDR_RD;
14250c54acb8STomasz Jeznach         }
14260c54acb8STomasz Jeznach 
14270c54acb8STomasz Jeznach         riscv_iommu_report_fault(s, ctx, ttype, fault, enable_pid,
14280c54acb8STomasz Jeznach                                  iotlb->iova, iotlb->translated_addr);
14290c54acb8STomasz Jeznach         return fault;
14300c54acb8STomasz Jeznach     }
14310c54acb8STomasz Jeznach 
14320c54acb8STomasz Jeznach     return 0;
14330c54acb8STomasz Jeznach }
14340c54acb8STomasz Jeznach 
14350c54acb8STomasz Jeznach /* IOMMU Command Interface */
14360c54acb8STomasz Jeznach static MemTxResult riscv_iommu_iofence(RISCVIOMMUState *s, bool notify,
14370c54acb8STomasz Jeznach     uint64_t addr, uint32_t data)
14380c54acb8STomasz Jeznach {
14390c54acb8STomasz Jeznach     /*
14400c54acb8STomasz Jeznach      * ATS processing in this implementation of the IOMMU is synchronous,
14410c54acb8STomasz Jeznach      * no need to wait for completions here.
14420c54acb8STomasz Jeznach      */
14430c54acb8STomasz Jeznach     if (!notify) {
14440c54acb8STomasz Jeznach         return MEMTX_OK;
14450c54acb8STomasz Jeznach     }
14460c54acb8STomasz Jeznach 
14470c54acb8STomasz Jeznach     return dma_memory_write(s->target_as, addr, &data, sizeof(data),
14480c54acb8STomasz Jeznach         MEMTXATTRS_UNSPECIFIED);
14490c54acb8STomasz Jeznach }
14500c54acb8STomasz Jeznach 
145169a9ae48STomasz Jeznach static void riscv_iommu_ats(RISCVIOMMUState *s,
145269a9ae48STomasz Jeznach     struct riscv_iommu_command *cmd, IOMMUNotifierFlag flag,
145369a9ae48STomasz Jeznach     IOMMUAccessFlags perm,
145469a9ae48STomasz Jeznach     void (*trace_fn)(const char *id))
145569a9ae48STomasz Jeznach {
145669a9ae48STomasz Jeznach     RISCVIOMMUSpace *as = NULL;
145769a9ae48STomasz Jeznach     IOMMUNotifier *n;
145869a9ae48STomasz Jeznach     IOMMUTLBEvent event;
145969a9ae48STomasz Jeznach     uint32_t pid;
146069a9ae48STomasz Jeznach     uint32_t devid;
146169a9ae48STomasz Jeznach     const bool pv = cmd->dword0 & RISCV_IOMMU_CMD_ATS_PV;
146269a9ae48STomasz Jeznach 
146369a9ae48STomasz Jeznach     if (cmd->dword0 & RISCV_IOMMU_CMD_ATS_DSV) {
146469a9ae48STomasz Jeznach         /* Use device segment and requester id */
146569a9ae48STomasz Jeznach         devid = get_field(cmd->dword0,
146669a9ae48STomasz Jeznach             RISCV_IOMMU_CMD_ATS_DSEG | RISCV_IOMMU_CMD_ATS_RID);
146769a9ae48STomasz Jeznach     } else {
146869a9ae48STomasz Jeznach         devid = get_field(cmd->dword0, RISCV_IOMMU_CMD_ATS_RID);
146969a9ae48STomasz Jeznach     }
147069a9ae48STomasz Jeznach 
147169a9ae48STomasz Jeznach     pid = get_field(cmd->dword0, RISCV_IOMMU_CMD_ATS_PID);
147269a9ae48STomasz Jeznach 
147369a9ae48STomasz Jeznach     QLIST_FOREACH(as, &s->spaces, list) {
147469a9ae48STomasz Jeznach         if (as->devid == devid) {
147569a9ae48STomasz Jeznach             break;
147669a9ae48STomasz Jeznach         }
147769a9ae48STomasz Jeznach     }
147869a9ae48STomasz Jeznach 
147969a9ae48STomasz Jeznach     if (!as || !as->notifier) {
148069a9ae48STomasz Jeznach         return;
148169a9ae48STomasz Jeznach     }
148269a9ae48STomasz Jeznach 
148369a9ae48STomasz Jeznach     event.type = flag;
148469a9ae48STomasz Jeznach     event.entry.perm = perm;
148569a9ae48STomasz Jeznach     event.entry.target_as = s->target_as;
148669a9ae48STomasz Jeznach 
148769a9ae48STomasz Jeznach     IOMMU_NOTIFIER_FOREACH(n, &as->iova_mr) {
148869a9ae48STomasz Jeznach         if (!pv || n->iommu_idx == pid) {
148969a9ae48STomasz Jeznach             event.entry.iova = n->start;
149069a9ae48STomasz Jeznach             event.entry.addr_mask = n->end - n->start;
149169a9ae48STomasz Jeznach             trace_fn(as->iova_mr.parent_obj.name);
149269a9ae48STomasz Jeznach             memory_region_notify_iommu_one(n, &event);
149369a9ae48STomasz Jeznach         }
149469a9ae48STomasz Jeznach     }
149569a9ae48STomasz Jeznach }
149669a9ae48STomasz Jeznach 
149769a9ae48STomasz Jeznach static void riscv_iommu_ats_inval(RISCVIOMMUState *s,
149869a9ae48STomasz Jeznach     struct riscv_iommu_command *cmd)
149969a9ae48STomasz Jeznach {
150069a9ae48STomasz Jeznach     return riscv_iommu_ats(s, cmd, IOMMU_NOTIFIER_DEVIOTLB_UNMAP, IOMMU_NONE,
150169a9ae48STomasz Jeznach                            trace_riscv_iommu_ats_inval);
150269a9ae48STomasz Jeznach }
150369a9ae48STomasz Jeznach 
150469a9ae48STomasz Jeznach static void riscv_iommu_ats_prgr(RISCVIOMMUState *s,
150569a9ae48STomasz Jeznach     struct riscv_iommu_command *cmd)
150669a9ae48STomasz Jeznach {
150769a9ae48STomasz Jeznach     unsigned resp_code = get_field(cmd->dword1,
150869a9ae48STomasz Jeznach                                    RISCV_IOMMU_CMD_ATS_PRGR_RESP_CODE);
150969a9ae48STomasz Jeznach 
151069a9ae48STomasz Jeznach     /* Using the access flag to carry response code information */
151169a9ae48STomasz Jeznach     IOMMUAccessFlags perm = resp_code ? IOMMU_NONE : IOMMU_RW;
151269a9ae48STomasz Jeznach     return riscv_iommu_ats(s, cmd, IOMMU_NOTIFIER_MAP, perm,
151369a9ae48STomasz Jeznach                            trace_riscv_iommu_ats_prgr);
151469a9ae48STomasz Jeznach }
151569a9ae48STomasz Jeznach 
15160c54acb8STomasz Jeznach static void riscv_iommu_process_ddtp(RISCVIOMMUState *s)
15170c54acb8STomasz Jeznach {
15180c54acb8STomasz Jeznach     uint64_t old_ddtp = s->ddtp;
15190c54acb8STomasz Jeznach     uint64_t new_ddtp = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_DDTP);
15200c54acb8STomasz Jeznach     unsigned new_mode = get_field(new_ddtp, RISCV_IOMMU_DDTP_MODE);
15210c54acb8STomasz Jeznach     unsigned old_mode = get_field(old_ddtp, RISCV_IOMMU_DDTP_MODE);
15220c54acb8STomasz Jeznach     bool ok = false;
15230c54acb8STomasz Jeznach 
15240c54acb8STomasz Jeznach     /*
15250c54acb8STomasz Jeznach      * Check for allowed DDTP.MODE transitions:
15260c54acb8STomasz Jeznach      * {OFF, BARE}        -> {OFF, BARE, 1LVL, 2LVL, 3LVL}
15270c54acb8STomasz Jeznach      * {1LVL, 2LVL, 3LVL} -> {OFF, BARE}
15280c54acb8STomasz Jeznach      */
15290c54acb8STomasz Jeznach     if (new_mode == old_mode ||
15300c54acb8STomasz Jeznach         new_mode == RISCV_IOMMU_DDTP_MODE_OFF ||
15310c54acb8STomasz Jeznach         new_mode == RISCV_IOMMU_DDTP_MODE_BARE) {
15320c54acb8STomasz Jeznach         ok = true;
15330c54acb8STomasz Jeznach     } else if (new_mode == RISCV_IOMMU_DDTP_MODE_1LVL ||
15340c54acb8STomasz Jeznach                new_mode == RISCV_IOMMU_DDTP_MODE_2LVL ||
15350c54acb8STomasz Jeznach                new_mode == RISCV_IOMMU_DDTP_MODE_3LVL) {
15360c54acb8STomasz Jeznach         ok = old_mode == RISCV_IOMMU_DDTP_MODE_OFF ||
15370c54acb8STomasz Jeznach              old_mode == RISCV_IOMMU_DDTP_MODE_BARE;
15380c54acb8STomasz Jeznach     }
15390c54acb8STomasz Jeznach 
15400c54acb8STomasz Jeznach     if (ok) {
15410c54acb8STomasz Jeznach         /* clear reserved and busy bits, report back sanitized version */
15420c54acb8STomasz Jeznach         new_ddtp = set_field(new_ddtp & RISCV_IOMMU_DDTP_PPN,
15430c54acb8STomasz Jeznach                              RISCV_IOMMU_DDTP_MODE, new_mode);
15440c54acb8STomasz Jeznach     } else {
15450c54acb8STomasz Jeznach         new_ddtp = old_ddtp;
15460c54acb8STomasz Jeznach     }
15470c54acb8STomasz Jeznach     s->ddtp = new_ddtp;
15480c54acb8STomasz Jeznach 
15490c54acb8STomasz Jeznach     riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_DDTP, new_ddtp);
15500c54acb8STomasz Jeznach }
15510c54acb8STomasz Jeznach 
15520c54acb8STomasz Jeznach /* Command function and opcode field. */
15530c54acb8STomasz Jeznach #define RISCV_IOMMU_CMD(func, op) (((func) << 7) | (op))
15540c54acb8STomasz Jeznach 
15550c54acb8STomasz Jeznach static void riscv_iommu_process_cq_tail(RISCVIOMMUState *s)
15560c54acb8STomasz Jeznach {
15570c54acb8STomasz Jeznach     struct riscv_iommu_command cmd;
15580c54acb8STomasz Jeznach     MemTxResult res;
15590c54acb8STomasz Jeznach     dma_addr_t addr;
15600c54acb8STomasz Jeznach     uint32_t tail, head, ctrl;
15610c54acb8STomasz Jeznach     uint64_t cmd_opcode;
15620c54acb8STomasz Jeznach     GHFunc func;
15630c54acb8STomasz Jeznach 
15640c54acb8STomasz Jeznach     ctrl = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQCSR);
15650c54acb8STomasz Jeznach     tail = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQT) & s->cq_mask;
15660c54acb8STomasz Jeznach     head = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQH) & s->cq_mask;
15670c54acb8STomasz Jeznach 
15680c54acb8STomasz Jeznach     /* Check for pending error or queue processing disabled */
15690c54acb8STomasz Jeznach     if (!(ctrl & RISCV_IOMMU_CQCSR_CQON) ||
15700c54acb8STomasz Jeznach         !!(ctrl & (RISCV_IOMMU_CQCSR_CMD_ILL | RISCV_IOMMU_CQCSR_CQMF))) {
15710c54acb8STomasz Jeznach         return;
15720c54acb8STomasz Jeznach     }
15730c54acb8STomasz Jeznach 
15740c54acb8STomasz Jeznach     while (tail != head) {
15750c54acb8STomasz Jeznach         addr = s->cq_addr  + head * sizeof(cmd);
15760c54acb8STomasz Jeznach         res = dma_memory_read(s->target_as, addr, &cmd, sizeof(cmd),
15770c54acb8STomasz Jeznach                               MEMTXATTRS_UNSPECIFIED);
15780c54acb8STomasz Jeznach 
15790c54acb8STomasz Jeznach         if (res != MEMTX_OK) {
15800c54acb8STomasz Jeznach             riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR,
15810c54acb8STomasz Jeznach                                   RISCV_IOMMU_CQCSR_CQMF, 0);
15820c54acb8STomasz Jeznach             goto fault;
15830c54acb8STomasz Jeznach         }
15840c54acb8STomasz Jeznach 
15850c54acb8STomasz Jeznach         trace_riscv_iommu_cmd(s->parent_obj.id, cmd.dword0, cmd.dword1);
15860c54acb8STomasz Jeznach 
15870c54acb8STomasz Jeznach         cmd_opcode = get_field(cmd.dword0,
15880c54acb8STomasz Jeznach                                RISCV_IOMMU_CMD_OPCODE | RISCV_IOMMU_CMD_FUNC);
15890c54acb8STomasz Jeznach 
15900c54acb8STomasz Jeznach         switch (cmd_opcode) {
15910c54acb8STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IOFENCE_FUNC_C,
15920c54acb8STomasz Jeznach                              RISCV_IOMMU_CMD_IOFENCE_OPCODE):
15930c54acb8STomasz Jeznach             res = riscv_iommu_iofence(s,
15940c54acb8STomasz Jeznach                 cmd.dword0 & RISCV_IOMMU_CMD_IOFENCE_AV, cmd.dword1 << 2,
15950c54acb8STomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IOFENCE_DATA));
15960c54acb8STomasz Jeznach 
15970c54acb8STomasz Jeznach             if (res != MEMTX_OK) {
15980c54acb8STomasz Jeznach                 riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR,
15990c54acb8STomasz Jeznach                                       RISCV_IOMMU_CQCSR_CQMF, 0);
16000c54acb8STomasz Jeznach                 goto fault;
16010c54acb8STomasz Jeznach             }
16020c54acb8STomasz Jeznach             break;
16030c54acb8STomasz Jeznach 
16040c54acb8STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IOTINVAL_FUNC_GVMA,
16050c54acb8STomasz Jeznach                              RISCV_IOMMU_CMD_IOTINVAL_OPCODE):
16060c54acb8STomasz Jeznach             if (cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_PSCV) {
16070c54acb8STomasz Jeznach                 /* illegal command arguments IOTINVAL.GVMA & PSCV == 1 */
16080c54acb8STomasz Jeznach                 goto cmd_ill;
16099d085a1cSTomasz Jeznach             } else if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_GV)) {
16109d085a1cSTomasz Jeznach                 /* invalidate all cache mappings */
16119d085a1cSTomasz Jeznach                 func = riscv_iommu_iot_inval_all;
16129d085a1cSTomasz Jeznach             } else if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_AV)) {
16139d085a1cSTomasz Jeznach                 /* invalidate cache matching GSCID */
16149d085a1cSTomasz Jeznach                 func = riscv_iommu_iot_inval_gscid;
16159d085a1cSTomasz Jeznach             } else {
16169d085a1cSTomasz Jeznach                 /* invalidate cache matching GSCID and ADDR (GPA) */
16179d085a1cSTomasz Jeznach                 func = riscv_iommu_iot_inval_gscid_gpa;
16180c54acb8STomasz Jeznach             }
16199d085a1cSTomasz Jeznach             riscv_iommu_iot_inval(s, func,
16209d085a1cSTomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IOTINVAL_GSCID), 0,
16219d085a1cSTomasz Jeznach                 cmd.dword1 << 2 & TARGET_PAGE_MASK);
16220c54acb8STomasz Jeznach             break;
16230c54acb8STomasz Jeznach 
16240c54acb8STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IOTINVAL_FUNC_VMA,
16250c54acb8STomasz Jeznach                              RISCV_IOMMU_CMD_IOTINVAL_OPCODE):
16269d085a1cSTomasz Jeznach             if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_GV)) {
16279d085a1cSTomasz Jeznach                 /* invalidate all cache mappings, simplified model */
16289d085a1cSTomasz Jeznach                 func = riscv_iommu_iot_inval_all;
16299d085a1cSTomasz Jeznach             } else if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_PSCV)) {
16309d085a1cSTomasz Jeznach                 /* invalidate cache matching GSCID, simplified model */
16319d085a1cSTomasz Jeznach                 func = riscv_iommu_iot_inval_gscid;
16329d085a1cSTomasz Jeznach             } else if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_AV)) {
16339d085a1cSTomasz Jeznach                 /* invalidate cache matching GSCID and PSCID */
16349d085a1cSTomasz Jeznach                 func = riscv_iommu_iot_inval_pscid;
16359d085a1cSTomasz Jeznach             } else {
16369d085a1cSTomasz Jeznach                 /* invalidate cache matching GSCID and PSCID and ADDR (IOVA) */
16379d085a1cSTomasz Jeznach                 func = riscv_iommu_iot_inval_pscid_iova;
16389d085a1cSTomasz Jeznach             }
16399d085a1cSTomasz Jeznach             riscv_iommu_iot_inval(s, func,
16409d085a1cSTomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IOTINVAL_GSCID),
16419d085a1cSTomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IOTINVAL_PSCID),
16429d085a1cSTomasz Jeznach                 cmd.dword1 << 2 & TARGET_PAGE_MASK);
16430c54acb8STomasz Jeznach             break;
16440c54acb8STomasz Jeznach 
16450c54acb8STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IODIR_FUNC_INVAL_DDT,
16460c54acb8STomasz Jeznach                              RISCV_IOMMU_CMD_IODIR_OPCODE):
16470c54acb8STomasz Jeznach             if (!(cmd.dword0 & RISCV_IOMMU_CMD_IODIR_DV)) {
16480c54acb8STomasz Jeznach                 /* invalidate all device context cache mappings */
16490c54acb8STomasz Jeznach                 func = riscv_iommu_ctx_inval_all;
16500c54acb8STomasz Jeznach             } else {
16510c54acb8STomasz Jeznach                 /* invalidate all device context matching DID */
16520c54acb8STomasz Jeznach                 func = riscv_iommu_ctx_inval_devid;
16530c54acb8STomasz Jeznach             }
16540c54acb8STomasz Jeznach             riscv_iommu_ctx_inval(s, func,
16550c54acb8STomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IODIR_DID), 0);
16560c54acb8STomasz Jeznach             break;
16570c54acb8STomasz Jeznach 
16580c54acb8STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IODIR_FUNC_INVAL_PDT,
16590c54acb8STomasz Jeznach                              RISCV_IOMMU_CMD_IODIR_OPCODE):
16600c54acb8STomasz Jeznach             if (!(cmd.dword0 & RISCV_IOMMU_CMD_IODIR_DV)) {
16610c54acb8STomasz Jeznach                 /* illegal command arguments IODIR_PDT & DV == 0 */
16620c54acb8STomasz Jeznach                 goto cmd_ill;
16630c54acb8STomasz Jeznach             } else {
16640c54acb8STomasz Jeznach                 func = riscv_iommu_ctx_inval_devid_procid;
16650c54acb8STomasz Jeznach             }
16660c54acb8STomasz Jeznach             riscv_iommu_ctx_inval(s, func,
16670c54acb8STomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IODIR_DID),
16680c54acb8STomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IODIR_PID));
16690c54acb8STomasz Jeznach             break;
16700c54acb8STomasz Jeznach 
167169a9ae48STomasz Jeznach         /* ATS commands */
167269a9ae48STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_ATS_FUNC_INVAL,
167369a9ae48STomasz Jeznach                              RISCV_IOMMU_CMD_ATS_OPCODE):
167469a9ae48STomasz Jeznach             if (!s->enable_ats) {
167569a9ae48STomasz Jeznach                 goto cmd_ill;
167669a9ae48STomasz Jeznach             }
167769a9ae48STomasz Jeznach 
167869a9ae48STomasz Jeznach             riscv_iommu_ats_inval(s, &cmd);
167969a9ae48STomasz Jeznach             break;
168069a9ae48STomasz Jeznach 
168169a9ae48STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_ATS_FUNC_PRGR,
168269a9ae48STomasz Jeznach                              RISCV_IOMMU_CMD_ATS_OPCODE):
168369a9ae48STomasz Jeznach             if (!s->enable_ats) {
168469a9ae48STomasz Jeznach                 goto cmd_ill;
168569a9ae48STomasz Jeznach             }
168669a9ae48STomasz Jeznach 
168769a9ae48STomasz Jeznach             riscv_iommu_ats_prgr(s, &cmd);
168869a9ae48STomasz Jeznach             break;
168969a9ae48STomasz Jeznach 
16900c54acb8STomasz Jeznach         default:
16910c54acb8STomasz Jeznach         cmd_ill:
16920c54acb8STomasz Jeznach             /* Invalid instruction, do not advance instruction index. */
16930c54acb8STomasz Jeznach             riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR,
16940c54acb8STomasz Jeznach                 RISCV_IOMMU_CQCSR_CMD_ILL, 0);
16950c54acb8STomasz Jeznach             goto fault;
16960c54acb8STomasz Jeznach         }
16970c54acb8STomasz Jeznach 
16980c54acb8STomasz Jeznach         /* Advance and update head pointer after command completes. */
16990c54acb8STomasz Jeznach         head = (head + 1) & s->cq_mask;
17000c54acb8STomasz Jeznach         riscv_iommu_reg_set32(s, RISCV_IOMMU_REG_CQH, head);
17010c54acb8STomasz Jeznach     }
17020c54acb8STomasz Jeznach     return;
17030c54acb8STomasz Jeznach 
17040c54acb8STomasz Jeznach fault:
17050c54acb8STomasz Jeznach     if (ctrl & RISCV_IOMMU_CQCSR_CIE) {
17060c54acb8STomasz Jeznach         riscv_iommu_notify(s, RISCV_IOMMU_INTR_CQ);
17070c54acb8STomasz Jeznach     }
17080c54acb8STomasz Jeznach }
17090c54acb8STomasz Jeznach 
17100c54acb8STomasz Jeznach static void riscv_iommu_process_cq_control(RISCVIOMMUState *s)
17110c54acb8STomasz Jeznach {
17120c54acb8STomasz Jeznach     uint64_t base;
17130c54acb8STomasz Jeznach     uint32_t ctrl_set = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQCSR);
17140c54acb8STomasz Jeznach     uint32_t ctrl_clr;
17150c54acb8STomasz Jeznach     bool enable = !!(ctrl_set & RISCV_IOMMU_CQCSR_CQEN);
17160c54acb8STomasz Jeznach     bool active = !!(ctrl_set & RISCV_IOMMU_CQCSR_CQON);
17170c54acb8STomasz Jeznach 
17180c54acb8STomasz Jeznach     if (enable && !active) {
17190c54acb8STomasz Jeznach         base = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_CQB);
17200c54acb8STomasz Jeznach         s->cq_mask = (2ULL << get_field(base, RISCV_IOMMU_CQB_LOG2SZ)) - 1;
17210c54acb8STomasz Jeznach         s->cq_addr = PPN_PHYS(get_field(base, RISCV_IOMMU_CQB_PPN));
17220c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQT], ~s->cq_mask);
17230c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_CQH], 0);
17240c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_CQT], 0);
17250c54acb8STomasz Jeznach         ctrl_set = RISCV_IOMMU_CQCSR_CQON;
17260c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_CQCSR_BUSY | RISCV_IOMMU_CQCSR_CQMF |
17270c54acb8STomasz Jeznach                    RISCV_IOMMU_CQCSR_CMD_ILL | RISCV_IOMMU_CQCSR_CMD_TO |
17280c54acb8STomasz Jeznach                    RISCV_IOMMU_CQCSR_FENCE_W_IP;
17290c54acb8STomasz Jeznach     } else if (!enable && active) {
17300c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQT], ~0);
17310c54acb8STomasz Jeznach         ctrl_set = 0;
17320c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_CQCSR_BUSY | RISCV_IOMMU_CQCSR_CQON;
17330c54acb8STomasz Jeznach     } else {
17340c54acb8STomasz Jeznach         ctrl_set = 0;
17350c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_CQCSR_BUSY;
17360c54acb8STomasz Jeznach     }
17370c54acb8STomasz Jeznach 
17380c54acb8STomasz Jeznach     riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR, ctrl_set, ctrl_clr);
17390c54acb8STomasz Jeznach }
17400c54acb8STomasz Jeznach 
17410c54acb8STomasz Jeznach static void riscv_iommu_process_fq_control(RISCVIOMMUState *s)
17420c54acb8STomasz Jeznach {
17430c54acb8STomasz Jeznach     uint64_t base;
17440c54acb8STomasz Jeznach     uint32_t ctrl_set = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQCSR);
17450c54acb8STomasz Jeznach     uint32_t ctrl_clr;
17460c54acb8STomasz Jeznach     bool enable = !!(ctrl_set & RISCV_IOMMU_FQCSR_FQEN);
17470c54acb8STomasz Jeznach     bool active = !!(ctrl_set & RISCV_IOMMU_FQCSR_FQON);
17480c54acb8STomasz Jeznach 
17490c54acb8STomasz Jeznach     if (enable && !active) {
17500c54acb8STomasz Jeznach         base = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_FQB);
17510c54acb8STomasz Jeznach         s->fq_mask = (2ULL << get_field(base, RISCV_IOMMU_FQB_LOG2SZ)) - 1;
17520c54acb8STomasz Jeznach         s->fq_addr = PPN_PHYS(get_field(base, RISCV_IOMMU_FQB_PPN));
17530c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQH], ~s->fq_mask);
17540c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_FQH], 0);
17550c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_FQT], 0);
17560c54acb8STomasz Jeznach         ctrl_set = RISCV_IOMMU_FQCSR_FQON;
17570c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_FQCSR_BUSY | RISCV_IOMMU_FQCSR_FQMF |
17580c54acb8STomasz Jeznach             RISCV_IOMMU_FQCSR_FQOF;
17590c54acb8STomasz Jeznach     } else if (!enable && active) {
17600c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQH], ~0);
17610c54acb8STomasz Jeznach         ctrl_set = 0;
17620c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_FQCSR_BUSY | RISCV_IOMMU_FQCSR_FQON;
17630c54acb8STomasz Jeznach     } else {
17640c54acb8STomasz Jeznach         ctrl_set = 0;
17650c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_FQCSR_BUSY;
17660c54acb8STomasz Jeznach     }
17670c54acb8STomasz Jeznach 
17680c54acb8STomasz Jeznach     riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_FQCSR, ctrl_set, ctrl_clr);
17690c54acb8STomasz Jeznach }
17700c54acb8STomasz Jeznach 
17710c54acb8STomasz Jeznach static void riscv_iommu_process_pq_control(RISCVIOMMUState *s)
17720c54acb8STomasz Jeznach {
17730c54acb8STomasz Jeznach     uint64_t base;
17740c54acb8STomasz Jeznach     uint32_t ctrl_set = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_PQCSR);
17750c54acb8STomasz Jeznach     uint32_t ctrl_clr;
17760c54acb8STomasz Jeznach     bool enable = !!(ctrl_set & RISCV_IOMMU_PQCSR_PQEN);
17770c54acb8STomasz Jeznach     bool active = !!(ctrl_set & RISCV_IOMMU_PQCSR_PQON);
17780c54acb8STomasz Jeznach 
17790c54acb8STomasz Jeznach     if (enable && !active) {
17800c54acb8STomasz Jeznach         base = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_PQB);
17810c54acb8STomasz Jeznach         s->pq_mask = (2ULL << get_field(base, RISCV_IOMMU_PQB_LOG2SZ)) - 1;
17820c54acb8STomasz Jeznach         s->pq_addr = PPN_PHYS(get_field(base, RISCV_IOMMU_PQB_PPN));
17830c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQH], ~s->pq_mask);
17840c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_PQH], 0);
17850c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_PQT], 0);
17860c54acb8STomasz Jeznach         ctrl_set = RISCV_IOMMU_PQCSR_PQON;
17870c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_PQCSR_BUSY | RISCV_IOMMU_PQCSR_PQMF |
17880c54acb8STomasz Jeznach             RISCV_IOMMU_PQCSR_PQOF;
17890c54acb8STomasz Jeznach     } else if (!enable && active) {
17900c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQH], ~0);
17910c54acb8STomasz Jeznach         ctrl_set = 0;
17920c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_PQCSR_BUSY | RISCV_IOMMU_PQCSR_PQON;
17930c54acb8STomasz Jeznach     } else {
17940c54acb8STomasz Jeznach         ctrl_set = 0;
17950c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_PQCSR_BUSY;
17960c54acb8STomasz Jeznach     }
17970c54acb8STomasz Jeznach 
17980c54acb8STomasz Jeznach     riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_PQCSR, ctrl_set, ctrl_clr);
17990c54acb8STomasz Jeznach }
18000c54acb8STomasz Jeznach 
1801a7aa525bSTomasz Jeznach static void riscv_iommu_process_dbg(RISCVIOMMUState *s)
1802a7aa525bSTomasz Jeznach {
1803a7aa525bSTomasz Jeznach     uint64_t iova = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_TR_REQ_IOVA);
1804a7aa525bSTomasz Jeznach     uint64_t ctrl = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_TR_REQ_CTL);
1805a7aa525bSTomasz Jeznach     unsigned devid = get_field(ctrl, RISCV_IOMMU_TR_REQ_CTL_DID);
1806a7aa525bSTomasz Jeznach     unsigned pid = get_field(ctrl, RISCV_IOMMU_TR_REQ_CTL_PID);
1807a7aa525bSTomasz Jeznach     RISCVIOMMUContext *ctx;
1808a7aa525bSTomasz Jeznach     void *ref;
1809a7aa525bSTomasz Jeznach 
1810a7aa525bSTomasz Jeznach     if (!(ctrl & RISCV_IOMMU_TR_REQ_CTL_GO_BUSY)) {
1811a7aa525bSTomasz Jeznach         return;
1812a7aa525bSTomasz Jeznach     }
1813a7aa525bSTomasz Jeznach 
1814a7aa525bSTomasz Jeznach     ctx = riscv_iommu_ctx(s, devid, pid, &ref);
1815a7aa525bSTomasz Jeznach     if (ctx == NULL) {
1816a7aa525bSTomasz Jeznach         riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_TR_RESPONSE,
1817a7aa525bSTomasz Jeznach                                  RISCV_IOMMU_TR_RESPONSE_FAULT |
1818a7aa525bSTomasz Jeznach                                  (RISCV_IOMMU_FQ_CAUSE_DMA_DISABLED << 10));
1819a7aa525bSTomasz Jeznach     } else {
1820a7aa525bSTomasz Jeznach         IOMMUTLBEntry iotlb = {
1821a7aa525bSTomasz Jeznach             .iova = iova,
1822a7aa525bSTomasz Jeznach             .perm = ctrl & RISCV_IOMMU_TR_REQ_CTL_NW ? IOMMU_RO : IOMMU_RW,
1823a7aa525bSTomasz Jeznach             .addr_mask = ~0,
1824a7aa525bSTomasz Jeznach             .target_as = NULL,
1825a7aa525bSTomasz Jeznach         };
1826a7aa525bSTomasz Jeznach         int fault = riscv_iommu_translate(s, ctx, &iotlb, false);
1827a7aa525bSTomasz Jeznach         if (fault) {
1828a7aa525bSTomasz Jeznach             iova = RISCV_IOMMU_TR_RESPONSE_FAULT | (((uint64_t) fault) << 10);
1829a7aa525bSTomasz Jeznach         } else {
1830a7aa525bSTomasz Jeznach             iova = iotlb.translated_addr & ~iotlb.addr_mask;
1831a7aa525bSTomasz Jeznach             iova >>= TARGET_PAGE_BITS;
1832a7aa525bSTomasz Jeznach             iova &= RISCV_IOMMU_TR_RESPONSE_PPN;
1833a7aa525bSTomasz Jeznach 
1834a7aa525bSTomasz Jeznach             /* We do not support superpages (> 4kbs) for now */
1835a7aa525bSTomasz Jeznach             iova &= ~RISCV_IOMMU_TR_RESPONSE_S;
1836a7aa525bSTomasz Jeznach         }
1837a7aa525bSTomasz Jeznach         riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_TR_RESPONSE, iova);
1838a7aa525bSTomasz Jeznach     }
1839a7aa525bSTomasz Jeznach 
1840a7aa525bSTomasz Jeznach     riscv_iommu_reg_mod64(s, RISCV_IOMMU_REG_TR_REQ_CTL, 0,
1841a7aa525bSTomasz Jeznach         RISCV_IOMMU_TR_REQ_CTL_GO_BUSY);
1842a7aa525bSTomasz Jeznach     riscv_iommu_ctx_put(s, ref);
1843a7aa525bSTomasz Jeznach }
1844a7aa525bSTomasz Jeznach 
18450c54acb8STomasz Jeznach typedef void riscv_iommu_process_fn(RISCVIOMMUState *s);
18460c54acb8STomasz Jeznach 
18470c54acb8STomasz Jeznach static void riscv_iommu_update_icvec(RISCVIOMMUState *s, uint64_t data)
18480c54acb8STomasz Jeznach {
18490c54acb8STomasz Jeznach     uint64_t icvec = 0;
18500c54acb8STomasz Jeznach 
18510c54acb8STomasz Jeznach     icvec |= MIN(data & RISCV_IOMMU_ICVEC_CIV,
18520c54acb8STomasz Jeznach                  s->icvec_avail_vectors & RISCV_IOMMU_ICVEC_CIV);
18530c54acb8STomasz Jeznach 
18540c54acb8STomasz Jeznach     icvec |= MIN(data & RISCV_IOMMU_ICVEC_FIV,
18550c54acb8STomasz Jeznach                  s->icvec_avail_vectors & RISCV_IOMMU_ICVEC_FIV);
18560c54acb8STomasz Jeznach 
18570c54acb8STomasz Jeznach     icvec |= MIN(data & RISCV_IOMMU_ICVEC_PMIV,
18580c54acb8STomasz Jeznach                  s->icvec_avail_vectors & RISCV_IOMMU_ICVEC_PMIV);
18590c54acb8STomasz Jeznach 
18600c54acb8STomasz Jeznach     icvec |= MIN(data & RISCV_IOMMU_ICVEC_PIV,
18610c54acb8STomasz Jeznach                  s->icvec_avail_vectors & RISCV_IOMMU_ICVEC_PIV);
18620c54acb8STomasz Jeznach 
18630c54acb8STomasz Jeznach     trace_riscv_iommu_icvec_write(data, icvec);
18640c54acb8STomasz Jeznach 
18650c54acb8STomasz Jeznach     riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_ICVEC, icvec);
18660c54acb8STomasz Jeznach }
18670c54acb8STomasz Jeznach 
18680c54acb8STomasz Jeznach static void riscv_iommu_update_ipsr(RISCVIOMMUState *s, uint64_t data)
18690c54acb8STomasz Jeznach {
18700c54acb8STomasz Jeznach     uint32_t cqcsr, fqcsr, pqcsr;
18710c54acb8STomasz Jeznach     uint32_t ipsr_set = 0;
18720c54acb8STomasz Jeznach     uint32_t ipsr_clr = 0;
18730c54acb8STomasz Jeznach 
18740c54acb8STomasz Jeznach     if (data & RISCV_IOMMU_IPSR_CIP) {
18750c54acb8STomasz Jeznach         cqcsr = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQCSR);
18760c54acb8STomasz Jeznach 
18770c54acb8STomasz Jeznach         if (cqcsr & RISCV_IOMMU_CQCSR_CIE &&
18780c54acb8STomasz Jeznach             (cqcsr & RISCV_IOMMU_CQCSR_FENCE_W_IP ||
18790c54acb8STomasz Jeznach              cqcsr & RISCV_IOMMU_CQCSR_CMD_ILL ||
18800c54acb8STomasz Jeznach              cqcsr & RISCV_IOMMU_CQCSR_CMD_TO ||
18810c54acb8STomasz Jeznach              cqcsr & RISCV_IOMMU_CQCSR_CQMF)) {
18820c54acb8STomasz Jeznach             ipsr_set |= RISCV_IOMMU_IPSR_CIP;
18830c54acb8STomasz Jeznach         } else {
18840c54acb8STomasz Jeznach             ipsr_clr |= RISCV_IOMMU_IPSR_CIP;
18850c54acb8STomasz Jeznach         }
18860c54acb8STomasz Jeznach     } else {
18870c54acb8STomasz Jeznach         ipsr_clr |= RISCV_IOMMU_IPSR_CIP;
18880c54acb8STomasz Jeznach     }
18890c54acb8STomasz Jeznach 
18900c54acb8STomasz Jeznach     if (data & RISCV_IOMMU_IPSR_FIP) {
18910c54acb8STomasz Jeznach         fqcsr = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQCSR);
18920c54acb8STomasz Jeznach 
18930c54acb8STomasz Jeznach         if (fqcsr & RISCV_IOMMU_FQCSR_FIE &&
18940c54acb8STomasz Jeznach             (fqcsr & RISCV_IOMMU_FQCSR_FQOF ||
18950c54acb8STomasz Jeznach              fqcsr & RISCV_IOMMU_FQCSR_FQMF)) {
18960c54acb8STomasz Jeznach             ipsr_set |= RISCV_IOMMU_IPSR_FIP;
18970c54acb8STomasz Jeznach         } else {
18980c54acb8STomasz Jeznach             ipsr_clr |= RISCV_IOMMU_IPSR_FIP;
18990c54acb8STomasz Jeznach         }
19000c54acb8STomasz Jeznach     } else {
19010c54acb8STomasz Jeznach         ipsr_clr |= RISCV_IOMMU_IPSR_FIP;
19020c54acb8STomasz Jeznach     }
19030c54acb8STomasz Jeznach 
19040c54acb8STomasz Jeznach     if (data & RISCV_IOMMU_IPSR_PIP) {
19050c54acb8STomasz Jeznach         pqcsr = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_PQCSR);
19060c54acb8STomasz Jeznach 
19070c54acb8STomasz Jeznach         if (pqcsr & RISCV_IOMMU_PQCSR_PIE &&
19080c54acb8STomasz Jeznach             (pqcsr & RISCV_IOMMU_PQCSR_PQOF ||
19090c54acb8STomasz Jeznach              pqcsr & RISCV_IOMMU_PQCSR_PQMF)) {
19100c54acb8STomasz Jeznach             ipsr_set |= RISCV_IOMMU_IPSR_PIP;
19110c54acb8STomasz Jeznach         } else {
19120c54acb8STomasz Jeznach             ipsr_clr |= RISCV_IOMMU_IPSR_PIP;
19130c54acb8STomasz Jeznach         }
19140c54acb8STomasz Jeznach     } else {
19150c54acb8STomasz Jeznach         ipsr_clr |= RISCV_IOMMU_IPSR_PIP;
19160c54acb8STomasz Jeznach     }
19170c54acb8STomasz Jeznach 
19180c54acb8STomasz Jeznach     riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_IPSR, ipsr_set, ipsr_clr);
19190c54acb8STomasz Jeznach }
19200c54acb8STomasz Jeznach 
19210c54acb8STomasz Jeznach /*
19220c54acb8STomasz Jeznach  * Write the resulting value of 'data' for the reg specified
19230c54acb8STomasz Jeznach  * by 'reg_addr', after considering read-only/read-write/write-clear
19240c54acb8STomasz Jeznach  * bits, in the pointer 'dest'.
19250c54acb8STomasz Jeznach  *
19260c54acb8STomasz Jeznach  * The result is written in little-endian.
19270c54acb8STomasz Jeznach  */
19280c54acb8STomasz Jeznach static void riscv_iommu_write_reg_val(RISCVIOMMUState *s,
19290c54acb8STomasz Jeznach                                       void *dest, hwaddr reg_addr,
19300c54acb8STomasz Jeznach                                       int size, uint64_t data)
19310c54acb8STomasz Jeznach {
19320c54acb8STomasz Jeznach     uint64_t ro = ldn_le_p(&s->regs_ro[reg_addr], size);
19330c54acb8STomasz Jeznach     uint64_t wc = ldn_le_p(&s->regs_wc[reg_addr], size);
19340c54acb8STomasz Jeznach     uint64_t rw = ldn_le_p(&s->regs_rw[reg_addr], size);
19350c54acb8STomasz Jeznach 
19360c54acb8STomasz Jeznach     stn_le_p(dest, size, ((rw & ro) | (data & ~ro)) & ~(data & wc));
19370c54acb8STomasz Jeznach }
19380c54acb8STomasz Jeznach 
19390c54acb8STomasz Jeznach static MemTxResult riscv_iommu_mmio_write(void *opaque, hwaddr addr,
19400c54acb8STomasz Jeznach                                           uint64_t data, unsigned size,
19410c54acb8STomasz Jeznach                                           MemTxAttrs attrs)
19420c54acb8STomasz Jeznach {
19430c54acb8STomasz Jeznach     riscv_iommu_process_fn *process_fn = NULL;
19440c54acb8STomasz Jeznach     RISCVIOMMUState *s = opaque;
19450c54acb8STomasz Jeznach     uint32_t regb = addr & ~3;
19460c54acb8STomasz Jeznach     uint32_t busy = 0;
19470c54acb8STomasz Jeznach     uint64_t val = 0;
19480c54acb8STomasz Jeznach 
19490c54acb8STomasz Jeznach     if ((addr & (size - 1)) != 0) {
19500c54acb8STomasz Jeznach         /* Unsupported MMIO alignment or access size */
19510c54acb8STomasz Jeznach         return MEMTX_ERROR;
19520c54acb8STomasz Jeznach     }
19530c54acb8STomasz Jeznach 
19540c54acb8STomasz Jeznach     if (addr + size > RISCV_IOMMU_REG_MSI_CONFIG) {
19550c54acb8STomasz Jeznach         /* Unsupported MMIO access location. */
19560c54acb8STomasz Jeznach         return MEMTX_ACCESS_ERROR;
19570c54acb8STomasz Jeznach     }
19580c54acb8STomasz Jeznach 
19590c54acb8STomasz Jeznach     /* Track actionable MMIO write. */
19600c54acb8STomasz Jeznach     switch (regb) {
19610c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_DDTP:
19620c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_DDTP + 4:
19630c54acb8STomasz Jeznach         process_fn = riscv_iommu_process_ddtp;
19640c54acb8STomasz Jeznach         regb = RISCV_IOMMU_REG_DDTP;
19650c54acb8STomasz Jeznach         busy = RISCV_IOMMU_DDTP_BUSY;
19660c54acb8STomasz Jeznach         break;
19670c54acb8STomasz Jeznach 
19680c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_CQT:
19690c54acb8STomasz Jeznach         process_fn = riscv_iommu_process_cq_tail;
19700c54acb8STomasz Jeznach         break;
19710c54acb8STomasz Jeznach 
19720c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_CQCSR:
19730c54acb8STomasz Jeznach         process_fn = riscv_iommu_process_cq_control;
19740c54acb8STomasz Jeznach         busy = RISCV_IOMMU_CQCSR_BUSY;
19750c54acb8STomasz Jeznach         break;
19760c54acb8STomasz Jeznach 
19770c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_FQCSR:
19780c54acb8STomasz Jeznach         process_fn = riscv_iommu_process_fq_control;
19790c54acb8STomasz Jeznach         busy = RISCV_IOMMU_FQCSR_BUSY;
19800c54acb8STomasz Jeznach         break;
19810c54acb8STomasz Jeznach 
19820c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_PQCSR:
19830c54acb8STomasz Jeznach         process_fn = riscv_iommu_process_pq_control;
19840c54acb8STomasz Jeznach         busy = RISCV_IOMMU_PQCSR_BUSY;
19850c54acb8STomasz Jeznach         break;
19860c54acb8STomasz Jeznach 
19870c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_ICVEC:
19880c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_IPSR:
19890c54acb8STomasz Jeznach         /*
19900c54acb8STomasz Jeznach          * ICVEC and IPSR have special read/write procedures. We'll
19910c54acb8STomasz Jeznach          * call their respective helpers and exit.
19920c54acb8STomasz Jeznach          */
19930c54acb8STomasz Jeznach         riscv_iommu_write_reg_val(s, &val, addr, size, data);
19940c54acb8STomasz Jeznach 
19950c54acb8STomasz Jeznach         /*
19960c54acb8STomasz Jeznach          * 'val' is stored as LE. Switch to host endianess
19970c54acb8STomasz Jeznach          * before using it.
19980c54acb8STomasz Jeznach          */
19990c54acb8STomasz Jeznach         val = le64_to_cpu(val);
20000c54acb8STomasz Jeznach 
20010c54acb8STomasz Jeznach         if (regb == RISCV_IOMMU_REG_ICVEC) {
20020c54acb8STomasz Jeznach             riscv_iommu_update_icvec(s, val);
20030c54acb8STomasz Jeznach         } else {
20040c54acb8STomasz Jeznach             riscv_iommu_update_ipsr(s, val);
20050c54acb8STomasz Jeznach         }
20060c54acb8STomasz Jeznach 
20070c54acb8STomasz Jeznach         return MEMTX_OK;
20080c54acb8STomasz Jeznach 
2009a7aa525bSTomasz Jeznach     case RISCV_IOMMU_REG_TR_REQ_CTL:
2010a7aa525bSTomasz Jeznach         process_fn = riscv_iommu_process_dbg;
2011a7aa525bSTomasz Jeznach         regb = RISCV_IOMMU_REG_TR_REQ_CTL;
2012a7aa525bSTomasz Jeznach         busy = RISCV_IOMMU_TR_REQ_CTL_GO_BUSY;
2013a7aa525bSTomasz Jeznach         break;
2014a7aa525bSTomasz Jeznach 
20150c54acb8STomasz Jeznach     default:
20160c54acb8STomasz Jeznach         break;
20170c54acb8STomasz Jeznach     }
20180c54acb8STomasz Jeznach 
20190c54acb8STomasz Jeznach     /*
20200c54acb8STomasz Jeznach      * Registers update might be not synchronized with core logic.
20210c54acb8STomasz Jeznach      * If system software updates register when relevant BUSY bit
20220c54acb8STomasz Jeznach      * is set IOMMU behavior of additional writes to the register
20230c54acb8STomasz Jeznach      * is UNSPECIFIED.
20240c54acb8STomasz Jeznach      */
20250c54acb8STomasz Jeznach     riscv_iommu_write_reg_val(s, &s->regs_rw[addr], addr, size, data);
20260c54acb8STomasz Jeznach 
20270c54acb8STomasz Jeznach     /* Busy flag update, MSB 4-byte register. */
20280c54acb8STomasz Jeznach     if (busy) {
20290c54acb8STomasz Jeznach         uint32_t rw = ldl_le_p(&s->regs_rw[regb]);
20300c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[regb], rw | busy);
20310c54acb8STomasz Jeznach     }
20320c54acb8STomasz Jeznach 
20330c54acb8STomasz Jeznach     if (process_fn) {
20340c54acb8STomasz Jeznach         process_fn(s);
20350c54acb8STomasz Jeznach     }
20360c54acb8STomasz Jeznach 
20370c54acb8STomasz Jeznach     return MEMTX_OK;
20380c54acb8STomasz Jeznach }
20390c54acb8STomasz Jeznach 
20400c54acb8STomasz Jeznach static MemTxResult riscv_iommu_mmio_read(void *opaque, hwaddr addr,
20410c54acb8STomasz Jeznach     uint64_t *data, unsigned size, MemTxAttrs attrs)
20420c54acb8STomasz Jeznach {
20430c54acb8STomasz Jeznach     RISCVIOMMUState *s = opaque;
20440c54acb8STomasz Jeznach     uint64_t val = -1;
20450c54acb8STomasz Jeznach     uint8_t *ptr;
20460c54acb8STomasz Jeznach 
20470c54acb8STomasz Jeznach     if ((addr & (size - 1)) != 0) {
20480c54acb8STomasz Jeznach         /* Unsupported MMIO alignment. */
20490c54acb8STomasz Jeznach         return MEMTX_ERROR;
20500c54acb8STomasz Jeznach     }
20510c54acb8STomasz Jeznach 
20520c54acb8STomasz Jeznach     if (addr + size > RISCV_IOMMU_REG_MSI_CONFIG) {
20530c54acb8STomasz Jeznach         return MEMTX_ACCESS_ERROR;
20540c54acb8STomasz Jeznach     }
20550c54acb8STomasz Jeznach 
20560c54acb8STomasz Jeznach     ptr = &s->regs_rw[addr];
20570c54acb8STomasz Jeznach     val = ldn_le_p(ptr, size);
20580c54acb8STomasz Jeznach 
20590c54acb8STomasz Jeznach     *data = val;
20600c54acb8STomasz Jeznach 
20610c54acb8STomasz Jeznach     return MEMTX_OK;
20620c54acb8STomasz Jeznach }
20630c54acb8STomasz Jeznach 
20640c54acb8STomasz Jeznach static const MemoryRegionOps riscv_iommu_mmio_ops = {
20650c54acb8STomasz Jeznach     .read_with_attrs = riscv_iommu_mmio_read,
20660c54acb8STomasz Jeznach     .write_with_attrs = riscv_iommu_mmio_write,
20670c54acb8STomasz Jeznach     .endianness = DEVICE_NATIVE_ENDIAN,
20680c54acb8STomasz Jeznach     .impl = {
20690c54acb8STomasz Jeznach         .min_access_size = 4,
20700c54acb8STomasz Jeznach         .max_access_size = 8,
20710c54acb8STomasz Jeznach         .unaligned = false,
20720c54acb8STomasz Jeznach     },
20730c54acb8STomasz Jeznach     .valid = {
20740c54acb8STomasz Jeznach         .min_access_size = 4,
20750c54acb8STomasz Jeznach         .max_access_size = 8,
20760c54acb8STomasz Jeznach     }
20770c54acb8STomasz Jeznach };
20780c54acb8STomasz Jeznach 
20790c54acb8STomasz Jeznach /*
20800c54acb8STomasz Jeznach  * Translations matching MSI pattern check are redirected to "riscv-iommu-trap"
20810c54acb8STomasz Jeznach  * memory region as untranslated address, for additional MSI/MRIF interception
20820c54acb8STomasz Jeznach  * by IOMMU interrupt remapping implementation.
20830c54acb8STomasz Jeznach  * Note: Device emulation code generating an MSI is expected to provide a valid
20840c54acb8STomasz Jeznach  * memory transaction attributes with requested_id set.
20850c54acb8STomasz Jeznach  */
20860c54acb8STomasz Jeznach static MemTxResult riscv_iommu_trap_write(void *opaque, hwaddr addr,
20870c54acb8STomasz Jeznach     uint64_t data, unsigned size, MemTxAttrs attrs)
20880c54acb8STomasz Jeznach {
20890c54acb8STomasz Jeznach     RISCVIOMMUState* s = (RISCVIOMMUState *)opaque;
20900c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx;
20910c54acb8STomasz Jeznach     MemTxResult res;
20920c54acb8STomasz Jeznach     void *ref;
20930c54acb8STomasz Jeznach     uint32_t devid = attrs.requester_id;
20940c54acb8STomasz Jeznach 
20950c54acb8STomasz Jeznach     if (attrs.unspecified) {
20960c54acb8STomasz Jeznach         return MEMTX_ACCESS_ERROR;
20970c54acb8STomasz Jeznach     }
20980c54acb8STomasz Jeznach 
20990c54acb8STomasz Jeznach     /* FIXME: PCIe bus remapping for attached endpoints. */
21000c54acb8STomasz Jeznach     devid |= s->bus << 8;
21010c54acb8STomasz Jeznach 
21020c54acb8STomasz Jeznach     ctx = riscv_iommu_ctx(s, devid, 0, &ref);
21030c54acb8STomasz Jeznach     if (ctx == NULL) {
21040c54acb8STomasz Jeznach         res = MEMTX_ACCESS_ERROR;
21050c54acb8STomasz Jeznach     } else {
21060c54acb8STomasz Jeznach         res = riscv_iommu_msi_write(s, ctx, addr, data, size, attrs);
21070c54acb8STomasz Jeznach     }
21080c54acb8STomasz Jeznach     riscv_iommu_ctx_put(s, ref);
21090c54acb8STomasz Jeznach     return res;
21100c54acb8STomasz Jeznach }
21110c54acb8STomasz Jeznach 
21120c54acb8STomasz Jeznach static MemTxResult riscv_iommu_trap_read(void *opaque, hwaddr addr,
21130c54acb8STomasz Jeznach     uint64_t *data, unsigned size, MemTxAttrs attrs)
21140c54acb8STomasz Jeznach {
21150c54acb8STomasz Jeznach     return MEMTX_ACCESS_ERROR;
21160c54acb8STomasz Jeznach }
21170c54acb8STomasz Jeznach 
21180c54acb8STomasz Jeznach static const MemoryRegionOps riscv_iommu_trap_ops = {
21190c54acb8STomasz Jeznach     .read_with_attrs = riscv_iommu_trap_read,
21200c54acb8STomasz Jeznach     .write_with_attrs = riscv_iommu_trap_write,
21210c54acb8STomasz Jeznach     .endianness = DEVICE_LITTLE_ENDIAN,
21220c54acb8STomasz Jeznach     .impl = {
21230c54acb8STomasz Jeznach         .min_access_size = 4,
21240c54acb8STomasz Jeznach         .max_access_size = 8,
21250c54acb8STomasz Jeznach         .unaligned = true,
21260c54acb8STomasz Jeznach     },
21270c54acb8STomasz Jeznach     .valid = {
21280c54acb8STomasz Jeznach         .min_access_size = 4,
21290c54acb8STomasz Jeznach         .max_access_size = 8,
21300c54acb8STomasz Jeznach     }
21310c54acb8STomasz Jeznach };
21320c54acb8STomasz Jeznach 
2133*4876e6f7SDaniel Henrique Barboza static void riscv_iommu_instance_init(Object *obj)
2134*4876e6f7SDaniel Henrique Barboza {
2135*4876e6f7SDaniel Henrique Barboza     RISCVIOMMUState *s = RISCV_IOMMU(obj);
2136*4876e6f7SDaniel Henrique Barboza 
2137*4876e6f7SDaniel Henrique Barboza     /* Enable translation debug interface */
2138*4876e6f7SDaniel Henrique Barboza     s->cap = RISCV_IOMMU_CAP_DBG;
2139*4876e6f7SDaniel Henrique Barboza 
2140*4876e6f7SDaniel Henrique Barboza     /* Report QEMU target physical address space limits */
2141*4876e6f7SDaniel Henrique Barboza     s->cap = set_field(s->cap, RISCV_IOMMU_CAP_PAS,
2142*4876e6f7SDaniel Henrique Barboza                        TARGET_PHYS_ADDR_SPACE_BITS);
2143*4876e6f7SDaniel Henrique Barboza 
2144*4876e6f7SDaniel Henrique Barboza     /* TODO: method to report supported PID bits */
2145*4876e6f7SDaniel Henrique Barboza     s->pid_bits = 8; /* restricted to size of MemTxAttrs.pid */
2146*4876e6f7SDaniel Henrique Barboza     s->cap |= RISCV_IOMMU_CAP_PD8;
2147*4876e6f7SDaniel Henrique Barboza 
2148*4876e6f7SDaniel Henrique Barboza     /* register storage */
2149*4876e6f7SDaniel Henrique Barboza     s->regs_rw = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE);
2150*4876e6f7SDaniel Henrique Barboza     s->regs_ro = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE);
2151*4876e6f7SDaniel Henrique Barboza     s->regs_wc = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE);
2152*4876e6f7SDaniel Henrique Barboza 
2153*4876e6f7SDaniel Henrique Barboza      /* Mark all registers read-only */
2154*4876e6f7SDaniel Henrique Barboza     memset(s->regs_ro, 0xff, RISCV_IOMMU_REG_SIZE);
2155*4876e6f7SDaniel Henrique Barboza 
2156*4876e6f7SDaniel Henrique Barboza     /* Device translation context cache */
2157*4876e6f7SDaniel Henrique Barboza     s->ctx_cache = g_hash_table_new_full(riscv_iommu_ctx_hash,
2158*4876e6f7SDaniel Henrique Barboza                                          riscv_iommu_ctx_equal,
2159*4876e6f7SDaniel Henrique Barboza                                          g_free, NULL);
2160*4876e6f7SDaniel Henrique Barboza 
2161*4876e6f7SDaniel Henrique Barboza     s->iot_cache = g_hash_table_new_full(riscv_iommu_iot_hash,
2162*4876e6f7SDaniel Henrique Barboza                                          riscv_iommu_iot_equal,
2163*4876e6f7SDaniel Henrique Barboza                                          g_free, NULL);
2164*4876e6f7SDaniel Henrique Barboza 
2165*4876e6f7SDaniel Henrique Barboza     s->iommus.le_next = NULL;
2166*4876e6f7SDaniel Henrique Barboza     s->iommus.le_prev = NULL;
2167*4876e6f7SDaniel Henrique Barboza     QLIST_INIT(&s->spaces);
2168*4876e6f7SDaniel Henrique Barboza }
2169*4876e6f7SDaniel Henrique Barboza 
21700c54acb8STomasz Jeznach static void riscv_iommu_realize(DeviceState *dev, Error **errp)
21710c54acb8STomasz Jeznach {
21720c54acb8STomasz Jeznach     RISCVIOMMUState *s = RISCV_IOMMU(dev);
21730c54acb8STomasz Jeznach 
2174*4876e6f7SDaniel Henrique Barboza     s->cap |= s->version & RISCV_IOMMU_CAP_VERSION;
21750c54acb8STomasz Jeznach     if (s->enable_msi) {
21760c54acb8STomasz Jeznach         s->cap |= RISCV_IOMMU_CAP_MSI_FLAT | RISCV_IOMMU_CAP_MSI_MRIF;
21770c54acb8STomasz Jeznach     }
217869a9ae48STomasz Jeznach     if (s->enable_ats) {
217969a9ae48STomasz Jeznach         s->cap |= RISCV_IOMMU_CAP_ATS;
218069a9ae48STomasz Jeznach     }
21810c54acb8STomasz Jeznach     if (s->enable_s_stage) {
21820c54acb8STomasz Jeznach         s->cap |= RISCV_IOMMU_CAP_SV32 | RISCV_IOMMU_CAP_SV39 |
21830c54acb8STomasz Jeznach                   RISCV_IOMMU_CAP_SV48 | RISCV_IOMMU_CAP_SV57;
21840c54acb8STomasz Jeznach     }
21850c54acb8STomasz Jeznach     if (s->enable_g_stage) {
21860c54acb8STomasz Jeznach         s->cap |= RISCV_IOMMU_CAP_SV32X4 | RISCV_IOMMU_CAP_SV39X4 |
21870c54acb8STomasz Jeznach                   RISCV_IOMMU_CAP_SV48X4 | RISCV_IOMMU_CAP_SV57X4;
21880c54acb8STomasz Jeznach     }
21890c54acb8STomasz Jeznach 
21900c54acb8STomasz Jeznach     /* Out-of-reset translation mode: OFF (DMA disabled) BARE (passthrough) */
21910c54acb8STomasz Jeznach     s->ddtp = set_field(0, RISCV_IOMMU_DDTP_MODE, s->enable_off ?
21920c54acb8STomasz Jeznach                         RISCV_IOMMU_DDTP_MODE_OFF : RISCV_IOMMU_DDTP_MODE_BARE);
21930c54acb8STomasz Jeznach 
21940c54acb8STomasz Jeznach     /*
21950c54acb8STomasz Jeznach      * Register complete MMIO space, including MSI/PBA registers.
21960c54acb8STomasz Jeznach      * Note, PCIDevice implementation will add overlapping MR for MSI/PBA,
21970c54acb8STomasz Jeznach      * managed directly by the PCIDevice implementation.
21980c54acb8STomasz Jeznach      */
21990c54acb8STomasz Jeznach     memory_region_init_io(&s->regs_mr, OBJECT(dev), &riscv_iommu_mmio_ops, s,
22000c54acb8STomasz Jeznach         "riscv-iommu-regs", RISCV_IOMMU_REG_SIZE);
22010c54acb8STomasz Jeznach 
22020c54acb8STomasz Jeznach     /* Set power-on register state */
22030c54acb8STomasz Jeznach     stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_CAP], s->cap);
22040c54acb8STomasz Jeznach     stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_FCTL], 0);
22050c54acb8STomasz Jeznach     stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_FCTL],
22060c54acb8STomasz Jeznach              ~(RISCV_IOMMU_FCTL_BE | RISCV_IOMMU_FCTL_WSI));
22070c54acb8STomasz Jeznach     stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_DDTP],
22080c54acb8STomasz Jeznach         ~(RISCV_IOMMU_DDTP_PPN | RISCV_IOMMU_DDTP_MODE));
22090c54acb8STomasz Jeznach     stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQB],
22100c54acb8STomasz Jeznach         ~(RISCV_IOMMU_CQB_LOG2SZ | RISCV_IOMMU_CQB_PPN));
22110c54acb8STomasz Jeznach     stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQB],
22120c54acb8STomasz Jeznach         ~(RISCV_IOMMU_FQB_LOG2SZ | RISCV_IOMMU_FQB_PPN));
22130c54acb8STomasz Jeznach     stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQB],
22140c54acb8STomasz Jeznach         ~(RISCV_IOMMU_PQB_LOG2SZ | RISCV_IOMMU_PQB_PPN));
22150c54acb8STomasz Jeznach     stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_CQCSR], RISCV_IOMMU_CQCSR_CQMF |
22160c54acb8STomasz Jeznach         RISCV_IOMMU_CQCSR_CMD_TO | RISCV_IOMMU_CQCSR_CMD_ILL);
22170c54acb8STomasz Jeznach     stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQCSR], RISCV_IOMMU_CQCSR_CQON |
22180c54acb8STomasz Jeznach         RISCV_IOMMU_CQCSR_BUSY);
22190c54acb8STomasz Jeznach     stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_FQCSR], RISCV_IOMMU_FQCSR_FQMF |
22200c54acb8STomasz Jeznach         RISCV_IOMMU_FQCSR_FQOF);
22210c54acb8STomasz Jeznach     stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQCSR], RISCV_IOMMU_FQCSR_FQON |
22220c54acb8STomasz Jeznach         RISCV_IOMMU_FQCSR_BUSY);
22230c54acb8STomasz Jeznach     stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_PQCSR], RISCV_IOMMU_PQCSR_PQMF |
22240c54acb8STomasz Jeznach         RISCV_IOMMU_PQCSR_PQOF);
22250c54acb8STomasz Jeznach     stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQCSR], RISCV_IOMMU_PQCSR_PQON |
22260c54acb8STomasz Jeznach         RISCV_IOMMU_PQCSR_BUSY);
22270c54acb8STomasz Jeznach     stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_IPSR], ~0);
22280c54acb8STomasz Jeznach     stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_ICVEC], 0);
22290c54acb8STomasz Jeznach     stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_DDTP], s->ddtp);
2230a7aa525bSTomasz Jeznach     /* If debug registers enabled. */
2231a7aa525bSTomasz Jeznach     if (s->cap & RISCV_IOMMU_CAP_DBG) {
2232a7aa525bSTomasz Jeznach         stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_TR_REQ_IOVA], 0);
2233a7aa525bSTomasz Jeznach         stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_TR_REQ_CTL],
2234a7aa525bSTomasz Jeznach             RISCV_IOMMU_TR_REQ_CTL_GO_BUSY);
2235a7aa525bSTomasz Jeznach     }
22360c54acb8STomasz Jeznach 
22370c54acb8STomasz Jeznach     /* Memory region for downstream access, if specified. */
22380c54acb8STomasz Jeznach     if (s->target_mr) {
22390c54acb8STomasz Jeznach         s->target_as = g_new0(AddressSpace, 1);
22400c54acb8STomasz Jeznach         address_space_init(s->target_as, s->target_mr,
22410c54acb8STomasz Jeznach             "riscv-iommu-downstream");
22420c54acb8STomasz Jeznach     } else {
22430c54acb8STomasz Jeznach         /* Fallback to global system memory. */
22440c54acb8STomasz Jeznach         s->target_as = &address_space_memory;
22450c54acb8STomasz Jeznach     }
22460c54acb8STomasz Jeznach 
22470c54acb8STomasz Jeznach     /* Memory region for untranslated MRIF/MSI writes */
22480c54acb8STomasz Jeznach     memory_region_init_io(&s->trap_mr, OBJECT(dev), &riscv_iommu_trap_ops, s,
22490c54acb8STomasz Jeznach             "riscv-iommu-trap", ~0ULL);
22500c54acb8STomasz Jeznach     address_space_init(&s->trap_as, &s->trap_mr, "riscv-iommu-trap-as");
22510c54acb8STomasz Jeznach }
22520c54acb8STomasz Jeznach 
22530c54acb8STomasz Jeznach static void riscv_iommu_unrealize(DeviceState *dev)
22540c54acb8STomasz Jeznach {
22550c54acb8STomasz Jeznach     RISCVIOMMUState *s = RISCV_IOMMU(dev);
22560c54acb8STomasz Jeznach 
22579d085a1cSTomasz Jeznach     g_hash_table_unref(s->iot_cache);
22580c54acb8STomasz Jeznach     g_hash_table_unref(s->ctx_cache);
22590c54acb8STomasz Jeznach }
22600c54acb8STomasz Jeznach 
2261766bade2SRichard Henderson static const Property riscv_iommu_properties[] = {
22620c54acb8STomasz Jeznach     DEFINE_PROP_UINT32("version", RISCVIOMMUState, version,
22630c54acb8STomasz Jeznach         RISCV_IOMMU_SPEC_DOT_VER),
22640c54acb8STomasz Jeznach     DEFINE_PROP_UINT32("bus", RISCVIOMMUState, bus, 0x0),
22659d085a1cSTomasz Jeznach     DEFINE_PROP_UINT32("ioatc-limit", RISCVIOMMUState, iot_limit,
22669d085a1cSTomasz Jeznach         LIMIT_CACHE_IOT),
22670c54acb8STomasz Jeznach     DEFINE_PROP_BOOL("intremap", RISCVIOMMUState, enable_msi, TRUE),
226869a9ae48STomasz Jeznach     DEFINE_PROP_BOOL("ats", RISCVIOMMUState, enable_ats, TRUE),
22690c54acb8STomasz Jeznach     DEFINE_PROP_BOOL("off", RISCVIOMMUState, enable_off, TRUE),
22700c54acb8STomasz Jeznach     DEFINE_PROP_BOOL("s-stage", RISCVIOMMUState, enable_s_stage, TRUE),
22710c54acb8STomasz Jeznach     DEFINE_PROP_BOOL("g-stage", RISCVIOMMUState, enable_g_stage, TRUE),
22720c54acb8STomasz Jeznach     DEFINE_PROP_LINK("downstream-mr", RISCVIOMMUState, target_mr,
22730c54acb8STomasz Jeznach         TYPE_MEMORY_REGION, MemoryRegion *),
22740c54acb8STomasz Jeznach     DEFINE_PROP_END_OF_LIST(),
22750c54acb8STomasz Jeznach };
22760c54acb8STomasz Jeznach 
22770c54acb8STomasz Jeznach static void riscv_iommu_class_init(ObjectClass *klass, void* data)
22780c54acb8STomasz Jeznach {
22790c54acb8STomasz Jeznach     DeviceClass *dc = DEVICE_CLASS(klass);
22800c54acb8STomasz Jeznach 
22810c54acb8STomasz Jeznach     /* internal device for riscv-iommu-{pci/sys}, not user-creatable */
22820c54acb8STomasz Jeznach     dc->user_creatable = false;
22830c54acb8STomasz Jeznach     dc->realize = riscv_iommu_realize;
22840c54acb8STomasz Jeznach     dc->unrealize = riscv_iommu_unrealize;
22850c54acb8STomasz Jeznach     device_class_set_props(dc, riscv_iommu_properties);
22860c54acb8STomasz Jeznach }
22870c54acb8STomasz Jeznach 
22880c54acb8STomasz Jeznach static const TypeInfo riscv_iommu_info = {
22890c54acb8STomasz Jeznach     .name = TYPE_RISCV_IOMMU,
22900c54acb8STomasz Jeznach     .parent = TYPE_DEVICE,
22910c54acb8STomasz Jeznach     .instance_size = sizeof(RISCVIOMMUState),
2292*4876e6f7SDaniel Henrique Barboza     .instance_init = riscv_iommu_instance_init,
22930c54acb8STomasz Jeznach     .class_init = riscv_iommu_class_init,
22940c54acb8STomasz Jeznach };
22950c54acb8STomasz Jeznach 
22960c54acb8STomasz Jeznach static const char *IOMMU_FLAG_STR[] = {
22970c54acb8STomasz Jeznach     "NA",
22980c54acb8STomasz Jeznach     "RO",
22990c54acb8STomasz Jeznach     "WR",
23000c54acb8STomasz Jeznach     "RW",
23010c54acb8STomasz Jeznach };
23020c54acb8STomasz Jeznach 
23030c54acb8STomasz Jeznach /* RISC-V IOMMU Memory Region - Address Translation Space */
23040c54acb8STomasz Jeznach static IOMMUTLBEntry riscv_iommu_memory_region_translate(
23050c54acb8STomasz Jeznach     IOMMUMemoryRegion *iommu_mr, hwaddr addr,
23060c54acb8STomasz Jeznach     IOMMUAccessFlags flag, int iommu_idx)
23070c54acb8STomasz Jeznach {
23080c54acb8STomasz Jeznach     RISCVIOMMUSpace *as = container_of(iommu_mr, RISCVIOMMUSpace, iova_mr);
23090c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx;
23100c54acb8STomasz Jeznach     void *ref;
23110c54acb8STomasz Jeznach     IOMMUTLBEntry iotlb = {
23120c54acb8STomasz Jeznach         .iova = addr,
23130c54acb8STomasz Jeznach         .target_as = as->iommu->target_as,
23140c54acb8STomasz Jeznach         .addr_mask = ~0ULL,
23150c54acb8STomasz Jeznach         .perm = flag,
23160c54acb8STomasz Jeznach     };
23170c54acb8STomasz Jeznach 
23180c54acb8STomasz Jeznach     ctx = riscv_iommu_ctx(as->iommu, as->devid, iommu_idx, &ref);
23190c54acb8STomasz Jeznach     if (ctx == NULL) {
23200c54acb8STomasz Jeznach         /* Translation disabled or invalid. */
23210c54acb8STomasz Jeznach         iotlb.addr_mask = 0;
23220c54acb8STomasz Jeznach         iotlb.perm = IOMMU_NONE;
23239d085a1cSTomasz Jeznach     } else if (riscv_iommu_translate(as->iommu, ctx, &iotlb, true)) {
23240c54acb8STomasz Jeznach         /* Translation disabled or fault reported. */
23250c54acb8STomasz Jeznach         iotlb.addr_mask = 0;
23260c54acb8STomasz Jeznach         iotlb.perm = IOMMU_NONE;
23270c54acb8STomasz Jeznach     }
23280c54acb8STomasz Jeznach 
23290c54acb8STomasz Jeznach     /* Trace all dma translations with original access flags. */
23300c54acb8STomasz Jeznach     trace_riscv_iommu_dma(as->iommu->parent_obj.id, PCI_BUS_NUM(as->devid),
23310c54acb8STomasz Jeznach                           PCI_SLOT(as->devid), PCI_FUNC(as->devid), iommu_idx,
23320c54acb8STomasz Jeznach                           IOMMU_FLAG_STR[flag & IOMMU_RW], iotlb.iova,
23330c54acb8STomasz Jeznach                           iotlb.translated_addr);
23340c54acb8STomasz Jeznach 
23350c54acb8STomasz Jeznach     riscv_iommu_ctx_put(as->iommu, ref);
23360c54acb8STomasz Jeznach 
23370c54acb8STomasz Jeznach     return iotlb;
23380c54acb8STomasz Jeznach }
23390c54acb8STomasz Jeznach 
23400c54acb8STomasz Jeznach static int riscv_iommu_memory_region_notify(
23410c54acb8STomasz Jeznach     IOMMUMemoryRegion *iommu_mr, IOMMUNotifierFlag old,
23420c54acb8STomasz Jeznach     IOMMUNotifierFlag new, Error **errp)
23430c54acb8STomasz Jeznach {
23440c54acb8STomasz Jeznach     RISCVIOMMUSpace *as = container_of(iommu_mr, RISCVIOMMUSpace, iova_mr);
23450c54acb8STomasz Jeznach 
23460c54acb8STomasz Jeznach     if (old == IOMMU_NOTIFIER_NONE) {
23470c54acb8STomasz Jeznach         as->notifier = true;
23480c54acb8STomasz Jeznach         trace_riscv_iommu_notifier_add(iommu_mr->parent_obj.name);
23490c54acb8STomasz Jeznach     } else if (new == IOMMU_NOTIFIER_NONE) {
23500c54acb8STomasz Jeznach         as->notifier = false;
23510c54acb8STomasz Jeznach         trace_riscv_iommu_notifier_del(iommu_mr->parent_obj.name);
23520c54acb8STomasz Jeznach     }
23530c54acb8STomasz Jeznach 
23540c54acb8STomasz Jeznach     return 0;
23550c54acb8STomasz Jeznach }
23560c54acb8STomasz Jeznach 
23570c54acb8STomasz Jeznach static inline bool pci_is_iommu(PCIDevice *pdev)
23580c54acb8STomasz Jeznach {
23590c54acb8STomasz Jeznach     return pci_get_word(pdev->config + PCI_CLASS_DEVICE) == 0x0806;
23600c54acb8STomasz Jeznach }
23610c54acb8STomasz Jeznach 
23620c54acb8STomasz Jeznach static AddressSpace *riscv_iommu_find_as(PCIBus *bus, void *opaque, int devfn)
23630c54acb8STomasz Jeznach {
23640c54acb8STomasz Jeznach     RISCVIOMMUState *s = (RISCVIOMMUState *) opaque;
23650c54acb8STomasz Jeznach     PCIDevice *pdev = pci_find_device(bus, pci_bus_num(bus), devfn);
23660c54acb8STomasz Jeznach     AddressSpace *as = NULL;
23670c54acb8STomasz Jeznach 
23680c54acb8STomasz Jeznach     if (pdev && pci_is_iommu(pdev)) {
23690c54acb8STomasz Jeznach         return s->target_as;
23700c54acb8STomasz Jeznach     }
23710c54acb8STomasz Jeznach 
23720c54acb8STomasz Jeznach     /* Find first registered IOMMU device */
23730c54acb8STomasz Jeznach     while (s->iommus.le_prev) {
23740c54acb8STomasz Jeznach         s = *(s->iommus.le_prev);
23750c54acb8STomasz Jeznach     }
23760c54acb8STomasz Jeznach 
23770c54acb8STomasz Jeznach     /* Find first matching IOMMU */
23780c54acb8STomasz Jeznach     while (s != NULL && as == NULL) {
23790c54acb8STomasz Jeznach         as = riscv_iommu_space(s, PCI_BUILD_BDF(pci_bus_num(bus), devfn));
23800c54acb8STomasz Jeznach         s = s->iommus.le_next;
23810c54acb8STomasz Jeznach     }
23820c54acb8STomasz Jeznach 
23830c54acb8STomasz Jeznach     return as ? as : &address_space_memory;
23840c54acb8STomasz Jeznach }
23850c54acb8STomasz Jeznach 
23860c54acb8STomasz Jeznach static const PCIIOMMUOps riscv_iommu_ops = {
23870c54acb8STomasz Jeznach     .get_address_space = riscv_iommu_find_as,
23880c54acb8STomasz Jeznach };
23890c54acb8STomasz Jeznach 
23900c54acb8STomasz Jeznach void riscv_iommu_pci_setup_iommu(RISCVIOMMUState *iommu, PCIBus *bus,
23910c54acb8STomasz Jeznach         Error **errp)
23920c54acb8STomasz Jeznach {
23930c54acb8STomasz Jeznach     if (bus->iommu_ops &&
23940c54acb8STomasz Jeznach         bus->iommu_ops->get_address_space == riscv_iommu_find_as) {
23950c54acb8STomasz Jeznach         /* Allow multiple IOMMUs on the same PCIe bus, link known devices */
23960c54acb8STomasz Jeznach         RISCVIOMMUState *last = (RISCVIOMMUState *)bus->iommu_opaque;
23970c54acb8STomasz Jeznach         QLIST_INSERT_AFTER(last, iommu, iommus);
23980c54acb8STomasz Jeznach     } else if (!bus->iommu_ops && !bus->iommu_opaque) {
23990c54acb8STomasz Jeznach         pci_setup_iommu(bus, &riscv_iommu_ops, iommu);
24000c54acb8STomasz Jeznach     } else {
24010c54acb8STomasz Jeznach         error_setg(errp, "can't register secondary IOMMU for PCI bus #%d",
24020c54acb8STomasz Jeznach             pci_bus_num(bus));
24030c54acb8STomasz Jeznach     }
24040c54acb8STomasz Jeznach }
24050c54acb8STomasz Jeznach 
24060c54acb8STomasz Jeznach static int riscv_iommu_memory_region_index(IOMMUMemoryRegion *iommu_mr,
24070c54acb8STomasz Jeznach     MemTxAttrs attrs)
24080c54acb8STomasz Jeznach {
24090c54acb8STomasz Jeznach     return attrs.unspecified ? RISCV_IOMMU_NOPROCID : (int)attrs.pid;
24100c54acb8STomasz Jeznach }
24110c54acb8STomasz Jeznach 
24120c54acb8STomasz Jeznach static int riscv_iommu_memory_region_index_len(IOMMUMemoryRegion *iommu_mr)
24130c54acb8STomasz Jeznach {
24140c54acb8STomasz Jeznach     RISCVIOMMUSpace *as = container_of(iommu_mr, RISCVIOMMUSpace, iova_mr);
24150c54acb8STomasz Jeznach     return 1 << as->iommu->pid_bits;
24160c54acb8STomasz Jeznach }
24170c54acb8STomasz Jeznach 
24180c54acb8STomasz Jeznach static void riscv_iommu_memory_region_init(ObjectClass *klass, void *data)
24190c54acb8STomasz Jeznach {
24200c54acb8STomasz Jeznach     IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass);
24210c54acb8STomasz Jeznach 
24220c54acb8STomasz Jeznach     imrc->translate = riscv_iommu_memory_region_translate;
24230c54acb8STomasz Jeznach     imrc->notify_flag_changed = riscv_iommu_memory_region_notify;
24240c54acb8STomasz Jeznach     imrc->attrs_to_index = riscv_iommu_memory_region_index;
24250c54acb8STomasz Jeznach     imrc->num_indexes = riscv_iommu_memory_region_index_len;
24260c54acb8STomasz Jeznach }
24270c54acb8STomasz Jeznach 
24280c54acb8STomasz Jeznach static const TypeInfo riscv_iommu_memory_region_info = {
24290c54acb8STomasz Jeznach     .parent = TYPE_IOMMU_MEMORY_REGION,
24300c54acb8STomasz Jeznach     .name = TYPE_RISCV_IOMMU_MEMORY_REGION,
24310c54acb8STomasz Jeznach     .class_init = riscv_iommu_memory_region_init,
24320c54acb8STomasz Jeznach };
24330c54acb8STomasz Jeznach 
24340c54acb8STomasz Jeznach static void riscv_iommu_register_mr_types(void)
24350c54acb8STomasz Jeznach {
24360c54acb8STomasz Jeznach     type_register_static(&riscv_iommu_memory_region_info);
24370c54acb8STomasz Jeznach     type_register_static(&riscv_iommu_info);
24380c54acb8STomasz Jeznach }
24390c54acb8STomasz Jeznach 
24400c54acb8STomasz Jeznach type_init(riscv_iommu_register_mr_types);
2441