Home | History | Annotate | Line # | Download | only in virtio
      1 /*	$NetBSD: virtgpu_ioctl.c,v 1.3 2021/12/18 23:45:45 riastradh Exp $	*/
      2 
      3 /*
      4  * Copyright (C) 2015 Red Hat, Inc.
      5  * All Rights Reserved.
      6  *
      7  * Authors:
      8  *    Dave Airlie
      9  *    Alon Levy
     10  *
     11  * Permission is hereby granted, free of charge, to any person obtaining a
     12  * copy of this software and associated documentation files (the "Software"),
     13  * to deal in the Software without restriction, including without limitation
     14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     15  * and/or sell copies of the Software, and to permit persons to whom the
     16  * Software is furnished to do so, subject to the following conditions:
     17  *
     18  * The above copyright notice and this permission notice shall be included in
     19  * all copies or substantial portions of the Software.
     20  *
     21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     24  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
     25  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     26  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     27  * OTHER DEALINGS IN THE SOFTWARE.
     28  */
     29 
     30 #include <sys/cdefs.h>
     31 __KERNEL_RCSID(0, "$NetBSD: virtgpu_ioctl.c,v 1.3 2021/12/18 23:45:45 riastradh Exp $");
     32 
     33 #include <linux/file.h>
     34 #include <linux/sync_file.h>
     35 
     36 #include <drm/drm_file.h>
     37 #include <drm/virtgpu_drm.h>
     38 
     39 #include "virtgpu_drv.h"
     40 
     41 static int virtio_gpu_map_ioctl(struct drm_device *dev, void *data,
     42 				struct drm_file *file_priv)
     43 {
     44 	struct virtio_gpu_device *vgdev = dev->dev_private;
     45 	struct drm_virtgpu_map *virtio_gpu_map = data;
     46 
     47 	return virtio_gpu_mode_dumb_mmap(file_priv, vgdev->ddev,
     48 					 virtio_gpu_map->handle,
     49 					 &virtio_gpu_map->offset);
     50 }
     51 
     52 /*
     53  * Usage of execbuffer:
     54  * Relocations need to take into account the full VIRTIO_GPUDrawable size.
     55  * However, the command as passed from user space must *not* contain the initial
     56  * VIRTIO_GPUReleaseInfo struct (first XXX bytes)
     57  */
     58 static int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data,
     59 				 struct drm_file *drm_file)
     60 {
     61 	struct drm_virtgpu_execbuffer *exbuf = data;
     62 	struct virtio_gpu_device *vgdev = dev->dev_private;
     63 	struct virtio_gpu_fpriv *vfpriv = drm_file->driver_priv;
     64 	struct virtio_gpu_fence *out_fence;
     65 	int ret;
     66 	uint32_t *bo_handles = NULL;
     67 	void __user *user_bo_handles = NULL;
     68 	struct virtio_gpu_object_array *buflist = NULL;
     69 	struct sync_file *sync_file;
     70 	int in_fence_fd = exbuf->fence_fd;
     71 	int out_fence_fd = -1;
     72 	void *buf;
     73 
     74 	if (vgdev->has_virgl_3d == false)
     75 		return -ENOSYS;
     76 
     77 	if ((exbuf->flags & ~VIRTGPU_EXECBUF_FLAGS))
     78 		return -EINVAL;
     79 
     80 	exbuf->fence_fd = -1;
     81 
     82 	if (exbuf->flags & VIRTGPU_EXECBUF_FENCE_FD_IN) {
     83 		struct dma_fence *in_fence;
     84 
     85 		in_fence = sync_file_get_fence(in_fence_fd);
     86 
     87 		if (!in_fence)
     88 			return -EINVAL;
     89 
     90 		/*
     91 		 * Wait if the fence is from a foreign context, or if the fence
     92 		 * array contains any fence from a foreign context.
     93 		 */
     94 		ret = 0;
     95 		if (!dma_fence_match_context(in_fence, vgdev->fence_drv.context))
     96 			ret = dma_fence_wait(in_fence, true);
     97 
     98 		dma_fence_put(in_fence);
     99 		if (ret)
    100 			return ret;
    101 	}
    102 
    103 	if (exbuf->flags & VIRTGPU_EXECBUF_FENCE_FD_OUT) {
    104 		out_fence_fd = get_unused_fd_flags(O_CLOEXEC);
    105 		if (out_fence_fd < 0)
    106 			return out_fence_fd;
    107 	}
    108 
    109 	if (exbuf->num_bo_handles) {
    110 		bo_handles = kvmalloc_array(exbuf->num_bo_handles,
    111 					    sizeof(uint32_t), GFP_KERNEL);
    112 		if (!bo_handles) {
    113 			ret = -ENOMEM;
    114 			goto out_unused_fd;
    115 		}
    116 
    117 		user_bo_handles = u64_to_user_ptr(exbuf->bo_handles);
    118 		if (copy_from_user(bo_handles, user_bo_handles,
    119 				   exbuf->num_bo_handles * sizeof(uint32_t))) {
    120 			ret = -EFAULT;
    121 			goto out_unused_fd;
    122 		}
    123 
    124 		buflist = virtio_gpu_array_from_handles(drm_file, bo_handles,
    125 							exbuf->num_bo_handles);
    126 		if (!buflist) {
    127 			ret = -ENOENT;
    128 			goto out_unused_fd;
    129 		}
    130 		kvfree(bo_handles);
    131 		bo_handles = NULL;
    132 	}
    133 
    134 	if (buflist) {
    135 		ret = virtio_gpu_array_lock_resv(buflist);
    136 		if (ret)
    137 			goto out_unused_fd;
    138 	}
    139 
    140 	buf = vmemdup_user(u64_to_user_ptr(exbuf->command), exbuf->size);
    141 	if (IS_ERR(buf)) {
    142 		ret = PTR_ERR(buf);
    143 		goto out_unresv;
    144 	}
    145 
    146 	out_fence = virtio_gpu_fence_alloc(vgdev);
    147 	if(!out_fence) {
    148 		ret = -ENOMEM;
    149 		goto out_memdup;
    150 	}
    151 
    152 	if (out_fence_fd >= 0) {
    153 		sync_file = sync_file_create(&out_fence->f);
    154 		if (!sync_file) {
    155 			dma_fence_put(&out_fence->f);
    156 			ret = -ENOMEM;
    157 			goto out_memdup;
    158 		}
    159 
    160 		exbuf->fence_fd = out_fence_fd;
    161 		fd_install(out_fence_fd, sync_file->file);
    162 	}
    163 
    164 	virtio_gpu_cmd_submit(vgdev, buf, exbuf->size,
    165 			      vfpriv->ctx_id, buflist, out_fence);
    166 	return 0;
    167 
    168 out_memdup:
    169 	kvfree(buf);
    170 out_unresv:
    171 	if (buflist)
    172 		virtio_gpu_array_unlock_resv(buflist);
    173 out_unused_fd:
    174 	kvfree(bo_handles);
    175 	if (buflist)
    176 		virtio_gpu_array_put_free(buflist);
    177 
    178 	if (out_fence_fd >= 0)
    179 		put_unused_fd(out_fence_fd);
    180 
    181 	return ret;
    182 }
    183 
    184 static int virtio_gpu_getparam_ioctl(struct drm_device *dev, void *data,
    185 				     struct drm_file *file_priv)
    186 {
    187 	struct virtio_gpu_device *vgdev = dev->dev_private;
    188 	struct drm_virtgpu_getparam *param = data;
    189 	int value;
    190 
    191 	switch (param->param) {
    192 	case VIRTGPU_PARAM_3D_FEATURES:
    193 		value = vgdev->has_virgl_3d == true ? 1 : 0;
    194 		break;
    195 	case VIRTGPU_PARAM_CAPSET_QUERY_FIX:
    196 		value = 1;
    197 		break;
    198 	default:
    199 		return -EINVAL;
    200 	}
    201 	if (copy_to_user(u64_to_user_ptr(param->value), &value, sizeof(int)))
    202 		return -EFAULT;
    203 
    204 	return 0;
    205 }
    206 
    207 static int virtio_gpu_resource_create_ioctl(struct drm_device *dev, void *data,
    208 					    struct drm_file *file_priv)
    209 {
    210 	struct virtio_gpu_device *vgdev = dev->dev_private;
    211 	struct drm_virtgpu_resource_create *rc = data;
    212 	struct virtio_gpu_fence *fence;
    213 	int ret;
    214 	struct virtio_gpu_object *qobj;
    215 	struct drm_gem_object *obj;
    216 	uint32_t handle = 0;
    217 	struct virtio_gpu_object_params params = { 0 };
    218 
    219 	if (vgdev->has_virgl_3d == false) {
    220 		if (rc->depth > 1)
    221 			return -EINVAL;
    222 		if (rc->nr_samples > 1)
    223 			return -EINVAL;
    224 		if (rc->last_level > 1)
    225 			return -EINVAL;
    226 		if (rc->target != 2)
    227 			return -EINVAL;
    228 		if (rc->array_size > 1)
    229 			return -EINVAL;
    230 	}
    231 
    232 	params.format = rc->format;
    233 	params.width = rc->width;
    234 	params.height = rc->height;
    235 	params.size = rc->size;
    236 	if (vgdev->has_virgl_3d) {
    237 		params.virgl = true;
    238 		params.target = rc->target;
    239 		params.bind = rc->bind;
    240 		params.depth = rc->depth;
    241 		params.array_size = rc->array_size;
    242 		params.last_level = rc->last_level;
    243 		params.nr_samples = rc->nr_samples;
    244 		params.flags = rc->flags;
    245 	}
    246 	/* allocate a single page size object */
    247 	if (params.size == 0)
    248 		params.size = PAGE_SIZE;
    249 
    250 	fence = virtio_gpu_fence_alloc(vgdev);
    251 	if (!fence)
    252 		return -ENOMEM;
    253 	ret = virtio_gpu_object_create(vgdev, &params, &qobj, fence);
    254 	dma_fence_put(&fence->f);
    255 	if (ret < 0)
    256 		return ret;
    257 	obj = &qobj->base.base;
    258 
    259 	ret = drm_gem_handle_create(file_priv, obj, &handle);
    260 	if (ret) {
    261 		drm_gem_object_release(obj);
    262 		return ret;
    263 	}
    264 	drm_gem_object_put_unlocked(obj);
    265 
    266 	rc->res_handle = qobj->hw_res_handle; /* similiar to a VM address */
    267 	rc->bo_handle = handle;
    268 	return 0;
    269 }
    270 
    271 static int virtio_gpu_resource_info_ioctl(struct drm_device *dev, void *data,
    272 					  struct drm_file *file_priv)
    273 {
    274 	struct drm_virtgpu_resource_info *ri = data;
    275 	struct drm_gem_object *gobj = NULL;
    276 	struct virtio_gpu_object *qobj = NULL;
    277 
    278 	gobj = drm_gem_object_lookup(file_priv, ri->bo_handle);
    279 	if (gobj == NULL)
    280 		return -ENOENT;
    281 
    282 	qobj = gem_to_virtio_gpu_obj(gobj);
    283 
    284 	ri->size = qobj->base.base.size;
    285 	ri->res_handle = qobj->hw_res_handle;
    286 	drm_gem_object_put_unlocked(gobj);
    287 	return 0;
    288 }
    289 
    290 static int virtio_gpu_transfer_from_host_ioctl(struct drm_device *dev,
    291 					       void *data,
    292 					       struct drm_file *file)
    293 {
    294 	struct virtio_gpu_device *vgdev = dev->dev_private;
    295 	struct virtio_gpu_fpriv *vfpriv = file->driver_priv;
    296 	struct drm_virtgpu_3d_transfer_from_host *args = data;
    297 	struct virtio_gpu_object_array *objs;
    298 	struct virtio_gpu_fence *fence;
    299 	int ret;
    300 	u32 offset = args->offset;
    301 
    302 	if (vgdev->has_virgl_3d == false)
    303 		return -ENOSYS;
    304 
    305 	objs = virtio_gpu_array_from_handles(file, &args->bo_handle, 1);
    306 	if (objs == NULL)
    307 		return -ENOENT;
    308 
    309 	ret = virtio_gpu_array_lock_resv(objs);
    310 	if (ret != 0)
    311 		goto err_put_free;
    312 
    313 	fence = virtio_gpu_fence_alloc(vgdev);
    314 	if (!fence) {
    315 		ret = -ENOMEM;
    316 		goto err_unlock;
    317 	}
    318 	virtio_gpu_cmd_transfer_from_host_3d
    319 		(vgdev, vfpriv->ctx_id, offset, args->level,
    320 		 &args->box, objs, fence);
    321 	dma_fence_put(&fence->f);
    322 	return 0;
    323 
    324 err_unlock:
    325 	virtio_gpu_array_unlock_resv(objs);
    326 err_put_free:
    327 	virtio_gpu_array_put_free(objs);
    328 	return ret;
    329 }
    330 
    331 static int virtio_gpu_transfer_to_host_ioctl(struct drm_device *dev, void *data,
    332 					     struct drm_file *file)
    333 {
    334 	struct virtio_gpu_device *vgdev = dev->dev_private;
    335 	struct virtio_gpu_fpriv *vfpriv = file->driver_priv;
    336 	struct drm_virtgpu_3d_transfer_to_host *args = data;
    337 	struct virtio_gpu_object_array *objs;
    338 	struct virtio_gpu_fence *fence;
    339 	int ret;
    340 	u32 offset = args->offset;
    341 
    342 	objs = virtio_gpu_array_from_handles(file, &args->bo_handle, 1);
    343 	if (objs == NULL)
    344 		return -ENOENT;
    345 
    346 	if (!vgdev->has_virgl_3d) {
    347 		virtio_gpu_cmd_transfer_to_host_2d
    348 			(vgdev, offset,
    349 			 args->box.w, args->box.h, args->box.x, args->box.y,
    350 			 objs, NULL);
    351 	} else {
    352 		ret = virtio_gpu_array_lock_resv(objs);
    353 		if (ret != 0)
    354 			goto err_put_free;
    355 
    356 		ret = -ENOMEM;
    357 		fence = virtio_gpu_fence_alloc(vgdev);
    358 		if (!fence)
    359 			goto err_unlock;
    360 
    361 		virtio_gpu_cmd_transfer_to_host_3d
    362 			(vgdev,
    363 			 vfpriv ? vfpriv->ctx_id : 0, offset,
    364 			 args->level, &args->box, objs, fence);
    365 		dma_fence_put(&fence->f);
    366 	}
    367 	return 0;
    368 
    369 err_unlock:
    370 	virtio_gpu_array_unlock_resv(objs);
    371 err_put_free:
    372 	virtio_gpu_array_put_free(objs);
    373 	return ret;
    374 }
    375 
    376 static int virtio_gpu_wait_ioctl(struct drm_device *dev, void *data,
    377 				 struct drm_file *file)
    378 {
    379 	struct drm_virtgpu_3d_wait *args = data;
    380 	struct drm_gem_object *obj;
    381 	long timeout = 15 * HZ;
    382 	int ret;
    383 
    384 	obj = drm_gem_object_lookup(file, args->handle);
    385 	if (obj == NULL)
    386 		return -ENOENT;
    387 
    388 	if (args->flags & VIRTGPU_WAIT_NOWAIT) {
    389 		ret = dma_resv_test_signaled_rcu(obj->resv, true);
    390 	} else {
    391 		ret = dma_resv_wait_timeout_rcu(obj->resv, true, true,
    392 						timeout);
    393 	}
    394 	if (ret == 0)
    395 		ret = -EBUSY;
    396 	else if (ret > 0)
    397 		ret = 0;
    398 
    399 	drm_gem_object_put_unlocked(obj);
    400 	return ret;
    401 }
    402 
    403 static int virtio_gpu_get_caps_ioctl(struct drm_device *dev,
    404 				void *data, struct drm_file *file)
    405 {
    406 	struct virtio_gpu_device *vgdev = dev->dev_private;
    407 	struct drm_virtgpu_get_caps *args = data;
    408 	unsigned size, host_caps_size;
    409 	int i;
    410 	int found_valid = -1;
    411 	int ret;
    412 	struct virtio_gpu_drv_cap_cache *cache_ent;
    413 	void *ptr;
    414 
    415 	if (vgdev->num_capsets == 0)
    416 		return -ENOSYS;
    417 
    418 	/* don't allow userspace to pass 0 */
    419 	if (args->size == 0)
    420 		return -EINVAL;
    421 
    422 	spin_lock(&vgdev->display_info_lock);
    423 	for (i = 0; i < vgdev->num_capsets; i++) {
    424 		if (vgdev->capsets[i].id == args->cap_set_id) {
    425 			if (vgdev->capsets[i].max_version >= args->cap_set_ver) {
    426 				found_valid = i;
    427 				break;
    428 			}
    429 		}
    430 	}
    431 
    432 	if (found_valid == -1) {
    433 		spin_unlock(&vgdev->display_info_lock);
    434 		return -EINVAL;
    435 	}
    436 
    437 	host_caps_size = vgdev->capsets[found_valid].max_size;
    438 	/* only copy to user the minimum of the host caps size or the guest caps size */
    439 	size = min(args->size, host_caps_size);
    440 
    441 	list_for_each_entry(cache_ent, &vgdev->cap_cache, head) {
    442 		if (cache_ent->id == args->cap_set_id &&
    443 		    cache_ent->version == args->cap_set_ver) {
    444 			spin_unlock(&vgdev->display_info_lock);
    445 			goto copy_exit;
    446 		}
    447 	}
    448 	spin_unlock(&vgdev->display_info_lock);
    449 
    450 	/* not in cache - need to talk to hw */
    451 	virtio_gpu_cmd_get_capset(vgdev, found_valid, args->cap_set_ver,
    452 				  &cache_ent);
    453 
    454 copy_exit:
    455 	ret = wait_event_timeout(vgdev->resp_wq,
    456 				 atomic_read(&cache_ent->is_valid), 5 * HZ);
    457 	if (!ret)
    458 		return -EBUSY;
    459 
    460 	/* is_valid check must proceed before copy of the cache entry. */
    461 	smp_rmb();
    462 
    463 	ptr = cache_ent->caps_cache;
    464 
    465 	if (copy_to_user(u64_to_user_ptr(args->addr), ptr, size))
    466 		return -EFAULT;
    467 
    468 	return 0;
    469 }
    470 
    471 struct drm_ioctl_desc virtio_gpu_ioctls[DRM_VIRTIO_NUM_IOCTLS] = {
    472 	DRM_IOCTL_DEF_DRV(VIRTGPU_MAP, virtio_gpu_map_ioctl,
    473 			  DRM_RENDER_ALLOW),
    474 
    475 	DRM_IOCTL_DEF_DRV(VIRTGPU_EXECBUFFER, virtio_gpu_execbuffer_ioctl,
    476 			  DRM_RENDER_ALLOW),
    477 
    478 	DRM_IOCTL_DEF_DRV(VIRTGPU_GETPARAM, virtio_gpu_getparam_ioctl,
    479 			  DRM_RENDER_ALLOW),
    480 
    481 	DRM_IOCTL_DEF_DRV(VIRTGPU_RESOURCE_CREATE,
    482 			  virtio_gpu_resource_create_ioctl,
    483 			  DRM_RENDER_ALLOW),
    484 
    485 	DRM_IOCTL_DEF_DRV(VIRTGPU_RESOURCE_INFO, virtio_gpu_resource_info_ioctl,
    486 			  DRM_RENDER_ALLOW),
    487 
    488 	/* make transfer async to the main ring? - no sure, can we
    489 	 * thread these in the underlying GL
    490 	 */
    491 	DRM_IOCTL_DEF_DRV(VIRTGPU_TRANSFER_FROM_HOST,
    492 			  virtio_gpu_transfer_from_host_ioctl,
    493 			  DRM_RENDER_ALLOW),
    494 	DRM_IOCTL_DEF_DRV(VIRTGPU_TRANSFER_TO_HOST,
    495 			  virtio_gpu_transfer_to_host_ioctl,
    496 			  DRM_RENDER_ALLOW),
    497 
    498 	DRM_IOCTL_DEF_DRV(VIRTGPU_WAIT, virtio_gpu_wait_ioctl,
    499 			  DRM_RENDER_ALLOW),
    500 
    501 	DRM_IOCTL_DEF_DRV(VIRTGPU_GET_CAPS, virtio_gpu_get_caps_ioctl,
    502 			  DRM_RENDER_ALLOW),
    503 };
    504