diff --git a/docs/devel/vfio-iommufd.rst b/docs/devel/vfio-iommufd.rst index 2d6e60dce1..6928b47643 100644 --- a/docs/devel/vfio-iommufd.rst +++ b/docs/devel/vfio-iommufd.rst @@ -169,3 +169,12 @@ otherwise below error shows: .. code-block:: none qemu-system-x86_64: -device vfio-pci,host=0000:02:00.0,bus=bridge1,iommufd=iommufd0: vfio 0000:02:00.0: Failed to set vIOMMU: Host device downstream to a PCI bridge is unsupported when x-flts=on + +If host IOMMU has ERRATA_772415_SPR17, running guest with "intel_iommu=on,sm_off" +is unsupported, kexec or reboot guest from "intel_iommu=on,sm_on" to +"intel_iommu=on,sm_off" is also unsupported. Configure scalable mode off as +below if it's not needed by guest: + +.. code-block:: bash + + -device intel-iommu,x-scalable-mode=off diff --git a/hw/vfio/device.c b/hw/vfio/device.c index 3bab082322..086f20f676 100644 --- a/hw/vfio/device.c +++ b/hw/vfio/device.c @@ -533,6 +533,20 @@ bool vfio_device_get_viommu_flags_want_nesting(VFIODevice *vbasedev) return false; } +bool vfio_device_get_host_iommu_quirk_bypass_ro(VFIODevice *vbasedev, + uint32_t type, void *caps, + uint32_t size) +{ + VFIOPCIDevice *vdev = vfio_pci_from_vfio_device(vbasedev); + + if (vdev) { + return !!(pci_device_get_host_iommu_quirks(PCI_DEVICE(vdev), type, + caps, size) & + HOST_IOMMU_QUIRK_NESTING_PARENT_BYPASS_RO); + } + return false; +} + /* * Traditional ioctl() based io */ diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 0bf68620d2..2947e1b80f 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -351,6 +351,7 @@ static bool iommufd_cdev_autodomains_get(VFIODevice *vbasedev, VFIOContainer *bcontainer = VFIO_IOMMU(container); uint32_t type, flags = 0; uint64_t hw_caps; + VendorCaps caps; VFIOIOASHwpt *hwpt; uint32_t hwpt_id; int ret; @@ -396,7 +397,8 @@ static bool iommufd_cdev_autodomains_get(VFIODevice *vbasedev, * instead. */ if (!iommufd_backend_get_device_info(vbasedev->iommufd, vbasedev->devid, - &type, NULL, 0, &hw_caps, errp)) { + &type, &caps, sizeof(caps), &hw_caps, + errp)) { return false; } @@ -411,6 +413,11 @@ static bool iommufd_cdev_autodomains_get(VFIODevice *vbasedev, */ if (vfio_device_get_viommu_flags_want_nesting(vbasedev)) { flags |= IOMMU_HWPT_ALLOC_NEST_PARENT; + + if (vfio_device_get_host_iommu_quirk_bypass_ro(vbasedev, type, + &caps, sizeof(caps))) { + bcontainer->bypass_ro = true; + } } if (cpr_is_incoming()) { diff --git a/hw/vfio/listener.c b/hw/vfio/listener.c index f193468dee..8ba1cd255d 100644 --- a/hw/vfio/listener.c +++ b/hw/vfio/listener.c @@ -502,7 +502,8 @@ void vfio_container_region_add(VFIOContainer *bcontainer, int ret; Error *err = NULL; - if (!vfio_listener_valid_section(section, false, "region_add")) { + if (!vfio_listener_valid_section(section, bcontainer->bypass_ro, + "region_add")) { return; } @@ -668,7 +669,8 @@ static void vfio_listener_region_del(MemoryListener *listener, int ret; bool try_unmap = true; - if (!vfio_listener_valid_section(section, false, "region_del")) { + if (!vfio_listener_valid_section(section, bcontainer->bypass_ro, + "region_del")) { return; } diff --git a/include/hw/vfio/vfio-container.h b/include/hw/vfio/vfio-container.h index 9f6e8cedfc..a7d5c5ed67 100644 --- a/include/hw/vfio/vfio-container.h +++ b/include/hw/vfio/vfio-container.h @@ -52,6 +52,7 @@ struct VFIOContainer { QLIST_HEAD(, VFIODevice) device_list; GList *iova_ranges; NotifierWithReturn cpr_reboot_notifier; + bool bypass_ro; }; #define TYPE_VFIO_IOMMU "vfio-iommu" diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h index 48d00c7bc4..f6f3d0e378 100644 --- a/include/hw/vfio/vfio-device.h +++ b/include/hw/vfio/vfio-device.h @@ -268,6 +268,9 @@ void vfio_device_prepare(VFIODevice *vbasedev, VFIOContainer *bcontainer, void vfio_device_unprepare(VFIODevice *vbasedev); bool vfio_device_get_viommu_flags_want_nesting(VFIODevice *vbasedev); +bool vfio_device_get_host_iommu_quirk_bypass_ro(VFIODevice *vbasedev, + uint32_t type, void *caps, + uint32_t size); int vfio_device_get_region_info(VFIODevice *vbasedev, int index, struct vfio_region_info **info);