]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
iommu/arm-smmu-v3: Report events that belong to devices attached to vIOMMU
authorNicolin Chen <nicolinc@nvidia.com>
Tue, 11 Mar 2025 19:44:31 +0000 (12:44 -0700)
committerJason Gunthorpe <jgg@nvidia.com>
Tue, 18 Mar 2025 17:17:48 +0000 (14:17 -0300)
Aside from the IOPF framework, iommufd provides an additional pathway to
report hardware events, via the vEVENTQ of vIOMMU infrastructure.

Define an iommu_vevent_arm_smmuv3 uAPI structure, and report stage-1 events
in the threaded IRQ handler. Also, add another four event record types that
can be forwarded to a VM.

Link: https://patch.msgid.link/r/5cf6719682fdfdabffdb08374cdf31ad2466d75a.1741719725.git.nicolinc@nvidia.com
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Reviewed-by: Pranjal Shrivastava <praan@google.com>
Acked-by: Will Deacon <will@kernel.org>
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
include/uapi/linux/iommufd.h

index dfc80e1a8e2e398b8ceeec7b0308ed86ee93087d..65adfed56969c89dc08dd136607b6d24300e9fed 100644 (file)
@@ -433,4 +433,21 @@ struct iommufd_viommu *arm_vsmmu_alloc(struct device *dev,
        return &vsmmu->core;
 }
 
+int arm_vmaster_report_event(struct arm_smmu_vmaster *vmaster, u64 *evt)
+{
+       struct iommu_vevent_arm_smmuv3 vevt;
+       int i;
+
+       lockdep_assert_held(&vmaster->vsmmu->smmu->streams_mutex);
+
+       vevt.evt[0] = cpu_to_le64((evt[0] & ~EVTQ_0_SID) |
+                                 FIELD_PREP(EVTQ_0_SID, vmaster->vsid));
+       for (i = 1; i < EVTQ_ENT_DWORDS; i++)
+               vevt.evt[i] = cpu_to_le64(evt[i]);
+
+       return iommufd_viommu_report_event(&vmaster->vsmmu->core,
+                                          IOMMU_VEVENTQ_TYPE_ARM_SMMUV3, &vevt,
+                                          sizeof(vevt));
+}
+
 MODULE_IMPORT_NS("IOMMUFD");
index 964d2cf27d3d2b1839e655d3b7258e0805c5c632..5fa817a8f5f11dbdb671ab5ab244882b34f395d8 100644 (file)
@@ -1813,8 +1813,8 @@ static void arm_smmu_decode_event(struct arm_smmu_device *smmu, u64 *raw,
        mutex_unlock(&smmu->streams_mutex);
 }
 
-static int arm_smmu_handle_event(struct arm_smmu_device *smmu,
-                              struct arm_smmu_event *event)
+static int arm_smmu_handle_event(struct arm_smmu_device *smmu, u64 *evt,
+                                struct arm_smmu_event *event)
 {
        int ret = 0;
        u32 perm = 0;
@@ -1823,6 +1823,10 @@ static int arm_smmu_handle_event(struct arm_smmu_device *smmu,
        struct iommu_fault *flt = &fault_evt.fault;
 
        switch (event->id) {
+       case EVT_ID_BAD_STE_CONFIG:
+       case EVT_ID_STREAM_DISABLED_FAULT:
+       case EVT_ID_BAD_SUBSTREAMID_CONFIG:
+       case EVT_ID_BAD_CD_CONFIG:
        case EVT_ID_TRANSLATION_FAULT:
        case EVT_ID_ADDR_SIZE_FAULT:
        case EVT_ID_ACCESS_FAULT:
@@ -1832,31 +1836,30 @@ static int arm_smmu_handle_event(struct arm_smmu_device *smmu,
                return -EOPNOTSUPP;
        }
 
-       if (!event->stall)
-               return -EOPNOTSUPP;
-
-       if (event->read)
-               perm |= IOMMU_FAULT_PERM_READ;
-       else
-               perm |= IOMMU_FAULT_PERM_WRITE;
+       if (event->stall) {
+               if (event->read)
+                       perm |= IOMMU_FAULT_PERM_READ;
+               else
+                       perm |= IOMMU_FAULT_PERM_WRITE;
 
-       if (event->instruction)
-               perm |= IOMMU_FAULT_PERM_EXEC;
+               if (event->instruction)
+                       perm |= IOMMU_FAULT_PERM_EXEC;
 
-       if (event->privileged)
-               perm |= IOMMU_FAULT_PERM_PRIV;
+               if (event->privileged)
+                       perm |= IOMMU_FAULT_PERM_PRIV;
 
-       flt->type = IOMMU_FAULT_PAGE_REQ;
-       flt->prm = (struct iommu_fault_page_request) {
-               .flags = IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE,
-               .grpid = event->stag,
-               .perm = perm,
-               .addr = event->iova,
-       };
+               flt->type = IOMMU_FAULT_PAGE_REQ;
+               flt->prm = (struct iommu_fault_page_request){
+                       .flags = IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE,
+                       .grpid = event->stag,
+                       .perm = perm,
+                       .addr = event->iova,
+               };
 
-       if (event->ssv) {
-               flt->prm.flags |= IOMMU_FAULT_PAGE_REQUEST_PASID_VALID;
-               flt->prm.pasid = event->ssid;
+               if (event->ssv) {
+                       flt->prm.flags |= IOMMU_FAULT_PAGE_REQUEST_PASID_VALID;
+                       flt->prm.pasid = event->ssid;
+               }
        }
 
        mutex_lock(&smmu->streams_mutex);
@@ -1866,7 +1869,12 @@ static int arm_smmu_handle_event(struct arm_smmu_device *smmu,
                goto out_unlock;
        }
 
-       ret = iommu_report_device_fault(master->dev, &fault_evt);
+       if (event->stall)
+               ret = iommu_report_device_fault(master->dev, &fault_evt);
+       else if (master->vmaster && !event->s2)
+               ret = arm_vmaster_report_event(master->vmaster, evt);
+       else
+               ret = -EOPNOTSUPP; /* Unhandled events should be pinned */
 out_unlock:
        mutex_unlock(&smmu->streams_mutex);
        return ret;
@@ -1944,7 +1952,7 @@ static irqreturn_t arm_smmu_evtq_thread(int irq, void *dev)
        do {
                while (!queue_remove_raw(q, evt)) {
                        arm_smmu_decode_event(smmu, evt, &event);
-                       if (arm_smmu_handle_event(smmu, &event))
+                       if (arm_smmu_handle_event(smmu, evt, &event))
                                arm_smmu_dump_event(smmu, evt, &event, &rs);
 
                        put_device(event.dev);
index 557b300a3a0f54bfa26e70c4c1ca9bd990214942..df06076a16982231b360a84b3e0876cebc279a34 100644 (file)
@@ -1066,6 +1066,7 @@ int arm_smmu_attach_prepare_vmaster(struct arm_smmu_attach_state *state,
                                    struct arm_smmu_nested_domain *nested_domain);
 void arm_smmu_attach_commit_vmaster(struct arm_smmu_attach_state *state);
 void arm_smmu_master_clear_vmaster(struct arm_smmu_master *master);
+int arm_vmaster_report_event(struct arm_smmu_vmaster *vmaster, u64 *evt);
 #else
 #define arm_smmu_hw_info NULL
 #define arm_vsmmu_alloc NULL
@@ -1086,6 +1087,12 @@ static inline void
 arm_smmu_master_clear_vmaster(struct arm_smmu_master *master)
 {
 }
+
+static inline int arm_vmaster_report_event(struct arm_smmu_vmaster *vmaster,
+                                          u64 *evt)
+{
+       return -EOPNOTSUPP;
+}
 #endif /* CONFIG_ARM_SMMU_V3_IOMMUFD */
 
 #endif /* _ARM_SMMU_V3_H */
index dbb8787d9c635ae447c566a0357ff1021d945dde..8719d4f5d6183a5ab4937aa82136da6fbcd2988c 100644 (file)
@@ -1054,9 +1054,32 @@ struct iommufd_vevent_header {
 /**
  * enum iommu_veventq_type - Virtual Event Queue Type
  * @IOMMU_VEVENTQ_TYPE_DEFAULT: Reserved for future use
+ * @IOMMU_VEVENTQ_TYPE_ARM_SMMUV3: ARM SMMUv3 Virtual Event Queue
  */
 enum iommu_veventq_type {
        IOMMU_VEVENTQ_TYPE_DEFAULT = 0,
+       IOMMU_VEVENTQ_TYPE_ARM_SMMUV3 = 1,
+};
+
+/**
+ * struct iommu_vevent_arm_smmuv3 - ARM SMMUv3 Virtual Event
+ *                                  (IOMMU_VEVENTQ_TYPE_ARM_SMMUV3)
+ * @evt: 256-bit ARM SMMUv3 Event record, little-endian.
+ *       Reported event records: (Refer to "7.3 Event records" in SMMUv3 HW Spec)
+ *       - 0x04 C_BAD_STE
+ *       - 0x06 F_STREAM_DISABLED
+ *       - 0x08 C_BAD_SUBSTREAMID
+ *       - 0x0a C_BAD_CD
+ *       - 0x10 F_TRANSLATION
+ *       - 0x11 F_ADDR_SIZE
+ *       - 0x12 F_ACCESS
+ *       - 0x13 F_PERMISSION
+ *
+ * StreamID field reports a virtual device ID. To receive a virtual event for a
+ * device, a vDEVICE must be allocated via IOMMU_VDEVICE_ALLOC.
+ */
+struct iommu_vevent_arm_smmuv3 {
+       __aligned_le64 evt[4];
 };
 
 /**