in vdpa_user/vduse_dev.c [919:1097]
static long vduse_dev_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct vduse_dev *dev = file->private_data;
void __user *argp = (void __user *)arg;
int ret;
if (unlikely(dev->broken))
return -EPERM;
switch (cmd) {
case VDUSE_IOTLB_GET_FD: {
struct vduse_iotlb_entry entry;
struct vhost_iotlb_map *map;
struct vdpa_map_file *map_file;
struct vduse_iova_domain *domain = dev->domain;
struct file *f = NULL;
ret = -EFAULT;
if (copy_from_user(&entry, argp, sizeof(entry)))
break;
ret = -EINVAL;
if (entry.start > entry.last)
break;
spin_lock(&domain->iotlb_lock);
map = vhost_iotlb_itree_first(domain->iotlb,
entry.start, entry.last);
if (map) {
map_file = (struct vdpa_map_file *)map->opaque;
f = get_file(map_file->file);
entry.offset = map_file->offset;
entry.start = map->start;
entry.last = map->last;
entry.perm = map->perm;
}
spin_unlock(&domain->iotlb_lock);
ret = -EINVAL;
if (!f)
break;
ret = -EFAULT;
if (copy_to_user(argp, &entry, sizeof(entry))) {
fput(f);
break;
}
ret = receive_fd(f, perm_to_file_flags(entry.perm));
fput(f);
break;
}
case VDUSE_DEV_GET_FEATURES:
/*
* Just mirror what driver wrote here.
* The driver is expected to check FEATURE_OK later.
*/
ret = put_user(dev->driver_features, (u64 __user *)argp);
break;
case VDUSE_DEV_SET_CONFIG: {
struct vduse_config_data config;
unsigned long size = offsetof(struct vduse_config_data,
buffer);
ret = -EFAULT;
if (copy_from_user(&config, argp, size))
break;
ret = -EINVAL;
if (config.offset > dev->config_size ||
config.length == 0 ||
config.length > dev->config_size - config.offset)
break;
ret = -EFAULT;
if (copy_from_user(dev->config + config.offset, argp + size,
config.length))
break;
ret = 0;
break;
}
case VDUSE_DEV_INJECT_CONFIG_IRQ:
ret = vduse_dev_queue_irq_work(dev, &dev->inject);
break;
case VDUSE_VQ_SETUP: {
struct vduse_vq_config config;
u32 index;
ret = -EFAULT;
if (copy_from_user(&config, argp, sizeof(config)))
break;
ret = -EINVAL;
if (config.index >= dev->vq_num)
break;
if (!is_mem_zero((const char *)config.reserved,
sizeof(config.reserved)))
break;
index = array_index_nospec(config.index, dev->vq_num);
dev->vqs[index].num_max = config.max_size;
ret = 0;
break;
}
case VDUSE_VQ_GET_INFO: {
struct vduse_vq_info vq_info;
struct vduse_virtqueue *vq;
u32 index;
ret = -EFAULT;
if (copy_from_user(&vq_info, argp, sizeof(vq_info)))
break;
ret = -EINVAL;
if (vq_info.index >= dev->vq_num)
break;
index = array_index_nospec(vq_info.index, dev->vq_num);
vq = &dev->vqs[index];
vq_info.desc_addr = vq->desc_addr;
vq_info.driver_addr = vq->driver_addr;
vq_info.device_addr = vq->device_addr;
vq_info.num = vq->num;
if (dev->driver_features & BIT_ULL(VIRTIO_F_RING_PACKED)) {
vq_info.packed.last_avail_counter =
vq->state.packed.last_avail_counter;
vq_info.packed.last_avail_idx =
vq->state.packed.last_avail_idx;
vq_info.packed.last_used_counter =
vq->state.packed.last_used_counter;
vq_info.packed.last_used_idx =
vq->state.packed.last_used_idx;
} else
vq_info.split.avail_index =
vq->state.split.avail_index;
vq_info.ready = vq->ready;
ret = -EFAULT;
if (copy_to_user(argp, &vq_info, sizeof(vq_info)))
break;
ret = 0;
break;
}
case VDUSE_VQ_SETUP_KICKFD: {
struct vduse_vq_eventfd eventfd;
ret = -EFAULT;
if (copy_from_user(&eventfd, argp, sizeof(eventfd)))
break;
ret = vduse_kickfd_setup(dev, &eventfd);
break;
}
case VDUSE_VQ_INJECT_IRQ: {
u32 index;
ret = -EFAULT;
if (get_user(index, (u32 __user *)argp))
break;
ret = -EINVAL;
if (index >= dev->vq_num)
break;
index = array_index_nospec(index, dev->vq_num);
ret = vduse_dev_queue_irq_work(dev, &dev->vqs[index].inject);
break;
}
default:
ret = -ENOIOCTLCMD;
break;
}
return ret;
}