xref: /freebsd/sys/dev/drm2/drm_gem.c (revision 685dc743)
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