From 539e12641dc2db30a6fea7a0f061e163bc245d79 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Wed, 22 Jun 2022 02:16:52 -0700 Subject: [PATCH] hw/arm/smmu-common: Add set/unset_iommu_device callback Implement a set_iommu_device callback: - Find an existing S2 hwpt to test attach() or allocate a new one (Devices behind the same physical SMMU should share an S2 HWPT.) - Attach the device to the S2 hwpt and add it to its device list And add an unset_iommu_device doing the opposite cleanup routine. Signed-off-by: Nicolin Chen --- hw/arm/smmu-common.c | 177 +++++++++++++++++++++++++++++++++++ hw/arm/trace-events | 2 + include/hw/arm/smmu-common.h | 21 +++++ 3 files changed, 200 insertions(+) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 03d9ff58d4..038ae857d8 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -20,6 +20,7 @@ #include "trace.h" #include "exec/target_page.h" #include "hw/core/cpu.h" +#include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "qapi/error.h" #include "qemu/jhash.h" @@ -639,8 +640,184 @@ static AddressSpace *smmu_find_add_as(PCIBus *bus, void *opaque, int devfn) return &sdev->as; } +static bool smmu_dev_attach_viommu(SMMUDevice *sdev, + HostIOMMUDeviceIOMMUFD *idev, Error **errp) +{ + struct iommu_hwpt_arm_smmuv3 bypass_data = { + .ste = { 0x9ULL, 0x0ULL }, //0x1ULL << (108 - 64) }, + }; + struct iommu_hwpt_arm_smmuv3 abort_data = { + .ste = { 0x1ULL, 0x0ULL }, + }; + SMMUState *s = sdev->smmu; + SMMUS2Hwpt *s2_hwpt; + SMMUViommu *viommu; + uint32_t s2_hwpt_id; + + if (s->viommu) { + return host_iommu_device_iommufd_attach_hwpt( + idev, s->viommu->s2_hwpt->hwpt_id, errp); + } + + if (!iommufd_backend_alloc_hwpt(idev->iommufd, idev->devid, idev->ioas_id, + IOMMU_HWPT_ALLOC_NEST_PARENT, + IOMMU_HWPT_DATA_NONE, 0, NULL, + &s2_hwpt_id, errp)) { + error_setg(errp, "failed to allocate an S2 hwpt"); + return false; + } + + /* Attach to S2 for MSI cookie */ + if (!host_iommu_device_iommufd_attach_hwpt(idev, s2_hwpt_id, errp)) { + error_setg(errp, "failed to attach stage-2 HW pagetable"); + goto free_s2_hwpt; + } + + viommu = g_new0(SMMUViommu, 1); + + viommu->core = iommufd_backend_alloc_viommu(idev->iommufd, idev->devid, + IOMMU_VIOMMU_TYPE_ARM_SMMUV3, + s2_hwpt_id); + if (!viommu->core) { + error_setg(errp, "failed to allocate a viommu"); + goto free_viommu; + } + + 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)) { + error_setg(errp, "failed to allocate an abort pagetable"); + goto free_viommu_core; + } + + 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)) { + error_setg(errp, "failed to allocate a bypass pagetable"); + goto free_abort_hwpt; + } + + if (!host_iommu_device_iommufd_attach_hwpt( + idev, viommu->bypass_hwpt_id, errp)) { + error_setg(errp, "failed to attach the bypass pagetable"); + goto free_bypass_hwpt; + } + + s2_hwpt = g_new0(SMMUS2Hwpt, 1); + s2_hwpt->iommufd = idev->iommufd; + s2_hwpt->hwpt_id = s2_hwpt_id; + s2_hwpt->ioas_id = idev->ioas_id; + + viommu->iommufd = idev->iommufd; + viommu->s2_hwpt = s2_hwpt; + + s->viommu = viommu; + return true; + +free_bypass_hwpt: + iommufd_backend_free_id(idev->iommufd, viommu->bypass_hwpt_id); +free_abort_hwpt: + iommufd_backend_free_id(idev->iommufd, viommu->abort_hwpt_id); +free_viommu_core: + iommufd_backend_free_id(idev->iommufd, viommu->core->viommu_id); + g_free(viommu->core); +free_viommu: + g_free(viommu); + host_iommu_device_iommufd_attach_hwpt(idev, sdev->idev->ioas_id, errp); +free_s2_hwpt: + iommufd_backend_free_id(idev->iommufd, s2_hwpt_id); + return false; +} + +static bool smmu_dev_set_iommu_device(PCIBus *bus, void *opaque, int devfn, + HostIOMMUDevice *hiod, Error **errp) +{ + HostIOMMUDeviceIOMMUFD *idev = HOST_IOMMU_DEVICE_IOMMUFD(hiod); + SMMUState *s = opaque; + SMMUPciBus *sbus = smmu_get_sbus(s, bus); + SMMUDevice *sdev = smmu_get_sdev(s, sbus, bus, devfn); + + if (!s->nested) { + return true; + } + + if (sdev->idev) { + if (sdev->idev != idev) { + return false;//-EEXIST; + } else { + return true; + } + } + + if (!idev) { + return true; + } + + if (!smmu_dev_attach_viommu(sdev, idev, errp)) { + error_report("Unable to attach viommu"); + return false; + } + + sdev->idev = idev; + sdev->viommu = s->viommu; + QLIST_INSERT_HEAD(&s->viommu->device_list, sdev, next); + trace_smmu_set_iommu_device(devfn, smmu_get_sid(sdev)); + + return true; +} + +static void smmu_dev_unset_iommu_device(PCIBus *bus, void *opaque, int devfn) +{ + SMMUDevice *sdev; + SMMUViommu *viommu; + SMMUState *s = opaque; + SMMUPciBus *sbus = g_hash_table_lookup(s->smmu_pcibus_by_busptr, bus); + + if (!s->nested) { + return; + } + + if (!sbus) { + return; + } + + sdev = sbus->pbdev[devfn]; + if (!sdev) { + return; + } + + if (!host_iommu_device_iommufd_attach_hwpt(sdev->idev, + sdev->idev->ioas_id, NULL)) { + error_report("Unable to attach dev to the default HW pagetable"); + } + + viommu = sdev->viommu; + + sdev->idev = NULL; + sdev->viommu = NULL; + QLIST_REMOVE(sdev, next); + trace_smmu_unset_iommu_device(devfn, smmu_get_sid(sdev)); + + 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); + g_free(viommu->core); + iommufd_backend_free_id(viommu->iommufd, viommu->s2_hwpt->hwpt_id); + g_free(viommu->s2_hwpt); + g_free(viommu); + s->viommu = NULL; + } +} + static const PCIIOMMUOps smmu_ops = { .get_address_space = smmu_find_add_as, + .set_iommu_device = smmu_dev_set_iommu_device, + .unset_iommu_device = smmu_dev_unset_iommu_device, }; IOMMUMemoryRegion *smmu_iommu_mr(SMMUState *s, uint32_t sid) diff --git a/hw/arm/trace-events b/hw/arm/trace-events index cdc1ea06a8..58e0636e95 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -5,6 +5,8 @@ virt_acpi_setup(void) "No fw cfg or ACPI disabled. Bailing out." # smmu-common.c smmu_add_mr(const char *name) "%s" +smmu_set_iommu_device(int devfn, uint32_t sid) "devfn=%d (sid=%d)" +smmu_unset_iommu_device(int devfn, uint32_t sid) "devfn=%d (sid=%d)" smmu_ptw_level(int stage, int level, uint64_t iova, size_t subpage_size, uint64_t baseaddr, uint32_t offset, uint64_t pte) "stage=%d level=%d iova=0x%"PRIx64" subpage_sz=0x%zx baseaddr=0x%"PRIx64" offset=%d => pte=0x%"PRIx64 smmu_ptw_invalid_pte(int stage, int level, uint64_t baseaddr, uint64_t pteaddr, uint32_t offset, uint64_t pte) "stage=%d level=%d base@=0x%"PRIx64" pte@=0x%"PRIx64" offset=%d pte=0x%"PRIx64 smmu_ptw_page_pte(int stage, int level, uint64_t iova, uint64_t baseaddr, uint64_t pteaddr, uint64_t pte, uint64_t address) "stage=%d level=%d iova=0x%"PRIx64" base@=0x%"PRIx64" pte@=0x%"PRIx64" pte=0x%"PRIx64" page address = 0x%"PRIx64 diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index eae5d4d05b..3bfb68cef6 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -23,6 +23,7 @@ #include "hw/pci/pci.h" #include "qom/object.h" #include "sysemu/iommufd.h" +#include #define SMMU_PCI_BUS_MAX 256 #define SMMU_PCI_DEVFN_MAX 256 @@ -107,11 +108,30 @@ typedef struct SMMUTransCfg { struct SMMUS2Cfg s2cfg; } SMMUTransCfg; +typedef struct SMMUS2Hwpt { + IOMMUFDBackend *iommufd; + uint32_t hwpt_id; + uint32_t ioas_id; +} SMMUS2Hwpt; + +typedef struct SMMUViommu { + void *smmu; + IOMMUFDBackend *iommufd; + IOMMUFDViommu *core; + SMMUS2Hwpt *s2_hwpt; + uint32_t bypass_hwpt_id; + uint32_t abort_hwpt_id; + QLIST_HEAD(, SMMUDevice) device_list; + QLIST_ENTRY(SMMUViommu) next; +} SMMUViommu; + typedef struct SMMUDevice { void *smmu; PCIBus *bus; int devfn; IOMMUMemoryRegion iommu; + HostIOMMUDeviceIOMMUFD *idev; + SMMUViommu *viommu; AddressSpace as; uint32_t cfg_cache_hits; uint32_t cfg_cache_misses; @@ -139,6 +159,7 @@ struct SMMUState { /* Nested SMMU */ bool nested; + SMMUViommu *viommu; GHashTable *smmu_pcibus_by_busptr; GHashTable *configs; /* cache for configuration data */ -- 2.41.0.windows.1