xref: /qemu/hw/riscv/riscv-iommu.c (revision fa622855eaaca8b543e19cf7ba8ab0304a1e4b84)
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 
67*fa622855SJason Chien typedef enum RISCVIOMMUTransTag {
68*fa622855SJason Chien     RISCV_IOMMU_TRANS_TAG_BY,  /* Bypass */
69*fa622855SJason Chien     RISCV_IOMMU_TRANS_TAG_SS,  /* Single Stage */
70*fa622855SJason Chien     RISCV_IOMMU_TRANS_TAG_VG,  /* G-stage only */
71*fa622855SJason Chien     RISCV_IOMMU_TRANS_TAG_VN,  /* Nested translation */
72*fa622855SJason Chien } RISCVIOMMUTransTag;
73*fa622855SJason Chien 
749d085a1cSTomasz Jeznach /* Address translation cache entry */
759d085a1cSTomasz Jeznach struct RISCVIOMMUEntry {
76*fa622855SJason Chien     RISCVIOMMUTransTag tag;     /* Translation Tag */
779d085a1cSTomasz Jeznach     uint64_t iova:44;           /* IOVA Page Number */
789d085a1cSTomasz Jeznach     uint64_t pscid:20;          /* Process Soft-Context identifier */
799d085a1cSTomasz Jeznach     uint64_t phys:44;           /* Physical Page Number */
809d085a1cSTomasz Jeznach     uint64_t gscid:16;          /* Guest Soft-Context identifier */
819d085a1cSTomasz Jeznach     uint64_t perm:2;            /* IOMMU_RW flags */
829d085a1cSTomasz Jeznach };
839d085a1cSTomasz Jeznach 
840c54acb8STomasz Jeznach /* IOMMU index for transactions without process_id specified. */
850c54acb8STomasz Jeznach #define RISCV_IOMMU_NOPROCID 0
860c54acb8STomasz Jeznach 
870c54acb8STomasz Jeznach static uint8_t riscv_iommu_get_icvec_vector(uint32_t icvec, uint32_t vec_type)
880c54acb8STomasz Jeznach {
890c54acb8STomasz Jeznach     switch (vec_type) {
900c54acb8STomasz Jeznach     case RISCV_IOMMU_INTR_CQ:
910c54acb8STomasz Jeznach         return icvec & RISCV_IOMMU_ICVEC_CIV;
920c54acb8STomasz Jeznach     case RISCV_IOMMU_INTR_FQ:
930c54acb8STomasz Jeznach         return (icvec & RISCV_IOMMU_ICVEC_FIV) >> 4;
940c54acb8STomasz Jeznach     case RISCV_IOMMU_INTR_PM:
950c54acb8STomasz Jeznach         return (icvec & RISCV_IOMMU_ICVEC_PMIV) >> 8;
960c54acb8STomasz Jeznach     case RISCV_IOMMU_INTR_PQ:
970c54acb8STomasz Jeznach         return (icvec & RISCV_IOMMU_ICVEC_PIV) >> 12;
980c54acb8STomasz Jeznach     default:
990c54acb8STomasz Jeznach         g_assert_not_reached();
1000c54acb8STomasz Jeznach     }
1010c54acb8STomasz Jeznach }
1020c54acb8STomasz Jeznach 
1030c54acb8STomasz Jeznach static void riscv_iommu_notify(RISCVIOMMUState *s, int vec_type)
1040c54acb8STomasz Jeznach {
1050c54acb8STomasz Jeznach     uint32_t ipsr, icvec, vector;
1060c54acb8STomasz Jeznach 
1075b128435STomasz Jeznach     if (!s->notify) {
1080c54acb8STomasz Jeznach         return;
1090c54acb8STomasz Jeznach     }
1100c54acb8STomasz Jeznach 
1110c54acb8STomasz Jeznach     icvec = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_ICVEC);
1120c54acb8STomasz Jeznach     ipsr = riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_IPSR, (1 << vec_type), 0);
1130c54acb8STomasz Jeznach 
1140c54acb8STomasz Jeznach     if (!(ipsr & (1 << vec_type))) {
1150c54acb8STomasz Jeznach         vector = riscv_iommu_get_icvec_vector(icvec, vec_type);
1160c54acb8STomasz Jeznach         s->notify(s, vector);
1170c54acb8STomasz Jeznach         trace_riscv_iommu_notify_int_vector(vec_type, vector);
1180c54acb8STomasz Jeznach     }
1190c54acb8STomasz Jeznach }
1200c54acb8STomasz Jeznach 
1210c54acb8STomasz Jeznach static void riscv_iommu_fault(RISCVIOMMUState *s,
1220c54acb8STomasz Jeznach                               struct riscv_iommu_fq_record *ev)
1230c54acb8STomasz Jeznach {
1240c54acb8STomasz Jeznach     uint32_t ctrl = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQCSR);
1250c54acb8STomasz Jeznach     uint32_t head = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQH) & s->fq_mask;
1260c54acb8STomasz Jeznach     uint32_t tail = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQT) & s->fq_mask;
1270c54acb8STomasz Jeznach     uint32_t next = (tail + 1) & s->fq_mask;
1280c54acb8STomasz Jeznach     uint32_t devid = get_field(ev->hdr, RISCV_IOMMU_FQ_HDR_DID);
1290c54acb8STomasz Jeznach 
1300c54acb8STomasz Jeznach     trace_riscv_iommu_flt(s->parent_obj.id, PCI_BUS_NUM(devid), PCI_SLOT(devid),
1310c54acb8STomasz Jeznach                           PCI_FUNC(devid), ev->hdr, ev->iotval);
1320c54acb8STomasz Jeznach 
1330c54acb8STomasz Jeznach     if (!(ctrl & RISCV_IOMMU_FQCSR_FQON) ||
1340c54acb8STomasz Jeznach         !!(ctrl & (RISCV_IOMMU_FQCSR_FQOF | RISCV_IOMMU_FQCSR_FQMF))) {
1350c54acb8STomasz Jeznach         return;
1360c54acb8STomasz Jeznach     }
1370c54acb8STomasz Jeznach 
1380c54acb8STomasz Jeznach     if (head == next) {
1390c54acb8STomasz Jeznach         riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_FQCSR,
1400c54acb8STomasz Jeznach                               RISCV_IOMMU_FQCSR_FQOF, 0);
1410c54acb8STomasz Jeznach     } else {
1420c54acb8STomasz Jeznach         dma_addr_t addr = s->fq_addr + tail * sizeof(*ev);
1430c54acb8STomasz Jeznach         if (dma_memory_write(s->target_as, addr, ev, sizeof(*ev),
1440c54acb8STomasz Jeznach                              MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
1450c54acb8STomasz Jeznach             riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_FQCSR,
1460c54acb8STomasz Jeznach                                   RISCV_IOMMU_FQCSR_FQMF, 0);
1470c54acb8STomasz Jeznach         } else {
1480c54acb8STomasz Jeznach             riscv_iommu_reg_set32(s, RISCV_IOMMU_REG_FQT, next);
1490c54acb8STomasz Jeznach         }
1500c54acb8STomasz Jeznach     }
1510c54acb8STomasz Jeznach 
1520c54acb8STomasz Jeznach     if (ctrl & RISCV_IOMMU_FQCSR_FIE) {
1530c54acb8STomasz Jeznach         riscv_iommu_notify(s, RISCV_IOMMU_INTR_FQ);
1540c54acb8STomasz Jeznach     }
1550c54acb8STomasz Jeznach }
1560c54acb8STomasz Jeznach 
1570c54acb8STomasz Jeznach static void riscv_iommu_pri(RISCVIOMMUState *s,
1580c54acb8STomasz Jeznach     struct riscv_iommu_pq_record *pr)
1590c54acb8STomasz Jeznach {
1600c54acb8STomasz Jeznach     uint32_t ctrl = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_PQCSR);
1610c54acb8STomasz Jeznach     uint32_t head = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_PQH) & s->pq_mask;
1620c54acb8STomasz Jeznach     uint32_t tail = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_PQT) & s->pq_mask;
1630c54acb8STomasz Jeznach     uint32_t next = (tail + 1) & s->pq_mask;
1640c54acb8STomasz Jeznach     uint32_t devid = get_field(pr->hdr, RISCV_IOMMU_PREQ_HDR_DID);
1650c54acb8STomasz Jeznach 
1660c54acb8STomasz Jeznach     trace_riscv_iommu_pri(s->parent_obj.id, PCI_BUS_NUM(devid), PCI_SLOT(devid),
1670c54acb8STomasz Jeznach                           PCI_FUNC(devid), pr->payload);
1680c54acb8STomasz Jeznach 
1690c54acb8STomasz Jeznach     if (!(ctrl & RISCV_IOMMU_PQCSR_PQON) ||
1700c54acb8STomasz Jeznach         !!(ctrl & (RISCV_IOMMU_PQCSR_PQOF | RISCV_IOMMU_PQCSR_PQMF))) {
1710c54acb8STomasz Jeznach         return;
1720c54acb8STomasz Jeznach     }
1730c54acb8STomasz Jeznach 
1740c54acb8STomasz Jeznach     if (head == next) {
1750c54acb8STomasz Jeznach         riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_PQCSR,
1760c54acb8STomasz Jeznach                               RISCV_IOMMU_PQCSR_PQOF, 0);
1770c54acb8STomasz Jeznach     } else {
1780c54acb8STomasz Jeznach         dma_addr_t addr = s->pq_addr + tail * sizeof(*pr);
1790c54acb8STomasz Jeznach         if (dma_memory_write(s->target_as, addr, pr, sizeof(*pr),
1800c54acb8STomasz Jeznach                              MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
1810c54acb8STomasz Jeznach             riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_PQCSR,
1820c54acb8STomasz Jeznach                                   RISCV_IOMMU_PQCSR_PQMF, 0);
1830c54acb8STomasz Jeznach         } else {
1840c54acb8STomasz Jeznach             riscv_iommu_reg_set32(s, RISCV_IOMMU_REG_PQT, next);
1850c54acb8STomasz Jeznach         }
1860c54acb8STomasz Jeznach     }
1870c54acb8STomasz Jeznach 
1880c54acb8STomasz Jeznach     if (ctrl & RISCV_IOMMU_PQCSR_PIE) {
1890c54acb8STomasz Jeznach         riscv_iommu_notify(s, RISCV_IOMMU_INTR_PQ);
1900c54acb8STomasz Jeznach     }
1910c54acb8STomasz Jeznach }
1920c54acb8STomasz Jeznach 
193d37eede7SPierrick Bouvier /*
194d37eede7SPierrick Bouvier  * Discards all bits from 'val' whose matching bits in the same
195d37eede7SPierrick Bouvier  * positions in the mask 'ext' are zeros, and packs the remaining
196d37eede7SPierrick Bouvier  * bits from 'val' contiguously at the least-significant end of the
197d37eede7SPierrick Bouvier  * result, keeping the same bit order as 'val' and filling any
198d37eede7SPierrick Bouvier  * other bits at the most-significant end of the result with zeros.
199d37eede7SPierrick Bouvier  *
200d37eede7SPierrick Bouvier  * For example, for the following 'val' and 'ext', the return 'ret'
201d37eede7SPierrick Bouvier  * will be:
202d37eede7SPierrick Bouvier  *
203d37eede7SPierrick Bouvier  * val = a b c d e f g h
204d37eede7SPierrick Bouvier  * ext = 1 0 1 0 0 1 1 0
205d37eede7SPierrick Bouvier  * ret = 0 0 0 0 a c f g
206d37eede7SPierrick Bouvier  *
207d37eede7SPierrick Bouvier  * This function, taken from the riscv-iommu 1.0 spec, section 2.3.3
208d37eede7SPierrick Bouvier  * "Process to translate addresses of MSIs", is similar to bit manip
209d37eede7SPierrick Bouvier  * function PEXT (Parallel bits extract) from x86.
210d37eede7SPierrick Bouvier  */
211d37eede7SPierrick Bouvier static uint64_t riscv_iommu_pext_u64(uint64_t val, uint64_t ext)
2120c54acb8STomasz Jeznach {
2130c54acb8STomasz Jeznach     uint64_t ret = 0;
2140c54acb8STomasz Jeznach     uint64_t rot = 1;
2150c54acb8STomasz Jeznach 
2160c54acb8STomasz Jeznach     while (ext) {
2170c54acb8STomasz Jeznach         if (ext & 1) {
2180c54acb8STomasz Jeznach             if (val & 1) {
2190c54acb8STomasz Jeznach                 ret |= rot;
2200c54acb8STomasz Jeznach             }
2210c54acb8STomasz Jeznach             rot <<= 1;
2220c54acb8STomasz Jeznach         }
2230c54acb8STomasz Jeznach         val >>= 1;
2240c54acb8STomasz Jeznach         ext >>= 1;
2250c54acb8STomasz Jeznach     }
2260c54acb8STomasz Jeznach 
2270c54acb8STomasz Jeznach     return ret;
2280c54acb8STomasz Jeznach }
2290c54acb8STomasz Jeznach 
2300c54acb8STomasz Jeznach /* Check if GPA matches MSI/MRIF pattern. */
2310c54acb8STomasz Jeznach static bool riscv_iommu_msi_check(RISCVIOMMUState *s, RISCVIOMMUContext *ctx,
2320c54acb8STomasz Jeznach     dma_addr_t gpa)
2330c54acb8STomasz Jeznach {
2340c54acb8STomasz Jeznach     if (!s->enable_msi) {
2350c54acb8STomasz Jeznach         return false;
2360c54acb8STomasz Jeznach     }
2370c54acb8STomasz Jeznach 
2380c54acb8STomasz Jeznach     if (get_field(ctx->msiptp, RISCV_IOMMU_DC_MSIPTP_MODE) !=
2390c54acb8STomasz Jeznach         RISCV_IOMMU_DC_MSIPTP_MODE_FLAT) {
2400c54acb8STomasz Jeznach         return false; /* Invalid MSI/MRIF mode */
2410c54acb8STomasz Jeznach     }
2420c54acb8STomasz Jeznach 
2430c54acb8STomasz Jeznach     if ((PPN_DOWN(gpa) ^ ctx->msi_addr_pattern) & ~ctx->msi_addr_mask) {
2440c54acb8STomasz Jeznach         return false; /* GPA not in MSI range defined by AIA IMSIC rules. */
2450c54acb8STomasz Jeznach     }
2460c54acb8STomasz Jeznach 
2470c54acb8STomasz Jeznach     return true;
2480c54acb8STomasz Jeznach }
2490c54acb8STomasz Jeznach 
2500c54acb8STomasz Jeznach /*
2510c54acb8STomasz Jeznach  * RISCV IOMMU Address Translation Lookup - Page Table Walk
2520c54acb8STomasz Jeznach  *
2530c54acb8STomasz Jeznach  * Note: Code is based on get_physical_address() from target/riscv/cpu_helper.c
2540c54acb8STomasz Jeznach  * Both implementation can be merged into single helper function in future.
2550c54acb8STomasz Jeznach  * Keeping them separate for now, as error reporting and flow specifics are
2560c54acb8STomasz Jeznach  * sufficiently different for separate implementation.
2570c54acb8STomasz Jeznach  *
2580c54acb8STomasz Jeznach  * @s        : IOMMU Device State
2590c54acb8STomasz Jeznach  * @ctx      : Translation context for device id and process address space id.
2600c54acb8STomasz Jeznach  * @iotlb    : translation data: physical address and access mode.
2610c54acb8STomasz Jeznach  * @return   : success or fault cause code.
2620c54acb8STomasz Jeznach  */
2630c54acb8STomasz Jeznach static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx,
2640c54acb8STomasz Jeznach     IOMMUTLBEntry *iotlb)
2650c54acb8STomasz Jeznach {
2660c54acb8STomasz Jeznach     dma_addr_t addr, base;
2670c54acb8STomasz Jeznach     uint64_t satp, gatp, pte;
2680c54acb8STomasz Jeznach     bool en_s, en_g;
2690c54acb8STomasz Jeznach     struct {
2700c54acb8STomasz Jeznach         unsigned char step;
2710c54acb8STomasz Jeznach         unsigned char levels;
2720c54acb8STomasz Jeznach         unsigned char ptidxbits;
2730c54acb8STomasz Jeznach         unsigned char ptesize;
2740c54acb8STomasz Jeznach     } sc[2];
2750c54acb8STomasz Jeznach     /* Translation stage phase */
2760c54acb8STomasz Jeznach     enum {
2770c54acb8STomasz Jeznach         S_STAGE = 0,
2780c54acb8STomasz Jeznach         G_STAGE = 1,
2790c54acb8STomasz Jeznach     } pass;
2800c54acb8STomasz Jeznach     MemTxResult ret;
2810c54acb8STomasz Jeznach 
2820c54acb8STomasz Jeznach     satp = get_field(ctx->satp, RISCV_IOMMU_ATP_MODE_FIELD);
2830c54acb8STomasz Jeznach     gatp = get_field(ctx->gatp, RISCV_IOMMU_ATP_MODE_FIELD);
2840c54acb8STomasz Jeznach 
2850c54acb8STomasz Jeznach     en_s = satp != RISCV_IOMMU_DC_FSC_MODE_BARE;
2860c54acb8STomasz Jeznach     en_g = gatp != RISCV_IOMMU_DC_IOHGATP_MODE_BARE;
2870c54acb8STomasz Jeznach 
2880c54acb8STomasz Jeznach     /*
2890c54acb8STomasz Jeznach      * Early check for MSI address match when IOVA == GPA.
2900c54acb8STomasz Jeznach      * Note that the (!en_s) condition means that the MSI
2910c54acb8STomasz Jeznach      * page table may only be used when guest pages are
2920c54acb8STomasz Jeznach      * mapped using the g-stage page table, whether single-
2930c54acb8STomasz Jeznach      * or two-stage paging is enabled. It's unavoidable though,
2940c54acb8STomasz Jeznach      * because the spec mandates that we do a first-stage
2950c54acb8STomasz Jeznach      * translation before we check the MSI page table, which
2960c54acb8STomasz Jeznach      * means we can't do an early MSI check unless we have
2970c54acb8STomasz Jeznach      * strictly !en_s.
2980c54acb8STomasz Jeznach      */
2990c54acb8STomasz Jeznach     if (!en_s && (iotlb->perm & IOMMU_WO) &&
3000c54acb8STomasz Jeznach         riscv_iommu_msi_check(s, ctx, iotlb->iova)) {
3010c54acb8STomasz Jeznach         iotlb->target_as = &s->trap_as;
3020c54acb8STomasz Jeznach         iotlb->translated_addr = iotlb->iova;
3030c54acb8STomasz Jeznach         iotlb->addr_mask = ~TARGET_PAGE_MASK;
3040c54acb8STomasz Jeznach         return 0;
3050c54acb8STomasz Jeznach     }
3060c54acb8STomasz Jeznach 
3070c54acb8STomasz Jeznach     /* Exit early for pass-through mode. */
3080c54acb8STomasz Jeznach     if (!(en_s || en_g)) {
3090c54acb8STomasz Jeznach         iotlb->translated_addr = iotlb->iova;
3100c54acb8STomasz Jeznach         iotlb->addr_mask = ~TARGET_PAGE_MASK;
3110c54acb8STomasz Jeznach         /* Allow R/W in pass-through mode */
3120c54acb8STomasz Jeznach         iotlb->perm = IOMMU_RW;
3130c54acb8STomasz Jeznach         return 0;
3140c54acb8STomasz Jeznach     }
3150c54acb8STomasz Jeznach 
3160c54acb8STomasz Jeznach     /* S/G translation parameters. */
3170c54acb8STomasz Jeznach     for (pass = 0; pass < 2; pass++) {
3180c54acb8STomasz Jeznach         uint32_t sv_mode;
3190c54acb8STomasz Jeznach 
3200c54acb8STomasz Jeznach         sc[pass].step = 0;
3210c54acb8STomasz Jeznach         if (pass ? (s->fctl & RISCV_IOMMU_FCTL_GXL) :
3220c54acb8STomasz Jeznach             (ctx->tc & RISCV_IOMMU_DC_TC_SXL)) {
3230c54acb8STomasz Jeznach             /* 32bit mode for GXL/SXL == 1 */
3240c54acb8STomasz Jeznach             switch (pass ? gatp : satp) {
3250c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_IOHGATP_MODE_BARE:
3260c54acb8STomasz Jeznach                 sc[pass].levels    = 0;
3270c54acb8STomasz Jeznach                 sc[pass].ptidxbits = 0;
3280c54acb8STomasz Jeznach                 sc[pass].ptesize   = 0;
3290c54acb8STomasz Jeznach                 break;
3300c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_IOHGATP_MODE_SV32X4:
3310c54acb8STomasz Jeznach                 sv_mode = pass ? RISCV_IOMMU_CAP_SV32X4 : RISCV_IOMMU_CAP_SV32;
3320c54acb8STomasz Jeznach                 if (!(s->cap & sv_mode)) {
3330c54acb8STomasz Jeznach                     return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
3340c54acb8STomasz Jeznach                 }
3350c54acb8STomasz Jeznach                 sc[pass].levels    = 2;
3360c54acb8STomasz Jeznach                 sc[pass].ptidxbits = 10;
3370c54acb8STomasz Jeznach                 sc[pass].ptesize   = 4;
3380c54acb8STomasz Jeznach                 break;
3390c54acb8STomasz Jeznach             default:
3400c54acb8STomasz Jeznach                 return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
3410c54acb8STomasz Jeznach             }
3420c54acb8STomasz Jeznach         } else {
3430c54acb8STomasz Jeznach             /* 64bit mode for GXL/SXL == 0 */
3440c54acb8STomasz Jeznach             switch (pass ? gatp : satp) {
3450c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_IOHGATP_MODE_BARE:
3460c54acb8STomasz Jeznach                 sc[pass].levels    = 0;
3470c54acb8STomasz Jeznach                 sc[pass].ptidxbits = 0;
3480c54acb8STomasz Jeznach                 sc[pass].ptesize   = 0;
3490c54acb8STomasz Jeznach                 break;
3500c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_IOHGATP_MODE_SV39X4:
3510c54acb8STomasz Jeznach                 sv_mode = pass ? RISCV_IOMMU_CAP_SV39X4 : RISCV_IOMMU_CAP_SV39;
3520c54acb8STomasz Jeznach                 if (!(s->cap & sv_mode)) {
3530c54acb8STomasz Jeznach                     return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
3540c54acb8STomasz Jeznach                 }
3550c54acb8STomasz Jeznach                 sc[pass].levels    = 3;
3560c54acb8STomasz Jeznach                 sc[pass].ptidxbits = 9;
3570c54acb8STomasz Jeznach                 sc[pass].ptesize   = 8;
3580c54acb8STomasz Jeznach                 break;
3590c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_IOHGATP_MODE_SV48X4:
3600c54acb8STomasz Jeznach                 sv_mode = pass ? RISCV_IOMMU_CAP_SV48X4 : RISCV_IOMMU_CAP_SV48;
3610c54acb8STomasz Jeznach                 if (!(s->cap & sv_mode)) {
3620c54acb8STomasz Jeznach                     return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
3630c54acb8STomasz Jeznach                 }
3640c54acb8STomasz Jeznach                 sc[pass].levels    = 4;
3650c54acb8STomasz Jeznach                 sc[pass].ptidxbits = 9;
3660c54acb8STomasz Jeznach                 sc[pass].ptesize   = 8;
3670c54acb8STomasz Jeznach                 break;
3680c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_IOHGATP_MODE_SV57X4:
3690c54acb8STomasz Jeznach                 sv_mode = pass ? RISCV_IOMMU_CAP_SV57X4 : RISCV_IOMMU_CAP_SV57;
3700c54acb8STomasz Jeznach                 if (!(s->cap & sv_mode)) {
3710c54acb8STomasz Jeznach                     return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
3720c54acb8STomasz Jeznach                 }
3730c54acb8STomasz Jeznach                 sc[pass].levels    = 5;
3740c54acb8STomasz Jeznach                 sc[pass].ptidxbits = 9;
3750c54acb8STomasz Jeznach                 sc[pass].ptesize   = 8;
3760c54acb8STomasz Jeznach                 break;
3770c54acb8STomasz Jeznach             default:
3780c54acb8STomasz Jeznach                 return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
3790c54acb8STomasz Jeznach             }
3800c54acb8STomasz Jeznach         }
3810c54acb8STomasz Jeznach     };
3820c54acb8STomasz Jeznach 
3830c54acb8STomasz Jeznach     /* S/G stages translation tables root pointers */
3840c54acb8STomasz Jeznach     gatp = PPN_PHYS(get_field(ctx->gatp, RISCV_IOMMU_ATP_PPN_FIELD));
3850c54acb8STomasz Jeznach     satp = PPN_PHYS(get_field(ctx->satp, RISCV_IOMMU_ATP_PPN_FIELD));
3860c54acb8STomasz Jeznach     addr = (en_s && en_g) ? satp : iotlb->iova;
3870c54acb8STomasz Jeznach     base = en_g ? gatp : satp;
3880c54acb8STomasz Jeznach     pass = en_g ? G_STAGE : S_STAGE;
3890c54acb8STomasz Jeznach 
3900c54acb8STomasz Jeznach     do {
3910c54acb8STomasz Jeznach         const unsigned widened = (pass && !sc[pass].step) ? 2 : 0;
3920c54acb8STomasz Jeznach         const unsigned va_bits = widened + sc[pass].ptidxbits;
3930c54acb8STomasz Jeznach         const unsigned va_skip = TARGET_PAGE_BITS + sc[pass].ptidxbits *
3940c54acb8STomasz Jeznach                                  (sc[pass].levels - 1 - sc[pass].step);
3950c54acb8STomasz Jeznach         const unsigned idx = (addr >> va_skip) & ((1 << va_bits) - 1);
3960c54acb8STomasz Jeznach         const dma_addr_t pte_addr = base + idx * sc[pass].ptesize;
3970c54acb8STomasz Jeznach         const bool ade =
3980c54acb8STomasz Jeznach             ctx->tc & (pass ? RISCV_IOMMU_DC_TC_GADE : RISCV_IOMMU_DC_TC_SADE);
3990c54acb8STomasz Jeznach 
4000c54acb8STomasz Jeznach         /* Address range check before first level lookup */
4010c54acb8STomasz Jeznach         if (!sc[pass].step) {
402e5d28bf2SJason Chien             const uint64_t va_len = va_skip + va_bits;
403e5d28bf2SJason Chien             const uint64_t va_mask = (1ULL << va_len) - 1;
404e5d28bf2SJason Chien 
405e5d28bf2SJason Chien             if (pass == S_STAGE && va_len > 32) {
406e5d28bf2SJason Chien                 target_ulong mask, masked_msbs;
407e5d28bf2SJason Chien 
408e5d28bf2SJason Chien                 mask = (1L << (TARGET_LONG_BITS - (va_len - 1))) - 1;
409e5d28bf2SJason Chien                 masked_msbs = (addr >> (va_len - 1)) & mask;
410e5d28bf2SJason Chien 
411e5d28bf2SJason Chien                 if (masked_msbs != 0 && masked_msbs != mask) {
412e5d28bf2SJason Chien                     return (iotlb->perm & IOMMU_WO) ?
413e5d28bf2SJason Chien                                 RISCV_IOMMU_FQ_CAUSE_WR_FAULT_S :
414e5d28bf2SJason Chien                                 RISCV_IOMMU_FQ_CAUSE_RD_FAULT_S;
415e5d28bf2SJason Chien                 }
416e5d28bf2SJason Chien             } else {
4170c54acb8STomasz Jeznach                 if ((addr & va_mask) != addr) {
418e5d28bf2SJason Chien                     return (iotlb->perm & IOMMU_WO) ?
419e5d28bf2SJason Chien                                 RISCV_IOMMU_FQ_CAUSE_WR_FAULT_VS :
420e5d28bf2SJason Chien                                 RISCV_IOMMU_FQ_CAUSE_RD_FAULT_VS;
421e5d28bf2SJason Chien                 }
4220c54acb8STomasz Jeznach             }
4230c54acb8STomasz Jeznach         }
4240c54acb8STomasz Jeznach 
4250c54acb8STomasz Jeznach         /* Read page table entry */
4260c54acb8STomasz Jeznach         if (sc[pass].ptesize == 4) {
4270c54acb8STomasz Jeznach             uint32_t pte32 = 0;
4280c54acb8STomasz Jeznach             ret = ldl_le_dma(s->target_as, pte_addr, &pte32,
4290c54acb8STomasz Jeznach                              MEMTXATTRS_UNSPECIFIED);
4300c54acb8STomasz Jeznach             pte = pte32;
4310c54acb8STomasz Jeznach         } else {
4320c54acb8STomasz Jeznach             ret = ldq_le_dma(s->target_as, pte_addr, &pte,
4330c54acb8STomasz Jeznach                              MEMTXATTRS_UNSPECIFIED);
4340c54acb8STomasz Jeznach         }
4350c54acb8STomasz Jeznach         if (ret != MEMTX_OK) {
4360c54acb8STomasz Jeznach             return (iotlb->perm & IOMMU_WO) ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT
4370c54acb8STomasz Jeznach                                             : RISCV_IOMMU_FQ_CAUSE_RD_FAULT;
4380c54acb8STomasz Jeznach         }
4390c54acb8STomasz Jeznach 
4400c54acb8STomasz Jeznach         sc[pass].step++;
4410c54acb8STomasz Jeznach         hwaddr ppn = pte >> PTE_PPN_SHIFT;
4420c54acb8STomasz Jeznach 
4430c54acb8STomasz Jeznach         if (!(pte & PTE_V)) {
4440c54acb8STomasz Jeznach             break;                /* Invalid PTE */
4450c54acb8STomasz Jeznach         } else if (!(pte & (PTE_R | PTE_W | PTE_X))) {
4460c54acb8STomasz Jeznach             base = PPN_PHYS(ppn); /* Inner PTE, continue walking */
4470c54acb8STomasz Jeznach         } else if ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) {
4480c54acb8STomasz Jeznach             break;                /* Reserved leaf PTE flags: PTE_W */
4490c54acb8STomasz Jeznach         } else if ((pte & (PTE_R | PTE_W | PTE_X)) == (PTE_W | PTE_X)) {
4500c54acb8STomasz Jeznach             break;                /* Reserved leaf PTE flags: PTE_W + PTE_X */
4510c54acb8STomasz Jeznach         } else if (ppn & ((1ULL << (va_skip - TARGET_PAGE_BITS)) - 1)) {
4520c54acb8STomasz Jeznach             break;                /* Misaligned PPN */
4530c54acb8STomasz Jeznach         } else if ((iotlb->perm & IOMMU_RO) && !(pte & PTE_R)) {
4540c54acb8STomasz Jeznach             break;                /* Read access check failed */
4550c54acb8STomasz Jeznach         } else if ((iotlb->perm & IOMMU_WO) && !(pte & PTE_W)) {
4560c54acb8STomasz Jeznach             break;                /* Write access check failed */
4570c54acb8STomasz Jeznach         } else if ((iotlb->perm & IOMMU_RO) && !ade && !(pte & PTE_A)) {
4580c54acb8STomasz Jeznach             break;                /* Access bit not set */
4590c54acb8STomasz Jeznach         } else if ((iotlb->perm & IOMMU_WO) && !ade && !(pte & PTE_D)) {
4600c54acb8STomasz Jeznach             break;                /* Dirty bit not set */
4610c54acb8STomasz Jeznach         } else {
4620c54acb8STomasz Jeznach             /* Leaf PTE, translation completed. */
4630c54acb8STomasz Jeznach             sc[pass].step = sc[pass].levels;
4640c54acb8STomasz Jeznach             base = PPN_PHYS(ppn) | (addr & ((1ULL << va_skip) - 1));
4650c54acb8STomasz Jeznach             /* Update address mask based on smallest translation granularity */
4660c54acb8STomasz Jeznach             iotlb->addr_mask &= (1ULL << va_skip) - 1;
4670c54acb8STomasz Jeznach             /* Continue with S-Stage translation? */
4680c54acb8STomasz Jeznach             if (pass && sc[0].step != sc[0].levels) {
4690c54acb8STomasz Jeznach                 pass = S_STAGE;
4700c54acb8STomasz Jeznach                 addr = iotlb->iova;
4710c54acb8STomasz Jeznach                 continue;
4720c54acb8STomasz Jeznach             }
4730c54acb8STomasz Jeznach             /* Translation phase completed (GPA or SPA) */
4740c54acb8STomasz Jeznach             iotlb->translated_addr = base;
4750c54acb8STomasz Jeznach             iotlb->perm = (pte & PTE_W) ? ((pte & PTE_R) ? IOMMU_RW : IOMMU_WO)
4760c54acb8STomasz Jeznach                                                          : IOMMU_RO;
4770c54acb8STomasz Jeznach 
4780c54acb8STomasz Jeznach             /* Check MSI GPA address match */
4790c54acb8STomasz Jeznach             if (pass == S_STAGE && (iotlb->perm & IOMMU_WO) &&
4800c54acb8STomasz Jeznach                 riscv_iommu_msi_check(s, ctx, base)) {
4810c54acb8STomasz Jeznach                 /* Trap MSI writes and return GPA address. */
4820c54acb8STomasz Jeznach                 iotlb->target_as = &s->trap_as;
4830c54acb8STomasz Jeznach                 iotlb->addr_mask = ~TARGET_PAGE_MASK;
4840c54acb8STomasz Jeznach                 return 0;
4850c54acb8STomasz Jeznach             }
4860c54acb8STomasz Jeznach 
4870c54acb8STomasz Jeznach             /* Continue with G-Stage translation? */
4880c54acb8STomasz Jeznach             if (!pass && en_g) {
4890c54acb8STomasz Jeznach                 pass = G_STAGE;
4900c54acb8STomasz Jeznach                 addr = base;
4910c54acb8STomasz Jeznach                 base = gatp;
4920c54acb8STomasz Jeznach                 sc[pass].step = 0;
4930c54acb8STomasz Jeznach                 continue;
4940c54acb8STomasz Jeznach             }
4950c54acb8STomasz Jeznach 
4960c54acb8STomasz Jeznach             return 0;
4970c54acb8STomasz Jeznach         }
4980c54acb8STomasz Jeznach 
4990c54acb8STomasz Jeznach         if (sc[pass].step == sc[pass].levels) {
5000c54acb8STomasz Jeznach             break; /* Can't find leaf PTE */
5010c54acb8STomasz Jeznach         }
5020c54acb8STomasz Jeznach 
5030c54acb8STomasz Jeznach         /* Continue with G-Stage translation? */
5040c54acb8STomasz Jeznach         if (!pass && en_g) {
5050c54acb8STomasz Jeznach             pass = G_STAGE;
5060c54acb8STomasz Jeznach             addr = base;
5070c54acb8STomasz Jeznach             base = gatp;
5080c54acb8STomasz Jeznach             sc[pass].step = 0;
5090c54acb8STomasz Jeznach         }
5100c54acb8STomasz Jeznach     } while (1);
5110c54acb8STomasz Jeznach 
5120c54acb8STomasz Jeznach     return (iotlb->perm & IOMMU_WO) ?
5130c54acb8STomasz Jeznach                 (pass ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT_VS :
5140c54acb8STomasz Jeznach                         RISCV_IOMMU_FQ_CAUSE_WR_FAULT_S) :
5150c54acb8STomasz Jeznach                 (pass ? RISCV_IOMMU_FQ_CAUSE_RD_FAULT_VS :
5160c54acb8STomasz Jeznach                         RISCV_IOMMU_FQ_CAUSE_RD_FAULT_S);
5170c54acb8STomasz Jeznach }
5180c54acb8STomasz Jeznach 
5190c54acb8STomasz Jeznach static void riscv_iommu_report_fault(RISCVIOMMUState *s,
5200c54acb8STomasz Jeznach                                      RISCVIOMMUContext *ctx,
5210c54acb8STomasz Jeznach                                      uint32_t fault_type, uint32_t cause,
5220c54acb8STomasz Jeznach                                      bool pv,
5230c54acb8STomasz Jeznach                                      uint64_t iotval, uint64_t iotval2)
5240c54acb8STomasz Jeznach {
5250c54acb8STomasz Jeznach     struct riscv_iommu_fq_record ev = { 0 };
5260c54acb8STomasz Jeznach 
5270c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_DTF) {
5280c54acb8STomasz Jeznach         switch (cause) {
5290c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_DMA_DISABLED:
5300c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_DDT_LOAD_FAULT:
5310c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_DDT_INVALID:
5320c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED:
5330c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_DDT_CORRUPTED:
5340c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_INTERNAL_DP_ERROR:
5350c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT:
5360c54acb8STomasz Jeznach             break;
5370c54acb8STomasz Jeznach         default:
5380c54acb8STomasz Jeznach             /* DTF prevents reporting a fault for this given cause */
5390c54acb8STomasz Jeznach             return;
5400c54acb8STomasz Jeznach         }
5410c54acb8STomasz Jeznach     }
5420c54acb8STomasz Jeznach 
5430c54acb8STomasz Jeznach     ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_CAUSE, cause);
5440c54acb8STomasz Jeznach     ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_TTYPE, fault_type);
5450c54acb8STomasz Jeznach     ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_DID, ctx->devid);
5460c54acb8STomasz Jeznach     ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_PV, true);
5470c54acb8STomasz Jeznach 
5480c54acb8STomasz Jeznach     if (pv) {
5490c54acb8STomasz Jeznach         ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_PID, ctx->process_id);
5500c54acb8STomasz Jeznach     }
5510c54acb8STomasz Jeznach 
5520c54acb8STomasz Jeznach     ev.iotval = iotval;
5530c54acb8STomasz Jeznach     ev.iotval2 = iotval2;
5540c54acb8STomasz Jeznach 
5550c54acb8STomasz Jeznach     riscv_iommu_fault(s, &ev);
5560c54acb8STomasz Jeznach }
5570c54acb8STomasz Jeznach 
5580c54acb8STomasz Jeznach /* Redirect MSI write for given GPA. */
5590c54acb8STomasz Jeznach static MemTxResult riscv_iommu_msi_write(RISCVIOMMUState *s,
5600c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx, uint64_t gpa, uint64_t data,
5610c54acb8STomasz Jeznach     unsigned size, MemTxAttrs attrs)
5620c54acb8STomasz Jeznach {
5630c54acb8STomasz Jeznach     MemTxResult res;
5640c54acb8STomasz Jeznach     dma_addr_t addr;
5650c54acb8STomasz Jeznach     uint64_t intn;
5660c54acb8STomasz Jeznach     uint32_t n190;
5670c54acb8STomasz Jeznach     uint64_t pte[2];
5680c54acb8STomasz Jeznach     int fault_type = RISCV_IOMMU_FQ_TTYPE_UADDR_WR;
5690c54acb8STomasz Jeznach     int cause;
5700c54acb8STomasz Jeznach 
5710c54acb8STomasz Jeznach     /* Interrupt File Number */
572d37eede7SPierrick Bouvier     intn = riscv_iommu_pext_u64(PPN_DOWN(gpa), ctx->msi_addr_mask);
5730c54acb8STomasz Jeznach     if (intn >= 256) {
5740c54acb8STomasz Jeznach         /* Interrupt file number out of range */
5750c54acb8STomasz Jeznach         res = MEMTX_ACCESS_ERROR;
5760c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT;
5770c54acb8STomasz Jeznach         goto err;
5780c54acb8STomasz Jeznach     }
5790c54acb8STomasz Jeznach 
5800c54acb8STomasz Jeznach     /* fetch MSI PTE */
5810c54acb8STomasz Jeznach     addr = PPN_PHYS(get_field(ctx->msiptp, RISCV_IOMMU_DC_MSIPTP_PPN));
5820c54acb8STomasz Jeznach     addr = addr | (intn * sizeof(pte));
5830c54acb8STomasz Jeznach     res = dma_memory_read(s->target_as, addr, &pte, sizeof(pte),
5840c54acb8STomasz Jeznach             MEMTXATTRS_UNSPECIFIED);
5850c54acb8STomasz Jeznach     if (res != MEMTX_OK) {
5860c54acb8STomasz Jeznach         if (res == MEMTX_DECODE_ERROR) {
5870c54acb8STomasz Jeznach             cause = RISCV_IOMMU_FQ_CAUSE_MSI_PT_CORRUPTED;
5880c54acb8STomasz Jeznach         } else {
5890c54acb8STomasz Jeznach             cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT;
5900c54acb8STomasz Jeznach         }
5910c54acb8STomasz Jeznach         goto err;
5920c54acb8STomasz Jeznach     }
5930c54acb8STomasz Jeznach 
5940c54acb8STomasz Jeznach     le64_to_cpus(&pte[0]);
5950c54acb8STomasz Jeznach     le64_to_cpus(&pte[1]);
5960c54acb8STomasz Jeznach 
5970c54acb8STomasz Jeznach     if (!(pte[0] & RISCV_IOMMU_MSI_PTE_V) || (pte[0] & RISCV_IOMMU_MSI_PTE_C)) {
5980c54acb8STomasz Jeznach         /*
5990c54acb8STomasz Jeznach          * The spec mentions that: "If msipte.C == 1, then further
6000c54acb8STomasz Jeznach          * processing to interpret the PTE is implementation
6010c54acb8STomasz Jeznach          * defined.". We'll abort with cause = 262 for this
6020c54acb8STomasz Jeznach          * case too.
6030c54acb8STomasz Jeznach          */
6040c54acb8STomasz Jeznach         res = MEMTX_ACCESS_ERROR;
6050c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_INVALID;
6060c54acb8STomasz Jeznach         goto err;
6070c54acb8STomasz Jeznach     }
6080c54acb8STomasz Jeznach 
6090c54acb8STomasz Jeznach     switch (get_field(pte[0], RISCV_IOMMU_MSI_PTE_M)) {
6100c54acb8STomasz Jeznach     case RISCV_IOMMU_MSI_PTE_M_BASIC:
6110c54acb8STomasz Jeznach         /* MSI Pass-through mode */
6120c54acb8STomasz Jeznach         addr = PPN_PHYS(get_field(pte[0], RISCV_IOMMU_MSI_PTE_PPN));
6130c54acb8STomasz Jeznach 
6140c54acb8STomasz Jeznach         trace_riscv_iommu_msi(s->parent_obj.id, PCI_BUS_NUM(ctx->devid),
6150c54acb8STomasz Jeznach                               PCI_SLOT(ctx->devid), PCI_FUNC(ctx->devid),
6160c54acb8STomasz Jeznach                               gpa, addr);
6170c54acb8STomasz Jeznach 
6180c54acb8STomasz Jeznach         res = dma_memory_write(s->target_as, addr, &data, size, attrs);
6190c54acb8STomasz Jeznach         if (res != MEMTX_OK) {
6200c54acb8STomasz Jeznach             cause = RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT;
6210c54acb8STomasz Jeznach             goto err;
6220c54acb8STomasz Jeznach         }
6230c54acb8STomasz Jeznach 
6240c54acb8STomasz Jeznach         return MEMTX_OK;
6250c54acb8STomasz Jeznach     case RISCV_IOMMU_MSI_PTE_M_MRIF:
6260c54acb8STomasz Jeznach         /* MRIF mode, continue. */
6270c54acb8STomasz Jeznach         break;
6280c54acb8STomasz Jeznach     default:
6290c54acb8STomasz Jeznach         res = MEMTX_ACCESS_ERROR;
6300c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_MISCONFIGURED;
6310c54acb8STomasz Jeznach         goto err;
6320c54acb8STomasz Jeznach     }
6330c54acb8STomasz Jeznach 
6340c54acb8STomasz Jeznach     /*
6350c54acb8STomasz Jeznach      * Report an error for interrupt identities exceeding the maximum allowed
6360c54acb8STomasz Jeznach      * for an IMSIC interrupt file (2047) or destination address is not 32-bit
6370c54acb8STomasz Jeznach      * aligned. See IOMMU Specification, Chapter 2.3. MSI page tables.
6380c54acb8STomasz Jeznach      */
6390c54acb8STomasz Jeznach     if ((data > 2047) || (gpa & 3)) {
6400c54acb8STomasz Jeznach         res = MEMTX_ACCESS_ERROR;
6410c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_MISCONFIGURED;
6420c54acb8STomasz Jeznach         goto err;
6430c54acb8STomasz Jeznach     }
6440c54acb8STomasz Jeznach 
6450c54acb8STomasz Jeznach     /* MSI MRIF mode, non atomic pending bit update */
6460c54acb8STomasz Jeznach 
6470c54acb8STomasz Jeznach     /* MRIF pending bit address */
6480c54acb8STomasz Jeznach     addr = get_field(pte[0], RISCV_IOMMU_MSI_PTE_MRIF_ADDR) << 9;
6490c54acb8STomasz Jeznach     addr = addr | ((data & 0x7c0) >> 3);
6500c54acb8STomasz Jeznach 
6510c54acb8STomasz Jeznach     trace_riscv_iommu_msi(s->parent_obj.id, PCI_BUS_NUM(ctx->devid),
6520c54acb8STomasz Jeznach                           PCI_SLOT(ctx->devid), PCI_FUNC(ctx->devid),
6530c54acb8STomasz Jeznach                           gpa, addr);
6540c54acb8STomasz Jeznach 
6550c54acb8STomasz Jeznach     /* MRIF pending bit mask */
6560c54acb8STomasz Jeznach     data = 1ULL << (data & 0x03f);
6570c54acb8STomasz Jeznach     res = dma_memory_read(s->target_as, addr, &intn, sizeof(intn), attrs);
6580c54acb8STomasz Jeznach     if (res != MEMTX_OK) {
6590c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT;
6600c54acb8STomasz Jeznach         goto err;
6610c54acb8STomasz Jeznach     }
6620c54acb8STomasz Jeznach 
6630c54acb8STomasz Jeznach     intn = intn | data;
6640c54acb8STomasz Jeznach     res = dma_memory_write(s->target_as, addr, &intn, sizeof(intn), attrs);
6650c54acb8STomasz Jeznach     if (res != MEMTX_OK) {
6660c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT;
6670c54acb8STomasz Jeznach         goto err;
6680c54acb8STomasz Jeznach     }
6690c54acb8STomasz Jeznach 
6700c54acb8STomasz Jeznach     /* Get MRIF enable bits */
6710c54acb8STomasz Jeznach     addr = addr + sizeof(intn);
6720c54acb8STomasz Jeznach     res = dma_memory_read(s->target_as, addr, &intn, sizeof(intn), attrs);
6730c54acb8STomasz Jeznach     if (res != MEMTX_OK) {
6740c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT;
6750c54acb8STomasz Jeznach         goto err;
6760c54acb8STomasz Jeznach     }
6770c54acb8STomasz Jeznach 
6780c54acb8STomasz Jeznach     if (!(intn & data)) {
6790c54acb8STomasz Jeznach         /* notification disabled, MRIF update completed. */
6800c54acb8STomasz Jeznach         return MEMTX_OK;
6810c54acb8STomasz Jeznach     }
6820c54acb8STomasz Jeznach 
6830c54acb8STomasz Jeznach     /* Send notification message */
6840c54acb8STomasz Jeznach     addr = PPN_PHYS(get_field(pte[1], RISCV_IOMMU_MSI_MRIF_NPPN));
6850c54acb8STomasz Jeznach     n190 = get_field(pte[1], RISCV_IOMMU_MSI_MRIF_NID) |
6860c54acb8STomasz Jeznach           (get_field(pte[1], RISCV_IOMMU_MSI_MRIF_NID_MSB) << 10);
6870c54acb8STomasz Jeznach 
6880c54acb8STomasz Jeznach     res = dma_memory_write(s->target_as, addr, &n190, sizeof(n190), attrs);
6890c54acb8STomasz Jeznach     if (res != MEMTX_OK) {
6900c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT;
6910c54acb8STomasz Jeznach         goto err;
6920c54acb8STomasz Jeznach     }
6930c54acb8STomasz Jeznach 
6940c54acb8STomasz Jeznach     trace_riscv_iommu_mrif_notification(s->parent_obj.id, n190, addr);
6950c54acb8STomasz Jeznach 
6960c54acb8STomasz Jeznach     return MEMTX_OK;
6970c54acb8STomasz Jeznach 
6980c54acb8STomasz Jeznach err:
6990c54acb8STomasz Jeznach     riscv_iommu_report_fault(s, ctx, fault_type, cause,
7000c54acb8STomasz Jeznach                              !!ctx->process_id, 0, 0);
7010c54acb8STomasz Jeznach     return res;
7020c54acb8STomasz Jeznach }
7030c54acb8STomasz Jeznach 
7040c54acb8STomasz Jeznach /*
7050c54acb8STomasz Jeznach  * Check device context configuration as described by the
7060c54acb8STomasz Jeznach  * riscv-iommu spec section "Device-context configuration
7070c54acb8STomasz Jeznach  * checks".
7080c54acb8STomasz Jeznach  */
7090c54acb8STomasz Jeznach static bool riscv_iommu_validate_device_ctx(RISCVIOMMUState *s,
7100c54acb8STomasz Jeznach                                             RISCVIOMMUContext *ctx)
7110c54acb8STomasz Jeznach {
7120c54acb8STomasz Jeznach     uint32_t fsc_mode, msi_mode;
71369a9ae48STomasz Jeznach     uint64_t gatp;
71469a9ae48STomasz Jeznach 
71569a9ae48STomasz Jeznach     if (!(s->cap & RISCV_IOMMU_CAP_ATS) &&
71669a9ae48STomasz Jeznach         (ctx->tc & RISCV_IOMMU_DC_TC_EN_ATS ||
71769a9ae48STomasz Jeznach          ctx->tc & RISCV_IOMMU_DC_TC_EN_PRI ||
71869a9ae48STomasz Jeznach          ctx->tc & RISCV_IOMMU_DC_TC_PRPR)) {
71969a9ae48STomasz Jeznach         return false;
72069a9ae48STomasz Jeznach     }
72169a9ae48STomasz Jeznach 
72269a9ae48STomasz Jeznach     if (!(ctx->tc & RISCV_IOMMU_DC_TC_EN_ATS) &&
72369a9ae48STomasz Jeznach         (ctx->tc & RISCV_IOMMU_DC_TC_T2GPA ||
72469a9ae48STomasz Jeznach          ctx->tc & RISCV_IOMMU_DC_TC_EN_PRI)) {
72569a9ae48STomasz Jeznach         return false;
72669a9ae48STomasz Jeznach     }
7270c54acb8STomasz Jeznach 
7280c54acb8STomasz Jeznach     if (!(ctx->tc & RISCV_IOMMU_DC_TC_EN_PRI) &&
7290c54acb8STomasz Jeznach         ctx->tc & RISCV_IOMMU_DC_TC_PRPR) {
7300c54acb8STomasz Jeznach         return false;
7310c54acb8STomasz Jeznach     }
7320c54acb8STomasz Jeznach 
7330c54acb8STomasz Jeznach     if (!(s->cap & RISCV_IOMMU_CAP_T2GPA) &&
7340c54acb8STomasz Jeznach         ctx->tc & RISCV_IOMMU_DC_TC_T2GPA) {
7350c54acb8STomasz Jeznach         return false;
7360c54acb8STomasz Jeznach     }
7370c54acb8STomasz Jeznach 
7380c54acb8STomasz Jeznach     if (s->cap & RISCV_IOMMU_CAP_MSI_FLAT) {
7390c54acb8STomasz Jeznach         msi_mode = get_field(ctx->msiptp, RISCV_IOMMU_DC_MSIPTP_MODE);
7400c54acb8STomasz Jeznach 
7410c54acb8STomasz Jeznach         if (msi_mode != RISCV_IOMMU_DC_MSIPTP_MODE_OFF &&
7420c54acb8STomasz Jeznach             msi_mode != RISCV_IOMMU_DC_MSIPTP_MODE_FLAT) {
7430c54acb8STomasz Jeznach             return false;
7440c54acb8STomasz Jeznach         }
7450c54acb8STomasz Jeznach     }
7460c54acb8STomasz Jeznach 
74769a9ae48STomasz Jeznach     gatp = get_field(ctx->gatp, RISCV_IOMMU_ATP_MODE_FIELD);
74869a9ae48STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_T2GPA &&
74969a9ae48STomasz Jeznach         gatp == RISCV_IOMMU_DC_IOHGATP_MODE_BARE) {
75069a9ae48STomasz Jeznach         return false;
75169a9ae48STomasz Jeznach     }
75269a9ae48STomasz Jeznach 
7530c54acb8STomasz Jeznach     fsc_mode = get_field(ctx->satp, RISCV_IOMMU_DC_FSC_MODE);
7540c54acb8STomasz Jeznach 
7550c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_PDTV) {
7560c54acb8STomasz Jeznach         switch (fsc_mode) {
7570c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_PDTP_MODE_PD8:
7580c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_PD8)) {
7590c54acb8STomasz Jeznach                 return false;
7600c54acb8STomasz Jeznach             }
7610c54acb8STomasz Jeznach             break;
7620c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_PDTP_MODE_PD17:
7630c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_PD17)) {
7640c54acb8STomasz Jeznach                 return false;
7650c54acb8STomasz Jeznach             }
7660c54acb8STomasz Jeznach             break;
7670c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_PDTP_MODE_PD20:
7680c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_PD20)) {
7690c54acb8STomasz Jeznach                 return false;
7700c54acb8STomasz Jeznach             }
7710c54acb8STomasz Jeznach             break;
7720c54acb8STomasz Jeznach         }
7730c54acb8STomasz Jeznach     } else {
7740c54acb8STomasz Jeznach         /* DC.tc.PDTV is 0 */
7750c54acb8STomasz Jeznach         if (ctx->tc & RISCV_IOMMU_DC_TC_DPE) {
7760c54acb8STomasz Jeznach             return false;
7770c54acb8STomasz Jeznach         }
7780c54acb8STomasz Jeznach 
7790c54acb8STomasz Jeznach         if (ctx->tc & RISCV_IOMMU_DC_TC_SXL) {
7800c54acb8STomasz Jeznach             if (fsc_mode == RISCV_IOMMU_CAP_SV32 &&
7810c54acb8STomasz Jeznach                 !(s->cap & RISCV_IOMMU_CAP_SV32)) {
7820c54acb8STomasz Jeznach                 return false;
7830c54acb8STomasz Jeznach             }
7840c54acb8STomasz Jeznach         } else {
7850c54acb8STomasz Jeznach             switch (fsc_mode) {
7860c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV39:
7870c54acb8STomasz Jeznach                 if (!(s->cap & RISCV_IOMMU_CAP_SV39)) {
7880c54acb8STomasz Jeznach                     return false;
7890c54acb8STomasz Jeznach                 }
7900c54acb8STomasz Jeznach                 break;
7910c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV48:
7920c54acb8STomasz Jeznach                 if (!(s->cap & RISCV_IOMMU_CAP_SV48)) {
7930c54acb8STomasz Jeznach                     return false;
7940c54acb8STomasz Jeznach                 }
7950c54acb8STomasz Jeznach             break;
7960c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57:
7970c54acb8STomasz Jeznach                 if (!(s->cap & RISCV_IOMMU_CAP_SV57)) {
7980c54acb8STomasz Jeznach                     return false;
7990c54acb8STomasz Jeznach                 }
8000c54acb8STomasz Jeznach                 break;
8010c54acb8STomasz Jeznach             }
8020c54acb8STomasz Jeznach         }
8030c54acb8STomasz Jeznach     }
8040c54acb8STomasz Jeznach 
8050c54acb8STomasz Jeznach     /*
8060c54acb8STomasz Jeznach      * CAP_END is always zero (only one endianess). FCTL_BE is
8070c54acb8STomasz Jeznach      * always zero (little-endian accesses). Thus TC_SBE must
8080c54acb8STomasz Jeznach      * always be LE, i.e. zero.
8090c54acb8STomasz Jeznach      */
8100c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_SBE) {
8110c54acb8STomasz Jeznach         return false;
8120c54acb8STomasz Jeznach     }
8130c54acb8STomasz Jeznach 
8140c54acb8STomasz Jeznach     return true;
8150c54acb8STomasz Jeznach }
8160c54acb8STomasz Jeznach 
8170c54acb8STomasz Jeznach /*
8180c54acb8STomasz Jeznach  * Validate process context (PC) according to section
8190c54acb8STomasz Jeznach  * "Process-context configuration checks".
8200c54acb8STomasz Jeznach  */
8210c54acb8STomasz Jeznach static bool riscv_iommu_validate_process_ctx(RISCVIOMMUState *s,
8220c54acb8STomasz Jeznach                                              RISCVIOMMUContext *ctx)
8230c54acb8STomasz Jeznach {
8240c54acb8STomasz Jeznach     uint32_t mode;
8250c54acb8STomasz Jeznach 
8260c54acb8STomasz Jeznach     if (get_field(ctx->ta, RISCV_IOMMU_PC_TA_RESERVED)) {
8270c54acb8STomasz Jeznach         return false;
8280c54acb8STomasz Jeznach     }
8290c54acb8STomasz Jeznach 
8300c54acb8STomasz Jeznach     if (get_field(ctx->satp, RISCV_IOMMU_PC_FSC_RESERVED)) {
8310c54acb8STomasz Jeznach         return false;
8320c54acb8STomasz Jeznach     }
8330c54acb8STomasz Jeznach 
8340c54acb8STomasz Jeznach     mode = get_field(ctx->satp, RISCV_IOMMU_DC_FSC_MODE);
8350c54acb8STomasz Jeznach     switch (mode) {
8360c54acb8STomasz Jeznach     case RISCV_IOMMU_DC_FSC_MODE_BARE:
8370c54acb8STomasz Jeznach     /* sv39 and sv32 modes have the same value (8) */
8380c54acb8STomasz Jeznach     case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV39:
8390c54acb8STomasz Jeznach     case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV48:
8400c54acb8STomasz Jeznach     case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57:
8410c54acb8STomasz Jeznach         break;
8420c54acb8STomasz Jeznach     default:
8430c54acb8STomasz Jeznach         return false;
8440c54acb8STomasz Jeznach     }
8450c54acb8STomasz Jeznach 
8460c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_SXL) {
847d3b96a53SDaniel Henrique Barboza         if (mode == RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV32 &&
8480c54acb8STomasz Jeznach             !(s->cap & RISCV_IOMMU_CAP_SV32)) {
8490c54acb8STomasz Jeznach                 return false;
8500c54acb8STomasz Jeznach         }
8510c54acb8STomasz Jeznach     } else {
8520c54acb8STomasz Jeznach         switch (mode) {
8530c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV39:
8540c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_SV39)) {
8550c54acb8STomasz Jeznach                 return false;
8560c54acb8STomasz Jeznach             }
8570c54acb8STomasz Jeznach             break;
8580c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV48:
8590c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_SV48)) {
8600c54acb8STomasz Jeznach                 return false;
8610c54acb8STomasz Jeznach             }
8620c54acb8STomasz Jeznach             break;
8630c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57:
8640c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_SV57)) {
8650c54acb8STomasz Jeznach                 return false;
8660c54acb8STomasz Jeznach             }
8670c54acb8STomasz Jeznach             break;
8680c54acb8STomasz Jeznach         }
8690c54acb8STomasz Jeznach     }
8700c54acb8STomasz Jeznach 
8710c54acb8STomasz Jeznach     return true;
8720c54acb8STomasz Jeznach }
8730c54acb8STomasz Jeznach 
8740c54acb8STomasz Jeznach /*
8750c54acb8STomasz Jeznach  * RISC-V IOMMU Device Context Loopkup - Device Directory Tree Walk
8760c54acb8STomasz Jeznach  *
8770c54acb8STomasz Jeznach  * @s         : IOMMU Device State
8780c54acb8STomasz Jeznach  * @ctx       : Device Translation Context with devid and process_id set.
8790c54acb8STomasz Jeznach  * @return    : success or fault code.
8800c54acb8STomasz Jeznach  */
8810c54acb8STomasz Jeznach static int riscv_iommu_ctx_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx)
8820c54acb8STomasz Jeznach {
8830c54acb8STomasz Jeznach     const uint64_t ddtp = s->ddtp;
8840c54acb8STomasz Jeznach     unsigned mode = get_field(ddtp, RISCV_IOMMU_DDTP_MODE);
8850c54acb8STomasz Jeznach     dma_addr_t addr = PPN_PHYS(get_field(ddtp, RISCV_IOMMU_DDTP_PPN));
8860c54acb8STomasz Jeznach     struct riscv_iommu_dc dc;
8870c54acb8STomasz Jeznach     /* Device Context format: 0: extended (64 bytes) | 1: base (32 bytes) */
8880c54acb8STomasz Jeznach     const int dc_fmt = !s->enable_msi;
8890c54acb8STomasz Jeznach     const size_t dc_len = sizeof(dc) >> dc_fmt;
890cd5d265fSDaniel Henrique Barboza     int depth;
8910c54acb8STomasz Jeznach     uint64_t de;
8920c54acb8STomasz Jeznach 
8930c54acb8STomasz Jeznach     switch (mode) {
8940c54acb8STomasz Jeznach     case RISCV_IOMMU_DDTP_MODE_OFF:
8950c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_DMA_DISABLED;
8960c54acb8STomasz Jeznach 
8970c54acb8STomasz Jeznach     case RISCV_IOMMU_DDTP_MODE_BARE:
8980c54acb8STomasz Jeznach         /* mock up pass-through translation context */
8990c54acb8STomasz Jeznach         ctx->gatp = set_field(0, RISCV_IOMMU_ATP_MODE_FIELD,
9000c54acb8STomasz Jeznach             RISCV_IOMMU_DC_IOHGATP_MODE_BARE);
9010c54acb8STomasz Jeznach         ctx->satp = set_field(0, RISCV_IOMMU_ATP_MODE_FIELD,
9020c54acb8STomasz Jeznach             RISCV_IOMMU_DC_FSC_MODE_BARE);
90369a9ae48STomasz Jeznach 
9040c54acb8STomasz Jeznach         ctx->tc = RISCV_IOMMU_DC_TC_V;
90569a9ae48STomasz Jeznach         if (s->enable_ats) {
90669a9ae48STomasz Jeznach             ctx->tc |= RISCV_IOMMU_DC_TC_EN_ATS;
90769a9ae48STomasz Jeznach         }
90869a9ae48STomasz Jeznach 
9090c54acb8STomasz Jeznach         ctx->ta = 0;
9100c54acb8STomasz Jeznach         ctx->msiptp = 0;
9110c54acb8STomasz Jeznach         return 0;
9120c54acb8STomasz Jeznach 
9130c54acb8STomasz Jeznach     case RISCV_IOMMU_DDTP_MODE_1LVL:
9140c54acb8STomasz Jeznach         depth = 0;
9150c54acb8STomasz Jeznach         break;
9160c54acb8STomasz Jeznach 
9170c54acb8STomasz Jeznach     case RISCV_IOMMU_DDTP_MODE_2LVL:
9180c54acb8STomasz Jeznach         depth = 1;
9190c54acb8STomasz Jeznach         break;
9200c54acb8STomasz Jeznach 
9210c54acb8STomasz Jeznach     case RISCV_IOMMU_DDTP_MODE_3LVL:
9220c54acb8STomasz Jeznach         depth = 2;
9230c54acb8STomasz Jeznach         break;
9240c54acb8STomasz Jeznach 
9250c54acb8STomasz Jeznach     default:
9260c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
9270c54acb8STomasz Jeznach     }
9280c54acb8STomasz Jeznach 
9290c54acb8STomasz Jeznach     /*
9300c54acb8STomasz Jeznach      * Check supported device id width (in bits).
9310c54acb8STomasz Jeznach      * See IOMMU Specification, Chapter 6. Software guidelines.
9320c54acb8STomasz Jeznach      * - if extended device-context format is used:
9330c54acb8STomasz Jeznach      *   1LVL: 6, 2LVL: 15, 3LVL: 24
9340c54acb8STomasz Jeznach      * - if base device-context format is used:
9350c54acb8STomasz Jeznach      *   1LVL: 7, 2LVL: 16, 3LVL: 24
9360c54acb8STomasz Jeznach      */
9370c54acb8STomasz Jeznach     if (ctx->devid >= (1 << (depth * 9 + 6 + (dc_fmt && depth != 2)))) {
9380c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_TTYPE_BLOCKED;
9390c54acb8STomasz Jeznach     }
9400c54acb8STomasz Jeznach 
9410c54acb8STomasz Jeznach     /* Device directory tree walk */
9420c54acb8STomasz Jeznach     for (; depth-- > 0; ) {
9430c54acb8STomasz Jeznach         /*
9440c54acb8STomasz Jeznach          * Select device id index bits based on device directory tree level
9450c54acb8STomasz Jeznach          * and device context format.
9460c54acb8STomasz Jeznach          * See IOMMU Specification, Chapter 2. Data Structures.
9470c54acb8STomasz Jeznach          * - if extended device-context format is used:
9480c54acb8STomasz Jeznach          *   device index: [23:15][14:6][5:0]
9490c54acb8STomasz Jeznach          * - if base device-context format is used:
9500c54acb8STomasz Jeznach          *   device index: [23:16][15:7][6:0]
9510c54acb8STomasz Jeznach          */
9520c54acb8STomasz Jeznach         const int split = depth * 9 + 6 + dc_fmt;
9530c54acb8STomasz Jeznach         addr |= ((ctx->devid >> split) << 3) & ~TARGET_PAGE_MASK;
9540c54acb8STomasz Jeznach         if (dma_memory_read(s->target_as, addr, &de, sizeof(de),
9550c54acb8STomasz Jeznach                             MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
9560c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_DDT_LOAD_FAULT;
9570c54acb8STomasz Jeznach         }
9580c54acb8STomasz Jeznach         le64_to_cpus(&de);
9590c54acb8STomasz Jeznach         if (!(de & RISCV_IOMMU_DDTE_VALID)) {
9600c54acb8STomasz Jeznach             /* invalid directory entry */
9610c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_DDT_INVALID;
9620c54acb8STomasz Jeznach         }
9630c54acb8STomasz Jeznach         if (de & ~(RISCV_IOMMU_DDTE_PPN | RISCV_IOMMU_DDTE_VALID)) {
9640c54acb8STomasz Jeznach             /* reserved bits set */
9650c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
9660c54acb8STomasz Jeznach         }
9670c54acb8STomasz Jeznach         addr = PPN_PHYS(get_field(de, RISCV_IOMMU_DDTE_PPN));
9680c54acb8STomasz Jeznach     }
9690c54acb8STomasz Jeznach 
9700c54acb8STomasz Jeznach     /* index into device context entry page */
9710c54acb8STomasz Jeznach     addr |= (ctx->devid * dc_len) & ~TARGET_PAGE_MASK;
9720c54acb8STomasz Jeznach 
9730c54acb8STomasz Jeznach     memset(&dc, 0, sizeof(dc));
9740c54acb8STomasz Jeznach     if (dma_memory_read(s->target_as, addr, &dc, dc_len,
9750c54acb8STomasz Jeznach                         MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
9760c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_DDT_LOAD_FAULT;
9770c54acb8STomasz Jeznach     }
9780c54acb8STomasz Jeznach 
9790c54acb8STomasz Jeznach     /* Set translation context. */
9800c54acb8STomasz Jeznach     ctx->tc = le64_to_cpu(dc.tc);
9810c54acb8STomasz Jeznach     ctx->gatp = le64_to_cpu(dc.iohgatp);
9820c54acb8STomasz Jeznach     ctx->satp = le64_to_cpu(dc.fsc);
9830c54acb8STomasz Jeznach     ctx->ta = le64_to_cpu(dc.ta);
9840c54acb8STomasz Jeznach     ctx->msiptp = le64_to_cpu(dc.msiptp);
9850c54acb8STomasz Jeznach     ctx->msi_addr_mask = le64_to_cpu(dc.msi_addr_mask);
9860c54acb8STomasz Jeznach     ctx->msi_addr_pattern = le64_to_cpu(dc.msi_addr_pattern);
9870c54acb8STomasz Jeznach 
9880c54acb8STomasz Jeznach     if (!(ctx->tc & RISCV_IOMMU_DC_TC_V)) {
9890c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_DDT_INVALID;
9900c54acb8STomasz Jeznach     }
9910c54acb8STomasz Jeznach 
9920c54acb8STomasz Jeznach     if (!riscv_iommu_validate_device_ctx(s, ctx)) {
9930c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
9940c54acb8STomasz Jeznach     }
9950c54acb8STomasz Jeznach 
9960c54acb8STomasz Jeznach     /* FSC field checks */
9970c54acb8STomasz Jeznach     mode = get_field(ctx->satp, RISCV_IOMMU_DC_FSC_MODE);
9980c54acb8STomasz Jeznach     addr = PPN_PHYS(get_field(ctx->satp, RISCV_IOMMU_DC_FSC_PPN));
9990c54acb8STomasz Jeznach 
10000c54acb8STomasz Jeznach     if (!(ctx->tc & RISCV_IOMMU_DC_TC_PDTV)) {
10010c54acb8STomasz Jeznach         if (ctx->process_id != RISCV_IOMMU_NOPROCID) {
10020c54acb8STomasz Jeznach             /* PID is disabled */
10030c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_TTYPE_BLOCKED;
10040c54acb8STomasz Jeznach         }
10050c54acb8STomasz Jeznach         if (mode > RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57) {
10060c54acb8STomasz Jeznach             /* Invalid translation mode */
10070c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_DDT_INVALID;
10080c54acb8STomasz Jeznach         }
10090c54acb8STomasz Jeznach         return 0;
10100c54acb8STomasz Jeznach     }
10110c54acb8STomasz Jeznach 
10120c54acb8STomasz Jeznach     if (ctx->process_id == RISCV_IOMMU_NOPROCID) {
10130c54acb8STomasz Jeznach         if (!(ctx->tc & RISCV_IOMMU_DC_TC_DPE)) {
10140c54acb8STomasz Jeznach             /* No default process_id enabled, set BARE mode */
10150c54acb8STomasz Jeznach             ctx->satp = 0ULL;
10160c54acb8STomasz Jeznach             return 0;
10170c54acb8STomasz Jeznach         } else {
10180c54acb8STomasz Jeznach             /* Use default process_id #0 */
10190c54acb8STomasz Jeznach             ctx->process_id = 0;
10200c54acb8STomasz Jeznach         }
10210c54acb8STomasz Jeznach     }
10220c54acb8STomasz Jeznach 
10230c54acb8STomasz Jeznach     if (mode == RISCV_IOMMU_DC_FSC_MODE_BARE) {
10240c54acb8STomasz Jeznach         /* No S-Stage translation, done. */
10250c54acb8STomasz Jeznach         return 0;
10260c54acb8STomasz Jeznach     }
10270c54acb8STomasz Jeznach 
10280c54acb8STomasz Jeznach     /* FSC.TC.PDTV enabled */
10290c54acb8STomasz Jeznach     if (mode > RISCV_IOMMU_DC_FSC_PDTP_MODE_PD20) {
10300c54acb8STomasz Jeznach         /* Invalid PDTP.MODE */
10310c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_PDT_MISCONFIGURED;
10320c54acb8STomasz Jeznach     }
10330c54acb8STomasz Jeznach 
10340c54acb8STomasz Jeznach     for (depth = mode - RISCV_IOMMU_DC_FSC_PDTP_MODE_PD8; depth-- > 0; ) {
10350c54acb8STomasz Jeznach         /*
10360c54acb8STomasz Jeznach          * Select process id index bits based on process directory tree
10370c54acb8STomasz Jeznach          * level. See IOMMU Specification, 2.2. Process-Directory-Table.
10380c54acb8STomasz Jeznach          */
10390c54acb8STomasz Jeznach         const int split = depth * 9 + 8;
10400c54acb8STomasz Jeznach         addr |= ((ctx->process_id >> split) << 3) & ~TARGET_PAGE_MASK;
10410c54acb8STomasz Jeznach         if (dma_memory_read(s->target_as, addr, &de, sizeof(de),
10420c54acb8STomasz Jeznach                             MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
10430c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_PDT_LOAD_FAULT;
10440c54acb8STomasz Jeznach         }
10450c54acb8STomasz Jeznach         le64_to_cpus(&de);
10460c54acb8STomasz Jeznach         if (!(de & RISCV_IOMMU_PC_TA_V)) {
10470c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_PDT_INVALID;
10480c54acb8STomasz Jeznach         }
10490c54acb8STomasz Jeznach         addr = PPN_PHYS(get_field(de, RISCV_IOMMU_PC_FSC_PPN));
10500c54acb8STomasz Jeznach     }
10510c54acb8STomasz Jeznach 
10520c54acb8STomasz Jeznach     /* Leaf entry in PDT */
10530c54acb8STomasz Jeznach     addr |= (ctx->process_id << 4) & ~TARGET_PAGE_MASK;
10540c54acb8STomasz Jeznach     if (dma_memory_read(s->target_as, addr, &dc.ta, sizeof(uint64_t) * 2,
10550c54acb8STomasz Jeznach                         MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
10560c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_PDT_LOAD_FAULT;
10570c54acb8STomasz Jeznach     }
10580c54acb8STomasz Jeznach 
10590c54acb8STomasz Jeznach     /* Use FSC and TA from process directory entry. */
10600c54acb8STomasz Jeznach     ctx->ta = le64_to_cpu(dc.ta);
10610c54acb8STomasz Jeznach     ctx->satp = le64_to_cpu(dc.fsc);
10620c54acb8STomasz Jeznach 
10630c54acb8STomasz Jeznach     if (!(ctx->ta & RISCV_IOMMU_PC_TA_V)) {
10640c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_PDT_INVALID;
10650c54acb8STomasz Jeznach     }
10660c54acb8STomasz Jeznach 
10670c54acb8STomasz Jeznach     if (!riscv_iommu_validate_process_ctx(s, ctx)) {
10680c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_PDT_MISCONFIGURED;
10690c54acb8STomasz Jeznach     }
10700c54acb8STomasz Jeznach 
10710c54acb8STomasz Jeznach     return 0;
10720c54acb8STomasz Jeznach }
10730c54acb8STomasz Jeznach 
10740c54acb8STomasz Jeznach /* Translation Context cache support */
10750c54acb8STomasz Jeznach static gboolean riscv_iommu_ctx_equal(gconstpointer v1, gconstpointer v2)
10760c54acb8STomasz Jeznach {
10770c54acb8STomasz Jeznach     RISCVIOMMUContext *c1 = (RISCVIOMMUContext *) v1;
10780c54acb8STomasz Jeznach     RISCVIOMMUContext *c2 = (RISCVIOMMUContext *) v2;
10790c54acb8STomasz Jeznach     return c1->devid == c2->devid &&
10800c54acb8STomasz Jeznach            c1->process_id == c2->process_id;
10810c54acb8STomasz Jeznach }
10820c54acb8STomasz Jeznach 
10830c54acb8STomasz Jeznach static guint riscv_iommu_ctx_hash(gconstpointer v)
10840c54acb8STomasz Jeznach {
10850c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx = (RISCVIOMMUContext *) v;
10860c54acb8STomasz Jeznach     /*
10870c54acb8STomasz Jeznach      * Generate simple hash of (process_id, devid)
10880c54acb8STomasz Jeznach      * assuming 24-bit wide devid.
10890c54acb8STomasz Jeznach      */
10900c54acb8STomasz Jeznach     return (guint)(ctx->devid) + ((guint)(ctx->process_id) << 24);
10910c54acb8STomasz Jeznach }
10920c54acb8STomasz Jeznach 
10930c54acb8STomasz Jeznach static void riscv_iommu_ctx_inval_devid_procid(gpointer key, gpointer value,
10940c54acb8STomasz Jeznach                                                gpointer data)
10950c54acb8STomasz Jeznach {
10960c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx = (RISCVIOMMUContext *) value;
10970c54acb8STomasz Jeznach     RISCVIOMMUContext *arg = (RISCVIOMMUContext *) data;
10980c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_V &&
10990c54acb8STomasz Jeznach         ctx->devid == arg->devid &&
11000c54acb8STomasz Jeznach         ctx->process_id == arg->process_id) {
11010c54acb8STomasz Jeznach         ctx->tc &= ~RISCV_IOMMU_DC_TC_V;
11020c54acb8STomasz Jeznach     }
11030c54acb8STomasz Jeznach }
11040c54acb8STomasz Jeznach 
11050c54acb8STomasz Jeznach static void riscv_iommu_ctx_inval_devid(gpointer key, gpointer value,
11060c54acb8STomasz Jeznach                                         gpointer data)
11070c54acb8STomasz Jeznach {
11080c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx = (RISCVIOMMUContext *) value;
11090c54acb8STomasz Jeznach     RISCVIOMMUContext *arg = (RISCVIOMMUContext *) data;
11100c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_V &&
11110c54acb8STomasz Jeznach         ctx->devid == arg->devid) {
11120c54acb8STomasz Jeznach         ctx->tc &= ~RISCV_IOMMU_DC_TC_V;
11130c54acb8STomasz Jeznach     }
11140c54acb8STomasz Jeznach }
11150c54acb8STomasz Jeznach 
11160c54acb8STomasz Jeznach static void riscv_iommu_ctx_inval_all(gpointer key, gpointer value,
11170c54acb8STomasz Jeznach                                       gpointer data)
11180c54acb8STomasz Jeznach {
11190c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx = (RISCVIOMMUContext *) value;
11200c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_V) {
11210c54acb8STomasz Jeznach         ctx->tc &= ~RISCV_IOMMU_DC_TC_V;
11220c54acb8STomasz Jeznach     }
11230c54acb8STomasz Jeznach }
11240c54acb8STomasz Jeznach 
11250c54acb8STomasz Jeznach static void riscv_iommu_ctx_inval(RISCVIOMMUState *s, GHFunc func,
11260c54acb8STomasz Jeznach                                   uint32_t devid, uint32_t process_id)
11270c54acb8STomasz Jeznach {
11280c54acb8STomasz Jeznach     GHashTable *ctx_cache;
11290c54acb8STomasz Jeznach     RISCVIOMMUContext key = {
11300c54acb8STomasz Jeznach         .devid = devid,
11310c54acb8STomasz Jeznach         .process_id = process_id,
11320c54acb8STomasz Jeznach     };
11330c54acb8STomasz Jeznach     ctx_cache = g_hash_table_ref(s->ctx_cache);
11340c54acb8STomasz Jeznach     g_hash_table_foreach(ctx_cache, func, &key);
11350c54acb8STomasz Jeznach     g_hash_table_unref(ctx_cache);
11360c54acb8STomasz Jeznach }
11370c54acb8STomasz Jeznach 
11380c54acb8STomasz Jeznach /* Find or allocate translation context for a given {device_id, process_id} */
11390c54acb8STomasz Jeznach static RISCVIOMMUContext *riscv_iommu_ctx(RISCVIOMMUState *s,
11400c54acb8STomasz Jeznach                                           unsigned devid, unsigned process_id,
11410c54acb8STomasz Jeznach                                           void **ref)
11420c54acb8STomasz Jeznach {
11430c54acb8STomasz Jeznach     GHashTable *ctx_cache;
11440c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx;
11450c54acb8STomasz Jeznach     RISCVIOMMUContext key = {
11460c54acb8STomasz Jeznach         .devid = devid,
11470c54acb8STomasz Jeznach         .process_id = process_id,
11480c54acb8STomasz Jeznach     };
11490c54acb8STomasz Jeznach 
11500c54acb8STomasz Jeznach     ctx_cache = g_hash_table_ref(s->ctx_cache);
11510c54acb8STomasz Jeznach     ctx = g_hash_table_lookup(ctx_cache, &key);
11520c54acb8STomasz Jeznach 
11530c54acb8STomasz Jeznach     if (ctx && (ctx->tc & RISCV_IOMMU_DC_TC_V)) {
11540c54acb8STomasz Jeznach         *ref = ctx_cache;
11550c54acb8STomasz Jeznach         return ctx;
11560c54acb8STomasz Jeznach     }
11570c54acb8STomasz Jeznach 
11580c54acb8STomasz Jeznach     ctx = g_new0(RISCVIOMMUContext, 1);
11590c54acb8STomasz Jeznach     ctx->devid = devid;
11600c54acb8STomasz Jeznach     ctx->process_id = process_id;
11610c54acb8STomasz Jeznach 
11620c54acb8STomasz Jeznach     int fault = riscv_iommu_ctx_fetch(s, ctx);
11630c54acb8STomasz Jeznach     if (!fault) {
11640c54acb8STomasz Jeznach         if (g_hash_table_size(ctx_cache) >= LIMIT_CACHE_CTX) {
11650c54acb8STomasz Jeznach             g_hash_table_unref(ctx_cache);
11660c54acb8STomasz Jeznach             ctx_cache = g_hash_table_new_full(riscv_iommu_ctx_hash,
11670c54acb8STomasz Jeznach                                               riscv_iommu_ctx_equal,
11680c54acb8STomasz Jeznach                                               g_free, NULL);
11690c54acb8STomasz Jeznach             g_hash_table_ref(ctx_cache);
11700c54acb8STomasz Jeznach             g_hash_table_unref(qatomic_xchg(&s->ctx_cache, ctx_cache));
11710c54acb8STomasz Jeznach         }
11720c54acb8STomasz Jeznach         g_hash_table_add(ctx_cache, ctx);
11730c54acb8STomasz Jeznach         *ref = ctx_cache;
11740c54acb8STomasz Jeznach         return ctx;
11750c54acb8STomasz Jeznach     }
11760c54acb8STomasz Jeznach 
11770c54acb8STomasz Jeznach     g_hash_table_unref(ctx_cache);
11780c54acb8STomasz Jeznach     *ref = NULL;
11790c54acb8STomasz Jeznach 
11800c54acb8STomasz Jeznach     riscv_iommu_report_fault(s, ctx, RISCV_IOMMU_FQ_TTYPE_UADDR_RD,
11810c54acb8STomasz Jeznach                              fault, !!process_id, 0, 0);
11820c54acb8STomasz Jeznach 
11830c54acb8STomasz Jeznach     g_free(ctx);
11840c54acb8STomasz Jeznach     return NULL;
11850c54acb8STomasz Jeznach }
11860c54acb8STomasz Jeznach 
11870c54acb8STomasz Jeznach static void riscv_iommu_ctx_put(RISCVIOMMUState *s, void *ref)
11880c54acb8STomasz Jeznach {
11890c54acb8STomasz Jeznach     if (ref) {
11900c54acb8STomasz Jeznach         g_hash_table_unref((GHashTable *)ref);
11910c54acb8STomasz Jeznach     }
11920c54acb8STomasz Jeznach }
11930c54acb8STomasz Jeznach 
11940c54acb8STomasz Jeznach /* Find or allocate address space for a given device */
11950c54acb8STomasz Jeznach static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, uint32_t devid)
11960c54acb8STomasz Jeznach {
11970c54acb8STomasz Jeznach     RISCVIOMMUSpace *as;
11980c54acb8STomasz Jeznach 
11990c54acb8STomasz Jeznach     /* FIXME: PCIe bus remapping for attached endpoints. */
12000c54acb8STomasz Jeznach     devid |= s->bus << 8;
12010c54acb8STomasz Jeznach 
12020c54acb8STomasz Jeznach     QLIST_FOREACH(as, &s->spaces, list) {
12030c54acb8STomasz Jeznach         if (as->devid == devid) {
12040c54acb8STomasz Jeznach             break;
12050c54acb8STomasz Jeznach         }
12060c54acb8STomasz Jeznach     }
12070c54acb8STomasz Jeznach 
12080c54acb8STomasz Jeznach     if (as == NULL) {
12090c54acb8STomasz Jeznach         char name[64];
12100c54acb8STomasz Jeznach         as = g_new0(RISCVIOMMUSpace, 1);
12110c54acb8STomasz Jeznach 
12120c54acb8STomasz Jeznach         as->iommu = s;
12130c54acb8STomasz Jeznach         as->devid = devid;
12140c54acb8STomasz Jeznach 
12150c54acb8STomasz Jeznach         snprintf(name, sizeof(name), "riscv-iommu-%04x:%02x.%d-iova",
12160c54acb8STomasz Jeznach             PCI_BUS_NUM(as->devid), PCI_SLOT(as->devid), PCI_FUNC(as->devid));
12170c54acb8STomasz Jeznach 
12180c54acb8STomasz Jeznach         /* IOVA address space, untranslated addresses */
12190c54acb8STomasz Jeznach         memory_region_init_iommu(&as->iova_mr, sizeof(as->iova_mr),
12200c54acb8STomasz Jeznach             TYPE_RISCV_IOMMU_MEMORY_REGION,
12210c54acb8STomasz Jeznach             OBJECT(as), "riscv_iommu", UINT64_MAX);
12220c54acb8STomasz Jeznach         address_space_init(&as->iova_as, MEMORY_REGION(&as->iova_mr), name);
12230c54acb8STomasz Jeznach 
12240c54acb8STomasz Jeznach         QLIST_INSERT_HEAD(&s->spaces, as, list);
12250c54acb8STomasz Jeznach 
12260c54acb8STomasz Jeznach         trace_riscv_iommu_new(s->parent_obj.id, PCI_BUS_NUM(as->devid),
12270c54acb8STomasz Jeznach                 PCI_SLOT(as->devid), PCI_FUNC(as->devid));
12280c54acb8STomasz Jeznach     }
12290c54acb8STomasz Jeznach     return &as->iova_as;
12300c54acb8STomasz Jeznach }
12310c54acb8STomasz Jeznach 
12329d085a1cSTomasz Jeznach /* Translation Object cache support */
12339d085a1cSTomasz Jeznach static gboolean riscv_iommu_iot_equal(gconstpointer v1, gconstpointer v2)
12340c54acb8STomasz Jeznach {
12359d085a1cSTomasz Jeznach     RISCVIOMMUEntry *t1 = (RISCVIOMMUEntry *) v1;
12369d085a1cSTomasz Jeznach     RISCVIOMMUEntry *t2 = (RISCVIOMMUEntry *) v2;
12379d085a1cSTomasz Jeznach     return t1->gscid == t2->gscid && t1->pscid == t2->pscid &&
1238*fa622855SJason Chien            t1->iova == t2->iova && t1->tag == t2->tag;
12399d085a1cSTomasz Jeznach }
12409d085a1cSTomasz Jeznach 
12419d085a1cSTomasz Jeznach static guint riscv_iommu_iot_hash(gconstpointer v)
12429d085a1cSTomasz Jeznach {
12439d085a1cSTomasz Jeznach     RISCVIOMMUEntry *t = (RISCVIOMMUEntry *) v;
12449d085a1cSTomasz Jeznach     return (guint)t->iova;
12459d085a1cSTomasz Jeznach }
12469d085a1cSTomasz Jeznach 
1247*fa622855SJason Chien /* GV: 0 AV: 0 PSCV: 0 GVMA: 0 */
1248*fa622855SJason Chien /* GV: 0 AV: 0 GVMA: 1 */
1249*fa622855SJason Chien static
1250*fa622855SJason Chien void riscv_iommu_iot_inval_all(gpointer key, gpointer value, gpointer data)
1251*fa622855SJason Chien {
1252*fa622855SJason Chien     RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value;
1253*fa622855SJason Chien     RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data;
1254*fa622855SJason Chien     if (iot->tag == arg->tag) {
1255*fa622855SJason Chien         iot->perm = IOMMU_NONE;
1256*fa622855SJason Chien     }
1257*fa622855SJason Chien }
1258*fa622855SJason Chien 
1259*fa622855SJason Chien /* GV: 0 AV: 0 PSCV: 1 GVMA: 0 */
1260*fa622855SJason Chien static
1261*fa622855SJason Chien void riscv_iommu_iot_inval_pscid(gpointer key, gpointer value, gpointer data)
1262*fa622855SJason Chien {
1263*fa622855SJason Chien     RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value;
1264*fa622855SJason Chien     RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data;
1265*fa622855SJason Chien     if (iot->tag == arg->tag &&
1266*fa622855SJason Chien         iot->pscid == arg->pscid) {
1267*fa622855SJason Chien         iot->perm = IOMMU_NONE;
1268*fa622855SJason Chien     }
1269*fa622855SJason Chien }
1270*fa622855SJason Chien 
1271*fa622855SJason Chien /* GV: 0 AV: 1 PSCV: 0 GVMA: 0 */
1272*fa622855SJason Chien static
1273*fa622855SJason Chien void riscv_iommu_iot_inval_iova(gpointer key, gpointer value, gpointer data)
1274*fa622855SJason Chien {
1275*fa622855SJason Chien     RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value;
1276*fa622855SJason Chien     RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data;
1277*fa622855SJason Chien     if (iot->tag == arg->tag &&
1278*fa622855SJason Chien         iot->iova == arg->iova) {
1279*fa622855SJason Chien         iot->perm = IOMMU_NONE;
1280*fa622855SJason Chien     }
1281*fa622855SJason Chien }
1282*fa622855SJason Chien 
1283*fa622855SJason Chien /* GV: 0 AV: 1 PSCV: 1 GVMA: 0 */
12849d085a1cSTomasz Jeznach static void riscv_iommu_iot_inval_pscid_iova(gpointer key, gpointer value,
12859d085a1cSTomasz Jeznach                                              gpointer data)
12869d085a1cSTomasz Jeznach {
12879d085a1cSTomasz Jeznach     RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value;
12889d085a1cSTomasz Jeznach     RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data;
1289*fa622855SJason Chien     if (iot->tag == arg->tag &&
12909d085a1cSTomasz Jeznach         iot->pscid == arg->pscid &&
12919d085a1cSTomasz Jeznach         iot->iova == arg->iova) {
12929d085a1cSTomasz Jeznach         iot->perm = IOMMU_NONE;
12939d085a1cSTomasz Jeznach     }
12949d085a1cSTomasz Jeznach }
12959d085a1cSTomasz Jeznach 
1296*fa622855SJason Chien /* GV: 1 AV: 0 PSCV: 0 GVMA: 0 */
1297*fa622855SJason Chien /* GV: 1 AV: 0 GVMA: 1 */
1298*fa622855SJason Chien static
1299*fa622855SJason Chien void riscv_iommu_iot_inval_gscid(gpointer key, gpointer value, gpointer data)
1300*fa622855SJason Chien {
1301*fa622855SJason Chien     RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value;
1302*fa622855SJason Chien     RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data;
1303*fa622855SJason Chien     if (iot->tag == arg->tag &&
1304*fa622855SJason Chien         iot->gscid == arg->gscid) {
1305*fa622855SJason Chien         iot->perm = IOMMU_NONE;
1306*fa622855SJason Chien     }
1307*fa622855SJason Chien }
1308*fa622855SJason Chien 
1309*fa622855SJason Chien /* GV: 1 AV: 0 PSCV: 1 GVMA: 0 */
1310*fa622855SJason Chien static void riscv_iommu_iot_inval_gscid_pscid(gpointer key, gpointer value,
13119d085a1cSTomasz Jeznach                                               gpointer data)
13129d085a1cSTomasz Jeznach {
13139d085a1cSTomasz Jeznach     RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value;
13149d085a1cSTomasz Jeznach     RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data;
1315*fa622855SJason Chien     if (iot->tag == arg->tag &&
1316*fa622855SJason Chien         iot->gscid == arg->gscid &&
13179d085a1cSTomasz Jeznach         iot->pscid == arg->pscid) {
13189d085a1cSTomasz Jeznach         iot->perm = IOMMU_NONE;
13199d085a1cSTomasz Jeznach     }
13209d085a1cSTomasz Jeznach }
13219d085a1cSTomasz Jeznach 
1322*fa622855SJason Chien /* GV: 1 AV: 1 PSCV: 0 GVMA: 0 */
1323*fa622855SJason Chien /* GV: 1 AV: 1 GVMA: 1 */
1324*fa622855SJason Chien static void riscv_iommu_iot_inval_gscid_iova(gpointer key, gpointer value,
13259d085a1cSTomasz Jeznach                                              gpointer data)
13269d085a1cSTomasz Jeznach {
13279d085a1cSTomasz Jeznach     RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value;
13289d085a1cSTomasz Jeznach     RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data;
1329*fa622855SJason Chien     if (iot->tag == arg->tag &&
1330*fa622855SJason Chien         iot->gscid == arg->gscid &&
1331*fa622855SJason Chien         iot->iova == arg->iova) {
13329d085a1cSTomasz Jeznach         iot->perm = IOMMU_NONE;
13339d085a1cSTomasz Jeznach     }
13349d085a1cSTomasz Jeznach }
13359d085a1cSTomasz Jeznach 
1336*fa622855SJason Chien /* GV: 1 AV: 1 PSCV: 1 GVMA: 0 */
1337*fa622855SJason Chien static void riscv_iommu_iot_inval_gscid_pscid_iova(gpointer key, gpointer value,
13389d085a1cSTomasz Jeznach                                                    gpointer data)
13399d085a1cSTomasz Jeznach {
13409d085a1cSTomasz Jeznach     RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value;
13419d085a1cSTomasz Jeznach     RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data;
1342*fa622855SJason Chien     if (iot->tag == arg->tag &&
1343*fa622855SJason Chien         iot->gscid == arg->gscid &&
1344*fa622855SJason Chien         iot->pscid == arg->pscid &&
1345*fa622855SJason Chien         iot->iova == arg->iova) {
13469d085a1cSTomasz Jeznach         iot->perm = IOMMU_NONE;
13479d085a1cSTomasz Jeznach     }
13489d085a1cSTomasz Jeznach }
13499d085a1cSTomasz Jeznach 
13509d085a1cSTomasz Jeznach /* caller should keep ref-count for iot_cache object */
13519d085a1cSTomasz Jeznach static RISCVIOMMUEntry *riscv_iommu_iot_lookup(RISCVIOMMUContext *ctx,
1352*fa622855SJason Chien     GHashTable *iot_cache, hwaddr iova, RISCVIOMMUTransTag transtag)
13539d085a1cSTomasz Jeznach {
13549d085a1cSTomasz Jeznach     RISCVIOMMUEntry key = {
1355*fa622855SJason Chien         .tag   = transtag,
13569d085a1cSTomasz Jeznach         .gscid = get_field(ctx->gatp, RISCV_IOMMU_DC_IOHGATP_GSCID),
13579d085a1cSTomasz Jeznach         .pscid = get_field(ctx->ta, RISCV_IOMMU_DC_TA_PSCID),
13589d085a1cSTomasz Jeznach         .iova  = PPN_DOWN(iova),
13599d085a1cSTomasz Jeznach     };
13609d085a1cSTomasz Jeznach     return g_hash_table_lookup(iot_cache, &key);
13619d085a1cSTomasz Jeznach }
13629d085a1cSTomasz Jeznach 
13639d085a1cSTomasz Jeznach /* caller should keep ref-count for iot_cache object */
13649d085a1cSTomasz Jeznach static void riscv_iommu_iot_update(RISCVIOMMUState *s,
13659d085a1cSTomasz Jeznach     GHashTable *iot_cache, RISCVIOMMUEntry *iot)
13669d085a1cSTomasz Jeznach {
13679d085a1cSTomasz Jeznach     if (!s->iot_limit) {
13689d085a1cSTomasz Jeznach         return;
13699d085a1cSTomasz Jeznach     }
13709d085a1cSTomasz Jeznach 
13719d085a1cSTomasz Jeznach     if (g_hash_table_size(s->iot_cache) >= s->iot_limit) {
13729d085a1cSTomasz Jeznach         iot_cache = g_hash_table_new_full(riscv_iommu_iot_hash,
13739d085a1cSTomasz Jeznach                                           riscv_iommu_iot_equal,
13749d085a1cSTomasz Jeznach                                           g_free, NULL);
13759d085a1cSTomasz Jeznach         g_hash_table_unref(qatomic_xchg(&s->iot_cache, iot_cache));
13769d085a1cSTomasz Jeznach     }
13779d085a1cSTomasz Jeznach     g_hash_table_add(iot_cache, iot);
13789d085a1cSTomasz Jeznach }
13799d085a1cSTomasz Jeznach 
13809d085a1cSTomasz Jeznach static void riscv_iommu_iot_inval(RISCVIOMMUState *s, GHFunc func,
1381*fa622855SJason Chien     uint32_t gscid, uint32_t pscid, hwaddr iova, RISCVIOMMUTransTag transtag)
13829d085a1cSTomasz Jeznach {
13839d085a1cSTomasz Jeznach     GHashTable *iot_cache;
13849d085a1cSTomasz Jeznach     RISCVIOMMUEntry key = {
1385*fa622855SJason Chien         .tag = transtag,
13869d085a1cSTomasz Jeznach         .gscid = gscid,
13879d085a1cSTomasz Jeznach         .pscid = pscid,
13889d085a1cSTomasz Jeznach         .iova  = PPN_DOWN(iova),
13899d085a1cSTomasz Jeznach     };
13909d085a1cSTomasz Jeznach 
13919d085a1cSTomasz Jeznach     iot_cache = g_hash_table_ref(s->iot_cache);
13929d085a1cSTomasz Jeznach     g_hash_table_foreach(iot_cache, func, &key);
13939d085a1cSTomasz Jeznach     g_hash_table_unref(iot_cache);
13949d085a1cSTomasz Jeznach }
13959d085a1cSTomasz Jeznach 
1396*fa622855SJason Chien static RISCVIOMMUTransTag riscv_iommu_get_transtag(RISCVIOMMUContext *ctx)
1397*fa622855SJason Chien {
1398*fa622855SJason Chien     uint64_t satp = get_field(ctx->satp, RISCV_IOMMU_ATP_MODE_FIELD);
1399*fa622855SJason Chien     uint64_t gatp = get_field(ctx->gatp, RISCV_IOMMU_ATP_MODE_FIELD);
1400*fa622855SJason Chien 
1401*fa622855SJason Chien     if (satp == RISCV_IOMMU_DC_FSC_MODE_BARE) {
1402*fa622855SJason Chien         return (gatp == RISCV_IOMMU_DC_IOHGATP_MODE_BARE) ?
1403*fa622855SJason Chien             RISCV_IOMMU_TRANS_TAG_BY : RISCV_IOMMU_TRANS_TAG_VG;
1404*fa622855SJason Chien     } else {
1405*fa622855SJason Chien         return (gatp == RISCV_IOMMU_DC_IOHGATP_MODE_BARE) ?
1406*fa622855SJason Chien             RISCV_IOMMU_TRANS_TAG_SS : RISCV_IOMMU_TRANS_TAG_VN;
1407*fa622855SJason Chien     }
1408*fa622855SJason Chien }
1409*fa622855SJason Chien 
14109d085a1cSTomasz Jeznach static int riscv_iommu_translate(RISCVIOMMUState *s, RISCVIOMMUContext *ctx,
14119d085a1cSTomasz Jeznach     IOMMUTLBEntry *iotlb, bool enable_cache)
14129d085a1cSTomasz Jeznach {
1413*fa622855SJason Chien     RISCVIOMMUTransTag transtag = riscv_iommu_get_transtag(ctx);
14149d085a1cSTomasz Jeznach     RISCVIOMMUEntry *iot;
14159d085a1cSTomasz Jeznach     IOMMUAccessFlags perm;
14160c54acb8STomasz Jeznach     bool enable_pid;
14170c54acb8STomasz Jeznach     bool enable_pri;
14189d085a1cSTomasz Jeznach     GHashTable *iot_cache;
14190c54acb8STomasz Jeznach     int fault;
14200c54acb8STomasz Jeznach 
14219d085a1cSTomasz Jeznach     iot_cache = g_hash_table_ref(s->iot_cache);
14220c54acb8STomasz Jeznach     /*
14230c54acb8STomasz Jeznach      * TC[32] is reserved for custom extensions, used here to temporarily
14240c54acb8STomasz Jeznach      * enable automatic page-request generation for ATS queries.
14250c54acb8STomasz Jeznach      */
14260c54acb8STomasz Jeznach     enable_pri = (iotlb->perm == IOMMU_NONE) && (ctx->tc & BIT_ULL(32));
14270c54acb8STomasz Jeznach     enable_pid = (ctx->tc & RISCV_IOMMU_DC_TC_PDTV);
14280c54acb8STomasz Jeznach 
142969a9ae48STomasz Jeznach     /* Check for ATS request. */
143069a9ae48STomasz Jeznach     if (iotlb->perm == IOMMU_NONE) {
143169a9ae48STomasz Jeznach         /* Check if ATS is disabled. */
143269a9ae48STomasz Jeznach         if (!(ctx->tc & RISCV_IOMMU_DC_TC_EN_ATS)) {
143369a9ae48STomasz Jeznach             enable_pri = false;
143469a9ae48STomasz Jeznach             fault = RISCV_IOMMU_FQ_CAUSE_TTYPE_BLOCKED;
143569a9ae48STomasz Jeznach             goto done;
143669a9ae48STomasz Jeznach         }
143769a9ae48STomasz Jeznach     }
143869a9ae48STomasz Jeznach 
1439*fa622855SJason Chien     iot = riscv_iommu_iot_lookup(ctx, iot_cache, iotlb->iova, transtag);
14409d085a1cSTomasz Jeznach     perm = iot ? iot->perm : IOMMU_NONE;
14419d085a1cSTomasz Jeznach     if (perm != IOMMU_NONE) {
14429d085a1cSTomasz Jeznach         iotlb->translated_addr = PPN_PHYS(iot->phys);
14439d085a1cSTomasz Jeznach         iotlb->addr_mask = ~TARGET_PAGE_MASK;
14449d085a1cSTomasz Jeznach         iotlb->perm = perm;
14459d085a1cSTomasz Jeznach         fault = 0;
14469d085a1cSTomasz Jeznach         goto done;
14479d085a1cSTomasz Jeznach     }
14489d085a1cSTomasz Jeznach 
14490c54acb8STomasz Jeznach     /* Translate using device directory / page table information. */
14500c54acb8STomasz Jeznach     fault = riscv_iommu_spa_fetch(s, ctx, iotlb);
14510c54acb8STomasz Jeznach 
14529d085a1cSTomasz Jeznach     if (!fault && iotlb->target_as == &s->trap_as) {
14539d085a1cSTomasz Jeznach         /* Do not cache trapped MSI translations */
14549d085a1cSTomasz Jeznach         goto done;
14559d085a1cSTomasz Jeznach     }
14569d085a1cSTomasz Jeznach 
14579d085a1cSTomasz Jeznach     /*
14589d085a1cSTomasz Jeznach      * We made an implementation choice to not cache identity-mapped
14599d085a1cSTomasz Jeznach      * translations, as allowed by the specification, to avoid
14609d085a1cSTomasz Jeznach      * translation cache evictions for other devices sharing the
14619d085a1cSTomasz Jeznach      * IOMMU hardware model.
14629d085a1cSTomasz Jeznach      */
14639d085a1cSTomasz Jeznach     if (!fault && iotlb->translated_addr != iotlb->iova && enable_cache) {
14649d085a1cSTomasz Jeznach         iot = g_new0(RISCVIOMMUEntry, 1);
14659d085a1cSTomasz Jeznach         iot->iova = PPN_DOWN(iotlb->iova);
14669d085a1cSTomasz Jeznach         iot->phys = PPN_DOWN(iotlb->translated_addr);
14679d085a1cSTomasz Jeznach         iot->gscid = get_field(ctx->gatp, RISCV_IOMMU_DC_IOHGATP_GSCID);
14689d085a1cSTomasz Jeznach         iot->pscid = get_field(ctx->ta, RISCV_IOMMU_DC_TA_PSCID);
14699d085a1cSTomasz Jeznach         iot->perm = iotlb->perm;
1470*fa622855SJason Chien         iot->tag = transtag;
14719d085a1cSTomasz Jeznach         riscv_iommu_iot_update(s, iot_cache, iot);
14729d085a1cSTomasz Jeznach     }
14739d085a1cSTomasz Jeznach 
14749d085a1cSTomasz Jeznach done:
14759d085a1cSTomasz Jeznach     g_hash_table_unref(iot_cache);
14769d085a1cSTomasz Jeznach 
14770c54acb8STomasz Jeznach     if (enable_pri && fault) {
14780c54acb8STomasz Jeznach         struct riscv_iommu_pq_record pr = {0};
14790c54acb8STomasz Jeznach         if (enable_pid) {
14800c54acb8STomasz Jeznach             pr.hdr = set_field(RISCV_IOMMU_PREQ_HDR_PV,
14810c54acb8STomasz Jeznach                                RISCV_IOMMU_PREQ_HDR_PID, ctx->process_id);
14820c54acb8STomasz Jeznach         }
14830c54acb8STomasz Jeznach         pr.hdr = set_field(pr.hdr, RISCV_IOMMU_PREQ_HDR_DID, ctx->devid);
14840c54acb8STomasz Jeznach         pr.payload = (iotlb->iova & TARGET_PAGE_MASK) |
14850c54acb8STomasz Jeznach                      RISCV_IOMMU_PREQ_PAYLOAD_M;
14860c54acb8STomasz Jeznach         riscv_iommu_pri(s, &pr);
14870c54acb8STomasz Jeznach         return fault;
14880c54acb8STomasz Jeznach     }
14890c54acb8STomasz Jeznach 
14900c54acb8STomasz Jeznach     if (fault) {
149169a9ae48STomasz Jeznach         unsigned ttype = RISCV_IOMMU_FQ_TTYPE_PCIE_ATS_REQ;
14920c54acb8STomasz Jeznach 
14930c54acb8STomasz Jeznach         if (iotlb->perm & IOMMU_RW) {
14940c54acb8STomasz Jeznach             ttype = RISCV_IOMMU_FQ_TTYPE_UADDR_WR;
149569a9ae48STomasz Jeznach         } else if (iotlb->perm & IOMMU_RO) {
14960c54acb8STomasz Jeznach             ttype = RISCV_IOMMU_FQ_TTYPE_UADDR_RD;
14970c54acb8STomasz Jeznach         }
14980c54acb8STomasz Jeznach 
14990c54acb8STomasz Jeznach         riscv_iommu_report_fault(s, ctx, ttype, fault, enable_pid,
15000c54acb8STomasz Jeznach                                  iotlb->iova, iotlb->translated_addr);
15010c54acb8STomasz Jeznach         return fault;
15020c54acb8STomasz Jeznach     }
15030c54acb8STomasz Jeznach 
15040c54acb8STomasz Jeznach     return 0;
15050c54acb8STomasz Jeznach }
15060c54acb8STomasz Jeznach 
15070c54acb8STomasz Jeznach /* IOMMU Command Interface */
15080c54acb8STomasz Jeznach static MemTxResult riscv_iommu_iofence(RISCVIOMMUState *s, bool notify,
15090c54acb8STomasz Jeznach     uint64_t addr, uint32_t data)
15100c54acb8STomasz Jeznach {
15110c54acb8STomasz Jeznach     /*
15120c54acb8STomasz Jeznach      * ATS processing in this implementation of the IOMMU is synchronous,
15130c54acb8STomasz Jeznach      * no need to wait for completions here.
15140c54acb8STomasz Jeznach      */
15150c54acb8STomasz Jeznach     if (!notify) {
15160c54acb8STomasz Jeznach         return MEMTX_OK;
15170c54acb8STomasz Jeznach     }
15180c54acb8STomasz Jeznach 
15190c54acb8STomasz Jeznach     return dma_memory_write(s->target_as, addr, &data, sizeof(data),
15200c54acb8STomasz Jeznach         MEMTXATTRS_UNSPECIFIED);
15210c54acb8STomasz Jeznach }
15220c54acb8STomasz Jeznach 
152369a9ae48STomasz Jeznach static void riscv_iommu_ats(RISCVIOMMUState *s,
152469a9ae48STomasz Jeznach     struct riscv_iommu_command *cmd, IOMMUNotifierFlag flag,
152569a9ae48STomasz Jeznach     IOMMUAccessFlags perm,
152669a9ae48STomasz Jeznach     void (*trace_fn)(const char *id))
152769a9ae48STomasz Jeznach {
152869a9ae48STomasz Jeznach     RISCVIOMMUSpace *as = NULL;
152969a9ae48STomasz Jeznach     IOMMUNotifier *n;
153069a9ae48STomasz Jeznach     IOMMUTLBEvent event;
153169a9ae48STomasz Jeznach     uint32_t pid;
153269a9ae48STomasz Jeznach     uint32_t devid;
153369a9ae48STomasz Jeznach     const bool pv = cmd->dword0 & RISCV_IOMMU_CMD_ATS_PV;
153469a9ae48STomasz Jeznach 
153569a9ae48STomasz Jeznach     if (cmd->dword0 & RISCV_IOMMU_CMD_ATS_DSV) {
153669a9ae48STomasz Jeznach         /* Use device segment and requester id */
153769a9ae48STomasz Jeznach         devid = get_field(cmd->dword0,
153869a9ae48STomasz Jeznach             RISCV_IOMMU_CMD_ATS_DSEG | RISCV_IOMMU_CMD_ATS_RID);
153969a9ae48STomasz Jeznach     } else {
154069a9ae48STomasz Jeznach         devid = get_field(cmd->dword0, RISCV_IOMMU_CMD_ATS_RID);
154169a9ae48STomasz Jeznach     }
154269a9ae48STomasz Jeznach 
154369a9ae48STomasz Jeznach     pid = get_field(cmd->dword0, RISCV_IOMMU_CMD_ATS_PID);
154469a9ae48STomasz Jeznach 
154569a9ae48STomasz Jeznach     QLIST_FOREACH(as, &s->spaces, list) {
154669a9ae48STomasz Jeznach         if (as->devid == devid) {
154769a9ae48STomasz Jeznach             break;
154869a9ae48STomasz Jeznach         }
154969a9ae48STomasz Jeznach     }
155069a9ae48STomasz Jeznach 
155169a9ae48STomasz Jeznach     if (!as || !as->notifier) {
155269a9ae48STomasz Jeznach         return;
155369a9ae48STomasz Jeznach     }
155469a9ae48STomasz Jeznach 
155569a9ae48STomasz Jeznach     event.type = flag;
155669a9ae48STomasz Jeznach     event.entry.perm = perm;
155769a9ae48STomasz Jeznach     event.entry.target_as = s->target_as;
155869a9ae48STomasz Jeznach 
155969a9ae48STomasz Jeznach     IOMMU_NOTIFIER_FOREACH(n, &as->iova_mr) {
156069a9ae48STomasz Jeznach         if (!pv || n->iommu_idx == pid) {
156169a9ae48STomasz Jeznach             event.entry.iova = n->start;
156269a9ae48STomasz Jeznach             event.entry.addr_mask = n->end - n->start;
156369a9ae48STomasz Jeznach             trace_fn(as->iova_mr.parent_obj.name);
156469a9ae48STomasz Jeznach             memory_region_notify_iommu_one(n, &event);
156569a9ae48STomasz Jeznach         }
156669a9ae48STomasz Jeznach     }
156769a9ae48STomasz Jeznach }
156869a9ae48STomasz Jeznach 
156969a9ae48STomasz Jeznach static void riscv_iommu_ats_inval(RISCVIOMMUState *s,
157069a9ae48STomasz Jeznach     struct riscv_iommu_command *cmd)
157169a9ae48STomasz Jeznach {
157269a9ae48STomasz Jeznach     return riscv_iommu_ats(s, cmd, IOMMU_NOTIFIER_DEVIOTLB_UNMAP, IOMMU_NONE,
157369a9ae48STomasz Jeznach                            trace_riscv_iommu_ats_inval);
157469a9ae48STomasz Jeznach }
157569a9ae48STomasz Jeznach 
157669a9ae48STomasz Jeznach static void riscv_iommu_ats_prgr(RISCVIOMMUState *s,
157769a9ae48STomasz Jeznach     struct riscv_iommu_command *cmd)
157869a9ae48STomasz Jeznach {
157969a9ae48STomasz Jeznach     unsigned resp_code = get_field(cmd->dword1,
158069a9ae48STomasz Jeznach                                    RISCV_IOMMU_CMD_ATS_PRGR_RESP_CODE);
158169a9ae48STomasz Jeznach 
158269a9ae48STomasz Jeznach     /* Using the access flag to carry response code information */
158369a9ae48STomasz Jeznach     IOMMUAccessFlags perm = resp_code ? IOMMU_NONE : IOMMU_RW;
158469a9ae48STomasz Jeznach     return riscv_iommu_ats(s, cmd, IOMMU_NOTIFIER_MAP, perm,
158569a9ae48STomasz Jeznach                            trace_riscv_iommu_ats_prgr);
158669a9ae48STomasz Jeznach }
158769a9ae48STomasz Jeznach 
15880c54acb8STomasz Jeznach static void riscv_iommu_process_ddtp(RISCVIOMMUState *s)
15890c54acb8STomasz Jeznach {
15900c54acb8STomasz Jeznach     uint64_t old_ddtp = s->ddtp;
15910c54acb8STomasz Jeznach     uint64_t new_ddtp = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_DDTP);
15920c54acb8STomasz Jeznach     unsigned new_mode = get_field(new_ddtp, RISCV_IOMMU_DDTP_MODE);
15930c54acb8STomasz Jeznach     unsigned old_mode = get_field(old_ddtp, RISCV_IOMMU_DDTP_MODE);
15940c54acb8STomasz Jeznach     bool ok = false;
15950c54acb8STomasz Jeznach 
15960c54acb8STomasz Jeznach     /*
15970c54acb8STomasz Jeznach      * Check for allowed DDTP.MODE transitions:
15980c54acb8STomasz Jeznach      * {OFF, BARE}        -> {OFF, BARE, 1LVL, 2LVL, 3LVL}
15990c54acb8STomasz Jeznach      * {1LVL, 2LVL, 3LVL} -> {OFF, BARE}
16000c54acb8STomasz Jeznach      */
16010c54acb8STomasz Jeznach     if (new_mode == old_mode ||
16020c54acb8STomasz Jeznach         new_mode == RISCV_IOMMU_DDTP_MODE_OFF ||
16030c54acb8STomasz Jeznach         new_mode == RISCV_IOMMU_DDTP_MODE_BARE) {
16040c54acb8STomasz Jeznach         ok = true;
16050c54acb8STomasz Jeznach     } else if (new_mode == RISCV_IOMMU_DDTP_MODE_1LVL ||
16060c54acb8STomasz Jeznach                new_mode == RISCV_IOMMU_DDTP_MODE_2LVL ||
16070c54acb8STomasz Jeznach                new_mode == RISCV_IOMMU_DDTP_MODE_3LVL) {
16080c54acb8STomasz Jeznach         ok = old_mode == RISCV_IOMMU_DDTP_MODE_OFF ||
16090c54acb8STomasz Jeznach              old_mode == RISCV_IOMMU_DDTP_MODE_BARE;
16100c54acb8STomasz Jeznach     }
16110c54acb8STomasz Jeznach 
16120c54acb8STomasz Jeznach     if (ok) {
16130c54acb8STomasz Jeznach         /* clear reserved and busy bits, report back sanitized version */
16140c54acb8STomasz Jeznach         new_ddtp = set_field(new_ddtp & RISCV_IOMMU_DDTP_PPN,
16150c54acb8STomasz Jeznach                              RISCV_IOMMU_DDTP_MODE, new_mode);
16160c54acb8STomasz Jeznach     } else {
16170c54acb8STomasz Jeznach         new_ddtp = old_ddtp;
16180c54acb8STomasz Jeznach     }
16190c54acb8STomasz Jeznach     s->ddtp = new_ddtp;
16200c54acb8STomasz Jeznach 
16210c54acb8STomasz Jeznach     riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_DDTP, new_ddtp);
16220c54acb8STomasz Jeznach }
16230c54acb8STomasz Jeznach 
16240c54acb8STomasz Jeznach /* Command function and opcode field. */
16250c54acb8STomasz Jeznach #define RISCV_IOMMU_CMD(func, op) (((func) << 7) | (op))
16260c54acb8STomasz Jeznach 
16270c54acb8STomasz Jeznach static void riscv_iommu_process_cq_tail(RISCVIOMMUState *s)
16280c54acb8STomasz Jeznach {
16290c54acb8STomasz Jeznach     struct riscv_iommu_command cmd;
16300c54acb8STomasz Jeznach     MemTxResult res;
16310c54acb8STomasz Jeznach     dma_addr_t addr;
16320c54acb8STomasz Jeznach     uint32_t tail, head, ctrl;
16330c54acb8STomasz Jeznach     uint64_t cmd_opcode;
16340c54acb8STomasz Jeznach     GHFunc func;
16350c54acb8STomasz Jeznach 
16360c54acb8STomasz Jeznach     ctrl = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQCSR);
16370c54acb8STomasz Jeznach     tail = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQT) & s->cq_mask;
16380c54acb8STomasz Jeznach     head = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQH) & s->cq_mask;
16390c54acb8STomasz Jeznach 
16400c54acb8STomasz Jeznach     /* Check for pending error or queue processing disabled */
16410c54acb8STomasz Jeznach     if (!(ctrl & RISCV_IOMMU_CQCSR_CQON) ||
16420c54acb8STomasz Jeznach         !!(ctrl & (RISCV_IOMMU_CQCSR_CMD_ILL | RISCV_IOMMU_CQCSR_CQMF))) {
16430c54acb8STomasz Jeznach         return;
16440c54acb8STomasz Jeznach     }
16450c54acb8STomasz Jeznach 
16460c54acb8STomasz Jeznach     while (tail != head) {
16470c54acb8STomasz Jeznach         addr = s->cq_addr  + head * sizeof(cmd);
16480c54acb8STomasz Jeznach         res = dma_memory_read(s->target_as, addr, &cmd, sizeof(cmd),
16490c54acb8STomasz Jeznach                               MEMTXATTRS_UNSPECIFIED);
16500c54acb8STomasz Jeznach 
16510c54acb8STomasz Jeznach         if (res != MEMTX_OK) {
16520c54acb8STomasz Jeznach             riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR,
16530c54acb8STomasz Jeznach                                   RISCV_IOMMU_CQCSR_CQMF, 0);
16540c54acb8STomasz Jeznach             goto fault;
16550c54acb8STomasz Jeznach         }
16560c54acb8STomasz Jeznach 
16570c54acb8STomasz Jeznach         trace_riscv_iommu_cmd(s->parent_obj.id, cmd.dword0, cmd.dword1);
16580c54acb8STomasz Jeznach 
16590c54acb8STomasz Jeznach         cmd_opcode = get_field(cmd.dword0,
16600c54acb8STomasz Jeznach                                RISCV_IOMMU_CMD_OPCODE | RISCV_IOMMU_CMD_FUNC);
16610c54acb8STomasz Jeznach 
16620c54acb8STomasz Jeznach         switch (cmd_opcode) {
16630c54acb8STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IOFENCE_FUNC_C,
16640c54acb8STomasz Jeznach                              RISCV_IOMMU_CMD_IOFENCE_OPCODE):
16650c54acb8STomasz Jeznach             res = riscv_iommu_iofence(s,
16660c54acb8STomasz Jeznach                 cmd.dword0 & RISCV_IOMMU_CMD_IOFENCE_AV, cmd.dword1 << 2,
16670c54acb8STomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IOFENCE_DATA));
16680c54acb8STomasz Jeznach 
16690c54acb8STomasz Jeznach             if (res != MEMTX_OK) {
16700c54acb8STomasz Jeznach                 riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR,
16710c54acb8STomasz Jeznach                                       RISCV_IOMMU_CQCSR_CQMF, 0);
16720c54acb8STomasz Jeznach                 goto fault;
16730c54acb8STomasz Jeznach             }
16740c54acb8STomasz Jeznach             break;
16750c54acb8STomasz Jeznach 
16760c54acb8STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IOTINVAL_FUNC_GVMA,
16770c54acb8STomasz Jeznach                              RISCV_IOMMU_CMD_IOTINVAL_OPCODE):
1678*fa622855SJason Chien         {
1679*fa622855SJason Chien             bool gv = !!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_GV);
1680*fa622855SJason Chien             bool av = !!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_AV);
1681*fa622855SJason Chien             bool pscv = !!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_PSCV);
1682*fa622855SJason Chien             uint32_t gscid = get_field(cmd.dword0,
1683*fa622855SJason Chien                                        RISCV_IOMMU_CMD_IOTINVAL_GSCID);
1684*fa622855SJason Chien             uint32_t pscid = get_field(cmd.dword0,
1685*fa622855SJason Chien                                        RISCV_IOMMU_CMD_IOTINVAL_PSCID);
1686*fa622855SJason Chien             hwaddr iova = (cmd.dword1 << 2) & TARGET_PAGE_MASK;
1687*fa622855SJason Chien 
1688*fa622855SJason Chien             if (pscv) {
16890c54acb8STomasz Jeznach                 /* illegal command arguments IOTINVAL.GVMA & PSCV == 1 */
16900c54acb8STomasz Jeznach                 goto cmd_ill;
16910c54acb8STomasz Jeznach             }
1692*fa622855SJason Chien 
1693*fa622855SJason Chien             func = riscv_iommu_iot_inval_all;
1694*fa622855SJason Chien 
1695*fa622855SJason Chien             if (gv) {
1696*fa622855SJason Chien                 func = (av) ? riscv_iommu_iot_inval_gscid_iova :
1697*fa622855SJason Chien                               riscv_iommu_iot_inval_gscid;
1698*fa622855SJason Chien             }
1699*fa622855SJason Chien 
1700*fa622855SJason Chien             riscv_iommu_iot_inval(
1701*fa622855SJason Chien                 s, func, gscid, pscid, iova, RISCV_IOMMU_TRANS_TAG_VG);
1702*fa622855SJason Chien 
1703*fa622855SJason Chien             riscv_iommu_iot_inval(
1704*fa622855SJason Chien                 s, func, gscid, pscid, iova, RISCV_IOMMU_TRANS_TAG_VN);
17050c54acb8STomasz Jeznach             break;
1706*fa622855SJason Chien         }
17070c54acb8STomasz Jeznach 
17080c54acb8STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IOTINVAL_FUNC_VMA,
17090c54acb8STomasz Jeznach                              RISCV_IOMMU_CMD_IOTINVAL_OPCODE):
1710*fa622855SJason Chien         {
1711*fa622855SJason Chien             bool gv = !!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_GV);
1712*fa622855SJason Chien             bool av = !!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_AV);
1713*fa622855SJason Chien             bool pscv = !!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_PSCV);
1714*fa622855SJason Chien             uint32_t gscid = get_field(cmd.dword0,
1715*fa622855SJason Chien                                        RISCV_IOMMU_CMD_IOTINVAL_GSCID);
1716*fa622855SJason Chien             uint32_t pscid = get_field(cmd.dword0,
1717*fa622855SJason Chien                                        RISCV_IOMMU_CMD_IOTINVAL_PSCID);
1718*fa622855SJason Chien             hwaddr iova = (cmd.dword1 << 2) & TARGET_PAGE_MASK;
1719*fa622855SJason Chien             RISCVIOMMUTransTag transtag;
1720*fa622855SJason Chien 
1721*fa622855SJason Chien             if (gv) {
1722*fa622855SJason Chien                 transtag = RISCV_IOMMU_TRANS_TAG_VN;
1723*fa622855SJason Chien                 if (pscv) {
1724*fa622855SJason Chien                     func = (av) ? riscv_iommu_iot_inval_gscid_pscid_iova :
1725*fa622855SJason Chien                                   riscv_iommu_iot_inval_gscid_pscid;
17269d085a1cSTomasz Jeznach                 } else {
1727*fa622855SJason Chien                     func = (av) ? riscv_iommu_iot_inval_gscid_iova :
1728*fa622855SJason Chien                                   riscv_iommu_iot_inval_gscid;
17299d085a1cSTomasz Jeznach                 }
1730*fa622855SJason Chien             } else {
1731*fa622855SJason Chien                 transtag = RISCV_IOMMU_TRANS_TAG_SS;
1732*fa622855SJason Chien                 if (pscv) {
1733*fa622855SJason Chien                     func = (av) ? riscv_iommu_iot_inval_pscid_iova :
1734*fa622855SJason Chien                                   riscv_iommu_iot_inval_pscid;
1735*fa622855SJason Chien                 } else {
1736*fa622855SJason Chien                     func = (av) ? riscv_iommu_iot_inval_iova :
1737*fa622855SJason Chien                                   riscv_iommu_iot_inval_all;
1738*fa622855SJason Chien                 }
1739*fa622855SJason Chien             }
1740*fa622855SJason Chien 
1741*fa622855SJason Chien             riscv_iommu_iot_inval(s, func, gscid, pscid, iova, transtag);
17420c54acb8STomasz Jeznach             break;
1743*fa622855SJason Chien         }
17440c54acb8STomasz Jeznach 
17450c54acb8STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IODIR_FUNC_INVAL_DDT,
17460c54acb8STomasz Jeznach                              RISCV_IOMMU_CMD_IODIR_OPCODE):
17470c54acb8STomasz Jeznach             if (!(cmd.dword0 & RISCV_IOMMU_CMD_IODIR_DV)) {
17480c54acb8STomasz Jeznach                 /* invalidate all device context cache mappings */
17490c54acb8STomasz Jeznach                 func = riscv_iommu_ctx_inval_all;
17500c54acb8STomasz Jeznach             } else {
17510c54acb8STomasz Jeznach                 /* invalidate all device context matching DID */
17520c54acb8STomasz Jeznach                 func = riscv_iommu_ctx_inval_devid;
17530c54acb8STomasz Jeznach             }
17540c54acb8STomasz Jeznach             riscv_iommu_ctx_inval(s, func,
17550c54acb8STomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IODIR_DID), 0);
17560c54acb8STomasz Jeznach             break;
17570c54acb8STomasz Jeznach 
17580c54acb8STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IODIR_FUNC_INVAL_PDT,
17590c54acb8STomasz Jeznach                              RISCV_IOMMU_CMD_IODIR_OPCODE):
17600c54acb8STomasz Jeznach             if (!(cmd.dword0 & RISCV_IOMMU_CMD_IODIR_DV)) {
17610c54acb8STomasz Jeznach                 /* illegal command arguments IODIR_PDT & DV == 0 */
17620c54acb8STomasz Jeznach                 goto cmd_ill;
17630c54acb8STomasz Jeznach             } else {
17640c54acb8STomasz Jeznach                 func = riscv_iommu_ctx_inval_devid_procid;
17650c54acb8STomasz Jeznach             }
17660c54acb8STomasz Jeznach             riscv_iommu_ctx_inval(s, func,
17670c54acb8STomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IODIR_DID),
17680c54acb8STomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IODIR_PID));
17690c54acb8STomasz Jeznach             break;
17700c54acb8STomasz Jeznach 
177169a9ae48STomasz Jeznach         /* ATS commands */
177269a9ae48STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_ATS_FUNC_INVAL,
177369a9ae48STomasz Jeznach                              RISCV_IOMMU_CMD_ATS_OPCODE):
177469a9ae48STomasz Jeznach             if (!s->enable_ats) {
177569a9ae48STomasz Jeznach                 goto cmd_ill;
177669a9ae48STomasz Jeznach             }
177769a9ae48STomasz Jeznach 
177869a9ae48STomasz Jeznach             riscv_iommu_ats_inval(s, &cmd);
177969a9ae48STomasz Jeznach             break;
178069a9ae48STomasz Jeznach 
178169a9ae48STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_ATS_FUNC_PRGR,
178269a9ae48STomasz Jeznach                              RISCV_IOMMU_CMD_ATS_OPCODE):
178369a9ae48STomasz Jeznach             if (!s->enable_ats) {
178469a9ae48STomasz Jeznach                 goto cmd_ill;
178569a9ae48STomasz Jeznach             }
178669a9ae48STomasz Jeznach 
178769a9ae48STomasz Jeznach             riscv_iommu_ats_prgr(s, &cmd);
178869a9ae48STomasz Jeznach             break;
178969a9ae48STomasz Jeznach 
17900c54acb8STomasz Jeznach         default:
17910c54acb8STomasz Jeznach         cmd_ill:
17920c54acb8STomasz Jeznach             /* Invalid instruction, do not advance instruction index. */
17930c54acb8STomasz Jeznach             riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR,
17940c54acb8STomasz Jeznach                 RISCV_IOMMU_CQCSR_CMD_ILL, 0);
17950c54acb8STomasz Jeznach             goto fault;
17960c54acb8STomasz Jeznach         }
17970c54acb8STomasz Jeznach 
17980c54acb8STomasz Jeznach         /* Advance and update head pointer after command completes. */
17990c54acb8STomasz Jeznach         head = (head + 1) & s->cq_mask;
18000c54acb8STomasz Jeznach         riscv_iommu_reg_set32(s, RISCV_IOMMU_REG_CQH, head);
18010c54acb8STomasz Jeznach     }
18020c54acb8STomasz Jeznach     return;
18030c54acb8STomasz Jeznach 
18040c54acb8STomasz Jeznach fault:
18050c54acb8STomasz Jeznach     if (ctrl & RISCV_IOMMU_CQCSR_CIE) {
18060c54acb8STomasz Jeznach         riscv_iommu_notify(s, RISCV_IOMMU_INTR_CQ);
18070c54acb8STomasz Jeznach     }
18080c54acb8STomasz Jeznach }
18090c54acb8STomasz Jeznach 
18100c54acb8STomasz Jeznach static void riscv_iommu_process_cq_control(RISCVIOMMUState *s)
18110c54acb8STomasz Jeznach {
18120c54acb8STomasz Jeznach     uint64_t base;
18130c54acb8STomasz Jeznach     uint32_t ctrl_set = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQCSR);
18140c54acb8STomasz Jeznach     uint32_t ctrl_clr;
18150c54acb8STomasz Jeznach     bool enable = !!(ctrl_set & RISCV_IOMMU_CQCSR_CQEN);
18160c54acb8STomasz Jeznach     bool active = !!(ctrl_set & RISCV_IOMMU_CQCSR_CQON);
18170c54acb8STomasz Jeznach 
18180c54acb8STomasz Jeznach     if (enable && !active) {
18190c54acb8STomasz Jeznach         base = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_CQB);
18200c54acb8STomasz Jeznach         s->cq_mask = (2ULL << get_field(base, RISCV_IOMMU_CQB_LOG2SZ)) - 1;
18210c54acb8STomasz Jeznach         s->cq_addr = PPN_PHYS(get_field(base, RISCV_IOMMU_CQB_PPN));
18220c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQT], ~s->cq_mask);
18230c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_CQH], 0);
18240c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_CQT], 0);
18250c54acb8STomasz Jeznach         ctrl_set = RISCV_IOMMU_CQCSR_CQON;
18260c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_CQCSR_BUSY | RISCV_IOMMU_CQCSR_CQMF |
18270c54acb8STomasz Jeznach                    RISCV_IOMMU_CQCSR_CMD_ILL | RISCV_IOMMU_CQCSR_CMD_TO |
18280c54acb8STomasz Jeznach                    RISCV_IOMMU_CQCSR_FENCE_W_IP;
18290c54acb8STomasz Jeznach     } else if (!enable && active) {
18300c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQT], ~0);
18310c54acb8STomasz Jeznach         ctrl_set = 0;
18320c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_CQCSR_BUSY | RISCV_IOMMU_CQCSR_CQON;
18330c54acb8STomasz Jeznach     } else {
18340c54acb8STomasz Jeznach         ctrl_set = 0;
18350c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_CQCSR_BUSY;
18360c54acb8STomasz Jeznach     }
18370c54acb8STomasz Jeznach 
18380c54acb8STomasz Jeznach     riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR, ctrl_set, ctrl_clr);
18390c54acb8STomasz Jeznach }
18400c54acb8STomasz Jeznach 
18410c54acb8STomasz Jeznach static void riscv_iommu_process_fq_control(RISCVIOMMUState *s)
18420c54acb8STomasz Jeznach {
18430c54acb8STomasz Jeznach     uint64_t base;
18440c54acb8STomasz Jeznach     uint32_t ctrl_set = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQCSR);
18450c54acb8STomasz Jeznach     uint32_t ctrl_clr;
18460c54acb8STomasz Jeznach     bool enable = !!(ctrl_set & RISCV_IOMMU_FQCSR_FQEN);
18470c54acb8STomasz Jeznach     bool active = !!(ctrl_set & RISCV_IOMMU_FQCSR_FQON);
18480c54acb8STomasz Jeznach 
18490c54acb8STomasz Jeznach     if (enable && !active) {
18500c54acb8STomasz Jeznach         base = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_FQB);
18510c54acb8STomasz Jeznach         s->fq_mask = (2ULL << get_field(base, RISCV_IOMMU_FQB_LOG2SZ)) - 1;
18520c54acb8STomasz Jeznach         s->fq_addr = PPN_PHYS(get_field(base, RISCV_IOMMU_FQB_PPN));
18530c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQH], ~s->fq_mask);
18540c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_FQH], 0);
18550c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_FQT], 0);
18560c54acb8STomasz Jeznach         ctrl_set = RISCV_IOMMU_FQCSR_FQON;
18570c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_FQCSR_BUSY | RISCV_IOMMU_FQCSR_FQMF |
18580c54acb8STomasz Jeznach             RISCV_IOMMU_FQCSR_FQOF;
18590c54acb8STomasz Jeznach     } else if (!enable && active) {
18600c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQH], ~0);
18610c54acb8STomasz Jeznach         ctrl_set = 0;
18620c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_FQCSR_BUSY | RISCV_IOMMU_FQCSR_FQON;
18630c54acb8STomasz Jeznach     } else {
18640c54acb8STomasz Jeznach         ctrl_set = 0;
18650c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_FQCSR_BUSY;
18660c54acb8STomasz Jeznach     }
18670c54acb8STomasz Jeznach 
18680c54acb8STomasz Jeznach     riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_FQCSR, ctrl_set, ctrl_clr);
18690c54acb8STomasz Jeznach }
18700c54acb8STomasz Jeznach 
18710c54acb8STomasz Jeznach static void riscv_iommu_process_pq_control(RISCVIOMMUState *s)
18720c54acb8STomasz Jeznach {
18730c54acb8STomasz Jeznach     uint64_t base;
18740c54acb8STomasz Jeznach     uint32_t ctrl_set = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_PQCSR);
18750c54acb8STomasz Jeznach     uint32_t ctrl_clr;
18760c54acb8STomasz Jeznach     bool enable = !!(ctrl_set & RISCV_IOMMU_PQCSR_PQEN);
18770c54acb8STomasz Jeznach     bool active = !!(ctrl_set & RISCV_IOMMU_PQCSR_PQON);
18780c54acb8STomasz Jeznach 
18790c54acb8STomasz Jeznach     if (enable && !active) {
18800c54acb8STomasz Jeznach         base = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_PQB);
18810c54acb8STomasz Jeznach         s->pq_mask = (2ULL << get_field(base, RISCV_IOMMU_PQB_LOG2SZ)) - 1;
18820c54acb8STomasz Jeznach         s->pq_addr = PPN_PHYS(get_field(base, RISCV_IOMMU_PQB_PPN));
18830c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQH], ~s->pq_mask);
18840c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_PQH], 0);
18850c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_PQT], 0);
18860c54acb8STomasz Jeznach         ctrl_set = RISCV_IOMMU_PQCSR_PQON;
18870c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_PQCSR_BUSY | RISCV_IOMMU_PQCSR_PQMF |
18880c54acb8STomasz Jeznach             RISCV_IOMMU_PQCSR_PQOF;
18890c54acb8STomasz Jeznach     } else if (!enable && active) {
18900c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQH], ~0);
18910c54acb8STomasz Jeznach         ctrl_set = 0;
18920c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_PQCSR_BUSY | RISCV_IOMMU_PQCSR_PQON;
18930c54acb8STomasz Jeznach     } else {
18940c54acb8STomasz Jeznach         ctrl_set = 0;
18950c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_PQCSR_BUSY;
18960c54acb8STomasz Jeznach     }
18970c54acb8STomasz Jeznach 
18980c54acb8STomasz Jeznach     riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_PQCSR, ctrl_set, ctrl_clr);
18990c54acb8STomasz Jeznach }
19000c54acb8STomasz Jeznach 
1901a7aa525bSTomasz Jeznach static void riscv_iommu_process_dbg(RISCVIOMMUState *s)
1902a7aa525bSTomasz Jeznach {
1903a7aa525bSTomasz Jeznach     uint64_t iova = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_TR_REQ_IOVA);
1904a7aa525bSTomasz Jeznach     uint64_t ctrl = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_TR_REQ_CTL);
1905a7aa525bSTomasz Jeznach     unsigned devid = get_field(ctrl, RISCV_IOMMU_TR_REQ_CTL_DID);
1906a7aa525bSTomasz Jeznach     unsigned pid = get_field(ctrl, RISCV_IOMMU_TR_REQ_CTL_PID);
1907a7aa525bSTomasz Jeznach     RISCVIOMMUContext *ctx;
1908a7aa525bSTomasz Jeznach     void *ref;
1909a7aa525bSTomasz Jeznach 
1910a7aa525bSTomasz Jeznach     if (!(ctrl & RISCV_IOMMU_TR_REQ_CTL_GO_BUSY)) {
1911a7aa525bSTomasz Jeznach         return;
1912a7aa525bSTomasz Jeznach     }
1913a7aa525bSTomasz Jeznach 
1914a7aa525bSTomasz Jeznach     ctx = riscv_iommu_ctx(s, devid, pid, &ref);
1915a7aa525bSTomasz Jeznach     if (ctx == NULL) {
1916a7aa525bSTomasz Jeznach         riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_TR_RESPONSE,
1917a7aa525bSTomasz Jeznach                                  RISCV_IOMMU_TR_RESPONSE_FAULT |
1918a7aa525bSTomasz Jeznach                                  (RISCV_IOMMU_FQ_CAUSE_DMA_DISABLED << 10));
1919a7aa525bSTomasz Jeznach     } else {
1920a7aa525bSTomasz Jeznach         IOMMUTLBEntry iotlb = {
1921a7aa525bSTomasz Jeznach             .iova = iova,
1922a7aa525bSTomasz Jeznach             .perm = ctrl & RISCV_IOMMU_TR_REQ_CTL_NW ? IOMMU_RO : IOMMU_RW,
1923a7aa525bSTomasz Jeznach             .addr_mask = ~0,
1924a7aa525bSTomasz Jeznach             .target_as = NULL,
1925a7aa525bSTomasz Jeznach         };
1926a7aa525bSTomasz Jeznach         int fault = riscv_iommu_translate(s, ctx, &iotlb, false);
1927a7aa525bSTomasz Jeznach         if (fault) {
1928a7aa525bSTomasz Jeznach             iova = RISCV_IOMMU_TR_RESPONSE_FAULT | (((uint64_t) fault) << 10);
1929a7aa525bSTomasz Jeznach         } else {
1930a7aa525bSTomasz Jeznach             iova = iotlb.translated_addr & ~iotlb.addr_mask;
1931a7aa525bSTomasz Jeznach             iova >>= TARGET_PAGE_BITS;
1932a7aa525bSTomasz Jeznach             iova &= RISCV_IOMMU_TR_RESPONSE_PPN;
1933a7aa525bSTomasz Jeznach 
1934a7aa525bSTomasz Jeznach             /* We do not support superpages (> 4kbs) for now */
1935a7aa525bSTomasz Jeznach             iova &= ~RISCV_IOMMU_TR_RESPONSE_S;
1936a7aa525bSTomasz Jeznach         }
1937a7aa525bSTomasz Jeznach         riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_TR_RESPONSE, iova);
1938a7aa525bSTomasz Jeznach     }
1939a7aa525bSTomasz Jeznach 
1940a7aa525bSTomasz Jeznach     riscv_iommu_reg_mod64(s, RISCV_IOMMU_REG_TR_REQ_CTL, 0,
1941a7aa525bSTomasz Jeznach         RISCV_IOMMU_TR_REQ_CTL_GO_BUSY);
1942a7aa525bSTomasz Jeznach     riscv_iommu_ctx_put(s, ref);
1943a7aa525bSTomasz Jeznach }
1944a7aa525bSTomasz Jeznach 
19450c54acb8STomasz Jeznach typedef void riscv_iommu_process_fn(RISCVIOMMUState *s);
19460c54acb8STomasz Jeznach 
19470c54acb8STomasz Jeznach static void riscv_iommu_update_icvec(RISCVIOMMUState *s, uint64_t data)
19480c54acb8STomasz Jeznach {
19490c54acb8STomasz Jeznach     uint64_t icvec = 0;
19500c54acb8STomasz Jeznach 
19510c54acb8STomasz Jeznach     icvec |= MIN(data & RISCV_IOMMU_ICVEC_CIV,
19520c54acb8STomasz Jeznach                  s->icvec_avail_vectors & RISCV_IOMMU_ICVEC_CIV);
19530c54acb8STomasz Jeznach 
19540c54acb8STomasz Jeznach     icvec |= MIN(data & RISCV_IOMMU_ICVEC_FIV,
19550c54acb8STomasz Jeznach                  s->icvec_avail_vectors & RISCV_IOMMU_ICVEC_FIV);
19560c54acb8STomasz Jeznach 
19570c54acb8STomasz Jeznach     icvec |= MIN(data & RISCV_IOMMU_ICVEC_PMIV,
19580c54acb8STomasz Jeznach                  s->icvec_avail_vectors & RISCV_IOMMU_ICVEC_PMIV);
19590c54acb8STomasz Jeznach 
19600c54acb8STomasz Jeznach     icvec |= MIN(data & RISCV_IOMMU_ICVEC_PIV,
19610c54acb8STomasz Jeznach                  s->icvec_avail_vectors & RISCV_IOMMU_ICVEC_PIV);
19620c54acb8STomasz Jeznach 
19630c54acb8STomasz Jeznach     trace_riscv_iommu_icvec_write(data, icvec);
19640c54acb8STomasz Jeznach 
19650c54acb8STomasz Jeznach     riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_ICVEC, icvec);
19660c54acb8STomasz Jeznach }
19670c54acb8STomasz Jeznach 
19680c54acb8STomasz Jeznach static void riscv_iommu_update_ipsr(RISCVIOMMUState *s, uint64_t data)
19690c54acb8STomasz Jeznach {
19700c54acb8STomasz Jeznach     uint32_t cqcsr, fqcsr, pqcsr;
19710c54acb8STomasz Jeznach     uint32_t ipsr_set = 0;
19720c54acb8STomasz Jeznach     uint32_t ipsr_clr = 0;
19730c54acb8STomasz Jeznach 
19740c54acb8STomasz Jeznach     if (data & RISCV_IOMMU_IPSR_CIP) {
19750c54acb8STomasz Jeznach         cqcsr = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQCSR);
19760c54acb8STomasz Jeznach 
19770c54acb8STomasz Jeznach         if (cqcsr & RISCV_IOMMU_CQCSR_CIE &&
19780c54acb8STomasz Jeznach             (cqcsr & RISCV_IOMMU_CQCSR_FENCE_W_IP ||
19790c54acb8STomasz Jeznach              cqcsr & RISCV_IOMMU_CQCSR_CMD_ILL ||
19800c54acb8STomasz Jeznach              cqcsr & RISCV_IOMMU_CQCSR_CMD_TO ||
19810c54acb8STomasz Jeznach              cqcsr & RISCV_IOMMU_CQCSR_CQMF)) {
19820c54acb8STomasz Jeznach             ipsr_set |= RISCV_IOMMU_IPSR_CIP;
19830c54acb8STomasz Jeznach         } else {
19840c54acb8STomasz Jeznach             ipsr_clr |= RISCV_IOMMU_IPSR_CIP;
19850c54acb8STomasz Jeznach         }
19860c54acb8STomasz Jeznach     } else {
19870c54acb8STomasz Jeznach         ipsr_clr |= RISCV_IOMMU_IPSR_CIP;
19880c54acb8STomasz Jeznach     }
19890c54acb8STomasz Jeznach 
19900c54acb8STomasz Jeznach     if (data & RISCV_IOMMU_IPSR_FIP) {
19910c54acb8STomasz Jeznach         fqcsr = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQCSR);
19920c54acb8STomasz Jeznach 
19930c54acb8STomasz Jeznach         if (fqcsr & RISCV_IOMMU_FQCSR_FIE &&
19940c54acb8STomasz Jeznach             (fqcsr & RISCV_IOMMU_FQCSR_FQOF ||
19950c54acb8STomasz Jeznach              fqcsr & RISCV_IOMMU_FQCSR_FQMF)) {
19960c54acb8STomasz Jeznach             ipsr_set |= RISCV_IOMMU_IPSR_FIP;
19970c54acb8STomasz Jeznach         } else {
19980c54acb8STomasz Jeznach             ipsr_clr |= RISCV_IOMMU_IPSR_FIP;
19990c54acb8STomasz Jeznach         }
20000c54acb8STomasz Jeznach     } else {
20010c54acb8STomasz Jeznach         ipsr_clr |= RISCV_IOMMU_IPSR_FIP;
20020c54acb8STomasz Jeznach     }
20030c54acb8STomasz Jeznach 
20040c54acb8STomasz Jeznach     if (data & RISCV_IOMMU_IPSR_PIP) {
20050c54acb8STomasz Jeznach         pqcsr = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_PQCSR);
20060c54acb8STomasz Jeznach 
20070c54acb8STomasz Jeznach         if (pqcsr & RISCV_IOMMU_PQCSR_PIE &&
20080c54acb8STomasz Jeznach             (pqcsr & RISCV_IOMMU_PQCSR_PQOF ||
20090c54acb8STomasz Jeznach              pqcsr & RISCV_IOMMU_PQCSR_PQMF)) {
20100c54acb8STomasz Jeznach             ipsr_set |= RISCV_IOMMU_IPSR_PIP;
20110c54acb8STomasz Jeznach         } else {
20120c54acb8STomasz Jeznach             ipsr_clr |= RISCV_IOMMU_IPSR_PIP;
20130c54acb8STomasz Jeznach         }
20140c54acb8STomasz Jeznach     } else {
20150c54acb8STomasz Jeznach         ipsr_clr |= RISCV_IOMMU_IPSR_PIP;
20160c54acb8STomasz Jeznach     }
20170c54acb8STomasz Jeznach 
20180c54acb8STomasz Jeznach     riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_IPSR, ipsr_set, ipsr_clr);
20190c54acb8STomasz Jeznach }
20200c54acb8STomasz Jeznach 
20210c54acb8STomasz Jeznach /*
20220c54acb8STomasz Jeznach  * Write the resulting value of 'data' for the reg specified
20230c54acb8STomasz Jeznach  * by 'reg_addr', after considering read-only/read-write/write-clear
20240c54acb8STomasz Jeznach  * bits, in the pointer 'dest'.
20250c54acb8STomasz Jeznach  *
20260c54acb8STomasz Jeznach  * The result is written in little-endian.
20270c54acb8STomasz Jeznach  */
20280c54acb8STomasz Jeznach static void riscv_iommu_write_reg_val(RISCVIOMMUState *s,
20290c54acb8STomasz Jeznach                                       void *dest, hwaddr reg_addr,
20300c54acb8STomasz Jeznach                                       int size, uint64_t data)
20310c54acb8STomasz Jeznach {
20320c54acb8STomasz Jeznach     uint64_t ro = ldn_le_p(&s->regs_ro[reg_addr], size);
20330c54acb8STomasz Jeznach     uint64_t wc = ldn_le_p(&s->regs_wc[reg_addr], size);
20340c54acb8STomasz Jeznach     uint64_t rw = ldn_le_p(&s->regs_rw[reg_addr], size);
20350c54acb8STomasz Jeznach 
20360c54acb8STomasz Jeznach     stn_le_p(dest, size, ((rw & ro) | (data & ~ro)) & ~(data & wc));
20370c54acb8STomasz Jeznach }
20380c54acb8STomasz Jeznach 
20390c54acb8STomasz Jeznach static MemTxResult riscv_iommu_mmio_write(void *opaque, hwaddr addr,
20400c54acb8STomasz Jeznach                                           uint64_t data, unsigned size,
20410c54acb8STomasz Jeznach                                           MemTxAttrs attrs)
20420c54acb8STomasz Jeznach {
20430c54acb8STomasz Jeznach     riscv_iommu_process_fn *process_fn = NULL;
20440c54acb8STomasz Jeznach     RISCVIOMMUState *s = opaque;
20450c54acb8STomasz Jeznach     uint32_t regb = addr & ~3;
20460c54acb8STomasz Jeznach     uint32_t busy = 0;
20470c54acb8STomasz Jeznach     uint64_t val = 0;
20480c54acb8STomasz Jeznach 
20490c54acb8STomasz Jeznach     if ((addr & (size - 1)) != 0) {
20500c54acb8STomasz Jeznach         /* Unsupported MMIO alignment or access size */
20510c54acb8STomasz Jeznach         return MEMTX_ERROR;
20520c54acb8STomasz Jeznach     }
20530c54acb8STomasz Jeznach 
20540c54acb8STomasz Jeznach     if (addr + size > RISCV_IOMMU_REG_MSI_CONFIG) {
20550c54acb8STomasz Jeznach         /* Unsupported MMIO access location. */
20560c54acb8STomasz Jeznach         return MEMTX_ACCESS_ERROR;
20570c54acb8STomasz Jeznach     }
20580c54acb8STomasz Jeznach 
20590c54acb8STomasz Jeznach     /* Track actionable MMIO write. */
20600c54acb8STomasz Jeznach     switch (regb) {
20610c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_DDTP:
20620c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_DDTP + 4:
20630c54acb8STomasz Jeznach         process_fn = riscv_iommu_process_ddtp;
20640c54acb8STomasz Jeznach         regb = RISCV_IOMMU_REG_DDTP;
20650c54acb8STomasz Jeznach         busy = RISCV_IOMMU_DDTP_BUSY;
20660c54acb8STomasz Jeznach         break;
20670c54acb8STomasz Jeznach 
20680c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_CQT:
20690c54acb8STomasz Jeznach         process_fn = riscv_iommu_process_cq_tail;
20700c54acb8STomasz Jeznach         break;
20710c54acb8STomasz Jeznach 
20720c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_CQCSR:
20730c54acb8STomasz Jeznach         process_fn = riscv_iommu_process_cq_control;
20740c54acb8STomasz Jeznach         busy = RISCV_IOMMU_CQCSR_BUSY;
20750c54acb8STomasz Jeznach         break;
20760c54acb8STomasz Jeznach 
20770c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_FQCSR:
20780c54acb8STomasz Jeznach         process_fn = riscv_iommu_process_fq_control;
20790c54acb8STomasz Jeznach         busy = RISCV_IOMMU_FQCSR_BUSY;
20800c54acb8STomasz Jeznach         break;
20810c54acb8STomasz Jeznach 
20820c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_PQCSR:
20830c54acb8STomasz Jeznach         process_fn = riscv_iommu_process_pq_control;
20840c54acb8STomasz Jeznach         busy = RISCV_IOMMU_PQCSR_BUSY;
20850c54acb8STomasz Jeznach         break;
20860c54acb8STomasz Jeznach 
20870c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_ICVEC:
20880c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_IPSR:
20890c54acb8STomasz Jeznach         /*
20900c54acb8STomasz Jeznach          * ICVEC and IPSR have special read/write procedures. We'll
20910c54acb8STomasz Jeznach          * call their respective helpers and exit.
20920c54acb8STomasz Jeznach          */
20930c54acb8STomasz Jeznach         riscv_iommu_write_reg_val(s, &val, addr, size, data);
20940c54acb8STomasz Jeznach 
20950c54acb8STomasz Jeznach         /*
20960c54acb8STomasz Jeznach          * 'val' is stored as LE. Switch to host endianess
20970c54acb8STomasz Jeznach          * before using it.
20980c54acb8STomasz Jeznach          */
20990c54acb8STomasz Jeznach         val = le64_to_cpu(val);
21000c54acb8STomasz Jeznach 
21010c54acb8STomasz Jeznach         if (regb == RISCV_IOMMU_REG_ICVEC) {
21020c54acb8STomasz Jeznach             riscv_iommu_update_icvec(s, val);
21030c54acb8STomasz Jeznach         } else {
21040c54acb8STomasz Jeznach             riscv_iommu_update_ipsr(s, val);
21050c54acb8STomasz Jeznach         }
21060c54acb8STomasz Jeznach 
21070c54acb8STomasz Jeznach         return MEMTX_OK;
21080c54acb8STomasz Jeznach 
2109a7aa525bSTomasz Jeznach     case RISCV_IOMMU_REG_TR_REQ_CTL:
2110a7aa525bSTomasz Jeznach         process_fn = riscv_iommu_process_dbg;
2111a7aa525bSTomasz Jeznach         regb = RISCV_IOMMU_REG_TR_REQ_CTL;
2112a7aa525bSTomasz Jeznach         busy = RISCV_IOMMU_TR_REQ_CTL_GO_BUSY;
2113a7aa525bSTomasz Jeznach         break;
2114a7aa525bSTomasz Jeznach 
21150c54acb8STomasz Jeznach     default:
21160c54acb8STomasz Jeznach         break;
21170c54acb8STomasz Jeznach     }
21180c54acb8STomasz Jeznach 
21190c54acb8STomasz Jeznach     /*
21200c54acb8STomasz Jeznach      * Registers update might be not synchronized with core logic.
21210c54acb8STomasz Jeznach      * If system software updates register when relevant BUSY bit
21220c54acb8STomasz Jeznach      * is set IOMMU behavior of additional writes to the register
21230c54acb8STomasz Jeznach      * is UNSPECIFIED.
21240c54acb8STomasz Jeznach      */
21250c54acb8STomasz Jeznach     riscv_iommu_write_reg_val(s, &s->regs_rw[addr], addr, size, data);
21260c54acb8STomasz Jeznach 
21270c54acb8STomasz Jeznach     /* Busy flag update, MSB 4-byte register. */
21280c54acb8STomasz Jeznach     if (busy) {
21290c54acb8STomasz Jeznach         uint32_t rw = ldl_le_p(&s->regs_rw[regb]);
21300c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[regb], rw | busy);
21310c54acb8STomasz Jeznach     }
21320c54acb8STomasz Jeznach 
21330c54acb8STomasz Jeznach     if (process_fn) {
21340c54acb8STomasz Jeznach         process_fn(s);
21350c54acb8STomasz Jeznach     }
21360c54acb8STomasz Jeznach 
21370c54acb8STomasz Jeznach     return MEMTX_OK;
21380c54acb8STomasz Jeznach }
21390c54acb8STomasz Jeznach 
21400c54acb8STomasz Jeznach static MemTxResult riscv_iommu_mmio_read(void *opaque, hwaddr addr,
21410c54acb8STomasz Jeznach     uint64_t *data, unsigned size, MemTxAttrs attrs)
21420c54acb8STomasz Jeznach {
21430c54acb8STomasz Jeznach     RISCVIOMMUState *s = opaque;
21440c54acb8STomasz Jeznach     uint64_t val = -1;
21450c54acb8STomasz Jeznach     uint8_t *ptr;
21460c54acb8STomasz Jeznach 
21470c54acb8STomasz Jeznach     if ((addr & (size - 1)) != 0) {
21480c54acb8STomasz Jeznach         /* Unsupported MMIO alignment. */
21490c54acb8STomasz Jeznach         return MEMTX_ERROR;
21500c54acb8STomasz Jeznach     }
21510c54acb8STomasz Jeznach 
21520c54acb8STomasz Jeznach     if (addr + size > RISCV_IOMMU_REG_MSI_CONFIG) {
21530c54acb8STomasz Jeznach         return MEMTX_ACCESS_ERROR;
21540c54acb8STomasz Jeznach     }
21550c54acb8STomasz Jeznach 
21560c54acb8STomasz Jeznach     ptr = &s->regs_rw[addr];
21570c54acb8STomasz Jeznach     val = ldn_le_p(ptr, size);
21580c54acb8STomasz Jeznach 
21590c54acb8STomasz Jeznach     *data = val;
21600c54acb8STomasz Jeznach 
21610c54acb8STomasz Jeznach     return MEMTX_OK;
21620c54acb8STomasz Jeznach }
21630c54acb8STomasz Jeznach 
21640c54acb8STomasz Jeznach static const MemoryRegionOps riscv_iommu_mmio_ops = {
21650c54acb8STomasz Jeznach     .read_with_attrs = riscv_iommu_mmio_read,
21660c54acb8STomasz Jeznach     .write_with_attrs = riscv_iommu_mmio_write,
21670c54acb8STomasz Jeznach     .endianness = DEVICE_NATIVE_ENDIAN,
21680c54acb8STomasz Jeznach     .impl = {
21690c54acb8STomasz Jeznach         .min_access_size = 4,
21700c54acb8STomasz Jeznach         .max_access_size = 8,
21710c54acb8STomasz Jeznach         .unaligned = false,
21720c54acb8STomasz Jeznach     },
21730c54acb8STomasz Jeznach     .valid = {
21740c54acb8STomasz Jeznach         .min_access_size = 4,
21750c54acb8STomasz Jeznach         .max_access_size = 8,
21760c54acb8STomasz Jeznach     }
21770c54acb8STomasz Jeznach };
21780c54acb8STomasz Jeznach 
21790c54acb8STomasz Jeznach /*
21800c54acb8STomasz Jeznach  * Translations matching MSI pattern check are redirected to "riscv-iommu-trap"
21810c54acb8STomasz Jeznach  * memory region as untranslated address, for additional MSI/MRIF interception
21820c54acb8STomasz Jeznach  * by IOMMU interrupt remapping implementation.
21830c54acb8STomasz Jeznach  * Note: Device emulation code generating an MSI is expected to provide a valid
21840c54acb8STomasz Jeznach  * memory transaction attributes with requested_id set.
21850c54acb8STomasz Jeznach  */
21860c54acb8STomasz Jeznach static MemTxResult riscv_iommu_trap_write(void *opaque, hwaddr addr,
21870c54acb8STomasz Jeznach     uint64_t data, unsigned size, MemTxAttrs attrs)
21880c54acb8STomasz Jeznach {
21890c54acb8STomasz Jeznach     RISCVIOMMUState* s = (RISCVIOMMUState *)opaque;
21900c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx;
21910c54acb8STomasz Jeznach     MemTxResult res;
21920c54acb8STomasz Jeznach     void *ref;
21930c54acb8STomasz Jeznach     uint32_t devid = attrs.requester_id;
21940c54acb8STomasz Jeznach 
21950c54acb8STomasz Jeznach     if (attrs.unspecified) {
21960c54acb8STomasz Jeznach         return MEMTX_ACCESS_ERROR;
21970c54acb8STomasz Jeznach     }
21980c54acb8STomasz Jeznach 
21990c54acb8STomasz Jeznach     /* FIXME: PCIe bus remapping for attached endpoints. */
22000c54acb8STomasz Jeznach     devid |= s->bus << 8;
22010c54acb8STomasz Jeznach 
22020c54acb8STomasz Jeznach     ctx = riscv_iommu_ctx(s, devid, 0, &ref);
22030c54acb8STomasz Jeznach     if (ctx == NULL) {
22040c54acb8STomasz Jeznach         res = MEMTX_ACCESS_ERROR;
22050c54acb8STomasz Jeznach     } else {
22060c54acb8STomasz Jeznach         res = riscv_iommu_msi_write(s, ctx, addr, data, size, attrs);
22070c54acb8STomasz Jeznach     }
22080c54acb8STomasz Jeznach     riscv_iommu_ctx_put(s, ref);
22090c54acb8STomasz Jeznach     return res;
22100c54acb8STomasz Jeznach }
22110c54acb8STomasz Jeznach 
22120c54acb8STomasz Jeznach static MemTxResult riscv_iommu_trap_read(void *opaque, hwaddr addr,
22130c54acb8STomasz Jeznach     uint64_t *data, unsigned size, MemTxAttrs attrs)
22140c54acb8STomasz Jeznach {
22150c54acb8STomasz Jeznach     return MEMTX_ACCESS_ERROR;
22160c54acb8STomasz Jeznach }
22170c54acb8STomasz Jeznach 
22180c54acb8STomasz Jeznach static const MemoryRegionOps riscv_iommu_trap_ops = {
22190c54acb8STomasz Jeznach     .read_with_attrs = riscv_iommu_trap_read,
22200c54acb8STomasz Jeznach     .write_with_attrs = riscv_iommu_trap_write,
22210c54acb8STomasz Jeznach     .endianness = DEVICE_LITTLE_ENDIAN,
22220c54acb8STomasz Jeznach     .impl = {
22230c54acb8STomasz Jeznach         .min_access_size = 4,
22240c54acb8STomasz Jeznach         .max_access_size = 8,
22250c54acb8STomasz Jeznach         .unaligned = true,
22260c54acb8STomasz Jeznach     },
22270c54acb8STomasz Jeznach     .valid = {
22280c54acb8STomasz Jeznach         .min_access_size = 4,
22290c54acb8STomasz Jeznach         .max_access_size = 8,
22300c54acb8STomasz Jeznach     }
22310c54acb8STomasz Jeznach };
22320c54acb8STomasz Jeznach 
2233d13346d1SDaniel Henrique Barboza void riscv_iommu_set_cap_igs(RISCVIOMMUState *s, riscv_iommu_igs_mode mode)
2234d13346d1SDaniel Henrique Barboza {
2235d13346d1SDaniel Henrique Barboza     s->cap = set_field(s->cap, RISCV_IOMMU_CAP_IGS, mode);
2236d13346d1SDaniel Henrique Barboza }
2237d13346d1SDaniel Henrique Barboza 
22384876e6f7SDaniel Henrique Barboza static void riscv_iommu_instance_init(Object *obj)
22394876e6f7SDaniel Henrique Barboza {
22404876e6f7SDaniel Henrique Barboza     RISCVIOMMUState *s = RISCV_IOMMU(obj);
22414876e6f7SDaniel Henrique Barboza 
22424876e6f7SDaniel Henrique Barboza     /* Enable translation debug interface */
22434876e6f7SDaniel Henrique Barboza     s->cap = RISCV_IOMMU_CAP_DBG;
22444876e6f7SDaniel Henrique Barboza 
22454876e6f7SDaniel Henrique Barboza     /* Report QEMU target physical address space limits */
22464876e6f7SDaniel Henrique Barboza     s->cap = set_field(s->cap, RISCV_IOMMU_CAP_PAS,
22474876e6f7SDaniel Henrique Barboza                        TARGET_PHYS_ADDR_SPACE_BITS);
22484876e6f7SDaniel Henrique Barboza 
22494876e6f7SDaniel Henrique Barboza     /* TODO: method to report supported PID bits */
22504876e6f7SDaniel Henrique Barboza     s->pid_bits = 8; /* restricted to size of MemTxAttrs.pid */
22514876e6f7SDaniel Henrique Barboza     s->cap |= RISCV_IOMMU_CAP_PD8;
22524876e6f7SDaniel Henrique Barboza 
22534876e6f7SDaniel Henrique Barboza     /* register storage */
22544876e6f7SDaniel Henrique Barboza     s->regs_rw = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE);
22554876e6f7SDaniel Henrique Barboza     s->regs_ro = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE);
22564876e6f7SDaniel Henrique Barboza     s->regs_wc = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE);
22574876e6f7SDaniel Henrique Barboza 
22584876e6f7SDaniel Henrique Barboza      /* Mark all registers read-only */
22594876e6f7SDaniel Henrique Barboza     memset(s->regs_ro, 0xff, RISCV_IOMMU_REG_SIZE);
22604876e6f7SDaniel Henrique Barboza 
22614876e6f7SDaniel Henrique Barboza     /* Device translation context cache */
22624876e6f7SDaniel Henrique Barboza     s->ctx_cache = g_hash_table_new_full(riscv_iommu_ctx_hash,
22634876e6f7SDaniel Henrique Barboza                                          riscv_iommu_ctx_equal,
22644876e6f7SDaniel Henrique Barboza                                          g_free, NULL);
22654876e6f7SDaniel Henrique Barboza 
22664876e6f7SDaniel Henrique Barboza     s->iot_cache = g_hash_table_new_full(riscv_iommu_iot_hash,
22674876e6f7SDaniel Henrique Barboza                                          riscv_iommu_iot_equal,
22684876e6f7SDaniel Henrique Barboza                                          g_free, NULL);
22694876e6f7SDaniel Henrique Barboza 
22704876e6f7SDaniel Henrique Barboza     s->iommus.le_next = NULL;
22714876e6f7SDaniel Henrique Barboza     s->iommus.le_prev = NULL;
22724876e6f7SDaniel Henrique Barboza     QLIST_INIT(&s->spaces);
22734876e6f7SDaniel Henrique Barboza }
22744876e6f7SDaniel Henrique Barboza 
22750c54acb8STomasz Jeznach static void riscv_iommu_realize(DeviceState *dev, Error **errp)
22760c54acb8STomasz Jeznach {
22770c54acb8STomasz Jeznach     RISCVIOMMUState *s = RISCV_IOMMU(dev);
22780c54acb8STomasz Jeznach 
22794876e6f7SDaniel Henrique Barboza     s->cap |= s->version & RISCV_IOMMU_CAP_VERSION;
22800c54acb8STomasz Jeznach     if (s->enable_msi) {
22810c54acb8STomasz Jeznach         s->cap |= RISCV_IOMMU_CAP_MSI_FLAT | RISCV_IOMMU_CAP_MSI_MRIF;
22820c54acb8STomasz Jeznach     }
228369a9ae48STomasz Jeznach     if (s->enable_ats) {
228469a9ae48STomasz Jeznach         s->cap |= RISCV_IOMMU_CAP_ATS;
228569a9ae48STomasz Jeznach     }
22860c54acb8STomasz Jeznach     if (s->enable_s_stage) {
22870c54acb8STomasz Jeznach         s->cap |= RISCV_IOMMU_CAP_SV32 | RISCV_IOMMU_CAP_SV39 |
22880c54acb8STomasz Jeznach                   RISCV_IOMMU_CAP_SV48 | RISCV_IOMMU_CAP_SV57;
22890c54acb8STomasz Jeznach     }
22900c54acb8STomasz Jeznach     if (s->enable_g_stage) {
22910c54acb8STomasz Jeznach         s->cap |= RISCV_IOMMU_CAP_SV32X4 | RISCV_IOMMU_CAP_SV39X4 |
22920c54acb8STomasz Jeznach                   RISCV_IOMMU_CAP_SV48X4 | RISCV_IOMMU_CAP_SV57X4;
22930c54acb8STomasz Jeznach     }
22940c54acb8STomasz Jeznach 
22950c54acb8STomasz Jeznach     /* Out-of-reset translation mode: OFF (DMA disabled) BARE (passthrough) */
22960c54acb8STomasz Jeznach     s->ddtp = set_field(0, RISCV_IOMMU_DDTP_MODE, s->enable_off ?
22970c54acb8STomasz Jeznach                         RISCV_IOMMU_DDTP_MODE_OFF : RISCV_IOMMU_DDTP_MODE_BARE);
22980c54acb8STomasz Jeznach 
22990c54acb8STomasz Jeznach     /*
23000c54acb8STomasz Jeznach      * Register complete MMIO space, including MSI/PBA registers.
23010c54acb8STomasz Jeznach      * Note, PCIDevice implementation will add overlapping MR for MSI/PBA,
23020c54acb8STomasz Jeznach      * managed directly by the PCIDevice implementation.
23030c54acb8STomasz Jeznach      */
23040c54acb8STomasz Jeznach     memory_region_init_io(&s->regs_mr, OBJECT(dev), &riscv_iommu_mmio_ops, s,
23050c54acb8STomasz Jeznach         "riscv-iommu-regs", RISCV_IOMMU_REG_SIZE);
23060c54acb8STomasz Jeznach 
23070c54acb8STomasz Jeznach     /* Set power-on register state */
23080c54acb8STomasz Jeznach     stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_CAP], s->cap);
23090c54acb8STomasz Jeznach     stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_FCTL], 0);
23100c54acb8STomasz Jeznach     stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_FCTL],
23110c54acb8STomasz Jeznach              ~(RISCV_IOMMU_FCTL_BE | RISCV_IOMMU_FCTL_WSI));
23120c54acb8STomasz Jeznach     stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_DDTP],
23130c54acb8STomasz Jeznach         ~(RISCV_IOMMU_DDTP_PPN | RISCV_IOMMU_DDTP_MODE));
23140c54acb8STomasz Jeznach     stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQB],
23150c54acb8STomasz Jeznach         ~(RISCV_IOMMU_CQB_LOG2SZ | RISCV_IOMMU_CQB_PPN));
23160c54acb8STomasz Jeznach     stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQB],
23170c54acb8STomasz Jeznach         ~(RISCV_IOMMU_FQB_LOG2SZ | RISCV_IOMMU_FQB_PPN));
23180c54acb8STomasz Jeznach     stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQB],
23190c54acb8STomasz Jeznach         ~(RISCV_IOMMU_PQB_LOG2SZ | RISCV_IOMMU_PQB_PPN));
23200c54acb8STomasz Jeznach     stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_CQCSR], RISCV_IOMMU_CQCSR_CQMF |
23210c54acb8STomasz Jeznach         RISCV_IOMMU_CQCSR_CMD_TO | RISCV_IOMMU_CQCSR_CMD_ILL);
23220c54acb8STomasz Jeznach     stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQCSR], RISCV_IOMMU_CQCSR_CQON |
23230c54acb8STomasz Jeznach         RISCV_IOMMU_CQCSR_BUSY);
23240c54acb8STomasz Jeznach     stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_FQCSR], RISCV_IOMMU_FQCSR_FQMF |
23250c54acb8STomasz Jeznach         RISCV_IOMMU_FQCSR_FQOF);
23260c54acb8STomasz Jeznach     stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQCSR], RISCV_IOMMU_FQCSR_FQON |
23270c54acb8STomasz Jeznach         RISCV_IOMMU_FQCSR_BUSY);
23280c54acb8STomasz Jeznach     stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_PQCSR], RISCV_IOMMU_PQCSR_PQMF |
23290c54acb8STomasz Jeznach         RISCV_IOMMU_PQCSR_PQOF);
23300c54acb8STomasz Jeznach     stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQCSR], RISCV_IOMMU_PQCSR_PQON |
23310c54acb8STomasz Jeznach         RISCV_IOMMU_PQCSR_BUSY);
23320c54acb8STomasz Jeznach     stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_IPSR], ~0);
23330c54acb8STomasz Jeznach     stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_ICVEC], 0);
23340c54acb8STomasz Jeznach     stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_DDTP], s->ddtp);
2335a7aa525bSTomasz Jeznach     /* If debug registers enabled. */
2336a7aa525bSTomasz Jeznach     if (s->cap & RISCV_IOMMU_CAP_DBG) {
2337a7aa525bSTomasz Jeznach         stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_TR_REQ_IOVA], 0);
2338a7aa525bSTomasz Jeznach         stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_TR_REQ_CTL],
2339a7aa525bSTomasz Jeznach             RISCV_IOMMU_TR_REQ_CTL_GO_BUSY);
2340a7aa525bSTomasz Jeznach     }
23410c54acb8STomasz Jeznach 
23420c54acb8STomasz Jeznach     /* Memory region for downstream access, if specified. */
23430c54acb8STomasz Jeznach     if (s->target_mr) {
23440c54acb8STomasz Jeznach         s->target_as = g_new0(AddressSpace, 1);
23450c54acb8STomasz Jeznach         address_space_init(s->target_as, s->target_mr,
23460c54acb8STomasz Jeznach             "riscv-iommu-downstream");
23470c54acb8STomasz Jeznach     } else {
23480c54acb8STomasz Jeznach         /* Fallback to global system memory. */
23490c54acb8STomasz Jeznach         s->target_as = &address_space_memory;
23500c54acb8STomasz Jeznach     }
23510c54acb8STomasz Jeznach 
23520c54acb8STomasz Jeznach     /* Memory region for untranslated MRIF/MSI writes */
23530c54acb8STomasz Jeznach     memory_region_init_io(&s->trap_mr, OBJECT(dev), &riscv_iommu_trap_ops, s,
23540c54acb8STomasz Jeznach             "riscv-iommu-trap", ~0ULL);
23550c54acb8STomasz Jeznach     address_space_init(&s->trap_as, &s->trap_mr, "riscv-iommu-trap-as");
23560c54acb8STomasz Jeznach }
23570c54acb8STomasz Jeznach 
23580c54acb8STomasz Jeznach static void riscv_iommu_unrealize(DeviceState *dev)
23590c54acb8STomasz Jeznach {
23600c54acb8STomasz Jeznach     RISCVIOMMUState *s = RISCV_IOMMU(dev);
23610c54acb8STomasz Jeznach 
23629d085a1cSTomasz Jeznach     g_hash_table_unref(s->iot_cache);
23630c54acb8STomasz Jeznach     g_hash_table_unref(s->ctx_cache);
23640c54acb8STomasz Jeznach }
23650c54acb8STomasz Jeznach 
23669afd2671SDaniel Henrique Barboza void riscv_iommu_reset(RISCVIOMMUState *s)
23679afd2671SDaniel Henrique Barboza {
23689afd2671SDaniel Henrique Barboza     uint32_t reg_clr;
23699afd2671SDaniel Henrique Barboza     int ddtp_mode;
23709afd2671SDaniel Henrique Barboza 
23719afd2671SDaniel Henrique Barboza     /*
23729afd2671SDaniel Henrique Barboza      * Clear DDTP while setting DDTP_mode back to user
23739afd2671SDaniel Henrique Barboza      * initial setting.
23749afd2671SDaniel Henrique Barboza      */
23759afd2671SDaniel Henrique Barboza     ddtp_mode = s->enable_off ?
23769afd2671SDaniel Henrique Barboza                 RISCV_IOMMU_DDTP_MODE_OFF : RISCV_IOMMU_DDTP_MODE_BARE;
23779afd2671SDaniel Henrique Barboza     s->ddtp = set_field(0, RISCV_IOMMU_DDTP_MODE, ddtp_mode);
23789afd2671SDaniel Henrique Barboza     riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_DDTP, s->ddtp);
23799afd2671SDaniel Henrique Barboza 
23809afd2671SDaniel Henrique Barboza     reg_clr = RISCV_IOMMU_CQCSR_CQEN | RISCV_IOMMU_CQCSR_CIE |
23819afd2671SDaniel Henrique Barboza               RISCV_IOMMU_CQCSR_CQON | RISCV_IOMMU_CQCSR_BUSY;
23829afd2671SDaniel Henrique Barboza     riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR, 0, reg_clr);
23839afd2671SDaniel Henrique Barboza 
23849afd2671SDaniel Henrique Barboza     reg_clr = RISCV_IOMMU_FQCSR_FQEN | RISCV_IOMMU_FQCSR_FIE |
23859afd2671SDaniel Henrique Barboza               RISCV_IOMMU_FQCSR_FQON | RISCV_IOMMU_FQCSR_BUSY;
23869afd2671SDaniel Henrique Barboza     riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_FQCSR, 0, reg_clr);
23879afd2671SDaniel Henrique Barboza 
23889afd2671SDaniel Henrique Barboza     reg_clr = RISCV_IOMMU_PQCSR_PQEN | RISCV_IOMMU_PQCSR_PIE |
23899afd2671SDaniel Henrique Barboza               RISCV_IOMMU_PQCSR_PQON | RISCV_IOMMU_PQCSR_BUSY;
23909afd2671SDaniel Henrique Barboza     riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_PQCSR, 0, reg_clr);
23919afd2671SDaniel Henrique Barboza 
23929afd2671SDaniel Henrique Barboza     riscv_iommu_reg_mod64(s, RISCV_IOMMU_REG_TR_REQ_CTL, 0,
23939afd2671SDaniel Henrique Barboza                           RISCV_IOMMU_TR_REQ_CTL_GO_BUSY);
23949afd2671SDaniel Henrique Barboza 
23959afd2671SDaniel Henrique Barboza     riscv_iommu_reg_set32(s, RISCV_IOMMU_REG_IPSR, 0);
23969afd2671SDaniel Henrique Barboza 
23979afd2671SDaniel Henrique Barboza     g_hash_table_remove_all(s->ctx_cache);
23989afd2671SDaniel Henrique Barboza     g_hash_table_remove_all(s->iot_cache);
23999afd2671SDaniel Henrique Barboza }
24009afd2671SDaniel Henrique Barboza 
2401766bade2SRichard Henderson static const Property riscv_iommu_properties[] = {
24020c54acb8STomasz Jeznach     DEFINE_PROP_UINT32("version", RISCVIOMMUState, version,
24030c54acb8STomasz Jeznach         RISCV_IOMMU_SPEC_DOT_VER),
24040c54acb8STomasz Jeznach     DEFINE_PROP_UINT32("bus", RISCVIOMMUState, bus, 0x0),
24059d085a1cSTomasz Jeznach     DEFINE_PROP_UINT32("ioatc-limit", RISCVIOMMUState, iot_limit,
24069d085a1cSTomasz Jeznach         LIMIT_CACHE_IOT),
24070c54acb8STomasz Jeznach     DEFINE_PROP_BOOL("intremap", RISCVIOMMUState, enable_msi, TRUE),
240869a9ae48STomasz Jeznach     DEFINE_PROP_BOOL("ats", RISCVIOMMUState, enable_ats, TRUE),
24090c54acb8STomasz Jeznach     DEFINE_PROP_BOOL("off", RISCVIOMMUState, enable_off, TRUE),
24100c54acb8STomasz Jeznach     DEFINE_PROP_BOOL("s-stage", RISCVIOMMUState, enable_s_stage, TRUE),
24110c54acb8STomasz Jeznach     DEFINE_PROP_BOOL("g-stage", RISCVIOMMUState, enable_g_stage, TRUE),
24120c54acb8STomasz Jeznach     DEFINE_PROP_LINK("downstream-mr", RISCVIOMMUState, target_mr,
24130c54acb8STomasz Jeznach         TYPE_MEMORY_REGION, MemoryRegion *),
24140c54acb8STomasz Jeznach };
24150c54acb8STomasz Jeznach 
24160c54acb8STomasz Jeznach static void riscv_iommu_class_init(ObjectClass *klass, void* data)
24170c54acb8STomasz Jeznach {
24180c54acb8STomasz Jeznach     DeviceClass *dc = DEVICE_CLASS(klass);
24190c54acb8STomasz Jeznach 
24200c54acb8STomasz Jeznach     /* internal device for riscv-iommu-{pci/sys}, not user-creatable */
24210c54acb8STomasz Jeznach     dc->user_creatable = false;
24220c54acb8STomasz Jeznach     dc->realize = riscv_iommu_realize;
24230c54acb8STomasz Jeznach     dc->unrealize = riscv_iommu_unrealize;
24240c54acb8STomasz Jeznach     device_class_set_props(dc, riscv_iommu_properties);
24250c54acb8STomasz Jeznach }
24260c54acb8STomasz Jeznach 
24270c54acb8STomasz Jeznach static const TypeInfo riscv_iommu_info = {
24280c54acb8STomasz Jeznach     .name = TYPE_RISCV_IOMMU,
24290c54acb8STomasz Jeznach     .parent = TYPE_DEVICE,
24300c54acb8STomasz Jeznach     .instance_size = sizeof(RISCVIOMMUState),
24314876e6f7SDaniel Henrique Barboza     .instance_init = riscv_iommu_instance_init,
24320c54acb8STomasz Jeznach     .class_init = riscv_iommu_class_init,
24330c54acb8STomasz Jeznach };
24340c54acb8STomasz Jeznach 
24350c54acb8STomasz Jeznach static const char *IOMMU_FLAG_STR[] = {
24360c54acb8STomasz Jeznach     "NA",
24370c54acb8STomasz Jeznach     "RO",
24380c54acb8STomasz Jeznach     "WR",
24390c54acb8STomasz Jeznach     "RW",
24400c54acb8STomasz Jeznach };
24410c54acb8STomasz Jeznach 
24420c54acb8STomasz Jeznach /* RISC-V IOMMU Memory Region - Address Translation Space */
24430c54acb8STomasz Jeznach static IOMMUTLBEntry riscv_iommu_memory_region_translate(
24440c54acb8STomasz Jeznach     IOMMUMemoryRegion *iommu_mr, hwaddr addr,
24450c54acb8STomasz Jeznach     IOMMUAccessFlags flag, int iommu_idx)
24460c54acb8STomasz Jeznach {
24470c54acb8STomasz Jeznach     RISCVIOMMUSpace *as = container_of(iommu_mr, RISCVIOMMUSpace, iova_mr);
24480c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx;
24490c54acb8STomasz Jeznach     void *ref;
24500c54acb8STomasz Jeznach     IOMMUTLBEntry iotlb = {
24510c54acb8STomasz Jeznach         .iova = addr,
24520c54acb8STomasz Jeznach         .target_as = as->iommu->target_as,
24530c54acb8STomasz Jeznach         .addr_mask = ~0ULL,
24540c54acb8STomasz Jeznach         .perm = flag,
24550c54acb8STomasz Jeznach     };
24560c54acb8STomasz Jeznach 
24570c54acb8STomasz Jeznach     ctx = riscv_iommu_ctx(as->iommu, as->devid, iommu_idx, &ref);
24580c54acb8STomasz Jeznach     if (ctx == NULL) {
24590c54acb8STomasz Jeznach         /* Translation disabled or invalid. */
24600c54acb8STomasz Jeznach         iotlb.addr_mask = 0;
24610c54acb8STomasz Jeznach         iotlb.perm = IOMMU_NONE;
24629d085a1cSTomasz Jeznach     } else if (riscv_iommu_translate(as->iommu, ctx, &iotlb, true)) {
24630c54acb8STomasz Jeznach         /* Translation disabled or fault reported. */
24640c54acb8STomasz Jeznach         iotlb.addr_mask = 0;
24650c54acb8STomasz Jeznach         iotlb.perm = IOMMU_NONE;
24660c54acb8STomasz Jeznach     }
24670c54acb8STomasz Jeznach 
24680c54acb8STomasz Jeznach     /* Trace all dma translations with original access flags. */
24690c54acb8STomasz Jeznach     trace_riscv_iommu_dma(as->iommu->parent_obj.id, PCI_BUS_NUM(as->devid),
24700c54acb8STomasz Jeznach                           PCI_SLOT(as->devid), PCI_FUNC(as->devid), iommu_idx,
24710c54acb8STomasz Jeznach                           IOMMU_FLAG_STR[flag & IOMMU_RW], iotlb.iova,
24720c54acb8STomasz Jeznach                           iotlb.translated_addr);
24730c54acb8STomasz Jeznach 
24740c54acb8STomasz Jeznach     riscv_iommu_ctx_put(as->iommu, ref);
24750c54acb8STomasz Jeznach 
24760c54acb8STomasz Jeznach     return iotlb;
24770c54acb8STomasz Jeznach }
24780c54acb8STomasz Jeznach 
24790c54acb8STomasz Jeznach static int riscv_iommu_memory_region_notify(
24800c54acb8STomasz Jeznach     IOMMUMemoryRegion *iommu_mr, IOMMUNotifierFlag old,
24810c54acb8STomasz Jeznach     IOMMUNotifierFlag new, Error **errp)
24820c54acb8STomasz Jeznach {
24830c54acb8STomasz Jeznach     RISCVIOMMUSpace *as = container_of(iommu_mr, RISCVIOMMUSpace, iova_mr);
24840c54acb8STomasz Jeznach 
24850c54acb8STomasz Jeznach     if (old == IOMMU_NOTIFIER_NONE) {
24860c54acb8STomasz Jeznach         as->notifier = true;
24870c54acb8STomasz Jeznach         trace_riscv_iommu_notifier_add(iommu_mr->parent_obj.name);
24880c54acb8STomasz Jeznach     } else if (new == IOMMU_NOTIFIER_NONE) {
24890c54acb8STomasz Jeznach         as->notifier = false;
24900c54acb8STomasz Jeznach         trace_riscv_iommu_notifier_del(iommu_mr->parent_obj.name);
24910c54acb8STomasz Jeznach     }
24920c54acb8STomasz Jeznach 
24930c54acb8STomasz Jeznach     return 0;
24940c54acb8STomasz Jeznach }
24950c54acb8STomasz Jeznach 
24960c54acb8STomasz Jeznach static inline bool pci_is_iommu(PCIDevice *pdev)
24970c54acb8STomasz Jeznach {
24980c54acb8STomasz Jeznach     return pci_get_word(pdev->config + PCI_CLASS_DEVICE) == 0x0806;
24990c54acb8STomasz Jeznach }
25000c54acb8STomasz Jeznach 
25010c54acb8STomasz Jeznach static AddressSpace *riscv_iommu_find_as(PCIBus *bus, void *opaque, int devfn)
25020c54acb8STomasz Jeznach {
25030c54acb8STomasz Jeznach     RISCVIOMMUState *s = (RISCVIOMMUState *) opaque;
25040c54acb8STomasz Jeznach     PCIDevice *pdev = pci_find_device(bus, pci_bus_num(bus), devfn);
25050c54acb8STomasz Jeznach     AddressSpace *as = NULL;
25060c54acb8STomasz Jeznach 
25070c54acb8STomasz Jeznach     if (pdev && pci_is_iommu(pdev)) {
25080c54acb8STomasz Jeznach         return s->target_as;
25090c54acb8STomasz Jeznach     }
25100c54acb8STomasz Jeznach 
25110c54acb8STomasz Jeznach     /* Find first registered IOMMU device */
25120c54acb8STomasz Jeznach     while (s->iommus.le_prev) {
25130c54acb8STomasz Jeznach         s = *(s->iommus.le_prev);
25140c54acb8STomasz Jeznach     }
25150c54acb8STomasz Jeznach 
25160c54acb8STomasz Jeznach     /* Find first matching IOMMU */
25170c54acb8STomasz Jeznach     while (s != NULL && as == NULL) {
25180c54acb8STomasz Jeznach         as = riscv_iommu_space(s, PCI_BUILD_BDF(pci_bus_num(bus), devfn));
25190c54acb8STomasz Jeznach         s = s->iommus.le_next;
25200c54acb8STomasz Jeznach     }
25210c54acb8STomasz Jeznach 
25220c54acb8STomasz Jeznach     return as ? as : &address_space_memory;
25230c54acb8STomasz Jeznach }
25240c54acb8STomasz Jeznach 
25250c54acb8STomasz Jeznach static const PCIIOMMUOps riscv_iommu_ops = {
25260c54acb8STomasz Jeznach     .get_address_space = riscv_iommu_find_as,
25270c54acb8STomasz Jeznach };
25280c54acb8STomasz Jeznach 
25290c54acb8STomasz Jeznach void riscv_iommu_pci_setup_iommu(RISCVIOMMUState *iommu, PCIBus *bus,
25300c54acb8STomasz Jeznach         Error **errp)
25310c54acb8STomasz Jeznach {
25320c54acb8STomasz Jeznach     if (bus->iommu_ops &&
25330c54acb8STomasz Jeznach         bus->iommu_ops->get_address_space == riscv_iommu_find_as) {
25340c54acb8STomasz Jeznach         /* Allow multiple IOMMUs on the same PCIe bus, link known devices */
25350c54acb8STomasz Jeznach         RISCVIOMMUState *last = (RISCVIOMMUState *)bus->iommu_opaque;
25360c54acb8STomasz Jeznach         QLIST_INSERT_AFTER(last, iommu, iommus);
25370c54acb8STomasz Jeznach     } else if (!bus->iommu_ops && !bus->iommu_opaque) {
25380c54acb8STomasz Jeznach         pci_setup_iommu(bus, &riscv_iommu_ops, iommu);
25390c54acb8STomasz Jeznach     } else {
25400c54acb8STomasz Jeznach         error_setg(errp, "can't register secondary IOMMU for PCI bus #%d",
25410c54acb8STomasz Jeznach             pci_bus_num(bus));
25420c54acb8STomasz Jeznach     }
25430c54acb8STomasz Jeznach }
25440c54acb8STomasz Jeznach 
25450c54acb8STomasz Jeznach static int riscv_iommu_memory_region_index(IOMMUMemoryRegion *iommu_mr,
25460c54acb8STomasz Jeznach     MemTxAttrs attrs)
25470c54acb8STomasz Jeznach {
25480c54acb8STomasz Jeznach     return attrs.unspecified ? RISCV_IOMMU_NOPROCID : (int)attrs.pid;
25490c54acb8STomasz Jeznach }
25500c54acb8STomasz Jeznach 
25510c54acb8STomasz Jeznach static int riscv_iommu_memory_region_index_len(IOMMUMemoryRegion *iommu_mr)
25520c54acb8STomasz Jeznach {
25530c54acb8STomasz Jeznach     RISCVIOMMUSpace *as = container_of(iommu_mr, RISCVIOMMUSpace, iova_mr);
25540c54acb8STomasz Jeznach     return 1 << as->iommu->pid_bits;
25550c54acb8STomasz Jeznach }
25560c54acb8STomasz Jeznach 
25570c54acb8STomasz Jeznach static void riscv_iommu_memory_region_init(ObjectClass *klass, void *data)
25580c54acb8STomasz Jeznach {
25590c54acb8STomasz Jeznach     IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass);
25600c54acb8STomasz Jeznach 
25610c54acb8STomasz Jeznach     imrc->translate = riscv_iommu_memory_region_translate;
25620c54acb8STomasz Jeznach     imrc->notify_flag_changed = riscv_iommu_memory_region_notify;
25630c54acb8STomasz Jeznach     imrc->attrs_to_index = riscv_iommu_memory_region_index;
25640c54acb8STomasz Jeznach     imrc->num_indexes = riscv_iommu_memory_region_index_len;
25650c54acb8STomasz Jeznach }
25660c54acb8STomasz Jeznach 
25670c54acb8STomasz Jeznach static const TypeInfo riscv_iommu_memory_region_info = {
25680c54acb8STomasz Jeznach     .parent = TYPE_IOMMU_MEMORY_REGION,
25690c54acb8STomasz Jeznach     .name = TYPE_RISCV_IOMMU_MEMORY_REGION,
25700c54acb8STomasz Jeznach     .class_init = riscv_iommu_memory_region_init,
25710c54acb8STomasz Jeznach };
25720c54acb8STomasz Jeznach 
25730c54acb8STomasz Jeznach static void riscv_iommu_register_mr_types(void)
25740c54acb8STomasz Jeznach {
25750c54acb8STomasz Jeznach     type_register_static(&riscv_iommu_memory_region_info);
25760c54acb8STomasz Jeznach     type_register_static(&riscv_iommu_info);
25770c54acb8STomasz Jeznach }
25780c54acb8STomasz Jeznach 
25790c54acb8STomasz Jeznach type_init(riscv_iommu_register_mr_types);
2580