111891Speter/*- 29Sjkh * Copyright (c) 2011 The FreeBSD Foundation 311891Speter * All rights reserved. 49Sjkh * 59Sjkh * This software was developed by Konstantin Belousov under sponsorship from 69Sjkh * the FreeBSD Foundation. 79Sjkh * 89Sjkh * Redistribution and use in source and binary forms, with or without 99Sjkh * modification, are permitted provided that the following conditions 109Sjkh * are met: 119Sjkh * 1. Redistributions of source code must retain the above copyright 129Sjkh * notice, this list of conditions and the following disclaimer. 139Sjkh * 2. Redistributions in binary form must reproduce the above copyright 149Sjkh * notice, this list of conditions and the following disclaimer in the 159Sjkh * documentation and/or other materials provided with the distribution. 169Sjkh * 179Sjkh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 189Sjkh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1911891Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2011891Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2111891Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 229Sjkh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 239Sjkh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 249Sjkh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 259Sjkh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 269Sjkh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 279Sjkh * SUCH DAMAGE. 289Sjkh */ 299Sjkh 309Sjkh#include <sys/cdefs.h> 319Sjkh__FBSDID("$FreeBSD: releng/10.3/sys/dev/drm2/drm_gem.c 282199 2015-04-28 19:35:05Z dumbbell $"); 329Sjkh 339Sjkh#include "opt_vm.h" 349Sjkh 359Sjkh#include <sys/param.h> 369Sjkh#include <sys/systm.h> 379Sjkh#include <sys/limits.h> 389Sjkh#include <sys/lock.h> 399Sjkh#include <sys/mutex.h> 409Sjkh 4150472Speter#include <vm/vm.h> 429Sjkh#include <vm/vm_page.h> 439Sjkh 4411891Speter#include <dev/drm2/drmP.h> 459Sjkh#include <dev/drm2/drm.h> 469Sjkh#include <dev/drm2/drm_sarea.h> 479Sjkh 489Sjkh/* 499Sjkh * We make up offsets for buffer objects so we can recognize them at 5011891Speter * mmap time. 5111891Speter */ 529Sjkh 539Sjkh/* pgoff in mmap is an unsigned long, so we need to make sure that 549Sjkh * the faked up offset will fit 559Sjkh */ 569Sjkh 579Sjkh#if BITS_PER_LONG == 64 589Sjkh#define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFFUL >> PAGE_SHIFT) + 1) 5911891Speter#define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFFUL >> PAGE_SHIFT) * 16) 609Sjkh#else 619Sjkh#define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFUL >> PAGE_SHIFT) + 1) 629Sjkh#define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFUL >> PAGE_SHIFT) * 16) 6311891Speter#endif 649Sjkh 659Sjkh/** 669Sjkh * Initialize the GEM device fields 679Sjkh */ 6811891Speter 699Sjkhint 709Sjkhdrm_gem_init(struct drm_device *dev) 719Sjkh{ 729Sjkh struct drm_gem_mm *mm; 739Sjkh 7411891Speter drm_gem_names_init(&dev->object_names); 759Sjkh 769Sjkh mm = malloc(sizeof(*mm), DRM_MEM_DRIVER, M_NOWAIT); 779Sjkh if (!mm) { 7811891Speter DRM_ERROR("out of memory\n"); 799Sjkh return -ENOMEM; 809Sjkh } 819Sjkh 829Sjkh dev->mm_private = mm; 839Sjkh 849Sjkh if (drm_ht_create(&mm->offset_hash, 19)) { 859Sjkh free(mm, DRM_MEM_DRIVER); 869Sjkh return -ENOMEM; 879Sjkh } 889Sjkh 899Sjkh mm->idxunr = new_unrhdr(0, DRM_GEM_MAX_IDX, NULL); 909Sjkh 919Sjkh return 0; 929Sjkh} 939Sjkh 949Sjkhvoid 959Sjkhdrm_gem_destroy(struct drm_device *dev) 969Sjkh{ 979Sjkh struct drm_gem_mm *mm = dev->mm_private; 989Sjkh 999Sjkh dev->mm_private = NULL; 1009Sjkh drm_ht_remove(&mm->offset_hash); 1019Sjkh delete_unrhdr(mm->idxunr); 1029Sjkh free(mm, DRM_MEM_DRIVER); 1039Sjkh drm_gem_names_fini(&dev->object_names); 10411891Speter} 10511891Speter 10611891Speterint drm_gem_object_init(struct drm_device *dev, 10711891Speter struct drm_gem_object *obj, size_t size) 10811891Speter{ 10911891Speter KASSERT((size & (PAGE_SIZE - 1)) == 0, 1109Sjkh ("Bad size %ju", (uintmax_t)size)); 1119Sjkh 1129Sjkh obj->dev = dev; 1139Sjkh obj->vm_obj = vm_pager_allocate(OBJT_DEFAULT, NULL, size, 1149Sjkh VM_PROT_READ | VM_PROT_WRITE, 0, curthread->td_ucred); 1159Sjkh 1169Sjkh obj->refcount = 1; 1179Sjkh obj->handle_count = 0; 1189Sjkh obj->size = size; 1199Sjkh 1209Sjkh return 0; 1219Sjkh} 12211891SpeterEXPORT_SYMBOL(drm_gem_object_init); 12311891Speter 12411891Speter/** 12511891Speter * Initialize an already allocated GEM object of the specified size with 1269Sjkh * no GEM provided backing store. Instead the caller is responsible for 1279Sjkh * backing the object and handling it. 12811891Speter */ 1299Sjkhint drm_gem_private_object_init(struct drm_device *dev, 1309Sjkh struct drm_gem_object *obj, size_t size) 1319Sjkh{ 13211891Speter MPASS((size & (PAGE_SIZE - 1)) == 0); 13311891Speter 13411891Speter obj->dev = dev; 13511891Speter obj->vm_obj = NULL; 13611891Speter 13711891Speter obj->refcount = 1; 13811891Speter atomic_store_rel_int(&obj->handle_count, 0); 1399Sjkh obj->size = size; 1409Sjkh 1419Sjkh return 0; 14211891Speter} 1439SjkhEXPORT_SYMBOL(drm_gem_private_object_init); 14411891Speter 1459Sjkhstruct drm_gem_object * 1469Sjkhdrm_gem_object_alloc(struct drm_device *dev, size_t size) 14711891Speter{ 1489Sjkh struct drm_gem_object *obj; 1499Sjkh 1509Sjkh obj = malloc(sizeof(*obj), DRM_MEM_DRIVER, M_NOWAIT | M_ZERO); 15111891Speter if (!obj) 15211891Speter goto free; 15311891Speter 15411891Speter if (drm_gem_object_init(dev, obj, size) != 0) 15511891Speter goto free; 15611891Speter 15711891Speter if (dev->driver->gem_init_object != NULL && 1589Sjkh dev->driver->gem_init_object(obj) != 0) { 1599Sjkh goto dealloc; 1609Sjkh } 1619Sjkh return obj; 1629Sjkhdealloc: 1639Sjkh vm_object_deallocate(obj->vm_obj); 1649Sjkhfree: 1659Sjkh free(obj, DRM_MEM_DRIVER); 1669Sjkh return NULL; 1679Sjkh} 1689SjkhEXPORT_SYMBOL(drm_gem_object_alloc); 1699Sjkh 1709Sjkh#if defined(FREEBSD_NOTYET) 1719Sjkhstatic void 1729Sjkhdrm_gem_remove_prime_handles(struct drm_gem_object *obj, struct drm_file *filp) 1739Sjkh{ 1749Sjkh if (obj->import_attach) { 1759Sjkh drm_prime_remove_buf_handle(&filp->prime, 1769Sjkh obj->import_attach->dmabuf); 1779Sjkh } 1789Sjkh if (obj->export_dma_buf) { 1799Sjkh drm_prime_remove_buf_handle(&filp->prime, 1809Sjkh obj->export_dma_buf); 1819Sjkh } 1829Sjkh} 1839Sjkh#endif 1849Sjkh 1859Sjkh/** 1869Sjkh * Removes the mapping from handle to filp for this object. 18711891Speter */ 1889Sjkhint 1899Sjkhdrm_gem_handle_delete(struct drm_file *filp, u32 handle) 19011891Speter{ 19111891Speter struct drm_device *dev; 19211891Speter struct drm_gem_object *obj; 1939Sjkh 1949Sjkh obj = drm_gem_names_remove(&filp->object_names, handle); 1959Sjkh if (obj == NULL) { 1969Sjkh return -EINVAL; 1979Sjkh } 1989Sjkh dev = obj->dev; 1999Sjkh 2009Sjkh#if defined(FREEBSD_NOTYET) 2019Sjkh drm_gem_remove_prime_handles(obj, filp); 2029Sjkh#endif 2039Sjkh 2049Sjkh if (dev->driver->gem_close_object) 20511891Speter dev->driver->gem_close_object(obj, filp); 2069Sjkh drm_gem_object_handle_unreference_unlocked(obj); 20711891Speter 20811891Speter return 0; 20911891Speter} 2109SjkhEXPORT_SYMBOL(drm_gem_handle_delete); 2119Sjkh 2129Sjkh/** 2139Sjkh * Create a handle for this object. This adds a handle reference 2149Sjkh * to the object, which includes a regular reference count. Callers 21511891Speter * will likely want to dereference the object afterwards. 2169Sjkh */ 21711891Speterint 21811891Speterdrm_gem_handle_create(struct drm_file *file_priv, 21911891Speter struct drm_gem_object *obj, 22011891Speter u32 *handlep) 22111891Speter{ 22211891Speter struct drm_device *dev = obj->dev; 22311891Speter int ret; 2249Sjkh 2259Sjkh *handlep = 0; 22611891Speter ret = drm_gem_name_create(&file_priv->object_names, obj, handlep); 2279Sjkh if (ret != 0) 22811891Speter return ret; 22911891Speter 2309Sjkh drm_gem_object_handle_reference(obj); 23111891Speter 2329Sjkh if (dev->driver->gem_open_object) { 2339Sjkh ret = dev->driver->gem_open_object(obj, file_priv); 2349Sjkh if (ret) { 2359Sjkh drm_gem_handle_delete(file_priv, *handlep); 2369Sjkh return ret; 2379Sjkh } 2389Sjkh } 2399Sjkh 2409Sjkh return 0; 2419Sjkh} 2429SjkhEXPORT_SYMBOL(drm_gem_handle_create); 2439Sjkh 2449Sjkhvoid 2459Sjkhdrm_gem_free_mmap_offset(struct drm_gem_object *obj) 24611891Speter{ 2479Sjkh struct drm_device *dev = obj->dev; 2489Sjkh struct drm_gem_mm *mm = dev->mm_private; 2499Sjkh struct drm_hash_item *list = &obj->map_list; 25011891Speter 25111891Speter if (!obj->on_map) 2529Sjkh return; 25311891Speter 2549Sjkh drm_ht_remove_item(&mm->offset_hash, list); 2559Sjkh free_unr(mm->idxunr, list->key); 25611891Speter obj->on_map = false; 2579Sjkh} 2589SjkhEXPORT_SYMBOL(drm_gem_free_mmap_offset); 2599Sjkh 2609Sjkhint 2619Sjkhdrm_gem_create_mmap_offset(struct drm_gem_object *obj) 2629Sjkh{ 2639Sjkh struct drm_device *dev = obj->dev; 2649Sjkh struct drm_gem_mm *mm = dev->mm_private; 2659Sjkh int ret; 26611891Speter 2679Sjkh if (obj->on_map) 2689Sjkh return 0; 2699Sjkh 2709Sjkh obj->map_list.key = alloc_unr(mm->idxunr); 2719Sjkh ret = drm_ht_insert_item(&mm->offset_hash, &obj->map_list); 2729Sjkh if (ret) { 2739Sjkh DRM_ERROR("failed to add to map hash\n"); 2749Sjkh free_unr(mm->idxunr, obj->map_list.key); 2759Sjkh return ret; 2769Sjkh } 2779Sjkh obj->on_map = true; 2789Sjkh 2799Sjkh return 0; 2809Sjkh} 2819SjkhEXPORT_SYMBOL(drm_gem_create_mmap_offset); 2829Sjkh 2839Sjkh/** Returns a reference to the object named by the handle. */ 2849Sjkhstruct drm_gem_object * 2859Sjkhdrm_gem_object_lookup(struct drm_device *dev, struct drm_file *filp, 2869Sjkh u32 handle) 2879Sjkh{ 2889Sjkh struct drm_gem_object *obj; 2899Sjkh 2909Sjkh obj = drm_gem_name_ref(&filp->object_names, handle, 2919Sjkh (void (*)(void *))drm_gem_object_reference); 2929Sjkh 2939Sjkh return obj; 2949Sjkh} 2959SjkhEXPORT_SYMBOL(drm_gem_object_lookup); 2969Sjkh 2979Sjkhint 2989Sjkhdrm_gem_close_ioctl(struct drm_device *dev, void *data, 2999Sjkh struct drm_file *file_priv) 3009Sjkh{ 3019Sjkh struct drm_gem_close *args = data; 3029Sjkh int ret; 30311891Speter 3049Sjkh if (!(dev->driver->driver_features & DRIVER_GEM)) 3059Sjkh return -ENODEV; 3069Sjkh 3079Sjkh ret = drm_gem_handle_delete(file_priv, args->handle); 3089Sjkh 3099Sjkh return ret; 3109Sjkh} 3119Sjkh 3129Sjkhint 3139Sjkhdrm_gem_flink_ioctl(struct drm_device *dev, void *data, 3149Sjkh struct drm_file *file_priv) 31511891Speter{ 31611891Speter struct drm_gem_flink *args = data; 31711891Speter struct drm_gem_object *obj; 31811891Speter int ret; 31911891Speter 32011891Speter if (!(dev->driver->driver_features & DRIVER_GEM)) 3219Sjkh return -ENODEV; 3229Sjkh 3239Sjkh obj = drm_gem_object_lookup(dev, file_priv, args->handle); 3249Sjkh if (obj == NULL) 3259Sjkh return -ENOENT; 3269Sjkh 3279Sjkh ret = drm_gem_name_create(&dev->object_names, obj, &obj->name); 3289Sjkh if (ret != 0) { 3299Sjkh if (ret == -EALREADY) 3309Sjkh ret = 0; 3319Sjkh drm_gem_object_unreference_unlocked(obj); 3329Sjkh } 3339Sjkh if (ret == 0) 334 args->name = obj->name; 335 return ret; 336} 337 338int 339drm_gem_open_ioctl(struct drm_device *dev, void *data, 340 struct drm_file *file_priv) 341{ 342 struct drm_gem_open *args = data; 343 struct drm_gem_object *obj; 344 int ret; 345 u32 handle; 346 347 if (!(dev->driver->driver_features & DRIVER_GEM)) 348 return -ENODEV; 349 350 obj = drm_gem_name_ref(&dev->object_names, args->name, 351 (void (*)(void *))drm_gem_object_reference); 352 if (!obj) 353 return -ENOENT; 354 355 ret = drm_gem_handle_create(file_priv, obj, &handle); 356 drm_gem_object_unreference_unlocked(obj); 357 if (ret) 358 return ret; 359 360 args->handle = handle; 361 args->size = obj->size; 362 363 return 0; 364} 365 366void 367drm_gem_open(struct drm_device *dev, struct drm_file *file_private) 368{ 369 370 drm_gem_names_init(&file_private->object_names); 371} 372 373static int 374drm_gem_object_release_handle(uint32_t name, void *ptr, void *data) 375{ 376 struct drm_file *file_priv = data; 377 struct drm_gem_object *obj = ptr; 378 struct drm_device *dev = obj->dev; 379 380#if defined(FREEBSD_NOTYET) 381 drm_gem_remove_prime_handles(obj, file_priv); 382#endif 383 384 if (dev->driver->gem_close_object) 385 dev->driver->gem_close_object(obj, file_priv); 386 387 drm_gem_object_handle_unreference_unlocked(obj); 388 389 return 0; 390} 391 392void 393drm_gem_release(struct drm_device *dev, struct drm_file *file_private) 394{ 395 drm_gem_names_foreach(&file_private->object_names, 396 drm_gem_object_release_handle, file_private); 397 398 drm_gem_names_fini(&file_private->object_names); 399} 400 401void 402drm_gem_object_release(struct drm_gem_object *obj) 403{ 404 405 /* 406 * obj->vm_obj can be NULL for private gem objects. 407 */ 408 vm_object_deallocate(obj->vm_obj); 409} 410EXPORT_SYMBOL(drm_gem_object_release); 411 412void 413drm_gem_object_free(struct drm_gem_object *obj) 414{ 415 struct drm_device *dev = obj->dev; 416 417 DRM_LOCK_ASSERT(dev); 418 if (dev->driver->gem_free_object != NULL) 419 dev->driver->gem_free_object(obj); 420} 421EXPORT_SYMBOL(drm_gem_object_free); 422 423void drm_gem_object_handle_free(struct drm_gem_object *obj) 424{ 425 struct drm_device *dev = obj->dev; 426 struct drm_gem_object *obj1; 427 428 if (obj->name) { 429 obj1 = drm_gem_names_remove(&dev->object_names, obj->name); 430 obj->name = 0; 431 drm_gem_object_unreference(obj1); 432 } 433} 434 435static struct drm_gem_object * 436drm_gem_object_from_offset(struct drm_device *dev, vm_ooffset_t offset) 437{ 438 struct drm_gem_object *obj; 439 struct drm_gem_mm *mm; 440 struct drm_hash_item *map_list; 441 442 if ((offset & DRM_GEM_MAPPING_MASK) != DRM_GEM_MAPPING_KEY) 443 return (NULL); 444 offset &= ~DRM_GEM_MAPPING_KEY; 445 mm = dev->mm_private; 446 if (drm_ht_find_item(&mm->offset_hash, DRM_GEM_MAPPING_IDX(offset), 447 &map_list) != 0) { 448 DRM_DEBUG("drm_gem_object_from_offset: offset 0x%jx obj not found\n", 449 (uintmax_t)offset); 450 return (NULL); 451 } 452 obj = __containerof(map_list, struct drm_gem_object, map_list); 453 return (obj); 454} 455 456int 457drm_gem_mmap_single(struct drm_device *dev, vm_ooffset_t *offset, vm_size_t size, 458 struct vm_object **obj_res, int nprot) 459{ 460 struct drm_gem_object *gem_obj; 461 struct vm_object *vm_obj; 462 463 DRM_LOCK(dev); 464 gem_obj = drm_gem_object_from_offset(dev, *offset); 465 if (gem_obj == NULL) { 466 DRM_UNLOCK(dev); 467 return (-ENODEV); 468 } 469 drm_gem_object_reference(gem_obj); 470 DRM_UNLOCK(dev); 471 vm_obj = cdev_pager_allocate(gem_obj, OBJT_MGTDEVICE, 472 dev->driver->gem_pager_ops, size, nprot, 473 DRM_GEM_MAPPING_MAPOFF(*offset), curthread->td_ucred); 474 if (vm_obj == NULL) { 475 drm_gem_object_unreference_unlocked(gem_obj); 476 return (-EINVAL); 477 } 478 *offset = DRM_GEM_MAPPING_MAPOFF(*offset); 479 *obj_res = vm_obj; 480 return (0); 481} 482 483void 484drm_gem_pager_dtr(void *handle) 485{ 486 struct drm_gem_object *obj; 487 struct drm_device *dev; 488 489 obj = handle; 490 dev = obj->dev; 491 492 DRM_LOCK(dev); 493 drm_gem_free_mmap_offset(obj); 494 drm_gem_object_unreference(obj); 495 DRM_UNLOCK(dev); 496} 497