1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2 of the License, or
6  * (at your option) any later version.
7  */
8 
9 #include <linux/shmem_fs.h>
10 
11 #include "vkms_drv.h"
12 
__vkms_gem_create(struct drm_device * dev,u64 size)13 static struct vkms_gem_object *__vkms_gem_create(struct drm_device *dev,
14 						 u64 size)
15 {
16 	struct vkms_gem_object *obj;
17 	int ret;
18 
19 	obj = kzalloc(sizeof(*obj), GFP_KERNEL);
20 	if (!obj)
21 		return ERR_PTR(-ENOMEM);
22 
23 	size = roundup(size, PAGE_SIZE);
24 	ret = drm_gem_object_init(dev, &obj->gem, size);
25 	if (ret) {
26 		kfree(obj);
27 		return ERR_PTR(ret);
28 	}
29 
30 	mutex_init(&obj->pages_lock);
31 
32 	return obj;
33 }
34 
vkms_gem_free_object(struct drm_gem_object * obj)35 void vkms_gem_free_object(struct drm_gem_object *obj)
36 {
37 	struct vkms_gem_object *gem = container_of(obj, struct vkms_gem_object,
38 						   gem);
39 
40 	kvfree(gem->pages);
41 	mutex_destroy(&gem->pages_lock);
42 	drm_gem_object_release(obj);
43 	kfree(gem);
44 }
45 
vkms_gem_fault(struct vm_fault * vmf)46 int vkms_gem_fault(struct vm_fault *vmf)
47 {
48 	struct vm_area_struct *vma = vmf->vma;
49 	struct vkms_gem_object *obj = vma->vm_private_data;
50 	unsigned long vaddr = vmf->address;
51 	pgoff_t page_offset;
52 	loff_t num_pages;
53 	int ret;
54 
55 	page_offset = (vaddr - vma->vm_start) >> PAGE_SHIFT;
56 	num_pages = DIV_ROUND_UP(obj->gem.size, PAGE_SIZE);
57 
58 	if (page_offset > num_pages)
59 		return VM_FAULT_SIGBUS;
60 
61 	ret = -ENOENT;
62 	mutex_lock(&obj->pages_lock);
63 	if (obj->pages) {
64 		get_page(obj->pages[page_offset]);
65 		vmf->page = obj->pages[page_offset];
66 		ret = 0;
67 	}
68 	mutex_unlock(&obj->pages_lock);
69 	if (ret) {
70 		struct page *page;
71 		struct address_space *mapping;
72 
73 		mapping = file_inode(obj->gem.filp)->i_mapping;
74 		page = shmem_read_mapping_page(mapping, page_offset);
75 
76 		if (!IS_ERR(page)) {
77 			vmf->page = page;
78 			ret = 0;
79 		} else {
80 			switch (PTR_ERR(page)) {
81 			case -ENOSPC:
82 			case -ENOMEM:
83 				ret = VM_FAULT_OOM;
84 				break;
85 			case -EBUSY:
86 				ret = VM_FAULT_RETRY;
87 				break;
88 			case -EFAULT:
89 			case -EINVAL:
90 				ret = VM_FAULT_SIGBUS;
91 				break;
92 			default:
93 				WARN_ON(PTR_ERR(page));
94 				ret = VM_FAULT_SIGBUS;
95 				break;
96 			}
97 		}
98 	}
99 	return ret;
100 }
101 
vkms_gem_create(struct drm_device * dev,struct drm_file * file,u32 * handle,u64 size)102 struct drm_gem_object *vkms_gem_create(struct drm_device *dev,
103 				       struct drm_file *file,
104 				       u32 *handle,
105 				       u64 size)
106 {
107 	struct vkms_gem_object *obj;
108 	int ret;
109 
110 	if (!file || !dev || !handle)
111 		return ERR_PTR(-EINVAL);
112 
113 	obj = __vkms_gem_create(dev, size);
114 	if (IS_ERR(obj))
115 		return ERR_CAST(obj);
116 
117 	ret = drm_gem_handle_create(file, &obj->gem, handle);
118 	drm_gem_object_put_unlocked(&obj->gem);
119 	if (ret) {
120 		drm_gem_object_release(&obj->gem);
121 		kfree(obj);
122 		return ERR_PTR(ret);
123 	}
124 
125 	return &obj->gem;
126 }
127 
vkms_dumb_create(struct drm_file * file,struct drm_device * dev,struct drm_mode_create_dumb * args)128 int vkms_dumb_create(struct drm_file *file, struct drm_device *dev,
129 		     struct drm_mode_create_dumb *args)
130 {
131 	struct drm_gem_object *gem_obj;
132 	u64 pitch, size;
133 
134 	if (!args || !dev || !file)
135 		return -EINVAL;
136 
137 	pitch = args->width * DIV_ROUND_UP(args->bpp, 8);
138 	size = pitch * args->height;
139 
140 	if (!size)
141 		return -EINVAL;
142 
143 	gem_obj = vkms_gem_create(dev, file, &args->handle, size);
144 	if (IS_ERR(gem_obj))
145 		return PTR_ERR(gem_obj);
146 
147 	args->size = gem_obj->size;
148 	args->pitch = pitch;
149 
150 	DRM_DEBUG_DRIVER("Created object of size %lld\n", size);
151 
152 	return 0;
153 }
154 
vkms_dumb_map(struct drm_file * file,struct drm_device * dev,u32 handle,u64 * offset)155 int vkms_dumb_map(struct drm_file *file, struct drm_device *dev,
156 		  u32 handle, u64 *offset)
157 {
158 	struct drm_gem_object *obj;
159 	int ret;
160 
161 	obj = drm_gem_object_lookup(file, handle);
162 	if (!obj)
163 		return -ENOENT;
164 
165 	if (!obj->filp) {
166 		ret = -EINVAL;
167 		goto unref;
168 	}
169 
170 	ret = drm_gem_create_mmap_offset(obj);
171 	if (ret)
172 		goto unref;
173 
174 	*offset = drm_vma_node_offset_addr(&obj->vma_node);
175 unref:
176 	drm_gem_object_put_unlocked(obj);
177 
178 	return ret;
179 }
180