diff options
Diffstat (limited to 'drivers/gpu/drm/drm_auth.c')
-rw-r--r-- | drivers/gpu/drm/drm_auth.c | 285 |
1 files changed, 263 insertions, 22 deletions
diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c index 50d0baa06db0..4153e8a193af 100644 --- a/drivers/gpu/drm/drm_auth.c +++ b/drivers/gpu/drm/drm_auth.c | |||
@@ -30,25 +30,36 @@ | |||
30 | 30 | ||
31 | #include <drm/drmP.h> | 31 | #include <drm/drmP.h> |
32 | #include "drm_internal.h" | 32 | #include "drm_internal.h" |
33 | #include "drm_legacy.h" | ||
33 | 34 | ||
34 | /** | 35 | /** |
35 | * drm_getmagic - Get unique magic of a client | 36 | * DOC: master and authentication |
36 | * @dev: DRM device to operate on | ||
37 | * @data: ioctl data containing the drm_auth object | ||
38 | * @file_priv: DRM file that performs the operation | ||
39 | * | 37 | * |
40 | * This looks up the unique magic of the passed client and returns it. If the | 38 | * struct &drm_master is used to track groups of clients with open |
41 | * client did not have a magic assigned, yet, a new one is registered. The magic | 39 | * primary/legacy device nodes. For every struct &drm_file which has had at |
42 | * is stored in the passed drm_auth object. | 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. | ||
43 | * | 45 | * |
44 | * Returns: 0 on success, negative error code on failure. | 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. | ||
45 | */ | 55 | */ |
56 | |||
46 | int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) | 57 | int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) |
47 | { | 58 | { |
48 | struct drm_auth *auth = data; | 59 | struct drm_auth *auth = data; |
49 | int ret = 0; | 60 | int ret = 0; |
50 | 61 | ||
51 | mutex_lock(&dev->struct_mutex); | 62 | mutex_lock(&dev->master_mutex); |
52 | if (!file_priv->magic) { | 63 | if (!file_priv->magic) { |
53 | ret = idr_alloc(&file_priv->master->magic_map, file_priv, | 64 | ret = idr_alloc(&file_priv->master->magic_map, file_priv, |
54 | 1, 0, GFP_KERNEL); | 65 | 1, 0, GFP_KERNEL); |
@@ -56,23 +67,13 @@ int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) | |||
56 | file_priv->magic = ret; | 67 | file_priv->magic = ret; |
57 | } | 68 | } |
58 | auth->magic = file_priv->magic; | 69 | auth->magic = file_priv->magic; |
59 | mutex_unlock(&dev->struct_mutex); | 70 | mutex_unlock(&dev->master_mutex); |
60 | 71 | ||
61 | DRM_DEBUG("%u\n", auth->magic); | 72 | DRM_DEBUG("%u\n", auth->magic); |
62 | 73 | ||
63 | return ret < 0 ? ret : 0; | 74 | return ret < 0 ? ret : 0; |
64 | } | 75 | } |
65 | 76 | ||
66 | /** | ||
67 | * drm_authmagic - Authenticate client with a magic | ||
68 | * @dev: DRM device to operate on | ||
69 | * @data: ioctl data containing the drm_auth object | ||
70 | * @file_priv: DRM file that performs the operation | ||
71 | * | ||
72 | * This looks up a DRM client by the passed magic and authenticates it. | ||
73 | * | ||
74 | * Returns: 0 on success, negative error code on failure. | ||
75 | */ | ||
76 | int drm_authmagic(struct drm_device *dev, void *data, | 77 | int drm_authmagic(struct drm_device *dev, void *data, |
77 | struct drm_file *file_priv) | 78 | struct drm_file *file_priv) |
78 | { | 79 | { |
@@ -81,13 +82,253 @@ int drm_authmagic(struct drm_device *dev, void *data, | |||
81 | 82 | ||
82 | DRM_DEBUG("%u\n", auth->magic); | 83 | DRM_DEBUG("%u\n", auth->magic); |
83 | 84 | ||
84 | mutex_lock(&dev->struct_mutex); | 85 | mutex_lock(&dev->master_mutex); |
85 | file = idr_find(&file_priv->master->magic_map, auth->magic); | 86 | file = idr_find(&file_priv->master->magic_map, auth->magic); |
86 | if (file) { | 87 | if (file) { |
87 | file->authenticated = 1; | 88 | file->authenticated = 1; |
88 | idr_replace(&file_priv->master->magic_map, NULL, auth->magic); | 89 | idr_replace(&file_priv->master->magic_map, NULL, auth->magic); |
89 | } | 90 | } |
90 | mutex_unlock(&dev->struct_mutex); | 91 | mutex_unlock(&dev->master_mutex); |
91 | 92 | ||
92 | return file ? 0 : -EINVAL; | 93 | return file ? 0 : -EINVAL; |
93 | } | 94 | } |
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 | 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); | ||
119 | if (dev->driver->master_set) { | ||
120 | ret = dev->driver->master_set(dev, fpriv, new_master); | ||
121 | if (unlikely(ret != 0)) { | ||
122 | drm_master_put(&dev->master); | ||
123 | } | ||
124 | } | ||
125 | |||
126 | return ret; | ||
127 | } | ||
128 | |||
129 | static int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv) | ||
130 | { | ||
131 | struct drm_master *old_master; | ||
132 | int ret; | ||
133 | |||
134 | lockdep_assert_held_once(&dev->master_mutex); | ||
135 | |||
136 | old_master = fpriv->master; | ||
137 | fpriv->master = drm_master_create(dev); | ||
138 | if (!fpriv->master) { | ||
139 | fpriv->master = old_master; | ||
140 | return -ENOMEM; | ||
141 | } | ||
142 | |||
143 | if (dev->driver->master_create) { | ||
144 | ret = dev->driver->master_create(dev, fpriv->master); | ||
145 | if (ret) | ||
146 | goto out_err; | ||
147 | } | ||
148 | fpriv->is_master = 1; | ||
149 | fpriv->authenticated = 1; | ||
150 | |||
151 | ret = drm_set_master(dev, fpriv, true); | ||
152 | if (ret) | ||
153 | goto out_err; | ||
154 | |||
155 | if (old_master) | ||
156 | drm_master_put(&old_master); | ||
157 | |||
158 | return 0; | ||
159 | |||
160 | out_err: | ||
161 | /* drop references and restore old master on failure */ | ||
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); | ||
174 | if (drm_is_current_master(file_priv)) | ||
175 | goto out_unlock; | ||
176 | |||
177 | if (dev->master) { | ||
178 | ret = -EINVAL; | ||
179 | goto out_unlock; | ||
180 | } | ||
181 | |||
182 | if (!file_priv->master) { | ||
183 | ret = -EINVAL; | ||
184 | goto out_unlock; | ||
185 | } | ||
186 | |||
187 | if (!file_priv->is_master) { | ||
188 | ret = drm_new_set_master(dev, file_priv); | ||
189 | goto out_unlock; | ||
190 | } | ||
191 | |||
192 | ret = drm_set_master(dev, file_priv, false); | ||
193 | out_unlock: | ||
194 | mutex_unlock(&dev->master_mutex); | ||
195 | return ret; | ||
196 | } | ||
197 | |||
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); | ||
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 (!drm_is_current_master(file_priv)) | ||
213 | goto out_unlock; | ||
214 | |||
215 | if (!dev->master) | ||
216 | goto out_unlock; | ||
217 | |||
218 | ret = 0; | ||
219 | drm_drop_master(dev, file_priv); | ||
220 | out_unlock: | ||
221 | mutex_unlock(&dev->master_mutex); | ||
222 | return ret; | ||
223 | } | ||
224 | |||
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); | ||
233 | if (!dev->master) | ||
234 | ret = drm_new_set_master(dev, file_priv); | ||
235 | else | ||
236 | file_priv->master = drm_master_get(dev->master); | ||
237 | mutex_unlock(&dev->master_mutex); | ||
238 | |||
239 | return ret; | ||
240 | } | ||
241 | |||
242 | void drm_master_release(struct drm_file *file_priv) | ||
243 | { | ||
244 | struct drm_device *dev = file_priv->minor->dev; | ||
245 | struct drm_master *master = file_priv->master; | ||
246 | |||
247 | mutex_lock(&dev->master_mutex); | ||
248 | if (file_priv->magic) | ||
249 | idr_remove(&file_priv->master->magic_map, file_priv->magic); | ||
250 | |||
251 | if (!drm_is_current_master(file_priv)) | ||
252 | goto out; | ||
253 | |||
254 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) { | ||
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); | ||
268 | } | ||
269 | |||
270 | if (dev->master == file_priv->master) | ||
271 | drm_drop_master(dev, file_priv); | ||
272 | out: | ||
273 | /* drop the master reference held by the file priv */ | ||
274 | if (file_priv->master) | ||
275 | drm_master_put(&file_priv->master); | ||
276 | mutex_unlock(&dev->master_mutex); | ||
277 | } | ||
278 | |||
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 | */ | ||
289 | bool drm_is_current_master(struct drm_file *fpriv) | ||
290 | { | ||
291 | return fpriv->is_master && fpriv->master == fpriv->minor->dev->master; | ||
292 | } | ||
293 | EXPORT_SYMBOL(drm_is_current_master); | ||
294 | |||
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 | */ | ||
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 | |||
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 | */ | ||
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); | ||