When the guest reboots with devices in nested mode (S1 + S2), any QEMU/UEFI
access to those devices can fail because S1 translation is not valid during
the reboot. For example, a passthrough NVMe device may hold GRUB boot info
that UEFI tries to read during the reboot.

Set S1 to bypass mode during reset to avoid such failures.

Reported-by: Matthew R. Ochs <[email protected]>
Signed-off-by: Shameer Kolothum <[email protected]>
---
 hw/arm/smmuv3-accel.c | 29 +++++++++++++++++++++++++++++
 hw/arm/smmuv3-accel.h |  4 ++++
 hw/arm/smmuv3.c       |  1 +
 3 files changed, 34 insertions(+)

diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c
index defeddbd8c..8396053a6c 100644
--- a/hw/arm/smmuv3-accel.c
+++ b/hw/arm/smmuv3-accel.c
@@ -634,6 +634,35 @@ static const PCIIOMMUOps smmuv3_accel_ops = {
     .get_msi_address_space = smmuv3_accel_find_msi_as,
 };
 
+/*
+ * If the guest reboots and devices are configured for S1+S2, Stage1 must
+ * be switched to bypass. Otherwise, QEMU/UEFI may fail when accessing a
+ * device, e.g. when UEFI retrieves boot partition information from an
+ * assigned vfio-pci NVMe device.
+ */
+void smmuv3_accel_attach_bypass_hwpt(SMMUv3State *s)
+{
+    SMMUv3AccelDevice *accel_dev;
+    SMMUViommu *viommu;
+
+    if (!s->accel || !s->s_accel->viommu) {
+        return;
+    }
+
+    viommu = s->s_accel->viommu;
+    QLIST_FOREACH(accel_dev, &viommu->device_list, next) {
+        if (!accel_dev->vdev) {
+            continue;
+        }
+        if (!host_iommu_device_iommufd_attach_hwpt(accel_dev->idev,
+                                                   viommu->bypass_hwpt_id,
+                                                   NULL)) {
+            error_report("Failed to install bypass hwpt id %u for dev id %u",
+                          viommu->bypass_hwpt_id, accel_dev->idev->devid);
+        }
+    }
+}
+
 void smmuv3_accel_init(SMMUv3State *s)
 {
     SMMUState *bs = ARM_SMMU(s);
diff --git a/hw/arm/smmuv3-accel.h b/hw/arm/smmuv3-accel.h
index 3bdba47616..75f858e34a 100644
--- a/hw/arm/smmuv3-accel.h
+++ b/hw/arm/smmuv3-accel.h
@@ -48,6 +48,7 @@ bool smmuv3_accel_install_nested_ste_range(SMMUv3State *s, 
SMMUSIDRange *range,
                                            Error **errp);
 bool smmuv3_accel_issue_inv_cmd(SMMUv3State *s, void *cmd, SMMUDevice *sdev,
                                 Error **errp);
+void smmuv3_accel_attach_bypass_hwpt(SMMUv3State *s);
 #else
 static inline void smmuv3_accel_init(SMMUv3State *s)
 {
@@ -70,6 +71,9 @@ smmuv3_accel_issue_inv_cmd(SMMUv3State *s, void *cmd, 
SMMUDevice *sdev,
 {
     return true;
 }
+static inline void smmuv3_accel_attach_bypass_hwpt(SMMUv3State *s)
+{
+}
 #endif
 
 #endif /* HW_ARM_SMMUV3_ACCEL_H */
diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index 5830cf5a03..94b2bbc374 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -1913,6 +1913,7 @@ static void smmu_reset_exit(Object *obj, ResetType type)
     if (c->parent_phases.exit) {
         c->parent_phases.exit(obj, type);
     }
+    smmuv3_accel_attach_bypass_hwpt(s);
 }
 
 static void smmu_realize(DeviceState *d, Error **errp)
-- 
2.43.0


Reply via email to