diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 4bfcd852f87245487609d50ae9fe099356f1eca9..8573895bc68a3c3da81090f90f6bfdf6aa07f771 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -2352,6 +2352,56 @@ int iommu_attach_pasid_table(struct iommu_domain *domain, } EXPORT_SYMBOL_GPL(iommu_attach_pasid_table); +int iommu_uapi_attach_pasid_table(struct iommu_domain *domain, + void __user *uinfo) +{ + struct iommu_pasid_table_config pasid_table_data = { 0 }; + u32 minsz; + + if (unlikely(!domain->ops->attach_pasid_table)) + return -ENODEV; + + /* + * No new spaces can be added before the variable sized union, the + * minimum size is the offset to the union. + */ + minsz = offsetof(struct iommu_pasid_table_config, vendor_data); + + /* Copy minsz from user to get flags and argsz */ + if (copy_from_user(&pasid_table_data, uinfo, minsz)) + return -EFAULT; + + /* Fields before the variable size union are mandatory */ + if (pasid_table_data.argsz < minsz) + return -EINVAL; + + /* PASID and address granu require additional info beyond minsz */ + if (pasid_table_data.version != PASID_TABLE_CFG_VERSION_1) + return -EINVAL; + if (pasid_table_data.format == IOMMU_PASID_FORMAT_SMMUV3 && + pasid_table_data.argsz < + offsetofend(struct iommu_pasid_table_config, vendor_data.smmuv3)) + return -EINVAL; + + /* + * User might be using a newer UAPI header which has a larger data + * size, we shall support the existing flags within the current + * size. Copy the remaining user data _after_ minsz but not more + * than the current kernel supported size. + */ + if (copy_from_user((void *)&pasid_table_data + minsz, uinfo + minsz, + min_t(u32, pasid_table_data.argsz, sizeof(pasid_table_data)) - minsz)) + return -EFAULT; + + /* Now the argsz is validated, check the content */ + if (pasid_table_data.config < IOMMU_PASID_CONFIG_TRANSLATE || + pasid_table_data.config > IOMMU_PASID_CONFIG_ABORT) + return -EINVAL; + + return domain->ops->attach_pasid_table(domain, &pasid_table_data); +} +EXPORT_SYMBOL_GPL(iommu_uapi_attach_pasid_table); + void iommu_detach_pasid_table(struct iommu_domain *domain) { if (unlikely(!domain->ops->detach_pasid_table)) diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 75b2953dbba6b72dfd8bb981eb75b17f35774439..e8d4c8bf4113cc8070ca488aac7ad832eeaeafba 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -243,12 +243,12 @@ struct iommu_iotlb_gather { * @cache_invalidate: invalidate translation caches * @sva_bind_gpasid: bind guest pasid and mm * @sva_unbind_gpasid: unbind guest pasid and mm + * @attach_pasid_table: attach a pasid table + * @detach_pasid_table: detach the pasid table * @def_domain_type: device default domain type, return value: * - IOMMU_DOMAIN_IDENTITY: must use an identity domain * - IOMMU_DOMAIN_DMA: must use a dma domain * - 0: use the default setting - * @attach_pasid_table: attach a pasid table - * @detach_pasid_table: detach the pasid table * @pgsize_bitmap: bitmap of all possible supported page sizes * @owner: Driver module providing these ops */ @@ -484,6 +484,8 @@ extern int iommu_sva_unbind_gpasid(struct iommu_domain *domain, struct device *dev, ioasid_t pasid); extern int iommu_attach_pasid_table(struct iommu_domain *domain, struct iommu_pasid_table_config *cfg); +extern int iommu_uapi_attach_pasid_table(struct iommu_domain *domain, + void __user *udata); extern void iommu_detach_pasid_table(struct iommu_domain *domain); extern struct iommu_domain *iommu_get_domain_for_dev(struct device *dev); extern struct iommu_domain *iommu_get_dma_domain(struct device *dev); @@ -1174,6 +1176,13 @@ int iommu_attach_pasid_table(struct iommu_domain *domain, return -ENODEV; } +static inline +int iommu_uapi_attach_pasid_table(struct iommu_domain *domain, + void __user *uinfo) +{ + return -ENODEV; +} + static inline void iommu_detach_pasid_table(struct iommu_domain *domain) {} diff --git a/include/uapi/linux/iommu.h b/include/uapi/linux/iommu.h index bed34a8c943093a1951663227ad9fc6c139b1f4d..40c28bb0e1bfa0552802926e46495ea25be0a76f 100644 --- a/include/uapi/linux/iommu.h +++ b/include/uapi/linux/iommu.h @@ -363,30 +363,33 @@ struct iommu_pasid_smmuv3 { /** * struct iommu_pasid_table_config - PASID table data used to bind guest PASID * table to the host IOMMU + * @argsz: User filled size of this data * @version: API version to prepare for future extensions - * @format: format of the PASID table * @base_ptr: guest physical address of the PASID table + * @format: format of the PASID table * @pasid_bits: number of PASID bits used in the PASID table * @config: indicates whether the guest translation stage must * be translated, bypassed or aborted. * @padding: reserved for future use (should be zero) - * @smmuv3: table information when @format is %IOMMU_PASID_FORMAT_SMMUV3 + * @vendor_data.smmuv3: table information when @format is + * %IOMMU_PASID_FORMAT_SMMUV3 */ struct iommu_pasid_table_config { + __u32 argsz; #define PASID_TABLE_CFG_VERSION_1 1 __u32 version; + __u64 base_ptr; #define IOMMU_PASID_FORMAT_SMMUV3 1 __u32 format; - __u64 base_ptr; __u8 pasid_bits; #define IOMMU_PASID_CONFIG_TRANSLATE 1 #define IOMMU_PASID_CONFIG_BYPASS 2 #define IOMMU_PASID_CONFIG_ABORT 3 __u8 config; - __u8 padding[6]; + __u8 padding[2]; union { struct iommu_pasid_smmuv3 smmuv3; - }; + } vendor_data; }; #endif /* _UAPI_IOMMU_H */