When guest enables scalable mode and setup first stage page table, we don't
want to use IOMMU MR but rather continue using the system MR for IOMMUFD
backed host device.
Then default HWPT in VFIO contains GPA->HPA mappings which could be reused
as nesting parent HWPT to construct nested HWPT in vIOMMU.
Move vtd_as_key into intel_iommu_internal.h as it's also used by accel code.
Suggested-by: Yi Liu <yi.l.liu@intel.com>
Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
Reviewed-by: Eric Auger <eric.auger@redhat.com>
---
hw/i386/intel_iommu_accel.h | 6 ++++++
hw/i386/intel_iommu_internal.h | 11 +++++++++++
hw/i386/intel_iommu.c | 28 +++++++++++++++-------------
hw/i386/intel_iommu_accel.c | 18 ++++++++++++++++++
4 files changed, 50 insertions(+), 13 deletions(-)
diff --git a/hw/i386/intel_iommu_accel.h b/hw/i386/intel_iommu_accel.h
index 76e0d26942..d049cab3e1 100644
--- a/hw/i386/intel_iommu_accel.h
+++ b/hw/i386/intel_iommu_accel.h
@@ -15,6 +15,7 @@
#ifdef CONFIG_VTD_ACCEL
bool vtd_check_hiod_accel(IntelIOMMUState *s, VTDHostIOMMUDevice *vtd_hiod,
Error **errp);
+VTDHostIOMMUDevice *vtd_find_hiod_iommufd(VTDAddressSpace *as);
#else
static inline bool vtd_check_hiod_accel(IntelIOMMUState *s,
VTDHostIOMMUDevice *vtd_hiod,
@@ -24,5 +25,10 @@ static inline bool vtd_check_hiod_accel(IntelIOMMUState *s,
error_append_hint(errp, "CONFIG_VTD_ACCEL is not enabled");
return false;
}
+
+static inline VTDHostIOMMUDevice *vtd_find_hiod_iommufd(VTDAddressSpace *as)
+{
+ return NULL;
+}
#endif
#endif
diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h
index 02522f64e0..d8dad18304 100644
--- a/hw/i386/intel_iommu_internal.h
+++ b/hw/i386/intel_iommu_internal.h
@@ -685,4 +685,15 @@ typedef struct VTDHostIOMMUDevice {
uint8_t devfn;
HostIOMMUDevice *hiod;
} VTDHostIOMMUDevice;
+
+/*
+ * PCI bus number (or SID) is not reliable since the device is usaully
+ * initialized before guest can configure the PCI bridge
+ * (SECONDARY_BUS_NUMBER).
+ */
+struct vtd_as_key {
+ PCIBus *bus;
+ uint8_t devfn;
+ uint32_t pasid;
+};
#endif
diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
index 5bf1c0e5a4..e4610a61b2 100644
--- a/hw/i386/intel_iommu.c
+++ b/hw/i386/intel_iommu.c
@@ -55,17 +55,6 @@
#define VTD_PE_GET_SS_LEVEL(pe) \
(2 + (((pe)->val[0] >> 2) & VTD_SM_PASID_ENTRY_AW))
-/*
- * PCI bus number (or SID) is not reliable since the device is usaully
- * initialized before guest can configure the PCI bridge
- * (SECONDARY_BUS_NUMBER).
- */
-struct vtd_as_key {
- PCIBus *bus;
- uint8_t devfn;
- uint32_t pasid;
-};
-
/* bus/devfn is PCI device's real BDF not the aliased one */
struct vtd_hiod_key {
PCIBus *bus;
@@ -1730,12 +1719,25 @@ static bool vtd_as_pt_enabled(VTDAddressSpace *as)
/* Return whether the device is using IOMMU translation. */
static bool vtd_switch_address_space(VTDAddressSpace *as)
{
+ IntelIOMMUState *s;
bool use_iommu, pt;
assert(as);
- use_iommu = as->iommu_state->dmar_enabled && !vtd_as_pt_enabled(as);
- pt = as->iommu_state->dmar_enabled && vtd_as_pt_enabled(as);
+ s = as->iommu_state;
+ use_iommu = s->dmar_enabled && !vtd_as_pt_enabled(as);
+ pt = s->dmar_enabled && vtd_as_pt_enabled(as);
+
+ /*
+ * When guest enables scalable mode and sets up first stage page table,
+ * we stick to system MR for IOMMUFD backed host device. Then its
+ * default hwpt contains GPA->HPA mappings which is used directly if
+ * PGTT=PT and used as nesting parent if PGTT=FST. Otherwise fall back
+ * to original processing.
+ */
+ if (s->root_scalable && s->fsts && vtd_find_hiod_iommufd(as)) {
+ use_iommu = false;
+ }
trace_vtd_switch_address_space(pci_bus_num(as->bus),
VTD_PCI_SLOT(as->devfn),
diff --git a/hw/i386/intel_iommu_accel.c b/hw/i386/intel_iommu_accel.c
index ead6c42879..ebfc503d64 100644
--- a/hw/i386/intel_iommu_accel.c
+++ b/hw/i386/intel_iommu_accel.c
@@ -50,3 +50,21 @@ bool vtd_check_hiod_accel(IntelIOMMUState *s, VTDHostIOMMUDevice *vtd_hiod,
"host IOMMU is incompatible with guest first stage translation");
return false;
}
+
+VTDHostIOMMUDevice *vtd_find_hiod_iommufd(VTDAddressSpace *as)
+{
+ IntelIOMMUState *s = as->iommu_state;
+ struct vtd_as_key key = {
+ .bus = as->bus,
+ .devfn = as->devfn,
+ };
+ VTDHostIOMMUDevice *vtd_hiod = g_hash_table_lookup(s->vtd_host_iommu_dev,
+ &key);
+
+ if (vtd_hiod && vtd_hiod->hiod &&
+ object_dynamic_cast(OBJECT(vtd_hiod->hiod),
+ TYPE_HOST_IOMMU_DEVICE_IOMMUFD)) {
+ return vtd_hiod;
+ }
+ return NULL;
+}
--
2.47.1