162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (C) 2015 Red Hat, Inc. 362306a36Sopenharmony_ci * All Rights Reserved. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: 662306a36Sopenharmony_ci * Dave Airlie 762306a36Sopenharmony_ci * Alon Levy 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 1062306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 1162306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation 1262306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 1362306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 1462306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 1762306a36Sopenharmony_ci * all copies or substantial portions of the Software. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 2062306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 2162306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 2262306a36Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 2362306a36Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 2462306a36Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 2562306a36Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE. 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include <linux/file.h> 2962306a36Sopenharmony_ci#include <linux/sync_file.h> 3062306a36Sopenharmony_ci#include <linux/uaccess.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include <drm/drm_file.h> 3362306a36Sopenharmony_ci#include <drm/virtgpu_drm.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include "virtgpu_drv.h" 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define VIRTGPU_BLOB_FLAG_USE_MASK (VIRTGPU_BLOB_FLAG_USE_MAPPABLE | \ 3862306a36Sopenharmony_ci VIRTGPU_BLOB_FLAG_USE_SHAREABLE | \ 3962306a36Sopenharmony_ci VIRTGPU_BLOB_FLAG_USE_CROSS_DEVICE) 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* Must be called with &virtio_gpu_fpriv.struct_mutex held. */ 4262306a36Sopenharmony_cistatic void virtio_gpu_create_context_locked(struct virtio_gpu_device *vgdev, 4362306a36Sopenharmony_ci struct virtio_gpu_fpriv *vfpriv) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci char dbgname[TASK_COMM_LEN]; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci get_task_comm(dbgname, current); 4862306a36Sopenharmony_ci virtio_gpu_cmd_context_create(vgdev, vfpriv->ctx_id, 4962306a36Sopenharmony_ci vfpriv->context_init, strlen(dbgname), 5062306a36Sopenharmony_ci dbgname); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci vfpriv->context_created = true; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_civoid virtio_gpu_create_context(struct drm_device *dev, struct drm_file *file) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci struct virtio_gpu_device *vgdev = dev->dev_private; 5862306a36Sopenharmony_ci struct virtio_gpu_fpriv *vfpriv = file->driver_priv; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci mutex_lock(&vfpriv->context_lock); 6162306a36Sopenharmony_ci if (vfpriv->context_created) 6262306a36Sopenharmony_ci goto out_unlock; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci virtio_gpu_create_context_locked(vgdev, vfpriv); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ciout_unlock: 6762306a36Sopenharmony_ci mutex_unlock(&vfpriv->context_lock); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic int virtio_gpu_map_ioctl(struct drm_device *dev, void *data, 7162306a36Sopenharmony_ci struct drm_file *file) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct virtio_gpu_device *vgdev = dev->dev_private; 7462306a36Sopenharmony_ci struct drm_virtgpu_map *virtio_gpu_map = data; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci return virtio_gpu_mode_dumb_mmap(file, vgdev->ddev, 7762306a36Sopenharmony_ci virtio_gpu_map->handle, 7862306a36Sopenharmony_ci &virtio_gpu_map->offset); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic int virtio_gpu_getparam_ioctl(struct drm_device *dev, void *data, 8262306a36Sopenharmony_ci struct drm_file *file) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct virtio_gpu_device *vgdev = dev->dev_private; 8562306a36Sopenharmony_ci struct drm_virtgpu_getparam *param = data; 8662306a36Sopenharmony_ci int value; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci switch (param->param) { 8962306a36Sopenharmony_ci case VIRTGPU_PARAM_3D_FEATURES: 9062306a36Sopenharmony_ci value = vgdev->has_virgl_3d ? 1 : 0; 9162306a36Sopenharmony_ci break; 9262306a36Sopenharmony_ci case VIRTGPU_PARAM_CAPSET_QUERY_FIX: 9362306a36Sopenharmony_ci value = 1; 9462306a36Sopenharmony_ci break; 9562306a36Sopenharmony_ci case VIRTGPU_PARAM_RESOURCE_BLOB: 9662306a36Sopenharmony_ci value = vgdev->has_resource_blob ? 1 : 0; 9762306a36Sopenharmony_ci break; 9862306a36Sopenharmony_ci case VIRTGPU_PARAM_HOST_VISIBLE: 9962306a36Sopenharmony_ci value = vgdev->has_host_visible ? 1 : 0; 10062306a36Sopenharmony_ci break; 10162306a36Sopenharmony_ci case VIRTGPU_PARAM_CROSS_DEVICE: 10262306a36Sopenharmony_ci value = vgdev->has_resource_assign_uuid ? 1 : 0; 10362306a36Sopenharmony_ci break; 10462306a36Sopenharmony_ci case VIRTGPU_PARAM_CONTEXT_INIT: 10562306a36Sopenharmony_ci value = vgdev->has_context_init ? 1 : 0; 10662306a36Sopenharmony_ci break; 10762306a36Sopenharmony_ci case VIRTGPU_PARAM_SUPPORTED_CAPSET_IDs: 10862306a36Sopenharmony_ci value = vgdev->capset_id_mask; 10962306a36Sopenharmony_ci break; 11062306a36Sopenharmony_ci default: 11162306a36Sopenharmony_ci return -EINVAL; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci if (copy_to_user(u64_to_user_ptr(param->value), &value, sizeof(int))) 11462306a36Sopenharmony_ci return -EFAULT; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return 0; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic int virtio_gpu_resource_create_ioctl(struct drm_device *dev, void *data, 12062306a36Sopenharmony_ci struct drm_file *file) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct virtio_gpu_device *vgdev = dev->dev_private; 12362306a36Sopenharmony_ci struct drm_virtgpu_resource_create *rc = data; 12462306a36Sopenharmony_ci struct virtio_gpu_fence *fence; 12562306a36Sopenharmony_ci int ret; 12662306a36Sopenharmony_ci struct virtio_gpu_object *qobj; 12762306a36Sopenharmony_ci struct drm_gem_object *obj; 12862306a36Sopenharmony_ci uint32_t handle = 0; 12962306a36Sopenharmony_ci struct virtio_gpu_object_params params = { 0 }; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if (vgdev->has_virgl_3d) { 13262306a36Sopenharmony_ci virtio_gpu_create_context(dev, file); 13362306a36Sopenharmony_ci params.virgl = true; 13462306a36Sopenharmony_ci params.target = rc->target; 13562306a36Sopenharmony_ci params.bind = rc->bind; 13662306a36Sopenharmony_ci params.depth = rc->depth; 13762306a36Sopenharmony_ci params.array_size = rc->array_size; 13862306a36Sopenharmony_ci params.last_level = rc->last_level; 13962306a36Sopenharmony_ci params.nr_samples = rc->nr_samples; 14062306a36Sopenharmony_ci params.flags = rc->flags; 14162306a36Sopenharmony_ci } else { 14262306a36Sopenharmony_ci if (rc->depth > 1) 14362306a36Sopenharmony_ci return -EINVAL; 14462306a36Sopenharmony_ci if (rc->nr_samples > 1) 14562306a36Sopenharmony_ci return -EINVAL; 14662306a36Sopenharmony_ci if (rc->last_level > 1) 14762306a36Sopenharmony_ci return -EINVAL; 14862306a36Sopenharmony_ci if (rc->target != 2) 14962306a36Sopenharmony_ci return -EINVAL; 15062306a36Sopenharmony_ci if (rc->array_size > 1) 15162306a36Sopenharmony_ci return -EINVAL; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci params.format = rc->format; 15562306a36Sopenharmony_ci params.width = rc->width; 15662306a36Sopenharmony_ci params.height = rc->height; 15762306a36Sopenharmony_ci params.size = rc->size; 15862306a36Sopenharmony_ci /* allocate a single page size object */ 15962306a36Sopenharmony_ci if (params.size == 0) 16062306a36Sopenharmony_ci params.size = PAGE_SIZE; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci fence = virtio_gpu_fence_alloc(vgdev, vgdev->fence_drv.context, 0); 16362306a36Sopenharmony_ci if (!fence) 16462306a36Sopenharmony_ci return -ENOMEM; 16562306a36Sopenharmony_ci ret = virtio_gpu_object_create(vgdev, ¶ms, &qobj, fence); 16662306a36Sopenharmony_ci dma_fence_put(&fence->f); 16762306a36Sopenharmony_ci if (ret < 0) 16862306a36Sopenharmony_ci return ret; 16962306a36Sopenharmony_ci obj = &qobj->base.base; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci ret = drm_gem_handle_create(file, obj, &handle); 17262306a36Sopenharmony_ci if (ret) { 17362306a36Sopenharmony_ci drm_gem_object_release(obj); 17462306a36Sopenharmony_ci return ret; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci rc->res_handle = qobj->hw_res_handle; /* similiar to a VM address */ 17862306a36Sopenharmony_ci rc->bo_handle = handle; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* 18162306a36Sopenharmony_ci * The handle owns the reference now. But we must drop our 18262306a36Sopenharmony_ci * remaining reference *after* we no longer need to dereference 18362306a36Sopenharmony_ci * the obj. Otherwise userspace could guess the handle and 18462306a36Sopenharmony_ci * race closing it from another thread. 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_ci drm_gem_object_put(obj); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return 0; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic int virtio_gpu_resource_info_ioctl(struct drm_device *dev, void *data, 19262306a36Sopenharmony_ci struct drm_file *file) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct drm_virtgpu_resource_info *ri = data; 19562306a36Sopenharmony_ci struct drm_gem_object *gobj = NULL; 19662306a36Sopenharmony_ci struct virtio_gpu_object *qobj = NULL; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci gobj = drm_gem_object_lookup(file, ri->bo_handle); 19962306a36Sopenharmony_ci if (gobj == NULL) 20062306a36Sopenharmony_ci return -ENOENT; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci qobj = gem_to_virtio_gpu_obj(gobj); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci ri->size = qobj->base.base.size; 20562306a36Sopenharmony_ci ri->res_handle = qobj->hw_res_handle; 20662306a36Sopenharmony_ci if (qobj->host3d_blob || qobj->guest_blob) 20762306a36Sopenharmony_ci ri->blob_mem = qobj->blob_mem; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci drm_gem_object_put(gobj); 21062306a36Sopenharmony_ci return 0; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic int virtio_gpu_transfer_from_host_ioctl(struct drm_device *dev, 21462306a36Sopenharmony_ci void *data, 21562306a36Sopenharmony_ci struct drm_file *file) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct virtio_gpu_device *vgdev = dev->dev_private; 21862306a36Sopenharmony_ci struct virtio_gpu_fpriv *vfpriv = file->driver_priv; 21962306a36Sopenharmony_ci struct drm_virtgpu_3d_transfer_from_host *args = data; 22062306a36Sopenharmony_ci struct virtio_gpu_object *bo; 22162306a36Sopenharmony_ci struct virtio_gpu_object_array *objs; 22262306a36Sopenharmony_ci struct virtio_gpu_fence *fence; 22362306a36Sopenharmony_ci int ret; 22462306a36Sopenharmony_ci u32 offset = args->offset; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (vgdev->has_virgl_3d == false) 22762306a36Sopenharmony_ci return -ENOSYS; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci virtio_gpu_create_context(dev, file); 23062306a36Sopenharmony_ci objs = virtio_gpu_array_from_handles(file, &args->bo_handle, 1); 23162306a36Sopenharmony_ci if (objs == NULL) 23262306a36Sopenharmony_ci return -ENOENT; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci bo = gem_to_virtio_gpu_obj(objs->objs[0]); 23562306a36Sopenharmony_ci if (bo->guest_blob && !bo->host3d_blob) { 23662306a36Sopenharmony_ci ret = -EINVAL; 23762306a36Sopenharmony_ci goto err_put_free; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci if (!bo->host3d_blob && (args->stride || args->layer_stride)) { 24162306a36Sopenharmony_ci ret = -EINVAL; 24262306a36Sopenharmony_ci goto err_put_free; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci ret = virtio_gpu_array_lock_resv(objs); 24662306a36Sopenharmony_ci if (ret != 0) 24762306a36Sopenharmony_ci goto err_put_free; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci fence = virtio_gpu_fence_alloc(vgdev, vgdev->fence_drv.context, 0); 25062306a36Sopenharmony_ci if (!fence) { 25162306a36Sopenharmony_ci ret = -ENOMEM; 25262306a36Sopenharmony_ci goto err_unlock; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci virtio_gpu_cmd_transfer_from_host_3d 25662306a36Sopenharmony_ci (vgdev, vfpriv->ctx_id, offset, args->level, args->stride, 25762306a36Sopenharmony_ci args->layer_stride, &args->box, objs, fence); 25862306a36Sopenharmony_ci dma_fence_put(&fence->f); 25962306a36Sopenharmony_ci virtio_gpu_notify(vgdev); 26062306a36Sopenharmony_ci return 0; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cierr_unlock: 26362306a36Sopenharmony_ci virtio_gpu_array_unlock_resv(objs); 26462306a36Sopenharmony_cierr_put_free: 26562306a36Sopenharmony_ci virtio_gpu_array_put_free(objs); 26662306a36Sopenharmony_ci return ret; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic int virtio_gpu_transfer_to_host_ioctl(struct drm_device *dev, void *data, 27062306a36Sopenharmony_ci struct drm_file *file) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci struct virtio_gpu_device *vgdev = dev->dev_private; 27362306a36Sopenharmony_ci struct virtio_gpu_fpriv *vfpriv = file->driver_priv; 27462306a36Sopenharmony_ci struct drm_virtgpu_3d_transfer_to_host *args = data; 27562306a36Sopenharmony_ci struct virtio_gpu_object *bo; 27662306a36Sopenharmony_ci struct virtio_gpu_object_array *objs; 27762306a36Sopenharmony_ci struct virtio_gpu_fence *fence; 27862306a36Sopenharmony_ci int ret; 27962306a36Sopenharmony_ci u32 offset = args->offset; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci objs = virtio_gpu_array_from_handles(file, &args->bo_handle, 1); 28262306a36Sopenharmony_ci if (objs == NULL) 28362306a36Sopenharmony_ci return -ENOENT; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci bo = gem_to_virtio_gpu_obj(objs->objs[0]); 28662306a36Sopenharmony_ci if (bo->guest_blob && !bo->host3d_blob) { 28762306a36Sopenharmony_ci ret = -EINVAL; 28862306a36Sopenharmony_ci goto err_put_free; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (!vgdev->has_virgl_3d) { 29262306a36Sopenharmony_ci virtio_gpu_cmd_transfer_to_host_2d 29362306a36Sopenharmony_ci (vgdev, offset, 29462306a36Sopenharmony_ci args->box.w, args->box.h, args->box.x, args->box.y, 29562306a36Sopenharmony_ci objs, NULL); 29662306a36Sopenharmony_ci } else { 29762306a36Sopenharmony_ci virtio_gpu_create_context(dev, file); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (!bo->host3d_blob && (args->stride || args->layer_stride)) { 30062306a36Sopenharmony_ci ret = -EINVAL; 30162306a36Sopenharmony_ci goto err_put_free; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci ret = virtio_gpu_array_lock_resv(objs); 30562306a36Sopenharmony_ci if (ret != 0) 30662306a36Sopenharmony_ci goto err_put_free; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci ret = -ENOMEM; 30962306a36Sopenharmony_ci fence = virtio_gpu_fence_alloc(vgdev, vgdev->fence_drv.context, 31062306a36Sopenharmony_ci 0); 31162306a36Sopenharmony_ci if (!fence) 31262306a36Sopenharmony_ci goto err_unlock; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci virtio_gpu_cmd_transfer_to_host_3d 31562306a36Sopenharmony_ci (vgdev, 31662306a36Sopenharmony_ci vfpriv ? vfpriv->ctx_id : 0, offset, args->level, 31762306a36Sopenharmony_ci args->stride, args->layer_stride, &args->box, objs, 31862306a36Sopenharmony_ci fence); 31962306a36Sopenharmony_ci dma_fence_put(&fence->f); 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci virtio_gpu_notify(vgdev); 32262306a36Sopenharmony_ci return 0; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cierr_unlock: 32562306a36Sopenharmony_ci virtio_gpu_array_unlock_resv(objs); 32662306a36Sopenharmony_cierr_put_free: 32762306a36Sopenharmony_ci virtio_gpu_array_put_free(objs); 32862306a36Sopenharmony_ci return ret; 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic int virtio_gpu_wait_ioctl(struct drm_device *dev, void *data, 33262306a36Sopenharmony_ci struct drm_file *file) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci struct drm_virtgpu_3d_wait *args = data; 33562306a36Sopenharmony_ci struct drm_gem_object *obj; 33662306a36Sopenharmony_ci long timeout = 15 * HZ; 33762306a36Sopenharmony_ci int ret; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci obj = drm_gem_object_lookup(file, args->handle); 34062306a36Sopenharmony_ci if (obj == NULL) 34162306a36Sopenharmony_ci return -ENOENT; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (args->flags & VIRTGPU_WAIT_NOWAIT) { 34462306a36Sopenharmony_ci ret = dma_resv_test_signaled(obj->resv, DMA_RESV_USAGE_READ); 34562306a36Sopenharmony_ci } else { 34662306a36Sopenharmony_ci ret = dma_resv_wait_timeout(obj->resv, DMA_RESV_USAGE_READ, 34762306a36Sopenharmony_ci true, timeout); 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci if (ret == 0) 35062306a36Sopenharmony_ci ret = -EBUSY; 35162306a36Sopenharmony_ci else if (ret > 0) 35262306a36Sopenharmony_ci ret = 0; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci drm_gem_object_put(obj); 35562306a36Sopenharmony_ci return ret; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic int virtio_gpu_get_caps_ioctl(struct drm_device *dev, 35962306a36Sopenharmony_ci void *data, struct drm_file *file) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci struct virtio_gpu_device *vgdev = dev->dev_private; 36262306a36Sopenharmony_ci struct drm_virtgpu_get_caps *args = data; 36362306a36Sopenharmony_ci unsigned size, host_caps_size; 36462306a36Sopenharmony_ci int i; 36562306a36Sopenharmony_ci int found_valid = -1; 36662306a36Sopenharmony_ci int ret; 36762306a36Sopenharmony_ci struct virtio_gpu_drv_cap_cache *cache_ent; 36862306a36Sopenharmony_ci void *ptr; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (vgdev->num_capsets == 0) 37162306a36Sopenharmony_ci return -ENOSYS; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* don't allow userspace to pass 0 */ 37462306a36Sopenharmony_ci if (args->size == 0) 37562306a36Sopenharmony_ci return -EINVAL; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci spin_lock(&vgdev->display_info_lock); 37862306a36Sopenharmony_ci for (i = 0; i < vgdev->num_capsets; i++) { 37962306a36Sopenharmony_ci if (vgdev->capsets[i].id == args->cap_set_id) { 38062306a36Sopenharmony_ci if (vgdev->capsets[i].max_version >= args->cap_set_ver) { 38162306a36Sopenharmony_ci found_valid = i; 38262306a36Sopenharmony_ci break; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (found_valid == -1) { 38862306a36Sopenharmony_ci spin_unlock(&vgdev->display_info_lock); 38962306a36Sopenharmony_ci return -EINVAL; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci host_caps_size = vgdev->capsets[found_valid].max_size; 39362306a36Sopenharmony_ci /* only copy to user the minimum of the host caps size or the guest caps size */ 39462306a36Sopenharmony_ci size = min(args->size, host_caps_size); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci list_for_each_entry(cache_ent, &vgdev->cap_cache, head) { 39762306a36Sopenharmony_ci if (cache_ent->id == args->cap_set_id && 39862306a36Sopenharmony_ci cache_ent->version == args->cap_set_ver) { 39962306a36Sopenharmony_ci spin_unlock(&vgdev->display_info_lock); 40062306a36Sopenharmony_ci goto copy_exit; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci spin_unlock(&vgdev->display_info_lock); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci /* not in cache - need to talk to hw */ 40662306a36Sopenharmony_ci ret = virtio_gpu_cmd_get_capset(vgdev, found_valid, args->cap_set_ver, 40762306a36Sopenharmony_ci &cache_ent); 40862306a36Sopenharmony_ci if (ret) 40962306a36Sopenharmony_ci return ret; 41062306a36Sopenharmony_ci virtio_gpu_notify(vgdev); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cicopy_exit: 41362306a36Sopenharmony_ci ret = wait_event_timeout(vgdev->resp_wq, 41462306a36Sopenharmony_ci atomic_read(&cache_ent->is_valid), 5 * HZ); 41562306a36Sopenharmony_ci if (!ret) 41662306a36Sopenharmony_ci return -EBUSY; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci /* is_valid check must proceed before copy of the cache entry. */ 41962306a36Sopenharmony_ci smp_rmb(); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci ptr = cache_ent->caps_cache; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (copy_to_user(u64_to_user_ptr(args->addr), ptr, size)) 42462306a36Sopenharmony_ci return -EFAULT; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci return 0; 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic int verify_blob(struct virtio_gpu_device *vgdev, 43062306a36Sopenharmony_ci struct virtio_gpu_fpriv *vfpriv, 43162306a36Sopenharmony_ci struct virtio_gpu_object_params *params, 43262306a36Sopenharmony_ci struct drm_virtgpu_resource_create_blob *rc_blob, 43362306a36Sopenharmony_ci bool *guest_blob, bool *host3d_blob) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci if (!vgdev->has_resource_blob) 43662306a36Sopenharmony_ci return -EINVAL; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (rc_blob->blob_flags & ~VIRTGPU_BLOB_FLAG_USE_MASK) 43962306a36Sopenharmony_ci return -EINVAL; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (rc_blob->blob_flags & VIRTGPU_BLOB_FLAG_USE_CROSS_DEVICE) { 44262306a36Sopenharmony_ci if (!vgdev->has_resource_assign_uuid) 44362306a36Sopenharmony_ci return -EINVAL; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci switch (rc_blob->blob_mem) { 44762306a36Sopenharmony_ci case VIRTGPU_BLOB_MEM_GUEST: 44862306a36Sopenharmony_ci *guest_blob = true; 44962306a36Sopenharmony_ci break; 45062306a36Sopenharmony_ci case VIRTGPU_BLOB_MEM_HOST3D_GUEST: 45162306a36Sopenharmony_ci *guest_blob = true; 45262306a36Sopenharmony_ci fallthrough; 45362306a36Sopenharmony_ci case VIRTGPU_BLOB_MEM_HOST3D: 45462306a36Sopenharmony_ci *host3d_blob = true; 45562306a36Sopenharmony_ci break; 45662306a36Sopenharmony_ci default: 45762306a36Sopenharmony_ci return -EINVAL; 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci if (*host3d_blob) { 46162306a36Sopenharmony_ci if (!vgdev->has_virgl_3d) 46262306a36Sopenharmony_ci return -EINVAL; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci /* Must be dword aligned. */ 46562306a36Sopenharmony_ci if (rc_blob->cmd_size % 4 != 0) 46662306a36Sopenharmony_ci return -EINVAL; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci params->ctx_id = vfpriv->ctx_id; 46962306a36Sopenharmony_ci params->blob_id = rc_blob->blob_id; 47062306a36Sopenharmony_ci } else { 47162306a36Sopenharmony_ci if (rc_blob->blob_id != 0) 47262306a36Sopenharmony_ci return -EINVAL; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci if (rc_blob->cmd_size != 0) 47562306a36Sopenharmony_ci return -EINVAL; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci params->blob_mem = rc_blob->blob_mem; 47962306a36Sopenharmony_ci params->size = rc_blob->size; 48062306a36Sopenharmony_ci params->blob = true; 48162306a36Sopenharmony_ci params->blob_flags = rc_blob->blob_flags; 48262306a36Sopenharmony_ci return 0; 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic int virtio_gpu_resource_create_blob_ioctl(struct drm_device *dev, 48662306a36Sopenharmony_ci void *data, 48762306a36Sopenharmony_ci struct drm_file *file) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci int ret = 0; 49062306a36Sopenharmony_ci uint32_t handle = 0; 49162306a36Sopenharmony_ci bool guest_blob = false; 49262306a36Sopenharmony_ci bool host3d_blob = false; 49362306a36Sopenharmony_ci struct drm_gem_object *obj; 49462306a36Sopenharmony_ci struct virtio_gpu_object *bo; 49562306a36Sopenharmony_ci struct virtio_gpu_object_params params = { 0 }; 49662306a36Sopenharmony_ci struct virtio_gpu_device *vgdev = dev->dev_private; 49762306a36Sopenharmony_ci struct virtio_gpu_fpriv *vfpriv = file->driver_priv; 49862306a36Sopenharmony_ci struct drm_virtgpu_resource_create_blob *rc_blob = data; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci if (verify_blob(vgdev, vfpriv, ¶ms, rc_blob, 50162306a36Sopenharmony_ci &guest_blob, &host3d_blob)) 50262306a36Sopenharmony_ci return -EINVAL; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (vgdev->has_virgl_3d) 50562306a36Sopenharmony_ci virtio_gpu_create_context(dev, file); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if (rc_blob->cmd_size) { 50862306a36Sopenharmony_ci void *buf; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci buf = memdup_user(u64_to_user_ptr(rc_blob->cmd), 51162306a36Sopenharmony_ci rc_blob->cmd_size); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if (IS_ERR(buf)) 51462306a36Sopenharmony_ci return PTR_ERR(buf); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci virtio_gpu_cmd_submit(vgdev, buf, rc_blob->cmd_size, 51762306a36Sopenharmony_ci vfpriv->ctx_id, NULL, NULL); 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (guest_blob) 52162306a36Sopenharmony_ci ret = virtio_gpu_object_create(vgdev, ¶ms, &bo, NULL); 52262306a36Sopenharmony_ci else if (!guest_blob && host3d_blob) 52362306a36Sopenharmony_ci ret = virtio_gpu_vram_create(vgdev, ¶ms, &bo); 52462306a36Sopenharmony_ci else 52562306a36Sopenharmony_ci return -EINVAL; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (ret < 0) 52862306a36Sopenharmony_ci return ret; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci bo->guest_blob = guest_blob; 53162306a36Sopenharmony_ci bo->host3d_blob = host3d_blob; 53262306a36Sopenharmony_ci bo->blob_mem = rc_blob->blob_mem; 53362306a36Sopenharmony_ci bo->blob_flags = rc_blob->blob_flags; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci obj = &bo->base.base; 53662306a36Sopenharmony_ci if (params.blob_flags & VIRTGPU_BLOB_FLAG_USE_CROSS_DEVICE) { 53762306a36Sopenharmony_ci ret = virtio_gpu_resource_assign_uuid(vgdev, bo); 53862306a36Sopenharmony_ci if (ret) { 53962306a36Sopenharmony_ci drm_gem_object_release(obj); 54062306a36Sopenharmony_ci return ret; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci ret = drm_gem_handle_create(file, obj, &handle); 54562306a36Sopenharmony_ci if (ret) { 54662306a36Sopenharmony_ci drm_gem_object_release(obj); 54762306a36Sopenharmony_ci return ret; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci rc_blob->res_handle = bo->hw_res_handle; 55162306a36Sopenharmony_ci rc_blob->bo_handle = handle; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci /* 55462306a36Sopenharmony_ci * The handle owns the reference now. But we must drop our 55562306a36Sopenharmony_ci * remaining reference *after* we no longer need to dereference 55662306a36Sopenharmony_ci * the obj. Otherwise userspace could guess the handle and 55762306a36Sopenharmony_ci * race closing it from another thread. 55862306a36Sopenharmony_ci */ 55962306a36Sopenharmony_ci drm_gem_object_put(obj); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci return 0; 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic int virtio_gpu_context_init_ioctl(struct drm_device *dev, 56562306a36Sopenharmony_ci void *data, struct drm_file *file) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci int ret = 0; 56862306a36Sopenharmony_ci uint32_t num_params, i, param, value; 56962306a36Sopenharmony_ci uint64_t valid_ring_mask; 57062306a36Sopenharmony_ci size_t len; 57162306a36Sopenharmony_ci struct drm_virtgpu_context_set_param *ctx_set_params = NULL; 57262306a36Sopenharmony_ci struct virtio_gpu_device *vgdev = dev->dev_private; 57362306a36Sopenharmony_ci struct virtio_gpu_fpriv *vfpriv = file->driver_priv; 57462306a36Sopenharmony_ci struct drm_virtgpu_context_init *args = data; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci num_params = args->num_params; 57762306a36Sopenharmony_ci len = num_params * sizeof(struct drm_virtgpu_context_set_param); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (!vgdev->has_context_init || !vgdev->has_virgl_3d) 58062306a36Sopenharmony_ci return -EINVAL; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci /* Number of unique parameters supported at this time. */ 58362306a36Sopenharmony_ci if (num_params > 3) 58462306a36Sopenharmony_ci return -EINVAL; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci ctx_set_params = memdup_user(u64_to_user_ptr(args->ctx_set_params), 58762306a36Sopenharmony_ci len); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci if (IS_ERR(ctx_set_params)) 59062306a36Sopenharmony_ci return PTR_ERR(ctx_set_params); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci mutex_lock(&vfpriv->context_lock); 59362306a36Sopenharmony_ci if (vfpriv->context_created) { 59462306a36Sopenharmony_ci ret = -EEXIST; 59562306a36Sopenharmony_ci goto out_unlock; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci for (i = 0; i < num_params; i++) { 59962306a36Sopenharmony_ci param = ctx_set_params[i].param; 60062306a36Sopenharmony_ci value = ctx_set_params[i].value; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci switch (param) { 60362306a36Sopenharmony_ci case VIRTGPU_CONTEXT_PARAM_CAPSET_ID: 60462306a36Sopenharmony_ci if (value > MAX_CAPSET_ID) { 60562306a36Sopenharmony_ci ret = -EINVAL; 60662306a36Sopenharmony_ci goto out_unlock; 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci if ((vgdev->capset_id_mask & (1ULL << value)) == 0) { 61062306a36Sopenharmony_ci ret = -EINVAL; 61162306a36Sopenharmony_ci goto out_unlock; 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci /* Context capset ID already set */ 61562306a36Sopenharmony_ci if (vfpriv->context_init & 61662306a36Sopenharmony_ci VIRTIO_GPU_CONTEXT_INIT_CAPSET_ID_MASK) { 61762306a36Sopenharmony_ci ret = -EINVAL; 61862306a36Sopenharmony_ci goto out_unlock; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci vfpriv->context_init |= value; 62262306a36Sopenharmony_ci break; 62362306a36Sopenharmony_ci case VIRTGPU_CONTEXT_PARAM_NUM_RINGS: 62462306a36Sopenharmony_ci if (vfpriv->base_fence_ctx) { 62562306a36Sopenharmony_ci ret = -EINVAL; 62662306a36Sopenharmony_ci goto out_unlock; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci if (value > MAX_RINGS) { 63062306a36Sopenharmony_ci ret = -EINVAL; 63162306a36Sopenharmony_ci goto out_unlock; 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci vfpriv->base_fence_ctx = dma_fence_context_alloc(value); 63562306a36Sopenharmony_ci vfpriv->num_rings = value; 63662306a36Sopenharmony_ci break; 63762306a36Sopenharmony_ci case VIRTGPU_CONTEXT_PARAM_POLL_RINGS_MASK: 63862306a36Sopenharmony_ci if (vfpriv->ring_idx_mask) { 63962306a36Sopenharmony_ci ret = -EINVAL; 64062306a36Sopenharmony_ci goto out_unlock; 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci vfpriv->ring_idx_mask = value; 64462306a36Sopenharmony_ci break; 64562306a36Sopenharmony_ci default: 64662306a36Sopenharmony_ci ret = -EINVAL; 64762306a36Sopenharmony_ci goto out_unlock; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci if (vfpriv->ring_idx_mask) { 65262306a36Sopenharmony_ci valid_ring_mask = 0; 65362306a36Sopenharmony_ci for (i = 0; i < vfpriv->num_rings; i++) 65462306a36Sopenharmony_ci valid_ring_mask |= 1ULL << i; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (~valid_ring_mask & vfpriv->ring_idx_mask) { 65762306a36Sopenharmony_ci ret = -EINVAL; 65862306a36Sopenharmony_ci goto out_unlock; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci virtio_gpu_create_context_locked(vgdev, vfpriv); 66362306a36Sopenharmony_ci virtio_gpu_notify(vgdev); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ciout_unlock: 66662306a36Sopenharmony_ci mutex_unlock(&vfpriv->context_lock); 66762306a36Sopenharmony_ci kfree(ctx_set_params); 66862306a36Sopenharmony_ci return ret; 66962306a36Sopenharmony_ci} 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_cistruct drm_ioctl_desc virtio_gpu_ioctls[DRM_VIRTIO_NUM_IOCTLS] = { 67262306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(VIRTGPU_MAP, virtio_gpu_map_ioctl, 67362306a36Sopenharmony_ci DRM_RENDER_ALLOW), 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(VIRTGPU_EXECBUFFER, virtio_gpu_execbuffer_ioctl, 67662306a36Sopenharmony_ci DRM_RENDER_ALLOW), 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(VIRTGPU_GETPARAM, virtio_gpu_getparam_ioctl, 67962306a36Sopenharmony_ci DRM_RENDER_ALLOW), 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(VIRTGPU_RESOURCE_CREATE, 68262306a36Sopenharmony_ci virtio_gpu_resource_create_ioctl, 68362306a36Sopenharmony_ci DRM_RENDER_ALLOW), 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(VIRTGPU_RESOURCE_INFO, virtio_gpu_resource_info_ioctl, 68662306a36Sopenharmony_ci DRM_RENDER_ALLOW), 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci /* make transfer async to the main ring? - no sure, can we 68962306a36Sopenharmony_ci * thread these in the underlying GL 69062306a36Sopenharmony_ci */ 69162306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(VIRTGPU_TRANSFER_FROM_HOST, 69262306a36Sopenharmony_ci virtio_gpu_transfer_from_host_ioctl, 69362306a36Sopenharmony_ci DRM_RENDER_ALLOW), 69462306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(VIRTGPU_TRANSFER_TO_HOST, 69562306a36Sopenharmony_ci virtio_gpu_transfer_to_host_ioctl, 69662306a36Sopenharmony_ci DRM_RENDER_ALLOW), 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(VIRTGPU_WAIT, virtio_gpu_wait_ioctl, 69962306a36Sopenharmony_ci DRM_RENDER_ALLOW), 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(VIRTGPU_GET_CAPS, virtio_gpu_get_caps_ioctl, 70262306a36Sopenharmony_ci DRM_RENDER_ALLOW), 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(VIRTGPU_RESOURCE_CREATE_BLOB, 70562306a36Sopenharmony_ci virtio_gpu_resource_create_blob_ioctl, 70662306a36Sopenharmony_ci DRM_RENDER_ALLOW), 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(VIRTGPU_CONTEXT_INIT, virtio_gpu_context_init_ioctl, 70962306a36Sopenharmony_ci DRM_RENDER_ALLOW), 71062306a36Sopenharmony_ci}; 711