162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include "virtgpu_drv.h"
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/dma-mapping.h>
562306a36Sopenharmony_ci
662306a36Sopenharmony_cistatic void virtio_gpu_vram_free(struct drm_gem_object *obj)
762306a36Sopenharmony_ci{
862306a36Sopenharmony_ci	struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(obj);
962306a36Sopenharmony_ci	struct virtio_gpu_device *vgdev = obj->dev->dev_private;
1062306a36Sopenharmony_ci	struct virtio_gpu_object_vram *vram = to_virtio_gpu_vram(bo);
1162306a36Sopenharmony_ci	bool unmap;
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci	if (bo->created) {
1462306a36Sopenharmony_ci		spin_lock(&vgdev->host_visible_lock);
1562306a36Sopenharmony_ci		unmap = drm_mm_node_allocated(&vram->vram_node);
1662306a36Sopenharmony_ci		spin_unlock(&vgdev->host_visible_lock);
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci		if (unmap)
1962306a36Sopenharmony_ci			virtio_gpu_cmd_unmap(vgdev, bo);
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci		virtio_gpu_cmd_unref_resource(vgdev, bo);
2262306a36Sopenharmony_ci		virtio_gpu_notify(vgdev);
2362306a36Sopenharmony_ci		return;
2462306a36Sopenharmony_ci	}
2562306a36Sopenharmony_ci}
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic const struct vm_operations_struct virtio_gpu_vram_vm_ops = {
2862306a36Sopenharmony_ci	.open = drm_gem_vm_open,
2962306a36Sopenharmony_ci	.close = drm_gem_vm_close,
3062306a36Sopenharmony_ci};
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic int virtio_gpu_vram_mmap(struct drm_gem_object *obj,
3362306a36Sopenharmony_ci				struct vm_area_struct *vma)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	int ret;
3662306a36Sopenharmony_ci	struct virtio_gpu_device *vgdev = obj->dev->dev_private;
3762306a36Sopenharmony_ci	struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(obj);
3862306a36Sopenharmony_ci	struct virtio_gpu_object_vram *vram = to_virtio_gpu_vram(bo);
3962306a36Sopenharmony_ci	unsigned long vm_size = vma->vm_end - vma->vm_start;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	if (!(bo->blob_flags & VIRTGPU_BLOB_FLAG_USE_MAPPABLE))
4262306a36Sopenharmony_ci		return -EINVAL;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	wait_event(vgdev->resp_wq, vram->map_state != STATE_INITIALIZING);
4562306a36Sopenharmony_ci	if (vram->map_state != STATE_OK)
4662306a36Sopenharmony_ci		return -EINVAL;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	vma->vm_pgoff -= drm_vma_node_start(&obj->vma_node);
4962306a36Sopenharmony_ci	vm_flags_set(vma, VM_MIXEDMAP | VM_DONTEXPAND);
5062306a36Sopenharmony_ci	vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
5162306a36Sopenharmony_ci	vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);
5262306a36Sopenharmony_ci	vma->vm_ops = &virtio_gpu_vram_vm_ops;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	if (vram->map_info == VIRTIO_GPU_MAP_CACHE_WC)
5562306a36Sopenharmony_ci		vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
5662306a36Sopenharmony_ci	else if (vram->map_info == VIRTIO_GPU_MAP_CACHE_UNCACHED)
5762306a36Sopenharmony_ci		vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	/* Partial mappings of GEM buffers don't happen much in practice. */
6062306a36Sopenharmony_ci	if (vm_size != vram->vram_node.size)
6162306a36Sopenharmony_ci		return -EINVAL;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	ret = io_remap_pfn_range(vma, vma->vm_start,
6462306a36Sopenharmony_ci				 vram->vram_node.start >> PAGE_SHIFT,
6562306a36Sopenharmony_ci				 vm_size, vma->vm_page_prot);
6662306a36Sopenharmony_ci	return ret;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistruct sg_table *virtio_gpu_vram_map_dma_buf(struct virtio_gpu_object *bo,
7062306a36Sopenharmony_ci					     struct device *dev,
7162306a36Sopenharmony_ci					     enum dma_data_direction dir)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	struct virtio_gpu_device *vgdev = bo->base.base.dev->dev_private;
7462306a36Sopenharmony_ci	struct virtio_gpu_object_vram *vram = to_virtio_gpu_vram(bo);
7562306a36Sopenharmony_ci	struct sg_table *sgt;
7662306a36Sopenharmony_ci	dma_addr_t addr;
7762306a36Sopenharmony_ci	int ret;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
8062306a36Sopenharmony_ci	if (!sgt)
8162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	if (!(bo->blob_flags & VIRTGPU_BLOB_FLAG_USE_MAPPABLE)) {
8462306a36Sopenharmony_ci		// Virtio devices can access the dma-buf via its UUID. Return a stub
8562306a36Sopenharmony_ci		// sg_table so the dma-buf API still works.
8662306a36Sopenharmony_ci		if (!is_virtio_device(dev) || !vgdev->has_resource_assign_uuid) {
8762306a36Sopenharmony_ci			ret = -EIO;
8862306a36Sopenharmony_ci			goto out;
8962306a36Sopenharmony_ci		}
9062306a36Sopenharmony_ci		return sgt;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
9462306a36Sopenharmony_ci	if (ret)
9562306a36Sopenharmony_ci		goto out;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	addr = dma_map_resource(dev, vram->vram_node.start,
9862306a36Sopenharmony_ci				vram->vram_node.size, dir,
9962306a36Sopenharmony_ci				DMA_ATTR_SKIP_CPU_SYNC);
10062306a36Sopenharmony_ci	ret = dma_mapping_error(dev, addr);
10162306a36Sopenharmony_ci	if (ret)
10262306a36Sopenharmony_ci		goto out;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	sg_set_page(sgt->sgl, NULL, vram->vram_node.size, 0);
10562306a36Sopenharmony_ci	sg_dma_address(sgt->sgl) = addr;
10662306a36Sopenharmony_ci	sg_dma_len(sgt->sgl) = vram->vram_node.size;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	return sgt;
10962306a36Sopenharmony_ciout:
11062306a36Sopenharmony_ci	sg_free_table(sgt);
11162306a36Sopenharmony_ci	kfree(sgt);
11262306a36Sopenharmony_ci	return ERR_PTR(ret);
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_civoid virtio_gpu_vram_unmap_dma_buf(struct device *dev,
11662306a36Sopenharmony_ci				   struct sg_table *sgt,
11762306a36Sopenharmony_ci				   enum dma_data_direction dir)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	if (sgt->nents) {
12062306a36Sopenharmony_ci		dma_unmap_resource(dev, sg_dma_address(sgt->sgl),
12162306a36Sopenharmony_ci				   sg_dma_len(sgt->sgl), dir,
12262306a36Sopenharmony_ci				   DMA_ATTR_SKIP_CPU_SYNC);
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci	sg_free_table(sgt);
12562306a36Sopenharmony_ci	kfree(sgt);
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic const struct drm_gem_object_funcs virtio_gpu_vram_funcs = {
12962306a36Sopenharmony_ci	.open = virtio_gpu_gem_object_open,
13062306a36Sopenharmony_ci	.close = virtio_gpu_gem_object_close,
13162306a36Sopenharmony_ci	.free = virtio_gpu_vram_free,
13262306a36Sopenharmony_ci	.mmap = virtio_gpu_vram_mmap,
13362306a36Sopenharmony_ci	.export = virtgpu_gem_prime_export,
13462306a36Sopenharmony_ci};
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cibool virtio_gpu_is_vram(struct virtio_gpu_object *bo)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	return bo->base.base.funcs == &virtio_gpu_vram_funcs;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic int virtio_gpu_vram_map(struct virtio_gpu_object *bo)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	int ret;
14462306a36Sopenharmony_ci	uint64_t offset;
14562306a36Sopenharmony_ci	struct virtio_gpu_object_array *objs;
14662306a36Sopenharmony_ci	struct virtio_gpu_device *vgdev = bo->base.base.dev->dev_private;
14762306a36Sopenharmony_ci	struct virtio_gpu_object_vram *vram = to_virtio_gpu_vram(bo);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	if (!vgdev->has_host_visible)
15062306a36Sopenharmony_ci		return -EINVAL;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	spin_lock(&vgdev->host_visible_lock);
15362306a36Sopenharmony_ci	ret = drm_mm_insert_node(&vgdev->host_visible_mm, &vram->vram_node,
15462306a36Sopenharmony_ci				 bo->base.base.size);
15562306a36Sopenharmony_ci	spin_unlock(&vgdev->host_visible_lock);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	if (ret)
15862306a36Sopenharmony_ci		return ret;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	objs = virtio_gpu_array_alloc(1);
16162306a36Sopenharmony_ci	if (!objs) {
16262306a36Sopenharmony_ci		ret = -ENOMEM;
16362306a36Sopenharmony_ci		goto err_remove_node;
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	virtio_gpu_array_add_obj(objs, &bo->base.base);
16762306a36Sopenharmony_ci	/*TODO: Add an error checking helper function in drm_mm.h */
16862306a36Sopenharmony_ci	offset = vram->vram_node.start - vgdev->host_visible_region.addr;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	ret = virtio_gpu_cmd_map(vgdev, objs, offset);
17162306a36Sopenharmony_ci	if (ret) {
17262306a36Sopenharmony_ci		virtio_gpu_array_put_free(objs);
17362306a36Sopenharmony_ci		goto err_remove_node;
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	return 0;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cierr_remove_node:
17962306a36Sopenharmony_ci	spin_lock(&vgdev->host_visible_lock);
18062306a36Sopenharmony_ci	drm_mm_remove_node(&vram->vram_node);
18162306a36Sopenharmony_ci	spin_unlock(&vgdev->host_visible_lock);
18262306a36Sopenharmony_ci	return ret;
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ciint virtio_gpu_vram_create(struct virtio_gpu_device *vgdev,
18662306a36Sopenharmony_ci			   struct virtio_gpu_object_params *params,
18762306a36Sopenharmony_ci			   struct virtio_gpu_object **bo_ptr)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	struct drm_gem_object *obj;
19062306a36Sopenharmony_ci	struct virtio_gpu_object_vram *vram;
19162306a36Sopenharmony_ci	int ret;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	vram = kzalloc(sizeof(*vram), GFP_KERNEL);
19462306a36Sopenharmony_ci	if (!vram)
19562306a36Sopenharmony_ci		return -ENOMEM;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	obj = &vram->base.base.base;
19862306a36Sopenharmony_ci	obj->funcs = &virtio_gpu_vram_funcs;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	params->size = PAGE_ALIGN(params->size);
20162306a36Sopenharmony_ci	drm_gem_private_object_init(vgdev->ddev, obj, params->size);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	/* Create fake offset */
20462306a36Sopenharmony_ci	ret = drm_gem_create_mmap_offset(obj);
20562306a36Sopenharmony_ci	if (ret) {
20662306a36Sopenharmony_ci		kfree(vram);
20762306a36Sopenharmony_ci		return ret;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	ret = virtio_gpu_resource_id_get(vgdev, &vram->base.hw_res_handle);
21162306a36Sopenharmony_ci	if (ret) {
21262306a36Sopenharmony_ci		kfree(vram);
21362306a36Sopenharmony_ci		return ret;
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	virtio_gpu_cmd_resource_create_blob(vgdev, &vram->base, params, NULL,
21762306a36Sopenharmony_ci					    0);
21862306a36Sopenharmony_ci	if (params->blob_flags & VIRTGPU_BLOB_FLAG_USE_MAPPABLE) {
21962306a36Sopenharmony_ci		ret = virtio_gpu_vram_map(&vram->base);
22062306a36Sopenharmony_ci		if (ret) {
22162306a36Sopenharmony_ci			virtio_gpu_vram_free(obj);
22262306a36Sopenharmony_ci			return ret;
22362306a36Sopenharmony_ci		}
22462306a36Sopenharmony_ci	}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	*bo_ptr = &vram->base;
22762306a36Sopenharmony_ci	return 0;
22862306a36Sopenharmony_ci}
229