From: Nicolin Chen <nicol...@nvidia.com> Implement a set_iommu_device callback: -If found an existing viommu reuse that. (Devices behind the same physical SMMU should share an S2 HWPT) -Else, Allocate a viommu with the nested parent S2 hwpt allocated by VFIO. Allocate bypass and abort hwpt. -And add the dev to viommu device list
Also add an unset_iommu_device to unwind/cleanup above. Signed-off-by: Nicolin Chen <nicol...@nvidia.com> Signed-off-by: Shameer Kolothum <shameerali.kolothum.th...@huawei.com> --- hw/arm/smmuv3-accel.c | 154 +++++++++++++++++++++++++++++++++++++++ hw/arm/smmuv3-accel.h | 20 +++++ hw/arm/trace-events | 4 + include/system/iommufd.h | 6 ++ 4 files changed, 184 insertions(+) diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c index 66cd4f5ece..fe90d48675 100644 --- a/hw/arm/smmuv3-accel.c +++ b/hw/arm/smmuv3-accel.c @@ -7,6 +7,7 @@ */ #include "qemu/osdep.h" +#include "trace.h" #include "qemu/error-report.h" #include "hw/arm/smmuv3.h" @@ -17,6 +18,9 @@ #include "smmuv3-accel.h" +#define SMMU_STE_VALID (1ULL << 0) +#define SMMU_STE_CFG_BYPASS (1ULL << 3) + static SMMUv3AccelDevice *smmuv3_accel_get_dev(SMMUState *bs, SMMUPciBus *sbus, PCIBus *bus, int devfn) { @@ -39,6 +43,154 @@ static SMMUv3AccelDevice *smmuv3_accel_get_dev(SMMUState *bs, SMMUPciBus *sbus, return accel_dev; } +static bool +smmuv3_accel_dev_alloc_viommu(SMMUv3AccelDevice *accel_dev, + HostIOMMUDeviceIOMMUFD *idev, Error **errp) +{ + struct iommu_hwpt_arm_smmuv3 bypass_data = { + .ste = { SMMU_STE_CFG_BYPASS | SMMU_STE_VALID, 0x0ULL }, + }; + struct iommu_hwpt_arm_smmuv3 abort_data = { + .ste = { SMMU_STE_VALID, 0x0ULL }, + }; + SMMUDevice *sdev = &accel_dev->sdev; + SMMUState *bs = sdev->smmu; + SMMUv3State *s = ARM_SMMUV3(bs); + SMMUv3AccelState *s_accel = s->s_accel; + uint32_t s2_hwpt_id = idev->hwpt_id; + SMMUS2Hwpt *s2_hwpt; + SMMUViommu *viommu; + uint32_t viommu_id; + + if (s_accel->viommu) { + accel_dev->viommu = s_accel->viommu; + return true; + } + + if (!iommufd_backend_alloc_viommu(idev->iommufd, idev->devid, + IOMMU_VIOMMU_TYPE_ARM_SMMUV3, + s2_hwpt_id, &viommu_id, errp)) { + return false; + } + + viommu = g_new0(SMMUViommu, 1); + viommu->core.viommu_id = viommu_id; + viommu->core.s2_hwpt_id = s2_hwpt_id; + viommu->core.iommufd = idev->iommufd; + + if (!iommufd_backend_alloc_hwpt(idev->iommufd, idev->devid, + viommu->core.viommu_id, 0, + IOMMU_HWPT_DATA_ARM_SMMUV3, + sizeof(abort_data), &abort_data, + &viommu->abort_hwpt_id, errp)) { + goto free_viommu; + } + + if (!iommufd_backend_alloc_hwpt(idev->iommufd, idev->devid, + viommu->core.viommu_id, 0, + IOMMU_HWPT_DATA_ARM_SMMUV3, + sizeof(bypass_data), &bypass_data, + &viommu->bypass_hwpt_id, errp)) { + goto free_abort_hwpt; + } + + s2_hwpt = g_new(SMMUS2Hwpt, 1); + s2_hwpt->iommufd = idev->iommufd; + s2_hwpt->hwpt_id = s2_hwpt_id; + + viommu->iommufd = idev->iommufd; + viommu->s2_hwpt = s2_hwpt; + + s_accel->viommu = viommu; + accel_dev->viommu = viommu; + return true; + +free_abort_hwpt: + iommufd_backend_free_id(idev->iommufd, viommu->abort_hwpt_id); +free_viommu: + iommufd_backend_free_id(idev->iommufd, viommu->core.viommu_id); + g_free(viommu); + return false; +} + +static bool smmuv3_accel_set_iommu_device(PCIBus *bus, void *opaque, int devfn, + HostIOMMUDevice *hiod, Error **errp) +{ + HostIOMMUDeviceIOMMUFD *idev = HOST_IOMMU_DEVICE_IOMMUFD(hiod); + SMMUState *bs = opaque; + SMMUv3State *s = ARM_SMMUV3(bs); + SMMUv3AccelState *s_accel = s->s_accel; + SMMUPciBus *sbus = smmu_get_sbus(bs, bus); + SMMUv3AccelDevice *accel_dev = smmuv3_accel_get_dev(bs, sbus, bus, devfn); + SMMUDevice *sdev = &accel_dev->sdev; + + if (!idev) { + return true; + } + + if (accel_dev->idev) { + if (accel_dev->idev != idev) { + error_report("Device 0x%x already has an associated idev", + smmu_get_sid(sdev)); + return false; + } else { + return true; + } + } + + if (!smmuv3_accel_dev_alloc_viommu(accel_dev, idev, errp)) { + error_report("Device 0x%x: Unable to alloc viommu", smmu_get_sid(sdev)); + return false; + } + + accel_dev->idev = idev; + QLIST_INSERT_HEAD(&s_accel->viommu->device_list, accel_dev, next); + trace_smmuv3_accel_set_iommu_device(devfn, smmu_get_sid(sdev)); + return true; +} + +static void smmuv3_accel_unset_iommu_device(PCIBus *bus, void *opaque, + int devfn) +{ + SMMUState *bs = opaque; + SMMUv3State *s = ARM_SMMUV3(bs); + SMMUPciBus *sbus = g_hash_table_lookup(bs->smmu_pcibus_by_busptr, bus); + SMMUv3AccelDevice *accel_dev; + SMMUViommu *viommu; + SMMUDevice *sdev; + + if (!sbus) { + return; + } + + sdev = sbus->pbdev[devfn]; + if (!sdev) { + return; + } + + accel_dev = container_of(sdev, SMMUv3AccelDevice, sdev); + if (!host_iommu_device_iommufd_attach_hwpt(accel_dev->idev, + accel_dev->idev->hwpt_id, + NULL)) { + error_report("Unable to attach dev to the default HW pagetable"); + } + + accel_dev->idev = NULL; + QLIST_REMOVE(accel_dev, next); + trace_smmuv3_accel_unset_iommu_device(devfn, smmu_get_sid(sdev)); + + viommu = s->s_accel->viommu; + if (QLIST_EMPTY(&viommu->device_list)) { + iommufd_backend_free_id(viommu->iommufd, viommu->bypass_hwpt_id); + iommufd_backend_free_id(viommu->iommufd, viommu->abort_hwpt_id); + iommufd_backend_free_id(viommu->iommufd, viommu->core.viommu_id); + iommufd_backend_free_id(viommu->iommufd, viommu->s2_hwpt->hwpt_id); + g_free(viommu->s2_hwpt); + g_free(viommu); + s->s_accel->viommu = NULL; + } +} + static bool smmuv3_accel_pdev_allowed(PCIDevice *pdev, bool *vfio_pci) { @@ -98,6 +250,8 @@ static uint64_t smmuv3_accel_get_viommu_cap(void *opaque) static const PCIIOMMUOps smmuv3_accel_ops = { .get_address_space = smmuv3_accel_find_add_as, .get_viommu_cap = smmuv3_accel_get_viommu_cap, + .set_iommu_device = smmuv3_accel_set_iommu_device, + .unset_iommu_device = smmuv3_accel_unset_iommu_device, }; void smmuv3_accel_init(SMMUv3State *s) diff --git a/hw/arm/smmuv3-accel.h b/hw/arm/smmuv3-accel.h index 2cd343103f..55a6a353fc 100644 --- a/hw/arm/smmuv3-accel.h +++ b/hw/arm/smmuv3-accel.h @@ -11,16 +11,36 @@ #include "hw/arm/smmuv3.h" #include "hw/arm/smmu-common.h" +#include "system/iommufd.h" +#include <linux/iommufd.h> #include CONFIG_DEVICES +typedef struct SMMUS2Hwpt { + IOMMUFDBackend *iommufd; + uint32_t hwpt_id; +} SMMUS2Hwpt; + +typedef struct SMMUViommu { + IOMMUFDBackend *iommufd; + IOMMUFDViommu core; + SMMUS2Hwpt *s2_hwpt; + uint32_t bypass_hwpt_id; + uint32_t abort_hwpt_id; + QLIST_HEAD(, SMMUv3AccelDevice) device_list; +} SMMUViommu; + typedef struct SMMUv3AccelDevice { SMMUDevice sdev; AddressSpace as_sysmem; + HostIOMMUDeviceIOMMUFD *idev; + SMMUViommu *viommu; + QLIST_ENTRY(SMMUv3AccelDevice) next; } SMMUv3AccelDevice; typedef struct SMMUv3AccelState { MemoryRegion root; MemoryRegion sysmem; + SMMUViommu *viommu; } SMMUv3AccelState; #if defined(CONFIG_ARM_SMMUV3) && defined(CONFIG_IOMMUFD) diff --git a/hw/arm/trace-events b/hw/arm/trace-events index f3386bd7ae..c4537ca1d6 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -66,6 +66,10 @@ smmuv3_notify_flag_del(const char *iommu) "DEL SMMUNotifier node for iommu mr=%s smmuv3_inv_notifiers_iova(const char *name, int asid, int vmid, uint64_t iova, uint8_t tg, uint64_t num_pages, int stage) "iommu mr=%s asid=%d vmid=%d iova=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64" stage=%d" smmu_reset_exit(void) "" +#smmuv3-accel.c +smmuv3_accel_set_iommu_device(int devfn, uint32_t sid) "devfn=0x%x (sid=0x%x)" +smmuv3_accel_unset_iommu_device(int devfn, uint32_t sid) "devfn=0x%x (sid=0x%x" + # strongarm.c strongarm_uart_update_parameters(const char *label, int speed, char parity, int data_bits, int stop_bits) "%s speed=%d parity=%c data=%d stop=%d" strongarm_ssp_read_underrun(void) "SSP rx underrun" diff --git a/include/system/iommufd.h b/include/system/iommufd.h index 6ab3ba3cb6..b7ad2cf10c 100644 --- a/include/system/iommufd.h +++ b/include/system/iommufd.h @@ -38,6 +38,12 @@ struct IOMMUFDBackend { /*< public >*/ }; +typedef struct IOMMUFDViommu { + IOMMUFDBackend *iommufd; + uint32_t s2_hwpt_id; + uint32_t viommu_id; +} IOMMUFDViommu; + bool iommufd_backend_connect(IOMMUFDBackend *be, Error **errp); void iommufd_backend_disconnect(IOMMUFDBackend *be); -- 2.34.1