Home | History | Annotate | Line # | Download | only in drm
drm_auth.c revision 1.1.1.3
      1 /*	$NetBSD: drm_auth.c,v 1.1.1.3 2021/12/18 20:10:59 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.1.1.3 2021/12/18 20:10:59 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 	mutex_lock(&dev->master_mutex);
     75 	if (!file_priv->magic) {
     76 		ret = idr_alloc(&file_priv->master->magic_map, file_priv,
     77 				1, 0, GFP_KERNEL);
     78 		if (ret >= 0)
     79 			file_priv->magic = ret;
     80 	}
     81 	auth->magic = file_priv->magic;
     82 	mutex_unlock(&dev->master_mutex);
     83 
     84 	DRM_DEBUG("%u\n", auth->magic);
     85 
     86 	return ret < 0 ? ret : 0;
     87 }
     88 
     89 int drm_authmagic(struct drm_device *dev, void *data,
     90 		  struct drm_file *file_priv)
     91 {
     92 	struct drm_auth *auth = data;
     93 	struct drm_file *file;
     94 
     95 	DRM_DEBUG("%u\n", auth->magic);
     96 
     97 	mutex_lock(&dev->master_mutex);
     98 	file = idr_find(&file_priv->master->magic_map, auth->magic);
     99 	if (file) {
    100 		file->authenticated = 1;
    101 		idr_replace(&file_priv->master->magic_map, NULL, auth->magic);
    102 	}
    103 	mutex_unlock(&dev->master_mutex);
    104 
    105 	return file ? 0 : -EINVAL;
    106 }
    107 
    108 struct drm_master *drm_master_create(struct drm_device *dev)
    109 {
    110 	struct drm_master *master;
    111 
    112 	master = kzalloc(sizeof(*master), GFP_KERNEL);
    113 	if (!master)
    114 		return NULL;
    115 
    116 	kref_init(&master->refcount);
    117 	drm_master_legacy_init(master);
    118 	idr_init(&master->magic_map);
    119 	master->dev = dev;
    120 
    121 	/* initialize the tree of output resource lessees */
    122 	INIT_LIST_HEAD(&master->lessees);
    123 	INIT_LIST_HEAD(&master->lessee_list);
    124 	idr_init(&master->leases);
    125 	idr_init(&master->lessee_idr);
    126 
    127 	return master;
    128 }
    129 
    130 static int drm_set_master(struct drm_device *dev, struct drm_file *fpriv,
    131 			  bool new_master)
    132 {
    133 	int ret = 0;
    134 
    135 	dev->master = drm_master_get(fpriv->master);
    136 	if (dev->driver->master_set) {
    137 		ret = dev->driver->master_set(dev, fpriv, new_master);
    138 		if (unlikely(ret != 0)) {
    139 			drm_master_put(&dev->master);
    140 		}
    141 	}
    142 
    143 	return ret;
    144 }
    145 
    146 static int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv)
    147 {
    148 	struct drm_master *old_master;
    149 	int ret;
    150 
    151 	lockdep_assert_held_once(&dev->master_mutex);
    152 
    153 	WARN_ON(fpriv->is_master);
    154 	old_master = fpriv->master;
    155 	fpriv->master = drm_master_create(dev);
    156 	if (!fpriv->master) {
    157 		fpriv->master = old_master;
    158 		return -ENOMEM;
    159 	}
    160 
    161 	if (dev->driver->master_create) {
    162 		ret = dev->driver->master_create(dev, fpriv->master);
    163 		if (ret)
    164 			goto out_err;
    165 	}
    166 	fpriv->is_master = 1;
    167 	fpriv->authenticated = 1;
    168 
    169 	ret = drm_set_master(dev, fpriv, true);
    170 	if (ret)
    171 		goto out_err;
    172 
    173 	if (old_master)
    174 		drm_master_put(&old_master);
    175 
    176 	return 0;
    177 
    178 out_err:
    179 	/* drop references and restore old master on failure */
    180 	drm_master_put(&fpriv->master);
    181 	fpriv->master = old_master;
    182 	fpriv->is_master = 0;
    183 
    184 	return ret;
    185 }
    186 
    187 int drm_setmaster_ioctl(struct drm_device *dev, void *data,
    188 			struct drm_file *file_priv)
    189 {
    190 	int ret = 0;
    191 
    192 	mutex_lock(&dev->master_mutex);
    193 	if (drm_is_current_master(file_priv))
    194 		goto out_unlock;
    195 
    196 	if (dev->master) {
    197 		ret = -EINVAL;
    198 		goto out_unlock;
    199 	}
    200 
    201 	if (!file_priv->master) {
    202 		ret = -EINVAL;
    203 		goto out_unlock;
    204 	}
    205 
    206 	if (!file_priv->is_master) {
    207 		ret = drm_new_set_master(dev, file_priv);
    208 		goto out_unlock;
    209 	}
    210 
    211 	if (file_priv->master->lessor != NULL) {
    212 		DRM_DEBUG_LEASE("Attempt to set lessee %d as master\n", file_priv->master->lessee_id);
    213 		ret = -EINVAL;
    214 		goto out_unlock;
    215 	}
    216 
    217 	ret = drm_set_master(dev, file_priv, false);
    218 out_unlock:
    219 	mutex_unlock(&dev->master_mutex);
    220 	return ret;
    221 }
    222 
    223 static void drm_drop_master(struct drm_device *dev,
    224 			    struct drm_file *fpriv)
    225 {
    226 	if (dev->driver->master_drop)
    227 		dev->driver->master_drop(dev, fpriv);
    228 	drm_master_put(&dev->master);
    229 }
    230 
    231 int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
    232 			 struct drm_file *file_priv)
    233 {
    234 	int ret = -EINVAL;
    235 
    236 	mutex_lock(&dev->master_mutex);
    237 	if (!drm_is_current_master(file_priv))
    238 		goto out_unlock;
    239 
    240 	if (!dev->master)
    241 		goto out_unlock;
    242 
    243 	if (file_priv->master->lessor != NULL) {
    244 		DRM_DEBUG_LEASE("Attempt to drop lessee %d as master\n", file_priv->master->lessee_id);
    245 		ret = -EINVAL;
    246 		goto out_unlock;
    247 	}
    248 
    249 	ret = 0;
    250 	drm_drop_master(dev, file_priv);
    251 out_unlock:
    252 	mutex_unlock(&dev->master_mutex);
    253 	return ret;
    254 }
    255 
    256 int drm_master_open(struct drm_file *file_priv)
    257 {
    258 	struct drm_device *dev = file_priv->minor->dev;
    259 	int ret = 0;
    260 
    261 	/* if there is no current master make this fd it, but do not create
    262 	 * any master object for render clients */
    263 	mutex_lock(&dev->master_mutex);
    264 	if (!dev->master)
    265 		ret = drm_new_set_master(dev, file_priv);
    266 	else
    267 		file_priv->master = drm_master_get(dev->master);
    268 	mutex_unlock(&dev->master_mutex);
    269 
    270 	return ret;
    271 }
    272 
    273 void drm_master_release(struct drm_file *file_priv)
    274 {
    275 	struct drm_device *dev = file_priv->minor->dev;
    276 	struct drm_master *master = file_priv->master;
    277 
    278 	mutex_lock(&dev->master_mutex);
    279 	if (file_priv->magic)
    280 		idr_remove(&file_priv->master->magic_map, file_priv->magic);
    281 
    282 	if (!drm_is_current_master(file_priv))
    283 		goto out;
    284 
    285 	drm_legacy_lock_master_cleanup(dev, master);
    286 
    287 	if (dev->master == file_priv->master)
    288 		drm_drop_master(dev, file_priv);
    289 out:
    290 	if (drm_core_check_feature(dev, DRIVER_MODESET) && file_priv->is_master) {
    291 		/* Revoke any leases held by this or lessees, but only if
    292 		 * this is the "real" master
    293 		 */
    294 		drm_lease_revoke(master);
    295 	}
    296 
    297 	/* drop the master reference held by the file priv */
    298 	if (file_priv->master)
    299 		drm_master_put(&file_priv->master);
    300 	mutex_unlock(&dev->master_mutex);
    301 }
    302 
    303 /**
    304  * drm_is_current_master - checks whether @priv is the current master
    305  * @fpriv: DRM file private
    306  *
    307  * Checks whether @fpriv is current master on its device. This decides whether a
    308  * client is allowed to run DRM_MASTER IOCTLs.
    309  *
    310  * Most of the modern IOCTL which require DRM_MASTER are for kernel modesetting
    311  * - the current master is assumed to own the non-shareable display hardware.
    312  */
    313 bool drm_is_current_master(struct drm_file *fpriv)
    314 {
    315 	return fpriv->is_master && drm_lease_owner(fpriv->master) == fpriv->minor->dev->master;
    316 }
    317 EXPORT_SYMBOL(drm_is_current_master);
    318 
    319 /**
    320  * drm_master_get - reference a master pointer
    321  * @master: &struct drm_master
    322  *
    323  * Increments the reference count of @master and returns a pointer to @master.
    324  */
    325 struct drm_master *drm_master_get(struct drm_master *master)
    326 {
    327 	kref_get(&master->refcount);
    328 	return master;
    329 }
    330 EXPORT_SYMBOL(drm_master_get);
    331 
    332 static void drm_master_destroy(struct kref *kref)
    333 {
    334 	struct drm_master *master = container_of(kref, struct drm_master, refcount);
    335 	struct drm_device *dev = master->dev;
    336 
    337 	if (drm_core_check_feature(dev, DRIVER_MODESET))
    338 		drm_lease_destroy(master);
    339 
    340 	if (dev->driver->master_destroy)
    341 		dev->driver->master_destroy(dev, master);
    342 
    343 	drm_legacy_master_rmmaps(dev, master);
    344 
    345 	idr_destroy(&master->magic_map);
    346 	idr_destroy(&master->leases);
    347 	idr_destroy(&master->lessee_idr);
    348 
    349 	kfree(master->unique);
    350 	kfree(master);
    351 }
    352 
    353 /**
    354  * drm_master_put - unreference and clear a master pointer
    355  * @master: pointer to a pointer of &struct drm_master
    356  *
    357  * This decrements the &drm_master behind @master and sets it to NULL.
    358  */
    359 void drm_master_put(struct drm_master **master)
    360 {
    361 	kref_put(&(*master)->refcount, drm_master_destroy);
    362 	*master = NULL;
    363 }
    364 EXPORT_SYMBOL(drm_master_put);
    365 
    366 /* Used by drm_client and drm_fb_helper */
    367 bool drm_master_internal_acquire(struct drm_device *dev)
    368 {
    369 	mutex_lock(&dev->master_mutex);
    370 	if (dev->master) {
    371 		mutex_unlock(&dev->master_mutex);
    372 		return false;
    373 	}
    374 
    375 	return true;
    376 }
    377 EXPORT_SYMBOL(drm_master_internal_acquire);
    378 
    379 /* Used by drm_client and drm_fb_helper */
    380 void drm_master_internal_release(struct drm_device *dev)
    381 {
    382 	mutex_unlock(&dev->master_mutex);
    383 }
    384 EXPORT_SYMBOL(drm_master_internal_release);
    385