linux/drivers/gpu/drm/drm_auth.c
<<
>>
Prefs
   1/*
   2 * Created: Tue Feb  2 08:37:54 1999 by faith@valinux.com
   3 *
   4 * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
   5 * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
   6 * All Rights Reserved.
   7 *
   8 * Author Rickard E. (Rik) Faith <faith@valinux.com>
   9 * Author Gareth Hughes <gareth@valinux.com>
  10 *
  11 * Permission is hereby granted, free of charge, to any person obtaining a
  12 * copy of this software and associated documentation files (the "Software"),
  13 * to deal in the Software without restriction, including without limitation
  14 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  15 * and/or sell copies of the Software, and to permit persons to whom the
  16 * Software is furnished to do so, subject to the following conditions:
  17 *
  18 * The above copyright notice and this permission notice (including the next
  19 * paragraph) shall be included in all copies or substantial portions of the
  20 * Software.
  21 *
  22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  23 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  24 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  25 * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
  26 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  27 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  28 * OTHER DEALINGS IN THE SOFTWARE.
  29 */
  30
  31#include <linux/slab.h>
  32
  33#include <drm/drm_auth.h>
  34#include <drm/drm_drv.h>
  35#include <drm/drm_file.h>
  36#include <drm/drm_lease.h>
  37#include <drm/drm_print.h>
  38
  39#include "drm_internal.h"
  40#include "drm_legacy.h"
  41
  42/**
  43 * DOC: master and authentication
  44 *
  45 * &struct drm_master is used to track groups of clients with open
  46 * primary/legacy device nodes. For every &struct drm_file which has had at
  47 * least once successfully became the device master (either through the
  48 * SET_MASTER IOCTL, or implicitly through opening the primary device node when
  49 * no one else is the current master that time) there exists one &drm_master.
  50 * This is noted in &drm_file.is_master. All other clients have just a pointer
  51 * to the &drm_master they are associated with.
  52 *
  53 * In addition only one &drm_master can be the current master for a &drm_device.
  54 * It can be switched through the DROP_MASTER and SET_MASTER IOCTL, or
  55 * implicitly through closing/openeing the primary device node. See also
  56 * drm_is_current_master().
  57 *
  58 * Clients can authenticate against the current master (if it matches their own)
  59 * using the GETMAGIC and AUTHMAGIC IOCTLs. Together with exchanging masters,
  60 * this allows controlled access to the device for an entire group of mutually
  61 * trusted clients.
  62 */
  63
  64int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv)
  65{
  66        struct drm_auth *auth = data;
  67        int ret = 0;
  68
  69        mutex_lock(&dev->master_mutex);
  70        if (!file_priv->magic) {
  71                ret = idr_alloc(&file_priv->master->magic_map, file_priv,
  72                                1, 0, GFP_KERNEL);
  73                if (ret >= 0)
  74                        file_priv->magic = ret;
  75        }
  76        auth->magic = file_priv->magic;
  77        mutex_unlock(&dev->master_mutex);
  78
  79        DRM_DEBUG("%u\n", auth->magic);
  80
  81        return ret < 0 ? ret : 0;
  82}
  83
  84int drm_authmagic(struct drm_device *dev, void *data,
  85                  struct drm_file *file_priv)
  86{
  87        struct drm_auth *auth = data;
  88        struct drm_file *file;
  89
  90        DRM_DEBUG("%u\n", auth->magic);
  91
  92        mutex_lock(&dev->master_mutex);
  93        file = idr_find(&file_priv->master->magic_map, auth->magic);
  94        if (file) {
  95                file->authenticated = 1;
  96                idr_replace(&file_priv->master->magic_map, NULL, auth->magic);
  97        }
  98        mutex_unlock(&dev->master_mutex);
  99
 100        return file ? 0 : -EINVAL;
 101}
 102
 103struct drm_master *drm_master_create(struct drm_device *dev)
 104{
 105        struct drm_master *master;
 106
 107        master = kzalloc(sizeof(*master), GFP_KERNEL);
 108        if (!master)
 109                return NULL;
 110
 111        kref_init(&master->refcount);
 112        drm_master_legacy_init(master);
 113        idr_init(&master->magic_map);
 114        master->dev = dev;
 115
 116        /* initialize the tree of output resource lessees */
 117        INIT_LIST_HEAD(&master->lessees);
 118        INIT_LIST_HEAD(&master->lessee_list);
 119        idr_init(&master->leases);
 120        idr_init(&master->lessee_idr);
 121
 122        return master;
 123}
 124
 125static int drm_set_master(struct drm_device *dev, struct drm_file *fpriv,
 126                          bool new_master)
 127{
 128        int ret = 0;
 129
 130        dev->master = drm_master_get(fpriv->master);
 131        if (dev->driver->master_set) {
 132                ret = dev->driver->master_set(dev, fpriv, new_master);
 133                if (unlikely(ret != 0)) {
 134                        drm_master_put(&dev->master);
 135                }
 136        }
 137
 138        return ret;
 139}
 140
 141static int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv)
 142{
 143        struct drm_master *old_master;
 144        int ret;
 145
 146        lockdep_assert_held_once(&dev->master_mutex);
 147
 148        WARN_ON(fpriv->is_master);
 149        old_master = fpriv->master;
 150        fpriv->master = drm_master_create(dev);
 151        if (!fpriv->master) {
 152                fpriv->master = old_master;
 153                return -ENOMEM;
 154        }
 155
 156        if (dev->driver->master_create) {
 157                ret = dev->driver->master_create(dev, fpriv->master);
 158                if (ret)
 159                        goto out_err;
 160        }
 161        fpriv->is_master = 1;
 162        fpriv->authenticated = 1;
 163
 164        ret = drm_set_master(dev, fpriv, true);
 165        if (ret)
 166                goto out_err;
 167
 168        if (old_master)
 169                drm_master_put(&old_master);
 170
 171        return 0;
 172
 173out_err:
 174        /* drop references and restore old master on failure */
 175        drm_master_put(&fpriv->master);
 176        fpriv->master = old_master;
 177        fpriv->is_master = 0;
 178
 179        return ret;
 180}
 181
 182int drm_setmaster_ioctl(struct drm_device *dev, void *data,
 183                        struct drm_file *file_priv)
 184{
 185        int ret = 0;
 186
 187        mutex_lock(&dev->master_mutex);
 188        if (drm_is_current_master(file_priv))
 189                goto out_unlock;
 190
 191        if (dev->master) {
 192                ret = -EINVAL;
 193                goto out_unlock;
 194        }
 195
 196        if (!file_priv->master) {
 197                ret = -EINVAL;
 198                goto out_unlock;
 199        }
 200
 201        if (!file_priv->is_master) {
 202                ret = drm_new_set_master(dev, file_priv);
 203                goto out_unlock;
 204        }
 205
 206        if (file_priv->master->lessor != NULL) {
 207                DRM_DEBUG_LEASE("Attempt to set lessee %d as master\n", file_priv->master->lessee_id);
 208                ret = -EINVAL;
 209                goto out_unlock;
 210        }
 211
 212        ret = drm_set_master(dev, file_priv, false);
 213out_unlock:
 214        mutex_unlock(&dev->master_mutex);
 215        return ret;
 216}
 217
 218static void drm_drop_master(struct drm_device *dev,
 219                            struct drm_file *fpriv)
 220{
 221        if (dev->driver->master_drop)
 222                dev->driver->master_drop(dev, fpriv);
 223        drm_master_put(&dev->master);
 224}
 225
 226int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
 227                         struct drm_file *file_priv)
 228{
 229        int ret = -EINVAL;
 230
 231        mutex_lock(&dev->master_mutex);
 232        if (!drm_is_current_master(file_priv))
 233                goto out_unlock;
 234
 235        if (!dev->master)
 236                goto out_unlock;
 237
 238        if (file_priv->master->lessor != NULL) {
 239                DRM_DEBUG_LEASE("Attempt to drop lessee %d as master\n", file_priv->master->lessee_id);
 240                ret = -EINVAL;
 241                goto out_unlock;
 242        }
 243
 244        ret = 0;
 245        drm_drop_master(dev, file_priv);
 246out_unlock:
 247        mutex_unlock(&dev->master_mutex);
 248        return ret;
 249}
 250
 251int drm_master_open(struct drm_file *file_priv)
 252{
 253        struct drm_device *dev = file_priv->minor->dev;
 254        int ret = 0;
 255
 256        /* if there is no current master make this fd it, but do not create
 257         * any master object for render clients */
 258        mutex_lock(&dev->master_mutex);
 259        if (!dev->master)
 260                ret = drm_new_set_master(dev, file_priv);
 261        else
 262                file_priv->master = drm_master_get(dev->master);
 263        mutex_unlock(&dev->master_mutex);
 264
 265        return ret;
 266}
 267
 268void drm_master_release(struct drm_file *file_priv)
 269{
 270        struct drm_device *dev = file_priv->minor->dev;
 271        struct drm_master *master = file_priv->master;
 272
 273        mutex_lock(&dev->master_mutex);
 274        if (file_priv->magic)
 275                idr_remove(&file_priv->master->magic_map, file_priv->magic);
 276
 277        if (!drm_is_current_master(file_priv))
 278                goto out;
 279
 280        drm_legacy_lock_master_cleanup(dev, master);
 281
 282        if (dev->master == file_priv->master)
 283                drm_drop_master(dev, file_priv);
 284out:
 285        if (drm_core_check_feature(dev, DRIVER_MODESET) && file_priv->is_master) {
 286                /* Revoke any leases held by this or lessees, but only if
 287                 * this is the "real" master
 288                 */
 289                drm_lease_revoke(master);
 290        }
 291
 292        /* drop the master reference held by the file priv */
 293        if (file_priv->master)
 294                drm_master_put(&file_priv->master);
 295        mutex_unlock(&dev->master_mutex);
 296}
 297
 298/**
 299 * drm_is_current_master - checks whether @priv is the current master
 300 * @fpriv: DRM file private
 301 *
 302 * Checks whether @fpriv is current master on its device. This decides whether a
 303 * client is allowed to run DRM_MASTER IOCTLs.
 304 *
 305 * Most of the modern IOCTL which require DRM_MASTER are for kernel modesetting
 306 * - the current master is assumed to own the non-shareable display hardware.
 307 */
 308bool drm_is_current_master(struct drm_file *fpriv)
 309{
 310        return fpriv->is_master && drm_lease_owner(fpriv->master) == fpriv->minor->dev->master;
 311}
 312EXPORT_SYMBOL(drm_is_current_master);
 313
 314/**
 315 * drm_master_get - reference a master pointer
 316 * @master: &struct drm_master
 317 *
 318 * Increments the reference count of @master and returns a pointer to @master.
 319 */
 320struct drm_master *drm_master_get(struct drm_master *master)
 321{
 322        kref_get(&master->refcount);
 323        return master;
 324}
 325EXPORT_SYMBOL(drm_master_get);
 326
 327static void drm_master_destroy(struct kref *kref)
 328{
 329        struct drm_master *master = container_of(kref, struct drm_master, refcount);
 330        struct drm_device *dev = master->dev;
 331
 332        if (drm_core_check_feature(dev, DRIVER_MODESET))
 333                drm_lease_destroy(master);
 334
 335        if (dev->driver->master_destroy)
 336                dev->driver->master_destroy(dev, master);
 337
 338        drm_legacy_master_rmmaps(dev, master);
 339
 340        idr_destroy(&master->magic_map);
 341        idr_destroy(&master->leases);
 342        idr_destroy(&master->lessee_idr);
 343
 344        kfree(master->unique);
 345        kfree(master);
 346}
 347
 348/**
 349 * drm_master_put - unreference and clear a master pointer
 350 * @master: pointer to a pointer of &struct drm_master
 351 *
 352 * This decrements the &drm_master behind @master and sets it to NULL.
 353 */
 354void drm_master_put(struct drm_master **master)
 355{
 356        kref_put(&(*master)->refcount, drm_master_destroy);
 357        *master = NULL;
 358}
 359EXPORT_SYMBOL(drm_master_put);
 360
 361/* Used by drm_client and drm_fb_helper */
 362bool drm_master_internal_acquire(struct drm_device *dev)
 363{
 364        mutex_lock(&dev->master_mutex);
 365        if (dev->master) {
 366                mutex_unlock(&dev->master_mutex);
 367                return false;
 368        }
 369
 370        return true;
 371}
 372EXPORT_SYMBOL(drm_master_internal_acquire);
 373
 374/* Used by drm_client and drm_fb_helper */
 375void drm_master_internal_release(struct drm_device *dev)
 376{
 377        mutex_unlock(&dev->master_mutex);
 378}
 379EXPORT_SYMBOL(drm_master_internal_release);
 380