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 | ||
760285e7 | 31 | #include <drm/drmP.h> |
67d0ec4e | 32 | #include "drm_internal.h" |
6548f4e7 | 33 | #include "drm_legacy.h" |
1da177e4 | 34 | |
1da177e4 | 35 | /** |
32e7b94a DH |
36 | * drm_getmagic - Get unique magic of a client |
37 | * @dev: DRM device to operate on | |
38 | * @data: ioctl data containing the drm_auth object | |
39 | * @file_priv: DRM file that performs the operation | |
1da177e4 | 40 | * |
32e7b94a DH |
41 | * This looks up the unique magic of the passed client and returns it. If the |
42 | * client did not have a magic assigned, yet, a new one is registered. The magic | |
43 | * is stored in the passed drm_auth object. | |
1da177e4 | 44 | * |
32e7b94a | 45 | * Returns: 0 on success, negative error code on failure. |
1da177e4 | 46 | */ |
c153f45f | 47 | int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) |
1da177e4 | 48 | { |
c153f45f | 49 | struct drm_auth *auth = data; |
32e7b94a | 50 | int ret = 0; |
1da177e4 | 51 | |
32e7b94a DH |
52 | mutex_lock(&dev->struct_mutex); |
53 | if (!file_priv->magic) { | |
54 | ret = idr_alloc(&file_priv->master->magic_map, file_priv, | |
55 | 1, 0, GFP_KERNEL); | |
56 | if (ret >= 0) | |
57 | file_priv->magic = ret; | |
1da177e4 | 58 | } |
32e7b94a DH |
59 | auth->magic = file_priv->magic; |
60 | mutex_unlock(&dev->struct_mutex); | |
1da177e4 | 61 | |
c153f45f EA |
62 | DRM_DEBUG("%u\n", auth->magic); |
63 | ||
32e7b94a | 64 | return ret < 0 ? ret : 0; |
1da177e4 LT |
65 | } |
66 | ||
67 | /** | |
32e7b94a DH |
68 | * drm_authmagic - Authenticate client with a magic |
69 | * @dev: DRM device to operate on | |
70 | * @data: ioctl data containing the drm_auth object | |
71 | * @file_priv: DRM file that performs the operation | |
1da177e4 | 72 | * |
32e7b94a | 73 | * This looks up a DRM client by the passed magic and authenticates it. |
1da177e4 | 74 | * |
32e7b94a | 75 | * Returns: 0 on success, negative error code on failure. |
1da177e4 | 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 DH |
84 | |
85 | mutex_lock(&dev->struct_mutex); | |
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 | } |
32e7b94a DH |
91 | mutex_unlock(&dev->struct_mutex); |
92 | ||
93 | return file ? 0 : -EINVAL; | |
1da177e4 | 94 | } |
6548f4e7 DV |
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 | ||
113 | /* | |
114 | * drm_new_set_master - Allocate a new master object and become master for the | |
115 | * associated master realm. | |
116 | * | |
117 | * @dev: The associated device. | |
118 | * @fpriv: File private identifying the client. | |
119 | * | |
120 | * This function must be called with dev::struct_mutex held. | |
121 | * Returns negative error code on failure. Zero on success. | |
122 | */ | |
2cbae7e6 | 123 | static int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv) |
6548f4e7 DV |
124 | { |
125 | struct drm_master *old_master; | |
126 | int ret; | |
127 | ||
128 | lockdep_assert_held_once(&dev->master_mutex); | |
129 | ||
130 | /* create a new master */ | |
131 | fpriv->minor->master = drm_master_create(fpriv->minor->dev); | |
132 | if (!fpriv->minor->master) | |
133 | return -ENOMEM; | |
134 | ||
135 | /* take another reference for the copy in the local file priv */ | |
136 | old_master = fpriv->master; | |
137 | fpriv->master = drm_master_get(fpriv->minor->master); | |
138 | ||
139 | if (dev->driver->master_create) { | |
140 | ret = dev->driver->master_create(dev, fpriv->master); | |
141 | if (ret) | |
142 | goto out_err; | |
143 | } | |
144 | if (dev->driver->master_set) { | |
145 | ret = dev->driver->master_set(dev, fpriv, true); | |
146 | if (ret) | |
147 | goto out_err; | |
148 | } | |
149 | ||
150 | fpriv->is_master = 1; | |
151 | fpriv->allowed_master = 1; | |
152 | fpriv->authenticated = 1; | |
153 | if (old_master) | |
154 | drm_master_put(&old_master); | |
155 | ||
156 | return 0; | |
157 | ||
158 | out_err: | |
159 | /* drop both references and restore old master on failure */ | |
160 | drm_master_put(&fpriv->minor->master); | |
161 | drm_master_put(&fpriv->master); | |
162 | fpriv->master = old_master; | |
163 | ||
164 | return ret; | |
165 | } | |
166 | ||
167 | int drm_setmaster_ioctl(struct drm_device *dev, void *data, | |
168 | struct drm_file *file_priv) | |
169 | { | |
170 | int ret = 0; | |
171 | ||
172 | mutex_lock(&dev->master_mutex); | |
173 | if (file_priv->is_master) | |
174 | goto out_unlock; | |
175 | ||
176 | if (file_priv->minor->master) { | |
177 | ret = -EINVAL; | |
178 | goto out_unlock; | |
179 | } | |
180 | ||
181 | if (!file_priv->master) { | |
182 | ret = -EINVAL; | |
183 | goto out_unlock; | |
184 | } | |
185 | ||
186 | if (!file_priv->allowed_master) { | |
187 | ret = drm_new_set_master(dev, file_priv); | |
188 | goto out_unlock; | |
189 | } | |
190 | ||
191 | file_priv->minor->master = drm_master_get(file_priv->master); | |
192 | file_priv->is_master = 1; | |
193 | if (dev->driver->master_set) { | |
194 | ret = dev->driver->master_set(dev, file_priv, false); | |
195 | if (unlikely(ret != 0)) { | |
196 | file_priv->is_master = 0; | |
197 | drm_master_put(&file_priv->minor->master); | |
198 | } | |
199 | } | |
200 | ||
201 | out_unlock: | |
202 | mutex_unlock(&dev->master_mutex); | |
203 | return ret; | |
204 | } | |
205 | ||
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); | |
212 | if (!file_priv->is_master) | |
213 | goto out_unlock; | |
214 | ||
215 | if (!file_priv->minor->master) | |
216 | goto out_unlock; | |
217 | ||
218 | ret = 0; | |
219 | if (dev->driver->master_drop) | |
220 | dev->driver->master_drop(dev, file_priv, false); | |
221 | drm_master_put(&file_priv->minor->master); | |
222 | file_priv->is_master = 0; | |
223 | ||
224 | out_unlock: | |
225 | mutex_unlock(&dev->master_mutex); | |
226 | return ret; | |
227 | } | |
228 | ||
2cbae7e6 DV |
229 | int drm_master_open(struct drm_file *file_priv) |
230 | { | |
231 | struct drm_device *dev = file_priv->minor->dev; | |
232 | int ret = 0; | |
233 | ||
234 | /* if there is no current master make this fd it, but do not create | |
235 | * any master object for render clients */ | |
236 | mutex_lock(&dev->master_mutex); | |
237 | if (!file_priv->minor->master) | |
238 | ret = drm_new_set_master(dev, file_priv); | |
239 | else | |
240 | file_priv->master = drm_master_get(file_priv->minor->master); | |
241 | mutex_unlock(&dev->master_mutex); | |
242 | ||
243 | return ret; | |
244 | } | |
245 | ||
6548f4e7 DV |
246 | struct drm_master *drm_master_get(struct drm_master *master) |
247 | { | |
248 | kref_get(&master->refcount); | |
249 | return master; | |
250 | } | |
251 | EXPORT_SYMBOL(drm_master_get); | |
252 | ||
253 | static void drm_master_destroy(struct kref *kref) | |
254 | { | |
255 | struct drm_master *master = container_of(kref, struct drm_master, refcount); | |
256 | struct drm_device *dev = master->dev; | |
257 | ||
258 | if (dev->driver->master_destroy) | |
259 | dev->driver->master_destroy(dev, master); | |
260 | ||
261 | drm_legacy_master_rmmaps(dev, master); | |
262 | ||
263 | idr_destroy(&master->magic_map); | |
264 | kfree(master->unique); | |
265 | kfree(master); | |
266 | } | |
267 | ||
268 | void drm_master_put(struct drm_master **master) | |
269 | { | |
270 | kref_put(&(*master)->refcount, drm_master_destroy); | |
271 | *master = NULL; | |
272 | } | |
273 | EXPORT_SYMBOL(drm_master_put); |