drm_cdevsw.c revision 1.17 1 /* $NetBSD: drm_cdevsw.c,v 1.17 2021/12/19 00:28:20 riastradh Exp $ */
2
3 /*-
4 * Copyright (c) 2013 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Taylor R. Campbell.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: drm_cdevsw.c,v 1.17 2021/12/19 00:28:20 riastradh Exp $");
34
35 #include <sys/param.h>
36 #include <sys/types.h>
37 #include <sys/conf.h>
38 #include <sys/device.h>
39 #include <sys/file.h>
40 #include <sys/filedesc.h>
41 #include <sys/ioccom.h>
42 #include <sys/kauth.h>
43 #ifndef _MODULE
44 /* XXX Mega-kludge because modules are broken. */
45 #include <sys/once.h>
46 #endif
47 #include <sys/pmf.h>
48 #include <sys/poll.h>
49 #ifndef _MODULE
50 #include <sys/reboot.h> /* XXX drm_init kludge */
51 #endif
52 #include <sys/select.h>
53
54 #include <uvm/uvm_extern.h>
55
56 #include <linux/err.h>
57
58 #include <linux/pm.h>
59
60 #include <drm/drmP.h>
61
62 #include "../dist/drm/drm_internal.h"
63 #include "../dist/drm/drm_legacy.h"
64
65 static dev_type_open(drm_open);
66
67 static int drm_firstopen(struct drm_device *);
68
69 static int drm_close(struct file *);
70 static int drm_read(struct file *, off_t *, struct uio *, kauth_cred_t,
71 int);
72 static int drm_dequeue_event(struct drm_file *, size_t,
73 struct drm_pending_event **, int);
74 static int drm_ioctl_shim(struct file *, unsigned long, void *);
75 static int drm_poll(struct file *, int);
76 static int drm_kqfilter(struct file *, struct knote *);
77 static int drm_stat(struct file *, struct stat *);
78 static int drm_fop_mmap(struct file *, off_t *, size_t, int, int *, int *,
79 struct uvm_object **, int *);
80 static paddr_t drm_legacy_mmap(dev_t, off_t, int);
81
82 const struct cdevsw drm_cdevsw = {
83 .d_open = drm_open,
84 .d_close = noclose,
85 .d_read = noread,
86 .d_write = nowrite,
87 .d_ioctl = noioctl,
88 .d_stop = nostop,
89 .d_tty = notty,
90 .d_poll = nopoll,
91 .d_mmap = drm_legacy_mmap,
92 .d_kqfilter = nokqfilter,
93 .d_discard = nodiscard,
94 /* XXX was D_TTY | D_NEGOFFSAFE */
95 /* XXX Add D_MPSAFE some day... */
96 .d_flag = D_NEGOFFSAFE,
97 };
98
99 static const struct fileops drm_fileops = {
100 .fo_name = "drm",
101 .fo_read = drm_read,
102 .fo_write = fbadop_write,
103 .fo_ioctl = drm_ioctl_shim,
104 .fo_fcntl = fnullop_fcntl,
105 .fo_poll = drm_poll,
106 .fo_stat = drm_stat,
107 .fo_close = drm_close,
108 .fo_kqfilter = drm_kqfilter,
109 .fo_restart = fnullop_restart,
110 .fo_mmap = drm_fop_mmap,
111 };
112
113 static int
114 drm_open(dev_t d, int flags, int fmt, struct lwp *l)
115 {
116 struct drm_minor *dminor;
117 struct drm_device *dev;
118 bool firstopen, lastclose;
119 int fd;
120 struct file *fp;
121 int error;
122
123 error = drm_guarantee_initialized();
124 if (error)
125 goto fail0;
126
127 if (flags & O_EXCL) {
128 error = EBUSY;
129 goto fail0;
130 }
131
132 dminor = drm_minor_acquire(minor(d));
133 if (IS_ERR(dminor)) {
134 /* XXX errno Linux->NetBSD */
135 error = -PTR_ERR(dminor);
136 goto fail0;
137 }
138 dev = dminor->dev;
139 if (dev->switch_power_state != DRM_SWITCH_POWER_ON) {
140 error = EINVAL;
141 goto fail1;
142 }
143
144 mutex_lock(&drm_global_mutex);
145 if (dev->open_count == INT_MAX) {
146 mutex_unlock(&drm_global_mutex);
147 error = EBUSY;
148 goto fail1;
149 }
150 firstopen = (dev->open_count == 0);
151 dev->open_count++;
152 mutex_unlock(&drm_global_mutex);
153
154 if (firstopen) {
155 /* XXX errno Linux->NetBSD */
156 error = -drm_firstopen(dev);
157 if (error)
158 goto fail2;
159 }
160
161 error = fd_allocfile(&fp, &fd);
162 if (error)
163 goto fail2;
164
165 struct drm_file *const file = kmem_zalloc(sizeof(*file), KM_SLEEP);
166 /* XXX errno Linux->NetBSD */
167 error = -drm_open_file(file, fp, dminor);
168 if (error)
169 goto fail3;
170
171 error = fd_clone(fp, fd, flags, &drm_fileops, file);
172 KASSERT(error == EMOVEFD); /* XXX */
173
174 /* Success! (But error has to be EMOVEFD, not 0.) */
175 return error;
176
177 fail3: kmem_free(file, sizeof(*file));
178 fd_abort(curproc, fp, fd);
179 fail2: mutex_lock(&drm_global_mutex);
180 KASSERT(0 < dev->open_count);
181 --dev->open_count;
182 lastclose = (dev->open_count == 0);
183 mutex_unlock(&drm_global_mutex);
184 if (lastclose)
185 (void)drm_lastclose(dev);
186 fail1: drm_minor_release(dminor);
187 fail0: KASSERT(error);
188 if (error == ERESTARTSYS)
189 error = ERESTART;
190 return error;
191 }
192
193 static int
194 drm_close(struct file *fp)
195 {
196 struct drm_file *const file = fp->f_data;
197 struct drm_minor *const dminor = file->minor;
198 struct drm_device *const dev = dminor->dev;
199 bool lastclose;
200
201 drm_close_file(file);
202 kmem_free(file, sizeof(*file));
203
204 mutex_lock(&drm_global_mutex);
205 KASSERT(0 < dev->open_count);
206 --dev->open_count;
207 lastclose = (dev->open_count == 0);
208 mutex_unlock(&drm_global_mutex);
209
210 if (lastclose)
211 (void)drm_lastclose(dev);
212
213 drm_minor_release(dminor);
214
215 return 0;
216 }
217
218 static int
219 drm_firstopen(struct drm_device *dev)
220 {
221 int ret;
222
223 if (drm_core_check_feature(dev, DRIVER_MODESET))
224 return 0;
225
226 if (dev->driver->firstopen) {
227 ret = (*dev->driver->firstopen)(dev);
228 if (ret)
229 goto fail0;
230 }
231
232 ret = drm_legacy_dma_setup(dev);
233 if (ret)
234 goto fail1;
235
236 return 0;
237
238 fail2: __unused
239 drm_legacy_dma_takedown(dev);
240 fail1: if (dev->driver->lastclose)
241 (*dev->driver->lastclose)(dev);
242 fail0: KASSERT(ret);
243 return ret;
244 }
245
246 int
247 drm_lastclose(struct drm_device *dev)
248 {
249
250 /* XXX Order is sketchy here... */
251 if (dev->driver->lastclose)
252 (*dev->driver->lastclose)(dev);
253 if (dev->irq_enabled && !drm_core_check_feature(dev, DRIVER_MODESET))
254 drm_irq_uninstall(dev);
255
256 mutex_lock(&dev->struct_mutex);
257 if (dev->agp)
258 drm_agp_clear(dev);
259 drm_legacy_sg_cleanup(dev);
260 drm_legacy_dma_takedown(dev);
261 mutex_unlock(&dev->struct_mutex);
262
263 /* XXX Synchronize with drm_legacy_dev_reinit. */
264 if (!drm_core_check_feature(dev, DRIVER_MODESET)) {
265 dev->sigdata.lock = NULL;
266 dev->context_flag = 0;
267 dev->last_context = 0;
268 dev->if_version = 0;
269 }
270
271 return 0;
272 }
273
274 static int
275 drm_read(struct file *fp, off_t *off, struct uio *uio, kauth_cred_t cred,
276 int flags)
277 {
278 struct drm_file *const file = fp->f_data;
279 struct drm_pending_event *event;
280 bool first;
281 int error = 0;
282
283 for (first = true; ; first = false) {
284 int f = 0;
285
286 if (!first || ISSET(fp->f_flag, FNONBLOCK))
287 f |= FNONBLOCK;
288
289 /* XXX errno Linux->NetBSD */
290 error = -drm_dequeue_event(file, uio->uio_resid, &event, f);
291 if (error) {
292 if ((error == EWOULDBLOCK) && !first)
293 error = 0;
294 break;
295 }
296 if (event == NULL)
297 break;
298 error = uiomove(event->event, event->event->length, uio);
299 if (error) /* XXX Requeue the event? */
300 break;
301 (*event->destroy)(event);
302 }
303
304 /* Success! */
305 if (error == ERESTARTSYS)
306 error = ERESTART;
307 return error;
308 }
309
310 static int
311 drm_dequeue_event(struct drm_file *file, size_t max_length,
312 struct drm_pending_event **eventp, int flags)
313 {
314 struct drm_device *const dev = file->minor->dev;
315 struct drm_pending_event *event = NULL;
316 unsigned long irqflags;
317 int ret = 0;
318
319 spin_lock_irqsave(&dev->event_lock, irqflags);
320
321 if (ISSET(flags, FNONBLOCK)) {
322 if (list_empty(&file->event_list))
323 ret = -EWOULDBLOCK;
324 } else {
325 DRM_SPIN_WAIT_UNTIL(ret, &file->event_wait, &dev->event_lock,
326 !list_empty(&file->event_list));
327 }
328 if (ret)
329 goto out;
330
331 event = list_first_entry(&file->event_list, struct drm_pending_event,
332 link);
333 if (event->event->length > max_length) {
334 /* Event is too large, can't return it. */
335 event = NULL;
336 ret = 0;
337 goto out;
338 }
339
340 file->event_space += event->event->length;
341 list_del(&event->link);
342
343 out: spin_unlock_irqrestore(&dev->event_lock, irqflags);
344 *eventp = event;
345 return ret;
346 }
347
348 static int
349 drm_ioctl_shim(struct file *fp, unsigned long cmd, void *data)
350 {
351 struct drm_file *file = fp->f_data;
352 struct drm_driver *driver = file->minor->dev->driver;
353 int error;
354
355 if (driver->ioctl_override)
356 error = driver->ioctl_override(fp, cmd, data);
357 else
358 error = drm_ioctl(fp, cmd, data);
359 if (error == ERESTARTSYS)
360 error = ERESTART;
361
362 return error;
363 }
364
365 static int
366 drm_poll(struct file *fp __unused, int events __unused)
367 {
368 struct drm_file *const file = fp->f_data;
369 struct drm_device *const dev = file->minor->dev;
370 int revents = 0;
371 unsigned long irqflags;
372
373 if (!ISSET(events, (POLLIN | POLLRDNORM)))
374 return 0;
375
376 spin_lock_irqsave(&dev->event_lock, irqflags);
377 if (list_empty(&file->event_list))
378 selrecord(curlwp, &file->event_selq);
379 else
380 revents |= (events & (POLLIN | POLLRDNORM));
381 spin_unlock_irqrestore(&dev->event_lock, irqflags);
382
383 return revents;
384 }
385
386 static void filt_drm_detach(struct knote *);
387 static int filt_drm_event(struct knote *, long);
388
389 static const struct filterops drm_filtops = {
390 .f_flags = FILTEROP_ISFD,
391 .f_attach = NULL,
392 .f_detach = filt_drm_detach,
393 .f_event = filt_drm_event,
394 };
395
396 static int
397 drm_kqfilter(struct file *fp, struct knote *kn)
398 {
399 struct drm_file *const file = fp->f_data;
400 struct drm_device *const dev = file->minor->dev;
401 unsigned long irqflags;
402
403 switch (kn->kn_filter) {
404 case EVFILT_READ:
405 kn->kn_fop = &drm_filtops;
406 kn->kn_hook = file;
407 spin_lock_irqsave(&dev->event_lock, irqflags);
408 selrecord_knote(&file->event_selq, kn);
409 spin_unlock_irqrestore(&dev->event_lock, irqflags);
410 return 0;
411 case EVFILT_WRITE:
412 default:
413 return EINVAL;
414 }
415 }
416
417 static void
418 filt_drm_detach(struct knote *kn)
419 {
420 struct drm_file *const file = kn->kn_hook;
421 struct drm_device *const dev = file->minor->dev;
422 unsigned long irqflags;
423
424 spin_lock_irqsave(&dev->event_lock, irqflags);
425 selremove_knote(&file->event_selq, kn);
426 spin_unlock_irqrestore(&dev->event_lock, irqflags);
427 }
428
429 static int
430 filt_drm_event(struct knote *kn, long hint)
431 {
432 struct drm_file *const file = kn->kn_hook;
433 struct drm_device *const dev = file->minor->dev;
434 unsigned long irqflags;
435 int ret;
436
437 if (hint == NOTE_SUBMIT)
438 KASSERT(spin_is_locked(&dev->event_lock));
439 else
440 spin_lock_irqsave(&dev->event_lock, irqflags);
441 if (list_empty(&file->event_list)) {
442 ret = 0;
443 } else {
444 struct drm_pending_event *const event =
445 list_first_entry(&file->event_list,
446 struct drm_pending_event, link);
447 kn->kn_data = event->event->length;
448 ret = 1;
449 }
450 if (hint == NOTE_SUBMIT)
451 KASSERT(spin_is_locked(&dev->event_lock));
452 else
453 spin_unlock_irqrestore(&dev->event_lock, irqflags);
454
455 return ret;
456 }
457
458 static int
459 drm_stat(struct file *fp, struct stat *st)
460 {
461 struct drm_file *const file = fp->f_data;
462 struct drm_minor *const dminor = file->minor;
463 const dev_t devno = makedev(cdevsw_lookup_major(&drm_cdevsw),
464 64*dminor->type + dminor->index);
465
466 (void)memset(st, 0, sizeof(*st));
467
468 st->st_dev = devno;
469 st->st_ino = 0; /* XXX (dev,ino) uniqueness bleh */
470 st->st_uid = kauth_cred_geteuid(fp->f_cred);
471 st->st_gid = kauth_cred_getegid(fp->f_cred);
472 st->st_mode = S_IFCHR; /* XXX what? */
473 st->st_rdev = devno;
474 /* XXX what else? */
475
476 return 0;
477 }
478
479 static int
480 drm_fop_mmap(struct file *fp, off_t *offp, size_t len, int prot, int *flagsp,
481 int *advicep, struct uvm_object **uobjp, int *maxprotp)
482 {
483 struct drm_file *const file = fp->f_data;
484 struct drm_device *const dev = file->minor->dev;
485 int error;
486
487 KASSERT(fp == file->filp);
488 /* XXX errno Linux->NetBSD */
489 error = -(*dev->driver->mmap_object)(dev, *offp, len, prot, uobjp,
490 offp, file->filp);
491 *maxprotp = prot;
492 *advicep = UVM_ADV_RANDOM;
493 if (error == ERESTARTSYS)
494 error = ERESTART;
495 return error;
496 }
497
498 static paddr_t
499 drm_legacy_mmap(dev_t d, off_t offset, int prot)
500 {
501 struct drm_minor *dminor;
502 paddr_t paddr;
503
504 dminor = drm_minor_acquire(minor(d));
505 if (IS_ERR(dminor))
506 return (paddr_t)-1;
507
508 paddr = drm_legacy_mmap_paddr(dminor->dev, offset, prot);
509
510 drm_minor_release(dminor);
511 return paddr;
512 }
513