xref: /openbmc/qemu/hw/riscv/riscv-iommu-pci.c (revision b9b28326)
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