1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2011 The FreeBSD Foundation
5 *
6 * This software was developed by Konstantin Belousov under sponsorship from
7 * the FreeBSD Foundation.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 #include <sys/cdefs.h>
32 #include "opt_vm.h"
33
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/limits.h>
37 #include <sys/lock.h>
38 #include <sys/mutex.h>
39
40 #include <vm/vm.h>
41 #include <vm/vm_page.h>
42
43 #include <dev/drm2/drmP.h>
44 #include <dev/drm2/drm.h>
45 #include <dev/drm2/drm_sarea.h>
46
47 /*
48 * We make up offsets for buffer objects so we can recognize them at
49 * mmap time.
50 */
51
52 /* pgoff in mmap is an unsigned long, so we need to make sure that
53 * the faked up offset will fit
54 */
55
56 #if BITS_PER_LONG == 64
57 #define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFFUL >> PAGE_SHIFT) + 1)
58 #define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFFUL >> PAGE_SHIFT) * 16)
59 #else
60 #define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFUL >> PAGE_SHIFT) + 1)
61 #define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFUL >> PAGE_SHIFT) * 16)
62 #endif
63
64 /**
65 * Initialize the GEM device fields
66 */
67
68 int
drm_gem_init(struct drm_device * dev)69 drm_gem_init(struct drm_device *dev)
70 {
71 struct drm_gem_mm *mm;
72
73 drm_gem_names_init(&dev->object_names);
74
75 mm = malloc(sizeof(*mm), DRM_MEM_DRIVER, M_NOWAIT);
76 if (!mm) {
77 DRM_ERROR("out of memory\n");
78 return -ENOMEM;
79 }
80
81 dev->mm_private = mm;
82
83 if (drm_ht_create(&mm->offset_hash, 19)) {
84 free(mm, DRM_MEM_DRIVER);
85 return -ENOMEM;
86 }
87
88 mm->idxunr = new_unrhdr(0, DRM_GEM_MAX_IDX, NULL);
89
90 return 0;
91 }
92
93 void
drm_gem_destroy(struct drm_device * dev)94 drm_gem_destroy(struct drm_device *dev)
95 {
96 struct drm_gem_mm *mm = dev->mm_private;
97
98 dev->mm_private = NULL;
99 drm_ht_remove(&mm->offset_hash);
100 delete_unrhdr(mm->idxunr);
101 free(mm, DRM_MEM_DRIVER);
102 drm_gem_names_fini(&dev->object_names);
103 }
104
drm_gem_object_init(struct drm_device * dev,struct drm_gem_object * obj,size_t size)105 int drm_gem_object_init(struct drm_device *dev,
106 struct drm_gem_object *obj, size_t size)
107 {
108 KASSERT((size & (PAGE_SIZE - 1)) == 0,
109 ("Bad size %ju", (uintmax_t)size));
110
111 obj->dev = dev;
112 obj->vm_obj = vm_pager_allocate(OBJT_SWAP, NULL, size,
113 VM_PROT_READ | VM_PROT_WRITE, 0, curthread->td_ucred);
114
115 obj->refcount = 1;
116 obj->handle_count = 0;
117 obj->size = size;
118
119 return 0;
120 }
121 EXPORT_SYMBOL(drm_gem_object_init);
122
123 /**
124 * Initialize an already allocated GEM object of the specified size with
125 * no GEM provided backing store. Instead the caller is responsible for
126 * backing the object and handling it.
127 */
drm_gem_private_object_init(struct drm_device * dev,struct drm_gem_object * obj,size_t size)128 int drm_gem_private_object_init(struct drm_device *dev,
129 struct drm_gem_object *obj, size_t size)
130 {
131 MPASS((size & (PAGE_SIZE - 1)) == 0);
132
133 obj->dev = dev;
134 obj->vm_obj = NULL;
135
136 obj->refcount = 1;
137 atomic_store_rel_int(&obj->handle_count, 0);
138 obj->size = size;
139
140 return 0;
141 }
142 EXPORT_SYMBOL(drm_gem_private_object_init);
143
144 struct drm_gem_object *
drm_gem_object_alloc(struct drm_device * dev,size_t size)145 drm_gem_object_alloc(struct drm_device *dev, size_t size)
146 {
147 struct drm_gem_object *obj;
148
149 obj = malloc(sizeof(*obj), DRM_MEM_DRIVER, M_NOWAIT | M_ZERO);
150 if (!obj)
151 goto free;
152
153 if (drm_gem_object_init(dev, obj, size) != 0)
154 goto free;
155
156 if (dev->driver->gem_init_object != NULL &&
157 dev->driver->gem_init_object(obj) != 0) {
158 goto dealloc;
159 }
160 return obj;
161 dealloc:
162 vm_object_deallocate(obj->vm_obj);
163 free:
164 free(obj, DRM_MEM_DRIVER);
165 return NULL;
166 }
167 EXPORT_SYMBOL(drm_gem_object_alloc);
168
169 #if defined(FREEBSD_NOTYET)
170 static void
drm_gem_remove_prime_handles(struct drm_gem_object * obj,struct drm_file * filp)171 drm_gem_remove_prime_handles(struct drm_gem_object *obj, struct drm_file *filp)
172 {
173 if (obj->import_attach) {
174 drm_prime_remove_buf_handle(&filp->prime,
175 obj->import_attach->dmabuf);
176 }
177 if (obj->export_dma_buf) {
178 drm_prime_remove_buf_handle(&filp->prime,
179 obj->export_dma_buf);
180 }
181 }
182 #endif
183
184 /**
185 * Removes the mapping from handle to filp for this object.
186 */
187 int
drm_gem_handle_delete(struct drm_file * filp,u32 handle)188 drm_gem_handle_delete(struct drm_file *filp, u32 handle)
189 {
190 struct drm_device *dev;
191 struct drm_gem_object *obj;
192
193 obj = drm_gem_names_remove(&filp->object_names, handle);
194 if (obj == NULL) {
195 return -EINVAL;
196 }
197 dev = obj->dev;
198
199 #if defined(FREEBSD_NOTYET)
200 drm_gem_remove_prime_handles(obj, filp);
201 #endif
202
203 if (dev->driver->gem_close_object)
204 dev->driver->gem_close_object(obj, filp);
205 drm_gem_object_handle_unreference_unlocked(obj);
206
207 return 0;
208 }
209 EXPORT_SYMBOL(drm_gem_handle_delete);
210
211 /**
212 * Create a handle for this object. This adds a handle reference
213 * to the object, which includes a regular reference count. Callers
214 * will likely want to dereference the object afterwards.
215 */
216 int
drm_gem_handle_create(struct drm_file * file_priv,struct drm_gem_object * obj,u32 * handlep)217 drm_gem_handle_create(struct drm_file *file_priv,
218 struct drm_gem_object *obj,
219 u32 *handlep)
220 {
221 struct drm_device *dev = obj->dev;
222 int ret;
223
224 *handlep = 0;
225 ret = drm_gem_name_create(&file_priv->object_names, obj, handlep);
226 if (ret != 0)
227 return ret;
228
229 drm_gem_object_handle_reference(obj);
230
231 if (dev->driver->gem_open_object) {
232 ret = dev->driver->gem_open_object(obj, file_priv);
233 if (ret) {
234 drm_gem_handle_delete(file_priv, *handlep);
235 return ret;
236 }
237 }
238
239 return 0;
240 }
241 EXPORT_SYMBOL(drm_gem_handle_create);
242
243 void
drm_gem_free_mmap_offset(struct drm_gem_object * obj)244 drm_gem_free_mmap_offset(struct drm_gem_object *obj)
245 {
246 struct drm_device *dev = obj->dev;
247 struct drm_gem_mm *mm = dev->mm_private;
248 struct drm_hash_item *list = &obj->map_list;
249
250 if (!obj->on_map)
251 return;
252
253 drm_ht_remove_item(&mm->offset_hash, list);
254 free_unr(mm->idxunr, list->key);
255 obj->on_map = false;
256 }
257 EXPORT_SYMBOL(drm_gem_free_mmap_offset);
258
259 int
drm_gem_create_mmap_offset(struct drm_gem_object * obj)260 drm_gem_create_mmap_offset(struct drm_gem_object *obj)
261 {
262 struct drm_device *dev = obj->dev;
263 struct drm_gem_mm *mm = dev->mm_private;
264 int ret;
265
266 if (obj->on_map)
267 return 0;
268
269 obj->map_list.key = alloc_unr(mm->idxunr);
270 ret = drm_ht_insert_item(&mm->offset_hash, &obj->map_list);
271 if (ret) {
272 DRM_ERROR("failed to add to map hash\n");
273 free_unr(mm->idxunr, obj->map_list.key);
274 return ret;
275 }
276 obj->on_map = true;
277
278 return 0;
279 }
280 EXPORT_SYMBOL(drm_gem_create_mmap_offset);
281
282 /** Returns a reference to the object named by the handle. */
283 struct drm_gem_object *
drm_gem_object_lookup(struct drm_device * dev,struct drm_file * filp,u32 handle)284 drm_gem_object_lookup(struct drm_device *dev, struct drm_file *filp,
285 u32 handle)
286 {
287 struct drm_gem_object *obj;
288
289 obj = drm_gem_name_ref(&filp->object_names, handle,
290 (void (*)(void *))drm_gem_object_reference);
291
292 return obj;
293 }
294 EXPORT_SYMBOL(drm_gem_object_lookup);
295
296 int
drm_gem_close_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)297 drm_gem_close_ioctl(struct drm_device *dev, void *data,
298 struct drm_file *file_priv)
299 {
300 struct drm_gem_close *args = data;
301 int ret;
302
303 if (!(dev->driver->driver_features & DRIVER_GEM))
304 return -ENODEV;
305
306 ret = drm_gem_handle_delete(file_priv, args->handle);
307
308 return ret;
309 }
310
311 int
drm_gem_flink_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)312 drm_gem_flink_ioctl(struct drm_device *dev, void *data,
313 struct drm_file *file_priv)
314 {
315 struct drm_gem_flink *args = data;
316 struct drm_gem_object *obj;
317 int ret;
318
319 if (!(dev->driver->driver_features & DRIVER_GEM))
320 return -ENODEV;
321
322 obj = drm_gem_object_lookup(dev, file_priv, args->handle);
323 if (obj == NULL)
324 return -ENOENT;
325
326 ret = drm_gem_name_create(&dev->object_names, obj, &obj->name);
327 if (ret != 0) {
328 if (ret == -EALREADY)
329 ret = 0;
330 drm_gem_object_unreference_unlocked(obj);
331 }
332 if (ret == 0)
333 args->name = obj->name;
334 return ret;
335 }
336
337 int
drm_gem_open_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)338 drm_gem_open_ioctl(struct drm_device *dev, void *data,
339 struct drm_file *file_priv)
340 {
341 struct drm_gem_open *args = data;
342 struct drm_gem_object *obj;
343 int ret;
344 u32 handle;
345
346 if (!(dev->driver->driver_features & DRIVER_GEM))
347 return -ENODEV;
348
349 obj = drm_gem_name_ref(&dev->object_names, args->name,
350 (void (*)(void *))drm_gem_object_reference);
351 if (!obj)
352 return -ENOENT;
353
354 ret = drm_gem_handle_create(file_priv, obj, &handle);
355 drm_gem_object_unreference_unlocked(obj);
356 if (ret)
357 return ret;
358
359 args->handle = handle;
360 args->size = obj->size;
361
362 return 0;
363 }
364
365 void
drm_gem_open(struct drm_device * dev,struct drm_file * file_private)366 drm_gem_open(struct drm_device *dev, struct drm_file *file_private)
367 {
368
369 drm_gem_names_init(&file_private->object_names);
370 }
371
372 static int
drm_gem_object_release_handle(uint32_t name,void * ptr,void * data)373 drm_gem_object_release_handle(uint32_t name, void *ptr, void *data)
374 {
375 struct drm_file *file_priv = data;
376 struct drm_gem_object *obj = ptr;
377 struct drm_device *dev = obj->dev;
378
379 #if defined(FREEBSD_NOTYET)
380 drm_gem_remove_prime_handles(obj, file_priv);
381 #endif
382
383 if (dev->driver->gem_close_object)
384 dev->driver->gem_close_object(obj, file_priv);
385
386 drm_gem_object_handle_unreference_unlocked(obj);
387
388 return 0;
389 }
390
391 void
drm_gem_release(struct drm_device * dev,struct drm_file * file_private)392 drm_gem_release(struct drm_device *dev, struct drm_file *file_private)
393 {
394 drm_gem_names_foreach(&file_private->object_names,
395 drm_gem_object_release_handle, file_private);
396
397 drm_gem_names_fini(&file_private->object_names);
398 }
399
400 void
drm_gem_object_release(struct drm_gem_object * obj)401 drm_gem_object_release(struct drm_gem_object *obj)
402 {
403
404 /*
405 * obj->vm_obj can be NULL for private gem objects.
406 */
407 vm_object_deallocate(obj->vm_obj);
408 }
409 EXPORT_SYMBOL(drm_gem_object_release);
410
411 void
drm_gem_object_free(struct drm_gem_object * obj)412 drm_gem_object_free(struct drm_gem_object *obj)
413 {
414 struct drm_device *dev = obj->dev;
415
416 DRM_LOCK_ASSERT(dev);
417 if (dev->driver->gem_free_object != NULL)
418 dev->driver->gem_free_object(obj);
419 }
420 EXPORT_SYMBOL(drm_gem_object_free);
421
drm_gem_object_handle_free(struct drm_gem_object * obj)422 void drm_gem_object_handle_free(struct drm_gem_object *obj)
423 {
424 struct drm_device *dev = obj->dev;
425 struct drm_gem_object *obj1;
426
427 if (obj->name) {
428 obj1 = drm_gem_names_remove(&dev->object_names, obj->name);
429 obj->name = 0;
430 drm_gem_object_unreference(obj1);
431 }
432 }
433
434 static struct drm_gem_object *
drm_gem_object_from_offset(struct drm_device * dev,vm_ooffset_t offset)435 drm_gem_object_from_offset(struct drm_device *dev, vm_ooffset_t offset)
436 {
437 struct drm_gem_object *obj;
438 struct drm_gem_mm *mm;
439 struct drm_hash_item *map_list;
440
441 if ((offset & DRM_GEM_MAPPING_MASK) != DRM_GEM_MAPPING_KEY)
442 return (NULL);
443 offset &= ~DRM_GEM_MAPPING_KEY;
444 mm = dev->mm_private;
445 if (drm_ht_find_item(&mm->offset_hash, DRM_GEM_MAPPING_IDX(offset),
446 &map_list) != 0) {
447 DRM_DEBUG("drm_gem_object_from_offset: offset 0x%jx obj not found\n",
448 (uintmax_t)offset);
449 return (NULL);
450 }
451 obj = __containerof(map_list, struct drm_gem_object, map_list);
452 return (obj);
453 }
454
455 int
drm_gem_mmap_single(struct drm_device * dev,vm_ooffset_t * offset,vm_size_t size,struct vm_object ** obj_res,int nprot)456 drm_gem_mmap_single(struct drm_device *dev, vm_ooffset_t *offset, vm_size_t size,
457 struct vm_object **obj_res, int nprot)
458 {
459 struct drm_gem_object *gem_obj;
460 struct vm_object *vm_obj;
461
462 DRM_LOCK(dev);
463 gem_obj = drm_gem_object_from_offset(dev, *offset);
464 if (gem_obj == NULL) {
465 DRM_UNLOCK(dev);
466 return (-ENODEV);
467 }
468 drm_gem_object_reference(gem_obj);
469 DRM_UNLOCK(dev);
470 vm_obj = cdev_pager_allocate(gem_obj, OBJT_MGTDEVICE,
471 dev->driver->gem_pager_ops, size, nprot,
472 DRM_GEM_MAPPING_MAPOFF(*offset), curthread->td_ucred);
473 if (vm_obj == NULL) {
474 drm_gem_object_unreference_unlocked(gem_obj);
475 return (-EINVAL);
476 }
477 *offset = DRM_GEM_MAPPING_MAPOFF(*offset);
478 *obj_res = vm_obj;
479 return (0);
480 }
481
482 void
drm_gem_pager_dtr(void * handle)483 drm_gem_pager_dtr(void *handle)
484 {
485 struct drm_gem_object *obj;
486 struct drm_device *dev;
487
488 obj = handle;
489 dev = obj->dev;
490
491 DRM_LOCK(dev);
492 drm_gem_free_mmap_offset(obj);
493 drm_gem_object_unreference(obj);
494 DRM_UNLOCK(dev);
495 }
496