1235783Skib/*-
2235783Skib * Copyright (c) 2011 The FreeBSD Foundation
3235783Skib * All rights reserved.
4235783Skib *
5235783Skib * This software was developed by Konstantin Belousov under sponsorship from
6235783Skib * the FreeBSD Foundation.
7235783Skib *
8235783Skib * Redistribution and use in source and binary forms, with or without
9235783Skib * modification, are permitted provided that the following conditions
10235783Skib * are met:
11235783Skib * 1. Redistributions of source code must retain the above copyright
12235783Skib *    notice, this list of conditions and the following disclaimer.
13235783Skib * 2. Redistributions in binary form must reproduce the above copyright
14235783Skib *    notice, this list of conditions and the following disclaimer in the
15235783Skib *    documentation and/or other materials provided with the distribution.
16235783Skib *
17235783Skib * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18235783Skib * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19235783Skib * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20235783Skib * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21235783Skib * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22235783Skib * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23235783Skib * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24235783Skib * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25235783Skib * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26235783Skib * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27235783Skib * SUCH DAMAGE.
28235783Skib */
29235783Skib
30235783Skib#include <sys/cdefs.h>
31235783Skib__FBSDID("$FreeBSD$");
32235783Skib
33235783Skib#include "opt_vm.h"
34235783Skib
35235783Skib#include <sys/param.h>
36235783Skib#include <sys/systm.h>
37235783Skib#include <sys/limits.h>
38235783Skib#include <sys/lock.h>
39235783Skib#include <sys/mutex.h>
40235783Skib
41235783Skib#include <vm/vm.h>
42235783Skib#include <vm/vm_page.h>
43235783Skib
44235783Skib#include <dev/drm2/drmP.h>
45235783Skib#include <dev/drm2/drm.h>
46235783Skib#include <dev/drm2/drm_sarea.h>
47235783Skib
48235783Skib/*
49235783Skib * We make up offsets for buffer objects so we can recognize them at
50235783Skib * mmap time.
51235783Skib */
52235783Skib
53235783Skib/* pgoff in mmap is an unsigned long, so we need to make sure that
54235783Skib * the faked up offset will fit
55235783Skib */
56235783Skib
57235783Skib#if ULONG_MAX == UINT64_MAX
58235783Skib#define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFFUL >> PAGE_SHIFT) + 1)
59235783Skib#define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFFUL >> PAGE_SHIFT) * 16)
60235783Skib#else
61235783Skib#define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFUL >> PAGE_SHIFT) + 1)
62235783Skib#define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFUL >> PAGE_SHIFT) * 16)
63235783Skib#endif
64235783Skib
65235783Skibint
66235783Skibdrm_gem_init(struct drm_device *dev)
67235783Skib{
68235783Skib	struct drm_gem_mm *mm;
69235783Skib
70235783Skib	drm_gem_names_init(&dev->object_names);
71235783Skib	mm = malloc(sizeof(*mm), DRM_MEM_DRIVER, M_WAITOK);
72235783Skib	dev->mm_private = mm;
73235783Skib	if (drm_ht_create(&mm->offset_hash, 19) != 0) {
74235783Skib		free(mm, DRM_MEM_DRIVER);
75235783Skib		return (ENOMEM);
76235783Skib	}
77235783Skib	mm->idxunr = new_unrhdr(0, DRM_GEM_MAX_IDX, NULL);
78235783Skib	return (0);
79235783Skib}
80235783Skib
81235783Skibvoid
82235783Skibdrm_gem_destroy(struct drm_device *dev)
83235783Skib{
84235783Skib	struct drm_gem_mm *mm;
85235783Skib
86235783Skib	mm = dev->mm_private;
87235783Skib	dev->mm_private = NULL;
88235783Skib	drm_ht_remove(&mm->offset_hash);
89235783Skib	delete_unrhdr(mm->idxunr);
90235783Skib	free(mm, DRM_MEM_DRIVER);
91235783Skib	drm_gem_names_fini(&dev->object_names);
92235783Skib}
93235783Skib
94235783Skibint
95235783Skibdrm_gem_object_init(struct drm_device *dev, struct drm_gem_object *obj,
96235783Skib    size_t size)
97235783Skib{
98235783Skib
99235783Skib	KASSERT((size & (PAGE_SIZE - 1)) == 0,
100235783Skib	    ("Bad size %ju", (uintmax_t)size));
101235783Skib
102235783Skib	obj->dev = dev;
103235783Skib	obj->vm_obj = vm_pager_allocate(OBJT_DEFAULT, NULL, size,
104235783Skib	    VM_PROT_READ | VM_PROT_WRITE, 0, curthread->td_ucred);
105235783Skib
106235783Skib	obj->refcount = 1;
107235783Skib	obj->handle_count = 0;
108235783Skib	obj->size = size;
109235783Skib
110235783Skib	return (0);
111235783Skib}
112235783Skib
113235783Skibint
114235783Skibdrm_gem_private_object_init(struct drm_device *dev, struct drm_gem_object *obj,
115235783Skib    size_t size)
116235783Skib{
117235783Skib
118235783Skib	MPASS((size & (PAGE_SIZE - 1)) == 0);
119235783Skib
120235783Skib	obj->dev = dev;
121235783Skib	obj->vm_obj = NULL;
122235783Skib
123235783Skib	obj->refcount = 1;
124254880Sdumbbell	atomic_store_rel_int(&obj->handle_count, 0);
125235783Skib	obj->size = size;
126235783Skib
127235783Skib	return (0);
128235783Skib}
129235783Skib
130235783Skib
131235783Skibstruct drm_gem_object *
132235783Skibdrm_gem_object_alloc(struct drm_device *dev, size_t size)
133235783Skib{
134235783Skib	struct drm_gem_object *obj;
135235783Skib
136235783Skib	obj = malloc(sizeof(*obj), DRM_MEM_DRIVER, M_WAITOK | M_ZERO);
137235783Skib	if (drm_gem_object_init(dev, obj, size) != 0)
138235783Skib		goto free;
139235783Skib
140235783Skib	if (dev->driver->gem_init_object != NULL &&
141235783Skib	    dev->driver->gem_init_object(obj) != 0)
142235783Skib		goto dealloc;
143235783Skib	return (obj);
144235783Skibdealloc:
145235783Skib	vm_object_deallocate(obj->vm_obj);
146235783Skibfree:
147235783Skib	free(obj, DRM_MEM_DRIVER);
148235783Skib	return (NULL);
149235783Skib}
150235783Skib
151235783Skibvoid
152235783Skibdrm_gem_object_free(struct drm_gem_object *obj)
153235783Skib{
154235783Skib	struct drm_device *dev;
155235783Skib
156235783Skib	dev = obj->dev;
157235783Skib	DRM_LOCK_ASSERT(dev);
158235783Skib	if (dev->driver->gem_free_object != NULL)
159235783Skib		dev->driver->gem_free_object(obj);
160235783Skib}
161235783Skib
162235783Skibvoid
163235783Skibdrm_gem_object_reference(struct drm_gem_object *obj)
164235783Skib{
165235783Skib
166254835Sdumbbell	KASSERT(obj->refcount > 0, ("Dangling obj %p", obj));
167235783Skib	refcount_acquire(&obj->refcount);
168235783Skib}
169235783Skib
170235783Skibvoid
171235783Skibdrm_gem_object_unreference(struct drm_gem_object *obj)
172235783Skib{
173235783Skib
174235783Skib	if (obj == NULL)
175235783Skib		return;
176235783Skib	if (refcount_release(&obj->refcount))
177235783Skib		drm_gem_object_free(obj);
178235783Skib}
179235783Skib
180235783Skibvoid
181235783Skibdrm_gem_object_unreference_unlocked(struct drm_gem_object *obj)
182235783Skib{
183235783Skib	struct drm_device *dev;
184235783Skib
185235783Skib	if (obj == NULL)
186235783Skib		return;
187235783Skib	dev = obj->dev;
188235783Skib	DRM_LOCK(dev);
189235783Skib	drm_gem_object_unreference(obj);
190235783Skib	DRM_UNLOCK(dev);
191235783Skib}
192235783Skib
193235783Skibvoid
194235783Skibdrm_gem_object_handle_reference(struct drm_gem_object *obj)
195235783Skib{
196235783Skib
197235783Skib	drm_gem_object_reference(obj);
198235783Skib	atomic_add_rel_int(&obj->handle_count, 1);
199235783Skib}
200235783Skib
201235783Skibvoid
202235783Skibdrm_gem_object_handle_free(struct drm_gem_object *obj)
203235783Skib{
204235783Skib	struct drm_device *dev;
205235783Skib	struct drm_gem_object *obj1;
206235783Skib
207235783Skib	dev = obj->dev;
208235783Skib	if (obj->name != 0) {
209235783Skib		obj1 = drm_gem_names_remove(&dev->object_names, obj->name);
210235783Skib		obj->name = 0;
211235783Skib		drm_gem_object_unreference(obj1);
212235783Skib	}
213235783Skib}
214235783Skib
215235783Skibvoid
216235783Skibdrm_gem_object_handle_unreference(struct drm_gem_object *obj)
217235783Skib{
218235783Skib
219235783Skib	if (obj == NULL ||
220235783Skib	    atomic_load_acq_int(&obj->handle_count) == 0)
221235783Skib		return;
222235783Skib
223235783Skib	if (atomic_fetchadd_int(&obj->handle_count, -1) == 1)
224235783Skib		drm_gem_object_handle_free(obj);
225235783Skib	drm_gem_object_unreference(obj);
226235783Skib}
227235783Skib
228235783Skibvoid
229235783Skibdrm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj)
230235783Skib{
231235783Skib
232235783Skib	if (obj == NULL ||
233235783Skib	    atomic_load_acq_int(&obj->handle_count) == 0)
234235783Skib		return;
235235783Skib
236235783Skib	if (atomic_fetchadd_int(&obj->handle_count, -1) == 1)
237235783Skib		drm_gem_object_handle_free(obj);
238235783Skib	drm_gem_object_unreference_unlocked(obj);
239235783Skib}
240235783Skib
241235783Skibint
242235783Skibdrm_gem_handle_create(struct drm_file *file_priv, struct drm_gem_object *obj,
243235783Skib    uint32_t *handle)
244235783Skib{
245254836Sdumbbell	struct drm_device *dev = obj->dev;
246254836Sdumbbell	int ret;
247235783Skib
248254836Sdumbbell	ret = drm_gem_name_create(&file_priv->object_names, obj, handle);
249254836Sdumbbell	if (ret != 0)
250254836Sdumbbell		return (ret);
251235783Skib	drm_gem_object_handle_reference(obj);
252254836Sdumbbell
253254836Sdumbbell	if (dev->driver->gem_open_object) {
254254836Sdumbbell		ret = dev->driver->gem_open_object(obj, file_priv);
255254836Sdumbbell		if (ret) {
256254836Sdumbbell			drm_gem_handle_delete(file_priv, *handle);
257254836Sdumbbell			return ret;
258254836Sdumbbell		}
259254836Sdumbbell	}
260254836Sdumbbell
261235783Skib	return (0);
262235783Skib}
263235783Skib
264235783Skibint
265235783Skibdrm_gem_handle_delete(struct drm_file *file_priv, uint32_t handle)
266235783Skib{
267254836Sdumbbell	struct drm_device *dev;
268235783Skib	struct drm_gem_object *obj;
269235783Skib
270235783Skib	obj = drm_gem_names_remove(&file_priv->object_names, handle);
271235783Skib	if (obj == NULL)
272235783Skib		return (EINVAL);
273254836Sdumbbell
274254836Sdumbbell	dev = obj->dev;
275254836Sdumbbell	if (dev->driver->gem_close_object)
276254836Sdumbbell		dev->driver->gem_close_object(obj, file_priv);
277235783Skib	drm_gem_object_handle_unreference_unlocked(obj);
278254836Sdumbbell
279235783Skib	return (0);
280235783Skib}
281235783Skib
282235783Skibvoid
283235783Skibdrm_gem_object_release(struct drm_gem_object *obj)
284235783Skib{
285235783Skib
286235783Skib	/*
287235783Skib	 * obj->vm_obj can be NULL for private gem objects.
288235783Skib	 */
289235783Skib	vm_object_deallocate(obj->vm_obj);
290235783Skib}
291235783Skib
292235783Skibint
293235783Skibdrm_gem_open_ioctl(struct drm_device *dev, void *data,
294235783Skib    struct drm_file *file_priv)
295235783Skib{
296235783Skib	struct drm_gem_open *args;
297235783Skib	struct drm_gem_object *obj;
298235783Skib	int ret;
299235783Skib	uint32_t handle;
300235783Skib
301235783Skib	if (!drm_core_check_feature(dev, DRIVER_GEM))
302235783Skib		return (ENODEV);
303235783Skib	args = data;
304235783Skib
305235783Skib	obj = drm_gem_name_ref(&dev->object_names, args->name,
306235783Skib	    (void (*)(void *))drm_gem_object_reference);
307235783Skib	if (obj == NULL)
308235783Skib		return (ENOENT);
309235783Skib	handle = 0;
310235783Skib	ret = drm_gem_handle_create(file_priv, obj, &handle);
311235783Skib	drm_gem_object_unreference_unlocked(obj);
312235783Skib	if (ret != 0)
313235783Skib		return (ret);
314235783Skib
315235783Skib	args->handle = handle;
316235783Skib	args->size = obj->size;
317235783Skib
318235783Skib	return (0);
319235783Skib}
320235783Skib
321235783Skibvoid
322235783Skibdrm_gem_open(struct drm_device *dev, struct drm_file *file_priv)
323235783Skib{
324235783Skib
325235783Skib	drm_gem_names_init(&file_priv->object_names);
326235783Skib}
327235783Skib
328235783Skibstatic int
329235783Skibdrm_gem_object_release_handle(uint32_t name, void *ptr, void *arg)
330235783Skib{
331254837Sdumbbell	struct drm_file *file_priv;
332235783Skib	struct drm_gem_object *obj;
333254837Sdumbbell	struct drm_device *dev;
334235783Skib
335254837Sdumbbell	file_priv = arg;
336235783Skib	obj = ptr;
337254837Sdumbbell	dev = obj->dev;
338254837Sdumbbell
339254837Sdumbbell	if (dev->driver->gem_close_object)
340254837Sdumbbell		dev->driver->gem_close_object(obj, file_priv);
341254837Sdumbbell
342235783Skib	drm_gem_object_handle_unreference(obj);
343235783Skib	return (0);
344235783Skib}
345235783Skib
346235783Skibvoid
347235783Skibdrm_gem_release(struct drm_device *dev, struct drm_file *file_priv)
348235783Skib{
349235783Skib
350235783Skib	drm_gem_names_foreach(&file_priv->object_names,
351254837Sdumbbell	    drm_gem_object_release_handle, file_priv);
352235783Skib	drm_gem_names_fini(&file_priv->object_names);
353235783Skib}
354235783Skib
355235783Skibint
356235783Skibdrm_gem_close_ioctl(struct drm_device *dev, void *data,
357235783Skib    struct drm_file *file_priv)
358235783Skib{
359235783Skib	struct drm_gem_close *args;
360235783Skib
361235783Skib	if (!drm_core_check_feature(dev, DRIVER_GEM))
362235783Skib		return (ENODEV);
363235783Skib	args = data;
364235783Skib
365235783Skib	return (drm_gem_handle_delete(file_priv, args->handle));
366235783Skib}
367235783Skib
368235783Skibint
369235783Skibdrm_gem_flink_ioctl(struct drm_device *dev, void *data,
370235783Skib    struct drm_file *file_priv)
371235783Skib{
372235783Skib	struct drm_gem_flink *args;
373235783Skib	struct drm_gem_object *obj;
374235783Skib	int error;
375235783Skib
376235783Skib	if (!drm_core_check_feature(dev, DRIVER_GEM))
377235783Skib		return (ENODEV);
378235783Skib	args = data;
379235783Skib
380235783Skib	obj = drm_gem_name_ref(&file_priv->object_names, args->handle,
381235783Skib	    (void (*)(void *))drm_gem_object_reference);
382235783Skib	if (obj == NULL)
383235783Skib		return (ENOENT);
384235783Skib	error = drm_gem_name_create(&dev->object_names, obj, &obj->name);
385235783Skib	if (error != 0) {
386235783Skib		if (error == EALREADY)
387235783Skib			error = 0;
388235783Skib		drm_gem_object_unreference_unlocked(obj);
389235783Skib	}
390235783Skib	if (error == 0)
391235783Skib		args->name = obj->name;
392235783Skib	return (error);
393235783Skib}
394235783Skib
395235783Skibstruct drm_gem_object *
396235783Skibdrm_gem_object_lookup(struct drm_device *dev, struct drm_file *file_priv,
397235783Skib    uint32_t handle)
398235783Skib{
399235783Skib	struct drm_gem_object *obj;
400235783Skib
401235783Skib	obj = drm_gem_name_ref(&file_priv->object_names, handle,
402235783Skib	    (void (*)(void *))drm_gem_object_reference);
403235783Skib	return (obj);
404235783Skib}
405235783Skib
406235783Skibstatic struct drm_gem_object *
407235783Skibdrm_gem_object_from_offset(struct drm_device *dev, vm_ooffset_t offset)
408235783Skib{
409235783Skib	struct drm_gem_object *obj;
410235783Skib	struct drm_gem_mm *mm;
411235783Skib	struct drm_hash_item *map_list;
412235783Skib
413235783Skib	if ((offset & DRM_GEM_MAPPING_MASK) != DRM_GEM_MAPPING_KEY)
414235783Skib		return (NULL);
415235783Skib	offset &= ~DRM_GEM_MAPPING_KEY;
416235783Skib	mm = dev->mm_private;
417235783Skib	if (drm_ht_find_item(&mm->offset_hash, DRM_GEM_MAPPING_IDX(offset),
418235783Skib	    &map_list) != 0) {
419235783Skib	DRM_DEBUG("drm_gem_object_from_offset: offset 0x%jx obj not found\n",
420235783Skib		    (uintmax_t)offset);
421235783Skib		return (NULL);
422235783Skib	}
423240539Sed	obj = __containerof(map_list, struct drm_gem_object, map_list);
424235783Skib	return (obj);
425235783Skib}
426235783Skib
427235783Skibint
428235783Skibdrm_gem_create_mmap_offset(struct drm_gem_object *obj)
429235783Skib{
430235783Skib	struct drm_device *dev;
431235783Skib	struct drm_gem_mm *mm;
432235783Skib	int ret;
433235783Skib
434235783Skib	if (obj->on_map)
435235783Skib		return (0);
436235783Skib	dev = obj->dev;
437235783Skib	mm = dev->mm_private;
438235783Skib	ret = 0;
439235783Skib
440235783Skib	obj->map_list.key = alloc_unr(mm->idxunr);
441235783Skib	ret = drm_ht_insert_item(&mm->offset_hash, &obj->map_list);
442235783Skib	if (ret != 0) {
443235783Skib		DRM_ERROR("failed to add to map hash\n");
444235783Skib		free_unr(mm->idxunr, obj->map_list.key);
445235783Skib		return (ret);
446235783Skib	}
447235783Skib	obj->on_map = true;
448235783Skib	return (0);
449235783Skib}
450235783Skib
451235783Skibvoid
452235783Skibdrm_gem_free_mmap_offset(struct drm_gem_object *obj)
453235783Skib{
454235783Skib	struct drm_hash_item *list;
455235783Skib	struct drm_gem_mm *mm;
456235783Skib
457235783Skib	if (!obj->on_map)
458235783Skib		return;
459235783Skib	mm = obj->dev->mm_private;
460235783Skib	list = &obj->map_list;
461235783Skib
462235783Skib	drm_ht_remove_item(&mm->offset_hash, list);
463235783Skib	free_unr(mm->idxunr, list->key);
464235783Skib	obj->on_map = false;
465235783Skib}
466235783Skib
467235783Skibint
468247835Skibdrm_gem_mmap_single(struct drm_device *dev, vm_ooffset_t *offset, vm_size_t size,
469235783Skib    struct vm_object **obj_res, int nprot)
470235783Skib{
471235783Skib	struct drm_gem_object *gem_obj;
472235783Skib	struct vm_object *vm_obj;
473235783Skib
474235783Skib	DRM_LOCK(dev);
475235783Skib	gem_obj = drm_gem_object_from_offset(dev, *offset);
476235783Skib	if (gem_obj == NULL) {
477235783Skib		DRM_UNLOCK(dev);
478235783Skib		return (ENODEV);
479235783Skib	}
480235783Skib	drm_gem_object_reference(gem_obj);
481235783Skib	DRM_UNLOCK(dev);
482235783Skib	vm_obj = cdev_pager_allocate(gem_obj, OBJT_MGTDEVICE,
483235783Skib	    dev->driver->gem_pager_ops, size, nprot,
484235783Skib	    DRM_GEM_MAPPING_MAPOFF(*offset), curthread->td_ucred);
485235783Skib	if (vm_obj == NULL) {
486235783Skib		drm_gem_object_unreference_unlocked(gem_obj);
487235783Skib		return (EINVAL);
488235783Skib	}
489235783Skib	*offset = DRM_GEM_MAPPING_MAPOFF(*offset);
490235783Skib	*obj_res = vm_obj;
491235783Skib	return (0);
492235783Skib}
493235783Skib
494235783Skibvoid
495235783Skibdrm_gem_pager_dtr(void *handle)
496235783Skib{
497235783Skib	struct drm_gem_object *obj;
498235783Skib	struct drm_device *dev;
499235783Skib
500235783Skib	obj = handle;
501235783Skib	dev = obj->dev;
502235783Skib
503235783Skib	DRM_LOCK(dev);
504235783Skib	drm_gem_free_mmap_offset(obj);
505235783Skib	drm_gem_object_unreference(obj);
506235783Skib	DRM_UNLOCK(dev);
507235783Skib}
508