161 lines
5.0 KiB
Diff
161 lines
5.0 KiB
Diff
|
|
From 5834bb1ccce592380a91a5cf127f90a031cd7cf2 Mon Sep 17 00:00:00 2001
|
||
|
|
From: Yi Liu <yi.l.liu@intel.com>
|
||
|
|
Date: Wed, 5 Jun 2024 16:30:42 +0800
|
||
|
|
Subject: [PATCH] intel_iommu: Implement [set|unset]_iommu_device() callbacks
|
||
|
|
|
||
|
|
Implement [set|unset]_iommu_device() callbacks in Intel vIOMMU.
|
||
|
|
In set call, we take a reference of HostIOMMUDevice and store it
|
||
|
|
in hash table indexed by PCI BDF.
|
||
|
|
|
||
|
|
Note this BDF index is device's real BDF not the aliased one which
|
||
|
|
is different from the index of VTDAddressSpace. There can be multiple
|
||
|
|
assigned devices under same virtual iommu group and share same
|
||
|
|
VTDAddressSpace, but each has its own HostIOMMUDevice.
|
||
|
|
|
||
|
|
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
|
||
|
|
Signed-off-by: Yi Sun <yi.y.sun@linux.intel.com>
|
||
|
|
Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
|
||
|
|
Reviewed-by: Eric Auger <eric.auger@redhat.com>
|
||
|
|
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
|
||
|
|
---
|
||
|
|
hw/i386/intel_iommu.c | 81 +++++++++++++++++++++++++++++++++++
|
||
|
|
include/hw/i386/intel_iommu.h | 2 +
|
||
|
|
2 files changed, 83 insertions(+)
|
||
|
|
|
||
|
|
diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
|
||
|
|
index 6716407b7a..bdc14f8438 100644
|
||
|
|
--- a/hw/i386/intel_iommu.c
|
||
|
|
+++ b/hw/i386/intel_iommu.c
|
||
|
|
@@ -61,6 +61,12 @@ struct vtd_as_key {
|
||
|
|
uint32_t pasid;
|
||
|
|
};
|
||
|
|
|
||
|
|
+/* bus/devfn is PCI device's real BDF not the aliased one */
|
||
|
|
+struct vtd_hiod_key {
|
||
|
|
+ PCIBus *bus;
|
||
|
|
+ uint8_t devfn;
|
||
|
|
+};
|
||
|
|
+
|
||
|
|
struct vtd_iotlb_key {
|
||
|
|
uint64_t gfn;
|
||
|
|
uint32_t pasid;
|
||
|
|
@@ -250,6 +256,25 @@ static guint vtd_as_hash(gconstpointer v)
|
||
|
|
return (guint)(value << 8 | key->devfn);
|
||
|
|
}
|
||
|
|
|
||
|
|
+/* Same implementation as vtd_as_hash() */
|
||
|
|
+static guint vtd_hiod_hash(gconstpointer v)
|
||
|
|
+{
|
||
|
|
+ return vtd_as_hash(v);
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+static gboolean vtd_hiod_equal(gconstpointer v1, gconstpointer v2)
|
||
|
|
+{
|
||
|
|
+ const struct vtd_hiod_key *key1 = v1;
|
||
|
|
+ const struct vtd_hiod_key *key2 = v2;
|
||
|
|
+
|
||
|
|
+ return (key1->bus == key2->bus) && (key1->devfn == key2->devfn);
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+static void vtd_hiod_destroy(gpointer v)
|
||
|
|
+{
|
||
|
|
+ object_unref(v);
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
static gboolean vtd_hash_remove_by_domain(gpointer key, gpointer value,
|
||
|
|
gpointer user_data)
|
||
|
|
{
|
||
|
|
@@ -3813,6 +3838,58 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus,
|
||
|
|
return vtd_dev_as;
|
||
|
|
}
|
||
|
|
|
||
|
|
+static bool vtd_dev_set_iommu_device(PCIBus *bus, void *opaque, int devfn,
|
||
|
|
+ HostIOMMUDevice *hiod, Error **errp)
|
||
|
|
+{
|
||
|
|
+ IntelIOMMUState *s = opaque;
|
||
|
|
+ struct vtd_as_key key = {
|
||
|
|
+ .bus = bus,
|
||
|
|
+ .devfn = devfn,
|
||
|
|
+ };
|
||
|
|
+ struct vtd_as_key *new_key;
|
||
|
|
+
|
||
|
|
+ assert(hiod);
|
||
|
|
+
|
||
|
|
+ vtd_iommu_lock(s);
|
||
|
|
+
|
||
|
|
+ if (g_hash_table_lookup(s->vtd_host_iommu_dev, &key)) {
|
||
|
|
+ error_setg(errp, "Host IOMMU device already exist");
|
||
|
|
+ vtd_iommu_unlock(s);
|
||
|
|
+ return false;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ new_key = g_malloc(sizeof(*new_key));
|
||
|
|
+ new_key->bus = bus;
|
||
|
|
+ new_key->devfn = devfn;
|
||
|
|
+
|
||
|
|
+ object_ref(hiod);
|
||
|
|
+ g_hash_table_insert(s->vtd_host_iommu_dev, new_key, hiod);
|
||
|
|
+
|
||
|
|
+ vtd_iommu_unlock(s);
|
||
|
|
+
|
||
|
|
+ return true;
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+static void vtd_dev_unset_iommu_device(PCIBus *bus, void *opaque, int devfn)
|
||
|
|
+{
|
||
|
|
+ IntelIOMMUState *s = opaque;
|
||
|
|
+ struct vtd_as_key key = {
|
||
|
|
+ .bus = bus,
|
||
|
|
+ .devfn = devfn,
|
||
|
|
+ };
|
||
|
|
+
|
||
|
|
+ vtd_iommu_lock(s);
|
||
|
|
+
|
||
|
|
+ if (!g_hash_table_lookup(s->vtd_host_iommu_dev, &key)) {
|
||
|
|
+ vtd_iommu_unlock(s);
|
||
|
|
+ return;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ g_hash_table_remove(s->vtd_host_iommu_dev, &key);
|
||
|
|
+
|
||
|
|
+ vtd_iommu_unlock(s);
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
/* Unmap the whole range in the notifier's scope. */
|
||
|
|
static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n)
|
||
|
|
{
|
||
|
|
@@ -4117,6 +4194,8 @@ static AddressSpace *vtd_host_dma_iommu(PCIBus *bus, void *opaque, int devfn)
|
||
|
|
|
||
|
|
static PCIIOMMUOps vtd_iommu_ops = {
|
||
|
|
.get_address_space = vtd_host_dma_iommu,
|
||
|
|
+ .set_iommu_device = vtd_dev_set_iommu_device,
|
||
|
|
+ .unset_iommu_device = vtd_dev_unset_iommu_device,
|
||
|
|
};
|
||
|
|
|
||
|
|
static bool vtd_decide_config(IntelIOMMUState *s, Error **errp)
|
||
|
|
@@ -4240,6 +4319,8 @@ static void vtd_realize(DeviceState *dev, Error **errp)
|
||
|
|
g_free, g_free);
|
||
|
|
s->vtd_address_spaces = g_hash_table_new_full(vtd_as_hash, vtd_as_equal,
|
||
|
|
g_free, g_free);
|
||
|
|
+ s->vtd_host_iommu_dev = g_hash_table_new_full(vtd_hiod_hash, vtd_hiod_equal,
|
||
|
|
+ g_free, vtd_hiod_destroy);
|
||
|
|
vtd_init(s);
|
||
|
|
pci_setup_iommu(bus, &vtd_iommu_ops, dev);
|
||
|
|
/* Pseudo address space under root PCI bus. */
|
||
|
|
diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h
|
||
|
|
index 7fa0a695c8..1eb05c29fc 100644
|
||
|
|
--- a/include/hw/i386/intel_iommu.h
|
||
|
|
+++ b/include/hw/i386/intel_iommu.h
|
||
|
|
@@ -292,6 +292,8 @@ struct IntelIOMMUState {
|
||
|
|
/* list of registered notifiers */
|
||
|
|
QLIST_HEAD(, VTDAddressSpace) vtd_as_with_notifiers;
|
||
|
|
|
||
|
|
+ GHashTable *vtd_host_iommu_dev; /* HostIOMMUDevice */
|
||
|
|
+
|
||
|
|
/* interrupt remapping */
|
||
|
|
bool intr_enabled; /* Whether guest enabled IR */
|
||
|
|
dma_addr_t intr_root; /* Interrupt remapping table pointer */
|
||
|
|
--
|
||
|
|
2.41.0.windows.1
|
||
|
|
|