284 lines
9.5 KiB
Diff
284 lines
9.5 KiB
Diff
|
|
From 539e12641dc2db30a6fea7a0f061e163bc245d79 Mon Sep 17 00:00:00 2001
|
||
|
|
From: Nicolin Chen <nicolinc@nvidia.com>
|
||
|
|
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 <nicolinc@nvidia.com>
|
||
|
|
---
|
||
|
|
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 <linux/iommufd.h>
|
||
|
|
|
||
|
|
#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
|
||
|
|
|