From: Alistair Francis <[email protected]> Legacy virtio devices only have 32 feature bits and therefore can't set the VIRTIO_F_ACCESS_PLATFORM (bit 33) feature. This means the vring_use_map_api() function will return false.
Currently Linux endpoint devices use the legacy virtio interface as they aren't able to advertise the Common configuration capability. As most PCI endpoint capable PCIe controllers do not allow modifying the capability list, and thus are unable to advertise the Common configuration capability. This means the device's inbound TLPs fault on the host SMMU because the vring descriptors carry raw physical addresses. This quirk forces a subset of legacy virtio devices to use the DMA Map API (vring_use_map_api() will return true), which fixes this issue. This doesn't affect existing devices as we are checking for an otherwise invalid vendor ID. Ideally we would update the endpoint devices (like scsi-pci-epf) to not use the legacy virtio interface, but lots of endpoint hardware (like the one in the RK3588) doesn't allow us to add custom capabilities. Signed-off-by: Alistair Francis <[email protected]> --- drivers/virtio/virtio_pci_legacy.c | 27 +++++++++++++++++++++++++++ drivers/virtio/virtio_ring.c | 7 +++++++ include/linux/virtio.h | 5 +++++ 3 files changed, 39 insertions(+) diff --git a/drivers/virtio/virtio_pci_legacy.c b/drivers/virtio/virtio_pci_legacy.c index d9cbb02b35a1..7b529bd451bb 100644 --- a/drivers/virtio/virtio_pci_legacy.c +++ b/drivers/virtio/virtio_pci_legacy.c @@ -16,6 +16,7 @@ #include "linux/virtio_pci_legacy.h" #include "virtio_pci_common.h" +#include <linux/virtio_ids.h> /* virtio config->get_features() implementation */ static u64 vp_get_features(struct virtio_device *vdev) @@ -220,6 +221,32 @@ int virtio_pci_legacy_probe(struct virtio_pci_device *vp_dev) vp_dev->vdev.config = &virtio_pci_config_ops; + /* + * Legacy virtio devices only have 32 feature bits and therefore can't + * set the VIRTIO_F_ACCESS_PLATFORM (bit 33) feature. This means the + * vring_use_map_api() function will return false. + * + * Currently Linux endpoint devices use the legacy virtio interface as + * they aren't able to advertise the Common configuration capability. + * This means the device's inbound TLPs fault on the host SMMU because + * the vring descriptors carry raw physical addresses. + * + * This quirk forces a subset of legacy virtio devices to use the + * DMA Map API (vring_use_map_api() will return true), which fixes this + * issue. + * + * This doesn't affect existing devices as we are checking for an + * otherwise invalid vendor ID. + * + * Ideally we would update the endpoint devices (like scsi-pci-epf) + * to not use the legacy virtio interface, but lots of endpoint + * hardware (like the one in the RK3588) doesn't allow us to add + * custom capabilities. + */ + if (pci_dev->subsystem_vendor == 0xFFFF && + pci_dev->subsystem_device == VIRTIO_ID_SCSI) + vp_dev->vdev.force_use_map_api = true; + vp_dev->config_vector = vp_config_vector; vp_dev->setup_vq = setup_vq; vp_dev->del_vq = del_vq; diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index 5c169fbb418a..c8f62180c9d3 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -384,6 +384,13 @@ static bool vring_use_map_api(const struct virtio_device *vdev) if (!virtio_has_dma_quirk(vdev)) return true; + /* + * A quirk set by certain legacy devices to force us to + * pretend the VIRTIO_F_ACCESS_PLATFORM feature is enabled. + */ + if (vdev->force_use_map_api) + return true; + /* Otherwise, we are left to guess. */ /* * In theory, it's possible to have a buggy QEMU-supposed diff --git a/include/linux/virtio.h b/include/linux/virtio.h index f923e42cfd01..305c331f33f1 100644 --- a/include/linux/virtio.h +++ b/include/linux/virtio.h @@ -151,6 +151,10 @@ struct virtio_admin_cmd { * @config_driver_disabled: configuration change reporting disabled by * a driver * @config_change_pending: configuration change reported while disabled + * @force_use_map_api: A quirk set by certain legacy devices to force us + * to pretend the VIRTIO_F_ACCESS_PLATFORM feature is + * enabled. Set by transports that have no way to + * negotiate ACCESS_PLATFORM but sit behind a real IOMMU. * @config_lock: protects configuration change reporting * @vqs_list_lock: protects @vqs. * @dev: underlying device. @@ -173,6 +177,7 @@ struct virtio_device { bool config_core_enabled; bool config_driver_disabled; bool config_change_pending; + bool force_use_map_api; spinlock_t config_lock; spinlock_t vqs_list_lock; struct device dev; -- 2.55.0

