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