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