1*b9b28326STomasz Jeznach /*
2*b9b28326STomasz Jeznach * QEMU emulation of an RISC-V IOMMU
3*b9b28326STomasz Jeznach *
4*b9b28326STomasz Jeznach * Copyright (C) 2022-2023 Rivos Inc.
5*b9b28326STomasz Jeznach *
6*b9b28326STomasz Jeznach * This program is free software; you can redistribute it and/or modify it
7*b9b28326STomasz Jeznach * under the terms and conditions of the GNU General Public License,
8*b9b28326STomasz Jeznach * version 2 or later, as published by the Free Software Foundation.
9*b9b28326STomasz Jeznach *
10*b9b28326STomasz Jeznach * This program is distributed in the hope that it will be useful,
11*b9b28326STomasz Jeznach * but WITHOUT ANY WARRANTY; without even the implied warranty of
12*b9b28326STomasz Jeznach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13*b9b28326STomasz Jeznach * GNU General Public License for more details.
14*b9b28326STomasz Jeznach *
15*b9b28326STomasz Jeznach * You should have received a copy of the GNU General Public License along
16*b9b28326STomasz Jeznach * with this program; if not, see <http://www.gnu.org/licenses/>.
17*b9b28326STomasz Jeznach */
18*b9b28326STomasz Jeznach
19*b9b28326STomasz Jeznach #include "qemu/osdep.h"
20*b9b28326STomasz Jeznach #include "hw/pci/msi.h"
21*b9b28326STomasz Jeznach #include "hw/pci/msix.h"
22*b9b28326STomasz Jeznach #include "hw/pci/pci_bus.h"
23*b9b28326STomasz Jeznach #include "hw/qdev-properties.h"
24*b9b28326STomasz Jeznach #include "hw/riscv/riscv_hart.h"
25*b9b28326STomasz Jeznach #include "migration/vmstate.h"
26*b9b28326STomasz Jeznach #include "qapi/error.h"
27*b9b28326STomasz Jeznach #include "qemu/error-report.h"
28*b9b28326STomasz Jeznach #include "qemu/host-utils.h"
29*b9b28326STomasz Jeznach #include "qom/object.h"
30*b9b28326STomasz Jeznach
31*b9b28326STomasz Jeznach #include "cpu_bits.h"
32*b9b28326STomasz Jeznach #include "riscv-iommu.h"
33*b9b28326STomasz Jeznach #include "riscv-iommu-bits.h"
34*b9b28326STomasz Jeznach
35*b9b28326STomasz Jeznach /* RISC-V IOMMU PCI Device Emulation */
36*b9b28326STomasz Jeznach #define RISCV_PCI_CLASS_SYSTEM_IOMMU 0x0806
37*b9b28326STomasz Jeznach
38*b9b28326STomasz Jeznach /*
39*b9b28326STomasz Jeznach * 4 MSIx vectors for ICVEC, one for MRIF. The spec mentions in
40*b9b28326STomasz Jeznach * the "Placement and data flow" section that:
41*b9b28326STomasz Jeznach *
42*b9b28326STomasz Jeznach * "The interfaces related to recording an incoming MSI in a memory-resident
43*b9b28326STomasz Jeznach * interrupt file (MRIF) are implementation-specific. The partitioning of
44*b9b28326STomasz Jeznach * responsibility between the IOMMU and the IO bridge for recording the
45*b9b28326STomasz Jeznach * incoming MSI in an MRIF and generating the associated notice MSI are
46*b9b28326STomasz Jeznach * implementation-specific."
47*b9b28326STomasz Jeznach *
48*b9b28326STomasz Jeznach * We're making a design decision to create the MSIx for MRIF in the
49*b9b28326STomasz Jeznach * IOMMU MSIx emulation.
50*b9b28326STomasz Jeznach */
51*b9b28326STomasz Jeznach #define RISCV_IOMMU_PCI_MSIX_VECTORS 5
52*b9b28326STomasz Jeznach
53*b9b28326STomasz Jeznach /*
54*b9b28326STomasz Jeznach * 4 vectors that can be used by civ, fiv, pmiv and piv. Number of
55*b9b28326STomasz Jeznach * vectors is represented by 2^N, where N = number of writable bits
56*b9b28326STomasz Jeznach * in each cause. For 4 vectors we'll write 0b11 (3) in each reg.
57*b9b28326STomasz Jeznach */
58*b9b28326STomasz Jeznach #define RISCV_IOMMU_PCI_ICVEC_VECTORS 0x3333
59*b9b28326STomasz Jeznach
60*b9b28326STomasz Jeznach typedef struct RISCVIOMMUStatePci {
61*b9b28326STomasz Jeznach PCIDevice pci; /* Parent PCIe device state */
62*b9b28326STomasz Jeznach uint16_t vendor_id;
63*b9b28326STomasz Jeznach uint16_t device_id;
64*b9b28326STomasz Jeznach uint8_t revision;
65*b9b28326STomasz Jeznach MemoryRegion bar0; /* PCI BAR (including MSI-x config) */
66*b9b28326STomasz Jeznach RISCVIOMMUState iommu; /* common IOMMU state */
67*b9b28326STomasz Jeznach } RISCVIOMMUStatePci;
68*b9b28326STomasz Jeznach
69*b9b28326STomasz Jeznach /* interrupt delivery callback */
riscv_iommu_pci_notify(RISCVIOMMUState * iommu,unsigned vector)70*b9b28326STomasz Jeznach static void riscv_iommu_pci_notify(RISCVIOMMUState *iommu, unsigned vector)
71*b9b28326STomasz Jeznach {
72*b9b28326STomasz Jeznach RISCVIOMMUStatePci *s = container_of(iommu, RISCVIOMMUStatePci, iommu);
73*b9b28326STomasz Jeznach
74*b9b28326STomasz Jeznach if (msix_enabled(&(s->pci))) {
75*b9b28326STomasz Jeznach msix_notify(&(s->pci), vector);
76*b9b28326STomasz Jeznach }
77*b9b28326STomasz Jeznach }
78*b9b28326STomasz Jeznach
riscv_iommu_pci_realize(PCIDevice * dev,Error ** errp)79*b9b28326STomasz Jeznach static void riscv_iommu_pci_realize(PCIDevice *dev, Error **errp)
80*b9b28326STomasz Jeznach {
81*b9b28326STomasz Jeznach RISCVIOMMUStatePci *s = DO_UPCAST(RISCVIOMMUStatePci, pci, dev);
82*b9b28326STomasz Jeznach RISCVIOMMUState *iommu = &s->iommu;
83*b9b28326STomasz Jeznach uint8_t *pci_conf = dev->config;
84*b9b28326STomasz Jeznach Error *err = NULL;
85*b9b28326STomasz Jeznach
86*b9b28326STomasz Jeznach pci_set_word(pci_conf + PCI_VENDOR_ID, s->vendor_id);
87*b9b28326STomasz Jeznach pci_set_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID, s->vendor_id);
88*b9b28326STomasz Jeznach pci_set_word(pci_conf + PCI_DEVICE_ID, s->device_id);
89*b9b28326STomasz Jeznach pci_set_word(pci_conf + PCI_SUBSYSTEM_ID, s->device_id);
90*b9b28326STomasz Jeznach pci_set_byte(pci_conf + PCI_REVISION_ID, s->revision);
91*b9b28326STomasz Jeznach
92*b9b28326STomasz Jeznach /* Set device id for trace / debug */
93*b9b28326STomasz Jeznach DEVICE(iommu)->id = g_strdup_printf("%02x:%02x.%01x",
94*b9b28326STomasz Jeznach pci_dev_bus_num(dev), PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
95*b9b28326STomasz Jeznach qdev_realize(DEVICE(iommu), NULL, errp);
96*b9b28326STomasz Jeznach
97*b9b28326STomasz Jeznach memory_region_init(&s->bar0, OBJECT(s), "riscv-iommu-bar0",
98*b9b28326STomasz Jeznach QEMU_ALIGN_UP(memory_region_size(&iommu->regs_mr), TARGET_PAGE_SIZE));
99*b9b28326STomasz Jeznach memory_region_add_subregion(&s->bar0, 0, &iommu->regs_mr);
100*b9b28326STomasz Jeznach
101*b9b28326STomasz Jeznach pcie_endpoint_cap_init(dev, 0);
102*b9b28326STomasz Jeznach
103*b9b28326STomasz Jeznach pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY |
104*b9b28326STomasz Jeznach PCI_BASE_ADDRESS_MEM_TYPE_64, &s->bar0);
105*b9b28326STomasz Jeznach
106*b9b28326STomasz Jeznach int ret = msix_init(dev, RISCV_IOMMU_PCI_MSIX_VECTORS,
107*b9b28326STomasz Jeznach &s->bar0, 0, RISCV_IOMMU_REG_MSI_CONFIG,
108*b9b28326STomasz Jeznach &s->bar0, 0, RISCV_IOMMU_REG_MSI_CONFIG + 256, 0, &err);
109*b9b28326STomasz Jeznach
110*b9b28326STomasz Jeznach if (ret == -ENOTSUP) {
111*b9b28326STomasz Jeznach /*
112*b9b28326STomasz Jeznach * MSI-x is not supported by the platform.
113*b9b28326STomasz Jeznach * Driver should use timer/polling based notification handlers.
114*b9b28326STomasz Jeznach */
115*b9b28326STomasz Jeznach warn_report_err(err);
116*b9b28326STomasz Jeznach } else if (ret < 0) {
117*b9b28326STomasz Jeznach error_propagate(errp, err);
118*b9b28326STomasz Jeznach return;
119*b9b28326STomasz Jeznach } else {
120*b9b28326STomasz Jeznach /* Mark all ICVEC MSIx vectors as used */
121*b9b28326STomasz Jeznach for (int i = 0; i < RISCV_IOMMU_PCI_MSIX_VECTORS; i++) {
122*b9b28326STomasz Jeznach msix_vector_use(dev, i);
123*b9b28326STomasz Jeznach }
124*b9b28326STomasz Jeznach
125*b9b28326STomasz Jeznach iommu->notify = riscv_iommu_pci_notify;
126*b9b28326STomasz Jeznach }
127*b9b28326STomasz Jeznach
128*b9b28326STomasz Jeznach PCIBus *bus = pci_device_root_bus(dev);
129*b9b28326STomasz Jeznach if (!bus) {
130*b9b28326STomasz Jeznach error_setg(errp, "can't find PCIe root port for %02x:%02x.%x",
131*b9b28326STomasz Jeznach pci_bus_num(pci_get_bus(dev)), PCI_SLOT(dev->devfn),
132*b9b28326STomasz Jeznach PCI_FUNC(dev->devfn));
133*b9b28326STomasz Jeznach return;
134*b9b28326STomasz Jeznach }
135*b9b28326STomasz Jeznach
136*b9b28326STomasz Jeznach riscv_iommu_pci_setup_iommu(iommu, bus, errp);
137*b9b28326STomasz Jeznach }
138*b9b28326STomasz Jeznach
riscv_iommu_pci_exit(PCIDevice * pci_dev)139*b9b28326STomasz Jeznach static void riscv_iommu_pci_exit(PCIDevice *pci_dev)
140*b9b28326STomasz Jeznach {
141*b9b28326STomasz Jeznach pci_setup_iommu(pci_device_root_bus(pci_dev), NULL, NULL);
142*b9b28326STomasz Jeznach }
143*b9b28326STomasz Jeznach
144*b9b28326STomasz Jeznach static const VMStateDescription riscv_iommu_vmstate = {
145*b9b28326STomasz Jeznach .name = "riscv-iommu",
146*b9b28326STomasz Jeznach .unmigratable = 1
147*b9b28326STomasz Jeznach };
148*b9b28326STomasz Jeznach
riscv_iommu_pci_init(Object * obj)149*b9b28326STomasz Jeznach static void riscv_iommu_pci_init(Object *obj)
150*b9b28326STomasz Jeznach {
151*b9b28326STomasz Jeznach RISCVIOMMUStatePci *s = RISCV_IOMMU_PCI(obj);
152*b9b28326STomasz Jeznach RISCVIOMMUState *iommu = &s->iommu;
153*b9b28326STomasz Jeznach
154*b9b28326STomasz Jeznach object_initialize_child(obj, "iommu", iommu, TYPE_RISCV_IOMMU);
155*b9b28326STomasz Jeznach qdev_alias_all_properties(DEVICE(iommu), obj);
156*b9b28326STomasz Jeznach
157*b9b28326STomasz Jeznach iommu->icvec_avail_vectors = RISCV_IOMMU_PCI_ICVEC_VECTORS;
158*b9b28326STomasz Jeznach }
159*b9b28326STomasz Jeznach
160*b9b28326STomasz Jeznach static Property riscv_iommu_pci_properties[] = {
161*b9b28326STomasz Jeznach DEFINE_PROP_UINT16("vendor-id", RISCVIOMMUStatePci, vendor_id,
162*b9b28326STomasz Jeznach PCI_VENDOR_ID_REDHAT),
163*b9b28326STomasz Jeznach DEFINE_PROP_UINT16("device-id", RISCVIOMMUStatePci, device_id,
164*b9b28326STomasz Jeznach PCI_DEVICE_ID_REDHAT_RISCV_IOMMU),
165*b9b28326STomasz Jeznach DEFINE_PROP_UINT8("revision", RISCVIOMMUStatePci, revision, 0x01),
166*b9b28326STomasz Jeznach DEFINE_PROP_END_OF_LIST(),
167*b9b28326STomasz Jeznach };
168*b9b28326STomasz Jeznach
riscv_iommu_pci_class_init(ObjectClass * klass,void * data)169*b9b28326STomasz Jeznach static void riscv_iommu_pci_class_init(ObjectClass *klass, void *data)
170*b9b28326STomasz Jeznach {
171*b9b28326STomasz Jeznach DeviceClass *dc = DEVICE_CLASS(klass);
172*b9b28326STomasz Jeznach PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
173*b9b28326STomasz Jeznach
174*b9b28326STomasz Jeznach k->realize = riscv_iommu_pci_realize;
175*b9b28326STomasz Jeznach k->exit = riscv_iommu_pci_exit;
176*b9b28326STomasz Jeznach k->class_id = RISCV_PCI_CLASS_SYSTEM_IOMMU;
177*b9b28326STomasz Jeznach dc->desc = "RISCV-IOMMU DMA Remapping device";
178*b9b28326STomasz Jeznach dc->vmsd = &riscv_iommu_vmstate;
179*b9b28326STomasz Jeznach dc->hotpluggable = false;
180*b9b28326STomasz Jeznach dc->user_creatable = true;
181*b9b28326STomasz Jeznach set_bit(DEVICE_CATEGORY_MISC, dc->categories);
182*b9b28326STomasz Jeznach device_class_set_props(dc, riscv_iommu_pci_properties);
183*b9b28326STomasz Jeznach }
184*b9b28326STomasz Jeznach
185*b9b28326STomasz Jeznach static const TypeInfo riscv_iommu_pci = {
186*b9b28326STomasz Jeznach .name = TYPE_RISCV_IOMMU_PCI,
187*b9b28326STomasz Jeznach .parent = TYPE_PCI_DEVICE,
188*b9b28326STomasz Jeznach .class_init = riscv_iommu_pci_class_init,
189*b9b28326STomasz Jeznach .instance_init = riscv_iommu_pci_init,
190*b9b28326STomasz Jeznach .instance_size = sizeof(RISCVIOMMUStatePci),
191*b9b28326STomasz Jeznach .interfaces = (InterfaceInfo[]) {
192*b9b28326STomasz Jeznach { INTERFACE_PCIE_DEVICE },
193*b9b28326STomasz Jeznach { },
194*b9b28326STomasz Jeznach },
195*b9b28326STomasz Jeznach };
196*b9b28326STomasz Jeznach
riscv_iommu_register_pci_types(void)197*b9b28326STomasz Jeznach static void riscv_iommu_register_pci_types(void)
198*b9b28326STomasz Jeznach {
199*b9b28326STomasz Jeznach type_register_static(&riscv_iommu_pci);
200*b9b28326STomasz Jeznach }
201*b9b28326STomasz Jeznach
202*b9b28326STomasz Jeznach type_init(riscv_iommu_register_pci_types);
203