Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 | * | |
32e7b94a DH |
8 | * Author Rickard E. (Rik) Faith <faith@valinux.com> |
9 | * Author Gareth Hughes <gareth@valinux.com> | |
10 | * | |
1da177e4 LT |
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 | ||
0500c04e SR |
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 | ||
67d0ec4e | 39 | #include "drm_internal.h" |
6548f4e7 | 40 | #include "drm_legacy.h" |
1da177e4 | 41 | |
1da177e4 | 42 | /** |
3b96a0b1 | 43 | * DOC: master and authentication |
1da177e4 | 44 | * |
ea0dd85a DV |
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 | |
3b96a0b1 DV |
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. | |
ef40cbf9 DV |
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. | |
1da177e4 | 52 | * |
3b96a0b1 DV |
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. | |
1da177e4 | 62 | */ |
3b96a0b1 | 63 | |
c153f45f | 64 | int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) |
1da177e4 | 65 | { |
c153f45f | 66 | struct drm_auth *auth = data; |
32e7b94a | 67 | int ret = 0; |
1da177e4 | 68 | |
d2b34ee6 | 69 | mutex_lock(&dev->master_mutex); |
32e7b94a DH |
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; | |
1da177e4 | 75 | } |
32e7b94a | 76 | auth->magic = file_priv->magic; |
d2b34ee6 | 77 | mutex_unlock(&dev->master_mutex); |
1da177e4 | 78 | |
c153f45f EA |
79 | DRM_DEBUG("%u\n", auth->magic); |
80 | ||
32e7b94a | 81 | return ret < 0 ? ret : 0; |
1da177e4 LT |
82 | } |
83 | ||
c153f45f EA |
84 | int drm_authmagic(struct drm_device *dev, void *data, |
85 | struct drm_file *file_priv) | |
1da177e4 | 86 | { |
c153f45f | 87 | struct drm_auth *auth = data; |
84b1fd10 | 88 | struct drm_file *file; |
1da177e4 | 89 | |
c153f45f | 90 | DRM_DEBUG("%u\n", auth->magic); |
32e7b94a | 91 | |
d2b34ee6 | 92 | mutex_lock(&dev->master_mutex); |
32e7b94a DH |
93 | file = idr_find(&file_priv->master->magic_map, auth->magic); |
94 | if (file) { | |
1da177e4 | 95 | file->authenticated = 1; |
32e7b94a | 96 | idr_replace(&file_priv->master->magic_map, NULL, auth->magic); |
1da177e4 | 97 | } |
d2b34ee6 | 98 | mutex_unlock(&dev->master_mutex); |
32e7b94a DH |
99 | |
100 | return file ? 0 : -EINVAL; | |
1da177e4 | 101 | } |
6548f4e7 | 102 | |
2ed077e4 | 103 | struct drm_master *drm_master_create(struct drm_device *dev) |
6548f4e7 DV |
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); | |
ee22f763 | 112 | drm_master_legacy_init(master); |
6548f4e7 DV |
113 | idr_init(&master->magic_map); |
114 | master->dev = dev; | |
115 | ||
2ed077e4 | 116 | /* initialize the tree of output resource lessees */ |
2ed077e4 KP |
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 | ||
6548f4e7 DV |
122 | return master; |
123 | } | |
124 | ||
d6ed682e DV |
125 | static 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); | |
d6ed682e DV |
131 | if (dev->driver->master_set) { |
132 | ret = dev->driver->master_set(dev, fpriv, new_master); | |
133 | if (unlikely(ret != 0)) { | |
d6ed682e DV |
134 | drm_master_put(&dev->master); |
135 | } | |
136 | } | |
137 | ||
138 | return ret; | |
139 | } | |
140 | ||
2cbae7e6 | 141 | static int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv) |
6548f4e7 DV |
142 | { |
143 | struct drm_master *old_master; | |
144 | int ret; | |
145 | ||
146 | lockdep_assert_held_once(&dev->master_mutex); | |
147 | ||
23a336b3 | 148 | WARN_ON(fpriv->is_master); |
6548f4e7 | 149 | old_master = fpriv->master; |
d6ed682e DV |
150 | fpriv->master = drm_master_create(dev); |
151 | if (!fpriv->master) { | |
152 | fpriv->master = old_master; | |
153 | return -ENOMEM; | |
154 | } | |
6548f4e7 | 155 | |
0aae5920 | 156 | fpriv->is_master = 1; |
6548f4e7 | 157 | fpriv->authenticated = 1; |
d6ed682e DV |
158 | |
159 | ret = drm_set_master(dev, fpriv, true); | |
160 | if (ret) | |
161 | goto out_err; | |
162 | ||
6548f4e7 DV |
163 | if (old_master) |
164 | drm_master_put(&old_master); | |
165 | ||
166 | return 0; | |
167 | ||
168 | out_err: | |
d6ed682e | 169 | /* drop references and restore old master on failure */ |
6548f4e7 DV |
170 | drm_master_put(&fpriv->master); |
171 | fpriv->master = old_master; | |
23a336b3 | 172 | fpriv->is_master = 0; |
6548f4e7 DV |
173 | |
174 | return ret; | |
175 | } | |
176 | ||
177 | int drm_setmaster_ioctl(struct drm_device *dev, void *data, | |
178 | struct drm_file *file_priv) | |
179 | { | |
180 | int ret = 0; | |
181 | ||
182 | mutex_lock(&dev->master_mutex); | |
b3ac9f25 | 183 | if (drm_is_current_master(file_priv)) |
6548f4e7 DV |
184 | goto out_unlock; |
185 | ||
95c081c1 | 186 | if (dev->master) { |
6548f4e7 DV |
187 | ret = -EINVAL; |
188 | goto out_unlock; | |
189 | } | |
190 | ||
191 | if (!file_priv->master) { | |
192 | ret = -EINVAL; | |
193 | goto out_unlock; | |
194 | } | |
195 | ||
0aae5920 | 196 | if (!file_priv->is_master) { |
6548f4e7 DV |
197 | ret = drm_new_set_master(dev, file_priv); |
198 | goto out_unlock; | |
199 | } | |
200 | ||
2ed077e4 KP |
201 | if (file_priv->master->lessor != NULL) { |
202 | DRM_DEBUG_LEASE("Attempt to set lessee %d as master\n", file_priv->master->lessee_id); | |
203 | ret = -EINVAL; | |
204 | goto out_unlock; | |
205 | } | |
206 | ||
d6ed682e | 207 | ret = drm_set_master(dev, file_priv, false); |
6548f4e7 DV |
208 | out_unlock: |
209 | mutex_unlock(&dev->master_mutex); | |
210 | return ret; | |
211 | } | |
212 | ||
d6ed682e DV |
213 | static void drm_drop_master(struct drm_device *dev, |
214 | struct drm_file *fpriv) | |
215 | { | |
216 | if (dev->driver->master_drop) | |
217 | dev->driver->master_drop(dev, fpriv); | |
218 | drm_master_put(&dev->master); | |
d6ed682e DV |
219 | } |
220 | ||
6548f4e7 DV |
221 | int drm_dropmaster_ioctl(struct drm_device *dev, void *data, |
222 | struct drm_file *file_priv) | |
223 | { | |
224 | int ret = -EINVAL; | |
225 | ||
226 | mutex_lock(&dev->master_mutex); | |
b3ac9f25 | 227 | if (!drm_is_current_master(file_priv)) |
6548f4e7 DV |
228 | goto out_unlock; |
229 | ||
95c081c1 | 230 | if (!dev->master) |
6548f4e7 DV |
231 | goto out_unlock; |
232 | ||
761e05a7 KP |
233 | if (file_priv->master->lessor != NULL) { |
234 | DRM_DEBUG_LEASE("Attempt to drop lessee %d as master\n", file_priv->master->lessee_id); | |
235 | ret = -EINVAL; | |
236 | goto out_unlock; | |
237 | } | |
238 | ||
6548f4e7 | 239 | ret = 0; |
d6ed682e | 240 | drm_drop_master(dev, file_priv); |
6548f4e7 DV |
241 | out_unlock: |
242 | mutex_unlock(&dev->master_mutex); | |
243 | return ret; | |
244 | } | |
245 | ||
2cbae7e6 DV |
246 | int drm_master_open(struct drm_file *file_priv) |
247 | { | |
248 | struct drm_device *dev = file_priv->minor->dev; | |
249 | int ret = 0; | |
250 | ||
251 | /* if there is no current master make this fd it, but do not create | |
252 | * any master object for render clients */ | |
253 | mutex_lock(&dev->master_mutex); | |
95c081c1 | 254 | if (!dev->master) |
2cbae7e6 DV |
255 | ret = drm_new_set_master(dev, file_priv); |
256 | else | |
95c081c1 | 257 | file_priv->master = drm_master_get(dev->master); |
2cbae7e6 DV |
258 | mutex_unlock(&dev->master_mutex); |
259 | ||
260 | return ret; | |
261 | } | |
262 | ||
14d71ebd DV |
263 | void drm_master_release(struct drm_file *file_priv) |
264 | { | |
265 | struct drm_device *dev = file_priv->minor->dev; | |
0de4cc99 | 266 | struct drm_master *master = file_priv->master; |
14d71ebd | 267 | |
d2b34ee6 | 268 | mutex_lock(&dev->master_mutex); |
a77316bf DV |
269 | if (file_priv->magic) |
270 | idr_remove(&file_priv->master->magic_map, file_priv->magic); | |
a77316bf | 271 | |
b3ac9f25 | 272 | if (!drm_is_current_master(file_priv)) |
0de4cc99 | 273 | goto out; |
14d71ebd | 274 | |
058ca50c | 275 | drm_legacy_lock_master_cleanup(dev, master); |
14d71ebd | 276 | |
d6ed682e DV |
277 | if (dev->master == file_priv->master) |
278 | drm_drop_master(dev, file_priv); | |
0de4cc99 | 279 | out: |
2ed077e4 KP |
280 | if (drm_core_check_feature(dev, DRIVER_MODESET) && file_priv->is_master) { |
281 | /* Revoke any leases held by this or lessees, but only if | |
282 | * this is the "real" master | |
283 | */ | |
284 | drm_lease_revoke(master); | |
285 | } | |
286 | ||
14d71ebd DV |
287 | /* drop the master reference held by the file priv */ |
288 | if (file_priv->master) | |
289 | drm_master_put(&file_priv->master); | |
14d71ebd DV |
290 | mutex_unlock(&dev->master_mutex); |
291 | } | |
292 | ||
3b96a0b1 DV |
293 | /** |
294 | * drm_is_current_master - checks whether @priv is the current master | |
295 | * @fpriv: DRM file private | |
296 | * | |
297 | * Checks whether @fpriv is current master on its device. This decides whether a | |
298 | * client is allowed to run DRM_MASTER IOCTLs. | |
299 | * | |
300 | * Most of the modern IOCTL which require DRM_MASTER are for kernel modesetting | |
301 | * - the current master is assumed to own the non-shareable display hardware. | |
302 | */ | |
b3ac9f25 DV |
303 | bool drm_is_current_master(struct drm_file *fpriv) |
304 | { | |
7de440db | 305 | return fpriv->is_master && drm_lease_owner(fpriv->master) == fpriv->minor->dev->master; |
b3ac9f25 DV |
306 | } |
307 | EXPORT_SYMBOL(drm_is_current_master); | |
308 | ||
3b96a0b1 DV |
309 | /** |
310 | * drm_master_get - reference a master pointer | |
ea0dd85a | 311 | * @master: &struct drm_master |
3b96a0b1 DV |
312 | * |
313 | * Increments the reference count of @master and returns a pointer to @master. | |
314 | */ | |
6548f4e7 DV |
315 | struct drm_master *drm_master_get(struct drm_master *master) |
316 | { | |
317 | kref_get(&master->refcount); | |
318 | return master; | |
319 | } | |
320 | EXPORT_SYMBOL(drm_master_get); | |
321 | ||
322 | static void drm_master_destroy(struct kref *kref) | |
323 | { | |
324 | struct drm_master *master = container_of(kref, struct drm_master, refcount); | |
325 | struct drm_device *dev = master->dev; | |
326 | ||
2ed077e4 KP |
327 | if (drm_core_check_feature(dev, DRIVER_MODESET)) |
328 | drm_lease_destroy(master); | |
329 | ||
6548f4e7 DV |
330 | drm_legacy_master_rmmaps(dev, master); |
331 | ||
332 | idr_destroy(&master->magic_map); | |
2ed077e4 KP |
333 | idr_destroy(&master->leases); |
334 | idr_destroy(&master->lessee_idr); | |
335 | ||
6548f4e7 DV |
336 | kfree(master->unique); |
337 | kfree(master); | |
338 | } | |
339 | ||
3b96a0b1 DV |
340 | /** |
341 | * drm_master_put - unreference and clear a master pointer | |
ea0dd85a | 342 | * @master: pointer to a pointer of &struct drm_master |
3b96a0b1 DV |
343 | * |
344 | * This decrements the &drm_master behind @master and sets it to NULL. | |
345 | */ | |
6548f4e7 DV |
346 | void drm_master_put(struct drm_master **master) |
347 | { | |
348 | kref_put(&(*master)->refcount, drm_master_destroy); | |
349 | *master = NULL; | |
350 | } | |
351 | EXPORT_SYMBOL(drm_master_put); | |
03a9606e NT |
352 | |
353 | /* Used by drm_client and drm_fb_helper */ | |
354 | bool drm_master_internal_acquire(struct drm_device *dev) | |
355 | { | |
356 | mutex_lock(&dev->master_mutex); | |
357 | if (dev->master) { | |
358 | mutex_unlock(&dev->master_mutex); | |
359 | return false; | |
360 | } | |
361 | ||
362 | return true; | |
363 | } | |
364 | EXPORT_SYMBOL(drm_master_internal_acquire); | |
365 | ||
366 | /* Used by drm_client and drm_fb_helper */ | |
367 | void drm_master_internal_release(struct drm_device *dev) | |
368 | { | |
369 | mutex_unlock(&dev->master_mutex); | |
370 | } | |
371 | EXPORT_SYMBOL(drm_master_internal_release); |