1280183Sdumbbell/**
2280183Sdumbbell * \file drm_auth.c
3280183Sdumbbell * IOCTLs for authentication
4280183Sdumbbell *
5280183Sdumbbell * \author Rickard E. (Rik) Faith <faith@valinux.com>
6280183Sdumbbell * \author Gareth Hughes <gareth@valinux.com>
7280183Sdumbbell */
8280183Sdumbbell
9280183Sdumbbell/*
10280183Sdumbbell * Created: Tue Feb  2 08:37:54 1999 by faith@valinux.com
11280183Sdumbbell *
12235783Skib * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
13235783Skib * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
14235783Skib * All Rights Reserved.
15235783Skib *
16235783Skib * Permission is hereby granted, free of charge, to any person obtaining a
17235783Skib * copy of this software and associated documentation files (the "Software"),
18235783Skib * to deal in the Software without restriction, including without limitation
19235783Skib * the rights to use, copy, modify, merge, publish, distribute, sublicense,
20235783Skib * and/or sell copies of the Software, and to permit persons to whom the
21235783Skib * Software is furnished to do so, subject to the following conditions:
22235783Skib *
23235783Skib * The above copyright notice and this permission notice (including the next
24235783Skib * paragraph) shall be included in all copies or substantial portions of the
25235783Skib * Software.
26235783Skib *
27235783Skib * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28235783Skib * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29235783Skib * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
30235783Skib * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
31235783Skib * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
32235783Skib * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
33235783Skib * OTHER DEALINGS IN THE SOFTWARE.
34235783Skib */
35235783Skib
36235783Skib#include <sys/cdefs.h>
37235783Skib__FBSDID("$FreeBSD$");
38235783Skib
39235783Skib#include <dev/drm2/drmP.h>
40235783Skib
41280183Sdumbbellstatic struct mtx drm_magic_lock;
42235783Skib
43235783Skib/**
44280183Sdumbbell * Find the file with the given magic number.
45280183Sdumbbell *
46280183Sdumbbell * \param dev DRM device.
47280183Sdumbbell * \param magic magic number.
48280183Sdumbbell *
49280183Sdumbbell * Searches in drm_device::magiclist within all files with the same hash key
50280183Sdumbbell * the one with matching magic number, while holding the drm_device::struct_mutex
51280183Sdumbbell * lock.
52235783Skib */
53280183Sdumbbellstatic struct drm_file *drm_find_file(struct drm_master *master, drm_magic_t magic)
54235783Skib{
55280183Sdumbbell	struct drm_file *retval = NULL;
56280183Sdumbbell	struct drm_magic_entry *pt;
57280183Sdumbbell	struct drm_hash_item *hash;
58280183Sdumbbell	struct drm_device *dev = master->minor->dev;
59235783Skib
60280183Sdumbbell	DRM_LOCK(dev);
61280183Sdumbbell	if (!drm_ht_find_item(&master->magiclist, (unsigned long)magic, &hash)) {
62280183Sdumbbell		pt = drm_hash_entry(hash, struct drm_magic_entry, hash_item);
63280183Sdumbbell		retval = pt->priv;
64235783Skib	}
65280183Sdumbbell	DRM_UNLOCK(dev);
66280183Sdumbbell	return retval;
67235783Skib}
68235783Skib
69235783Skib/**
70280183Sdumbbell * Adds a magic number.
71280183Sdumbbell *
72280183Sdumbbell * \param dev DRM device.
73280183Sdumbbell * \param priv file private data.
74280183Sdumbbell * \param magic magic number.
75280183Sdumbbell *
76280183Sdumbbell * Creates a drm_magic_entry structure and appends to the linked list
77280183Sdumbbell * associated the magic number hash key in drm_device::magiclist, while holding
78280183Sdumbbell * the drm_device::struct_mutex lock.
79235783Skib */
80280183Sdumbbellstatic int drm_add_magic(struct drm_master *master, struct drm_file *priv,
81235783Skib			 drm_magic_t magic)
82235783Skib{
83280183Sdumbbell	struct drm_magic_entry *entry;
84280183Sdumbbell	struct drm_device *dev = master->minor->dev;
85235783Skib	DRM_DEBUG("%d\n", magic);
86235783Skib
87235783Skib	entry = malloc(sizeof(*entry), DRM_MEM_MAGIC, M_ZERO | M_NOWAIT);
88235783Skib	if (!entry)
89280183Sdumbbell		return -ENOMEM;
90280183Sdumbbell	entry->priv = priv;
91280183Sdumbbell	entry->hash_item.key = (unsigned long)magic;
92280183Sdumbbell	DRM_LOCK(dev);
93280183Sdumbbell	drm_ht_insert_item(&master->magiclist, &entry->hash_item);
94280183Sdumbbell	list_add_tail(&entry->head, &master->magicfree);
95280183Sdumbbell	DRM_UNLOCK(dev);
96235783Skib
97235783Skib	return 0;
98235783Skib}
99235783Skib
100235783Skib/**
101280183Sdumbbell * Remove a magic number.
102280183Sdumbbell *
103280183Sdumbbell * \param dev DRM device.
104280183Sdumbbell * \param magic magic number.
105280183Sdumbbell *
106280183Sdumbbell * Searches and unlinks the entry in drm_device::magiclist with the magic
107280183Sdumbbell * number hash key, while holding the drm_device::struct_mutex lock.
108235783Skib */
109280183Sdumbbellint drm_remove_magic(struct drm_master *master, drm_magic_t magic)
110235783Skib{
111280183Sdumbbell	struct drm_magic_entry *pt;
112280183Sdumbbell	struct drm_hash_item *hash;
113280183Sdumbbell	struct drm_device *dev = master->minor->dev;
114235783Skib
115235783Skib	DRM_DEBUG("%d\n", magic);
116235783Skib
117280183Sdumbbell	DRM_LOCK(dev);
118280183Sdumbbell	if (drm_ht_find_item(&master->magiclist, (unsigned long)magic, &hash)) {
119280183Sdumbbell		DRM_UNLOCK(dev);
120280183Sdumbbell		return -EINVAL;
121235783Skib	}
122280183Sdumbbell	pt = drm_hash_entry(hash, struct drm_magic_entry, hash_item);
123280183Sdumbbell	drm_ht_remove_item(&master->magiclist, hash);
124280183Sdumbbell	list_del(&pt->head);
125280183Sdumbbell	DRM_UNLOCK(dev);
126235783Skib
127280183Sdumbbell	free(pt, DRM_MEM_MAGIC);
128280183Sdumbbell
129280183Sdumbbell	return 0;
130235783Skib}
131235783Skib
132235783Skib/**
133280183Sdumbbell * Get a unique magic number (ioctl).
134235783Skib *
135280183Sdumbbell * \param inode device inode.
136280183Sdumbbell * \param file_priv DRM file private.
137280183Sdumbbell * \param cmd command.
138280183Sdumbbell * \param arg pointer to a resulting drm_auth structure.
139280183Sdumbbell * \return zero on success, or a negative number on failure.
140280183Sdumbbell *
141280183Sdumbbell * If there is a magic number in drm_file::magic then use it, otherwise
142280183Sdumbbell * searches an unique non-zero magic number and add it associating it with \p
143280183Sdumbbell * file_priv.
144280183Sdumbbell * This ioctl needs protection by the drm_global_mutex, which protects
145280183Sdumbbell * struct drm_file::magic and struct drm_magic_entry::priv.
146235783Skib */
147235783Skibint drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv)
148235783Skib{
149235783Skib	static drm_magic_t sequence = 0;
150235783Skib	struct drm_auth *auth = data;
151235783Skib
152235783Skib	/* Find unique magic */
153235783Skib	if (file_priv->magic) {
154235783Skib		auth->magic = file_priv->magic;
155235783Skib	} else {
156235783Skib		do {
157280183Sdumbbell			mtx_lock(&drm_magic_lock);
158280183Sdumbbell			if (!sequence)
159280183Sdumbbell				++sequence;	/* reserve 0 */
160280183Sdumbbell			auth->magic = sequence++;
161280183Sdumbbell			mtx_unlock(&drm_magic_lock);
162280183Sdumbbell		} while (drm_find_file(file_priv->master, auth->magic));
163235783Skib		file_priv->magic = auth->magic;
164280183Sdumbbell		drm_add_magic(file_priv->master, file_priv, auth->magic);
165235783Skib	}
166235783Skib
167235783Skib	DRM_DEBUG("%u\n", auth->magic);
168235783Skib
169235783Skib	return 0;
170235783Skib}
171235783Skib
172235783Skib/**
173280183Sdumbbell * Authenticate with a magic.
174280183Sdumbbell *
175280183Sdumbbell * \param inode device inode.
176280183Sdumbbell * \param file_priv DRM file private.
177280183Sdumbbell * \param cmd command.
178280183Sdumbbell * \param arg pointer to a drm_auth structure.
179280183Sdumbbell * \return zero if authentication successed, or a negative number otherwise.
180280183Sdumbbell *
181280183Sdumbbell * Checks if \p file_priv is associated with the magic number passed in \arg.
182280183Sdumbbell * This ioctl needs protection by the drm_global_mutex, which protects
183280183Sdumbbell * struct drm_file::magic and struct drm_magic_entry::priv.
184235783Skib */
185235783Skibint drm_authmagic(struct drm_device *dev, void *data,
186235783Skib		  struct drm_file *file_priv)
187235783Skib{
188235783Skib	struct drm_auth *auth = data;
189280183Sdumbbell	struct drm_file *file;
190235783Skib
191235783Skib	DRM_DEBUG("%u\n", auth->magic);
192280183Sdumbbell	if ((file = drm_find_file(file_priv->master, auth->magic))) {
193280183Sdumbbell		file->authenticated = 1;
194280183Sdumbbell		drm_remove_magic(file_priv->master, auth->magic);
195235783Skib		return 0;
196235783Skib	}
197280183Sdumbbell	return -EINVAL;
198235783Skib}
199280183Sdumbbell
200280183Sdumbbellstatic int
201280183Sdumbbelldrm_magic_init(void *arg)
202280183Sdumbbell{
203280183Sdumbbell
204280183Sdumbbell	mtx_init(&drm_magic_lock, "drm_getmagic__lock", NULL, MTX_DEF);
205280183Sdumbbell	return (0);
206280183Sdumbbell}
207280183Sdumbbell
208280183Sdumbbellstatic void
209280183Sdumbbelldrm_magic_fini(void *arg)
210280183Sdumbbell{
211280183Sdumbbell
212280183Sdumbbell	mtx_destroy(&drm_magic_lock);
213280183Sdumbbell}
214280183Sdumbbell
215280183SdumbbellSYSINIT(drm_magic_init, SI_SUB_KLD, SI_ORDER_MIDDLE, drm_magic_init, NULL);
216280183SdumbbellSYSUNINIT(drm_magic_fini, SI_SUB_KLD, SI_ORDER_MIDDLE, drm_magic_fini, NULL);
217