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