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
