xref: /qemu/hw/riscv/riscv-iommu.c (revision 9afd26715ef4f887f5eaf2ecfe365a7837f2e500)
10c54acb8STomasz Jeznach /*
20c54acb8STomasz Jeznach  * QEMU emulation of an RISC-V IOMMU
30c54acb8STomasz Jeznach  *
40c54acb8STomasz Jeznach  * Copyright (C) 2021-2023, Rivos Inc.
50c54acb8STomasz Jeznach  *
60c54acb8STomasz Jeznach  * This program is free software; you can redistribute it and/or modify it
70c54acb8STomasz Jeznach  * under the terms and conditions of the GNU General Public License,
80c54acb8STomasz Jeznach  * version 2 or later, as published by the Free Software Foundation.
90c54acb8STomasz Jeznach  *
100c54acb8STomasz Jeznach  * This program is distributed in the hope that it will be useful,
110c54acb8STomasz Jeznach  * but WITHOUT ANY WARRANTY; without even the implied warranty of
120c54acb8STomasz Jeznach  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
130c54acb8STomasz Jeznach  * GNU General Public License for more details.
140c54acb8STomasz Jeznach  *
150c54acb8STomasz Jeznach  * You should have received a copy of the GNU General Public License along
160c54acb8STomasz Jeznach  * with this program; if not, see <http://www.gnu.org/licenses/>.
170c54acb8STomasz Jeznach  */
180c54acb8STomasz Jeznach 
190c54acb8STomasz Jeznach #include "qemu/osdep.h"
200c54acb8STomasz Jeznach #include "qom/object.h"
210c54acb8STomasz Jeznach #include "hw/pci/pci_bus.h"
220c54acb8STomasz Jeznach #include "hw/pci/pci_device.h"
230c54acb8STomasz Jeznach #include "hw/qdev-properties.h"
240c54acb8STomasz Jeznach #include "hw/riscv/riscv_hart.h"
250c54acb8STomasz Jeznach #include "migration/vmstate.h"
260c54acb8STomasz Jeznach #include "qapi/error.h"
270c54acb8STomasz Jeznach #include "qemu/timer.h"
280c54acb8STomasz Jeznach 
290c54acb8STomasz Jeznach #include "cpu_bits.h"
300c54acb8STomasz Jeznach #include "riscv-iommu.h"
310c54acb8STomasz Jeznach #include "riscv-iommu-bits.h"
320c54acb8STomasz Jeznach #include "trace.h"
330c54acb8STomasz Jeznach 
340c54acb8STomasz Jeznach #define LIMIT_CACHE_CTX               (1U << 7)
350c54acb8STomasz Jeznach #define LIMIT_CACHE_IOT               (1U << 20)
360c54acb8STomasz Jeznach 
370c54acb8STomasz Jeznach /* Physical page number coversions */
380c54acb8STomasz Jeznach #define PPN_PHYS(ppn)                 ((ppn) << TARGET_PAGE_BITS)
390c54acb8STomasz Jeznach #define PPN_DOWN(phy)                 ((phy) >> TARGET_PAGE_BITS)
400c54acb8STomasz Jeznach 
410c54acb8STomasz Jeznach typedef struct RISCVIOMMUContext RISCVIOMMUContext;
420c54acb8STomasz Jeznach typedef struct RISCVIOMMUEntry RISCVIOMMUEntry;
430c54acb8STomasz Jeznach 
440c54acb8STomasz Jeznach /* Device assigned I/O address space */
450c54acb8STomasz Jeznach struct RISCVIOMMUSpace {
460c54acb8STomasz Jeznach     IOMMUMemoryRegion iova_mr;  /* IOVA memory region for attached device */
470c54acb8STomasz Jeznach     AddressSpace iova_as;       /* IOVA address space for attached device */
480c54acb8STomasz Jeznach     RISCVIOMMUState *iommu;     /* Managing IOMMU device state */
490c54acb8STomasz Jeznach     uint32_t devid;             /* Requester identifier, AKA device_id */
500c54acb8STomasz Jeznach     bool notifier;              /* IOMMU unmap notifier enabled */
510c54acb8STomasz Jeznach     QLIST_ENTRY(RISCVIOMMUSpace) list;
520c54acb8STomasz Jeznach };
530c54acb8STomasz Jeznach 
540c54acb8STomasz Jeznach /* Device translation context state. */
550c54acb8STomasz Jeznach struct RISCVIOMMUContext {
560c54acb8STomasz Jeznach     uint64_t devid:24;          /* Requester Id, AKA device_id */
570c54acb8STomasz Jeznach     uint64_t process_id:20;     /* Process ID. PASID for PCIe */
580c54acb8STomasz Jeznach     uint64_t tc;                /* Translation Control */
590c54acb8STomasz Jeznach     uint64_t ta;                /* Translation Attributes */
600c54acb8STomasz Jeznach     uint64_t satp;              /* S-Stage address translation and protection */
610c54acb8STomasz Jeznach     uint64_t gatp;              /* G-Stage address translation and protection */
620c54acb8STomasz Jeznach     uint64_t msi_addr_mask;     /* MSI filtering - address mask */
630c54acb8STomasz Jeznach     uint64_t msi_addr_pattern;  /* MSI filtering - address pattern */
640c54acb8STomasz Jeznach     uint64_t msiptp;            /* MSI redirection page table pointer */
650c54acb8STomasz Jeznach };
660c54acb8STomasz Jeznach 
679d085a1cSTomasz Jeznach /* Address translation cache entry */
689d085a1cSTomasz Jeznach struct RISCVIOMMUEntry {
699d085a1cSTomasz Jeznach     uint64_t iova:44;           /* IOVA Page Number */
709d085a1cSTomasz Jeznach     uint64_t pscid:20;          /* Process Soft-Context identifier */
719d085a1cSTomasz Jeznach     uint64_t phys:44;           /* Physical Page Number */
729d085a1cSTomasz Jeznach     uint64_t gscid:16;          /* Guest Soft-Context identifier */
739d085a1cSTomasz Jeznach     uint64_t perm:2;            /* IOMMU_RW flags */
749d085a1cSTomasz Jeznach };
759d085a1cSTomasz Jeznach 
760c54acb8STomasz Jeznach /* IOMMU index for transactions without process_id specified. */
770c54acb8STomasz Jeznach #define RISCV_IOMMU_NOPROCID 0
780c54acb8STomasz Jeznach 
790c54acb8STomasz Jeznach static uint8_t riscv_iommu_get_icvec_vector(uint32_t icvec, uint32_t vec_type)
800c54acb8STomasz Jeznach {
810c54acb8STomasz Jeznach     switch (vec_type) {
820c54acb8STomasz Jeznach     case RISCV_IOMMU_INTR_CQ:
830c54acb8STomasz Jeznach         return icvec & RISCV_IOMMU_ICVEC_CIV;
840c54acb8STomasz Jeznach     case RISCV_IOMMU_INTR_FQ:
850c54acb8STomasz Jeznach         return (icvec & RISCV_IOMMU_ICVEC_FIV) >> 4;
860c54acb8STomasz Jeznach     case RISCV_IOMMU_INTR_PM:
870c54acb8STomasz Jeznach         return (icvec & RISCV_IOMMU_ICVEC_PMIV) >> 8;
880c54acb8STomasz Jeznach     case RISCV_IOMMU_INTR_PQ:
890c54acb8STomasz Jeznach         return (icvec & RISCV_IOMMU_ICVEC_PIV) >> 12;
900c54acb8STomasz Jeznach     default:
910c54acb8STomasz Jeznach         g_assert_not_reached();
920c54acb8STomasz Jeznach     }
930c54acb8STomasz Jeznach }
940c54acb8STomasz Jeznach 
950c54acb8STomasz Jeznach static void riscv_iommu_notify(RISCVIOMMUState *s, int vec_type)
960c54acb8STomasz Jeznach {
970c54acb8STomasz Jeznach     uint32_t ipsr, icvec, vector;
980c54acb8STomasz Jeznach 
995b128435STomasz Jeznach     if (!s->notify) {
1000c54acb8STomasz Jeznach         return;
1010c54acb8STomasz Jeznach     }
1020c54acb8STomasz Jeznach 
1030c54acb8STomasz Jeznach     icvec = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_ICVEC);
1040c54acb8STomasz Jeznach     ipsr = riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_IPSR, (1 << vec_type), 0);
1050c54acb8STomasz Jeznach 
1060c54acb8STomasz Jeznach     if (!(ipsr & (1 << vec_type))) {
1070c54acb8STomasz Jeznach         vector = riscv_iommu_get_icvec_vector(icvec, vec_type);
1080c54acb8STomasz Jeznach         s->notify(s, vector);
1090c54acb8STomasz Jeznach         trace_riscv_iommu_notify_int_vector(vec_type, vector);
1100c54acb8STomasz Jeznach     }
1110c54acb8STomasz Jeznach }
1120c54acb8STomasz Jeznach 
1130c54acb8STomasz Jeznach static void riscv_iommu_fault(RISCVIOMMUState *s,
1140c54acb8STomasz Jeznach                               struct riscv_iommu_fq_record *ev)
1150c54acb8STomasz Jeznach {
1160c54acb8STomasz Jeznach     uint32_t ctrl = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQCSR);
1170c54acb8STomasz Jeznach     uint32_t head = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQH) & s->fq_mask;
1180c54acb8STomasz Jeznach     uint32_t tail = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQT) & s->fq_mask;
1190c54acb8STomasz Jeznach     uint32_t next = (tail + 1) & s->fq_mask;
1200c54acb8STomasz Jeznach     uint32_t devid = get_field(ev->hdr, RISCV_IOMMU_FQ_HDR_DID);
1210c54acb8STomasz Jeznach 
1220c54acb8STomasz Jeznach     trace_riscv_iommu_flt(s->parent_obj.id, PCI_BUS_NUM(devid), PCI_SLOT(devid),
1230c54acb8STomasz Jeznach                           PCI_FUNC(devid), ev->hdr, ev->iotval);
1240c54acb8STomasz Jeznach 
1250c54acb8STomasz Jeznach     if (!(ctrl & RISCV_IOMMU_FQCSR_FQON) ||
1260c54acb8STomasz Jeznach         !!(ctrl & (RISCV_IOMMU_FQCSR_FQOF | RISCV_IOMMU_FQCSR_FQMF))) {
1270c54acb8STomasz Jeznach         return;
1280c54acb8STomasz Jeznach     }
1290c54acb8STomasz Jeznach 
1300c54acb8STomasz Jeznach     if (head == next) {
1310c54acb8STomasz Jeznach         riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_FQCSR,
1320c54acb8STomasz Jeznach                               RISCV_IOMMU_FQCSR_FQOF, 0);
1330c54acb8STomasz Jeznach     } else {
1340c54acb8STomasz Jeznach         dma_addr_t addr = s->fq_addr + tail * sizeof(*ev);
1350c54acb8STomasz Jeznach         if (dma_memory_write(s->target_as, addr, ev, sizeof(*ev),
1360c54acb8STomasz Jeznach                              MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
1370c54acb8STomasz Jeznach             riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_FQCSR,
1380c54acb8STomasz Jeznach                                   RISCV_IOMMU_FQCSR_FQMF, 0);
1390c54acb8STomasz Jeznach         } else {
1400c54acb8STomasz Jeznach             riscv_iommu_reg_set32(s, RISCV_IOMMU_REG_FQT, next);
1410c54acb8STomasz Jeznach         }
1420c54acb8STomasz Jeznach     }
1430c54acb8STomasz Jeznach 
1440c54acb8STomasz Jeznach     if (ctrl & RISCV_IOMMU_FQCSR_FIE) {
1450c54acb8STomasz Jeznach         riscv_iommu_notify(s, RISCV_IOMMU_INTR_FQ);
1460c54acb8STomasz Jeznach     }
1470c54acb8STomasz Jeznach }
1480c54acb8STomasz Jeznach 
1490c54acb8STomasz Jeznach static void riscv_iommu_pri(RISCVIOMMUState *s,
1500c54acb8STomasz Jeznach     struct riscv_iommu_pq_record *pr)
1510c54acb8STomasz Jeznach {
1520c54acb8STomasz Jeznach     uint32_t ctrl = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_PQCSR);
1530c54acb8STomasz Jeznach     uint32_t head = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_PQH) & s->pq_mask;
1540c54acb8STomasz Jeznach     uint32_t tail = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_PQT) & s->pq_mask;
1550c54acb8STomasz Jeznach     uint32_t next = (tail + 1) & s->pq_mask;
1560c54acb8STomasz Jeznach     uint32_t devid = get_field(pr->hdr, RISCV_IOMMU_PREQ_HDR_DID);
1570c54acb8STomasz Jeznach 
1580c54acb8STomasz Jeznach     trace_riscv_iommu_pri(s->parent_obj.id, PCI_BUS_NUM(devid), PCI_SLOT(devid),
1590c54acb8STomasz Jeznach                           PCI_FUNC(devid), pr->payload);
1600c54acb8STomasz Jeznach 
1610c54acb8STomasz Jeznach     if (!(ctrl & RISCV_IOMMU_PQCSR_PQON) ||
1620c54acb8STomasz Jeznach         !!(ctrl & (RISCV_IOMMU_PQCSR_PQOF | RISCV_IOMMU_PQCSR_PQMF))) {
1630c54acb8STomasz Jeznach         return;
1640c54acb8STomasz Jeznach     }
1650c54acb8STomasz Jeznach 
1660c54acb8STomasz Jeznach     if (head == next) {
1670c54acb8STomasz Jeznach         riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_PQCSR,
1680c54acb8STomasz Jeznach                               RISCV_IOMMU_PQCSR_PQOF, 0);
1690c54acb8STomasz Jeznach     } else {
1700c54acb8STomasz Jeznach         dma_addr_t addr = s->pq_addr + tail * sizeof(*pr);
1710c54acb8STomasz Jeznach         if (dma_memory_write(s->target_as, addr, pr, sizeof(*pr),
1720c54acb8STomasz Jeznach                              MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
1730c54acb8STomasz Jeznach             riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_PQCSR,
1740c54acb8STomasz Jeznach                                   RISCV_IOMMU_PQCSR_PQMF, 0);
1750c54acb8STomasz Jeznach         } else {
1760c54acb8STomasz Jeznach             riscv_iommu_reg_set32(s, RISCV_IOMMU_REG_PQT, next);
1770c54acb8STomasz Jeznach         }
1780c54acb8STomasz Jeznach     }
1790c54acb8STomasz Jeznach 
1800c54acb8STomasz Jeznach     if (ctrl & RISCV_IOMMU_PQCSR_PIE) {
1810c54acb8STomasz Jeznach         riscv_iommu_notify(s, RISCV_IOMMU_INTR_PQ);
1820c54acb8STomasz Jeznach     }
1830c54acb8STomasz Jeznach }
1840c54acb8STomasz Jeznach 
185d37eede7SPierrick Bouvier /*
186d37eede7SPierrick Bouvier  * Discards all bits from 'val' whose matching bits in the same
187d37eede7SPierrick Bouvier  * positions in the mask 'ext' are zeros, and packs the remaining
188d37eede7SPierrick Bouvier  * bits from 'val' contiguously at the least-significant end of the
189d37eede7SPierrick Bouvier  * result, keeping the same bit order as 'val' and filling any
190d37eede7SPierrick Bouvier  * other bits at the most-significant end of the result with zeros.
191d37eede7SPierrick Bouvier  *
192d37eede7SPierrick Bouvier  * For example, for the following 'val' and 'ext', the return 'ret'
193d37eede7SPierrick Bouvier  * will be:
194d37eede7SPierrick Bouvier  *
195d37eede7SPierrick Bouvier  * val = a b c d e f g h
196d37eede7SPierrick Bouvier  * ext = 1 0 1 0 0 1 1 0
197d37eede7SPierrick Bouvier  * ret = 0 0 0 0 a c f g
198d37eede7SPierrick Bouvier  *
199d37eede7SPierrick Bouvier  * This function, taken from the riscv-iommu 1.0 spec, section 2.3.3
200d37eede7SPierrick Bouvier  * "Process to translate addresses of MSIs", is similar to bit manip
201d37eede7SPierrick Bouvier  * function PEXT (Parallel bits extract) from x86.
202d37eede7SPierrick Bouvier  */
203d37eede7SPierrick Bouvier static uint64_t riscv_iommu_pext_u64(uint64_t val, uint64_t ext)
2040c54acb8STomasz Jeznach {
2050c54acb8STomasz Jeznach     uint64_t ret = 0;
2060c54acb8STomasz Jeznach     uint64_t rot = 1;
2070c54acb8STomasz Jeznach 
2080c54acb8STomasz Jeznach     while (ext) {
2090c54acb8STomasz Jeznach         if (ext & 1) {
2100c54acb8STomasz Jeznach             if (val & 1) {
2110c54acb8STomasz Jeznach                 ret |= rot;
2120c54acb8STomasz Jeznach             }
2130c54acb8STomasz Jeznach             rot <<= 1;
2140c54acb8STomasz Jeznach         }
2150c54acb8STomasz Jeznach         val >>= 1;
2160c54acb8STomasz Jeznach         ext >>= 1;
2170c54acb8STomasz Jeznach     }
2180c54acb8STomasz Jeznach 
2190c54acb8STomasz Jeznach     return ret;
2200c54acb8STomasz Jeznach }
2210c54acb8STomasz Jeznach 
2220c54acb8STomasz Jeznach /* Check if GPA matches MSI/MRIF pattern. */
2230c54acb8STomasz Jeznach static bool riscv_iommu_msi_check(RISCVIOMMUState *s, RISCVIOMMUContext *ctx,
2240c54acb8STomasz Jeznach     dma_addr_t gpa)
2250c54acb8STomasz Jeznach {
2260c54acb8STomasz Jeznach     if (!s->enable_msi) {
2270c54acb8STomasz Jeznach         return false;
2280c54acb8STomasz Jeznach     }
2290c54acb8STomasz Jeznach 
2300c54acb8STomasz Jeznach     if (get_field(ctx->msiptp, RISCV_IOMMU_DC_MSIPTP_MODE) !=
2310c54acb8STomasz Jeznach         RISCV_IOMMU_DC_MSIPTP_MODE_FLAT) {
2320c54acb8STomasz Jeznach         return false; /* Invalid MSI/MRIF mode */
2330c54acb8STomasz Jeznach     }
2340c54acb8STomasz Jeznach 
2350c54acb8STomasz Jeznach     if ((PPN_DOWN(gpa) ^ ctx->msi_addr_pattern) & ~ctx->msi_addr_mask) {
2360c54acb8STomasz Jeznach         return false; /* GPA not in MSI range defined by AIA IMSIC rules. */
2370c54acb8STomasz Jeznach     }
2380c54acb8STomasz Jeznach 
2390c54acb8STomasz Jeznach     return true;
2400c54acb8STomasz Jeznach }
2410c54acb8STomasz Jeznach 
2420c54acb8STomasz Jeznach /*
2430c54acb8STomasz Jeznach  * RISCV IOMMU Address Translation Lookup - Page Table Walk
2440c54acb8STomasz Jeznach  *
2450c54acb8STomasz Jeznach  * Note: Code is based on get_physical_address() from target/riscv/cpu_helper.c
2460c54acb8STomasz Jeznach  * Both implementation can be merged into single helper function in future.
2470c54acb8STomasz Jeznach  * Keeping them separate for now, as error reporting and flow specifics are
2480c54acb8STomasz Jeznach  * sufficiently different for separate implementation.
2490c54acb8STomasz Jeznach  *
2500c54acb8STomasz Jeznach  * @s        : IOMMU Device State
2510c54acb8STomasz Jeznach  * @ctx      : Translation context for device id and process address space id.
2520c54acb8STomasz Jeznach  * @iotlb    : translation data: physical address and access mode.
2530c54acb8STomasz Jeznach  * @return   : success or fault cause code.
2540c54acb8STomasz Jeznach  */
2550c54acb8STomasz Jeznach static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx,
2560c54acb8STomasz Jeznach     IOMMUTLBEntry *iotlb)
2570c54acb8STomasz Jeznach {
2580c54acb8STomasz Jeznach     dma_addr_t addr, base;
2590c54acb8STomasz Jeznach     uint64_t satp, gatp, pte;
2600c54acb8STomasz Jeznach     bool en_s, en_g;
2610c54acb8STomasz Jeznach     struct {
2620c54acb8STomasz Jeznach         unsigned char step;
2630c54acb8STomasz Jeznach         unsigned char levels;
2640c54acb8STomasz Jeznach         unsigned char ptidxbits;
2650c54acb8STomasz Jeznach         unsigned char ptesize;
2660c54acb8STomasz Jeznach     } sc[2];
2670c54acb8STomasz Jeznach     /* Translation stage phase */
2680c54acb8STomasz Jeznach     enum {
2690c54acb8STomasz Jeznach         S_STAGE = 0,
2700c54acb8STomasz Jeznach         G_STAGE = 1,
2710c54acb8STomasz Jeznach     } pass;
2720c54acb8STomasz Jeznach     MemTxResult ret;
2730c54acb8STomasz Jeznach 
2740c54acb8STomasz Jeznach     satp = get_field(ctx->satp, RISCV_IOMMU_ATP_MODE_FIELD);
2750c54acb8STomasz Jeznach     gatp = get_field(ctx->gatp, RISCV_IOMMU_ATP_MODE_FIELD);
2760c54acb8STomasz Jeznach 
2770c54acb8STomasz Jeznach     en_s = satp != RISCV_IOMMU_DC_FSC_MODE_BARE;
2780c54acb8STomasz Jeznach     en_g = gatp != RISCV_IOMMU_DC_IOHGATP_MODE_BARE;
2790c54acb8STomasz Jeznach 
2800c54acb8STomasz Jeznach     /*
2810c54acb8STomasz Jeznach      * Early check for MSI address match when IOVA == GPA.
2820c54acb8STomasz Jeznach      * Note that the (!en_s) condition means that the MSI
2830c54acb8STomasz Jeznach      * page table may only be used when guest pages are
2840c54acb8STomasz Jeznach      * mapped using the g-stage page table, whether single-
2850c54acb8STomasz Jeznach      * or two-stage paging is enabled. It's unavoidable though,
2860c54acb8STomasz Jeznach      * because the spec mandates that we do a first-stage
2870c54acb8STomasz Jeznach      * translation before we check the MSI page table, which
2880c54acb8STomasz Jeznach      * means we can't do an early MSI check unless we have
2890c54acb8STomasz Jeznach      * strictly !en_s.
2900c54acb8STomasz Jeznach      */
2910c54acb8STomasz Jeznach     if (!en_s && (iotlb->perm & IOMMU_WO) &&
2920c54acb8STomasz Jeznach         riscv_iommu_msi_check(s, ctx, iotlb->iova)) {
2930c54acb8STomasz Jeznach         iotlb->target_as = &s->trap_as;
2940c54acb8STomasz Jeznach         iotlb->translated_addr = iotlb->iova;
2950c54acb8STomasz Jeznach         iotlb->addr_mask = ~TARGET_PAGE_MASK;
2960c54acb8STomasz Jeznach         return 0;
2970c54acb8STomasz Jeznach     }
2980c54acb8STomasz Jeznach 
2990c54acb8STomasz Jeznach     /* Exit early for pass-through mode. */
3000c54acb8STomasz Jeznach     if (!(en_s || en_g)) {
3010c54acb8STomasz Jeznach         iotlb->translated_addr = iotlb->iova;
3020c54acb8STomasz Jeznach         iotlb->addr_mask = ~TARGET_PAGE_MASK;
3030c54acb8STomasz Jeznach         /* Allow R/W in pass-through mode */
3040c54acb8STomasz Jeznach         iotlb->perm = IOMMU_RW;
3050c54acb8STomasz Jeznach         return 0;
3060c54acb8STomasz Jeznach     }
3070c54acb8STomasz Jeznach 
3080c54acb8STomasz Jeznach     /* S/G translation parameters. */
3090c54acb8STomasz Jeznach     for (pass = 0; pass < 2; pass++) {
3100c54acb8STomasz Jeznach         uint32_t sv_mode;
3110c54acb8STomasz Jeznach 
3120c54acb8STomasz Jeznach         sc[pass].step = 0;
3130c54acb8STomasz Jeznach         if (pass ? (s->fctl & RISCV_IOMMU_FCTL_GXL) :
3140c54acb8STomasz Jeznach             (ctx->tc & RISCV_IOMMU_DC_TC_SXL)) {
3150c54acb8STomasz Jeznach             /* 32bit mode for GXL/SXL == 1 */
3160c54acb8STomasz Jeznach             switch (pass ? gatp : satp) {
3170c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_IOHGATP_MODE_BARE:
3180c54acb8STomasz Jeznach                 sc[pass].levels    = 0;
3190c54acb8STomasz Jeznach                 sc[pass].ptidxbits = 0;
3200c54acb8STomasz Jeznach                 sc[pass].ptesize   = 0;
3210c54acb8STomasz Jeznach                 break;
3220c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_IOHGATP_MODE_SV32X4:
3230c54acb8STomasz Jeznach                 sv_mode = pass ? RISCV_IOMMU_CAP_SV32X4 : RISCV_IOMMU_CAP_SV32;
3240c54acb8STomasz Jeznach                 if (!(s->cap & sv_mode)) {
3250c54acb8STomasz Jeznach                     return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
3260c54acb8STomasz Jeznach                 }
3270c54acb8STomasz Jeznach                 sc[pass].levels    = 2;
3280c54acb8STomasz Jeznach                 sc[pass].ptidxbits = 10;
3290c54acb8STomasz Jeznach                 sc[pass].ptesize   = 4;
3300c54acb8STomasz Jeznach                 break;
3310c54acb8STomasz Jeznach             default:
3320c54acb8STomasz Jeznach                 return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
3330c54acb8STomasz Jeznach             }
3340c54acb8STomasz Jeznach         } else {
3350c54acb8STomasz Jeznach             /* 64bit mode for GXL/SXL == 0 */
3360c54acb8STomasz Jeznach             switch (pass ? gatp : satp) {
3370c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_IOHGATP_MODE_BARE:
3380c54acb8STomasz Jeznach                 sc[pass].levels    = 0;
3390c54acb8STomasz Jeznach                 sc[pass].ptidxbits = 0;
3400c54acb8STomasz Jeznach                 sc[pass].ptesize   = 0;
3410c54acb8STomasz Jeznach                 break;
3420c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_IOHGATP_MODE_SV39X4:
3430c54acb8STomasz Jeznach                 sv_mode = pass ? RISCV_IOMMU_CAP_SV39X4 : RISCV_IOMMU_CAP_SV39;
3440c54acb8STomasz Jeznach                 if (!(s->cap & sv_mode)) {
3450c54acb8STomasz Jeznach                     return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
3460c54acb8STomasz Jeznach                 }
3470c54acb8STomasz Jeznach                 sc[pass].levels    = 3;
3480c54acb8STomasz Jeznach                 sc[pass].ptidxbits = 9;
3490c54acb8STomasz Jeznach                 sc[pass].ptesize   = 8;
3500c54acb8STomasz Jeznach                 break;
3510c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_IOHGATP_MODE_SV48X4:
3520c54acb8STomasz Jeznach                 sv_mode = pass ? RISCV_IOMMU_CAP_SV48X4 : RISCV_IOMMU_CAP_SV48;
3530c54acb8STomasz Jeznach                 if (!(s->cap & sv_mode)) {
3540c54acb8STomasz Jeznach                     return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
3550c54acb8STomasz Jeznach                 }
3560c54acb8STomasz Jeznach                 sc[pass].levels    = 4;
3570c54acb8STomasz Jeznach                 sc[pass].ptidxbits = 9;
3580c54acb8STomasz Jeznach                 sc[pass].ptesize   = 8;
3590c54acb8STomasz Jeznach                 break;
3600c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_IOHGATP_MODE_SV57X4:
3610c54acb8STomasz Jeznach                 sv_mode = pass ? RISCV_IOMMU_CAP_SV57X4 : RISCV_IOMMU_CAP_SV57;
3620c54acb8STomasz Jeznach                 if (!(s->cap & sv_mode)) {
3630c54acb8STomasz Jeznach                     return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
3640c54acb8STomasz Jeznach                 }
3650c54acb8STomasz Jeznach                 sc[pass].levels    = 5;
3660c54acb8STomasz Jeznach                 sc[pass].ptidxbits = 9;
3670c54acb8STomasz Jeznach                 sc[pass].ptesize   = 8;
3680c54acb8STomasz Jeznach                 break;
3690c54acb8STomasz Jeznach             default:
3700c54acb8STomasz Jeznach                 return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
3710c54acb8STomasz Jeznach             }
3720c54acb8STomasz Jeznach         }
3730c54acb8STomasz Jeznach     };
3740c54acb8STomasz Jeznach 
3750c54acb8STomasz Jeznach     /* S/G stages translation tables root pointers */
3760c54acb8STomasz Jeznach     gatp = PPN_PHYS(get_field(ctx->gatp, RISCV_IOMMU_ATP_PPN_FIELD));
3770c54acb8STomasz Jeznach     satp = PPN_PHYS(get_field(ctx->satp, RISCV_IOMMU_ATP_PPN_FIELD));
3780c54acb8STomasz Jeznach     addr = (en_s && en_g) ? satp : iotlb->iova;
3790c54acb8STomasz Jeznach     base = en_g ? gatp : satp;
3800c54acb8STomasz Jeznach     pass = en_g ? G_STAGE : S_STAGE;
3810c54acb8STomasz Jeznach 
3820c54acb8STomasz Jeznach     do {
3830c54acb8STomasz Jeznach         const unsigned widened = (pass && !sc[pass].step) ? 2 : 0;
3840c54acb8STomasz Jeznach         const unsigned va_bits = widened + sc[pass].ptidxbits;
3850c54acb8STomasz Jeznach         const unsigned va_skip = TARGET_PAGE_BITS + sc[pass].ptidxbits *
3860c54acb8STomasz Jeznach                                  (sc[pass].levels - 1 - sc[pass].step);
3870c54acb8STomasz Jeznach         const unsigned idx = (addr >> va_skip) & ((1 << va_bits) - 1);
3880c54acb8STomasz Jeznach         const dma_addr_t pte_addr = base + idx * sc[pass].ptesize;
3890c54acb8STomasz Jeznach         const bool ade =
3900c54acb8STomasz Jeznach             ctx->tc & (pass ? RISCV_IOMMU_DC_TC_GADE : RISCV_IOMMU_DC_TC_SADE);
3910c54acb8STomasz Jeznach 
3920c54acb8STomasz Jeznach         /* Address range check before first level lookup */
3930c54acb8STomasz Jeznach         if (!sc[pass].step) {
394e5d28bf2SJason Chien             const uint64_t va_len = va_skip + va_bits;
395e5d28bf2SJason Chien             const uint64_t va_mask = (1ULL << va_len) - 1;
396e5d28bf2SJason Chien 
397e5d28bf2SJason Chien             if (pass == S_STAGE && va_len > 32) {
398e5d28bf2SJason Chien                 target_ulong mask, masked_msbs;
399e5d28bf2SJason Chien 
400e5d28bf2SJason Chien                 mask = (1L << (TARGET_LONG_BITS - (va_len - 1))) - 1;
401e5d28bf2SJason Chien                 masked_msbs = (addr >> (va_len - 1)) & mask;
402e5d28bf2SJason Chien 
403e5d28bf2SJason Chien                 if (masked_msbs != 0 && masked_msbs != mask) {
404e5d28bf2SJason Chien                     return (iotlb->perm & IOMMU_WO) ?
405e5d28bf2SJason Chien                                 RISCV_IOMMU_FQ_CAUSE_WR_FAULT_S :
406e5d28bf2SJason Chien                                 RISCV_IOMMU_FQ_CAUSE_RD_FAULT_S;
407e5d28bf2SJason Chien                 }
408e5d28bf2SJason Chien             } else {
4090c54acb8STomasz Jeznach                 if ((addr & va_mask) != addr) {
410e5d28bf2SJason Chien                     return (iotlb->perm & IOMMU_WO) ?
411e5d28bf2SJason Chien                                 RISCV_IOMMU_FQ_CAUSE_WR_FAULT_VS :
412e5d28bf2SJason Chien                                 RISCV_IOMMU_FQ_CAUSE_RD_FAULT_VS;
413e5d28bf2SJason Chien                 }
4140c54acb8STomasz Jeznach             }
4150c54acb8STomasz Jeznach         }
4160c54acb8STomasz Jeznach 
4170c54acb8STomasz Jeznach         /* Read page table entry */
4180c54acb8STomasz Jeznach         if (sc[pass].ptesize == 4) {
4190c54acb8STomasz Jeznach             uint32_t pte32 = 0;
4200c54acb8STomasz Jeznach             ret = ldl_le_dma(s->target_as, pte_addr, &pte32,
4210c54acb8STomasz Jeznach                              MEMTXATTRS_UNSPECIFIED);
4220c54acb8STomasz Jeznach             pte = pte32;
4230c54acb8STomasz Jeznach         } else {
4240c54acb8STomasz Jeznach             ret = ldq_le_dma(s->target_as, pte_addr, &pte,
4250c54acb8STomasz Jeznach                              MEMTXATTRS_UNSPECIFIED);
4260c54acb8STomasz Jeznach         }
4270c54acb8STomasz Jeznach         if (ret != MEMTX_OK) {
4280c54acb8STomasz Jeznach             return (iotlb->perm & IOMMU_WO) ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT
4290c54acb8STomasz Jeznach                                             : RISCV_IOMMU_FQ_CAUSE_RD_FAULT;
4300c54acb8STomasz Jeznach         }
4310c54acb8STomasz Jeznach 
4320c54acb8STomasz Jeznach         sc[pass].step++;
4330c54acb8STomasz Jeznach         hwaddr ppn = pte >> PTE_PPN_SHIFT;
4340c54acb8STomasz Jeznach 
4350c54acb8STomasz Jeznach         if (!(pte & PTE_V)) {
4360c54acb8STomasz Jeznach             break;                /* Invalid PTE */
4370c54acb8STomasz Jeznach         } else if (!(pte & (PTE_R | PTE_W | PTE_X))) {
4380c54acb8STomasz Jeznach             base = PPN_PHYS(ppn); /* Inner PTE, continue walking */
4390c54acb8STomasz Jeznach         } else if ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) {
4400c54acb8STomasz Jeznach             break;                /* Reserved leaf PTE flags: PTE_W */
4410c54acb8STomasz Jeznach         } else if ((pte & (PTE_R | PTE_W | PTE_X)) == (PTE_W | PTE_X)) {
4420c54acb8STomasz Jeznach             break;                /* Reserved leaf PTE flags: PTE_W + PTE_X */
4430c54acb8STomasz Jeznach         } else if (ppn & ((1ULL << (va_skip - TARGET_PAGE_BITS)) - 1)) {
4440c54acb8STomasz Jeznach             break;                /* Misaligned PPN */
4450c54acb8STomasz Jeznach         } else if ((iotlb->perm & IOMMU_RO) && !(pte & PTE_R)) {
4460c54acb8STomasz Jeznach             break;                /* Read access check failed */
4470c54acb8STomasz Jeznach         } else if ((iotlb->perm & IOMMU_WO) && !(pte & PTE_W)) {
4480c54acb8STomasz Jeznach             break;                /* Write access check failed */
4490c54acb8STomasz Jeznach         } else if ((iotlb->perm & IOMMU_RO) && !ade && !(pte & PTE_A)) {
4500c54acb8STomasz Jeznach             break;                /* Access bit not set */
4510c54acb8STomasz Jeznach         } else if ((iotlb->perm & IOMMU_WO) && !ade && !(pte & PTE_D)) {
4520c54acb8STomasz Jeznach             break;                /* Dirty bit not set */
4530c54acb8STomasz Jeznach         } else {
4540c54acb8STomasz Jeznach             /* Leaf PTE, translation completed. */
4550c54acb8STomasz Jeznach             sc[pass].step = sc[pass].levels;
4560c54acb8STomasz Jeznach             base = PPN_PHYS(ppn) | (addr & ((1ULL << va_skip) - 1));
4570c54acb8STomasz Jeznach             /* Update address mask based on smallest translation granularity */
4580c54acb8STomasz Jeznach             iotlb->addr_mask &= (1ULL << va_skip) - 1;
4590c54acb8STomasz Jeznach             /* Continue with S-Stage translation? */
4600c54acb8STomasz Jeznach             if (pass && sc[0].step != sc[0].levels) {
4610c54acb8STomasz Jeznach                 pass = S_STAGE;
4620c54acb8STomasz Jeznach                 addr = iotlb->iova;
4630c54acb8STomasz Jeznach                 continue;
4640c54acb8STomasz Jeznach             }
4650c54acb8STomasz Jeznach             /* Translation phase completed (GPA or SPA) */
4660c54acb8STomasz Jeznach             iotlb->translated_addr = base;
4670c54acb8STomasz Jeznach             iotlb->perm = (pte & PTE_W) ? ((pte & PTE_R) ? IOMMU_RW : IOMMU_WO)
4680c54acb8STomasz Jeznach                                                          : IOMMU_RO;
4690c54acb8STomasz Jeznach 
4700c54acb8STomasz Jeznach             /* Check MSI GPA address match */
4710c54acb8STomasz Jeznach             if (pass == S_STAGE && (iotlb->perm & IOMMU_WO) &&
4720c54acb8STomasz Jeznach                 riscv_iommu_msi_check(s, ctx, base)) {
4730c54acb8STomasz Jeznach                 /* Trap MSI writes and return GPA address. */
4740c54acb8STomasz Jeznach                 iotlb->target_as = &s->trap_as;
4750c54acb8STomasz Jeznach                 iotlb->addr_mask = ~TARGET_PAGE_MASK;
4760c54acb8STomasz Jeznach                 return 0;
4770c54acb8STomasz Jeznach             }
4780c54acb8STomasz Jeznach 
4790c54acb8STomasz Jeznach             /* Continue with G-Stage translation? */
4800c54acb8STomasz Jeznach             if (!pass && en_g) {
4810c54acb8STomasz Jeznach                 pass = G_STAGE;
4820c54acb8STomasz Jeznach                 addr = base;
4830c54acb8STomasz Jeznach                 base = gatp;
4840c54acb8STomasz Jeznach                 sc[pass].step = 0;
4850c54acb8STomasz Jeznach                 continue;
4860c54acb8STomasz Jeznach             }
4870c54acb8STomasz Jeznach 
4880c54acb8STomasz Jeznach             return 0;
4890c54acb8STomasz Jeznach         }
4900c54acb8STomasz Jeznach 
4910c54acb8STomasz Jeznach         if (sc[pass].step == sc[pass].levels) {
4920c54acb8STomasz Jeznach             break; /* Can't find leaf PTE */
4930c54acb8STomasz Jeznach         }
4940c54acb8STomasz Jeznach 
4950c54acb8STomasz Jeznach         /* Continue with G-Stage translation? */
4960c54acb8STomasz Jeznach         if (!pass && en_g) {
4970c54acb8STomasz Jeznach             pass = G_STAGE;
4980c54acb8STomasz Jeznach             addr = base;
4990c54acb8STomasz Jeznach             base = gatp;
5000c54acb8STomasz Jeznach             sc[pass].step = 0;
5010c54acb8STomasz Jeznach         }
5020c54acb8STomasz Jeznach     } while (1);
5030c54acb8STomasz Jeznach 
5040c54acb8STomasz Jeznach     return (iotlb->perm & IOMMU_WO) ?
5050c54acb8STomasz Jeznach                 (pass ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT_VS :
5060c54acb8STomasz Jeznach                         RISCV_IOMMU_FQ_CAUSE_WR_FAULT_S) :
5070c54acb8STomasz Jeznach                 (pass ? RISCV_IOMMU_FQ_CAUSE_RD_FAULT_VS :
5080c54acb8STomasz Jeznach                         RISCV_IOMMU_FQ_CAUSE_RD_FAULT_S);
5090c54acb8STomasz Jeznach }
5100c54acb8STomasz Jeznach 
5110c54acb8STomasz Jeznach static void riscv_iommu_report_fault(RISCVIOMMUState *s,
5120c54acb8STomasz Jeznach                                      RISCVIOMMUContext *ctx,
5130c54acb8STomasz Jeznach                                      uint32_t fault_type, uint32_t cause,
5140c54acb8STomasz Jeznach                                      bool pv,
5150c54acb8STomasz Jeznach                                      uint64_t iotval, uint64_t iotval2)
5160c54acb8STomasz Jeznach {
5170c54acb8STomasz Jeznach     struct riscv_iommu_fq_record ev = { 0 };
5180c54acb8STomasz Jeznach 
5190c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_DTF) {
5200c54acb8STomasz Jeznach         switch (cause) {
5210c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_DMA_DISABLED:
5220c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_DDT_LOAD_FAULT:
5230c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_DDT_INVALID:
5240c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED:
5250c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_DDT_CORRUPTED:
5260c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_INTERNAL_DP_ERROR:
5270c54acb8STomasz Jeznach         case RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT:
5280c54acb8STomasz Jeznach             break;
5290c54acb8STomasz Jeznach         default:
5300c54acb8STomasz Jeznach             /* DTF prevents reporting a fault for this given cause */
5310c54acb8STomasz Jeznach             return;
5320c54acb8STomasz Jeznach         }
5330c54acb8STomasz Jeznach     }
5340c54acb8STomasz Jeznach 
5350c54acb8STomasz Jeznach     ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_CAUSE, cause);
5360c54acb8STomasz Jeznach     ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_TTYPE, fault_type);
5370c54acb8STomasz Jeznach     ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_DID, ctx->devid);
5380c54acb8STomasz Jeznach     ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_PV, true);
5390c54acb8STomasz Jeznach 
5400c54acb8STomasz Jeznach     if (pv) {
5410c54acb8STomasz Jeznach         ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_PID, ctx->process_id);
5420c54acb8STomasz Jeznach     }
5430c54acb8STomasz Jeznach 
5440c54acb8STomasz Jeznach     ev.iotval = iotval;
5450c54acb8STomasz Jeznach     ev.iotval2 = iotval2;
5460c54acb8STomasz Jeznach 
5470c54acb8STomasz Jeznach     riscv_iommu_fault(s, &ev);
5480c54acb8STomasz Jeznach }
5490c54acb8STomasz Jeznach 
5500c54acb8STomasz Jeznach /* Redirect MSI write for given GPA. */
5510c54acb8STomasz Jeznach static MemTxResult riscv_iommu_msi_write(RISCVIOMMUState *s,
5520c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx, uint64_t gpa, uint64_t data,
5530c54acb8STomasz Jeznach     unsigned size, MemTxAttrs attrs)
5540c54acb8STomasz Jeznach {
5550c54acb8STomasz Jeznach     MemTxResult res;
5560c54acb8STomasz Jeznach     dma_addr_t addr;
5570c54acb8STomasz Jeznach     uint64_t intn;
5580c54acb8STomasz Jeznach     uint32_t n190;
5590c54acb8STomasz Jeznach     uint64_t pte[2];
5600c54acb8STomasz Jeznach     int fault_type = RISCV_IOMMU_FQ_TTYPE_UADDR_WR;
5610c54acb8STomasz Jeznach     int cause;
5620c54acb8STomasz Jeznach 
5630c54acb8STomasz Jeznach     /* Interrupt File Number */
564d37eede7SPierrick Bouvier     intn = riscv_iommu_pext_u64(PPN_DOWN(gpa), ctx->msi_addr_mask);
5650c54acb8STomasz Jeznach     if (intn >= 256) {
5660c54acb8STomasz Jeznach         /* Interrupt file number out of range */
5670c54acb8STomasz Jeznach         res = MEMTX_ACCESS_ERROR;
5680c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT;
5690c54acb8STomasz Jeznach         goto err;
5700c54acb8STomasz Jeznach     }
5710c54acb8STomasz Jeznach 
5720c54acb8STomasz Jeznach     /* fetch MSI PTE */
5730c54acb8STomasz Jeznach     addr = PPN_PHYS(get_field(ctx->msiptp, RISCV_IOMMU_DC_MSIPTP_PPN));
5740c54acb8STomasz Jeznach     addr = addr | (intn * sizeof(pte));
5750c54acb8STomasz Jeznach     res = dma_memory_read(s->target_as, addr, &pte, sizeof(pte),
5760c54acb8STomasz Jeznach             MEMTXATTRS_UNSPECIFIED);
5770c54acb8STomasz Jeznach     if (res != MEMTX_OK) {
5780c54acb8STomasz Jeznach         if (res == MEMTX_DECODE_ERROR) {
5790c54acb8STomasz Jeznach             cause = RISCV_IOMMU_FQ_CAUSE_MSI_PT_CORRUPTED;
5800c54acb8STomasz Jeznach         } else {
5810c54acb8STomasz Jeznach             cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT;
5820c54acb8STomasz Jeznach         }
5830c54acb8STomasz Jeznach         goto err;
5840c54acb8STomasz Jeznach     }
5850c54acb8STomasz Jeznach 
5860c54acb8STomasz Jeznach     le64_to_cpus(&pte[0]);
5870c54acb8STomasz Jeznach     le64_to_cpus(&pte[1]);
5880c54acb8STomasz Jeznach 
5890c54acb8STomasz Jeznach     if (!(pte[0] & RISCV_IOMMU_MSI_PTE_V) || (pte[0] & RISCV_IOMMU_MSI_PTE_C)) {
5900c54acb8STomasz Jeznach         /*
5910c54acb8STomasz Jeznach          * The spec mentions that: "If msipte.C == 1, then further
5920c54acb8STomasz Jeznach          * processing to interpret the PTE is implementation
5930c54acb8STomasz Jeznach          * defined.". We'll abort with cause = 262 for this
5940c54acb8STomasz Jeznach          * case too.
5950c54acb8STomasz Jeznach          */
5960c54acb8STomasz Jeznach         res = MEMTX_ACCESS_ERROR;
5970c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_INVALID;
5980c54acb8STomasz Jeznach         goto err;
5990c54acb8STomasz Jeznach     }
6000c54acb8STomasz Jeznach 
6010c54acb8STomasz Jeznach     switch (get_field(pte[0], RISCV_IOMMU_MSI_PTE_M)) {
6020c54acb8STomasz Jeznach     case RISCV_IOMMU_MSI_PTE_M_BASIC:
6030c54acb8STomasz Jeznach         /* MSI Pass-through mode */
6040c54acb8STomasz Jeznach         addr = PPN_PHYS(get_field(pte[0], RISCV_IOMMU_MSI_PTE_PPN));
6050c54acb8STomasz Jeznach 
6060c54acb8STomasz Jeznach         trace_riscv_iommu_msi(s->parent_obj.id, PCI_BUS_NUM(ctx->devid),
6070c54acb8STomasz Jeznach                               PCI_SLOT(ctx->devid), PCI_FUNC(ctx->devid),
6080c54acb8STomasz Jeznach                               gpa, addr);
6090c54acb8STomasz Jeznach 
6100c54acb8STomasz Jeznach         res = dma_memory_write(s->target_as, addr, &data, size, attrs);
6110c54acb8STomasz Jeznach         if (res != MEMTX_OK) {
6120c54acb8STomasz Jeznach             cause = RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT;
6130c54acb8STomasz Jeznach             goto err;
6140c54acb8STomasz Jeznach         }
6150c54acb8STomasz Jeznach 
6160c54acb8STomasz Jeznach         return MEMTX_OK;
6170c54acb8STomasz Jeznach     case RISCV_IOMMU_MSI_PTE_M_MRIF:
6180c54acb8STomasz Jeznach         /* MRIF mode, continue. */
6190c54acb8STomasz Jeznach         break;
6200c54acb8STomasz Jeznach     default:
6210c54acb8STomasz Jeznach         res = MEMTX_ACCESS_ERROR;
6220c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_MISCONFIGURED;
6230c54acb8STomasz Jeznach         goto err;
6240c54acb8STomasz Jeznach     }
6250c54acb8STomasz Jeznach 
6260c54acb8STomasz Jeznach     /*
6270c54acb8STomasz Jeznach      * Report an error for interrupt identities exceeding the maximum allowed
6280c54acb8STomasz Jeznach      * for an IMSIC interrupt file (2047) or destination address is not 32-bit
6290c54acb8STomasz Jeznach      * aligned. See IOMMU Specification, Chapter 2.3. MSI page tables.
6300c54acb8STomasz Jeznach      */
6310c54acb8STomasz Jeznach     if ((data > 2047) || (gpa & 3)) {
6320c54acb8STomasz Jeznach         res = MEMTX_ACCESS_ERROR;
6330c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_MISCONFIGURED;
6340c54acb8STomasz Jeznach         goto err;
6350c54acb8STomasz Jeznach     }
6360c54acb8STomasz Jeznach 
6370c54acb8STomasz Jeznach     /* MSI MRIF mode, non atomic pending bit update */
6380c54acb8STomasz Jeznach 
6390c54acb8STomasz Jeznach     /* MRIF pending bit address */
6400c54acb8STomasz Jeznach     addr = get_field(pte[0], RISCV_IOMMU_MSI_PTE_MRIF_ADDR) << 9;
6410c54acb8STomasz Jeznach     addr = addr | ((data & 0x7c0) >> 3);
6420c54acb8STomasz Jeznach 
6430c54acb8STomasz Jeznach     trace_riscv_iommu_msi(s->parent_obj.id, PCI_BUS_NUM(ctx->devid),
6440c54acb8STomasz Jeznach                           PCI_SLOT(ctx->devid), PCI_FUNC(ctx->devid),
6450c54acb8STomasz Jeznach                           gpa, addr);
6460c54acb8STomasz Jeznach 
6470c54acb8STomasz Jeznach     /* MRIF pending bit mask */
6480c54acb8STomasz Jeznach     data = 1ULL << (data & 0x03f);
6490c54acb8STomasz Jeznach     res = dma_memory_read(s->target_as, addr, &intn, sizeof(intn), attrs);
6500c54acb8STomasz Jeznach     if (res != MEMTX_OK) {
6510c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT;
6520c54acb8STomasz Jeznach         goto err;
6530c54acb8STomasz Jeznach     }
6540c54acb8STomasz Jeznach 
6550c54acb8STomasz Jeznach     intn = intn | data;
6560c54acb8STomasz Jeznach     res = dma_memory_write(s->target_as, addr, &intn, sizeof(intn), attrs);
6570c54acb8STomasz Jeznach     if (res != MEMTX_OK) {
6580c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT;
6590c54acb8STomasz Jeznach         goto err;
6600c54acb8STomasz Jeznach     }
6610c54acb8STomasz Jeznach 
6620c54acb8STomasz Jeznach     /* Get MRIF enable bits */
6630c54acb8STomasz Jeznach     addr = addr + sizeof(intn);
6640c54acb8STomasz Jeznach     res = dma_memory_read(s->target_as, addr, &intn, sizeof(intn), attrs);
6650c54acb8STomasz Jeznach     if (res != MEMTX_OK) {
6660c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT;
6670c54acb8STomasz Jeznach         goto err;
6680c54acb8STomasz Jeznach     }
6690c54acb8STomasz Jeznach 
6700c54acb8STomasz Jeznach     if (!(intn & data)) {
6710c54acb8STomasz Jeznach         /* notification disabled, MRIF update completed. */
6720c54acb8STomasz Jeznach         return MEMTX_OK;
6730c54acb8STomasz Jeznach     }
6740c54acb8STomasz Jeznach 
6750c54acb8STomasz Jeznach     /* Send notification message */
6760c54acb8STomasz Jeznach     addr = PPN_PHYS(get_field(pte[1], RISCV_IOMMU_MSI_MRIF_NPPN));
6770c54acb8STomasz Jeznach     n190 = get_field(pte[1], RISCV_IOMMU_MSI_MRIF_NID) |
6780c54acb8STomasz Jeznach           (get_field(pte[1], RISCV_IOMMU_MSI_MRIF_NID_MSB) << 10);
6790c54acb8STomasz Jeznach 
6800c54acb8STomasz Jeznach     res = dma_memory_write(s->target_as, addr, &n190, sizeof(n190), attrs);
6810c54acb8STomasz Jeznach     if (res != MEMTX_OK) {
6820c54acb8STomasz Jeznach         cause = RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT;
6830c54acb8STomasz Jeznach         goto err;
6840c54acb8STomasz Jeznach     }
6850c54acb8STomasz Jeznach 
6860c54acb8STomasz Jeznach     trace_riscv_iommu_mrif_notification(s->parent_obj.id, n190, addr);
6870c54acb8STomasz Jeznach 
6880c54acb8STomasz Jeznach     return MEMTX_OK;
6890c54acb8STomasz Jeznach 
6900c54acb8STomasz Jeznach err:
6910c54acb8STomasz Jeznach     riscv_iommu_report_fault(s, ctx, fault_type, cause,
6920c54acb8STomasz Jeznach                              !!ctx->process_id, 0, 0);
6930c54acb8STomasz Jeznach     return res;
6940c54acb8STomasz Jeznach }
6950c54acb8STomasz Jeznach 
6960c54acb8STomasz Jeznach /*
6970c54acb8STomasz Jeznach  * Check device context configuration as described by the
6980c54acb8STomasz Jeznach  * riscv-iommu spec section "Device-context configuration
6990c54acb8STomasz Jeznach  * checks".
7000c54acb8STomasz Jeznach  */
7010c54acb8STomasz Jeznach static bool riscv_iommu_validate_device_ctx(RISCVIOMMUState *s,
7020c54acb8STomasz Jeznach                                             RISCVIOMMUContext *ctx)
7030c54acb8STomasz Jeznach {
7040c54acb8STomasz Jeznach     uint32_t fsc_mode, msi_mode;
70569a9ae48STomasz Jeznach     uint64_t gatp;
70669a9ae48STomasz Jeznach 
70769a9ae48STomasz Jeznach     if (!(s->cap & RISCV_IOMMU_CAP_ATS) &&
70869a9ae48STomasz Jeznach         (ctx->tc & RISCV_IOMMU_DC_TC_EN_ATS ||
70969a9ae48STomasz Jeznach          ctx->tc & RISCV_IOMMU_DC_TC_EN_PRI ||
71069a9ae48STomasz Jeznach          ctx->tc & RISCV_IOMMU_DC_TC_PRPR)) {
71169a9ae48STomasz Jeznach         return false;
71269a9ae48STomasz Jeznach     }
71369a9ae48STomasz Jeznach 
71469a9ae48STomasz Jeznach     if (!(ctx->tc & RISCV_IOMMU_DC_TC_EN_ATS) &&
71569a9ae48STomasz Jeznach         (ctx->tc & RISCV_IOMMU_DC_TC_T2GPA ||
71669a9ae48STomasz Jeznach          ctx->tc & RISCV_IOMMU_DC_TC_EN_PRI)) {
71769a9ae48STomasz Jeznach         return false;
71869a9ae48STomasz Jeznach     }
7190c54acb8STomasz Jeznach 
7200c54acb8STomasz Jeznach     if (!(ctx->tc & RISCV_IOMMU_DC_TC_EN_PRI) &&
7210c54acb8STomasz Jeznach         ctx->tc & RISCV_IOMMU_DC_TC_PRPR) {
7220c54acb8STomasz Jeznach         return false;
7230c54acb8STomasz Jeznach     }
7240c54acb8STomasz Jeznach 
7250c54acb8STomasz Jeznach     if (!(s->cap & RISCV_IOMMU_CAP_T2GPA) &&
7260c54acb8STomasz Jeznach         ctx->tc & RISCV_IOMMU_DC_TC_T2GPA) {
7270c54acb8STomasz Jeznach         return false;
7280c54acb8STomasz Jeznach     }
7290c54acb8STomasz Jeznach 
7300c54acb8STomasz Jeznach     if (s->cap & RISCV_IOMMU_CAP_MSI_FLAT) {
7310c54acb8STomasz Jeznach         msi_mode = get_field(ctx->msiptp, RISCV_IOMMU_DC_MSIPTP_MODE);
7320c54acb8STomasz Jeznach 
7330c54acb8STomasz Jeznach         if (msi_mode != RISCV_IOMMU_DC_MSIPTP_MODE_OFF &&
7340c54acb8STomasz Jeznach             msi_mode != RISCV_IOMMU_DC_MSIPTP_MODE_FLAT) {
7350c54acb8STomasz Jeznach             return false;
7360c54acb8STomasz Jeznach         }
7370c54acb8STomasz Jeznach     }
7380c54acb8STomasz Jeznach 
73969a9ae48STomasz Jeznach     gatp = get_field(ctx->gatp, RISCV_IOMMU_ATP_MODE_FIELD);
74069a9ae48STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_T2GPA &&
74169a9ae48STomasz Jeznach         gatp == RISCV_IOMMU_DC_IOHGATP_MODE_BARE) {
74269a9ae48STomasz Jeznach         return false;
74369a9ae48STomasz Jeznach     }
74469a9ae48STomasz Jeznach 
7450c54acb8STomasz Jeznach     fsc_mode = get_field(ctx->satp, RISCV_IOMMU_DC_FSC_MODE);
7460c54acb8STomasz Jeznach 
7470c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_PDTV) {
7480c54acb8STomasz Jeznach         switch (fsc_mode) {
7490c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_PDTP_MODE_PD8:
7500c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_PD8)) {
7510c54acb8STomasz Jeznach                 return false;
7520c54acb8STomasz Jeznach             }
7530c54acb8STomasz Jeznach             break;
7540c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_PDTP_MODE_PD17:
7550c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_PD17)) {
7560c54acb8STomasz Jeznach                 return false;
7570c54acb8STomasz Jeznach             }
7580c54acb8STomasz Jeznach             break;
7590c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_PDTP_MODE_PD20:
7600c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_PD20)) {
7610c54acb8STomasz Jeznach                 return false;
7620c54acb8STomasz Jeznach             }
7630c54acb8STomasz Jeznach             break;
7640c54acb8STomasz Jeznach         }
7650c54acb8STomasz Jeznach     } else {
7660c54acb8STomasz Jeznach         /* DC.tc.PDTV is 0 */
7670c54acb8STomasz Jeznach         if (ctx->tc & RISCV_IOMMU_DC_TC_DPE) {
7680c54acb8STomasz Jeznach             return false;
7690c54acb8STomasz Jeznach         }
7700c54acb8STomasz Jeznach 
7710c54acb8STomasz Jeznach         if (ctx->tc & RISCV_IOMMU_DC_TC_SXL) {
7720c54acb8STomasz Jeznach             if (fsc_mode == RISCV_IOMMU_CAP_SV32 &&
7730c54acb8STomasz Jeznach                 !(s->cap & RISCV_IOMMU_CAP_SV32)) {
7740c54acb8STomasz Jeznach                 return false;
7750c54acb8STomasz Jeznach             }
7760c54acb8STomasz Jeznach         } else {
7770c54acb8STomasz Jeznach             switch (fsc_mode) {
7780c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV39:
7790c54acb8STomasz Jeznach                 if (!(s->cap & RISCV_IOMMU_CAP_SV39)) {
7800c54acb8STomasz Jeznach                     return false;
7810c54acb8STomasz Jeznach                 }
7820c54acb8STomasz Jeznach                 break;
7830c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV48:
7840c54acb8STomasz Jeznach                 if (!(s->cap & RISCV_IOMMU_CAP_SV48)) {
7850c54acb8STomasz Jeznach                     return false;
7860c54acb8STomasz Jeznach                 }
7870c54acb8STomasz Jeznach             break;
7880c54acb8STomasz Jeznach             case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57:
7890c54acb8STomasz Jeznach                 if (!(s->cap & RISCV_IOMMU_CAP_SV57)) {
7900c54acb8STomasz Jeznach                     return false;
7910c54acb8STomasz Jeznach                 }
7920c54acb8STomasz Jeznach                 break;
7930c54acb8STomasz Jeznach             }
7940c54acb8STomasz Jeznach         }
7950c54acb8STomasz Jeznach     }
7960c54acb8STomasz Jeznach 
7970c54acb8STomasz Jeznach     /*
7980c54acb8STomasz Jeznach      * CAP_END is always zero (only one endianess). FCTL_BE is
7990c54acb8STomasz Jeznach      * always zero (little-endian accesses). Thus TC_SBE must
8000c54acb8STomasz Jeznach      * always be LE, i.e. zero.
8010c54acb8STomasz Jeznach      */
8020c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_SBE) {
8030c54acb8STomasz Jeznach         return false;
8040c54acb8STomasz Jeznach     }
8050c54acb8STomasz Jeznach 
8060c54acb8STomasz Jeznach     return true;
8070c54acb8STomasz Jeznach }
8080c54acb8STomasz Jeznach 
8090c54acb8STomasz Jeznach /*
8100c54acb8STomasz Jeznach  * Validate process context (PC) according to section
8110c54acb8STomasz Jeznach  * "Process-context configuration checks".
8120c54acb8STomasz Jeznach  */
8130c54acb8STomasz Jeznach static bool riscv_iommu_validate_process_ctx(RISCVIOMMUState *s,
8140c54acb8STomasz Jeznach                                              RISCVIOMMUContext *ctx)
8150c54acb8STomasz Jeznach {
8160c54acb8STomasz Jeznach     uint32_t mode;
8170c54acb8STomasz Jeznach 
8180c54acb8STomasz Jeznach     if (get_field(ctx->ta, RISCV_IOMMU_PC_TA_RESERVED)) {
8190c54acb8STomasz Jeznach         return false;
8200c54acb8STomasz Jeznach     }
8210c54acb8STomasz Jeznach 
8220c54acb8STomasz Jeznach     if (get_field(ctx->satp, RISCV_IOMMU_PC_FSC_RESERVED)) {
8230c54acb8STomasz Jeznach         return false;
8240c54acb8STomasz Jeznach     }
8250c54acb8STomasz Jeznach 
8260c54acb8STomasz Jeznach     mode = get_field(ctx->satp, RISCV_IOMMU_DC_FSC_MODE);
8270c54acb8STomasz Jeznach     switch (mode) {
8280c54acb8STomasz Jeznach     case RISCV_IOMMU_DC_FSC_MODE_BARE:
8290c54acb8STomasz Jeznach     /* sv39 and sv32 modes have the same value (8) */
8300c54acb8STomasz Jeznach     case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV39:
8310c54acb8STomasz Jeznach     case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV48:
8320c54acb8STomasz Jeznach     case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57:
8330c54acb8STomasz Jeznach         break;
8340c54acb8STomasz Jeznach     default:
8350c54acb8STomasz Jeznach         return false;
8360c54acb8STomasz Jeznach     }
8370c54acb8STomasz Jeznach 
8380c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_SXL) {
839d3b96a53SDaniel Henrique Barboza         if (mode == RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV32 &&
8400c54acb8STomasz Jeznach             !(s->cap & RISCV_IOMMU_CAP_SV32)) {
8410c54acb8STomasz Jeznach                 return false;
8420c54acb8STomasz Jeznach         }
8430c54acb8STomasz Jeznach     } else {
8440c54acb8STomasz Jeznach         switch (mode) {
8450c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV39:
8460c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_SV39)) {
8470c54acb8STomasz Jeznach                 return false;
8480c54acb8STomasz Jeznach             }
8490c54acb8STomasz Jeznach             break;
8500c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV48:
8510c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_SV48)) {
8520c54acb8STomasz Jeznach                 return false;
8530c54acb8STomasz Jeznach             }
8540c54acb8STomasz Jeznach             break;
8550c54acb8STomasz Jeznach         case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57:
8560c54acb8STomasz Jeznach             if (!(s->cap & RISCV_IOMMU_CAP_SV57)) {
8570c54acb8STomasz Jeznach                 return false;
8580c54acb8STomasz Jeznach             }
8590c54acb8STomasz Jeznach             break;
8600c54acb8STomasz Jeznach         }
8610c54acb8STomasz Jeznach     }
8620c54acb8STomasz Jeznach 
8630c54acb8STomasz Jeznach     return true;
8640c54acb8STomasz Jeznach }
8650c54acb8STomasz Jeznach 
8660c54acb8STomasz Jeznach /*
8670c54acb8STomasz Jeznach  * RISC-V IOMMU Device Context Loopkup - Device Directory Tree Walk
8680c54acb8STomasz Jeznach  *
8690c54acb8STomasz Jeznach  * @s         : IOMMU Device State
8700c54acb8STomasz Jeznach  * @ctx       : Device Translation Context with devid and process_id set.
8710c54acb8STomasz Jeznach  * @return    : success or fault code.
8720c54acb8STomasz Jeznach  */
8730c54acb8STomasz Jeznach static int riscv_iommu_ctx_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx)
8740c54acb8STomasz Jeznach {
8750c54acb8STomasz Jeznach     const uint64_t ddtp = s->ddtp;
8760c54acb8STomasz Jeznach     unsigned mode = get_field(ddtp, RISCV_IOMMU_DDTP_MODE);
8770c54acb8STomasz Jeznach     dma_addr_t addr = PPN_PHYS(get_field(ddtp, RISCV_IOMMU_DDTP_PPN));
8780c54acb8STomasz Jeznach     struct riscv_iommu_dc dc;
8790c54acb8STomasz Jeznach     /* Device Context format: 0: extended (64 bytes) | 1: base (32 bytes) */
8800c54acb8STomasz Jeznach     const int dc_fmt = !s->enable_msi;
8810c54acb8STomasz Jeznach     const size_t dc_len = sizeof(dc) >> dc_fmt;
882cd5d265fSDaniel Henrique Barboza     int depth;
8830c54acb8STomasz Jeznach     uint64_t de;
8840c54acb8STomasz Jeznach 
8850c54acb8STomasz Jeznach     switch (mode) {
8860c54acb8STomasz Jeznach     case RISCV_IOMMU_DDTP_MODE_OFF:
8870c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_DMA_DISABLED;
8880c54acb8STomasz Jeznach 
8890c54acb8STomasz Jeznach     case RISCV_IOMMU_DDTP_MODE_BARE:
8900c54acb8STomasz Jeznach         /* mock up pass-through translation context */
8910c54acb8STomasz Jeznach         ctx->gatp = set_field(0, RISCV_IOMMU_ATP_MODE_FIELD,
8920c54acb8STomasz Jeznach             RISCV_IOMMU_DC_IOHGATP_MODE_BARE);
8930c54acb8STomasz Jeznach         ctx->satp = set_field(0, RISCV_IOMMU_ATP_MODE_FIELD,
8940c54acb8STomasz Jeznach             RISCV_IOMMU_DC_FSC_MODE_BARE);
89569a9ae48STomasz Jeznach 
8960c54acb8STomasz Jeznach         ctx->tc = RISCV_IOMMU_DC_TC_V;
89769a9ae48STomasz Jeznach         if (s->enable_ats) {
89869a9ae48STomasz Jeznach             ctx->tc |= RISCV_IOMMU_DC_TC_EN_ATS;
89969a9ae48STomasz Jeznach         }
90069a9ae48STomasz Jeznach 
9010c54acb8STomasz Jeznach         ctx->ta = 0;
9020c54acb8STomasz Jeznach         ctx->msiptp = 0;
9030c54acb8STomasz Jeznach         return 0;
9040c54acb8STomasz Jeznach 
9050c54acb8STomasz Jeznach     case RISCV_IOMMU_DDTP_MODE_1LVL:
9060c54acb8STomasz Jeznach         depth = 0;
9070c54acb8STomasz Jeznach         break;
9080c54acb8STomasz Jeznach 
9090c54acb8STomasz Jeznach     case RISCV_IOMMU_DDTP_MODE_2LVL:
9100c54acb8STomasz Jeznach         depth = 1;
9110c54acb8STomasz Jeznach         break;
9120c54acb8STomasz Jeznach 
9130c54acb8STomasz Jeznach     case RISCV_IOMMU_DDTP_MODE_3LVL:
9140c54acb8STomasz Jeznach         depth = 2;
9150c54acb8STomasz Jeznach         break;
9160c54acb8STomasz Jeznach 
9170c54acb8STomasz Jeznach     default:
9180c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
9190c54acb8STomasz Jeznach     }
9200c54acb8STomasz Jeznach 
9210c54acb8STomasz Jeznach     /*
9220c54acb8STomasz Jeznach      * Check supported device id width (in bits).
9230c54acb8STomasz Jeznach      * See IOMMU Specification, Chapter 6. Software guidelines.
9240c54acb8STomasz Jeznach      * - if extended device-context format is used:
9250c54acb8STomasz Jeznach      *   1LVL: 6, 2LVL: 15, 3LVL: 24
9260c54acb8STomasz Jeznach      * - if base device-context format is used:
9270c54acb8STomasz Jeznach      *   1LVL: 7, 2LVL: 16, 3LVL: 24
9280c54acb8STomasz Jeznach      */
9290c54acb8STomasz Jeznach     if (ctx->devid >= (1 << (depth * 9 + 6 + (dc_fmt && depth != 2)))) {
9300c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_TTYPE_BLOCKED;
9310c54acb8STomasz Jeznach     }
9320c54acb8STomasz Jeznach 
9330c54acb8STomasz Jeznach     /* Device directory tree walk */
9340c54acb8STomasz Jeznach     for (; depth-- > 0; ) {
9350c54acb8STomasz Jeznach         /*
9360c54acb8STomasz Jeznach          * Select device id index bits based on device directory tree level
9370c54acb8STomasz Jeznach          * and device context format.
9380c54acb8STomasz Jeznach          * See IOMMU Specification, Chapter 2. Data Structures.
9390c54acb8STomasz Jeznach          * - if extended device-context format is used:
9400c54acb8STomasz Jeznach          *   device index: [23:15][14:6][5:0]
9410c54acb8STomasz Jeznach          * - if base device-context format is used:
9420c54acb8STomasz Jeznach          *   device index: [23:16][15:7][6:0]
9430c54acb8STomasz Jeznach          */
9440c54acb8STomasz Jeznach         const int split = depth * 9 + 6 + dc_fmt;
9450c54acb8STomasz Jeznach         addr |= ((ctx->devid >> split) << 3) & ~TARGET_PAGE_MASK;
9460c54acb8STomasz Jeznach         if (dma_memory_read(s->target_as, addr, &de, sizeof(de),
9470c54acb8STomasz Jeznach                             MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
9480c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_DDT_LOAD_FAULT;
9490c54acb8STomasz Jeznach         }
9500c54acb8STomasz Jeznach         le64_to_cpus(&de);
9510c54acb8STomasz Jeznach         if (!(de & RISCV_IOMMU_DDTE_VALID)) {
9520c54acb8STomasz Jeznach             /* invalid directory entry */
9530c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_DDT_INVALID;
9540c54acb8STomasz Jeznach         }
9550c54acb8STomasz Jeznach         if (de & ~(RISCV_IOMMU_DDTE_PPN | RISCV_IOMMU_DDTE_VALID)) {
9560c54acb8STomasz Jeznach             /* reserved bits set */
9570c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
9580c54acb8STomasz Jeznach         }
9590c54acb8STomasz Jeznach         addr = PPN_PHYS(get_field(de, RISCV_IOMMU_DDTE_PPN));
9600c54acb8STomasz Jeznach     }
9610c54acb8STomasz Jeznach 
9620c54acb8STomasz Jeznach     /* index into device context entry page */
9630c54acb8STomasz Jeznach     addr |= (ctx->devid * dc_len) & ~TARGET_PAGE_MASK;
9640c54acb8STomasz Jeznach 
9650c54acb8STomasz Jeznach     memset(&dc, 0, sizeof(dc));
9660c54acb8STomasz Jeznach     if (dma_memory_read(s->target_as, addr, &dc, dc_len,
9670c54acb8STomasz Jeznach                         MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
9680c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_DDT_LOAD_FAULT;
9690c54acb8STomasz Jeznach     }
9700c54acb8STomasz Jeznach 
9710c54acb8STomasz Jeznach     /* Set translation context. */
9720c54acb8STomasz Jeznach     ctx->tc = le64_to_cpu(dc.tc);
9730c54acb8STomasz Jeznach     ctx->gatp = le64_to_cpu(dc.iohgatp);
9740c54acb8STomasz Jeznach     ctx->satp = le64_to_cpu(dc.fsc);
9750c54acb8STomasz Jeznach     ctx->ta = le64_to_cpu(dc.ta);
9760c54acb8STomasz Jeznach     ctx->msiptp = le64_to_cpu(dc.msiptp);
9770c54acb8STomasz Jeznach     ctx->msi_addr_mask = le64_to_cpu(dc.msi_addr_mask);
9780c54acb8STomasz Jeznach     ctx->msi_addr_pattern = le64_to_cpu(dc.msi_addr_pattern);
9790c54acb8STomasz Jeznach 
9800c54acb8STomasz Jeznach     if (!(ctx->tc & RISCV_IOMMU_DC_TC_V)) {
9810c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_DDT_INVALID;
9820c54acb8STomasz Jeznach     }
9830c54acb8STomasz Jeznach 
9840c54acb8STomasz Jeznach     if (!riscv_iommu_validate_device_ctx(s, ctx)) {
9850c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED;
9860c54acb8STomasz Jeznach     }
9870c54acb8STomasz Jeznach 
9880c54acb8STomasz Jeznach     /* FSC field checks */
9890c54acb8STomasz Jeznach     mode = get_field(ctx->satp, RISCV_IOMMU_DC_FSC_MODE);
9900c54acb8STomasz Jeznach     addr = PPN_PHYS(get_field(ctx->satp, RISCV_IOMMU_DC_FSC_PPN));
9910c54acb8STomasz Jeznach 
9920c54acb8STomasz Jeznach     if (!(ctx->tc & RISCV_IOMMU_DC_TC_PDTV)) {
9930c54acb8STomasz Jeznach         if (ctx->process_id != RISCV_IOMMU_NOPROCID) {
9940c54acb8STomasz Jeznach             /* PID is disabled */
9950c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_TTYPE_BLOCKED;
9960c54acb8STomasz Jeznach         }
9970c54acb8STomasz Jeznach         if (mode > RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57) {
9980c54acb8STomasz Jeznach             /* Invalid translation mode */
9990c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_DDT_INVALID;
10000c54acb8STomasz Jeznach         }
10010c54acb8STomasz Jeznach         return 0;
10020c54acb8STomasz Jeznach     }
10030c54acb8STomasz Jeznach 
10040c54acb8STomasz Jeznach     if (ctx->process_id == RISCV_IOMMU_NOPROCID) {
10050c54acb8STomasz Jeznach         if (!(ctx->tc & RISCV_IOMMU_DC_TC_DPE)) {
10060c54acb8STomasz Jeznach             /* No default process_id enabled, set BARE mode */
10070c54acb8STomasz Jeznach             ctx->satp = 0ULL;
10080c54acb8STomasz Jeznach             return 0;
10090c54acb8STomasz Jeznach         } else {
10100c54acb8STomasz Jeznach             /* Use default process_id #0 */
10110c54acb8STomasz Jeznach             ctx->process_id = 0;
10120c54acb8STomasz Jeznach         }
10130c54acb8STomasz Jeznach     }
10140c54acb8STomasz Jeznach 
10150c54acb8STomasz Jeznach     if (mode == RISCV_IOMMU_DC_FSC_MODE_BARE) {
10160c54acb8STomasz Jeznach         /* No S-Stage translation, done. */
10170c54acb8STomasz Jeznach         return 0;
10180c54acb8STomasz Jeznach     }
10190c54acb8STomasz Jeznach 
10200c54acb8STomasz Jeznach     /* FSC.TC.PDTV enabled */
10210c54acb8STomasz Jeznach     if (mode > RISCV_IOMMU_DC_FSC_PDTP_MODE_PD20) {
10220c54acb8STomasz Jeznach         /* Invalid PDTP.MODE */
10230c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_PDT_MISCONFIGURED;
10240c54acb8STomasz Jeznach     }
10250c54acb8STomasz Jeznach 
10260c54acb8STomasz Jeznach     for (depth = mode - RISCV_IOMMU_DC_FSC_PDTP_MODE_PD8; depth-- > 0; ) {
10270c54acb8STomasz Jeznach         /*
10280c54acb8STomasz Jeznach          * Select process id index bits based on process directory tree
10290c54acb8STomasz Jeznach          * level. See IOMMU Specification, 2.2. Process-Directory-Table.
10300c54acb8STomasz Jeznach          */
10310c54acb8STomasz Jeznach         const int split = depth * 9 + 8;
10320c54acb8STomasz Jeznach         addr |= ((ctx->process_id >> split) << 3) & ~TARGET_PAGE_MASK;
10330c54acb8STomasz Jeznach         if (dma_memory_read(s->target_as, addr, &de, sizeof(de),
10340c54acb8STomasz Jeznach                             MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
10350c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_PDT_LOAD_FAULT;
10360c54acb8STomasz Jeznach         }
10370c54acb8STomasz Jeznach         le64_to_cpus(&de);
10380c54acb8STomasz Jeznach         if (!(de & RISCV_IOMMU_PC_TA_V)) {
10390c54acb8STomasz Jeznach             return RISCV_IOMMU_FQ_CAUSE_PDT_INVALID;
10400c54acb8STomasz Jeznach         }
10410c54acb8STomasz Jeznach         addr = PPN_PHYS(get_field(de, RISCV_IOMMU_PC_FSC_PPN));
10420c54acb8STomasz Jeznach     }
10430c54acb8STomasz Jeznach 
10440c54acb8STomasz Jeznach     /* Leaf entry in PDT */
10450c54acb8STomasz Jeznach     addr |= (ctx->process_id << 4) & ~TARGET_PAGE_MASK;
10460c54acb8STomasz Jeznach     if (dma_memory_read(s->target_as, addr, &dc.ta, sizeof(uint64_t) * 2,
10470c54acb8STomasz Jeznach                         MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
10480c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_PDT_LOAD_FAULT;
10490c54acb8STomasz Jeznach     }
10500c54acb8STomasz Jeznach 
10510c54acb8STomasz Jeznach     /* Use FSC and TA from process directory entry. */
10520c54acb8STomasz Jeznach     ctx->ta = le64_to_cpu(dc.ta);
10530c54acb8STomasz Jeznach     ctx->satp = le64_to_cpu(dc.fsc);
10540c54acb8STomasz Jeznach 
10550c54acb8STomasz Jeznach     if (!(ctx->ta & RISCV_IOMMU_PC_TA_V)) {
10560c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_PDT_INVALID;
10570c54acb8STomasz Jeznach     }
10580c54acb8STomasz Jeznach 
10590c54acb8STomasz Jeznach     if (!riscv_iommu_validate_process_ctx(s, ctx)) {
10600c54acb8STomasz Jeznach         return RISCV_IOMMU_FQ_CAUSE_PDT_MISCONFIGURED;
10610c54acb8STomasz Jeznach     }
10620c54acb8STomasz Jeznach 
10630c54acb8STomasz Jeznach     return 0;
10640c54acb8STomasz Jeznach }
10650c54acb8STomasz Jeznach 
10660c54acb8STomasz Jeznach /* Translation Context cache support */
10670c54acb8STomasz Jeznach static gboolean riscv_iommu_ctx_equal(gconstpointer v1, gconstpointer v2)
10680c54acb8STomasz Jeznach {
10690c54acb8STomasz Jeznach     RISCVIOMMUContext *c1 = (RISCVIOMMUContext *) v1;
10700c54acb8STomasz Jeznach     RISCVIOMMUContext *c2 = (RISCVIOMMUContext *) v2;
10710c54acb8STomasz Jeznach     return c1->devid == c2->devid &&
10720c54acb8STomasz Jeznach            c1->process_id == c2->process_id;
10730c54acb8STomasz Jeznach }
10740c54acb8STomasz Jeznach 
10750c54acb8STomasz Jeznach static guint riscv_iommu_ctx_hash(gconstpointer v)
10760c54acb8STomasz Jeznach {
10770c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx = (RISCVIOMMUContext *) v;
10780c54acb8STomasz Jeznach     /*
10790c54acb8STomasz Jeznach      * Generate simple hash of (process_id, devid)
10800c54acb8STomasz Jeznach      * assuming 24-bit wide devid.
10810c54acb8STomasz Jeznach      */
10820c54acb8STomasz Jeznach     return (guint)(ctx->devid) + ((guint)(ctx->process_id) << 24);
10830c54acb8STomasz Jeznach }
10840c54acb8STomasz Jeznach 
10850c54acb8STomasz Jeznach static void riscv_iommu_ctx_inval_devid_procid(gpointer key, gpointer value,
10860c54acb8STomasz Jeznach                                                gpointer data)
10870c54acb8STomasz Jeznach {
10880c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx = (RISCVIOMMUContext *) value;
10890c54acb8STomasz Jeznach     RISCVIOMMUContext *arg = (RISCVIOMMUContext *) data;
10900c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_V &&
10910c54acb8STomasz Jeznach         ctx->devid == arg->devid &&
10920c54acb8STomasz Jeznach         ctx->process_id == arg->process_id) {
10930c54acb8STomasz Jeznach         ctx->tc &= ~RISCV_IOMMU_DC_TC_V;
10940c54acb8STomasz Jeznach     }
10950c54acb8STomasz Jeznach }
10960c54acb8STomasz Jeznach 
10970c54acb8STomasz Jeznach static void riscv_iommu_ctx_inval_devid(gpointer key, gpointer value,
10980c54acb8STomasz Jeznach                                         gpointer data)
10990c54acb8STomasz Jeznach {
11000c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx = (RISCVIOMMUContext *) value;
11010c54acb8STomasz Jeznach     RISCVIOMMUContext *arg = (RISCVIOMMUContext *) data;
11020c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_V &&
11030c54acb8STomasz Jeznach         ctx->devid == arg->devid) {
11040c54acb8STomasz Jeznach         ctx->tc &= ~RISCV_IOMMU_DC_TC_V;
11050c54acb8STomasz Jeznach     }
11060c54acb8STomasz Jeznach }
11070c54acb8STomasz Jeznach 
11080c54acb8STomasz Jeznach static void riscv_iommu_ctx_inval_all(gpointer key, gpointer value,
11090c54acb8STomasz Jeznach                                       gpointer data)
11100c54acb8STomasz Jeznach {
11110c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx = (RISCVIOMMUContext *) value;
11120c54acb8STomasz Jeznach     if (ctx->tc & RISCV_IOMMU_DC_TC_V) {
11130c54acb8STomasz Jeznach         ctx->tc &= ~RISCV_IOMMU_DC_TC_V;
11140c54acb8STomasz Jeznach     }
11150c54acb8STomasz Jeznach }
11160c54acb8STomasz Jeznach 
11170c54acb8STomasz Jeznach static void riscv_iommu_ctx_inval(RISCVIOMMUState *s, GHFunc func,
11180c54acb8STomasz Jeznach                                   uint32_t devid, uint32_t process_id)
11190c54acb8STomasz Jeznach {
11200c54acb8STomasz Jeznach     GHashTable *ctx_cache;
11210c54acb8STomasz Jeznach     RISCVIOMMUContext key = {
11220c54acb8STomasz Jeznach         .devid = devid,
11230c54acb8STomasz Jeznach         .process_id = process_id,
11240c54acb8STomasz Jeznach     };
11250c54acb8STomasz Jeznach     ctx_cache = g_hash_table_ref(s->ctx_cache);
11260c54acb8STomasz Jeznach     g_hash_table_foreach(ctx_cache, func, &key);
11270c54acb8STomasz Jeznach     g_hash_table_unref(ctx_cache);
11280c54acb8STomasz Jeznach }
11290c54acb8STomasz Jeznach 
11300c54acb8STomasz Jeznach /* Find or allocate translation context for a given {device_id, process_id} */
11310c54acb8STomasz Jeznach static RISCVIOMMUContext *riscv_iommu_ctx(RISCVIOMMUState *s,
11320c54acb8STomasz Jeznach                                           unsigned devid, unsigned process_id,
11330c54acb8STomasz Jeznach                                           void **ref)
11340c54acb8STomasz Jeznach {
11350c54acb8STomasz Jeznach     GHashTable *ctx_cache;
11360c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx;
11370c54acb8STomasz Jeznach     RISCVIOMMUContext key = {
11380c54acb8STomasz Jeznach         .devid = devid,
11390c54acb8STomasz Jeznach         .process_id = process_id,
11400c54acb8STomasz Jeznach     };
11410c54acb8STomasz Jeznach 
11420c54acb8STomasz Jeznach     ctx_cache = g_hash_table_ref(s->ctx_cache);
11430c54acb8STomasz Jeznach     ctx = g_hash_table_lookup(ctx_cache, &key);
11440c54acb8STomasz Jeznach 
11450c54acb8STomasz Jeznach     if (ctx && (ctx->tc & RISCV_IOMMU_DC_TC_V)) {
11460c54acb8STomasz Jeznach         *ref = ctx_cache;
11470c54acb8STomasz Jeznach         return ctx;
11480c54acb8STomasz Jeznach     }
11490c54acb8STomasz Jeznach 
11500c54acb8STomasz Jeznach     ctx = g_new0(RISCVIOMMUContext, 1);
11510c54acb8STomasz Jeznach     ctx->devid = devid;
11520c54acb8STomasz Jeznach     ctx->process_id = process_id;
11530c54acb8STomasz Jeznach 
11540c54acb8STomasz Jeznach     int fault = riscv_iommu_ctx_fetch(s, ctx);
11550c54acb8STomasz Jeznach     if (!fault) {
11560c54acb8STomasz Jeznach         if (g_hash_table_size(ctx_cache) >= LIMIT_CACHE_CTX) {
11570c54acb8STomasz Jeznach             g_hash_table_unref(ctx_cache);
11580c54acb8STomasz Jeznach             ctx_cache = g_hash_table_new_full(riscv_iommu_ctx_hash,
11590c54acb8STomasz Jeznach                                               riscv_iommu_ctx_equal,
11600c54acb8STomasz Jeznach                                               g_free, NULL);
11610c54acb8STomasz Jeznach             g_hash_table_ref(ctx_cache);
11620c54acb8STomasz Jeznach             g_hash_table_unref(qatomic_xchg(&s->ctx_cache, ctx_cache));
11630c54acb8STomasz Jeznach         }
11640c54acb8STomasz Jeznach         g_hash_table_add(ctx_cache, ctx);
11650c54acb8STomasz Jeznach         *ref = ctx_cache;
11660c54acb8STomasz Jeznach         return ctx;
11670c54acb8STomasz Jeznach     }
11680c54acb8STomasz Jeznach 
11690c54acb8STomasz Jeznach     g_hash_table_unref(ctx_cache);
11700c54acb8STomasz Jeznach     *ref = NULL;
11710c54acb8STomasz Jeznach 
11720c54acb8STomasz Jeznach     riscv_iommu_report_fault(s, ctx, RISCV_IOMMU_FQ_TTYPE_UADDR_RD,
11730c54acb8STomasz Jeznach                              fault, !!process_id, 0, 0);
11740c54acb8STomasz Jeznach 
11750c54acb8STomasz Jeznach     g_free(ctx);
11760c54acb8STomasz Jeznach     return NULL;
11770c54acb8STomasz Jeznach }
11780c54acb8STomasz Jeznach 
11790c54acb8STomasz Jeznach static void riscv_iommu_ctx_put(RISCVIOMMUState *s, void *ref)
11800c54acb8STomasz Jeznach {
11810c54acb8STomasz Jeznach     if (ref) {
11820c54acb8STomasz Jeznach         g_hash_table_unref((GHashTable *)ref);
11830c54acb8STomasz Jeznach     }
11840c54acb8STomasz Jeznach }
11850c54acb8STomasz Jeznach 
11860c54acb8STomasz Jeznach /* Find or allocate address space for a given device */
11870c54acb8STomasz Jeznach static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, uint32_t devid)
11880c54acb8STomasz Jeznach {
11890c54acb8STomasz Jeznach     RISCVIOMMUSpace *as;
11900c54acb8STomasz Jeznach 
11910c54acb8STomasz Jeznach     /* FIXME: PCIe bus remapping for attached endpoints. */
11920c54acb8STomasz Jeznach     devid |= s->bus << 8;
11930c54acb8STomasz Jeznach 
11940c54acb8STomasz Jeznach     QLIST_FOREACH(as, &s->spaces, list) {
11950c54acb8STomasz Jeznach         if (as->devid == devid) {
11960c54acb8STomasz Jeznach             break;
11970c54acb8STomasz Jeznach         }
11980c54acb8STomasz Jeznach     }
11990c54acb8STomasz Jeznach 
12000c54acb8STomasz Jeznach     if (as == NULL) {
12010c54acb8STomasz Jeznach         char name[64];
12020c54acb8STomasz Jeznach         as = g_new0(RISCVIOMMUSpace, 1);
12030c54acb8STomasz Jeznach 
12040c54acb8STomasz Jeznach         as->iommu = s;
12050c54acb8STomasz Jeznach         as->devid = devid;
12060c54acb8STomasz Jeznach 
12070c54acb8STomasz Jeznach         snprintf(name, sizeof(name), "riscv-iommu-%04x:%02x.%d-iova",
12080c54acb8STomasz Jeznach             PCI_BUS_NUM(as->devid), PCI_SLOT(as->devid), PCI_FUNC(as->devid));
12090c54acb8STomasz Jeznach 
12100c54acb8STomasz Jeznach         /* IOVA address space, untranslated addresses */
12110c54acb8STomasz Jeznach         memory_region_init_iommu(&as->iova_mr, sizeof(as->iova_mr),
12120c54acb8STomasz Jeznach             TYPE_RISCV_IOMMU_MEMORY_REGION,
12130c54acb8STomasz Jeznach             OBJECT(as), "riscv_iommu", UINT64_MAX);
12140c54acb8STomasz Jeznach         address_space_init(&as->iova_as, MEMORY_REGION(&as->iova_mr), name);
12150c54acb8STomasz Jeznach 
12160c54acb8STomasz Jeznach         QLIST_INSERT_HEAD(&s->spaces, as, list);
12170c54acb8STomasz Jeznach 
12180c54acb8STomasz Jeznach         trace_riscv_iommu_new(s->parent_obj.id, PCI_BUS_NUM(as->devid),
12190c54acb8STomasz Jeznach                 PCI_SLOT(as->devid), PCI_FUNC(as->devid));
12200c54acb8STomasz Jeznach     }
12210c54acb8STomasz Jeznach     return &as->iova_as;
12220c54acb8STomasz Jeznach }
12230c54acb8STomasz Jeznach 
12249d085a1cSTomasz Jeznach /* Translation Object cache support */
12259d085a1cSTomasz Jeznach static gboolean riscv_iommu_iot_equal(gconstpointer v1, gconstpointer v2)
12260c54acb8STomasz Jeznach {
12279d085a1cSTomasz Jeznach     RISCVIOMMUEntry *t1 = (RISCVIOMMUEntry *) v1;
12289d085a1cSTomasz Jeznach     RISCVIOMMUEntry *t2 = (RISCVIOMMUEntry *) v2;
12299d085a1cSTomasz Jeznach     return t1->gscid == t2->gscid && t1->pscid == t2->pscid &&
12309d085a1cSTomasz Jeznach            t1->iova == t2->iova;
12319d085a1cSTomasz Jeznach }
12329d085a1cSTomasz Jeznach 
12339d085a1cSTomasz Jeznach static guint riscv_iommu_iot_hash(gconstpointer v)
12349d085a1cSTomasz Jeznach {
12359d085a1cSTomasz Jeznach     RISCVIOMMUEntry *t = (RISCVIOMMUEntry *) v;
12369d085a1cSTomasz Jeznach     return (guint)t->iova;
12379d085a1cSTomasz Jeznach }
12389d085a1cSTomasz Jeznach 
12399d085a1cSTomasz Jeznach /* GV: 1 PSCV: 1 AV: 1 */
12409d085a1cSTomasz Jeznach static void riscv_iommu_iot_inval_pscid_iova(gpointer key, gpointer value,
12419d085a1cSTomasz Jeznach                                              gpointer data)
12429d085a1cSTomasz Jeznach {
12439d085a1cSTomasz Jeznach     RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value;
12449d085a1cSTomasz Jeznach     RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data;
12459d085a1cSTomasz Jeznach     if (iot->gscid == arg->gscid &&
12469d085a1cSTomasz Jeznach         iot->pscid == arg->pscid &&
12479d085a1cSTomasz Jeznach         iot->iova == arg->iova) {
12489d085a1cSTomasz Jeznach         iot->perm = IOMMU_NONE;
12499d085a1cSTomasz Jeznach     }
12509d085a1cSTomasz Jeznach }
12519d085a1cSTomasz Jeznach 
12529d085a1cSTomasz Jeznach /* GV: 1 PSCV: 1 AV: 0 */
12539d085a1cSTomasz Jeznach static void riscv_iommu_iot_inval_pscid(gpointer key, gpointer value,
12549d085a1cSTomasz Jeznach                                         gpointer data)
12559d085a1cSTomasz Jeznach {
12569d085a1cSTomasz Jeznach     RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value;
12579d085a1cSTomasz Jeznach     RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data;
12589d085a1cSTomasz Jeznach     if (iot->gscid == arg->gscid &&
12599d085a1cSTomasz Jeznach         iot->pscid == arg->pscid) {
12609d085a1cSTomasz Jeznach         iot->perm = IOMMU_NONE;
12619d085a1cSTomasz Jeznach     }
12629d085a1cSTomasz Jeznach }
12639d085a1cSTomasz Jeznach 
12649d085a1cSTomasz Jeznach /* GV: 1 GVMA: 1 */
12659d085a1cSTomasz Jeznach static void riscv_iommu_iot_inval_gscid_gpa(gpointer key, gpointer value,
12669d085a1cSTomasz Jeznach                                             gpointer data)
12679d085a1cSTomasz Jeznach {
12689d085a1cSTomasz Jeznach     RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value;
12699d085a1cSTomasz Jeznach     RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data;
12709d085a1cSTomasz Jeznach     if (iot->gscid == arg->gscid) {
12719d085a1cSTomasz Jeznach         /* simplified cache, no GPA matching */
12729d085a1cSTomasz Jeznach         iot->perm = IOMMU_NONE;
12739d085a1cSTomasz Jeznach     }
12749d085a1cSTomasz Jeznach }
12759d085a1cSTomasz Jeznach 
12769d085a1cSTomasz Jeznach /* GV: 1 GVMA: 0 */
12779d085a1cSTomasz Jeznach static void riscv_iommu_iot_inval_gscid(gpointer key, gpointer value,
12789d085a1cSTomasz Jeznach                                         gpointer data)
12799d085a1cSTomasz Jeznach {
12809d085a1cSTomasz Jeznach     RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value;
12819d085a1cSTomasz Jeznach     RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data;
12829d085a1cSTomasz Jeznach     if (iot->gscid == arg->gscid) {
12839d085a1cSTomasz Jeznach         iot->perm = IOMMU_NONE;
12849d085a1cSTomasz Jeznach     }
12859d085a1cSTomasz Jeznach }
12869d085a1cSTomasz Jeznach 
12879d085a1cSTomasz Jeznach /* GV: 0 */
12889d085a1cSTomasz Jeznach static void riscv_iommu_iot_inval_all(gpointer key, gpointer value,
12899d085a1cSTomasz Jeznach                                       gpointer data)
12909d085a1cSTomasz Jeznach {
12919d085a1cSTomasz Jeznach     RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value;
12929d085a1cSTomasz Jeznach     iot->perm = IOMMU_NONE;
12939d085a1cSTomasz Jeznach }
12949d085a1cSTomasz Jeznach 
12959d085a1cSTomasz Jeznach /* caller should keep ref-count for iot_cache object */
12969d085a1cSTomasz Jeznach static RISCVIOMMUEntry *riscv_iommu_iot_lookup(RISCVIOMMUContext *ctx,
12979d085a1cSTomasz Jeznach     GHashTable *iot_cache, hwaddr iova)
12989d085a1cSTomasz Jeznach {
12999d085a1cSTomasz Jeznach     RISCVIOMMUEntry key = {
13009d085a1cSTomasz Jeznach         .gscid = get_field(ctx->gatp, RISCV_IOMMU_DC_IOHGATP_GSCID),
13019d085a1cSTomasz Jeznach         .pscid = get_field(ctx->ta, RISCV_IOMMU_DC_TA_PSCID),
13029d085a1cSTomasz Jeznach         .iova  = PPN_DOWN(iova),
13039d085a1cSTomasz Jeznach     };
13049d085a1cSTomasz Jeznach     return g_hash_table_lookup(iot_cache, &key);
13059d085a1cSTomasz Jeznach }
13069d085a1cSTomasz Jeznach 
13079d085a1cSTomasz Jeznach /* caller should keep ref-count for iot_cache object */
13089d085a1cSTomasz Jeznach static void riscv_iommu_iot_update(RISCVIOMMUState *s,
13099d085a1cSTomasz Jeznach     GHashTable *iot_cache, RISCVIOMMUEntry *iot)
13109d085a1cSTomasz Jeznach {
13119d085a1cSTomasz Jeznach     if (!s->iot_limit) {
13129d085a1cSTomasz Jeznach         return;
13139d085a1cSTomasz Jeznach     }
13149d085a1cSTomasz Jeznach 
13159d085a1cSTomasz Jeznach     if (g_hash_table_size(s->iot_cache) >= s->iot_limit) {
13169d085a1cSTomasz Jeznach         iot_cache = g_hash_table_new_full(riscv_iommu_iot_hash,
13179d085a1cSTomasz Jeznach                                           riscv_iommu_iot_equal,
13189d085a1cSTomasz Jeznach                                           g_free, NULL);
13199d085a1cSTomasz Jeznach         g_hash_table_unref(qatomic_xchg(&s->iot_cache, iot_cache));
13209d085a1cSTomasz Jeznach     }
13219d085a1cSTomasz Jeznach     g_hash_table_add(iot_cache, iot);
13229d085a1cSTomasz Jeznach }
13239d085a1cSTomasz Jeznach 
13249d085a1cSTomasz Jeznach static void riscv_iommu_iot_inval(RISCVIOMMUState *s, GHFunc func,
13259d085a1cSTomasz Jeznach     uint32_t gscid, uint32_t pscid, hwaddr iova)
13269d085a1cSTomasz Jeznach {
13279d085a1cSTomasz Jeznach     GHashTable *iot_cache;
13289d085a1cSTomasz Jeznach     RISCVIOMMUEntry key = {
13299d085a1cSTomasz Jeznach         .gscid = gscid,
13309d085a1cSTomasz Jeznach         .pscid = pscid,
13319d085a1cSTomasz Jeznach         .iova  = PPN_DOWN(iova),
13329d085a1cSTomasz Jeznach     };
13339d085a1cSTomasz Jeznach 
13349d085a1cSTomasz Jeznach     iot_cache = g_hash_table_ref(s->iot_cache);
13359d085a1cSTomasz Jeznach     g_hash_table_foreach(iot_cache, func, &key);
13369d085a1cSTomasz Jeznach     g_hash_table_unref(iot_cache);
13379d085a1cSTomasz Jeznach }
13389d085a1cSTomasz Jeznach 
13399d085a1cSTomasz Jeznach static int riscv_iommu_translate(RISCVIOMMUState *s, RISCVIOMMUContext *ctx,
13409d085a1cSTomasz Jeznach     IOMMUTLBEntry *iotlb, bool enable_cache)
13419d085a1cSTomasz Jeznach {
13429d085a1cSTomasz Jeznach     RISCVIOMMUEntry *iot;
13439d085a1cSTomasz Jeznach     IOMMUAccessFlags perm;
13440c54acb8STomasz Jeznach     bool enable_pid;
13450c54acb8STomasz Jeznach     bool enable_pri;
13469d085a1cSTomasz Jeznach     GHashTable *iot_cache;
13470c54acb8STomasz Jeznach     int fault;
13480c54acb8STomasz Jeznach 
13499d085a1cSTomasz Jeznach     iot_cache = g_hash_table_ref(s->iot_cache);
13500c54acb8STomasz Jeznach     /*
13510c54acb8STomasz Jeznach      * TC[32] is reserved for custom extensions, used here to temporarily
13520c54acb8STomasz Jeznach      * enable automatic page-request generation for ATS queries.
13530c54acb8STomasz Jeznach      */
13540c54acb8STomasz Jeznach     enable_pri = (iotlb->perm == IOMMU_NONE) && (ctx->tc & BIT_ULL(32));
13550c54acb8STomasz Jeznach     enable_pid = (ctx->tc & RISCV_IOMMU_DC_TC_PDTV);
13560c54acb8STomasz Jeznach 
135769a9ae48STomasz Jeznach     /* Check for ATS request. */
135869a9ae48STomasz Jeznach     if (iotlb->perm == IOMMU_NONE) {
135969a9ae48STomasz Jeznach         /* Check if ATS is disabled. */
136069a9ae48STomasz Jeznach         if (!(ctx->tc & RISCV_IOMMU_DC_TC_EN_ATS)) {
136169a9ae48STomasz Jeznach             enable_pri = false;
136269a9ae48STomasz Jeznach             fault = RISCV_IOMMU_FQ_CAUSE_TTYPE_BLOCKED;
136369a9ae48STomasz Jeznach             goto done;
136469a9ae48STomasz Jeznach         }
136569a9ae48STomasz Jeznach     }
136669a9ae48STomasz Jeznach 
13679d085a1cSTomasz Jeznach     iot = riscv_iommu_iot_lookup(ctx, iot_cache, iotlb->iova);
13689d085a1cSTomasz Jeznach     perm = iot ? iot->perm : IOMMU_NONE;
13699d085a1cSTomasz Jeznach     if (perm != IOMMU_NONE) {
13709d085a1cSTomasz Jeznach         iotlb->translated_addr = PPN_PHYS(iot->phys);
13719d085a1cSTomasz Jeznach         iotlb->addr_mask = ~TARGET_PAGE_MASK;
13729d085a1cSTomasz Jeznach         iotlb->perm = perm;
13739d085a1cSTomasz Jeznach         fault = 0;
13749d085a1cSTomasz Jeznach         goto done;
13759d085a1cSTomasz Jeznach     }
13769d085a1cSTomasz Jeznach 
13770c54acb8STomasz Jeznach     /* Translate using device directory / page table information. */
13780c54acb8STomasz Jeznach     fault = riscv_iommu_spa_fetch(s, ctx, iotlb);
13790c54acb8STomasz Jeznach 
13809d085a1cSTomasz Jeznach     if (!fault && iotlb->target_as == &s->trap_as) {
13819d085a1cSTomasz Jeznach         /* Do not cache trapped MSI translations */
13829d085a1cSTomasz Jeznach         goto done;
13839d085a1cSTomasz Jeznach     }
13849d085a1cSTomasz Jeznach 
13859d085a1cSTomasz Jeznach     /*
13869d085a1cSTomasz Jeznach      * We made an implementation choice to not cache identity-mapped
13879d085a1cSTomasz Jeznach      * translations, as allowed by the specification, to avoid
13889d085a1cSTomasz Jeznach      * translation cache evictions for other devices sharing the
13899d085a1cSTomasz Jeznach      * IOMMU hardware model.
13909d085a1cSTomasz Jeznach      */
13919d085a1cSTomasz Jeznach     if (!fault && iotlb->translated_addr != iotlb->iova && enable_cache) {
13929d085a1cSTomasz Jeznach         iot = g_new0(RISCVIOMMUEntry, 1);
13939d085a1cSTomasz Jeznach         iot->iova = PPN_DOWN(iotlb->iova);
13949d085a1cSTomasz Jeznach         iot->phys = PPN_DOWN(iotlb->translated_addr);
13959d085a1cSTomasz Jeznach         iot->gscid = get_field(ctx->gatp, RISCV_IOMMU_DC_IOHGATP_GSCID);
13969d085a1cSTomasz Jeznach         iot->pscid = get_field(ctx->ta, RISCV_IOMMU_DC_TA_PSCID);
13979d085a1cSTomasz Jeznach         iot->perm = iotlb->perm;
13989d085a1cSTomasz Jeznach         riscv_iommu_iot_update(s, iot_cache, iot);
13999d085a1cSTomasz Jeznach     }
14009d085a1cSTomasz Jeznach 
14019d085a1cSTomasz Jeznach done:
14029d085a1cSTomasz Jeznach     g_hash_table_unref(iot_cache);
14039d085a1cSTomasz Jeznach 
14040c54acb8STomasz Jeznach     if (enable_pri && fault) {
14050c54acb8STomasz Jeznach         struct riscv_iommu_pq_record pr = {0};
14060c54acb8STomasz Jeznach         if (enable_pid) {
14070c54acb8STomasz Jeznach             pr.hdr = set_field(RISCV_IOMMU_PREQ_HDR_PV,
14080c54acb8STomasz Jeznach                                RISCV_IOMMU_PREQ_HDR_PID, ctx->process_id);
14090c54acb8STomasz Jeznach         }
14100c54acb8STomasz Jeznach         pr.hdr = set_field(pr.hdr, RISCV_IOMMU_PREQ_HDR_DID, ctx->devid);
14110c54acb8STomasz Jeznach         pr.payload = (iotlb->iova & TARGET_PAGE_MASK) |
14120c54acb8STomasz Jeznach                      RISCV_IOMMU_PREQ_PAYLOAD_M;
14130c54acb8STomasz Jeznach         riscv_iommu_pri(s, &pr);
14140c54acb8STomasz Jeznach         return fault;
14150c54acb8STomasz Jeznach     }
14160c54acb8STomasz Jeznach 
14170c54acb8STomasz Jeznach     if (fault) {
141869a9ae48STomasz Jeznach         unsigned ttype = RISCV_IOMMU_FQ_TTYPE_PCIE_ATS_REQ;
14190c54acb8STomasz Jeznach 
14200c54acb8STomasz Jeznach         if (iotlb->perm & IOMMU_RW) {
14210c54acb8STomasz Jeznach             ttype = RISCV_IOMMU_FQ_TTYPE_UADDR_WR;
142269a9ae48STomasz Jeznach         } else if (iotlb->perm & IOMMU_RO) {
14230c54acb8STomasz Jeznach             ttype = RISCV_IOMMU_FQ_TTYPE_UADDR_RD;
14240c54acb8STomasz Jeznach         }
14250c54acb8STomasz Jeznach 
14260c54acb8STomasz Jeznach         riscv_iommu_report_fault(s, ctx, ttype, fault, enable_pid,
14270c54acb8STomasz Jeznach                                  iotlb->iova, iotlb->translated_addr);
14280c54acb8STomasz Jeznach         return fault;
14290c54acb8STomasz Jeznach     }
14300c54acb8STomasz Jeznach 
14310c54acb8STomasz Jeznach     return 0;
14320c54acb8STomasz Jeznach }
14330c54acb8STomasz Jeznach 
14340c54acb8STomasz Jeznach /* IOMMU Command Interface */
14350c54acb8STomasz Jeznach static MemTxResult riscv_iommu_iofence(RISCVIOMMUState *s, bool notify,
14360c54acb8STomasz Jeznach     uint64_t addr, uint32_t data)
14370c54acb8STomasz Jeznach {
14380c54acb8STomasz Jeznach     /*
14390c54acb8STomasz Jeznach      * ATS processing in this implementation of the IOMMU is synchronous,
14400c54acb8STomasz Jeznach      * no need to wait for completions here.
14410c54acb8STomasz Jeznach      */
14420c54acb8STomasz Jeznach     if (!notify) {
14430c54acb8STomasz Jeznach         return MEMTX_OK;
14440c54acb8STomasz Jeznach     }
14450c54acb8STomasz Jeznach 
14460c54acb8STomasz Jeznach     return dma_memory_write(s->target_as, addr, &data, sizeof(data),
14470c54acb8STomasz Jeznach         MEMTXATTRS_UNSPECIFIED);
14480c54acb8STomasz Jeznach }
14490c54acb8STomasz Jeznach 
145069a9ae48STomasz Jeznach static void riscv_iommu_ats(RISCVIOMMUState *s,
145169a9ae48STomasz Jeznach     struct riscv_iommu_command *cmd, IOMMUNotifierFlag flag,
145269a9ae48STomasz Jeznach     IOMMUAccessFlags perm,
145369a9ae48STomasz Jeznach     void (*trace_fn)(const char *id))
145469a9ae48STomasz Jeznach {
145569a9ae48STomasz Jeznach     RISCVIOMMUSpace *as = NULL;
145669a9ae48STomasz Jeznach     IOMMUNotifier *n;
145769a9ae48STomasz Jeznach     IOMMUTLBEvent event;
145869a9ae48STomasz Jeznach     uint32_t pid;
145969a9ae48STomasz Jeznach     uint32_t devid;
146069a9ae48STomasz Jeznach     const bool pv = cmd->dword0 & RISCV_IOMMU_CMD_ATS_PV;
146169a9ae48STomasz Jeznach 
146269a9ae48STomasz Jeznach     if (cmd->dword0 & RISCV_IOMMU_CMD_ATS_DSV) {
146369a9ae48STomasz Jeznach         /* Use device segment and requester id */
146469a9ae48STomasz Jeznach         devid = get_field(cmd->dword0,
146569a9ae48STomasz Jeznach             RISCV_IOMMU_CMD_ATS_DSEG | RISCV_IOMMU_CMD_ATS_RID);
146669a9ae48STomasz Jeznach     } else {
146769a9ae48STomasz Jeznach         devid = get_field(cmd->dword0, RISCV_IOMMU_CMD_ATS_RID);
146869a9ae48STomasz Jeznach     }
146969a9ae48STomasz Jeznach 
147069a9ae48STomasz Jeznach     pid = get_field(cmd->dword0, RISCV_IOMMU_CMD_ATS_PID);
147169a9ae48STomasz Jeznach 
147269a9ae48STomasz Jeznach     QLIST_FOREACH(as, &s->spaces, list) {
147369a9ae48STomasz Jeznach         if (as->devid == devid) {
147469a9ae48STomasz Jeznach             break;
147569a9ae48STomasz Jeznach         }
147669a9ae48STomasz Jeznach     }
147769a9ae48STomasz Jeznach 
147869a9ae48STomasz Jeznach     if (!as || !as->notifier) {
147969a9ae48STomasz Jeznach         return;
148069a9ae48STomasz Jeznach     }
148169a9ae48STomasz Jeznach 
148269a9ae48STomasz Jeznach     event.type = flag;
148369a9ae48STomasz Jeznach     event.entry.perm = perm;
148469a9ae48STomasz Jeznach     event.entry.target_as = s->target_as;
148569a9ae48STomasz Jeznach 
148669a9ae48STomasz Jeznach     IOMMU_NOTIFIER_FOREACH(n, &as->iova_mr) {
148769a9ae48STomasz Jeznach         if (!pv || n->iommu_idx == pid) {
148869a9ae48STomasz Jeznach             event.entry.iova = n->start;
148969a9ae48STomasz Jeznach             event.entry.addr_mask = n->end - n->start;
149069a9ae48STomasz Jeznach             trace_fn(as->iova_mr.parent_obj.name);
149169a9ae48STomasz Jeznach             memory_region_notify_iommu_one(n, &event);
149269a9ae48STomasz Jeznach         }
149369a9ae48STomasz Jeznach     }
149469a9ae48STomasz Jeznach }
149569a9ae48STomasz Jeznach 
149669a9ae48STomasz Jeznach static void riscv_iommu_ats_inval(RISCVIOMMUState *s,
149769a9ae48STomasz Jeznach     struct riscv_iommu_command *cmd)
149869a9ae48STomasz Jeznach {
149969a9ae48STomasz Jeznach     return riscv_iommu_ats(s, cmd, IOMMU_NOTIFIER_DEVIOTLB_UNMAP, IOMMU_NONE,
150069a9ae48STomasz Jeznach                            trace_riscv_iommu_ats_inval);
150169a9ae48STomasz Jeznach }
150269a9ae48STomasz Jeznach 
150369a9ae48STomasz Jeznach static void riscv_iommu_ats_prgr(RISCVIOMMUState *s,
150469a9ae48STomasz Jeznach     struct riscv_iommu_command *cmd)
150569a9ae48STomasz Jeznach {
150669a9ae48STomasz Jeznach     unsigned resp_code = get_field(cmd->dword1,
150769a9ae48STomasz Jeznach                                    RISCV_IOMMU_CMD_ATS_PRGR_RESP_CODE);
150869a9ae48STomasz Jeznach 
150969a9ae48STomasz Jeznach     /* Using the access flag to carry response code information */
151069a9ae48STomasz Jeznach     IOMMUAccessFlags perm = resp_code ? IOMMU_NONE : IOMMU_RW;
151169a9ae48STomasz Jeznach     return riscv_iommu_ats(s, cmd, IOMMU_NOTIFIER_MAP, perm,
151269a9ae48STomasz Jeznach                            trace_riscv_iommu_ats_prgr);
151369a9ae48STomasz Jeznach }
151469a9ae48STomasz Jeznach 
15150c54acb8STomasz Jeznach static void riscv_iommu_process_ddtp(RISCVIOMMUState *s)
15160c54acb8STomasz Jeznach {
15170c54acb8STomasz Jeznach     uint64_t old_ddtp = s->ddtp;
15180c54acb8STomasz Jeznach     uint64_t new_ddtp = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_DDTP);
15190c54acb8STomasz Jeznach     unsigned new_mode = get_field(new_ddtp, RISCV_IOMMU_DDTP_MODE);
15200c54acb8STomasz Jeznach     unsigned old_mode = get_field(old_ddtp, RISCV_IOMMU_DDTP_MODE);
15210c54acb8STomasz Jeznach     bool ok = false;
15220c54acb8STomasz Jeznach 
15230c54acb8STomasz Jeznach     /*
15240c54acb8STomasz Jeznach      * Check for allowed DDTP.MODE transitions:
15250c54acb8STomasz Jeznach      * {OFF, BARE}        -> {OFF, BARE, 1LVL, 2LVL, 3LVL}
15260c54acb8STomasz Jeznach      * {1LVL, 2LVL, 3LVL} -> {OFF, BARE}
15270c54acb8STomasz Jeznach      */
15280c54acb8STomasz Jeznach     if (new_mode == old_mode ||
15290c54acb8STomasz Jeznach         new_mode == RISCV_IOMMU_DDTP_MODE_OFF ||
15300c54acb8STomasz Jeznach         new_mode == RISCV_IOMMU_DDTP_MODE_BARE) {
15310c54acb8STomasz Jeznach         ok = true;
15320c54acb8STomasz Jeznach     } else if (new_mode == RISCV_IOMMU_DDTP_MODE_1LVL ||
15330c54acb8STomasz Jeznach                new_mode == RISCV_IOMMU_DDTP_MODE_2LVL ||
15340c54acb8STomasz Jeznach                new_mode == RISCV_IOMMU_DDTP_MODE_3LVL) {
15350c54acb8STomasz Jeznach         ok = old_mode == RISCV_IOMMU_DDTP_MODE_OFF ||
15360c54acb8STomasz Jeznach              old_mode == RISCV_IOMMU_DDTP_MODE_BARE;
15370c54acb8STomasz Jeznach     }
15380c54acb8STomasz Jeznach 
15390c54acb8STomasz Jeznach     if (ok) {
15400c54acb8STomasz Jeznach         /* clear reserved and busy bits, report back sanitized version */
15410c54acb8STomasz Jeznach         new_ddtp = set_field(new_ddtp & RISCV_IOMMU_DDTP_PPN,
15420c54acb8STomasz Jeznach                              RISCV_IOMMU_DDTP_MODE, new_mode);
15430c54acb8STomasz Jeznach     } else {
15440c54acb8STomasz Jeznach         new_ddtp = old_ddtp;
15450c54acb8STomasz Jeznach     }
15460c54acb8STomasz Jeznach     s->ddtp = new_ddtp;
15470c54acb8STomasz Jeznach 
15480c54acb8STomasz Jeznach     riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_DDTP, new_ddtp);
15490c54acb8STomasz Jeznach }
15500c54acb8STomasz Jeznach 
15510c54acb8STomasz Jeznach /* Command function and opcode field. */
15520c54acb8STomasz Jeznach #define RISCV_IOMMU_CMD(func, op) (((func) << 7) | (op))
15530c54acb8STomasz Jeznach 
15540c54acb8STomasz Jeznach static void riscv_iommu_process_cq_tail(RISCVIOMMUState *s)
15550c54acb8STomasz Jeznach {
15560c54acb8STomasz Jeznach     struct riscv_iommu_command cmd;
15570c54acb8STomasz Jeznach     MemTxResult res;
15580c54acb8STomasz Jeznach     dma_addr_t addr;
15590c54acb8STomasz Jeznach     uint32_t tail, head, ctrl;
15600c54acb8STomasz Jeznach     uint64_t cmd_opcode;
15610c54acb8STomasz Jeznach     GHFunc func;
15620c54acb8STomasz Jeznach 
15630c54acb8STomasz Jeznach     ctrl = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQCSR);
15640c54acb8STomasz Jeznach     tail = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQT) & s->cq_mask;
15650c54acb8STomasz Jeznach     head = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQH) & s->cq_mask;
15660c54acb8STomasz Jeznach 
15670c54acb8STomasz Jeznach     /* Check for pending error or queue processing disabled */
15680c54acb8STomasz Jeznach     if (!(ctrl & RISCV_IOMMU_CQCSR_CQON) ||
15690c54acb8STomasz Jeznach         !!(ctrl & (RISCV_IOMMU_CQCSR_CMD_ILL | RISCV_IOMMU_CQCSR_CQMF))) {
15700c54acb8STomasz Jeznach         return;
15710c54acb8STomasz Jeznach     }
15720c54acb8STomasz Jeznach 
15730c54acb8STomasz Jeznach     while (tail != head) {
15740c54acb8STomasz Jeznach         addr = s->cq_addr  + head * sizeof(cmd);
15750c54acb8STomasz Jeznach         res = dma_memory_read(s->target_as, addr, &cmd, sizeof(cmd),
15760c54acb8STomasz Jeznach                               MEMTXATTRS_UNSPECIFIED);
15770c54acb8STomasz Jeznach 
15780c54acb8STomasz Jeznach         if (res != MEMTX_OK) {
15790c54acb8STomasz Jeznach             riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR,
15800c54acb8STomasz Jeznach                                   RISCV_IOMMU_CQCSR_CQMF, 0);
15810c54acb8STomasz Jeznach             goto fault;
15820c54acb8STomasz Jeznach         }
15830c54acb8STomasz Jeznach 
15840c54acb8STomasz Jeznach         trace_riscv_iommu_cmd(s->parent_obj.id, cmd.dword0, cmd.dword1);
15850c54acb8STomasz Jeznach 
15860c54acb8STomasz Jeznach         cmd_opcode = get_field(cmd.dword0,
15870c54acb8STomasz Jeznach                                RISCV_IOMMU_CMD_OPCODE | RISCV_IOMMU_CMD_FUNC);
15880c54acb8STomasz Jeznach 
15890c54acb8STomasz Jeznach         switch (cmd_opcode) {
15900c54acb8STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IOFENCE_FUNC_C,
15910c54acb8STomasz Jeznach                              RISCV_IOMMU_CMD_IOFENCE_OPCODE):
15920c54acb8STomasz Jeznach             res = riscv_iommu_iofence(s,
15930c54acb8STomasz Jeznach                 cmd.dword0 & RISCV_IOMMU_CMD_IOFENCE_AV, cmd.dword1 << 2,
15940c54acb8STomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IOFENCE_DATA));
15950c54acb8STomasz Jeznach 
15960c54acb8STomasz Jeznach             if (res != MEMTX_OK) {
15970c54acb8STomasz Jeznach                 riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR,
15980c54acb8STomasz Jeznach                                       RISCV_IOMMU_CQCSR_CQMF, 0);
15990c54acb8STomasz Jeznach                 goto fault;
16000c54acb8STomasz Jeznach             }
16010c54acb8STomasz Jeznach             break;
16020c54acb8STomasz Jeznach 
16030c54acb8STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IOTINVAL_FUNC_GVMA,
16040c54acb8STomasz Jeznach                              RISCV_IOMMU_CMD_IOTINVAL_OPCODE):
16050c54acb8STomasz Jeznach             if (cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_PSCV) {
16060c54acb8STomasz Jeznach                 /* illegal command arguments IOTINVAL.GVMA & PSCV == 1 */
16070c54acb8STomasz Jeznach                 goto cmd_ill;
16089d085a1cSTomasz Jeznach             } else if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_GV)) {
16099d085a1cSTomasz Jeznach                 /* invalidate all cache mappings */
16109d085a1cSTomasz Jeznach                 func = riscv_iommu_iot_inval_all;
16119d085a1cSTomasz Jeznach             } else if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_AV)) {
16129d085a1cSTomasz Jeznach                 /* invalidate cache matching GSCID */
16139d085a1cSTomasz Jeznach                 func = riscv_iommu_iot_inval_gscid;
16149d085a1cSTomasz Jeznach             } else {
16159d085a1cSTomasz Jeznach                 /* invalidate cache matching GSCID and ADDR (GPA) */
16169d085a1cSTomasz Jeznach                 func = riscv_iommu_iot_inval_gscid_gpa;
16170c54acb8STomasz Jeznach             }
16189d085a1cSTomasz Jeznach             riscv_iommu_iot_inval(s, func,
16199d085a1cSTomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IOTINVAL_GSCID), 0,
16209d085a1cSTomasz Jeznach                 cmd.dword1 << 2 & TARGET_PAGE_MASK);
16210c54acb8STomasz Jeznach             break;
16220c54acb8STomasz Jeznach 
16230c54acb8STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IOTINVAL_FUNC_VMA,
16240c54acb8STomasz Jeznach                              RISCV_IOMMU_CMD_IOTINVAL_OPCODE):
16259d085a1cSTomasz Jeznach             if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_GV)) {
16269d085a1cSTomasz Jeznach                 /* invalidate all cache mappings, simplified model */
16279d085a1cSTomasz Jeznach                 func = riscv_iommu_iot_inval_all;
16289d085a1cSTomasz Jeznach             } else if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_PSCV)) {
16299d085a1cSTomasz Jeznach                 /* invalidate cache matching GSCID, simplified model */
16309d085a1cSTomasz Jeznach                 func = riscv_iommu_iot_inval_gscid;
16319d085a1cSTomasz Jeznach             } else if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_AV)) {
16329d085a1cSTomasz Jeznach                 /* invalidate cache matching GSCID and PSCID */
16339d085a1cSTomasz Jeznach                 func = riscv_iommu_iot_inval_pscid;
16349d085a1cSTomasz Jeznach             } else {
16359d085a1cSTomasz Jeznach                 /* invalidate cache matching GSCID and PSCID and ADDR (IOVA) */
16369d085a1cSTomasz Jeznach                 func = riscv_iommu_iot_inval_pscid_iova;
16379d085a1cSTomasz Jeznach             }
16389d085a1cSTomasz Jeznach             riscv_iommu_iot_inval(s, func,
16399d085a1cSTomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IOTINVAL_GSCID),
16409d085a1cSTomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IOTINVAL_PSCID),
16419d085a1cSTomasz Jeznach                 cmd.dword1 << 2 & TARGET_PAGE_MASK);
16420c54acb8STomasz Jeznach             break;
16430c54acb8STomasz Jeznach 
16440c54acb8STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IODIR_FUNC_INVAL_DDT,
16450c54acb8STomasz Jeznach                              RISCV_IOMMU_CMD_IODIR_OPCODE):
16460c54acb8STomasz Jeznach             if (!(cmd.dword0 & RISCV_IOMMU_CMD_IODIR_DV)) {
16470c54acb8STomasz Jeznach                 /* invalidate all device context cache mappings */
16480c54acb8STomasz Jeznach                 func = riscv_iommu_ctx_inval_all;
16490c54acb8STomasz Jeznach             } else {
16500c54acb8STomasz Jeznach                 /* invalidate all device context matching DID */
16510c54acb8STomasz Jeznach                 func = riscv_iommu_ctx_inval_devid;
16520c54acb8STomasz Jeznach             }
16530c54acb8STomasz Jeznach             riscv_iommu_ctx_inval(s, func,
16540c54acb8STomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IODIR_DID), 0);
16550c54acb8STomasz Jeznach             break;
16560c54acb8STomasz Jeznach 
16570c54acb8STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IODIR_FUNC_INVAL_PDT,
16580c54acb8STomasz Jeznach                              RISCV_IOMMU_CMD_IODIR_OPCODE):
16590c54acb8STomasz Jeznach             if (!(cmd.dword0 & RISCV_IOMMU_CMD_IODIR_DV)) {
16600c54acb8STomasz Jeznach                 /* illegal command arguments IODIR_PDT & DV == 0 */
16610c54acb8STomasz Jeznach                 goto cmd_ill;
16620c54acb8STomasz Jeznach             } else {
16630c54acb8STomasz Jeznach                 func = riscv_iommu_ctx_inval_devid_procid;
16640c54acb8STomasz Jeznach             }
16650c54acb8STomasz Jeznach             riscv_iommu_ctx_inval(s, func,
16660c54acb8STomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IODIR_DID),
16670c54acb8STomasz Jeznach                 get_field(cmd.dword0, RISCV_IOMMU_CMD_IODIR_PID));
16680c54acb8STomasz Jeznach             break;
16690c54acb8STomasz Jeznach 
167069a9ae48STomasz Jeznach         /* ATS commands */
167169a9ae48STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_ATS_FUNC_INVAL,
167269a9ae48STomasz Jeznach                              RISCV_IOMMU_CMD_ATS_OPCODE):
167369a9ae48STomasz Jeznach             if (!s->enable_ats) {
167469a9ae48STomasz Jeznach                 goto cmd_ill;
167569a9ae48STomasz Jeznach             }
167669a9ae48STomasz Jeznach 
167769a9ae48STomasz Jeznach             riscv_iommu_ats_inval(s, &cmd);
167869a9ae48STomasz Jeznach             break;
167969a9ae48STomasz Jeznach 
168069a9ae48STomasz Jeznach         case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_ATS_FUNC_PRGR,
168169a9ae48STomasz Jeznach                              RISCV_IOMMU_CMD_ATS_OPCODE):
168269a9ae48STomasz Jeznach             if (!s->enable_ats) {
168369a9ae48STomasz Jeznach                 goto cmd_ill;
168469a9ae48STomasz Jeznach             }
168569a9ae48STomasz Jeznach 
168669a9ae48STomasz Jeznach             riscv_iommu_ats_prgr(s, &cmd);
168769a9ae48STomasz Jeznach             break;
168869a9ae48STomasz Jeznach 
16890c54acb8STomasz Jeznach         default:
16900c54acb8STomasz Jeznach         cmd_ill:
16910c54acb8STomasz Jeznach             /* Invalid instruction, do not advance instruction index. */
16920c54acb8STomasz Jeznach             riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR,
16930c54acb8STomasz Jeznach                 RISCV_IOMMU_CQCSR_CMD_ILL, 0);
16940c54acb8STomasz Jeznach             goto fault;
16950c54acb8STomasz Jeznach         }
16960c54acb8STomasz Jeznach 
16970c54acb8STomasz Jeznach         /* Advance and update head pointer after command completes. */
16980c54acb8STomasz Jeznach         head = (head + 1) & s->cq_mask;
16990c54acb8STomasz Jeznach         riscv_iommu_reg_set32(s, RISCV_IOMMU_REG_CQH, head);
17000c54acb8STomasz Jeznach     }
17010c54acb8STomasz Jeznach     return;
17020c54acb8STomasz Jeznach 
17030c54acb8STomasz Jeznach fault:
17040c54acb8STomasz Jeznach     if (ctrl & RISCV_IOMMU_CQCSR_CIE) {
17050c54acb8STomasz Jeznach         riscv_iommu_notify(s, RISCV_IOMMU_INTR_CQ);
17060c54acb8STomasz Jeznach     }
17070c54acb8STomasz Jeznach }
17080c54acb8STomasz Jeznach 
17090c54acb8STomasz Jeznach static void riscv_iommu_process_cq_control(RISCVIOMMUState *s)
17100c54acb8STomasz Jeznach {
17110c54acb8STomasz Jeznach     uint64_t base;
17120c54acb8STomasz Jeznach     uint32_t ctrl_set = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQCSR);
17130c54acb8STomasz Jeznach     uint32_t ctrl_clr;
17140c54acb8STomasz Jeznach     bool enable = !!(ctrl_set & RISCV_IOMMU_CQCSR_CQEN);
17150c54acb8STomasz Jeznach     bool active = !!(ctrl_set & RISCV_IOMMU_CQCSR_CQON);
17160c54acb8STomasz Jeznach 
17170c54acb8STomasz Jeznach     if (enable && !active) {
17180c54acb8STomasz Jeznach         base = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_CQB);
17190c54acb8STomasz Jeznach         s->cq_mask = (2ULL << get_field(base, RISCV_IOMMU_CQB_LOG2SZ)) - 1;
17200c54acb8STomasz Jeznach         s->cq_addr = PPN_PHYS(get_field(base, RISCV_IOMMU_CQB_PPN));
17210c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQT], ~s->cq_mask);
17220c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_CQH], 0);
17230c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_CQT], 0);
17240c54acb8STomasz Jeznach         ctrl_set = RISCV_IOMMU_CQCSR_CQON;
17250c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_CQCSR_BUSY | RISCV_IOMMU_CQCSR_CQMF |
17260c54acb8STomasz Jeznach                    RISCV_IOMMU_CQCSR_CMD_ILL | RISCV_IOMMU_CQCSR_CMD_TO |
17270c54acb8STomasz Jeznach                    RISCV_IOMMU_CQCSR_FENCE_W_IP;
17280c54acb8STomasz Jeznach     } else if (!enable && active) {
17290c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQT], ~0);
17300c54acb8STomasz Jeznach         ctrl_set = 0;
17310c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_CQCSR_BUSY | RISCV_IOMMU_CQCSR_CQON;
17320c54acb8STomasz Jeznach     } else {
17330c54acb8STomasz Jeznach         ctrl_set = 0;
17340c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_CQCSR_BUSY;
17350c54acb8STomasz Jeznach     }
17360c54acb8STomasz Jeznach 
17370c54acb8STomasz Jeznach     riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR, ctrl_set, ctrl_clr);
17380c54acb8STomasz Jeznach }
17390c54acb8STomasz Jeznach 
17400c54acb8STomasz Jeznach static void riscv_iommu_process_fq_control(RISCVIOMMUState *s)
17410c54acb8STomasz Jeznach {
17420c54acb8STomasz Jeznach     uint64_t base;
17430c54acb8STomasz Jeznach     uint32_t ctrl_set = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQCSR);
17440c54acb8STomasz Jeznach     uint32_t ctrl_clr;
17450c54acb8STomasz Jeznach     bool enable = !!(ctrl_set & RISCV_IOMMU_FQCSR_FQEN);
17460c54acb8STomasz Jeznach     bool active = !!(ctrl_set & RISCV_IOMMU_FQCSR_FQON);
17470c54acb8STomasz Jeznach 
17480c54acb8STomasz Jeznach     if (enable && !active) {
17490c54acb8STomasz Jeznach         base = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_FQB);
17500c54acb8STomasz Jeznach         s->fq_mask = (2ULL << get_field(base, RISCV_IOMMU_FQB_LOG2SZ)) - 1;
17510c54acb8STomasz Jeznach         s->fq_addr = PPN_PHYS(get_field(base, RISCV_IOMMU_FQB_PPN));
17520c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQH], ~s->fq_mask);
17530c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_FQH], 0);
17540c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_FQT], 0);
17550c54acb8STomasz Jeznach         ctrl_set = RISCV_IOMMU_FQCSR_FQON;
17560c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_FQCSR_BUSY | RISCV_IOMMU_FQCSR_FQMF |
17570c54acb8STomasz Jeznach             RISCV_IOMMU_FQCSR_FQOF;
17580c54acb8STomasz Jeznach     } else if (!enable && active) {
17590c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQH], ~0);
17600c54acb8STomasz Jeznach         ctrl_set = 0;
17610c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_FQCSR_BUSY | RISCV_IOMMU_FQCSR_FQON;
17620c54acb8STomasz Jeznach     } else {
17630c54acb8STomasz Jeznach         ctrl_set = 0;
17640c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_FQCSR_BUSY;
17650c54acb8STomasz Jeznach     }
17660c54acb8STomasz Jeznach 
17670c54acb8STomasz Jeznach     riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_FQCSR, ctrl_set, ctrl_clr);
17680c54acb8STomasz Jeznach }
17690c54acb8STomasz Jeznach 
17700c54acb8STomasz Jeznach static void riscv_iommu_process_pq_control(RISCVIOMMUState *s)
17710c54acb8STomasz Jeznach {
17720c54acb8STomasz Jeznach     uint64_t base;
17730c54acb8STomasz Jeznach     uint32_t ctrl_set = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_PQCSR);
17740c54acb8STomasz Jeznach     uint32_t ctrl_clr;
17750c54acb8STomasz Jeznach     bool enable = !!(ctrl_set & RISCV_IOMMU_PQCSR_PQEN);
17760c54acb8STomasz Jeznach     bool active = !!(ctrl_set & RISCV_IOMMU_PQCSR_PQON);
17770c54acb8STomasz Jeznach 
17780c54acb8STomasz Jeznach     if (enable && !active) {
17790c54acb8STomasz Jeznach         base = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_PQB);
17800c54acb8STomasz Jeznach         s->pq_mask = (2ULL << get_field(base, RISCV_IOMMU_PQB_LOG2SZ)) - 1;
17810c54acb8STomasz Jeznach         s->pq_addr = PPN_PHYS(get_field(base, RISCV_IOMMU_PQB_PPN));
17820c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQH], ~s->pq_mask);
17830c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_PQH], 0);
17840c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_PQT], 0);
17850c54acb8STomasz Jeznach         ctrl_set = RISCV_IOMMU_PQCSR_PQON;
17860c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_PQCSR_BUSY | RISCV_IOMMU_PQCSR_PQMF |
17870c54acb8STomasz Jeznach             RISCV_IOMMU_PQCSR_PQOF;
17880c54acb8STomasz Jeznach     } else if (!enable && active) {
17890c54acb8STomasz Jeznach         stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQH], ~0);
17900c54acb8STomasz Jeznach         ctrl_set = 0;
17910c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_PQCSR_BUSY | RISCV_IOMMU_PQCSR_PQON;
17920c54acb8STomasz Jeznach     } else {
17930c54acb8STomasz Jeznach         ctrl_set = 0;
17940c54acb8STomasz Jeznach         ctrl_clr = RISCV_IOMMU_PQCSR_BUSY;
17950c54acb8STomasz Jeznach     }
17960c54acb8STomasz Jeznach 
17970c54acb8STomasz Jeznach     riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_PQCSR, ctrl_set, ctrl_clr);
17980c54acb8STomasz Jeznach }
17990c54acb8STomasz Jeznach 
1800a7aa525bSTomasz Jeznach static void riscv_iommu_process_dbg(RISCVIOMMUState *s)
1801a7aa525bSTomasz Jeznach {
1802a7aa525bSTomasz Jeznach     uint64_t iova = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_TR_REQ_IOVA);
1803a7aa525bSTomasz Jeznach     uint64_t ctrl = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_TR_REQ_CTL);
1804a7aa525bSTomasz Jeznach     unsigned devid = get_field(ctrl, RISCV_IOMMU_TR_REQ_CTL_DID);
1805a7aa525bSTomasz Jeznach     unsigned pid = get_field(ctrl, RISCV_IOMMU_TR_REQ_CTL_PID);
1806a7aa525bSTomasz Jeznach     RISCVIOMMUContext *ctx;
1807a7aa525bSTomasz Jeznach     void *ref;
1808a7aa525bSTomasz Jeznach 
1809a7aa525bSTomasz Jeznach     if (!(ctrl & RISCV_IOMMU_TR_REQ_CTL_GO_BUSY)) {
1810a7aa525bSTomasz Jeznach         return;
1811a7aa525bSTomasz Jeznach     }
1812a7aa525bSTomasz Jeznach 
1813a7aa525bSTomasz Jeznach     ctx = riscv_iommu_ctx(s, devid, pid, &ref);
1814a7aa525bSTomasz Jeznach     if (ctx == NULL) {
1815a7aa525bSTomasz Jeznach         riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_TR_RESPONSE,
1816a7aa525bSTomasz Jeznach                                  RISCV_IOMMU_TR_RESPONSE_FAULT |
1817a7aa525bSTomasz Jeznach                                  (RISCV_IOMMU_FQ_CAUSE_DMA_DISABLED << 10));
1818a7aa525bSTomasz Jeznach     } else {
1819a7aa525bSTomasz Jeznach         IOMMUTLBEntry iotlb = {
1820a7aa525bSTomasz Jeznach             .iova = iova,
1821a7aa525bSTomasz Jeznach             .perm = ctrl & RISCV_IOMMU_TR_REQ_CTL_NW ? IOMMU_RO : IOMMU_RW,
1822a7aa525bSTomasz Jeznach             .addr_mask = ~0,
1823a7aa525bSTomasz Jeznach             .target_as = NULL,
1824a7aa525bSTomasz Jeznach         };
1825a7aa525bSTomasz Jeznach         int fault = riscv_iommu_translate(s, ctx, &iotlb, false);
1826a7aa525bSTomasz Jeznach         if (fault) {
1827a7aa525bSTomasz Jeznach             iova = RISCV_IOMMU_TR_RESPONSE_FAULT | (((uint64_t) fault) << 10);
1828a7aa525bSTomasz Jeznach         } else {
1829a7aa525bSTomasz Jeznach             iova = iotlb.translated_addr & ~iotlb.addr_mask;
1830a7aa525bSTomasz Jeznach             iova >>= TARGET_PAGE_BITS;
1831a7aa525bSTomasz Jeznach             iova &= RISCV_IOMMU_TR_RESPONSE_PPN;
1832a7aa525bSTomasz Jeznach 
1833a7aa525bSTomasz Jeznach             /* We do not support superpages (> 4kbs) for now */
1834a7aa525bSTomasz Jeznach             iova &= ~RISCV_IOMMU_TR_RESPONSE_S;
1835a7aa525bSTomasz Jeznach         }
1836a7aa525bSTomasz Jeznach         riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_TR_RESPONSE, iova);
1837a7aa525bSTomasz Jeznach     }
1838a7aa525bSTomasz Jeznach 
1839a7aa525bSTomasz Jeznach     riscv_iommu_reg_mod64(s, RISCV_IOMMU_REG_TR_REQ_CTL, 0,
1840a7aa525bSTomasz Jeznach         RISCV_IOMMU_TR_REQ_CTL_GO_BUSY);
1841a7aa525bSTomasz Jeznach     riscv_iommu_ctx_put(s, ref);
1842a7aa525bSTomasz Jeznach }
1843a7aa525bSTomasz Jeznach 
18440c54acb8STomasz Jeznach typedef void riscv_iommu_process_fn(RISCVIOMMUState *s);
18450c54acb8STomasz Jeznach 
18460c54acb8STomasz Jeznach static void riscv_iommu_update_icvec(RISCVIOMMUState *s, uint64_t data)
18470c54acb8STomasz Jeznach {
18480c54acb8STomasz Jeznach     uint64_t icvec = 0;
18490c54acb8STomasz Jeznach 
18500c54acb8STomasz Jeznach     icvec |= MIN(data & RISCV_IOMMU_ICVEC_CIV,
18510c54acb8STomasz Jeznach                  s->icvec_avail_vectors & RISCV_IOMMU_ICVEC_CIV);
18520c54acb8STomasz Jeznach 
18530c54acb8STomasz Jeznach     icvec |= MIN(data & RISCV_IOMMU_ICVEC_FIV,
18540c54acb8STomasz Jeznach                  s->icvec_avail_vectors & RISCV_IOMMU_ICVEC_FIV);
18550c54acb8STomasz Jeznach 
18560c54acb8STomasz Jeznach     icvec |= MIN(data & RISCV_IOMMU_ICVEC_PMIV,
18570c54acb8STomasz Jeznach                  s->icvec_avail_vectors & RISCV_IOMMU_ICVEC_PMIV);
18580c54acb8STomasz Jeznach 
18590c54acb8STomasz Jeznach     icvec |= MIN(data & RISCV_IOMMU_ICVEC_PIV,
18600c54acb8STomasz Jeznach                  s->icvec_avail_vectors & RISCV_IOMMU_ICVEC_PIV);
18610c54acb8STomasz Jeznach 
18620c54acb8STomasz Jeznach     trace_riscv_iommu_icvec_write(data, icvec);
18630c54acb8STomasz Jeznach 
18640c54acb8STomasz Jeznach     riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_ICVEC, icvec);
18650c54acb8STomasz Jeznach }
18660c54acb8STomasz Jeznach 
18670c54acb8STomasz Jeznach static void riscv_iommu_update_ipsr(RISCVIOMMUState *s, uint64_t data)
18680c54acb8STomasz Jeznach {
18690c54acb8STomasz Jeznach     uint32_t cqcsr, fqcsr, pqcsr;
18700c54acb8STomasz Jeznach     uint32_t ipsr_set = 0;
18710c54acb8STomasz Jeznach     uint32_t ipsr_clr = 0;
18720c54acb8STomasz Jeznach 
18730c54acb8STomasz Jeznach     if (data & RISCV_IOMMU_IPSR_CIP) {
18740c54acb8STomasz Jeznach         cqcsr = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQCSR);
18750c54acb8STomasz Jeznach 
18760c54acb8STomasz Jeznach         if (cqcsr & RISCV_IOMMU_CQCSR_CIE &&
18770c54acb8STomasz Jeznach             (cqcsr & RISCV_IOMMU_CQCSR_FENCE_W_IP ||
18780c54acb8STomasz Jeznach              cqcsr & RISCV_IOMMU_CQCSR_CMD_ILL ||
18790c54acb8STomasz Jeznach              cqcsr & RISCV_IOMMU_CQCSR_CMD_TO ||
18800c54acb8STomasz Jeznach              cqcsr & RISCV_IOMMU_CQCSR_CQMF)) {
18810c54acb8STomasz Jeznach             ipsr_set |= RISCV_IOMMU_IPSR_CIP;
18820c54acb8STomasz Jeznach         } else {
18830c54acb8STomasz Jeznach             ipsr_clr |= RISCV_IOMMU_IPSR_CIP;
18840c54acb8STomasz Jeznach         }
18850c54acb8STomasz Jeznach     } else {
18860c54acb8STomasz Jeznach         ipsr_clr |= RISCV_IOMMU_IPSR_CIP;
18870c54acb8STomasz Jeznach     }
18880c54acb8STomasz Jeznach 
18890c54acb8STomasz Jeznach     if (data & RISCV_IOMMU_IPSR_FIP) {
18900c54acb8STomasz Jeznach         fqcsr = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQCSR);
18910c54acb8STomasz Jeznach 
18920c54acb8STomasz Jeznach         if (fqcsr & RISCV_IOMMU_FQCSR_FIE &&
18930c54acb8STomasz Jeznach             (fqcsr & RISCV_IOMMU_FQCSR_FQOF ||
18940c54acb8STomasz Jeznach              fqcsr & RISCV_IOMMU_FQCSR_FQMF)) {
18950c54acb8STomasz Jeznach             ipsr_set |= RISCV_IOMMU_IPSR_FIP;
18960c54acb8STomasz Jeznach         } else {
18970c54acb8STomasz Jeznach             ipsr_clr |= RISCV_IOMMU_IPSR_FIP;
18980c54acb8STomasz Jeznach         }
18990c54acb8STomasz Jeznach     } else {
19000c54acb8STomasz Jeznach         ipsr_clr |= RISCV_IOMMU_IPSR_FIP;
19010c54acb8STomasz Jeznach     }
19020c54acb8STomasz Jeznach 
19030c54acb8STomasz Jeznach     if (data & RISCV_IOMMU_IPSR_PIP) {
19040c54acb8STomasz Jeznach         pqcsr = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_PQCSR);
19050c54acb8STomasz Jeznach 
19060c54acb8STomasz Jeznach         if (pqcsr & RISCV_IOMMU_PQCSR_PIE &&
19070c54acb8STomasz Jeznach             (pqcsr & RISCV_IOMMU_PQCSR_PQOF ||
19080c54acb8STomasz Jeznach              pqcsr & RISCV_IOMMU_PQCSR_PQMF)) {
19090c54acb8STomasz Jeznach             ipsr_set |= RISCV_IOMMU_IPSR_PIP;
19100c54acb8STomasz Jeznach         } else {
19110c54acb8STomasz Jeznach             ipsr_clr |= RISCV_IOMMU_IPSR_PIP;
19120c54acb8STomasz Jeznach         }
19130c54acb8STomasz Jeznach     } else {
19140c54acb8STomasz Jeznach         ipsr_clr |= RISCV_IOMMU_IPSR_PIP;
19150c54acb8STomasz Jeznach     }
19160c54acb8STomasz Jeznach 
19170c54acb8STomasz Jeznach     riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_IPSR, ipsr_set, ipsr_clr);
19180c54acb8STomasz Jeznach }
19190c54acb8STomasz Jeznach 
19200c54acb8STomasz Jeznach /*
19210c54acb8STomasz Jeznach  * Write the resulting value of 'data' for the reg specified
19220c54acb8STomasz Jeznach  * by 'reg_addr', after considering read-only/read-write/write-clear
19230c54acb8STomasz Jeznach  * bits, in the pointer 'dest'.
19240c54acb8STomasz Jeznach  *
19250c54acb8STomasz Jeznach  * The result is written in little-endian.
19260c54acb8STomasz Jeznach  */
19270c54acb8STomasz Jeznach static void riscv_iommu_write_reg_val(RISCVIOMMUState *s,
19280c54acb8STomasz Jeznach                                       void *dest, hwaddr reg_addr,
19290c54acb8STomasz Jeznach                                       int size, uint64_t data)
19300c54acb8STomasz Jeznach {
19310c54acb8STomasz Jeznach     uint64_t ro = ldn_le_p(&s->regs_ro[reg_addr], size);
19320c54acb8STomasz Jeznach     uint64_t wc = ldn_le_p(&s->regs_wc[reg_addr], size);
19330c54acb8STomasz Jeznach     uint64_t rw = ldn_le_p(&s->regs_rw[reg_addr], size);
19340c54acb8STomasz Jeznach 
19350c54acb8STomasz Jeznach     stn_le_p(dest, size, ((rw & ro) | (data & ~ro)) & ~(data & wc));
19360c54acb8STomasz Jeznach }
19370c54acb8STomasz Jeznach 
19380c54acb8STomasz Jeznach static MemTxResult riscv_iommu_mmio_write(void *opaque, hwaddr addr,
19390c54acb8STomasz Jeznach                                           uint64_t data, unsigned size,
19400c54acb8STomasz Jeznach                                           MemTxAttrs attrs)
19410c54acb8STomasz Jeznach {
19420c54acb8STomasz Jeznach     riscv_iommu_process_fn *process_fn = NULL;
19430c54acb8STomasz Jeznach     RISCVIOMMUState *s = opaque;
19440c54acb8STomasz Jeznach     uint32_t regb = addr & ~3;
19450c54acb8STomasz Jeznach     uint32_t busy = 0;
19460c54acb8STomasz Jeznach     uint64_t val = 0;
19470c54acb8STomasz Jeznach 
19480c54acb8STomasz Jeznach     if ((addr & (size - 1)) != 0) {
19490c54acb8STomasz Jeznach         /* Unsupported MMIO alignment or access size */
19500c54acb8STomasz Jeznach         return MEMTX_ERROR;
19510c54acb8STomasz Jeznach     }
19520c54acb8STomasz Jeznach 
19530c54acb8STomasz Jeznach     if (addr + size > RISCV_IOMMU_REG_MSI_CONFIG) {
19540c54acb8STomasz Jeznach         /* Unsupported MMIO access location. */
19550c54acb8STomasz Jeznach         return MEMTX_ACCESS_ERROR;
19560c54acb8STomasz Jeznach     }
19570c54acb8STomasz Jeznach 
19580c54acb8STomasz Jeznach     /* Track actionable MMIO write. */
19590c54acb8STomasz Jeznach     switch (regb) {
19600c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_DDTP:
19610c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_DDTP + 4:
19620c54acb8STomasz Jeznach         process_fn = riscv_iommu_process_ddtp;
19630c54acb8STomasz Jeznach         regb = RISCV_IOMMU_REG_DDTP;
19640c54acb8STomasz Jeznach         busy = RISCV_IOMMU_DDTP_BUSY;
19650c54acb8STomasz Jeznach         break;
19660c54acb8STomasz Jeznach 
19670c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_CQT:
19680c54acb8STomasz Jeznach         process_fn = riscv_iommu_process_cq_tail;
19690c54acb8STomasz Jeznach         break;
19700c54acb8STomasz Jeznach 
19710c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_CQCSR:
19720c54acb8STomasz Jeznach         process_fn = riscv_iommu_process_cq_control;
19730c54acb8STomasz Jeznach         busy = RISCV_IOMMU_CQCSR_BUSY;
19740c54acb8STomasz Jeznach         break;
19750c54acb8STomasz Jeznach 
19760c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_FQCSR:
19770c54acb8STomasz Jeznach         process_fn = riscv_iommu_process_fq_control;
19780c54acb8STomasz Jeznach         busy = RISCV_IOMMU_FQCSR_BUSY;
19790c54acb8STomasz Jeznach         break;
19800c54acb8STomasz Jeznach 
19810c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_PQCSR:
19820c54acb8STomasz Jeznach         process_fn = riscv_iommu_process_pq_control;
19830c54acb8STomasz Jeznach         busy = RISCV_IOMMU_PQCSR_BUSY;
19840c54acb8STomasz Jeznach         break;
19850c54acb8STomasz Jeznach 
19860c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_ICVEC:
19870c54acb8STomasz Jeznach     case RISCV_IOMMU_REG_IPSR:
19880c54acb8STomasz Jeznach         /*
19890c54acb8STomasz Jeznach          * ICVEC and IPSR have special read/write procedures. We'll
19900c54acb8STomasz Jeznach          * call their respective helpers and exit.
19910c54acb8STomasz Jeznach          */
19920c54acb8STomasz Jeznach         riscv_iommu_write_reg_val(s, &val, addr, size, data);
19930c54acb8STomasz Jeznach 
19940c54acb8STomasz Jeznach         /*
19950c54acb8STomasz Jeznach          * 'val' is stored as LE. Switch to host endianess
19960c54acb8STomasz Jeznach          * before using it.
19970c54acb8STomasz Jeznach          */
19980c54acb8STomasz Jeznach         val = le64_to_cpu(val);
19990c54acb8STomasz Jeznach 
20000c54acb8STomasz Jeznach         if (regb == RISCV_IOMMU_REG_ICVEC) {
20010c54acb8STomasz Jeznach             riscv_iommu_update_icvec(s, val);
20020c54acb8STomasz Jeznach         } else {
20030c54acb8STomasz Jeznach             riscv_iommu_update_ipsr(s, val);
20040c54acb8STomasz Jeznach         }
20050c54acb8STomasz Jeznach 
20060c54acb8STomasz Jeznach         return MEMTX_OK;
20070c54acb8STomasz Jeznach 
2008a7aa525bSTomasz Jeznach     case RISCV_IOMMU_REG_TR_REQ_CTL:
2009a7aa525bSTomasz Jeznach         process_fn = riscv_iommu_process_dbg;
2010a7aa525bSTomasz Jeznach         regb = RISCV_IOMMU_REG_TR_REQ_CTL;
2011a7aa525bSTomasz Jeznach         busy = RISCV_IOMMU_TR_REQ_CTL_GO_BUSY;
2012a7aa525bSTomasz Jeznach         break;
2013a7aa525bSTomasz Jeznach 
20140c54acb8STomasz Jeznach     default:
20150c54acb8STomasz Jeznach         break;
20160c54acb8STomasz Jeznach     }
20170c54acb8STomasz Jeznach 
20180c54acb8STomasz Jeznach     /*
20190c54acb8STomasz Jeznach      * Registers update might be not synchronized with core logic.
20200c54acb8STomasz Jeznach      * If system software updates register when relevant BUSY bit
20210c54acb8STomasz Jeznach      * is set IOMMU behavior of additional writes to the register
20220c54acb8STomasz Jeznach      * is UNSPECIFIED.
20230c54acb8STomasz Jeznach      */
20240c54acb8STomasz Jeznach     riscv_iommu_write_reg_val(s, &s->regs_rw[addr], addr, size, data);
20250c54acb8STomasz Jeznach 
20260c54acb8STomasz Jeznach     /* Busy flag update, MSB 4-byte register. */
20270c54acb8STomasz Jeznach     if (busy) {
20280c54acb8STomasz Jeznach         uint32_t rw = ldl_le_p(&s->regs_rw[regb]);
20290c54acb8STomasz Jeznach         stl_le_p(&s->regs_rw[regb], rw | busy);
20300c54acb8STomasz Jeznach     }
20310c54acb8STomasz Jeznach 
20320c54acb8STomasz Jeznach     if (process_fn) {
20330c54acb8STomasz Jeznach         process_fn(s);
20340c54acb8STomasz Jeznach     }
20350c54acb8STomasz Jeznach 
20360c54acb8STomasz Jeznach     return MEMTX_OK;
20370c54acb8STomasz Jeznach }
20380c54acb8STomasz Jeznach 
20390c54acb8STomasz Jeznach static MemTxResult riscv_iommu_mmio_read(void *opaque, hwaddr addr,
20400c54acb8STomasz Jeznach     uint64_t *data, unsigned size, MemTxAttrs attrs)
20410c54acb8STomasz Jeznach {
20420c54acb8STomasz Jeznach     RISCVIOMMUState *s = opaque;
20430c54acb8STomasz Jeznach     uint64_t val = -1;
20440c54acb8STomasz Jeznach     uint8_t *ptr;
20450c54acb8STomasz Jeznach 
20460c54acb8STomasz Jeznach     if ((addr & (size - 1)) != 0) {
20470c54acb8STomasz Jeznach         /* Unsupported MMIO alignment. */
20480c54acb8STomasz Jeznach         return MEMTX_ERROR;
20490c54acb8STomasz Jeznach     }
20500c54acb8STomasz Jeznach 
20510c54acb8STomasz Jeznach     if (addr + size > RISCV_IOMMU_REG_MSI_CONFIG) {
20520c54acb8STomasz Jeznach         return MEMTX_ACCESS_ERROR;
20530c54acb8STomasz Jeznach     }
20540c54acb8STomasz Jeznach 
20550c54acb8STomasz Jeznach     ptr = &s->regs_rw[addr];
20560c54acb8STomasz Jeznach     val = ldn_le_p(ptr, size);
20570c54acb8STomasz Jeznach 
20580c54acb8STomasz Jeznach     *data = val;
20590c54acb8STomasz Jeznach 
20600c54acb8STomasz Jeznach     return MEMTX_OK;
20610c54acb8STomasz Jeznach }
20620c54acb8STomasz Jeznach 
20630c54acb8STomasz Jeznach static const MemoryRegionOps riscv_iommu_mmio_ops = {
20640c54acb8STomasz Jeznach     .read_with_attrs = riscv_iommu_mmio_read,
20650c54acb8STomasz Jeznach     .write_with_attrs = riscv_iommu_mmio_write,
20660c54acb8STomasz Jeznach     .endianness = DEVICE_NATIVE_ENDIAN,
20670c54acb8STomasz Jeznach     .impl = {
20680c54acb8STomasz Jeznach         .min_access_size = 4,
20690c54acb8STomasz Jeznach         .max_access_size = 8,
20700c54acb8STomasz Jeznach         .unaligned = false,
20710c54acb8STomasz Jeznach     },
20720c54acb8STomasz Jeznach     .valid = {
20730c54acb8STomasz Jeznach         .min_access_size = 4,
20740c54acb8STomasz Jeznach         .max_access_size = 8,
20750c54acb8STomasz Jeznach     }
20760c54acb8STomasz Jeznach };
20770c54acb8STomasz Jeznach 
20780c54acb8STomasz Jeznach /*
20790c54acb8STomasz Jeznach  * Translations matching MSI pattern check are redirected to "riscv-iommu-trap"
20800c54acb8STomasz Jeznach  * memory region as untranslated address, for additional MSI/MRIF interception
20810c54acb8STomasz Jeznach  * by IOMMU interrupt remapping implementation.
20820c54acb8STomasz Jeznach  * Note: Device emulation code generating an MSI is expected to provide a valid
20830c54acb8STomasz Jeznach  * memory transaction attributes with requested_id set.
20840c54acb8STomasz Jeznach  */
20850c54acb8STomasz Jeznach static MemTxResult riscv_iommu_trap_write(void *opaque, hwaddr addr,
20860c54acb8STomasz Jeznach     uint64_t data, unsigned size, MemTxAttrs attrs)
20870c54acb8STomasz Jeznach {
20880c54acb8STomasz Jeznach     RISCVIOMMUState* s = (RISCVIOMMUState *)opaque;
20890c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx;
20900c54acb8STomasz Jeznach     MemTxResult res;
20910c54acb8STomasz Jeznach     void *ref;
20920c54acb8STomasz Jeznach     uint32_t devid = attrs.requester_id;
20930c54acb8STomasz Jeznach 
20940c54acb8STomasz Jeznach     if (attrs.unspecified) {
20950c54acb8STomasz Jeznach         return MEMTX_ACCESS_ERROR;
20960c54acb8STomasz Jeznach     }
20970c54acb8STomasz Jeznach 
20980c54acb8STomasz Jeznach     /* FIXME: PCIe bus remapping for attached endpoints. */
20990c54acb8STomasz Jeznach     devid |= s->bus << 8;
21000c54acb8STomasz Jeznach 
21010c54acb8STomasz Jeznach     ctx = riscv_iommu_ctx(s, devid, 0, &ref);
21020c54acb8STomasz Jeznach     if (ctx == NULL) {
21030c54acb8STomasz Jeznach         res = MEMTX_ACCESS_ERROR;
21040c54acb8STomasz Jeznach     } else {
21050c54acb8STomasz Jeznach         res = riscv_iommu_msi_write(s, ctx, addr, data, size, attrs);
21060c54acb8STomasz Jeznach     }
21070c54acb8STomasz Jeznach     riscv_iommu_ctx_put(s, ref);
21080c54acb8STomasz Jeznach     return res;
21090c54acb8STomasz Jeznach }
21100c54acb8STomasz Jeznach 
21110c54acb8STomasz Jeznach static MemTxResult riscv_iommu_trap_read(void *opaque, hwaddr addr,
21120c54acb8STomasz Jeznach     uint64_t *data, unsigned size, MemTxAttrs attrs)
21130c54acb8STomasz Jeznach {
21140c54acb8STomasz Jeznach     return MEMTX_ACCESS_ERROR;
21150c54acb8STomasz Jeznach }
21160c54acb8STomasz Jeznach 
21170c54acb8STomasz Jeznach static const MemoryRegionOps riscv_iommu_trap_ops = {
21180c54acb8STomasz Jeznach     .read_with_attrs = riscv_iommu_trap_read,
21190c54acb8STomasz Jeznach     .write_with_attrs = riscv_iommu_trap_write,
21200c54acb8STomasz Jeznach     .endianness = DEVICE_LITTLE_ENDIAN,
21210c54acb8STomasz Jeznach     .impl = {
21220c54acb8STomasz Jeznach         .min_access_size = 4,
21230c54acb8STomasz Jeznach         .max_access_size = 8,
21240c54acb8STomasz Jeznach         .unaligned = true,
21250c54acb8STomasz Jeznach     },
21260c54acb8STomasz Jeznach     .valid = {
21270c54acb8STomasz Jeznach         .min_access_size = 4,
21280c54acb8STomasz Jeznach         .max_access_size = 8,
21290c54acb8STomasz Jeznach     }
21300c54acb8STomasz Jeznach };
21310c54acb8STomasz Jeznach 
2132d13346d1SDaniel Henrique Barboza void riscv_iommu_set_cap_igs(RISCVIOMMUState *s, riscv_iommu_igs_mode mode)
2133d13346d1SDaniel Henrique Barboza {
2134d13346d1SDaniel Henrique Barboza     s->cap = set_field(s->cap, RISCV_IOMMU_CAP_IGS, mode);
2135d13346d1SDaniel Henrique Barboza }
2136d13346d1SDaniel Henrique Barboza 
21374876e6f7SDaniel Henrique Barboza static void riscv_iommu_instance_init(Object *obj)
21384876e6f7SDaniel Henrique Barboza {
21394876e6f7SDaniel Henrique Barboza     RISCVIOMMUState *s = RISCV_IOMMU(obj);
21404876e6f7SDaniel Henrique Barboza 
21414876e6f7SDaniel Henrique Barboza     /* Enable translation debug interface */
21424876e6f7SDaniel Henrique Barboza     s->cap = RISCV_IOMMU_CAP_DBG;
21434876e6f7SDaniel Henrique Barboza 
21444876e6f7SDaniel Henrique Barboza     /* Report QEMU target physical address space limits */
21454876e6f7SDaniel Henrique Barboza     s->cap = set_field(s->cap, RISCV_IOMMU_CAP_PAS,
21464876e6f7SDaniel Henrique Barboza                        TARGET_PHYS_ADDR_SPACE_BITS);
21474876e6f7SDaniel Henrique Barboza 
21484876e6f7SDaniel Henrique Barboza     /* TODO: method to report supported PID bits */
21494876e6f7SDaniel Henrique Barboza     s->pid_bits = 8; /* restricted to size of MemTxAttrs.pid */
21504876e6f7SDaniel Henrique Barboza     s->cap |= RISCV_IOMMU_CAP_PD8;
21514876e6f7SDaniel Henrique Barboza 
21524876e6f7SDaniel Henrique Barboza     /* register storage */
21534876e6f7SDaniel Henrique Barboza     s->regs_rw = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE);
21544876e6f7SDaniel Henrique Barboza     s->regs_ro = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE);
21554876e6f7SDaniel Henrique Barboza     s->regs_wc = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE);
21564876e6f7SDaniel Henrique Barboza 
21574876e6f7SDaniel Henrique Barboza      /* Mark all registers read-only */
21584876e6f7SDaniel Henrique Barboza     memset(s->regs_ro, 0xff, RISCV_IOMMU_REG_SIZE);
21594876e6f7SDaniel Henrique Barboza 
21604876e6f7SDaniel Henrique Barboza     /* Device translation context cache */
21614876e6f7SDaniel Henrique Barboza     s->ctx_cache = g_hash_table_new_full(riscv_iommu_ctx_hash,
21624876e6f7SDaniel Henrique Barboza                                          riscv_iommu_ctx_equal,
21634876e6f7SDaniel Henrique Barboza                                          g_free, NULL);
21644876e6f7SDaniel Henrique Barboza 
21654876e6f7SDaniel Henrique Barboza     s->iot_cache = g_hash_table_new_full(riscv_iommu_iot_hash,
21664876e6f7SDaniel Henrique Barboza                                          riscv_iommu_iot_equal,
21674876e6f7SDaniel Henrique Barboza                                          g_free, NULL);
21684876e6f7SDaniel Henrique Barboza 
21694876e6f7SDaniel Henrique Barboza     s->iommus.le_next = NULL;
21704876e6f7SDaniel Henrique Barboza     s->iommus.le_prev = NULL;
21714876e6f7SDaniel Henrique Barboza     QLIST_INIT(&s->spaces);
21724876e6f7SDaniel Henrique Barboza }
21734876e6f7SDaniel Henrique Barboza 
21740c54acb8STomasz Jeznach static void riscv_iommu_realize(DeviceState *dev, Error **errp)
21750c54acb8STomasz Jeznach {
21760c54acb8STomasz Jeznach     RISCVIOMMUState *s = RISCV_IOMMU(dev);
21770c54acb8STomasz Jeznach 
21784876e6f7SDaniel Henrique Barboza     s->cap |= s->version & RISCV_IOMMU_CAP_VERSION;
21790c54acb8STomasz Jeznach     if (s->enable_msi) {
21800c54acb8STomasz Jeznach         s->cap |= RISCV_IOMMU_CAP_MSI_FLAT | RISCV_IOMMU_CAP_MSI_MRIF;
21810c54acb8STomasz Jeznach     }
218269a9ae48STomasz Jeznach     if (s->enable_ats) {
218369a9ae48STomasz Jeznach         s->cap |= RISCV_IOMMU_CAP_ATS;
218469a9ae48STomasz Jeznach     }
21850c54acb8STomasz Jeznach     if (s->enable_s_stage) {
21860c54acb8STomasz Jeznach         s->cap |= RISCV_IOMMU_CAP_SV32 | RISCV_IOMMU_CAP_SV39 |
21870c54acb8STomasz Jeznach                   RISCV_IOMMU_CAP_SV48 | RISCV_IOMMU_CAP_SV57;
21880c54acb8STomasz Jeznach     }
21890c54acb8STomasz Jeznach     if (s->enable_g_stage) {
21900c54acb8STomasz Jeznach         s->cap |= RISCV_IOMMU_CAP_SV32X4 | RISCV_IOMMU_CAP_SV39X4 |
21910c54acb8STomasz Jeznach                   RISCV_IOMMU_CAP_SV48X4 | RISCV_IOMMU_CAP_SV57X4;
21920c54acb8STomasz Jeznach     }
21930c54acb8STomasz Jeznach 
21940c54acb8STomasz Jeznach     /* Out-of-reset translation mode: OFF (DMA disabled) BARE (passthrough) */
21950c54acb8STomasz Jeznach     s->ddtp = set_field(0, RISCV_IOMMU_DDTP_MODE, s->enable_off ?
21960c54acb8STomasz Jeznach                         RISCV_IOMMU_DDTP_MODE_OFF : RISCV_IOMMU_DDTP_MODE_BARE);
21970c54acb8STomasz Jeznach 
21980c54acb8STomasz Jeznach     /*
21990c54acb8STomasz Jeznach      * Register complete MMIO space, including MSI/PBA registers.
22000c54acb8STomasz Jeznach      * Note, PCIDevice implementation will add overlapping MR for MSI/PBA,
22010c54acb8STomasz Jeznach      * managed directly by the PCIDevice implementation.
22020c54acb8STomasz Jeznach      */
22030c54acb8STomasz Jeznach     memory_region_init_io(&s->regs_mr, OBJECT(dev), &riscv_iommu_mmio_ops, s,
22040c54acb8STomasz Jeznach         "riscv-iommu-regs", RISCV_IOMMU_REG_SIZE);
22050c54acb8STomasz Jeznach 
22060c54acb8STomasz Jeznach     /* Set power-on register state */
22070c54acb8STomasz Jeznach     stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_CAP], s->cap);
22080c54acb8STomasz Jeznach     stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_FCTL], 0);
22090c54acb8STomasz Jeznach     stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_FCTL],
22100c54acb8STomasz Jeznach              ~(RISCV_IOMMU_FCTL_BE | RISCV_IOMMU_FCTL_WSI));
22110c54acb8STomasz Jeznach     stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_DDTP],
22120c54acb8STomasz Jeznach         ~(RISCV_IOMMU_DDTP_PPN | RISCV_IOMMU_DDTP_MODE));
22130c54acb8STomasz Jeznach     stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQB],
22140c54acb8STomasz Jeznach         ~(RISCV_IOMMU_CQB_LOG2SZ | RISCV_IOMMU_CQB_PPN));
22150c54acb8STomasz Jeznach     stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQB],
22160c54acb8STomasz Jeznach         ~(RISCV_IOMMU_FQB_LOG2SZ | RISCV_IOMMU_FQB_PPN));
22170c54acb8STomasz Jeznach     stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQB],
22180c54acb8STomasz Jeznach         ~(RISCV_IOMMU_PQB_LOG2SZ | RISCV_IOMMU_PQB_PPN));
22190c54acb8STomasz Jeznach     stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_CQCSR], RISCV_IOMMU_CQCSR_CQMF |
22200c54acb8STomasz Jeznach         RISCV_IOMMU_CQCSR_CMD_TO | RISCV_IOMMU_CQCSR_CMD_ILL);
22210c54acb8STomasz Jeznach     stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQCSR], RISCV_IOMMU_CQCSR_CQON |
22220c54acb8STomasz Jeznach         RISCV_IOMMU_CQCSR_BUSY);
22230c54acb8STomasz Jeznach     stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_FQCSR], RISCV_IOMMU_FQCSR_FQMF |
22240c54acb8STomasz Jeznach         RISCV_IOMMU_FQCSR_FQOF);
22250c54acb8STomasz Jeznach     stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQCSR], RISCV_IOMMU_FQCSR_FQON |
22260c54acb8STomasz Jeznach         RISCV_IOMMU_FQCSR_BUSY);
22270c54acb8STomasz Jeznach     stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_PQCSR], RISCV_IOMMU_PQCSR_PQMF |
22280c54acb8STomasz Jeznach         RISCV_IOMMU_PQCSR_PQOF);
22290c54acb8STomasz Jeznach     stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQCSR], RISCV_IOMMU_PQCSR_PQON |
22300c54acb8STomasz Jeznach         RISCV_IOMMU_PQCSR_BUSY);
22310c54acb8STomasz Jeznach     stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_IPSR], ~0);
22320c54acb8STomasz Jeznach     stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_ICVEC], 0);
22330c54acb8STomasz Jeznach     stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_DDTP], s->ddtp);
2234a7aa525bSTomasz Jeznach     /* If debug registers enabled. */
2235a7aa525bSTomasz Jeznach     if (s->cap & RISCV_IOMMU_CAP_DBG) {
2236a7aa525bSTomasz Jeznach         stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_TR_REQ_IOVA], 0);
2237a7aa525bSTomasz Jeznach         stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_TR_REQ_CTL],
2238a7aa525bSTomasz Jeznach             RISCV_IOMMU_TR_REQ_CTL_GO_BUSY);
2239a7aa525bSTomasz Jeznach     }
22400c54acb8STomasz Jeznach 
22410c54acb8STomasz Jeznach     /* Memory region for downstream access, if specified. */
22420c54acb8STomasz Jeznach     if (s->target_mr) {
22430c54acb8STomasz Jeznach         s->target_as = g_new0(AddressSpace, 1);
22440c54acb8STomasz Jeznach         address_space_init(s->target_as, s->target_mr,
22450c54acb8STomasz Jeznach             "riscv-iommu-downstream");
22460c54acb8STomasz Jeznach     } else {
22470c54acb8STomasz Jeznach         /* Fallback to global system memory. */
22480c54acb8STomasz Jeznach         s->target_as = &address_space_memory;
22490c54acb8STomasz Jeznach     }
22500c54acb8STomasz Jeznach 
22510c54acb8STomasz Jeznach     /* Memory region for untranslated MRIF/MSI writes */
22520c54acb8STomasz Jeznach     memory_region_init_io(&s->trap_mr, OBJECT(dev), &riscv_iommu_trap_ops, s,
22530c54acb8STomasz Jeznach             "riscv-iommu-trap", ~0ULL);
22540c54acb8STomasz Jeznach     address_space_init(&s->trap_as, &s->trap_mr, "riscv-iommu-trap-as");
22550c54acb8STomasz Jeznach }
22560c54acb8STomasz Jeznach 
22570c54acb8STomasz Jeznach static void riscv_iommu_unrealize(DeviceState *dev)
22580c54acb8STomasz Jeznach {
22590c54acb8STomasz Jeznach     RISCVIOMMUState *s = RISCV_IOMMU(dev);
22600c54acb8STomasz Jeznach 
22619d085a1cSTomasz Jeznach     g_hash_table_unref(s->iot_cache);
22620c54acb8STomasz Jeznach     g_hash_table_unref(s->ctx_cache);
22630c54acb8STomasz Jeznach }
22640c54acb8STomasz Jeznach 
2265*9afd2671SDaniel Henrique Barboza void riscv_iommu_reset(RISCVIOMMUState *s)
2266*9afd2671SDaniel Henrique Barboza {
2267*9afd2671SDaniel Henrique Barboza     uint32_t reg_clr;
2268*9afd2671SDaniel Henrique Barboza     int ddtp_mode;
2269*9afd2671SDaniel Henrique Barboza 
2270*9afd2671SDaniel Henrique Barboza     /*
2271*9afd2671SDaniel Henrique Barboza      * Clear DDTP while setting DDTP_mode back to user
2272*9afd2671SDaniel Henrique Barboza      * initial setting.
2273*9afd2671SDaniel Henrique Barboza      */
2274*9afd2671SDaniel Henrique Barboza     ddtp_mode = s->enable_off ?
2275*9afd2671SDaniel Henrique Barboza                 RISCV_IOMMU_DDTP_MODE_OFF : RISCV_IOMMU_DDTP_MODE_BARE;
2276*9afd2671SDaniel Henrique Barboza     s->ddtp = set_field(0, RISCV_IOMMU_DDTP_MODE, ddtp_mode);
2277*9afd2671SDaniel Henrique Barboza     riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_DDTP, s->ddtp);
2278*9afd2671SDaniel Henrique Barboza 
2279*9afd2671SDaniel Henrique Barboza     reg_clr = RISCV_IOMMU_CQCSR_CQEN | RISCV_IOMMU_CQCSR_CIE |
2280*9afd2671SDaniel Henrique Barboza               RISCV_IOMMU_CQCSR_CQON | RISCV_IOMMU_CQCSR_BUSY;
2281*9afd2671SDaniel Henrique Barboza     riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR, 0, reg_clr);
2282*9afd2671SDaniel Henrique Barboza 
2283*9afd2671SDaniel Henrique Barboza     reg_clr = RISCV_IOMMU_FQCSR_FQEN | RISCV_IOMMU_FQCSR_FIE |
2284*9afd2671SDaniel Henrique Barboza               RISCV_IOMMU_FQCSR_FQON | RISCV_IOMMU_FQCSR_BUSY;
2285*9afd2671SDaniel Henrique Barboza     riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_FQCSR, 0, reg_clr);
2286*9afd2671SDaniel Henrique Barboza 
2287*9afd2671SDaniel Henrique Barboza     reg_clr = RISCV_IOMMU_PQCSR_PQEN | RISCV_IOMMU_PQCSR_PIE |
2288*9afd2671SDaniel Henrique Barboza               RISCV_IOMMU_PQCSR_PQON | RISCV_IOMMU_PQCSR_BUSY;
2289*9afd2671SDaniel Henrique Barboza     riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_PQCSR, 0, reg_clr);
2290*9afd2671SDaniel Henrique Barboza 
2291*9afd2671SDaniel Henrique Barboza     riscv_iommu_reg_mod64(s, RISCV_IOMMU_REG_TR_REQ_CTL, 0,
2292*9afd2671SDaniel Henrique Barboza                           RISCV_IOMMU_TR_REQ_CTL_GO_BUSY);
2293*9afd2671SDaniel Henrique Barboza 
2294*9afd2671SDaniel Henrique Barboza     riscv_iommu_reg_set32(s, RISCV_IOMMU_REG_IPSR, 0);
2295*9afd2671SDaniel Henrique Barboza 
2296*9afd2671SDaniel Henrique Barboza     g_hash_table_remove_all(s->ctx_cache);
2297*9afd2671SDaniel Henrique Barboza     g_hash_table_remove_all(s->iot_cache);
2298*9afd2671SDaniel Henrique Barboza }
2299*9afd2671SDaniel Henrique Barboza 
2300766bade2SRichard Henderson static const Property riscv_iommu_properties[] = {
23010c54acb8STomasz Jeznach     DEFINE_PROP_UINT32("version", RISCVIOMMUState, version,
23020c54acb8STomasz Jeznach         RISCV_IOMMU_SPEC_DOT_VER),
23030c54acb8STomasz Jeznach     DEFINE_PROP_UINT32("bus", RISCVIOMMUState, bus, 0x0),
23049d085a1cSTomasz Jeznach     DEFINE_PROP_UINT32("ioatc-limit", RISCVIOMMUState, iot_limit,
23059d085a1cSTomasz Jeznach         LIMIT_CACHE_IOT),
23060c54acb8STomasz Jeznach     DEFINE_PROP_BOOL("intremap", RISCVIOMMUState, enable_msi, TRUE),
230769a9ae48STomasz Jeznach     DEFINE_PROP_BOOL("ats", RISCVIOMMUState, enable_ats, TRUE),
23080c54acb8STomasz Jeznach     DEFINE_PROP_BOOL("off", RISCVIOMMUState, enable_off, TRUE),
23090c54acb8STomasz Jeznach     DEFINE_PROP_BOOL("s-stage", RISCVIOMMUState, enable_s_stage, TRUE),
23100c54acb8STomasz Jeznach     DEFINE_PROP_BOOL("g-stage", RISCVIOMMUState, enable_g_stage, TRUE),
23110c54acb8STomasz Jeznach     DEFINE_PROP_LINK("downstream-mr", RISCVIOMMUState, target_mr,
23120c54acb8STomasz Jeznach         TYPE_MEMORY_REGION, MemoryRegion *),
23130c54acb8STomasz Jeznach     DEFINE_PROP_END_OF_LIST(),
23140c54acb8STomasz Jeznach };
23150c54acb8STomasz Jeznach 
23160c54acb8STomasz Jeznach static void riscv_iommu_class_init(ObjectClass *klass, void* data)
23170c54acb8STomasz Jeznach {
23180c54acb8STomasz Jeznach     DeviceClass *dc = DEVICE_CLASS(klass);
23190c54acb8STomasz Jeznach 
23200c54acb8STomasz Jeznach     /* internal device for riscv-iommu-{pci/sys}, not user-creatable */
23210c54acb8STomasz Jeznach     dc->user_creatable = false;
23220c54acb8STomasz Jeznach     dc->realize = riscv_iommu_realize;
23230c54acb8STomasz Jeznach     dc->unrealize = riscv_iommu_unrealize;
23240c54acb8STomasz Jeznach     device_class_set_props(dc, riscv_iommu_properties);
23250c54acb8STomasz Jeznach }
23260c54acb8STomasz Jeznach 
23270c54acb8STomasz Jeznach static const TypeInfo riscv_iommu_info = {
23280c54acb8STomasz Jeznach     .name = TYPE_RISCV_IOMMU,
23290c54acb8STomasz Jeznach     .parent = TYPE_DEVICE,
23300c54acb8STomasz Jeznach     .instance_size = sizeof(RISCVIOMMUState),
23314876e6f7SDaniel Henrique Barboza     .instance_init = riscv_iommu_instance_init,
23320c54acb8STomasz Jeznach     .class_init = riscv_iommu_class_init,
23330c54acb8STomasz Jeznach };
23340c54acb8STomasz Jeznach 
23350c54acb8STomasz Jeznach static const char *IOMMU_FLAG_STR[] = {
23360c54acb8STomasz Jeznach     "NA",
23370c54acb8STomasz Jeznach     "RO",
23380c54acb8STomasz Jeznach     "WR",
23390c54acb8STomasz Jeznach     "RW",
23400c54acb8STomasz Jeznach };
23410c54acb8STomasz Jeznach 
23420c54acb8STomasz Jeznach /* RISC-V IOMMU Memory Region - Address Translation Space */
23430c54acb8STomasz Jeznach static IOMMUTLBEntry riscv_iommu_memory_region_translate(
23440c54acb8STomasz Jeznach     IOMMUMemoryRegion *iommu_mr, hwaddr addr,
23450c54acb8STomasz Jeznach     IOMMUAccessFlags flag, int iommu_idx)
23460c54acb8STomasz Jeznach {
23470c54acb8STomasz Jeznach     RISCVIOMMUSpace *as = container_of(iommu_mr, RISCVIOMMUSpace, iova_mr);
23480c54acb8STomasz Jeznach     RISCVIOMMUContext *ctx;
23490c54acb8STomasz Jeznach     void *ref;
23500c54acb8STomasz Jeznach     IOMMUTLBEntry iotlb = {
23510c54acb8STomasz Jeznach         .iova = addr,
23520c54acb8STomasz Jeznach         .target_as = as->iommu->target_as,
23530c54acb8STomasz Jeznach         .addr_mask = ~0ULL,
23540c54acb8STomasz Jeznach         .perm = flag,
23550c54acb8STomasz Jeznach     };
23560c54acb8STomasz Jeznach 
23570c54acb8STomasz Jeznach     ctx = riscv_iommu_ctx(as->iommu, as->devid, iommu_idx, &ref);
23580c54acb8STomasz Jeznach     if (ctx == NULL) {
23590c54acb8STomasz Jeznach         /* Translation disabled or invalid. */
23600c54acb8STomasz Jeznach         iotlb.addr_mask = 0;
23610c54acb8STomasz Jeznach         iotlb.perm = IOMMU_NONE;
23629d085a1cSTomasz Jeznach     } else if (riscv_iommu_translate(as->iommu, ctx, &iotlb, true)) {
23630c54acb8STomasz Jeznach         /* Translation disabled or fault reported. */
23640c54acb8STomasz Jeznach         iotlb.addr_mask = 0;
23650c54acb8STomasz Jeznach         iotlb.perm = IOMMU_NONE;
23660c54acb8STomasz Jeznach     }
23670c54acb8STomasz Jeznach 
23680c54acb8STomasz Jeznach     /* Trace all dma translations with original access flags. */
23690c54acb8STomasz Jeznach     trace_riscv_iommu_dma(as->iommu->parent_obj.id, PCI_BUS_NUM(as->devid),
23700c54acb8STomasz Jeznach                           PCI_SLOT(as->devid), PCI_FUNC(as->devid), iommu_idx,
23710c54acb8STomasz Jeznach                           IOMMU_FLAG_STR[flag & IOMMU_RW], iotlb.iova,
23720c54acb8STomasz Jeznach                           iotlb.translated_addr);
23730c54acb8STomasz Jeznach 
23740c54acb8STomasz Jeznach     riscv_iommu_ctx_put(as->iommu, ref);
23750c54acb8STomasz Jeznach 
23760c54acb8STomasz Jeznach     return iotlb;
23770c54acb8STomasz Jeznach }
23780c54acb8STomasz Jeznach 
23790c54acb8STomasz Jeznach static int riscv_iommu_memory_region_notify(
23800c54acb8STomasz Jeznach     IOMMUMemoryRegion *iommu_mr, IOMMUNotifierFlag old,
23810c54acb8STomasz Jeznach     IOMMUNotifierFlag new, Error **errp)
23820c54acb8STomasz Jeznach {
23830c54acb8STomasz Jeznach     RISCVIOMMUSpace *as = container_of(iommu_mr, RISCVIOMMUSpace, iova_mr);
23840c54acb8STomasz Jeznach 
23850c54acb8STomasz Jeznach     if (old == IOMMU_NOTIFIER_NONE) {
23860c54acb8STomasz Jeznach         as->notifier = true;
23870c54acb8STomasz Jeznach         trace_riscv_iommu_notifier_add(iommu_mr->parent_obj.name);
23880c54acb8STomasz Jeznach     } else if (new == IOMMU_NOTIFIER_NONE) {
23890c54acb8STomasz Jeznach         as->notifier = false;
23900c54acb8STomasz Jeznach         trace_riscv_iommu_notifier_del(iommu_mr->parent_obj.name);
23910c54acb8STomasz Jeznach     }
23920c54acb8STomasz Jeznach 
23930c54acb8STomasz Jeznach     return 0;
23940c54acb8STomasz Jeznach }
23950c54acb8STomasz Jeznach 
23960c54acb8STomasz Jeznach static inline bool pci_is_iommu(PCIDevice *pdev)
23970c54acb8STomasz Jeznach {
23980c54acb8STomasz Jeznach     return pci_get_word(pdev->config + PCI_CLASS_DEVICE) == 0x0806;
23990c54acb8STomasz Jeznach }
24000c54acb8STomasz Jeznach 
24010c54acb8STomasz Jeznach static AddressSpace *riscv_iommu_find_as(PCIBus *bus, void *opaque, int devfn)
24020c54acb8STomasz Jeznach {
24030c54acb8STomasz Jeznach     RISCVIOMMUState *s = (RISCVIOMMUState *) opaque;
24040c54acb8STomasz Jeznach     PCIDevice *pdev = pci_find_device(bus, pci_bus_num(bus), devfn);
24050c54acb8STomasz Jeznach     AddressSpace *as = NULL;
24060c54acb8STomasz Jeznach 
24070c54acb8STomasz Jeznach     if (pdev && pci_is_iommu(pdev)) {
24080c54acb8STomasz Jeznach         return s->target_as;
24090c54acb8STomasz Jeznach     }
24100c54acb8STomasz Jeznach 
24110c54acb8STomasz Jeznach     /* Find first registered IOMMU device */
24120c54acb8STomasz Jeznach     while (s->iommus.le_prev) {
24130c54acb8STomasz Jeznach         s = *(s->iommus.le_prev);
24140c54acb8STomasz Jeznach     }
24150c54acb8STomasz Jeznach 
24160c54acb8STomasz Jeznach     /* Find first matching IOMMU */
24170c54acb8STomasz Jeznach     while (s != NULL && as == NULL) {
24180c54acb8STomasz Jeznach         as = riscv_iommu_space(s, PCI_BUILD_BDF(pci_bus_num(bus), devfn));
24190c54acb8STomasz Jeznach         s = s->iommus.le_next;
24200c54acb8STomasz Jeznach     }
24210c54acb8STomasz Jeznach 
24220c54acb8STomasz Jeznach     return as ? as : &address_space_memory;
24230c54acb8STomasz Jeznach }
24240c54acb8STomasz Jeznach 
24250c54acb8STomasz Jeznach static const PCIIOMMUOps riscv_iommu_ops = {
24260c54acb8STomasz Jeznach     .get_address_space = riscv_iommu_find_as,
24270c54acb8STomasz Jeznach };
24280c54acb8STomasz Jeznach 
24290c54acb8STomasz Jeznach void riscv_iommu_pci_setup_iommu(RISCVIOMMUState *iommu, PCIBus *bus,
24300c54acb8STomasz Jeznach         Error **errp)
24310c54acb8STomasz Jeznach {
24320c54acb8STomasz Jeznach     if (bus->iommu_ops &&
24330c54acb8STomasz Jeznach         bus->iommu_ops->get_address_space == riscv_iommu_find_as) {
24340c54acb8STomasz Jeznach         /* Allow multiple IOMMUs on the same PCIe bus, link known devices */
24350c54acb8STomasz Jeznach         RISCVIOMMUState *last = (RISCVIOMMUState *)bus->iommu_opaque;
24360c54acb8STomasz Jeznach         QLIST_INSERT_AFTER(last, iommu, iommus);
24370c54acb8STomasz Jeznach     } else if (!bus->iommu_ops && !bus->iommu_opaque) {
24380c54acb8STomasz Jeznach         pci_setup_iommu(bus, &riscv_iommu_ops, iommu);
24390c54acb8STomasz Jeznach     } else {
24400c54acb8STomasz Jeznach         error_setg(errp, "can't register secondary IOMMU for PCI bus #%d",
24410c54acb8STomasz Jeznach             pci_bus_num(bus));
24420c54acb8STomasz Jeznach     }
24430c54acb8STomasz Jeznach }
24440c54acb8STomasz Jeznach 
24450c54acb8STomasz Jeznach static int riscv_iommu_memory_region_index(IOMMUMemoryRegion *iommu_mr,
24460c54acb8STomasz Jeznach     MemTxAttrs attrs)
24470c54acb8STomasz Jeznach {
24480c54acb8STomasz Jeznach     return attrs.unspecified ? RISCV_IOMMU_NOPROCID : (int)attrs.pid;
24490c54acb8STomasz Jeznach }
24500c54acb8STomasz Jeznach 
24510c54acb8STomasz Jeznach static int riscv_iommu_memory_region_index_len(IOMMUMemoryRegion *iommu_mr)
24520c54acb8STomasz Jeznach {
24530c54acb8STomasz Jeznach     RISCVIOMMUSpace *as = container_of(iommu_mr, RISCVIOMMUSpace, iova_mr);
24540c54acb8STomasz Jeznach     return 1 << as->iommu->pid_bits;
24550c54acb8STomasz Jeznach }
24560c54acb8STomasz Jeznach 
24570c54acb8STomasz Jeznach static void riscv_iommu_memory_region_init(ObjectClass *klass, void *data)
24580c54acb8STomasz Jeznach {
24590c54acb8STomasz Jeznach     IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass);
24600c54acb8STomasz Jeznach 
24610c54acb8STomasz Jeznach     imrc->translate = riscv_iommu_memory_region_translate;
24620c54acb8STomasz Jeznach     imrc->notify_flag_changed = riscv_iommu_memory_region_notify;
24630c54acb8STomasz Jeznach     imrc->attrs_to_index = riscv_iommu_memory_region_index;
24640c54acb8STomasz Jeznach     imrc->num_indexes = riscv_iommu_memory_region_index_len;
24650c54acb8STomasz Jeznach }
24660c54acb8STomasz Jeznach 
24670c54acb8STomasz Jeznach static const TypeInfo riscv_iommu_memory_region_info = {
24680c54acb8STomasz Jeznach     .parent = TYPE_IOMMU_MEMORY_REGION,
24690c54acb8STomasz Jeznach     .name = TYPE_RISCV_IOMMU_MEMORY_REGION,
24700c54acb8STomasz Jeznach     .class_init = riscv_iommu_memory_region_init,
24710c54acb8STomasz Jeznach };
24720c54acb8STomasz Jeznach 
24730c54acb8STomasz Jeznach static void riscv_iommu_register_mr_types(void)
24740c54acb8STomasz Jeznach {
24750c54acb8STomasz Jeznach     type_register_static(&riscv_iommu_memory_region_info);
24760c54acb8STomasz Jeznach     type_register_static(&riscv_iommu_info);
24770c54acb8STomasz Jeznach }
24780c54acb8STomasz Jeznach 
24790c54acb8STomasz Jeznach type_init(riscv_iommu_register_mr_types);
2480