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