From: Yi Liu <yi.l.liu@intel.com>
To: alex.williamson@redhat.com, jgg@nvidia.com, kevin.tian@intel.com,
baolu.lu@linux.intel.com
Cc: joro@8bytes.org, eric.auger@redhat.com, nicolinc@nvidia.com,
kvm@vger.kernel.org, chao.p.peng@linux.intel.com,
yi.l.liu@intel.com, iommu@lists.linux.dev,
zhenzhong.duan@intel.com, vasant.hegde@amd.com, will@kernel.org
Subject: [PATCH v5 4/5] vfio: Add vfio_copy_user_data()
Date: Fri, 8 Nov 2024 04:17:41 -0800 [thread overview]
Message-ID: <20241108121742.18889-5-yi.l.liu@intel.com> (raw)
In-Reply-To: <20241108121742.18889-1-yi.l.liu@intel.com>
This generalizes the logic of copying user data when the user struct
Have new fields introduced. The helpers can be used by the vfio uapis
that have the argsz and flags fields in the beginning 8 bytes.
As an example, the vfio_device_{at|de}tach_iommufd_pt paths are updated
to use the helpers.
The flags may be defined to mark a new field in the structure, reuse
reserved fields, or special handling of an existing field. The extended
size would differ for different flags. Each user API that wants to use
the generalized helpers should define an array to store the corresponding
extended sizes for each defined flag.
For example, we start out with the below, minsz is 12.
struct vfio_foo_struct {
__u32 argsz;
__u32 flags;
__u32 pt_id;
};
And then here it becomes:
struct vfio_foo_struct {
__u32 argsz;
__u32 flags;
#define VFIO_FOO_STRUCT_PASID (1 << 0)
__u32 pt_id;
__u32 pasid;
};
The array is { 16 }.
If the next flag is simply related to the processing of @pt_id and
doesn't require @pasid, then the extended size of the new flag is
12. The array become { 16, 12 }
struct vfio_foo_struct {
__u32 argsz;
__u32 flags;
#define VFIO_FOO_STRUCT_PASID (1 << 0)
#define VFIO_FOO_STRUCT_SPECICAL_PTID (1 << 1)
__u32 pt_id;
__u32 pasid;
};
Similarly, rather than adding new field, we might have reused a previously
reserved field, for instance what if we already expanded the structure
as the below, array is already { 24 }.
struct vfio_foo_struct {
__u32 argsz;
__u32 flags;
#define VFIO_FOO_STRUCT_XXX (1 << 0)
__u32 pt_id;
__u32 reserved;
__u64 xxx;
};
If we then want to add @pasid, we might really prefer to take advantage
of that reserved field and the array becomes { 24, 16 }.
struct vfio_foo_struct {
__u32 argsz;
__u32 flags;
#define VFIO_FOO_STRUCT_XXX (1 << 0)
#define VFIO_FOO_STRUCT_PASID (1 << 1)
__u32 pt_id;
__u32 reserved;
__u64 xxx;
};
Suggested-by: Alex Williamson <alex.williamson@redhat.com>
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
---
drivers/vfio/device_cdev.c | 81 +++++++++++++-------------------------
drivers/vfio/vfio.h | 18 +++++++++
drivers/vfio/vfio_main.c | 55 ++++++++++++++++++++++++++
3 files changed, 100 insertions(+), 54 deletions(-)
diff --git a/drivers/vfio/device_cdev.c b/drivers/vfio/device_cdev.c
index 4519f482e212..35c7664b9a97 100644
--- a/drivers/vfio/device_cdev.c
+++ b/drivers/vfio/device_cdev.c
@@ -159,40 +159,33 @@ void vfio_df_unbind_iommufd(struct vfio_device_file *df)
vfio_device_unblock_group(device);
}
+#define VFIO_ATTACH_FLAGS_MASK VFIO_DEVICE_ATTACH_PASID
+static unsigned long
+vfio_attach_xends[ilog2(VFIO_ATTACH_FLAGS_MASK) + 1] = {
+ XEND_SIZE(VFIO_DEVICE_ATTACH_PASID,
+ struct vfio_device_attach_iommufd_pt, pasid),
+};
+
+#define VFIO_DETACH_FLAGS_MASK VFIO_DEVICE_DETACH_PASID
+static unsigned long
+vfio_detach_xends[ilog2(VFIO_DETACH_FLAGS_MASK) + 1] = {
+ XEND_SIZE(VFIO_DEVICE_DETACH_PASID,
+ struct vfio_device_detach_iommufd_pt, pasid),
+};
+
int vfio_df_ioctl_attach_pt(struct vfio_device_file *df,
struct vfio_device_attach_iommufd_pt __user *arg)
{
struct vfio_device_attach_iommufd_pt attach;
struct vfio_device *device = df->device;
- unsigned long minsz, xend = 0;
int ret;
- minsz = offsetofend(struct vfio_device_attach_iommufd_pt, pt_id);
-
- if (copy_from_user(&attach, arg, minsz))
- return -EFAULT;
-
- if (attach.argsz < minsz)
- return -EINVAL;
-
- if (attach.flags & (~VFIO_DEVICE_ATTACH_PASID))
- return -EINVAL;
-
- if (attach.flags & VFIO_DEVICE_ATTACH_PASID)
- xend = offsetofend(struct vfio_device_attach_iommufd_pt, pasid);
-
- /*
- * xend may be equal to minsz if a flag is defined for reusing a
- * reserved field or a special usage of an existing field.
- */
- if (xend > minsz) {
- if (attach.argsz < xend)
- return -EINVAL;
-
- if (copy_from_user((void *)&attach + minsz,
- (void __user *)arg + minsz, xend - minsz))
- return -EFAULT;
- }
+ ret = vfio_copy_user_data((void __user *)arg, &attach,
+ struct vfio_device_attach_iommufd_pt,
+ pt_id, VFIO_ATTACH_FLAGS_MASK,
+ vfio_attach_xends);
+ if (ret)
+ return ret;
if ((attach.flags & VFIO_DEVICE_ATTACH_PASID) &&
!device->ops->pasid_attach_ioas)
@@ -227,34 +220,14 @@ int vfio_df_ioctl_detach_pt(struct vfio_device_file *df,
{
struct vfio_device_detach_iommufd_pt detach;
struct vfio_device *device = df->device;
- unsigned long minsz, xend = 0;
-
- minsz = offsetofend(struct vfio_device_detach_iommufd_pt, flags);
-
- if (copy_from_user(&detach, arg, minsz))
- return -EFAULT;
-
- if (detach.argsz < minsz)
- return -EINVAL;
-
- if (detach.flags & (~VFIO_DEVICE_DETACH_PASID))
- return -EINVAL;
-
- if (detach.flags & VFIO_DEVICE_DETACH_PASID)
- xend = offsetofend(struct vfio_device_detach_iommufd_pt, pasid);
-
- /*
- * xend may be equal to minsz if a flag is defined for reusing a
- * reserved field or a special usage of an existing field.
- */
- if (xend > minsz) {
- if (detach.argsz < xend)
- return -EINVAL;
+ int ret;
- if (copy_from_user((void *)&detach + minsz,
- (void __user *)arg + minsz, xend - minsz))
- return -EFAULT;
- }
+ ret = vfio_copy_user_data((void __user *)arg, &detach,
+ struct vfio_device_detach_iommufd_pt,
+ flags, VFIO_DETACH_FLAGS_MASK,
+ vfio_detach_xends);
+ if (ret)
+ return ret;
if ((detach.flags & VFIO_DEVICE_DETACH_PASID) &&
!device->ops->pasid_detach_ioas)
diff --git a/drivers/vfio/vfio.h b/drivers/vfio/vfio.h
index 50128da18bca..87bed550c46e 100644
--- a/drivers/vfio/vfio.h
+++ b/drivers/vfio/vfio.h
@@ -34,6 +34,24 @@ void vfio_df_close(struct vfio_device_file *df);
struct vfio_device_file *
vfio_allocate_device_file(struct vfio_device *device);
+int vfio_copy_from_user(void *buffer, void __user *arg,
+ unsigned long minsz, u32 flags_mask,
+ unsigned long *xend_array);
+
+#define vfio_copy_user_data(_arg, _local_buffer, _struct, _min_last, \
+ _flags_mask, _xend_array) \
+ vfio_copy_from_user(_local_buffer, _arg, \
+ offsetofend(_struct, _min_last) + \
+ BUILD_BUG_ON_ZERO(offsetof(_struct, argsz) != \
+ 0) + \
+ BUILD_BUG_ON_ZERO(offsetof(_struct, flags) != \
+ sizeof(u32)), \
+ _flags_mask, _xend_array)
+
+#define XEND_SIZE(_flag, _struct, _xlast) \
+ [ilog2(_flag)] = offsetofend(_struct, _xlast) + \
+ BUILD_BUG_ON_ZERO(_flag == 0) \
+
extern const struct file_operations vfio_device_fops;
#ifdef CONFIG_VFIO_NOIOMMU
diff --git a/drivers/vfio/vfio_main.c b/drivers/vfio/vfio_main.c
index a5a62d9d963f..c61336ea5123 100644
--- a/drivers/vfio/vfio_main.c
+++ b/drivers/vfio/vfio_main.c
@@ -1694,6 +1694,61 @@ int vfio_dma_rw(struct vfio_device *device, dma_addr_t iova, void *data,
}
EXPORT_SYMBOL(vfio_dma_rw);
+/**
+ * vfio_copy_from_user - Copy the user struct that may have extended fields
+ *
+ * @buffer: The local buffer to store the data copied from user
+ * @arg: The user buffer pointer
+ * @minsz: The minimum size of the user struct
+ * @flags_mask: The combination of all the falgs defined
+ * @xend_array: The array that stores the xend size for set flags.
+ *
+ * This helper requires the user struct put the argsz and flags fields in
+ * the first 8 bytes.
+ *
+ * Return 0 for success, otherwise -errno
+ */
+int vfio_copy_from_user(void *buffer, void __user *arg,
+ unsigned long minsz, u32 flags_mask,
+ unsigned long *xend_array)
+{
+ unsigned long xend = minsz;
+ struct user_header {
+ u32 argsz;
+ u32 flags;
+ } *header;
+ unsigned long flags;
+ u32 flag;
+
+ if (copy_from_user(buffer, arg, minsz))
+ return -EFAULT;
+
+ header = (struct user_header *)buffer;
+ if (header->argsz < minsz)
+ return -EINVAL;
+
+ if (header->flags & ~flags_mask)
+ return -EINVAL;
+
+ /* Loop each set flag to decide the xend */
+ flags = header->flags;
+ for_each_set_bit(flag, &flags, BITS_PER_TYPE(u32)) {
+ if (xend_array[flag] > xend)
+ xend = xend_array[flag];
+ }
+
+ if (xend > minsz) {
+ if (header->argsz < xend)
+ return -EINVAL;
+
+ if (copy_from_user(buffer + minsz,
+ arg + minsz, xend - minsz))
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
/*
* Module/class support
*/
--
2.34.1
next prev parent reply other threads:[~2024-11-08 12:17 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-11-08 12:17 [PATCH v5 0/5] vfio-pci support pasid attach/detach Yi Liu
2024-11-08 12:17 ` [PATCH v5 1/5] ida: Add ida_find_first_range() Yi Liu
2024-11-08 12:17 ` [PATCH v5 2/5] vfio-iommufd: Support pasid [at|de]tach for physical VFIO devices Yi Liu
2024-11-08 12:17 ` [PATCH v5 3/5] vfio: VFIO_DEVICE_[AT|DE]TACH_IOMMUFD_PT support pasid Yi Liu
2024-11-12 0:02 ` Alex Williamson
2024-11-12 1:53 ` Yi Liu
2024-11-08 12:17 ` Yi Liu [this message]
2024-11-12 0:03 ` [PATCH v5 4/5] vfio: Add vfio_copy_user_data() Alex Williamson
2024-11-12 9:18 ` Yi Liu
2024-11-12 13:52 ` Alex Williamson
2024-11-13 7:22 ` Yi Liu
2024-11-14 18:19 ` Alex Williamson
2024-11-15 12:10 ` Yi Liu
2024-11-08 12:17 ` [PATCH v5 5/5] iommufd: Extend IOMMU_GET_HW_INFO to report PASID capability Yi Liu
2024-12-11 2:43 ` Zhangfei Gao
2024-12-11 3:12 ` Yi Liu
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20241108121742.18889-5-yi.l.liu@intel.com \
--to=yi.l.liu@intel.com \
--cc=alex.williamson@redhat.com \
--cc=baolu.lu@linux.intel.com \
--cc=chao.p.peng@linux.intel.com \
--cc=eric.auger@redhat.com \
--cc=iommu@lists.linux.dev \
--cc=jgg@nvidia.com \
--cc=joro@8bytes.org \
--cc=kevin.tian@intel.com \
--cc=kvm@vger.kernel.org \
--cc=nicolinc@nvidia.com \
--cc=vasant.hegde@amd.com \
--cc=will@kernel.org \
--cc=zhenzhong.duan@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox