1/* 2 * This program is free software; you can redistribute it and/or modify 3 * it under the terms of the GNU General Public License as published by 4 * the Free Software Foundation; either version 2 of the License, or 5 * (at your option) any later version. 6 */ 7 8#include "bochs.h" 9 10static void bochs_ttm_placement(struct bochs_bo *bo, int domain); 11 12/* ---------------------------------------------------------------------- */ 13 14static inline struct bochs_device *bochs_bdev(struct ttm_bo_device *bd) 15{ 16 return container_of(bd, struct bochs_device, ttm.bdev); 17} 18 19static int bochs_ttm_mem_global_init(struct drm_global_reference *ref) 20{ 21 return ttm_mem_global_init(ref->object); 22} 23 24static void bochs_ttm_mem_global_release(struct drm_global_reference *ref) 25{ 26 ttm_mem_global_release(ref->object); 27} 28 29static int bochs_ttm_global_init(struct bochs_device *bochs) 30{ 31 struct drm_global_reference *global_ref; 32 int r; 33 34 global_ref = &bochs->ttm.mem_global_ref; 35 global_ref->global_type = DRM_GLOBAL_TTM_MEM; 36 global_ref->size = sizeof(struct ttm_mem_global); 37 global_ref->init = &bochs_ttm_mem_global_init; 38 global_ref->release = &bochs_ttm_mem_global_release; 39 r = drm_global_item_ref(global_ref); 40 if (r != 0) { 41 DRM_ERROR("Failed setting up TTM memory accounting " 42 "subsystem.\n"); 43 return r; 44 } 45 46 bochs->ttm.bo_global_ref.mem_glob = 47 bochs->ttm.mem_global_ref.object; 48 global_ref = &bochs->ttm.bo_global_ref.ref; 49 global_ref->global_type = DRM_GLOBAL_TTM_BO; 50 global_ref->size = sizeof(struct ttm_bo_global); 51 global_ref->init = &ttm_bo_global_init; 52 global_ref->release = &ttm_bo_global_release; 53 r = drm_global_item_ref(global_ref); 54 if (r != 0) { 55 DRM_ERROR("Failed setting up TTM BO subsystem.\n"); 56 drm_global_item_unref(&bochs->ttm.mem_global_ref); 57 return r; 58 } 59 60 return 0; 61} 62 63static void bochs_ttm_global_release(struct bochs_device *bochs) 64{ 65 if (bochs->ttm.mem_global_ref.release == NULL) 66 return; 67 68 drm_global_item_unref(&bochs->ttm.bo_global_ref.ref); 69 drm_global_item_unref(&bochs->ttm.mem_global_ref); 70 bochs->ttm.mem_global_ref.release = NULL; 71} 72 73 74static void bochs_bo_ttm_destroy(struct ttm_buffer_object *tbo) 75{ 76 struct bochs_bo *bo; 77 78 bo = container_of(tbo, struct bochs_bo, bo); 79 drm_gem_object_release(&bo->gem); 80 kfree(bo); 81} 82 83static bool bochs_ttm_bo_is_bochs_bo(struct ttm_buffer_object *bo) 84{ 85 if (bo->destroy == &bochs_bo_ttm_destroy) 86 return true; 87 return false; 88} 89 90static int bochs_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, 91 struct ttm_mem_type_manager *man) 92{ 93 switch (type) { 94 case TTM_PL_SYSTEM: 95 man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; 96 man->available_caching = TTM_PL_MASK_CACHING; 97 man->default_caching = TTM_PL_FLAG_CACHED; 98 break; 99 case TTM_PL_VRAM: 100 man->func = &ttm_bo_manager_func; 101 man->flags = TTM_MEMTYPE_FLAG_FIXED | 102 TTM_MEMTYPE_FLAG_MAPPABLE; 103 man->available_caching = TTM_PL_FLAG_UNCACHED | 104 TTM_PL_FLAG_WC; 105 man->default_caching = TTM_PL_FLAG_WC; 106 break; 107 default: 108 DRM_ERROR("Unsupported memory type %u\n", (unsigned)type); 109 return -EINVAL; 110 } 111 return 0; 112} 113 114static void 115bochs_bo_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *pl) 116{ 117 struct bochs_bo *bochsbo = bochs_bo(bo); 118 119 if (!bochs_ttm_bo_is_bochs_bo(bo)) 120 return; 121 122 bochs_ttm_placement(bochsbo, TTM_PL_FLAG_SYSTEM); 123 *pl = bochsbo->placement; 124} 125 126static int bochs_bo_verify_access(struct ttm_buffer_object *bo, 127 struct file *filp) 128{ 129 struct bochs_bo *bochsbo = bochs_bo(bo); 130 131 return drm_vma_node_verify_access(&bochsbo->gem.vma_node, filp); 132} 133 134static int bochs_ttm_io_mem_reserve(struct ttm_bo_device *bdev, 135 struct ttm_mem_reg *mem) 136{ 137 struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; 138 struct bochs_device *bochs = bochs_bdev(bdev); 139 140 mem->bus.addr = NULL; 141 mem->bus.offset = 0; 142 mem->bus.size = mem->num_pages << PAGE_SHIFT; 143 mem->bus.base = 0; 144 mem->bus.is_iomem = false; 145 if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) 146 return -EINVAL; 147 switch (mem->mem_type) { 148 case TTM_PL_SYSTEM: 149 /* system memory */ 150 return 0; 151 case TTM_PL_VRAM: 152 mem->bus.offset = mem->start << PAGE_SHIFT; 153 mem->bus.base = bochs->fb_base; 154 mem->bus.is_iomem = true; 155 break; 156 default: 157 return -EINVAL; 158 break; 159 } 160 return 0; 161} 162 163static void bochs_ttm_io_mem_free(struct ttm_bo_device *bdev, 164 struct ttm_mem_reg *mem) 165{ 166} 167 168static int bochs_bo_move(struct ttm_buffer_object *bo, 169 bool evict, bool interruptible, 170 bool no_wait_gpu, 171 struct ttm_mem_reg *new_mem) 172{ 173 return ttm_bo_move_memcpy(bo, evict, no_wait_gpu, new_mem); 174} 175 176 177static void bochs_ttm_backend_destroy(struct ttm_tt *tt) 178{ 179 ttm_tt_fini(tt); 180 kfree(tt); 181} 182 183static struct ttm_backend_func bochs_tt_backend_func = { 184 .destroy = &bochs_ttm_backend_destroy, 185}; 186 187static struct ttm_tt *bochs_ttm_tt_create(struct ttm_bo_device *bdev, 188 unsigned long size, 189 uint32_t page_flags, 190 struct page *dummy_read_page) 191{ 192 struct ttm_tt *tt; 193 194 tt = kzalloc(sizeof(struct ttm_tt), GFP_KERNEL); 195 if (tt == NULL) 196 return NULL; 197 tt->func = &bochs_tt_backend_func; 198 if (ttm_tt_init(tt, bdev, size, page_flags, dummy_read_page)) { 199 kfree(tt); 200 return NULL; 201 } 202 return tt; 203} 204 205struct ttm_bo_driver bochs_bo_driver = { 206 .ttm_tt_create = bochs_ttm_tt_create, 207 .ttm_tt_populate = ttm_pool_populate, 208 .ttm_tt_unpopulate = ttm_pool_unpopulate, 209 .init_mem_type = bochs_bo_init_mem_type, 210 .evict_flags = bochs_bo_evict_flags, 211 .move = bochs_bo_move, 212 .verify_access = bochs_bo_verify_access, 213 .io_mem_reserve = &bochs_ttm_io_mem_reserve, 214 .io_mem_free = &bochs_ttm_io_mem_free, 215}; 216 217int bochs_mm_init(struct bochs_device *bochs) 218{ 219 struct ttm_bo_device *bdev = &bochs->ttm.bdev; 220 int ret; 221 222 ret = bochs_ttm_global_init(bochs); 223 if (ret) 224 return ret; 225 226 ret = ttm_bo_device_init(&bochs->ttm.bdev, 227 bochs->ttm.bo_global_ref.ref.object, 228 &bochs_bo_driver, 229 bochs->dev->anon_inode->i_mapping, 230 DRM_FILE_PAGE_OFFSET, 231 true); 232 if (ret) { 233 DRM_ERROR("Error initialising bo driver; %d\n", ret); 234 return ret; 235 } 236 237 ret = ttm_bo_init_mm(bdev, TTM_PL_VRAM, 238 bochs->fb_size >> PAGE_SHIFT); 239 if (ret) { 240 DRM_ERROR("Failed ttm VRAM init: %d\n", ret); 241 return ret; 242 } 243 244 bochs->ttm.initialized = true; 245 return 0; 246} 247 248void bochs_mm_fini(struct bochs_device *bochs) 249{ 250 if (!bochs->ttm.initialized) 251 return; 252 253 ttm_bo_device_release(&bochs->ttm.bdev); 254 bochs_ttm_global_release(bochs); 255 bochs->ttm.initialized = false; 256} 257 258static void bochs_ttm_placement(struct bochs_bo *bo, int domain) 259{ 260 unsigned i; 261 u32 c = 0; 262 bo->placement.placement = bo->placements; 263 bo->placement.busy_placement = bo->placements; 264 if (domain & TTM_PL_FLAG_VRAM) { 265 bo->placements[c++].flags = TTM_PL_FLAG_WC 266 | TTM_PL_FLAG_UNCACHED 267 | TTM_PL_FLAG_VRAM; 268 } 269 if (domain & TTM_PL_FLAG_SYSTEM) { 270 bo->placements[c++].flags = TTM_PL_MASK_CACHING 271 | TTM_PL_FLAG_SYSTEM; 272 } 273 if (!c) { 274 bo->placements[c++].flags = TTM_PL_MASK_CACHING 275 | TTM_PL_FLAG_SYSTEM; 276 } 277 for (i = 0; i < c; ++i) { 278 bo->placements[i].fpfn = 0; 279 bo->placements[i].lpfn = 0; 280 } 281 bo->placement.num_placement = c; 282 bo->placement.num_busy_placement = c; 283} 284 285static inline u64 bochs_bo_gpu_offset(struct bochs_bo *bo) 286{ 287 return bo->bo.offset; 288} 289 290int bochs_bo_pin(struct bochs_bo *bo, u32 pl_flag, u64 *gpu_addr) 291{ 292 int i, ret; 293 294 if (bo->pin_count) { 295 bo->pin_count++; 296 if (gpu_addr) 297 *gpu_addr = bochs_bo_gpu_offset(bo); 298 return 0; 299 } 300 301 bochs_ttm_placement(bo, pl_flag); 302 for (i = 0; i < bo->placement.num_placement; i++) 303 bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT; 304 ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); 305 if (ret) 306 return ret; 307 308 bo->pin_count = 1; 309 if (gpu_addr) 310 *gpu_addr = bochs_bo_gpu_offset(bo); 311 return 0; 312} 313 314int bochs_bo_unpin(struct bochs_bo *bo) 315{ 316 int i, ret; 317 318 if (!bo->pin_count) { 319 DRM_ERROR("unpin bad %p\n", bo); 320 return 0; 321 } 322 bo->pin_count--; 323 324 if (bo->pin_count) 325 return 0; 326 327 for (i = 0; i < bo->placement.num_placement; i++) 328 bo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT; 329 ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); 330 if (ret) 331 return ret; 332 333 return 0; 334} 335 336int bochs_mmap(struct file *filp, struct vm_area_struct *vma) 337{ 338 struct drm_file *file_priv; 339 struct bochs_device *bochs; 340 341 if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET)) 342 return -EINVAL; 343 344 file_priv = filp->private_data; 345 bochs = file_priv->minor->dev->dev_private; 346 return ttm_bo_mmap(filp, vma, &bochs->ttm.bdev); 347} 348 349/* ---------------------------------------------------------------------- */ 350 351static int bochs_bo_create(struct drm_device *dev, int size, int align, 352 uint32_t flags, struct bochs_bo **pbochsbo) 353{ 354 struct bochs_device *bochs = dev->dev_private; 355 struct bochs_bo *bochsbo; 356 size_t acc_size; 357 int ret; 358 359 bochsbo = kzalloc(sizeof(struct bochs_bo), GFP_KERNEL); 360 if (!bochsbo) 361 return -ENOMEM; 362 363 ret = drm_gem_object_init(dev, &bochsbo->gem, size); 364 if (ret) { 365 kfree(bochsbo); 366 return ret; 367 } 368 369 bochsbo->bo.bdev = &bochs->ttm.bdev; 370 bochsbo->bo.bdev->dev_mapping = dev->anon_inode->i_mapping; 371 372 bochs_ttm_placement(bochsbo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM); 373 374 acc_size = ttm_bo_dma_acc_size(&bochs->ttm.bdev, size, 375 sizeof(struct bochs_bo)); 376 377 ret = ttm_bo_init(&bochs->ttm.bdev, &bochsbo->bo, size, 378 ttm_bo_type_device, &bochsbo->placement, 379 align >> PAGE_SHIFT, false, NULL, acc_size, 380 NULL, NULL, bochs_bo_ttm_destroy); 381 if (ret) 382 return ret; 383 384 *pbochsbo = bochsbo; 385 return 0; 386} 387 388int bochs_gem_create(struct drm_device *dev, u32 size, bool iskernel, 389 struct drm_gem_object **obj) 390{ 391 struct bochs_bo *bochsbo; 392 int ret; 393 394 *obj = NULL; 395 396 size = PAGE_ALIGN(size); 397 if (size == 0) 398 return -EINVAL; 399 400 ret = bochs_bo_create(dev, size, 0, 0, &bochsbo); 401 if (ret) { 402 if (ret != -ERESTARTSYS) 403 DRM_ERROR("failed to allocate GEM object\n"); 404 return ret; 405 } 406 *obj = &bochsbo->gem; 407 return 0; 408} 409 410int bochs_dumb_create(struct drm_file *file, struct drm_device *dev, 411 struct drm_mode_create_dumb *args) 412{ 413 struct drm_gem_object *gobj; 414 u32 handle; 415 int ret; 416 417 args->pitch = args->width * ((args->bpp + 7) / 8); 418 args->size = args->pitch * args->height; 419 420 ret = bochs_gem_create(dev, args->size, false, 421 &gobj); 422 if (ret) 423 return ret; 424 425 ret = drm_gem_handle_create(file, gobj, &handle); 426 drm_gem_object_unreference_unlocked(gobj); 427 if (ret) 428 return ret; 429 430 args->handle = handle; 431 return 0; 432} 433 434static void bochs_bo_unref(struct bochs_bo **bo) 435{ 436 struct ttm_buffer_object *tbo; 437 438 if ((*bo) == NULL) 439 return; 440 441 tbo = &((*bo)->bo); 442 ttm_bo_unref(&tbo); 443 *bo = NULL; 444} 445 446void bochs_gem_free_object(struct drm_gem_object *obj) 447{ 448 struct bochs_bo *bochs_bo = gem_to_bochs_bo(obj); 449 450 bochs_bo_unref(&bochs_bo); 451} 452 453int bochs_dumb_mmap_offset(struct drm_file *file, struct drm_device *dev, 454 uint32_t handle, uint64_t *offset) 455{ 456 struct drm_gem_object *obj; 457 int ret; 458 struct bochs_bo *bo; 459 460 mutex_lock(&dev->struct_mutex); 461 obj = drm_gem_object_lookup(dev, file, handle); 462 if (obj == NULL) { 463 ret = -ENOENT; 464 goto out_unlock; 465 } 466 467 bo = gem_to_bochs_bo(obj); 468 *offset = bochs_bo_mmap_offset(bo); 469 470 drm_gem_object_unreference(obj); 471 ret = 0; 472out_unlock: 473 mutex_unlock(&dev->struct_mutex); 474 return ret; 475 476} 477 478/* ---------------------------------------------------------------------- */ 479 480static void bochs_user_framebuffer_destroy(struct drm_framebuffer *fb) 481{ 482 struct bochs_framebuffer *bochs_fb = to_bochs_framebuffer(fb); 483 if (bochs_fb->obj) 484 drm_gem_object_unreference_unlocked(bochs_fb->obj); 485 drm_framebuffer_cleanup(fb); 486 kfree(fb); 487} 488 489static const struct drm_framebuffer_funcs bochs_fb_funcs = { 490 .destroy = bochs_user_framebuffer_destroy, 491}; 492 493int bochs_framebuffer_init(struct drm_device *dev, 494 struct bochs_framebuffer *gfb, 495 struct drm_mode_fb_cmd2 *mode_cmd, 496 struct drm_gem_object *obj) 497{ 498 int ret; 499 500 drm_helper_mode_fill_fb_struct(&gfb->base, mode_cmd); 501 gfb->obj = obj; 502 ret = drm_framebuffer_init(dev, &gfb->base, &bochs_fb_funcs); 503 if (ret) { 504 DRM_ERROR("drm_framebuffer_init failed: %d\n", ret); 505 return ret; 506 } 507 return 0; 508} 509 510static struct drm_framebuffer * 511bochs_user_framebuffer_create(struct drm_device *dev, 512 struct drm_file *filp, 513 struct drm_mode_fb_cmd2 *mode_cmd) 514{ 515 struct drm_gem_object *obj; 516 struct bochs_framebuffer *bochs_fb; 517 int ret; 518 519 DRM_DEBUG_DRIVER("%dx%d, format %c%c%c%c\n", 520 mode_cmd->width, mode_cmd->height, 521 (mode_cmd->pixel_format) & 0xff, 522 (mode_cmd->pixel_format >> 8) & 0xff, 523 (mode_cmd->pixel_format >> 16) & 0xff, 524 (mode_cmd->pixel_format >> 24) & 0xff); 525 526 if (mode_cmd->pixel_format != DRM_FORMAT_XRGB8888) 527 return ERR_PTR(-ENOENT); 528 529 obj = drm_gem_object_lookup(dev, filp, mode_cmd->handles[0]); 530 if (obj == NULL) 531 return ERR_PTR(-ENOENT); 532 533 bochs_fb = kzalloc(sizeof(*bochs_fb), GFP_KERNEL); 534 if (!bochs_fb) { 535 drm_gem_object_unreference_unlocked(obj); 536 return ERR_PTR(-ENOMEM); 537 } 538 539 ret = bochs_framebuffer_init(dev, bochs_fb, mode_cmd, obj); 540 if (ret) { 541 drm_gem_object_unreference_unlocked(obj); 542 kfree(bochs_fb); 543 return ERR_PTR(ret); 544 } 545 return &bochs_fb->base; 546} 547 548const struct drm_mode_config_funcs bochs_mode_funcs = { 549 .fb_create = bochs_user_framebuffer_create, 550}; 551