From: Mirsad Ostrakovic <mirsad_ostrakovic@epam.com>
Until now SMMU could only be created for PCIe bus.
This patch adds support to assotiate SMMU with simple
bus in case SMMU created with type: 'generic-primary-bus'.
The 'primary-bus' is now renamed to 'pci-primary-bus'
to avoid confusion.
Signed-off-by: Mirsad Ostrakovic <mirsad_ostrakovic@epam.com>
Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
hw/arm/smmu-common.c | 69 ++++++++++++++++++++++++++++++------
hw/arm/virt.c | 2 +-
hw/core/bus.c | 13 +++++++
include/hw/arm/smmu-common.h | 22 +++++++++---
include/hw/qdev-core.h | 12 +++++++
5 files changed, 103 insertions(+), 15 deletions(-)
diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c
index 62a7612184..52ef49d7f6 100644
--- a/hw/arm/smmu-common.c
+++ b/hw/arm/smmu-common.c
@@ -25,6 +25,7 @@
#include "qapi/error.h"
#include "qemu/jhash.h"
#include "qemu/module.h"
+#include "hw/qdev-core.h"
#include "qemu/error-report.h"
#include "hw/arm/smmu-common.h"
@@ -824,7 +825,7 @@ SMMUTLBEntry *smmu_translate(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t addr,
/**
* The bus number is used for lookup when SID based invalidation occurs.
* In that case we lazily populate the SMMUPciBus array from the bus hash
- * table. At the time the SMMUPciBus is created (smmu_find_add_as), the bus
+ * table. At the time the SMMUPciBus is created (pci_smmu_find_add_as), the bus
* numbers may not be always initialized yet.
*/
SMMUPciBus *smmu_find_smmu_pcibus(SMMUState *s, uint8_t bus_num)
@@ -847,7 +848,7 @@ SMMUPciBus *smmu_find_smmu_pcibus(SMMUState *s, uint8_t bus_num)
return NULL;
}
-static AddressSpace *smmu_find_add_as(PCIBus *bus, void *opaque, int devfn)
+static AddressSpace *pci_smmu_find_add_as(PCIBus *bus, void *opaque, int devfn)
{
SMMUState *s = opaque;
SMMUPciBus *sbus = g_hash_table_lookup(s->smmu_pcibus_by_busptr, bus);
@@ -870,6 +871,7 @@ static AddressSpace *smmu_find_add_as(PCIBus *bus, void *opaque, int devfn)
sdev->smmu = s;
sdev->bus = bus;
sdev->devfn = devfn;
+ sdev->pcie_device = true;
memory_region_init_iommu(&sdev->iommu, sizeof(sdev->iommu),
s->mrtypename,
@@ -883,8 +885,48 @@ static AddressSpace *smmu_find_add_as(PCIBus *bus, void *opaque, int devfn)
return &sdev->as;
}
-static const PCIIOMMUOps smmu_ops = {
- .get_address_space = smmu_find_add_as,
+static AddressSpace *bus_smmu_find_add_as(BusState *bus, void *opaque, int devid)
+{
+ SMMUState *s = opaque;
+ SMMUBus *sbus = g_hash_table_lookup(s->smmu_bus_by_busptr, bus);
+ SMMUDevice *sdev;
+ static unsigned int index;
+
+ if (!sbus) {
+ sbus = g_malloc0(sizeof(SMMUBus) +
+ sizeof(SMMUDevice *) * SMMU_DEVID_MAX);
+ sbus->bus = bus;
+ g_hash_table_insert(s->smmu_bus_by_busptr, bus, sbus);
+ }
+
+ sdev = sbus->pbdev[devid];
+ if (!sdev) {
+ char *name = g_strdup_printf("%s-%d-%d", s->mrtypename, devid, index++);
+
+ sdev = sbus->pbdev[devid] = g_new0(SMMUDevice, 1);
+
+ sdev->smmu = s;
+ sdev->bus = bus;
+ sdev->devfn = devid;
+
+ memory_region_init_iommu(&sdev->iommu, sizeof(sdev->iommu),
+ s->mrtypename,
+ OBJECT(s), name, UINT64_MAX);
+ address_space_init(&sdev->as,
+ MEMORY_REGION(&sdev->iommu), name);
+ trace_smmu_add_mr(name);
+ g_free(name);
+ }
+
+ return &sdev->as;
+}
+
+static const PCIIOMMUOps pci_smmu_ops = {
+ .get_address_space = pci_smmu_find_add_as,
+};
+
+static const BusIOMMUOps bus_smmu_ops = {
+ .get_address_space = bus_smmu_find_add_as,
};
SMMUDevice *smmu_find_sdev(SMMUState *s, uint32_t sid)
@@ -926,7 +968,7 @@ static void smmu_base_realize(DeviceState *dev, Error **errp)
{
SMMUState *s = ARM_SMMU(dev);
SMMUBaseClass *sbc = ARM_SMMU_GET_CLASS(dev);
- PCIBus *pci_bus = s->primary_bus;
+ PCIBus *pci_bus = s->pci_primary_bus;
Error *local_err = NULL;
sbc->parent_realize(dev, &local_err);
@@ -939,6 +981,11 @@ static void smmu_base_realize(DeviceState *dev, Error **errp)
g_free, g_free);
s->smmu_pcibus_by_busptr = g_hash_table_new(NULL, NULL);
+ if (s->generic_primary_bus ) {
+ bus_setup_iommu(s->generic_primary_bus, &bus_smmu_ops, s);
+ return;
+ }
+
if (!pci_bus) {
error_setg(errp, "SMMU is not attached to any PCI bus!");
return;
@@ -962,10 +1009,10 @@ static void smmu_base_realize(DeviceState *dev, Error **errp)
}
}
- if (s->smmu_per_bus) {
- pci_setup_iommu_per_bus(pci_bus, &smmu_ops, s);
+ if (s->pci_smmu_per_bus) {
+ pci_setup_iommu_per_bus(pci_bus, &pci_smmu_ops, s);
} else {
- pci_setup_iommu(pci_bus, &smmu_ops, s);
+ pci_setup_iommu(pci_bus, &pci_smmu_ops, s);
}
return;
}
@@ -991,9 +1038,11 @@ static void smmu_base_reset_exit(Object *obj, ResetType type)
static const Property smmu_dev_properties[] = {
DEFINE_PROP_UINT8("bus_num", SMMUState, bus_num, 0),
- DEFINE_PROP_BOOL("smmu_per_bus", SMMUState, smmu_per_bus, false),
- DEFINE_PROP_LINK("primary-bus", SMMUState, primary_bus,
+ DEFINE_PROP_BOOL("pci_smmu_per_bus", SMMUState, pci_smmu_per_bus, false),
+ DEFINE_PROP_LINK("pci-primary-bus", SMMUState, pci_primary_bus,
TYPE_PCI_BUS, PCIBus *),
+ DEFINE_PROP_LINK("generic-primary-bus", SMMUState, generic_primary_bus,
+ TYPE_BUS, BusState *),
};
static void smmu_base_class_init(ObjectClass *klass, const void *data)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 25fb2bab56..d9d7b982b3 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -3050,7 +3050,7 @@ static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
"iommu=smmuv3" : "virtio-iommu");
} else if (vms->iommu == VIRT_IOMMU_NONE) {
/* The new SMMUv3 device is specific to the PCI bus */
- object_property_set_bool(OBJECT(dev), "smmu_per_bus", true, NULL);
+ object_property_set_bool(OBJECT(dev), "pci_smmu_per_bus", true, NULL);
}
}
}
diff --git a/hw/core/bus.c b/hw/core/bus.c
index bddfc22d38..6d1483fdbd 100644
--- a/hw/core/bus.c
+++ b/hw/core/bus.c
@@ -80,6 +80,19 @@ bool bus_is_in_reset(BusState *bus)
return resettable_is_in_reset(OBJECT(bus));
}
+void bus_setup_iommu(BusState *bus, const BusIOMMUOps *ops, void *opaque)
+{
+ /*
+ * If called, bus_setup_iommu() should provide a minimum set of
+ * useful callbacks for the bus.
+ */
+ assert(ops);
+ assert(ops->get_address_space);
+
+ bus->iommu_ops = ops;
+ bus->iommu_opaque = opaque;
+}
+
static ResettableState *bus_get_reset_state(Object *obj)
{
BusState *bus = BUS(obj);
diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h
index 80d0fecfde..670ae46930 100644
--- a/include/hw/arm/smmu-common.h
+++ b/include/hw/arm/smmu-common.h
@@ -27,6 +27,8 @@
#define SMMU_PCI_DEVFN_MAX 256
#define SMMU_PCI_DEVFN(sid) (sid & 0xFF)
+#define SMMU_DEVID_MAX 256
+
/* VMSAv8-64 Translation constants and functions */
#define VMSA_LEVELS 4
#define VMSA_MAX_S2_CONCAT 16
@@ -120,12 +122,13 @@ typedef struct SMMUTransCfg {
typedef struct SMMUDevice {
void *smmu;
- PCIBus *bus;
+ void *bus;
int devfn;
IOMMUMemoryRegion iommu;
AddressSpace as;
uint32_t cfg_cache_hits;
uint32_t cfg_cache_misses;
+ bool pcie_device;
QLIST_ENTRY(SMMUDevice) next;
} SMMUDevice;
@@ -134,6 +137,11 @@ typedef struct SMMUPciBus {
SMMUDevice *pbdev[]; /* Parent array is sparse, so dynamically alloc */
} SMMUPciBus;
+typedef struct SMMUBus {
+ BusState *bus;
+ SMMUDevice *pbdev[]; /* Parent array is sparse, so dynamically alloc */
+} SMMUBus;
+
typedef struct SMMUIOTLBKey {
uint64_t iova;
int asid;
@@ -154,14 +162,16 @@ struct SMMUState {
MemoryRegion iomem;
GHashTable *smmu_pcibus_by_busptr;
+ GHashTable *smmu_bus_by_busptr;
GHashTable *configs; /* cache for configuration data */
GHashTable *iotlb;
SMMUPciBus *smmu_pcibus_by_bus_num[SMMU_PCI_BUS_MAX];
PCIBus *pci_bus;
QLIST_HEAD(, SMMUDevice) devices_with_notifiers;
uint8_t bus_num;
- PCIBus *primary_bus;
- bool smmu_per_bus; /* SMMU is specific to the primary_bus */
+ PCIBus *pci_primary_bus;
+ bool pci_smmu_per_bus; /* SMMU is specific to the pci_primary_bus */
+ BusState *generic_primary_bus;
};
struct SMMUBaseClass {
@@ -183,7 +193,11 @@ SMMUPciBus *smmu_find_smmu_pcibus(SMMUState *s, uint8_t bus_num);
/* Return the stream ID of an SMMU device */
static inline uint16_t smmu_get_sid(SMMUDevice *sdev)
{
- return PCI_BUILD_BDF(pci_bus_num(sdev->bus), sdev->devfn);
+ if (sdev->pcie_device) {
+ return PCI_BUILD_BDF(pci_bus_num(sdev->bus), sdev->devfn);
+ } else {
+ return sdev->devfn;
+ }
}
/**
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
index e3862279da..2092450b90 100644
--- a/include/hw/qdev-core.h
+++ b/include/hw/qdev-core.h
@@ -954,6 +954,18 @@ bool device_is_in_reset(DeviceState *dev);
*/
bool bus_is_in_reset(BusState *bus);
+/**
+ * bus_setup_iommu() - Set up IOMMU operations for a bus
+ * @bus: the bus to configure
+ * @ops: IOMMU operations structure containing callback functions
+ * @opaque: opaque data passed to IOMMU operation callbacks
+ *
+ * Configure IOMMU operations for the specified bus. The ops structure
+ * must contain at least the get_address_space callback. The opaque
+ * parameter is passed through to the operation callbacks.
+ */
+void bus_setup_iommu(BusState *bus, const BusIOMMUOps *ops, void *opaque);
+
/* This should go away once we get rid of the NULL bus hack */
BusState *sysbus_get_default(void);
--
2.43.0
© 2016 - 2026 Red Hat, Inc.