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.
Unified · Split
| @@ -433,4 +433,21 @@ struct iommufd_viommu *arm_vsmmu_alloc(struct device *dev, · 432 unmodified lines | ||
| 433 | 433 | return &vsmmu->core; |
| 434 | 434 | } |
| 435 | 435 | |
| 436 | +int arm_vmaster_report_event(struct arm_smmu_vmaster *vmaster, u64 *evt) | |
| 437 | +{ | |
| 438 | + struct iommu_vevent_arm_smmuv3 vevt; | |
| 439 | + int i; | |
| 440 | + | |
| 441 | + lockdep_assert_held(&vmaster->vsmmu->smmu->streams_mutex); | |
| 442 | + | |
| 443 | + vevt.evt[0] = cpu_to_le64((evt[0] & ~EVTQ_0_SID) | | |
| 444 | + FIELD_PREP(EVTQ_0_SID, vmaster->vsid)); | |
| 445 | + for (i = 1; i < EVTQ_ENT_DWORDS; i++) | |
| 446 | + vevt.evt[i] = cpu_to_le64(evt[i]); | |
| 447 | + | |
| 448 | + return iommufd_viommu_report_event(&vmaster->vsmmu->core, | |
| 449 | + IOMMU_VEVENTQ_TYPE_ARM_SMMUV3, &vevt, | |
| 450 | + sizeof(vevt)); | |
| 451 | +} | |
| 452 | + | |
| 436 | 453 | MODULE_IMPORT_NS("IOMMUFD"); |
| @@ -1813,8 +1813,8 @@ static void arm_smmu_decode_event(struct arm_smmu_device *smmu, u64 *raw, · 1812 unmodified lines | ||
| 1813 | 1813 | mutex_unlock(&smmu->streams_mutex); |
| 1814 | 1814 | } |
| 1815 | 1815 | |
| 1816 | −static int arm_smmu_handle_event(struct arm_smmu_device *smmu, | |
| 1817 | − struct arm_smmu_event *event) | |
| 1816 | +static int arm_smmu_handle_event(struct arm_smmu_device *smmu, u64 *evt, | |
| 1817 | + struct arm_smmu_event *event) | |
| 1818 | 1818 | { |
| 1819 | 1819 | int ret = 0; |
| 1820 | 1820 | u32 perm = 0; |
| @@ -1823,6 +1823,10 @@ static int arm_smmu_handle_event(struct arm_smmu_device *smmu, · 2 unmodified lines | ||
| 1823 | 1823 | struct iommu_fault *flt = &fault_evt.fault; |
| 1824 | 1824 | |
| 1825 | 1825 | switch (event->id) { |
| 1826 | + case EVT_ID_BAD_STE_CONFIG: | |
| 1827 | + case EVT_ID_STREAM_DISABLED_FAULT: | |
| 1828 | + case EVT_ID_BAD_SUBSTREAMID_CONFIG: | |
| 1829 | + case EVT_ID_BAD_CD_CONFIG: | |
| 1826 | 1830 | case EVT_ID_TRANSLATION_FAULT: |
| 1827 | 1831 | case EVT_ID_ADDR_SIZE_FAULT: |
| 1828 | 1832 | case EVT_ID_ACCESS_FAULT: |
| @@ -1832,31 +1836,30 @@ static int arm_smmu_handle_event(struct arm_smmu_device *smmu, · 3 unmodified lines | ||
| 1832 | 1836 | return -EOPNOTSUPP; |
| 1833 | 1837 | } |
| 1834 | 1838 | |
| 1835 | − if (!event->stall) | |
| 1836 | − return -EOPNOTSUPP; | |
| 1837 | − | |
| 1838 | − if (event->read) | |
| 1839 | − perm |= IOMMU_FAULT_PERM_READ; | |
| 1840 | − else | |
| 1841 | − perm |= IOMMU_FAULT_PERM_WRITE; | |
| 1839 | + if (event->stall) { | |
| 1840 | + if (event->read) | |
| 1841 | + perm |= IOMMU_FAULT_PERM_READ; | |
| 1842 | + else | |
| 1843 | + perm |= IOMMU_FAULT_PERM_WRITE; | |
| 1842 | 1844 | |
| 1843 | − if (event->instruction) | |
| 1844 | − perm |= IOMMU_FAULT_PERM_EXEC; | |
| 1845 | + if (event->instruction) | |
| 1846 | + perm |= IOMMU_FAULT_PERM_EXEC; | |
| 1845 | 1847 | |
| 1846 | − if (event->privileged) | |
| 1847 | − perm |= IOMMU_FAULT_PERM_PRIV; | |
| 1848 | + if (event->privileged) | |
| 1849 | + perm |= IOMMU_FAULT_PERM_PRIV; | |
| 1848 | 1850 | |
| 1849 | − flt->type = IOMMU_FAULT_PAGE_REQ; | |
| 1850 | − flt->prm = (struct iommu_fault_page_request) { | |
| 1851 | − .flags = IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE, | |
| 1852 | − .grpid = event->stag, | |
| 1853 | − .perm = perm, | |
| 1854 | − .addr = event->iova, | |
| 1855 | − }; | |
| 1851 | + flt->type = IOMMU_FAULT_PAGE_REQ; | |
| 1852 | + flt->prm = (struct iommu_fault_page_request){ | |
| 1853 | + .flags = IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE, | |
| 1854 | + .grpid = event->stag, | |
| 1855 | + .perm = perm, | |
| 1856 | + .addr = event->iova, | |
| 1857 | + }; | |
| 1856 | 1858 | |
| 1857 | − if (event->ssv) { | |
| 1858 | − flt->prm.flags |= IOMMU_FAULT_PAGE_REQUEST_PASID_VALID; | |
| 1859 | − flt->prm.pasid = event->ssid; | |
| 1859 | + if (event->ssv) { | |
| 1860 | + flt->prm.flags |= IOMMU_FAULT_PAGE_REQUEST_PASID_VALID; | |
| 1861 | + flt->prm.pasid = event->ssid; | |
| 1862 | + } | |
| 1860 | 1863 | } |
| 1861 | 1864 | |
| 1862 | 1865 | mutex_lock(&smmu->streams_mutex); |
| @@ -1866,7 +1869,12 @@ static int arm_smmu_handle_event(struct arm_smmu_device *smmu, · 3 unmodified lines | ||
| 1866 | 1869 | goto out_unlock; |
| 1867 | 1870 | } |
| 1868 | 1871 | |
| 1869 | − ret = iommu_report_device_fault(master->dev, &fault_evt); | |
| 1872 | + if (event->stall) | |
| 1873 | + ret = iommu_report_device_fault(master->dev, &fault_evt); | |
| 1874 | + else if (master->vmaster && !event->s2) | |
| 1875 | + ret = arm_vmaster_report_event(master->vmaster, evt); | |
| 1876 | + else | |
| 1877 | + ret = -EOPNOTSUPP; /* Unhandled events should be pinned */ | |
| 1870 | 1878 | out_unlock: |
| 1871 | 1879 | mutex_unlock(&smmu->streams_mutex); |
| 1872 | 1880 | return ret; |
| @@ -1944,7 +1952,7 @@ static irqreturn_t arm_smmu_evtq_thread(int irq, void *dev) · 71 unmodified lines | ||
| 1944 | 1952 | do { |
| 1945 | 1953 | while (!queue_remove_raw(q, evt)) { |
| 1946 | 1954 | arm_smmu_decode_event(smmu, evt, &event); |
| 1947 | − if (arm_smmu_handle_event(smmu, &event)) | |
| 1955 | + if (arm_smmu_handle_event(smmu, evt, &event)) | |
| 1948 | 1956 | arm_smmu_dump_event(smmu, evt, &event, &rs); |
| 1949 | 1957 | |
| 1950 | 1958 | put_device(event.dev); |
| @@ -1066,6 +1066,7 @@ int arm_smmu_attach_prepare_vmaster(struct arm_smmu_attach_state *state, · 1065 unmodified lines | ||
| 1066 | 1066 | struct arm_smmu_nested_domain *nested_domain); |
| 1067 | 1067 | void arm_smmu_attach_commit_vmaster(struct arm_smmu_attach_state *state); |
| 1068 | 1068 | void arm_smmu_master_clear_vmaster(struct arm_smmu_master *master); |
| 1069 | +int arm_vmaster_report_event(struct arm_smmu_vmaster *vmaster, u64 *evt); | |
| 1069 | 1070 | #else |
| 1070 | 1071 | #define arm_smmu_hw_info NULL |
| 1071 | 1072 | #define arm_vsmmu_alloc NULL |
| @@ -1086,6 +1087,12 @@ static inline void · 14 unmodified lines | ||
| 1086 | 1087 | arm_smmu_master_clear_vmaster(struct arm_smmu_master *master) |
| 1087 | 1088 | { |
| 1088 | 1089 | } |
| 1090 | + | |
| 1091 | +static inline int arm_vmaster_report_event(struct arm_smmu_vmaster *vmaster, | |
| 1092 | + u64 *evt) | |
| 1093 | +{ | |
| 1094 | + return -EOPNOTSUPP; | |
| 1095 | +} | |
| 1089 | 1096 | #endif /* CONFIG_ARM_SMMU_V3_IOMMUFD */ |
| 1090 | 1097 | |
| 1091 | 1098 | #endif /* _ARM_SMMU_V3_H */ |
| @@ -1054,9 +1054,32 @@ struct iommufd_vevent_header { · 1053 unmodified lines | ||
| 1054 | 1054 | /** |
| 1055 | 1055 | * enum iommu_veventq_type - Virtual Event Queue Type |
| 1056 | 1056 | * @IOMMU_VEVENTQ_TYPE_DEFAULT: Reserved for future use |
| 1057 | + * @IOMMU_VEVENTQ_TYPE_ARM_SMMUV3: ARM SMMUv3 Virtual Event Queue | |
| 1057 | 1058 | */ |
| 1058 | 1059 | enum iommu_veventq_type { |
| 1059 | 1060 | IOMMU_VEVENTQ_TYPE_DEFAULT = 0, |
| 1061 | + IOMMU_VEVENTQ_TYPE_ARM_SMMUV3 = 1, | |
| 1062 | +}; | |
| 1063 | + | |
| 1064 | +/** | |
| 1065 | + * struct iommu_vevent_arm_smmuv3 - ARM SMMUv3 Virtual Event | |
| 1066 | + * (IOMMU_VEVENTQ_TYPE_ARM_SMMUV3) | |
| 1067 | + * @evt: 256-bit ARM SMMUv3 Event record, little-endian. | |
| 1068 | + * Reported event records: (Refer to "7.3 Event records" in SMMUv3 HW Spec) | |
| 1069 | + * - 0x04 C_BAD_STE | |
| 1070 | + * - 0x06 F_STREAM_DISABLED | |
| 1071 | + * - 0x08 C_BAD_SUBSTREAMID | |
| 1072 | + * - 0x0a C_BAD_CD | |
| 1073 | + * - 0x10 F_TRANSLATION | |
| 1074 | + * - 0x11 F_ADDR_SIZE | |
| 1075 | + * - 0x12 F_ACCESS | |
| 1076 | + * - 0x13 F_PERMISSION | |
| 1077 | + * | |
| 1078 | + * StreamID field reports a virtual device ID. To receive a virtual event for a | |
| 1079 | + * device, a vDEVICE must be allocated via IOMMU_VDEVICE_ALLOC. | |
| 1080 | + */ | |
| 1081 | +struct iommu_vevent_arm_smmuv3 { | |
| 1082 | + __aligned_le64 evt[4]; | |
| 1060 | 1083 | }; |
| 1061 | 1084 | |
| 1062 | 1085 | /** |