Commit 775f726a authored by Jason Gunthorpe's avatar Jason Gunthorpe Committed by Alex Williamson
Browse files

vfio: Add get_region_info_caps op



This op does the copy to/from user for the info and can return back
a cap chain through a vfio_info_cap * result.

Reviewed-by: default avatarKevin Tian <kevin.tian@intel.com>
Reviewed-by: default avatarPranjal Shrivastava <praan@google.com>
Signed-off-by: default avatarJason Gunthorpe <jgg@nvidia.com>
Link: https://lore.kernel.org/r/15-v2-2a9e24d62f1b+e10a-vfio_get_region_info_op_jgg@nvidia.com


Signed-off-by: default avatarAlex Williamson <alex@shazbot.org>
parent f9785950
Loading
Loading
Loading
Loading
+52 −4
Original line number Diff line number Diff line
@@ -1259,6 +1259,57 @@ static int vfio_ioctl_device_feature(struct vfio_device *device,
	}
}

static long vfio_get_region_info(struct vfio_device *device,
				 struct vfio_region_info __user *arg)
{
	unsigned long minsz = offsetofend(struct vfio_region_info, offset);
	struct vfio_region_info info = {};
	struct vfio_info_cap caps = {};
	int ret;

	if (copy_from_user(&info, arg, minsz))
		return -EFAULT;
	if (info.argsz < minsz)
		return -EINVAL;

	if (device->ops->get_region_info_caps) {
		ret = device->ops->get_region_info_caps(device, &info, &caps);
		if (ret)
			goto out_free;

		if (caps.size) {
			info.flags |= VFIO_REGION_INFO_FLAG_CAPS;
			if (info.argsz < sizeof(info) + caps.size) {
				info.argsz = sizeof(info) + caps.size;
				info.cap_offset = 0;
			} else {
				vfio_info_cap_shift(&caps, sizeof(info));
				if (copy_to_user(arg + 1, caps.buf,
						 caps.size)) {
					ret = -EFAULT;
					goto out_free;
				}
				info.cap_offset = sizeof(info);
			}
		}

		if (copy_to_user(arg, &info, minsz)) {
			ret = -EFAULT;
			goto out_free;
		}
	} else if (device->ops->get_region_info) {
		ret = device->ops->get_region_info(device, arg);
		if (ret)
			return ret;
	} else {
		return -EINVAL;
	}

out_free:
	kfree(caps.buf);
	return ret;
}

static long vfio_device_fops_unl_ioctl(struct file *filep,
				       unsigned int cmd, unsigned long arg)
{
@@ -1297,10 +1348,7 @@ static long vfio_device_fops_unl_ioctl(struct file *filep,
		break;

	case VFIO_DEVICE_GET_REGION_INFO:
		if (unlikely(!device->ops->get_region_info))
			ret = -EINVAL;
		else
			ret = device->ops->get_region_info(device, uptr);
		ret = vfio_get_region_info(device, uptr);
		break;

	default:
+4 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ struct kvm;
struct iommufd_ctx;
struct iommufd_device;
struct iommufd_access;
struct vfio_info_cap;

/*
 * VFIO devices can be placed in a set, this allows all devices to share this
@@ -134,6 +135,9 @@ struct vfio_device_ops {
			 unsigned long arg);
	int	(*get_region_info)(struct vfio_device *vdev,
				   struct vfio_region_info __user *arg);
	int	(*get_region_info_caps)(struct vfio_device *vdev,
					struct vfio_region_info *info,
					struct vfio_info_cap *caps);
	int	(*mmap)(struct vfio_device *vdev, struct vm_area_struct *vma);
	void	(*request)(struct vfio_device *vdev, unsigned int count);
	int	(*match)(struct vfio_device *vdev, char *buf);