|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH v5 20/22] xen/arm: vIOMMU vSID->pSID mapping layer
Introduce a vIOMMU mapping layer to support passthrough of IOMMU devices
attached to different physical IOMMUs, including devices with the same
Stream ID.
Add a new generic vIOMMU API, viommu_allocate_vsid(), which allocates a
guest vSID and maps it to the input pSID and the backing physical SMMU.
Once the mapping is established, the guest uses the vSID for stage-1
commands, and Xen translates the vSID to the corresponding pSID before
processing the command on the physical SMMU.
Implement usage of the new API for dom0less guests. Allocate vSIDs when
creating the guest device tree and replace the original pSIDs with vSIDs
that are subsequently used by the guest driver.
Signed-off-by: Milan Djokic <milan_djokic@xxxxxxxx>
---
xen/arch/arm/include/asm/viommu.h | 12 ++
xen/common/device-tree/dom0less-build.c | 35 +++--
xen/drivers/passthrough/arm/smmu-v3.c | 10 +-
xen/drivers/passthrough/arm/viommu.c | 9 ++
xen/drivers/passthrough/arm/vsmmu-v3.c | 184 +++++++++++++++++++++++-
xen/include/xen/iommu.h | 6 +-
6 files changed, 242 insertions(+), 14 deletions(-)
diff --git a/xen/arch/arm/include/asm/viommu.h
b/xen/arch/arm/include/asm/viommu.h
index 5ae21042f0..3d0eb92655 100644
--- a/xen/arch/arm/include/asm/viommu.h
+++ b/xen/arch/arm/include/asm/viommu.h
@@ -34,6 +34,16 @@ struct viommu_ops {
* Called during domain destruction to free resources used by vIOMMU.
*/
int (*relinquish_resources)(struct domain *d);
+
+ /*
+ * Allocate free vSID/vRID for the guest device and establish vID->pID
mapping
+ * Called during domain device assignment.
+ * Returns 0 on success and sets vsid argument to newly allocated vSID/vRID
+ * mapped to physical ID (id argument).
+ * Negative error code returned if allocation fails.
+ */
+ int (*allocate_vsid)(struct domain *d, uint32_t id, uint32_t phandle,
+ uint32_t *vsid);
};
struct viommu_desc {
@@ -54,6 +64,8 @@ extern struct list_head host_iommu_list;
int domain_viommu_init(struct domain *d, uint8_t viommu_type);
int viommu_relinquish_resources(struct domain *d);
uint8_t viommu_get_type(void);
+int viommu_allocate_vsid(struct domain *d, uint32_t id, uint32_t phandle,
+ uint32_t *vsid);
void add_to_host_iommu_list(paddr_t addr, paddr_t size,
const struct dt_device_node *node,
const void *iommu);
diff --git a/xen/common/device-tree/dom0less-build.c
b/xen/common/device-tree/dom0less-build.c
index 970f89ef68..3b2a888698 100644
--- a/xen/common/device-tree/dom0less-build.c
+++ b/xen/common/device-tree/dom0less-build.c
@@ -32,6 +32,10 @@
#include <xen/static-memory.h>
#include <xen/static-shmem.h>
+#ifdef CONFIG_ARM_VIRTUAL_IOMMU
+#include <asm/viommu.h>
+#endif
+
#define XENSTORE_PFN_LATE_ALLOC UINT64_MAX
static domid_t __initdata xs_domid = DOMID_INVALID;
@@ -333,11 +337,12 @@ static int __init handle_prop_pfdt(struct kernel_info
*kinfo,
}
#ifdef CONFIG_ARM_VIRTUAL_IOMMU
-static int __init modify_pfdt_node(void *pfdt, int nodeoff)
+static int __init modify_pfdt_node(void *pfdt, int nodeoff, struct domain *d)
{
int proplen, i, rc;
const fdt32_t *prop;
fdt32_t *prop_c;
+ uint32_t psid, phandle, vsid;
prop = fdt_getprop(pfdt, nodeoff, "iommus", &proplen);
if ( !prop )
@@ -347,10 +352,23 @@ static int __init modify_pfdt_node(void *pfdt, int
nodeoff)
if ( !prop_c )
return -ENOMEM;
+ /*
+ * Assign <vIOMMU vSID> pairs to iommus property and establish
+ * vSID->pSID mappings
+ */
for ( i = 0; i < proplen / 8; ++i )
{
+ phandle = fdt32_to_cpu(prop[i * 2]);
+ psid = fdt32_to_cpu(prop[i * 2 + 1]);
+ rc = viommu_allocate_vsid(d, psid, phandle, &vsid);
+ if ( rc )
+ {
+ dprintk(XENLOG_ERR, "(DOMID=%u) Failed to allocate vSID for "
+ "pSID: %u, pIOMMU: %u", d->domain_id, psid, phandle);
+ goto out;
+ }
prop_c[i * 2] = cpu_to_fdt32(GUEST_PHANDLE_VSMMUV3);
- prop_c[i * 2 + 1] = prop[i * 2 + 1];
+ prop_c[i * 2 + 1] = cpu_to_fdt32(vsid);
}
rc = fdt_setprop(pfdt, nodeoff, "iommus", prop_c, proplen);
@@ -365,7 +383,8 @@ static int __init modify_pfdt_node(void *pfdt, int nodeoff)
return rc;
}
#else
-static inline int __init modify_pfdt_node(void *pfdt, int nodeoff)
+static inline int __init modify_pfdt_node(void *pfdt, int nodeoff,
+ struct domain *d)
{
return -EOPNOTSUPP;
}
@@ -374,7 +393,7 @@ static inline int __init modify_pfdt_node(void *pfdt, int
nodeoff)
static int __init scan_pfdt_node(struct kernel_info *kinfo, void *pfdt,
int nodeoff,
uint32_t address_cells, uint32_t size_cells,
- bool scan_passthrough_prop)
+ bool scan_passthrough_prop, struct domain *d)
{
int rc = 0;
void *fdt = kinfo->fdt;
@@ -397,12 +416,12 @@ static int __init scan_pfdt_node(struct kernel_info
*kinfo, void *pfdt,
node_next = fdt_first_subnode(pfdt, nodeoff);
while ( node_next > 0 )
{
- rc = modify_pfdt_node(pfdt, node_next);
+ rc = modify_pfdt_node(pfdt, node_next, d);
if ( rc )
return rc;
rc = scan_pfdt_node(kinfo, pfdt, node_next, address_cells, size_cells,
- scan_passthrough_prop);
+ scan_passthrough_prop, d);
if ( rc )
return rc;
@@ -489,7 +508,7 @@ static int __init domain_handle_dtb_boot_module(struct
domain *d,
res = scan_pfdt_node(kinfo, pfdt, node_next,
DT_ROOT_NODE_ADDR_CELLS_DEFAULT,
DT_ROOT_NODE_SIZE_CELLS_DEFAULT,
- false);
+ false, d);
if ( res )
goto out;
continue;
@@ -499,7 +518,7 @@ static int __init domain_handle_dtb_boot_module(struct
domain *d,
res = scan_pfdt_node(kinfo, pfdt, node_next,
DT_ROOT_NODE_ADDR_CELLS_DEFAULT,
DT_ROOT_NODE_SIZE_CELLS_DEFAULT,
- true);
+ true, d);
if ( res )
goto out;
continue;
diff --git a/xen/drivers/passthrough/arm/smmu-v3.c
b/xen/drivers/passthrough/arm/smmu-v3.c
index f213b3de12..b0ec004960 100644
--- a/xen/drivers/passthrough/arm/smmu-v3.c
+++ b/xen/drivers/passthrough/arm/smmu-v3.c
@@ -3103,7 +3103,7 @@ static struct arm_smmu_device *arm_smmu_get_by_dev(const
struct device *dev)
}
static struct iommu_domain *arm_smmu_get_domain_by_sid(struct domain *d,
- u32 sid)
+ u32 sid, const struct arm_smmu_device *smmu)
{
int i;
unsigned long flags;
@@ -3119,6 +3119,9 @@ static struct iommu_domain
*arm_smmu_get_domain_by_sid(struct domain *d,
list_for_each_entry(io_domain, &xen_domain->contexts, list) {
smmu_domain = to_smmu_domain(io_domain);
+ if (smmu_domain->smmu != smmu)
+ continue;
+
spin_lock_irqsave(&smmu_domain->devices_lock, flags);
list_for_each_entry(master, &smmu_domain->devices, domain_head)
{
for (i = 0; i < master->num_streams; i++) {
@@ -3347,7 +3350,8 @@ static void arm_smmu_iommu_xen_domain_teardown(struct
domain *d)
}
static int arm_smmu_attach_guest_config(struct domain *d, uint32_t sid,
- const struct iommu_guest_config *cfg)
+ const struct iommu_guest_config *cfg,
+ const struct arm_smmu_device *smmu)
{
int ret = -EINVAL;
unsigned long flags;
@@ -3357,7 +3361,7 @@ static int arm_smmu_attach_guest_config(struct domain *d,
uint32_t sid,
struct iommu_domain *io_domain;
spin_lock(&xen_domain->lock);
- io_domain = arm_smmu_get_domain_by_sid(d, sid);
+ io_domain = arm_smmu_get_domain_by_sid(d, sid, smmu);
if (!io_domain) {
ret = -ENODEV;
goto out;
diff --git a/xen/drivers/passthrough/arm/viommu.c
b/xen/drivers/passthrough/arm/viommu.c
index b4b91ab1cb..e476567b49 100644
--- a/xen/drivers/passthrough/arm/viommu.c
+++ b/xen/drivers/passthrough/arm/viommu.c
@@ -76,6 +76,15 @@ int viommu_relinquish_resources(struct domain *d)
return cur_viommu->ops->relinquish_resources(d);
}
+int viommu_allocate_vsid(struct domain *d, uint32_t id, uint32_t phandle,
+ uint32_t *vsid)
+{
+ if ( !cur_viommu )
+ return -ENODEV;
+
+ return cur_viommu->ops->allocate_vsid(d, id, phandle, vsid);
+}
+
uint8_t viommu_get_type(void)
{
if ( !cur_viommu || host_iommu_list_incomplete ||
diff --git a/xen/drivers/passthrough/arm/vsmmu-v3.c
b/xen/drivers/passthrough/arm/vsmmu-v3.c
index 24588affe9..5d225ccc08 100644
--- a/xen/drivers/passthrough/arm/vsmmu-v3.c
+++ b/xen/drivers/passthrough/arm/vsmmu-v3.c
@@ -86,6 +86,8 @@
#define smmu_get_ste_s1ctxptr(x) FIELD_PREP(STRTAB_STE_0_S1CTXPTR_MASK, \
FIELD_GET(STRTAB_STE_0_S1CTXPTR_MASK, x))
+#define MAX_VSID (1 << SMMU_IDR1_SIDSIZE)
+
/* event queue entry */
struct arm_smmu_evtq_ent {
/* Common fields */
@@ -133,6 +135,14 @@ struct arm_vsmmu_queue {
uint8_t max_n_shift;
};
+/* vSID->pSID mapping entry */
+struct vsid_entry {
+ struct list_head entry;
+ uint32_t vsid;
+ uint32_t psid;
+ struct arm_smmu_device *smmu;
+};
+
struct virt_smmu {
struct domain *d;
struct list_head viommu_list;
@@ -157,6 +167,8 @@ struct virt_smmu {
spinlock_t cr2_lock;
spinlock_t strtab_cfg_lock;
spinlock_t irq_cfg_lock;
+ spinlock_t vsids_lock;
+ struct list_head vsids;
};
/* Helper functions */
@@ -379,8 +391,31 @@ int arm_vsmmu_handle_evt(struct domain *d, struct device
*dev, uint64_t *evt)
}
else
{
+ uint32_t sid = FIELD_GET(EVTQ_0_SID, evt[0]);
+ struct vsid_entry *vsid;
+ bool found = false;
+
smmu = list_entry(d->arch.viommu_list.next,
struct virt_smmu, viommu_list);
+
+ spin_lock(&smmu->vsids_lock);
+
+ /* Translate pSID->vSID */
+ list_for_each_entry(vsid, &smmu->vsids, entry)
+ {
+ if ( vsid->psid == sid && vsid->smmu->dev == dev )
+ {
+ evt[0] &= ~EVTQ_0_SID;
+ evt[0] |= FIELD_PREP(EVTQ_0_SID, vsid->vsid);
+ found = true;
+ break;
+ }
+ }
+
+ spin_unlock(&smmu->vsids_lock);
+
+ if ( !found )
+ return -ENOENT;
}
ret = arm_vsmmu_write_evtq(smmu, evt);
@@ -558,6 +593,71 @@ static int arm_vsmmu_handle_cfgi_ste(struct virt_smmu
*smmu, uint64_t *cmdptr)
struct arm_vsmmu_s1_trans_cfg s1_cfg = {0};
uint32_t sid = smmu_cmd_get_sid(cmdptr[0]);
struct iommu_guest_config guest_cfg = {0};
+ struct arm_smmu_device *phys_smmu = NULL;
+ struct host_iommu *host_iommu;
+ uint32_t psid;
+ struct arm_smmu_evtq_ent ent = {
+ .opcode = EVT_ID_BAD_STREAMID,
+ .sid = sid,
+ .c_bad_ste_streamid = {
+ .ssid = 0,
+ .ssv = false,
+ },
+ };
+
+ /* vIOMMU->pIOMMU identity mapped for HW domain */
+ if ( domain_use_host_layout(d) )
+ {
+ psid = sid;
+ list_for_each_entry(host_iommu, &host_iommu_list, entry)
+ {
+ if ( host_iommu->addr == smmu->addr )
+ {
+ phys_smmu = (struct arm_smmu_device *) host_iommu->iommu;
+ break;
+ }
+ }
+ }
+ else {
+ struct vsid_entry *vsid_entry;
+ bool entry_found = false;
+
+ spin_lock(&smmu->vsids_lock);
+ /* vSID out of range or no vSIDs allocated */
+ if ( sid >= MAX_VSID || list_empty(&smmu->vsids) )
+ {
+ spin_unlock(&smmu->vsids_lock);
+ arm_vsmmu_send_event(smmu, &ent);
+ return -EINVAL;
+ }
+
+ /* get vSID entry corresponding to passed vSID */
+ list_for_each_entry(vsid_entry, &smmu->vsids, entry)
+ if ( vsid_entry->vsid == sid )
+ {
+ entry_found = true;
+ break;
+ }
+
+ /* vSID not mapped */
+ if ( !entry_found )
+ {
+ spin_unlock(&smmu->vsids_lock);
+ arm_vsmmu_send_event(smmu, &ent);
+ return -EINVAL;
+ }
+
+ psid = vsid_entry->psid;
+ phys_smmu = vsid_entry->smmu;
+ spin_unlock(&smmu->vsids_lock);
+ }
+
+ /* vSID mapped to invalid physical SMMU */
+ if (!phys_smmu)
+ {
+ arm_vsmmu_send_event(smmu, &ent);
+ return -EINVAL;
+ }
ret = arm_vsmmu_find_ste(smmu, sid, ste);
if ( ret )
@@ -578,7 +678,7 @@ static int arm_vsmmu_handle_cfgi_ste(struct virt_smmu
*smmu, uint64_t *cmdptr)
else
guest_cfg.config = ARM_SMMU_DOMAIN_S1;
- ret = hd->platform_ops->attach_guest_config(d, sid, &guest_cfg);
+ ret = hd->platform_ops->attach_guest_config(d, psid, &guest_cfg,
phys_smmu);
if ( ret )
return ret;
@@ -1189,6 +1289,13 @@ static int vsmmuv3_init_single(struct domain *d, paddr_t
addr,
smmu->cmdq.ent_size = CMDQ_ENT_DWORDS * DWORDS_BYTES;
smmu->evtq.ent_size = EVTQ_ENT_DWORDS * DWORDS_BYTES;
+ /*
+ * vSID->pSID mapping layer is only used in the context of domains that
+ * are not direct-mapped
+ */
+ if ( !domain_use_host_layout(d) )
+ INIT_LIST_HEAD(&smmu->vsids);
+
spin_lock_init(&smmu->cmd_queue_lock);
spin_lock_init(&smmu->evt_queue_lock);
spin_lock_init(&smmu->gerror_lock);
@@ -1197,6 +1304,7 @@ static int vsmmuv3_init_single(struct domain *d, paddr_t
addr,
spin_lock_init(&smmu->cr2_lock);
spin_lock_init(&smmu->strtab_cfg_lock);
spin_lock_init(&smmu->irq_cfg_lock);
+ spin_lock_init(&smmu->vsids_lock);
ret = vgic_reserve_virq(d, virq);
if ( !ret )
@@ -1317,8 +1425,16 @@ static int vsmmuv3_relinquish_resources(struct domain *d)
if ( list_head_is_null(&d->arch.viommu_list) )
return 0;
- list_for_each_entry_safe(pos, temp, &d->arch.viommu_list, viommu_list )
+ list_for_each_entry_safe(pos, temp, &d->arch.viommu_list, viommu_list)
{
+ struct vsid_entry *vsid, *vsid_temp;
+
+ list_for_each_entry_safe(vsid, vsid_temp, &pos->vsids, entry)
+ {
+ list_del(&vsid->entry);
+ xfree(vsid);
+ }
+
list_del(&pos->viommu_list);
xfree(pos);
}
@@ -1326,8 +1442,72 @@ static int vsmmuv3_relinquish_resources(struct domain *d)
return 0;
}
+static int vsmmuv3_allocate_vsid(struct domain *d, uint32_t id,
+ uint32_t phandle, uint32_t *vsid)
+{
+ uint32_t i = 0;
+ struct virt_smmu *smmu;
+ struct host_iommu *host_iommu;
+ struct arm_smmu_device *phys_smmu = NULL;
+ struct vsid_entry *entry;
+ struct list_head *vsid_head;
+
+ /* vSID->pSID mapping layer not used for direct-mapped domains */
+ if ( domain_use_host_layout(d) )
+ return -EOPNOTSUPP;
+
+ if ( list_head_is_null(&d->arch.viommu_list) )
+ return -ENODEV;
+
+ smmu = list_first_entry(&d->arch.viommu_list, struct virt_smmu,
viommu_list);
+
+ /*
+ * Find the physical SMMU corresponding to the device-tree phandle.
+ */
+ list_for_each_entry(host_iommu, &host_iommu_list, entry)
+ {
+ if ( host_iommu->dt_node->phandle == phandle )
+ {
+ phys_smmu = (struct arm_smmu_device *) host_iommu->iommu;
+ break;
+ }
+ }
+
+ if ( !phys_smmu )
+ return -ENODEV;
+
+ spin_lock(&smmu->vsids_lock);
+ /* Get first free vSID index */
+ list_for_each(vsid_head, &smmu->vsids)
+ i++;
+
+ /* Max number of vSIDs already allocated? */
+ if ( i == MAX_VSID )
+ {
+ spin_unlock(&smmu->vsids_lock);
+ return -ENOMEM;
+ }
+
+ entry = xzalloc(struct vsid_entry);
+ if ( !entry )
+ {
+ spin_unlock(&smmu->vsids_lock);
+ return -ENOMEM;
+ }
+
+ /* Establish vSID->pSID mapping */
+ entry->vsid = i;
+ entry->psid = id;
+ entry->smmu = phys_smmu;
+ *vsid = entry->vsid;
+ list_add_tail(&entry->entry, &smmu->vsids);
+ spin_unlock(&smmu->vsids_lock);
+ return 0;
+}
+
static const struct viommu_ops vsmmuv3_ops = {
.domain_init = domain_vsmmuv3_init,
+ .allocate_vsid = vsmmuv3_allocate_vsid,
.relinquish_resources = vsmmuv3_relinquish_resources,
};
diff --git a/xen/include/xen/iommu.h b/xen/include/xen/iommu.h
index 253f8bb6cb..c94ee4f08e 100644
--- a/xen/include/xen/iommu.h
+++ b/xen/include/xen/iommu.h
@@ -312,7 +312,10 @@ static inline int iommu_add_dt_pci_sideband_ids(struct
pci_dev *pdev)
#endif /* HAS_DEVICE_TREE_DISCOVERY */
struct page_info;
+#ifdef CONFIG_ARM
struct iommu_guest_config;
+struct arm_smmu_device;
+#endif
/*
* Any non-zero value returned from callbacks of this type will cause the
@@ -391,7 +394,8 @@ struct iommu_ops {
#ifdef CONFIG_ARM
int (*attach_guest_config)(struct domain *d, uint32_t sid,
- const struct iommu_guest_config *cfg);
+ const struct iommu_guest_config *cfg,
+ const struct arm_smmu_device *smmu);
#endif
};
--
2.43.0
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |