| .. | .. |
|---|
| 21 | 21 | */ |
|---|
| 22 | 22 | |
|---|
| 23 | 23 | #include <linux/export.h> |
|---|
| 24 | | -#include <drm/drmP.h> |
|---|
| 25 | | -#include <drm/drm_auth.h> |
|---|
| 26 | | -#include <drm/drm_framebuffer.h> |
|---|
| 27 | | -#include <drm/drm_atomic.h> |
|---|
| 28 | | -#include <drm/drm_print.h> |
|---|
| 24 | +#include <linux/uaccess.h> |
|---|
| 29 | 25 | |
|---|
| 30 | | -#include "drm_internal.h" |
|---|
| 26 | +#include <drm/drm_atomic.h> |
|---|
| 27 | +#include <drm/drm_atomic_uapi.h> |
|---|
| 28 | +#include <drm/drm_auth.h> |
|---|
| 29 | +#include <drm/drm_debugfs.h> |
|---|
| 30 | +#include <drm/drm_drv.h> |
|---|
| 31 | +#include <drm/drm_file.h> |
|---|
| 32 | +#include <drm/drm_fourcc.h> |
|---|
| 33 | +#include <drm/drm_framebuffer.h> |
|---|
| 34 | +#include <drm/drm_gem.h> |
|---|
| 35 | +#include <drm/drm_print.h> |
|---|
| 36 | +#include <drm/drm_util.h> |
|---|
| 37 | + |
|---|
| 31 | 38 | #include "drm_crtc_internal.h" |
|---|
| 39 | +#include "drm_internal.h" |
|---|
| 32 | 40 | |
|---|
| 33 | 41 | /** |
|---|
| 34 | 42 | * DOC: overview |
|---|
| .. | .. |
|---|
| 112 | 120 | struct drm_mode_fb_cmd2 r = {}; |
|---|
| 113 | 121 | int ret; |
|---|
| 114 | 122 | |
|---|
| 123 | + if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
|---|
| 124 | + return -EOPNOTSUPP; |
|---|
| 125 | + |
|---|
| 126 | + r.pixel_format = drm_driver_legacy_fb_format(dev, or->bpp, or->depth); |
|---|
| 127 | + if (r.pixel_format == DRM_FORMAT_INVALID) { |
|---|
| 128 | + DRM_DEBUG("bad {bpp:%d, depth:%d}\n", or->bpp, or->depth); |
|---|
| 129 | + return -EINVAL; |
|---|
| 130 | + } |
|---|
| 131 | + |
|---|
| 115 | 132 | /* convert to new format and call new ioctl */ |
|---|
| 116 | 133 | r.fb_id = or->fb_id; |
|---|
| 117 | 134 | r.width = or->width; |
|---|
| 118 | 135 | r.height = or->height; |
|---|
| 119 | 136 | r.pitches[0] = or->pitch; |
|---|
| 120 | | - r.pixel_format = drm_mode_legacy_fb_format(or->bpp, or->depth); |
|---|
| 121 | 137 | r.handles[0] = or->handle; |
|---|
| 122 | | - |
|---|
| 123 | | - if (r.pixel_format == DRM_FORMAT_XRGB2101010 && |
|---|
| 124 | | - dev->driver->driver_features & DRIVER_PREFER_XBGR_30BPP) |
|---|
| 125 | | - r.pixel_format = DRM_FORMAT_XBGR2101010; |
|---|
| 126 | 138 | |
|---|
| 127 | 139 | ret = drm_mode_addfb2(dev, &r, file_priv); |
|---|
| 128 | 140 | if (ret) |
|---|
| .. | .. |
|---|
| 164 | 176 | int i; |
|---|
| 165 | 177 | |
|---|
| 166 | 178 | /* check if the format is supported at all */ |
|---|
| 167 | | - info = __drm_format_info(r->pixel_format & ~DRM_FORMAT_BIG_ENDIAN); |
|---|
| 168 | | - if (!info) { |
|---|
| 179 | + if (!__drm_format_info(r->pixel_format)) { |
|---|
| 169 | 180 | struct drm_format_name_buf format_name; |
|---|
| 170 | 181 | |
|---|
| 171 | 182 | DRM_DEBUG_KMS("bad framebuffer format %s\n", |
|---|
| .. | .. |
|---|
| 173 | 184 | &format_name)); |
|---|
| 174 | 185 | return -EINVAL; |
|---|
| 175 | 186 | } |
|---|
| 176 | | - |
|---|
| 177 | | - /* now let the driver pick its own format info */ |
|---|
| 178 | | - info = drm_get_format_info(dev, r); |
|---|
| 179 | 187 | |
|---|
| 180 | 188 | if (r->width == 0) { |
|---|
| 181 | 189 | DRM_DEBUG_KMS("bad framebuffer width %u\n", r->width); |
|---|
| .. | .. |
|---|
| 187 | 195 | return -EINVAL; |
|---|
| 188 | 196 | } |
|---|
| 189 | 197 | |
|---|
| 198 | + /* now let the driver pick its own format info */ |
|---|
| 199 | + info = drm_get_format_info(dev, r); |
|---|
| 200 | + |
|---|
| 190 | 201 | for (i = 0; i < info->num_planes; i++) { |
|---|
| 191 | 202 | unsigned int width = fb_plane_width(r->width, info, i); |
|---|
| 192 | 203 | unsigned int height = fb_plane_height(r->height, info, i); |
|---|
| 193 | | - unsigned int bpp = info->bpp[i]; |
|---|
| 204 | + unsigned int block_size = info->char_per_block[i]; |
|---|
| 205 | + u64 min_pitch = drm_format_info_min_pitch(info, i, width); |
|---|
| 206 | + |
|---|
| 207 | + if (!block_size && (r->modifier[i] == DRM_FORMAT_MOD_LINEAR)) { |
|---|
| 208 | + DRM_DEBUG_KMS("Format requires non-linear modifier for plane %d\n", i); |
|---|
| 209 | + return -EINVAL; |
|---|
| 210 | + } |
|---|
| 194 | 211 | |
|---|
| 195 | 212 | if (!r->handles[i]) { |
|---|
| 196 | 213 | DRM_DEBUG_KMS("no buffer object handle for plane %d\n", i); |
|---|
| 197 | 214 | return -EINVAL; |
|---|
| 198 | 215 | } |
|---|
| 199 | 216 | |
|---|
| 200 | | - if ((uint64_t) width * bpp / 8 > UINT_MAX) |
|---|
| 217 | + if (min_pitch > UINT_MAX) |
|---|
| 201 | 218 | return -ERANGE; |
|---|
| 202 | 219 | |
|---|
| 203 | 220 | if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX) |
|---|
| 204 | 221 | return -ERANGE; |
|---|
| 205 | 222 | |
|---|
| 206 | | - if (r->pitches[i] < roundup(width * bpp, 8) / 8) { |
|---|
| 223 | + if (block_size && r->pitches[i] < min_pitch) { |
|---|
| 207 | 224 | DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i); |
|---|
| 208 | 225 | return -EINVAL; |
|---|
| 209 | 226 | } |
|---|
| .. | .. |
|---|
| 278 | 295 | struct drm_framebuffer *fb; |
|---|
| 279 | 296 | int ret; |
|---|
| 280 | 297 | |
|---|
| 281 | | - if (r->flags & ~(DRM_MODE_FB_INTERLACED | DRM_MODE_FB_MODIFIERS | |
|---|
| 282 | | - DRM_MODE_FB_SECURE)) { |
|---|
| 298 | + if (r->flags & ~(DRM_MODE_FB_INTERLACED | DRM_MODE_FB_MODIFIERS)) { |
|---|
| 283 | 299 | DRM_DEBUG_KMS("bad framebuffer flags 0x%08x\n", r->flags); |
|---|
| 284 | 300 | return ERR_PTR(-EINVAL); |
|---|
| 285 | 301 | } |
|---|
| .. | .. |
|---|
| 313 | 329 | |
|---|
| 314 | 330 | return fb; |
|---|
| 315 | 331 | } |
|---|
| 332 | +EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_internal_framebuffer_create); |
|---|
| 316 | 333 | |
|---|
| 317 | 334 | /** |
|---|
| 318 | 335 | * drm_mode_addfb2 - add an FB to the graphics configuration |
|---|
| .. | .. |
|---|
| 336 | 353 | struct drm_framebuffer *fb; |
|---|
| 337 | 354 | |
|---|
| 338 | 355 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
|---|
| 339 | | - return -EINVAL; |
|---|
| 356 | + return -EOPNOTSUPP; |
|---|
| 340 | 357 | |
|---|
| 341 | 358 | fb = drm_internal_framebuffer_create(dev, r, file_priv); |
|---|
| 342 | 359 | if (IS_ERR(fb)) |
|---|
| .. | .. |
|---|
| 351 | 368 | mutex_unlock(&file_priv->fbs_lock); |
|---|
| 352 | 369 | |
|---|
| 353 | 370 | return 0; |
|---|
| 371 | +} |
|---|
| 372 | + |
|---|
| 373 | +int drm_mode_addfb2_ioctl(struct drm_device *dev, |
|---|
| 374 | + void *data, struct drm_file *file_priv) |
|---|
| 375 | +{ |
|---|
| 376 | +#ifdef __BIG_ENDIAN |
|---|
| 377 | + if (!dev->mode_config.quirk_addfb_prefer_host_byte_order) { |
|---|
| 378 | + /* |
|---|
| 379 | + * Drivers must set the |
|---|
| 380 | + * quirk_addfb_prefer_host_byte_order quirk to make |
|---|
| 381 | + * the drm_mode_addfb() compat code work correctly on |
|---|
| 382 | + * bigendian machines. |
|---|
| 383 | + * |
|---|
| 384 | + * If they don't they interpret pixel_format values |
|---|
| 385 | + * incorrectly for bug compatibility, which in turn |
|---|
| 386 | + * implies the ADDFB2 ioctl does not work correctly |
|---|
| 387 | + * then. So block it to make userspace fallback to |
|---|
| 388 | + * ADDFB. |
|---|
| 389 | + */ |
|---|
| 390 | + DRM_DEBUG_KMS("addfb2 broken on bigendian"); |
|---|
| 391 | + return -EOPNOTSUPP; |
|---|
| 392 | + } |
|---|
| 393 | +#endif |
|---|
| 394 | + return drm_mode_addfb2(dev, data, file_priv); |
|---|
| 354 | 395 | } |
|---|
| 355 | 396 | |
|---|
| 356 | 397 | struct drm_mode_rmfb_work { |
|---|
| .. | .. |
|---|
| 392 | 433 | int found = 0; |
|---|
| 393 | 434 | |
|---|
| 394 | 435 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
|---|
| 395 | | - return -EINVAL; |
|---|
| 436 | + return -EOPNOTSUPP; |
|---|
| 396 | 437 | |
|---|
| 397 | 438 | fb = drm_framebuffer_lookup(dev, file_priv, fb_id); |
|---|
| 398 | 439 | if (!fb) |
|---|
| .. | .. |
|---|
| 469 | 510 | int ret; |
|---|
| 470 | 511 | |
|---|
| 471 | 512 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
|---|
| 472 | | - return -EINVAL; |
|---|
| 513 | + return -EOPNOTSUPP; |
|---|
| 473 | 514 | |
|---|
| 474 | 515 | fb = drm_framebuffer_lookup(dev, file_priv, r->fb_id); |
|---|
| 475 | 516 | if (!fb) |
|---|
| .. | .. |
|---|
| 489 | 530 | r->height = fb->height; |
|---|
| 490 | 531 | r->width = fb->width; |
|---|
| 491 | 532 | r->depth = fb->format->depth; |
|---|
| 492 | | - r->bpp = fb->format->bpp[0]; |
|---|
| 533 | + r->bpp = fb->format->cpp[0] * 8; |
|---|
| 493 | 534 | r->pitch = fb->pitches[0]; |
|---|
| 494 | 535 | |
|---|
| 495 | 536 | /* GET_FB() is an unprivileged ioctl so we must not return a |
|---|
| .. | .. |
|---|
| 507 | 548 | |
|---|
| 508 | 549 | out: |
|---|
| 509 | 550 | drm_framebuffer_put(fb); |
|---|
| 551 | + return ret; |
|---|
| 552 | +} |
|---|
| 510 | 553 | |
|---|
| 554 | +/** |
|---|
| 555 | + * drm_mode_getfb2 - get extended FB info |
|---|
| 556 | + * @dev: drm device for the ioctl |
|---|
| 557 | + * @data: data pointer for the ioctl |
|---|
| 558 | + * @file_priv: drm file for the ioctl call |
|---|
| 559 | + * |
|---|
| 560 | + * Lookup the FB given its ID and return info about it. |
|---|
| 561 | + * |
|---|
| 562 | + * Called by the user via ioctl. |
|---|
| 563 | + * |
|---|
| 564 | + * Returns: |
|---|
| 565 | + * Zero on success, negative errno on failure. |
|---|
| 566 | + */ |
|---|
| 567 | +int drm_mode_getfb2_ioctl(struct drm_device *dev, |
|---|
| 568 | + void *data, struct drm_file *file_priv) |
|---|
| 569 | +{ |
|---|
| 570 | + struct drm_mode_fb_cmd2 *r = data; |
|---|
| 571 | + struct drm_framebuffer *fb; |
|---|
| 572 | + unsigned int i; |
|---|
| 573 | + int ret; |
|---|
| 574 | + |
|---|
| 575 | + if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
|---|
| 576 | + return -EINVAL; |
|---|
| 577 | + |
|---|
| 578 | + fb = drm_framebuffer_lookup(dev, file_priv, r->fb_id); |
|---|
| 579 | + if (!fb) |
|---|
| 580 | + return -ENOENT; |
|---|
| 581 | + |
|---|
| 582 | + /* For multi-plane framebuffers, we require the driver to place the |
|---|
| 583 | + * GEM objects directly in the drm_framebuffer. For single-plane |
|---|
| 584 | + * framebuffers, we can fall back to create_handle. |
|---|
| 585 | + */ |
|---|
| 586 | + if (!fb->obj[0] && |
|---|
| 587 | + (fb->format->num_planes > 1 || !fb->funcs->create_handle)) { |
|---|
| 588 | + ret = -ENODEV; |
|---|
| 589 | + goto out; |
|---|
| 590 | + } |
|---|
| 591 | + |
|---|
| 592 | + r->height = fb->height; |
|---|
| 593 | + r->width = fb->width; |
|---|
| 594 | + r->pixel_format = fb->format->format; |
|---|
| 595 | + |
|---|
| 596 | + r->flags = 0; |
|---|
| 597 | + if (dev->mode_config.allow_fb_modifiers) |
|---|
| 598 | + r->flags |= DRM_MODE_FB_MODIFIERS; |
|---|
| 599 | + |
|---|
| 600 | + for (i = 0; i < ARRAY_SIZE(r->handles); i++) { |
|---|
| 601 | + r->handles[i] = 0; |
|---|
| 602 | + r->pitches[i] = 0; |
|---|
| 603 | + r->offsets[i] = 0; |
|---|
| 604 | + r->modifier[i] = 0; |
|---|
| 605 | + } |
|---|
| 606 | + |
|---|
| 607 | + for (i = 0; i < fb->format->num_planes; i++) { |
|---|
| 608 | + r->pitches[i] = fb->pitches[i]; |
|---|
| 609 | + r->offsets[i] = fb->offsets[i]; |
|---|
| 610 | + if (dev->mode_config.allow_fb_modifiers) |
|---|
| 611 | + r->modifier[i] = fb->modifier; |
|---|
| 612 | + } |
|---|
| 613 | + |
|---|
| 614 | + /* GET_FB2() is an unprivileged ioctl so we must not return a |
|---|
| 615 | + * buffer-handle to non master/root processes! To match GET_FB() |
|---|
| 616 | + * just return invalid handles (0) for non masters/root |
|---|
| 617 | + * rather than making GET_FB2() privileged. |
|---|
| 618 | + */ |
|---|
| 619 | + if (!drm_is_current_master(file_priv) && !capable(CAP_SYS_ADMIN)) { |
|---|
| 620 | + ret = 0; |
|---|
| 621 | + goto out; |
|---|
| 622 | + } |
|---|
| 623 | + |
|---|
| 624 | + for (i = 0; i < fb->format->num_planes; i++) { |
|---|
| 625 | + int j; |
|---|
| 626 | + |
|---|
| 627 | + /* If we reuse the same object for multiple planes, also |
|---|
| 628 | + * return the same handle. |
|---|
| 629 | + */ |
|---|
| 630 | + for (j = 0; j < i; j++) { |
|---|
| 631 | + if (fb->obj[i] == fb->obj[j]) { |
|---|
| 632 | + r->handles[i] = r->handles[j]; |
|---|
| 633 | + break; |
|---|
| 634 | + } |
|---|
| 635 | + } |
|---|
| 636 | + |
|---|
| 637 | + if (r->handles[i]) |
|---|
| 638 | + continue; |
|---|
| 639 | + |
|---|
| 640 | + if (fb->obj[i]) { |
|---|
| 641 | + ret = drm_gem_handle_create(file_priv, fb->obj[i], |
|---|
| 642 | + &r->handles[i]); |
|---|
| 643 | + } else { |
|---|
| 644 | + WARN_ON(i > 0); |
|---|
| 645 | + ret = fb->funcs->create_handle(fb, file_priv, |
|---|
| 646 | + &r->handles[i]); |
|---|
| 647 | + } |
|---|
| 648 | + |
|---|
| 649 | + if (ret != 0) |
|---|
| 650 | + goto out; |
|---|
| 651 | + } |
|---|
| 652 | + |
|---|
| 653 | +out: |
|---|
| 654 | + if (ret != 0) { |
|---|
| 655 | + /* Delete any previously-created handles on failure. */ |
|---|
| 656 | + for (i = 0; i < ARRAY_SIZE(r->handles); i++) { |
|---|
| 657 | + int j; |
|---|
| 658 | + |
|---|
| 659 | + if (r->handles[i]) |
|---|
| 660 | + drm_gem_handle_delete(file_priv, r->handles[i]); |
|---|
| 661 | + |
|---|
| 662 | + /* Zero out any handles identical to the one we just |
|---|
| 663 | + * deleted. |
|---|
| 664 | + */ |
|---|
| 665 | + for (j = i + 1; j < ARRAY_SIZE(r->handles); j++) { |
|---|
| 666 | + if (r->handles[j] == r->handles[i]) |
|---|
| 667 | + r->handles[j] = 0; |
|---|
| 668 | + } |
|---|
| 669 | + } |
|---|
| 670 | + } |
|---|
| 671 | + |
|---|
| 672 | + drm_framebuffer_put(fb); |
|---|
| 511 | 673 | return ret; |
|---|
| 512 | 674 | } |
|---|
| 513 | 675 | |
|---|
| .. | .. |
|---|
| 542 | 704 | int ret; |
|---|
| 543 | 705 | |
|---|
| 544 | 706 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
|---|
| 545 | | - return -EINVAL; |
|---|
| 707 | + return -EOPNOTSUPP; |
|---|
| 546 | 708 | |
|---|
| 547 | 709 | fb = drm_framebuffer_lookup(dev, file_priv, r->fb_id); |
|---|
| 548 | 710 | if (!fb) |
|---|
| .. | .. |
|---|
| 737 | 899 | * @fb: fb to unregister |
|---|
| 738 | 900 | * |
|---|
| 739 | 901 | * Drivers need to call this when cleaning up driver-private framebuffers, e.g. |
|---|
| 740 | | - * those used for fbdev. Note that the caller must hold a reference of it's own, |
|---|
| 902 | + * those used for fbdev. Note that the caller must hold a reference of its own, |
|---|
| 741 | 903 | * i.e. the object may not be destroyed through this call (since it'll lead to a |
|---|
| 742 | 904 | * locking inversion). |
|---|
| 743 | 905 | * |
|---|
| .. | .. |
|---|
| 947 | 1109 | if (drm_framebuffer_read_refcount(fb) > 1) { |
|---|
| 948 | 1110 | if (drm_drv_uses_atomic_modeset(dev)) { |
|---|
| 949 | 1111 | int ret = atomic_remove_fb(fb); |
|---|
| 1112 | + |
|---|
| 950 | 1113 | WARN(ret, "atomic remove_fb failed with %i\n", ret); |
|---|
| 951 | 1114 | } else |
|---|
| 952 | 1115 | legacy_remove_fb(fb); |
|---|
| .. | .. |
|---|
| 1044 | 1207 | { "framebuffer", drm_framebuffer_info, 0 }, |
|---|
| 1045 | 1208 | }; |
|---|
| 1046 | 1209 | |
|---|
| 1047 | | -int drm_framebuffer_debugfs_init(struct drm_minor *minor) |
|---|
| 1210 | +void drm_framebuffer_debugfs_init(struct drm_minor *minor) |
|---|
| 1048 | 1211 | { |
|---|
| 1049 | | - return drm_debugfs_create_files(drm_framebuffer_debugfs_list, |
|---|
| 1050 | | - ARRAY_SIZE(drm_framebuffer_debugfs_list), |
|---|
| 1051 | | - minor->debugfs_root, minor); |
|---|
| 1212 | + drm_debugfs_create_files(drm_framebuffer_debugfs_list, |
|---|
| 1213 | + ARRAY_SIZE(drm_framebuffer_debugfs_list), |
|---|
| 1214 | + minor->debugfs_root, minor); |
|---|
| 1052 | 1215 | } |
|---|
| 1053 | 1216 | #endif |
|---|