]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Created: Tue Feb 2 08:37:54 1999 by [email protected] | |
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 DR |
8 | * Author Rickard E. (Rik) Faith <[email protected]> |
9 | * Author Gareth Hughes <[email protected]> | |
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 | ||
760285e7 | 31 | #include <drm/drmP.h> |
67d0ec4e | 32 | #include "drm_internal.h" |
6548f4e7 | 33 | #include "drm_legacy.h" |
1da177e4 | 34 | |
1da177e4 | 35 | /** |
3b96a0b1 | 36 | * DOC: master and authentication |
1da177e4 | 37 | * |
3b96a0b1 SV |
38 | * struct &drm_master is used to track groups of clients with open |
39 | * primary/legacy device nodes. For every struct &drm_file which has had at | |
40 | * least once successfully became the device master (either through the | |
41 | * SET_MASTER IOCTL, or implicitly through opening the primary device node when | |
42 | * no one else is the current master that time) there exists one &drm_master. | |
43 | * This is noted in the is_master member of &drm_file. All other clients have | |
44 | * just a pointer to the &drm_master they are associated with. | |
1da177e4 | 45 | * |
3b96a0b1 SV |
46 | * In addition only one &drm_master can be the current master for a &drm_device. |
47 | * It can be switched through the DROP_MASTER and SET_MASTER IOCTL, or | |
48 | * implicitly through closing/openeing the primary device node. See also | |
49 | * drm_is_current_master(). | |
50 | * | |
51 | * Clients can authenticate against the current master (if it matches their own) | |
52 | * using the GETMAGIC and AUTHMAGIC IOCTLs. Together with exchanging masters, | |
53 | * this allows controlled access to the device for an entire group of mutually | |
54 | * trusted clients. | |
1da177e4 | 55 | */ |
3b96a0b1 | 56 | |
c153f45f | 57 | int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) |
1da177e4 | 58 | { |
c153f45f | 59 | struct drm_auth *auth = data; |
32e7b94a | 60 | int ret = 0; |
1da177e4 | 61 | |
d2b34ee6 | 62 | mutex_lock(&dev->master_mutex); |
32e7b94a DR |
63 | if (!file_priv->magic) { |
64 | ret = idr_alloc(&file_priv->master->magic_map, file_priv, | |
65 | 1, 0, GFP_KERNEL); | |
66 | if (ret >= 0) | |
67 | file_priv->magic = ret; | |
1da177e4 | 68 | } |
32e7b94a | 69 | auth->magic = file_priv->magic; |
d2b34ee6 | 70 | mutex_unlock(&dev->master_mutex); |
1da177e4 | 71 | |
c153f45f EA |
72 | DRM_DEBUG("%u\n", auth->magic); |
73 | ||
32e7b94a | 74 | return ret < 0 ? ret : 0; |
1da177e4 LT |
75 | } |
76 | ||
c153f45f EA |
77 | int drm_authmagic(struct drm_device *dev, void *data, |
78 | struct drm_file *file_priv) | |
1da177e4 | 79 | { |
c153f45f | 80 | struct drm_auth *auth = data; |
84b1fd10 | 81 | struct drm_file *file; |
1da177e4 | 82 | |
c153f45f | 83 | DRM_DEBUG("%u\n", auth->magic); |
32e7b94a | 84 | |
d2b34ee6 | 85 | mutex_lock(&dev->master_mutex); |
32e7b94a DR |
86 | file = idr_find(&file_priv->master->magic_map, auth->magic); |
87 | if (file) { | |
1da177e4 | 88 | file->authenticated = 1; |
32e7b94a | 89 | idr_replace(&file_priv->master->magic_map, NULL, auth->magic); |
1da177e4 | 90 | } |
d2b34ee6 | 91 | mutex_unlock(&dev->master_mutex); |
32e7b94a DR |
92 | |
93 | return file ? 0 : -EINVAL; | |
1da177e4 | 94 | } |
6548f4e7 SV |
95 | |
96 | static struct drm_master *drm_master_create(struct drm_device *dev) | |
97 | { | |
98 | struct drm_master *master; | |
99 | ||
100 | master = kzalloc(sizeof(*master), GFP_KERNEL); | |
101 | if (!master) | |
102 | return NULL; | |
103 | ||
104 | kref_init(&master->refcount); | |
105 | spin_lock_init(&master->lock.spinlock); | |
106 | init_waitqueue_head(&master->lock.lock_queue); | |
107 | idr_init(&master->magic_map); | |
108 | master->dev = dev; | |
109 | ||
110 | return master; | |
111 | } | |
112 | ||
d6ed682e SV |
113 | static int drm_set_master(struct drm_device *dev, struct drm_file *fpriv, |
114 | bool new_master) | |
115 | { | |
116 | int ret = 0; | |
117 | ||
118 | dev->master = drm_master_get(fpriv->master); | |
d6ed682e SV |
119 | if (dev->driver->master_set) { |
120 | ret = dev->driver->master_set(dev, fpriv, new_master); | |
121 | if (unlikely(ret != 0)) { | |
d6ed682e SV |
122 | drm_master_put(&dev->master); |
123 | } | |
124 | } | |
125 | ||
126 | return ret; | |
127 | } | |
128 | ||
2cbae7e6 | 129 | static int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv) |
6548f4e7 SV |
130 | { |
131 | struct drm_master *old_master; | |
132 | int ret; | |
133 | ||
134 | lockdep_assert_held_once(&dev->master_mutex); | |
135 | ||
6548f4e7 | 136 | old_master = fpriv->master; |
d6ed682e SV |
137 | fpriv->master = drm_master_create(dev); |
138 | if (!fpriv->master) { | |
139 | fpriv->master = old_master; | |
140 | return -ENOMEM; | |
141 | } | |
6548f4e7 SV |
142 | |
143 | if (dev->driver->master_create) { | |
144 | ret = dev->driver->master_create(dev, fpriv->master); | |
145 | if (ret) | |
146 | goto out_err; | |
147 | } | |
0aae5920 | 148 | fpriv->is_master = 1; |
6548f4e7 | 149 | fpriv->authenticated = 1; |
d6ed682e SV |
150 | |
151 | ret = drm_set_master(dev, fpriv, true); | |
152 | if (ret) | |
153 | goto out_err; | |
154 | ||
6548f4e7 SV |
155 | if (old_master) |
156 | drm_master_put(&old_master); | |
157 | ||
158 | return 0; | |
159 | ||
160 | out_err: | |
d6ed682e | 161 | /* drop references and restore old master on failure */ |
6548f4e7 SV |
162 | drm_master_put(&fpriv->master); |
163 | fpriv->master = old_master; | |
164 | ||
165 | return ret; | |
166 | } | |
167 | ||
168 | int drm_setmaster_ioctl(struct drm_device *dev, void *data, | |
169 | struct drm_file *file_priv) | |
170 | { | |
171 | int ret = 0; | |
172 | ||
173 | mutex_lock(&dev->master_mutex); | |
b3ac9f25 | 174 | if (drm_is_current_master(file_priv)) |
6548f4e7 SV |
175 | goto out_unlock; |
176 | ||
95c081c1 | 177 | if (dev->master) { |
6548f4e7 SV |
178 | ret = -EINVAL; |
179 | goto out_unlock; | |
180 | } | |
181 | ||
182 | if (!file_priv->master) { | |
183 | ret = -EINVAL; | |
184 | goto out_unlock; | |
185 | } | |
186 | ||
0aae5920 | 187 | if (!file_priv->is_master) { |
6548f4e7 SV |
188 | ret = drm_new_set_master(dev, file_priv); |
189 | goto out_unlock; | |
190 | } | |
191 | ||
d6ed682e | 192 | ret = drm_set_master(dev, file_priv, false); |
6548f4e7 SV |
193 | out_unlock: |
194 | mutex_unlock(&dev->master_mutex); | |
195 | return ret; | |
196 | } | |
197 | ||
d6ed682e SV |
198 | static void drm_drop_master(struct drm_device *dev, |
199 | struct drm_file *fpriv) | |
200 | { | |
201 | if (dev->driver->master_drop) | |
202 | dev->driver->master_drop(dev, fpriv); | |
203 | drm_master_put(&dev->master); | |
d6ed682e SV |
204 | } |
205 | ||
6548f4e7 SV |
206 | int drm_dropmaster_ioctl(struct drm_device *dev, void *data, |
207 | struct drm_file *file_priv) | |
208 | { | |
209 | int ret = -EINVAL; | |
210 | ||
211 | mutex_lock(&dev->master_mutex); | |
b3ac9f25 | 212 | if (!drm_is_current_master(file_priv)) |
6548f4e7 SV |
213 | goto out_unlock; |
214 | ||
95c081c1 | 215 | if (!dev->master) |
6548f4e7 SV |
216 | goto out_unlock; |
217 | ||
218 | ret = 0; | |
d6ed682e | 219 | drm_drop_master(dev, file_priv); |
6548f4e7 SV |
220 | out_unlock: |
221 | mutex_unlock(&dev->master_mutex); | |
222 | return ret; | |
223 | } | |
224 | ||
2cbae7e6 SV |
225 | int drm_master_open(struct drm_file *file_priv) |
226 | { | |
227 | struct drm_device *dev = file_priv->minor->dev; | |
228 | int ret = 0; | |
229 | ||
230 | /* if there is no current master make this fd it, but do not create | |
231 | * any master object for render clients */ | |
232 | mutex_lock(&dev->master_mutex); | |
95c081c1 | 233 | if (!dev->master) |
2cbae7e6 SV |
234 | ret = drm_new_set_master(dev, file_priv); |
235 | else | |
95c081c1 | 236 | file_priv->master = drm_master_get(dev->master); |
2cbae7e6 SV |
237 | mutex_unlock(&dev->master_mutex); |
238 | ||
239 | return ret; | |
240 | } | |
241 | ||
14d71ebd SV |
242 | void drm_master_release(struct drm_file *file_priv) |
243 | { | |
244 | struct drm_device *dev = file_priv->minor->dev; | |
0de4cc99 | 245 | struct drm_master *master = file_priv->master; |
14d71ebd | 246 | |
d2b34ee6 | 247 | mutex_lock(&dev->master_mutex); |
a77316bf SV |
248 | if (file_priv->magic) |
249 | idr_remove(&file_priv->master->magic_map, file_priv->magic); | |
a77316bf | 250 | |
b3ac9f25 | 251 | if (!drm_is_current_master(file_priv)) |
0de4cc99 | 252 | goto out; |
14d71ebd | 253 | |
0de4cc99 | 254 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) { |
14d71ebd SV |
255 | /* |
256 | * Since the master is disappearing, so is the | |
257 | * possibility to lock. | |
258 | */ | |
259 | mutex_lock(&dev->struct_mutex); | |
260 | if (master->lock.hw_lock) { | |
261 | if (dev->sigdata.lock == master->lock.hw_lock) | |
262 | dev->sigdata.lock = NULL; | |
263 | master->lock.hw_lock = NULL; | |
264 | master->lock.file_priv = NULL; | |
265 | wake_up_interruptible_all(&master->lock.lock_queue); | |
266 | } | |
267 | mutex_unlock(&dev->struct_mutex); | |
14d71ebd SV |
268 | } |
269 | ||
d6ed682e SV |
270 | if (dev->master == file_priv->master) |
271 | drm_drop_master(dev, file_priv); | |
0de4cc99 | 272 | out: |
14d71ebd SV |
273 | /* drop the master reference held by the file priv */ |
274 | if (file_priv->master) | |
275 | drm_master_put(&file_priv->master); | |
14d71ebd SV |
276 | mutex_unlock(&dev->master_mutex); |
277 | } | |
278 | ||
3b96a0b1 SV |
279 | /** |
280 | * drm_is_current_master - checks whether @priv is the current master | |
281 | * @fpriv: DRM file private | |
282 | * | |
283 | * Checks whether @fpriv is current master on its device. This decides whether a | |
284 | * client is allowed to run DRM_MASTER IOCTLs. | |
285 | * | |
286 | * Most of the modern IOCTL which require DRM_MASTER are for kernel modesetting | |
287 | * - the current master is assumed to own the non-shareable display hardware. | |
288 | */ | |
b3ac9f25 SV |
289 | bool drm_is_current_master(struct drm_file *fpriv) |
290 | { | |
0aae5920 | 291 | return fpriv->is_master && fpriv->master == fpriv->minor->dev->master; |
b3ac9f25 SV |
292 | } |
293 | EXPORT_SYMBOL(drm_is_current_master); | |
294 | ||
3b96a0b1 SV |
295 | /** |
296 | * drm_master_get - reference a master pointer | |
297 | * @master: struct &drm_master | |
298 | * | |
299 | * Increments the reference count of @master and returns a pointer to @master. | |
300 | */ | |
6548f4e7 SV |
301 | struct drm_master *drm_master_get(struct drm_master *master) |
302 | { | |
303 | kref_get(&master->refcount); | |
304 | return master; | |
305 | } | |
306 | EXPORT_SYMBOL(drm_master_get); | |
307 | ||
308 | static void drm_master_destroy(struct kref *kref) | |
309 | { | |
310 | struct drm_master *master = container_of(kref, struct drm_master, refcount); | |
311 | struct drm_device *dev = master->dev; | |
312 | ||
313 | if (dev->driver->master_destroy) | |
314 | dev->driver->master_destroy(dev, master); | |
315 | ||
316 | drm_legacy_master_rmmaps(dev, master); | |
317 | ||
318 | idr_destroy(&master->magic_map); | |
319 | kfree(master->unique); | |
320 | kfree(master); | |
321 | } | |
322 | ||
3b96a0b1 SV |
323 | /** |
324 | * drm_master_put - unreference and clear a master pointer | |
325 | * @master: pointer to a pointer of struct &drm_master | |
326 | * | |
327 | * This decrements the &drm_master behind @master and sets it to NULL. | |
328 | */ | |
6548f4e7 SV |
329 | void drm_master_put(struct drm_master **master) |
330 | { | |
331 | kref_put(&(*master)->refcount, drm_master_destroy); | |
332 | *master = NULL; | |
333 | } | |
334 | EXPORT_SYMBOL(drm_master_put); |