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