[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH v4 17/22] xen/arm: vsmmuv3: Add support to send stage-1 event to guest
On 10/7/26 03:07, Milan Djokic wrote:
Hello Milan
From: Rahul Singh <rahul.singh@xxxxxxx>
Stage-1 translation is handled by guest, therefore stage-1 fault has to
be forwarded to guest.
Signed-off-by: Rahul Singh <rahul.singh@xxxxxxx>
Signed-off-by: Milan Djokic <milan_djokic@xxxxxxxx>
---
xen/drivers/passthrough/arm/smmu-v3.c | 48 ++++++++++++++++++++++++--
xen/drivers/passthrough/arm/vsmmu-v3.c | 44 +++++++++++++++++++++++
xen/drivers/passthrough/arm/vsmmu-v3.h | 8 +++++
3 files changed, 98 insertions(+), 2 deletions(-)
diff --git a/xen/drivers/passthrough/arm/smmu-v3.c
b/xen/drivers/passthrough/arm/smmu-v3.c
index 196ac660ef..e44f872d2d 100644
--- a/xen/drivers/passthrough/arm/smmu-v3.c
+++ b/xen/drivers/passthrough/arm/smmu-v3.c
@@ -871,7 +871,6 @@ static int arm_smmu_init_l2_strtab(struct arm_smmu_device
*smmu, u32 sid)
return 0;
}
-__maybe_unused
static struct arm_smmu_master *
arm_smmu_find_master(struct arm_smmu_device *smmu, u32 sid)
{
@@ -892,10 +891,51 @@ arm_smmu_find_master(struct arm_smmu_device *smmu, u32
sid)
return NULL;
}
+static int arm_smmu_handle_evt(struct arm_smmu_device *smmu, u64 *evt)
+{
+ int ret;
+ struct arm_smmu_master *master;
+ u32 sid = FIELD_GET(EVTQ_0_SID, evt[0]);
Maybe my comment should better be suited for commit #20/22 (where the
vSID->pSID mapping is introduced)...
Look like the event is copied to the guest unchanged (with patch 20
applied). It carries the pSID instead of the vSID, even though patch
20’s table could translate it. So the domain might not find the device,
or might blame a different one whose vSID matches that number. Or I
missed something?
+
+ switch (FIELD_GET(EVTQ_0_ID, evt[0])) {
+ case EVT_ID_TRANSLATION_FAULT:
+ break;
+ case EVT_ID_ADDR_SIZE_FAULT:
+ break;
+ case EVT_ID_ACCESS_FAULT:
+ break;
+ case EVT_ID_PERMISSION_FAULT:
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ /* Stage-2 event */
+ if (evt[1] & EVTQ_1_S2)
+ return -EFAULT;
+
+ mutex_lock(&smmu->streams_mutex);
+ master = arm_smmu_find_master(smmu, sid);
+ if (!master) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ ret = arm_vsmmu_handle_evt(master->domain->d, smmu->dev, evt);
^^^ dereferences master->domain->d with no NULL check. Would it be
possible that a fault event queued earlier can still arrive after the
device is detached (on domain destruction), so master->domain is NULL?
+ if (ret) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+out_unlock:
+ mutex_unlock(&smmu->streams_mutex);
+ return ret;
+}
+
/* IRQ and event handlers */
static void arm_smmu_evtq_tasklet(void *dev)
{
- int i;
+ int i, ret;
struct arm_smmu_device *smmu = dev;
struct arm_smmu_queue *q = &smmu->evtq.q;
struct arm_smmu_ll_queue *llq = &q->llq;
@@ -905,6 +945,10 @@ static void arm_smmu_evtq_tasklet(void *dev)
while (!queue_remove_raw(q, evt)) {
u8 id = FIELD_GET(EVTQ_0_ID, evt[0]);
+ ret = arm_smmu_handle_evt(smmu, evt);
+ if (!ret)
+ continue;
+
dev_info(smmu->dev, "event 0x%02x received:\n", id);
for (i = 0; i < ARRAY_SIZE(evt); ++i)
dev_info(smmu->dev, "\t0x%016llx\n",
diff --git a/xen/drivers/passthrough/arm/vsmmu-v3.c
b/xen/drivers/passthrough/arm/vsmmu-v3.c
index 87342fdf69..b71511c2eb 100644
--- a/xen/drivers/passthrough/arm/vsmmu-v3.c
+++ b/xen/drivers/passthrough/arm/vsmmu-v3.c
@@ -338,6 +338,49 @@ static void arm_vsmmu_send_event(struct virt_smmu *smmu,
return;
}
+static struct virt_smmu *vsmmuv3_find_by_addr(struct domain *d, paddr_t paddr)
+{
+ struct virt_smmu *smmu;
+
+ list_for_each_entry( smmu, &d->arch.viommu_list, viommu_list )
+ {
+ if ( smmu->addr == paddr )
+ return smmu;
+ }
+
+ return NULL;
+}
+
+int arm_vsmmu_handle_evt(struct domain *d, struct device *dev, uint64_t *evt)
+{
+ int ret;
+ struct virt_smmu *smmu;
+
+ if ( is_hardware_domain(d) )
+ {
+ paddr_t paddr;
+ /* Base address */
+ ret = dt_device_get_address(dev_to_dt(dev), 0, &paddr, NULL);
+ if ( ret )
+ return -EINVAL;
+
+ smmu = vsmmuv3_find_by_addr(d, paddr);
+ if ( !smmu )
+ return -ENODEV;
+ }
+ else
+ {
+ smmu = list_entry(d->arch.viommu_list.next,
+ struct virt_smmu, viommu_list);
+ }
+
+ ret = arm_vsmmu_write_evtq(smmu, evt);
+ if ( ret )
+ arm_vsmmu_set_gerror(smmu, GERROR_EVTQ_ABT_ERR);
+
+ return 0;
+}
+
static int arm_vsmmu_find_ste(struct virt_smmu *smmu, uint32_t sid,
uint64_t *ste)
{
@@ -1118,6 +1161,7 @@ static int vsmmuv3_init_single(struct domain *d, paddr_t
addr,
smmu->d = d;
smmu->features = features;
smmu->virq = virq;
+ smmu->addr = addr;
smmu->cmdq.ent_size = CMDQ_ENT_DWORDS * DWORDS_BYTES;
smmu->evtq.ent_size = EVTQ_ENT_DWORDS * DWORDS_BYTES;
diff --git a/xen/drivers/passthrough/arm/vsmmu-v3.h b/xen/drivers/passthrough/arm/vsmmu-v3.h
index 9f4c86abd5..d4b7cf805f 100644
--- a/xen/drivers/passthrough/arm/vsmmu-v3.h
+++ b/xen/drivers/passthrough/arm/vsmmu-v3.h
@@ -8,6 +8,8 @@
void vsmmuv3_set_type(void);
+int arm_vsmmu_handle_evt(struct domain *d, struct device *dev, uint64_t *evt);
+
#else
static inline void __init vsmmuv3_set_type(void)
@@ -15,6 +17,12 @@ static inline void __init vsmmuv3_set_type(void)
return;
}
+static inline int arm_vsmmu_handle_evt(struct domain *d,
+ struct device *dev, uint64_t *evt)
+{
+ return -EINVAL;
+}
+
#endif /* CONFIG_VIRTUAL_ARM_SMMU_V3 */
#endif /* __ARCH_ARM_VSMMU_V3_H__ */
|