|
@@ -28,8 +28,69 @@
|
|
#include "qemu/error-report.h"
|
|
#include "qemu/error-report.h"
|
|
#include "hw/arm/smmu-common.h"
|
|
#include "hw/arm/smmu-common.h"
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * 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
|
|
|
|
+ * numbers may not be always initialized yet.
|
|
|
|
+ */
|
|
|
|
+SMMUPciBus *smmu_find_smmu_pcibus(SMMUState *s, uint8_t bus_num)
|
|
|
|
+{
|
|
|
|
+ SMMUPciBus *smmu_pci_bus = s->smmu_pcibus_by_bus_num[bus_num];
|
|
|
|
+
|
|
|
|
+ if (!smmu_pci_bus) {
|
|
|
|
+ GHashTableIter iter;
|
|
|
|
+
|
|
|
|
+ g_hash_table_iter_init(&iter, s->smmu_pcibus_by_busptr);
|
|
|
|
+ while (g_hash_table_iter_next(&iter, NULL, (void **)&smmu_pci_bus)) {
|
|
|
|
+ if (pci_bus_num(smmu_pci_bus->bus) == bus_num) {
|
|
|
|
+ s->smmu_pcibus_by_bus_num[bus_num] = smmu_pci_bus;
|
|
|
|
+ return smmu_pci_bus;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return smmu_pci_bus;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static AddressSpace *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);
|
|
|
|
+ SMMUDevice *sdev;
|
|
|
|
+
|
|
|
|
+ if (!sbus) {
|
|
|
|
+ sbus = g_malloc0(sizeof(SMMUPciBus) +
|
|
|
|
+ sizeof(SMMUDevice *) * SMMU_PCI_DEVFN_MAX);
|
|
|
|
+ sbus->bus = bus;
|
|
|
|
+ g_hash_table_insert(s->smmu_pcibus_by_busptr, bus, sbus);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ sdev = sbus->pbdev[devfn];
|
|
|
|
+ if (!sdev) {
|
|
|
|
+ char *name = g_strdup_printf("%s-%d-%d",
|
|
|
|
+ s->mrtypename,
|
|
|
|
+ pci_bus_num(bus), devfn);
|
|
|
|
+ sdev = sbus->pbdev[devfn] = g_new0(SMMUDevice, 1);
|
|
|
|
+
|
|
|
|
+ sdev->smmu = s;
|
|
|
|
+ sdev->bus = bus;
|
|
|
|
+ sdev->devfn = devfn;
|
|
|
|
+
|
|
|
|
+ memory_region_init_iommu(&sdev->iommu, sizeof(sdev->iommu),
|
|
|
|
+ s->mrtypename,
|
|
|
|
+ OBJECT(s), name, 1ULL << SMMU_MAX_VA_BITS);
|
|
|
|
+ address_space_init(&sdev->as,
|
|
|
|
+ MEMORY_REGION(&sdev->iommu), name);
|
|
|
|
+ trace_smmu_add_mr(name);
|
|
|
|
+ g_free(name);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return &sdev->as;
|
|
|
|
+}
|
|
|
|
+
|
|
static void smmu_base_realize(DeviceState *dev, Error **errp)
|
|
static void smmu_base_realize(DeviceState *dev, Error **errp)
|
|
{
|
|
{
|
|
|
|
+ SMMUState *s = ARM_SMMU(dev);
|
|
SMMUBaseClass *sbc = ARM_SMMU_GET_CLASS(dev);
|
|
SMMUBaseClass *sbc = ARM_SMMU_GET_CLASS(dev);
|
|
Error *local_err = NULL;
|
|
Error *local_err = NULL;
|
|
|
|
|
|
@@ -38,6 +99,14 @@ static void smmu_base_realize(DeviceState *dev, Error **errp)
|
|
error_propagate(errp, local_err);
|
|
error_propagate(errp, local_err);
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ s->smmu_pcibus_by_busptr = g_hash_table_new(NULL, NULL);
|
|
|
|
+
|
|
|
|
+ if (s->primary_bus) {
|
|
|
|
+ pci_setup_iommu(s->primary_bus, smmu_find_add_as, s);
|
|
|
|
+ } else {
|
|
|
|
+ error_setg(errp, "SMMU is not attached to any PCI bus!");
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
static void smmu_base_reset(DeviceState *dev)
|
|
static void smmu_base_reset(DeviceState *dev)
|