]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
iommufd: Extend IOMMU_GET_HW_INFO to report PASID capability
authorYi Liu <yi.l.liu@intel.com>
Fri, 21 Mar 2025 18:01:42 +0000 (11:01 -0700)
committerJason Gunthorpe <jgg@nvidia.com>
Fri, 28 Mar 2025 13:07:23 +0000 (10:07 -0300)
PASID usage requires PASID support in both device and IOMMU. Since the
iommu drivers always enable the PASID capability for the device if it
is supported, this extends the IOMMU_GET_HW_INFO to report the PASID
capability to userspace. Also, enhances the selftest accordingly.

Link: https://patch.msgid.link/r/20250321180143.8468-5-yi.l.liu@intel.com
Cc: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Tested-by: Zhangfei Gao <zhangfei.gao@linaro.org> #aarch64 platform
Tested-by: Nicolin Chen <nicolinc@nvidia.com>
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
drivers/iommu/iommufd/device.c
drivers/pci/ats.c
include/linux/pci-ats.h
include/uapi/linux/iommufd.h

index 1605f6c0e1eee5e01acee30c55f5837d0496ff9f..2307daad65c0fac6e5a4e7267ac8752b7ca67db9 100644 (file)
@@ -3,6 +3,7 @@
  */
 #include <linux/iommu.h>
 #include <linux/iommufd.h>
+#include <linux/pci-ats.h>
 #include <linux/slab.h>
 #include <uapi/linux/iommufd.h>
 
@@ -1455,7 +1456,8 @@ int iommufd_get_hw_info(struct iommufd_ucmd *ucmd)
        void *data;
        int rc;
 
-       if (cmd->flags || cmd->__reserved)
+       if (cmd->flags || cmd->__reserved[0] || cmd->__reserved[1] ||
+           cmd->__reserved[2])
                return -EOPNOTSUPP;
 
        idev = iommufd_get_device(ucmd, cmd->dev_id);
@@ -1512,6 +1514,36 @@ int iommufd_get_hw_info(struct iommufd_ucmd *ucmd)
        if (device_iommu_capable(idev->dev, IOMMU_CAP_DIRTY_TRACKING))
                cmd->out_capabilities |= IOMMU_HW_CAP_DIRTY_TRACKING;
 
+       cmd->out_max_pasid_log2 = 0;
+       /*
+        * Currently, all iommu drivers enable PASID in the probe_device()
+        * op if iommu and device supports it. So the max_pasids stored in
+        * dev->iommu indicates both PASID support and enable status. A
+        * non-zero dev->iommu->max_pasids means PASID is supported and
+        * enabled. The iommufd only reports PASID capability to userspace
+        * if it's enabled.
+        */
+       if (idev->dev->iommu->max_pasids) {
+               cmd->out_max_pasid_log2 = ilog2(idev->dev->iommu->max_pasids);
+
+               if (dev_is_pci(idev->dev)) {
+                       struct pci_dev *pdev = to_pci_dev(idev->dev);
+                       int ctrl;
+
+                       ctrl = pci_pasid_status(pdev);
+
+                       WARN_ON_ONCE(ctrl < 0 ||
+                                    !(ctrl & PCI_PASID_CTRL_ENABLE));
+
+                       if (ctrl & PCI_PASID_CTRL_EXEC)
+                               cmd->out_capabilities |=
+                                               IOMMU_HW_CAP_PCI_PASID_EXEC;
+                       if (ctrl & PCI_PASID_CTRL_PRIV)
+                               cmd->out_capabilities |=
+                                               IOMMU_HW_CAP_PCI_PASID_PRIV;
+               }
+       }
+
        rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
 out_free:
        kfree(data);
index c6b266c772c81cfbe82fe6dd9d16a262140f818d..ec6c8dbdc5e9c9959e822e016ab301bf483713a5 100644 (file)
@@ -538,4 +538,37 @@ int pci_max_pasids(struct pci_dev *pdev)
        return (1 << FIELD_GET(PCI_PASID_CAP_WIDTH, supported));
 }
 EXPORT_SYMBOL_GPL(pci_max_pasids);
+
+/**
+ * pci_pasid_status - Check the PASID status
+ * @pdev: PCI device structure
+ *
+ * Returns a negative value when no PASID capability is present.
+ * Otherwise the value of the control register is returned.
+ * Status reported are:
+ *
+ * PCI_PASID_CTRL_ENABLE - PASID enabled
+ * PCI_PASID_CTRL_EXEC - Execute permission enabled
+ * PCI_PASID_CTRL_PRIV - Privileged mode enabled
+ */
+int pci_pasid_status(struct pci_dev *pdev)
+{
+       int pasid;
+       u16 ctrl;
+
+       if (pdev->is_virtfn)
+               pdev = pci_physfn(pdev);
+
+       pasid = pdev->pasid_cap;
+       if (!pasid)
+               return -EINVAL;
+
+       pci_read_config_word(pdev, pasid + PCI_PASID_CTRL, &ctrl);
+
+       ctrl &= PCI_PASID_CTRL_ENABLE | PCI_PASID_CTRL_EXEC |
+               PCI_PASID_CTRL_PRIV;
+
+       return ctrl;
+}
+EXPORT_SYMBOL_GPL(pci_pasid_status);
 #endif /* CONFIG_PCI_PASID */
index 0e8b74e63767a6f09d8f4110f0cd74cb417569c9..75c6c86cf09dcbf0515a4b04c496b679718df4ba 100644 (file)
@@ -42,6 +42,7 @@ int pci_enable_pasid(struct pci_dev *pdev, int features);
 void pci_disable_pasid(struct pci_dev *pdev);
 int pci_pasid_features(struct pci_dev *pdev);
 int pci_max_pasids(struct pci_dev *pdev);
+int pci_pasid_status(struct pci_dev *pdev);
 #else /* CONFIG_PCI_PASID */
 static inline int pci_enable_pasid(struct pci_dev *pdev, int features)
 { return -EINVAL; }
@@ -50,6 +51,8 @@ static inline int pci_pasid_features(struct pci_dev *pdev)
 { return -EINVAL; }
 static inline int pci_max_pasids(struct pci_dev *pdev)
 { return -EINVAL; }
+static inline int pci_pasid_status(struct pci_dev *pdev)
+{ return -EINVAL; }
 #endif /* CONFIG_PCI_PASID */
 
 #endif /* LINUX_PCI_ATS_H */
index 6901804ec736ad4d553a43fefd675d9b6983224f..e2c04e58a997d81b444c569fdf35d64aa1ed7e94 100644 (file)
@@ -612,9 +612,17 @@ enum iommu_hw_info_type {
  *                                   IOMMU_HWPT_GET_DIRTY_BITMAP
  *                                   IOMMU_HWPT_SET_DIRTY_TRACKING
  *
+ * @IOMMU_HW_CAP_PCI_PASID_EXEC: Execute Permission Supported, user ignores it
+ *                               when the struct
+ *                               iommu_hw_info::out_max_pasid_log2 is zero.
+ * @IOMMU_HW_CAP_PCI_PASID_PRIV: Privileged Mode Supported, user ignores it
+ *                               when the struct
+ *                               iommu_hw_info::out_max_pasid_log2 is zero.
  */
 enum iommufd_hw_capabilities {
        IOMMU_HW_CAP_DIRTY_TRACKING = 1 << 0,
+       IOMMU_HW_CAP_PCI_PASID_EXEC = 1 << 1,
+       IOMMU_HW_CAP_PCI_PASID_PRIV = 1 << 2,
 };
 
 /**
@@ -630,6 +638,9 @@ enum iommufd_hw_capabilities {
  *                 iommu_hw_info_type.
  * @out_capabilities: Output the generic iommu capability info type as defined
  *                    in the enum iommu_hw_capabilities.
+ * @out_max_pasid_log2: Output the width of PASIDs. 0 means no PASID support.
+ *                      PCI devices turn to out_capabilities to check if the
+ *                      specific capabilities is supported or not.
  * @__reserved: Must be 0
  *
  * Query an iommu type specific hardware information data from an iommu behind
@@ -653,7 +664,8 @@ struct iommu_hw_info {
        __u32 data_len;
        __aligned_u64 data_uptr;
        __u32 out_data_type;
-       __u32 __reserved;
+       __u8 out_max_pasid_log2;
+       __u8 __reserved[3];
        __aligned_u64 out_capabilities;
 };
 #define IOMMU_GET_HW_INFO _IO(IOMMUFD_TYPE, IOMMUFD_CMD_GET_HW_INFO)