| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright © 2017 Keith Packard <keithp@keithp.com> |
|---|
| 3 | | - * |
|---|
| 4 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 5 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 6 | | - * the Free Software Foundation, either version 2 of the License, or |
|---|
| 7 | | - * (at your option) any later version. |
|---|
| 8 | | - * |
|---|
| 9 | | - * This program is distributed in the hope that it will be useful, but |
|---|
| 10 | | - * WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 11 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|---|
| 12 | | - * General Public License for more details. |
|---|
| 13 | 4 | */ |
|---|
| 5 | +#include <linux/file.h> |
|---|
| 6 | +#include <linux/uaccess.h> |
|---|
| 14 | 7 | |
|---|
| 15 | | -#include <drm/drmP.h> |
|---|
| 16 | | -#include "drm_internal.h" |
|---|
| 17 | | -#include "drm_legacy.h" |
|---|
| 18 | | -#include "drm_crtc_internal.h" |
|---|
| 19 | | -#include <drm/drm_lease.h> |
|---|
| 20 | 8 | #include <drm/drm_auth.h> |
|---|
| 21 | 9 | #include <drm/drm_crtc_helper.h> |
|---|
| 10 | +#include <drm/drm_drv.h> |
|---|
| 11 | +#include <drm/drm_file.h> |
|---|
| 12 | +#include <drm/drm_lease.h> |
|---|
| 13 | +#include <drm/drm_print.h> |
|---|
| 14 | + |
|---|
| 15 | +#include "drm_crtc_internal.h" |
|---|
| 16 | +#include "drm_internal.h" |
|---|
| 17 | +#include "drm_legacy.h" |
|---|
| 22 | 18 | |
|---|
| 23 | 19 | #define drm_for_each_lessee(lessee, lessor) \ |
|---|
| 24 | 20 | list_for_each_entry((lessee), &(lessor)->lessees, lessee_list) |
|---|
| .. | .. |
|---|
| 39 | 35 | master = master->lessor; |
|---|
| 40 | 36 | return master; |
|---|
| 41 | 37 | } |
|---|
| 42 | | -EXPORT_SYMBOL(drm_lease_owner); |
|---|
| 43 | 38 | |
|---|
| 44 | 39 | /** |
|---|
| 45 | 40 | * _drm_find_lessee - find lessee by id (idr_mutex held) |
|---|
| .. | .. |
|---|
| 112 | 107 | */ |
|---|
| 113 | 108 | bool _drm_lease_held(struct drm_file *file_priv, int id) |
|---|
| 114 | 109 | { |
|---|
| 115 | | - if (file_priv == NULL || file_priv->master == NULL) |
|---|
| 110 | + if (!file_priv || !file_priv->master) |
|---|
| 116 | 111 | return true; |
|---|
| 117 | 112 | |
|---|
| 118 | 113 | return _drm_lease_held_master(file_priv->master, id); |
|---|
| 119 | 114 | } |
|---|
| 120 | | -EXPORT_SYMBOL(_drm_lease_held); |
|---|
| 121 | 115 | |
|---|
| 122 | 116 | /** |
|---|
| 123 | 117 | * drm_lease_held - check drm_mode_object lease status (idr_mutex not held) |
|---|
| .. | .. |
|---|
| 135 | 129 | struct drm_master *master; |
|---|
| 136 | 130 | bool ret; |
|---|
| 137 | 131 | |
|---|
| 138 | | - if (file_priv == NULL || file_priv->master == NULL) |
|---|
| 132 | + if (!file_priv || !file_priv->master || !file_priv->master->lessor) |
|---|
| 139 | 133 | return true; |
|---|
| 140 | 134 | |
|---|
| 141 | 135 | master = file_priv->master; |
|---|
| .. | .. |
|---|
| 144 | 138 | mutex_unlock(&master->dev->mode_config.idr_mutex); |
|---|
| 145 | 139 | return ret; |
|---|
| 146 | 140 | } |
|---|
| 147 | | -EXPORT_SYMBOL(drm_lease_held); |
|---|
| 148 | 141 | |
|---|
| 149 | 142 | /** |
|---|
| 150 | 143 | * drm_lease_filter_crtcs - restricted crtc set to leased values (idr_mutex not held) |
|---|
| .. | .. |
|---|
| 162 | 155 | int count_in, count_out; |
|---|
| 163 | 156 | uint32_t crtcs_out = 0; |
|---|
| 164 | 157 | |
|---|
| 165 | | - if (file_priv == NULL || file_priv->master == NULL) |
|---|
| 158 | + if (!file_priv || !file_priv->master || !file_priv->master->lessor) |
|---|
| 166 | 159 | return crtcs_in; |
|---|
| 167 | 160 | |
|---|
| 168 | 161 | master = file_priv->master; |
|---|
| .. | .. |
|---|
| 173 | 166 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
|---|
| 174 | 167 | if (_drm_lease_held_master(master, crtc->base.id)) { |
|---|
| 175 | 168 | uint32_t mask_in = 1ul << count_in; |
|---|
| 169 | + |
|---|
| 176 | 170 | if ((crtcs_in & mask_in) != 0) { |
|---|
| 177 | 171 | uint32_t mask_out = 1ul << count_out; |
|---|
| 172 | + |
|---|
| 178 | 173 | crtcs_out |= mask_out; |
|---|
| 179 | 174 | } |
|---|
| 180 | 175 | count_out++; |
|---|
| .. | .. |
|---|
| 184 | 179 | mutex_unlock(&master->dev->mode_config.idr_mutex); |
|---|
| 185 | 180 | return crtcs_out; |
|---|
| 186 | 181 | } |
|---|
| 187 | | -EXPORT_SYMBOL(drm_lease_filter_crtcs); |
|---|
| 188 | 182 | |
|---|
| 189 | 183 | /* |
|---|
| 190 | 184 | * drm_lease_create - create a new drm_master with leased objects (idr_mutex not held) |
|---|
| .. | .. |
|---|
| 195 | 189 | * make sure all of the desired objects can be leased, atomically |
|---|
| 196 | 190 | * leasing them to the new drmmaster. |
|---|
| 197 | 191 | * |
|---|
| 198 | | - * ERR_PTR(-EACCESS) some other master holds the title to any object |
|---|
| 192 | + * ERR_PTR(-EACCES) some other master holds the title to any object |
|---|
| 199 | 193 | * ERR_PTR(-ENOENT) some object is not a valid DRM object for this device |
|---|
| 200 | 194 | * ERR_PTR(-EBUSY) some other lessee holds title to this object |
|---|
| 201 | 195 | * ERR_PTR(-EEXIST) same object specified more than once in the provided list |
|---|
| .. | .. |
|---|
| 222 | 216 | |
|---|
| 223 | 217 | idr_for_each_entry(leases, entry, object) { |
|---|
| 224 | 218 | error = 0; |
|---|
| 225 | | - if (!idr_find(&dev->mode_config.crtc_idr, object)) |
|---|
| 219 | + if (!idr_find(&dev->mode_config.object_idr, object)) |
|---|
| 226 | 220 | error = -ENOENT; |
|---|
| 227 | | - else if (!_drm_lease_held_master(lessor, object)) |
|---|
| 228 | | - error = -EACCES; |
|---|
| 229 | 221 | else if (_drm_has_leased(lessor, object)) |
|---|
| 230 | 222 | error = -EBUSY; |
|---|
| 231 | 223 | |
|---|
| .. | .. |
|---|
| 357 | 349 | } |
|---|
| 358 | 350 | |
|---|
| 359 | 351 | static int validate_lease(struct drm_device *dev, |
|---|
| 360 | | - struct drm_file *lessor_priv, |
|---|
| 361 | 352 | int object_count, |
|---|
| 362 | | - struct drm_mode_object **objects) |
|---|
| 353 | + struct drm_mode_object **objects, |
|---|
| 354 | + bool universal_planes) |
|---|
| 363 | 355 | { |
|---|
| 364 | 356 | int o; |
|---|
| 365 | 357 | int has_crtc = -1; |
|---|
| .. | .. |
|---|
| 376 | 368 | if (objects[o]->type == DRM_MODE_OBJECT_CONNECTOR && has_connector == -1) |
|---|
| 377 | 369 | has_connector = o; |
|---|
| 378 | 370 | |
|---|
| 379 | | - if (lessor_priv->universal_planes) { |
|---|
| 371 | + if (universal_planes) { |
|---|
| 380 | 372 | if (objects[o]->type == DRM_MODE_OBJECT_PLANE && has_plane == -1) |
|---|
| 381 | 373 | has_plane = o; |
|---|
| 382 | 374 | } |
|---|
| 383 | 375 | } |
|---|
| 384 | 376 | if (has_crtc == -1 || has_connector == -1) |
|---|
| 385 | 377 | return -EINVAL; |
|---|
| 386 | | - if (lessor_priv->universal_planes && has_plane == -1) |
|---|
| 378 | + if (universal_planes && has_plane == -1) |
|---|
| 387 | 379 | return -EINVAL; |
|---|
| 388 | 380 | return 0; |
|---|
| 389 | 381 | } |
|---|
| .. | .. |
|---|
| 397 | 389 | struct drm_mode_object **objects; |
|---|
| 398 | 390 | u32 o; |
|---|
| 399 | 391 | int ret; |
|---|
| 392 | + bool universal_planes = READ_ONCE(lessor_priv->universal_planes); |
|---|
| 393 | + |
|---|
| 400 | 394 | objects = kcalloc(object_count, sizeof(struct drm_mode_object *), |
|---|
| 401 | 395 | GFP_KERNEL); |
|---|
| 402 | 396 | if (!objects) |
|---|
| .. | .. |
|---|
| 405 | 399 | /* step one - get references to all the mode objects |
|---|
| 406 | 400 | and check for validity. */ |
|---|
| 407 | 401 | for (o = 0; o < object_count; o++) { |
|---|
| 408 | | - if ((int) object_ids[o] < 0) { |
|---|
| 409 | | - ret = -EINVAL; |
|---|
| 410 | | - goto out_free_objects; |
|---|
| 411 | | - } |
|---|
| 412 | | - |
|---|
| 413 | 402 | objects[o] = drm_mode_object_find(dev, lessor_priv, |
|---|
| 414 | 403 | object_ids[o], |
|---|
| 415 | 404 | DRM_MODE_OBJECT_ANY); |
|---|
| .. | .. |
|---|
| 419 | 408 | } |
|---|
| 420 | 409 | |
|---|
| 421 | 410 | if (!drm_mode_object_lease_required(objects[o]->type)) { |
|---|
| 411 | + DRM_DEBUG_KMS("invalid object for lease\n"); |
|---|
| 422 | 412 | ret = -EINVAL; |
|---|
| 423 | 413 | goto out_free_objects; |
|---|
| 424 | 414 | } |
|---|
| 425 | 415 | } |
|---|
| 426 | 416 | |
|---|
| 427 | | - ret = validate_lease(dev, lessor_priv, object_count, objects); |
|---|
| 428 | | - if (ret) |
|---|
| 417 | + ret = validate_lease(dev, object_count, objects, universal_planes); |
|---|
| 418 | + if (ret) { |
|---|
| 419 | + DRM_DEBUG_LEASE("lease validation failed\n"); |
|---|
| 429 | 420 | goto out_free_objects; |
|---|
| 421 | + } |
|---|
| 430 | 422 | |
|---|
| 431 | 423 | /* add their IDs to the lease request - taking into account |
|---|
| 432 | 424 | universal planes */ |
|---|
| 433 | 425 | for (o = 0; o < object_count; o++) { |
|---|
| 434 | 426 | struct drm_mode_object *obj = objects[o]; |
|---|
| 435 | 427 | u32 object_id = objects[o]->id; |
|---|
| 428 | + |
|---|
| 436 | 429 | DRM_DEBUG_LEASE("Adding object %d to lease\n", object_id); |
|---|
| 437 | 430 | |
|---|
| 438 | 431 | /* |
|---|
| 439 | 432 | * We're using an IDR to hold the set of leased |
|---|
| 440 | 433 | * objects, but we don't need to point at the object's |
|---|
| 441 | | - * data structure from the lease as the main crtc_idr |
|---|
| 434 | + * data structure from the lease as the main object_idr |
|---|
| 442 | 435 | * will be used to actually find that. Instead, all we |
|---|
| 443 | 436 | * really want is a 'leased/not-leased' result, for |
|---|
| 444 | 437 | * which any non-NULL pointer will work fine. |
|---|
| .. | .. |
|---|
| 449 | 442 | object_id, ret); |
|---|
| 450 | 443 | goto out_free_objects; |
|---|
| 451 | 444 | } |
|---|
| 452 | | - if (obj->type == DRM_MODE_OBJECT_CRTC && !lessor_priv->universal_planes) { |
|---|
| 445 | + if (obj->type == DRM_MODE_OBJECT_CRTC && !universal_planes) { |
|---|
| 453 | 446 | struct drm_crtc *crtc = obj_to_crtc(obj); |
|---|
| 447 | + |
|---|
| 454 | 448 | ret = idr_alloc(leases, &drm_lease_idr_object, crtc->primary->base.id, crtc->primary->base.id + 1, GFP_KERNEL); |
|---|
| 455 | 449 | if (ret < 0) { |
|---|
| 456 | 450 | DRM_DEBUG_LEASE("Object primary plane %d cannot be inserted into leases (%d)\n", |
|---|
| .. | .. |
|---|
| 506 | 500 | |
|---|
| 507 | 501 | /* Can't lease without MODESET */ |
|---|
| 508 | 502 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
|---|
| 509 | | - return -EINVAL; |
|---|
| 503 | + return -EOPNOTSUPP; |
|---|
| 510 | 504 | |
|---|
| 511 | 505 | /* Do not allow sub-leases */ |
|---|
| 512 | | - if (lessor->lessor) |
|---|
| 506 | + if (lessor->lessor) { |
|---|
| 507 | + DRM_DEBUG_LEASE("recursive leasing not allowed\n"); |
|---|
| 513 | 508 | return -EINVAL; |
|---|
| 509 | + } |
|---|
| 514 | 510 | |
|---|
| 515 | 511 | /* need some objects */ |
|---|
| 516 | | - if (cl->object_count == 0) |
|---|
| 512 | + if (cl->object_count == 0) { |
|---|
| 513 | + DRM_DEBUG_LEASE("no objects in lease\n"); |
|---|
| 517 | 514 | return -EINVAL; |
|---|
| 515 | + } |
|---|
| 518 | 516 | |
|---|
| 519 | | - if (cl->flags && (cl->flags & ~(O_CLOEXEC | O_NONBLOCK))) |
|---|
| 517 | + if (cl->flags && (cl->flags & ~(O_CLOEXEC | O_NONBLOCK))) { |
|---|
| 518 | + DRM_DEBUG_LEASE("invalid flags\n"); |
|---|
| 520 | 519 | return -EINVAL; |
|---|
| 520 | + } |
|---|
| 521 | 521 | |
|---|
| 522 | 522 | object_count = cl->object_count; |
|---|
| 523 | 523 | |
|---|
| .. | .. |
|---|
| 533 | 533 | object_count, object_ids); |
|---|
| 534 | 534 | kfree(object_ids); |
|---|
| 535 | 535 | if (ret) { |
|---|
| 536 | + DRM_DEBUG_LEASE("lease object lookup failed: %i\n", ret); |
|---|
| 536 | 537 | idr_destroy(&leases); |
|---|
| 537 | 538 | return ret; |
|---|
| 538 | 539 | } |
|---|
| .. | .. |
|---|
| 617 | 618 | |
|---|
| 618 | 619 | /* Can't lease without MODESET */ |
|---|
| 619 | 620 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
|---|
| 620 | | - return -EINVAL; |
|---|
| 621 | + return -EOPNOTSUPP; |
|---|
| 621 | 622 | |
|---|
| 622 | 623 | DRM_DEBUG_LEASE("List lessees for %d\n", lessor->lessee_id); |
|---|
| 623 | 624 | |
|---|
| .. | .. |
|---|
| 673 | 674 | |
|---|
| 674 | 675 | /* Can't lease without MODESET */ |
|---|
| 675 | 676 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
|---|
| 676 | | - return -EINVAL; |
|---|
| 677 | + return -EOPNOTSUPP; |
|---|
| 677 | 678 | |
|---|
| 678 | 679 | DRM_DEBUG_LEASE("get lease for %d\n", lessee->lessee_id); |
|---|
| 679 | 680 | |
|---|
| .. | .. |
|---|
| 681 | 682 | |
|---|
| 682 | 683 | if (lessee->lessor == NULL) |
|---|
| 683 | 684 | /* owner can use all objects */ |
|---|
| 684 | | - object_idr = &lessee->dev->mode_config.crtc_idr; |
|---|
| 685 | + object_idr = &lessee->dev->mode_config.object_idr; |
|---|
| 685 | 686 | else |
|---|
| 686 | 687 | /* lessee can only use allowed object */ |
|---|
| 687 | 688 | object_idr = &lessee->leases; |
|---|
| .. | .. |
|---|
| 728 | 729 | |
|---|
| 729 | 730 | /* Can't lease without MODESET */ |
|---|
| 730 | 731 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
|---|
| 731 | | - return -EINVAL; |
|---|
| 732 | + return -EOPNOTSUPP; |
|---|
| 732 | 733 | |
|---|
| 733 | 734 | mutex_lock(&dev->mode_config.idr_mutex); |
|---|
| 734 | 735 | |
|---|