1 /* $NetBSD: drm_cdevsw.c,v 1.15 2020/12/19 22:09:15 thorpej 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.15 2020/12/19 22:09:15 thorpej 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 #include <drm/drm_internal.h> 62 #include "../dist/drm/drm_legacy.h" 63 64 static dev_type_open(drm_open); 65 66 static int drm_firstopen(struct drm_device *); 67 68 static int drm_close(struct file *); 69 static int drm_read(struct file *, off_t *, struct uio *, kauth_cred_t, 70 int); 71 static int drm_dequeue_event(struct drm_file *, size_t, 72 struct drm_pending_event **, int); 73 static int drm_ioctl_shim(struct file *, unsigned long, void *); 74 static int drm_poll(struct file *, int); 75 static int drm_kqfilter(struct file *, struct knote *); 76 static int drm_stat(struct file *, struct stat *); 77 static int drm_fop_mmap(struct file *, off_t *, size_t, int, int *, int *, 78 struct uvm_object **, int *); 79 static paddr_t drm_legacy_mmap(dev_t, off_t, int); 80 81 const struct cdevsw drm_cdevsw = { 82 .d_open = drm_open, 83 .d_close = noclose, 84 .d_read = noread, 85 .d_write = nowrite, 86 .d_ioctl = noioctl, 87 .d_stop = nostop, 88 .d_tty = notty, 89 .d_poll = nopoll, 90 .d_mmap = drm_legacy_mmap, 91 .d_kqfilter = nokqfilter, 92 .d_discard = nodiscard, 93 /* XXX was D_TTY | D_NEGOFFSAFE */ 94 /* XXX Add D_MPSAFE some day... */ 95 .d_flag = D_NEGOFFSAFE, 96 }; 97 98 static const struct fileops drm_fileops = { 99 .fo_name = "drm", 100 .fo_read = drm_read, 101 .fo_write = fbadop_write, 102 .fo_ioctl = drm_ioctl_shim, 103 .fo_fcntl = fnullop_fcntl, 104 .fo_poll = drm_poll, 105 .fo_stat = drm_stat, 106 .fo_close = drm_close, 107 .fo_kqfilter = drm_kqfilter, 108 .fo_restart = fnullop_restart, 109 .fo_mmap = drm_fop_mmap, 110 }; 111 112 static int 113 drm_open(dev_t d, int flags, int fmt, struct lwp *l) 114 { 115 struct drm_minor *dminor; 116 struct drm_device *dev; 117 bool firstopen, lastclose; 118 int fd; 119 struct file *fp; 120 int error; 121 122 error = drm_guarantee_initialized(); 123 if (error) 124 goto fail0; 125 126 if (flags & O_EXCL) { 127 error = EBUSY; 128 goto fail0; 129 } 130 131 dminor = drm_minor_acquire(minor(d)); 132 if (IS_ERR(dminor)) { 133 /* XXX errno Linux->NetBSD */ 134 error = -PTR_ERR(dminor); 135 goto fail0; 136 } 137 dev = dminor->dev; 138 if (dev->switch_power_state != DRM_SWITCH_POWER_ON) { 139 error = EINVAL; 140 goto fail1; 141 } 142 143 mutex_lock(&drm_global_mutex); 144 if (dev->open_count == INT_MAX) { 145 mutex_unlock(&drm_global_mutex); 146 error = EBUSY; 147 goto fail1; 148 } 149 firstopen = (dev->open_count == 0); 150 dev->open_count++; 151 mutex_unlock(&drm_global_mutex); 152 153 if (firstopen) { 154 /* XXX errno Linux->NetBSD */ 155 error = -drm_firstopen(dev); 156 if (error) 157 goto fail2; 158 } 159 160 error = fd_allocfile(&fp, &fd); 161 if (error) 162 goto fail2; 163 164 struct drm_file *const file = kmem_zalloc(sizeof(*file), KM_SLEEP); 165 /* XXX errno Linux->NetBSD */ 166 error = -drm_open_file(file, fp, dminor); 167 if (error) 168 goto fail3; 169 170 error = fd_clone(fp, fd, flags, &drm_fileops, file); 171 KASSERT(error == EMOVEFD); /* XXX */ 172 173 /* Success! (But error has to be EMOVEFD, not 0.) */ 174 return error; 175 176 fail3: kmem_free(file, sizeof(*file)); 177 fd_abort(curproc, fp, fd); 178 fail2: mutex_lock(&drm_global_mutex); 179 KASSERT(0 < dev->open_count); 180 --dev->open_count; 181 lastclose = (dev->open_count == 0); 182 mutex_unlock(&drm_global_mutex); 183 if (lastclose) 184 (void)drm_lastclose(dev); 185 fail1: drm_minor_release(dminor); 186 fail0: KASSERT(error); 187 if (error == ERESTARTSYS) 188 error = ERESTART; 189 return error; 190 } 191 192 static int 193 drm_close(struct file *fp) 194 { 195 struct drm_file *const file = fp->f_data; 196 struct drm_minor *const dminor = file->minor; 197 struct drm_device *const dev = dminor->dev; 198 bool lastclose; 199 200 drm_close_file(file); 201 kmem_free(file, sizeof(*file)); 202 203 mutex_lock(&drm_global_mutex); 204 KASSERT(0 < dev->open_count); 205 --dev->open_count; 206 lastclose = (dev->open_count == 0); 207 mutex_unlock(&drm_global_mutex); 208 209 if (lastclose) 210 (void)drm_lastclose(dev); 211 212 drm_minor_release(dminor); 213 214 return 0; 215 } 216 217 static int 218 drm_firstopen(struct drm_device *dev) 219 { 220 int ret; 221 222 if (drm_core_check_feature(dev, DRIVER_MODESET)) 223 return 0; 224 225 if (dev->driver->firstopen) { 226 ret = (*dev->driver->firstopen)(dev); 227 if (ret) 228 goto fail0; 229 } 230 231 ret = drm_legacy_dma_setup(dev); 232 if (ret) 233 goto fail1; 234 235 return 0; 236 237 fail2: __unused 238 drm_legacy_dma_takedown(dev); 239 fail1: if (dev->driver->lastclose) 240 (*dev->driver->lastclose)(dev); 241 fail0: KASSERT(ret); 242 return ret; 243 } 244 245 int 246 drm_lastclose(struct drm_device *dev) 247 { 248 249 /* XXX Order is sketchy here... */ 250 if (dev->driver->lastclose) 251 (*dev->driver->lastclose)(dev); 252 if (dev->irq_enabled && !drm_core_check_feature(dev, DRIVER_MODESET)) 253 drm_irq_uninstall(dev); 254 255 mutex_lock(&dev->struct_mutex); 256 if (dev->agp) 257 drm_agp_clear(dev); 258 drm_legacy_sg_cleanup(dev); 259 drm_legacy_dma_takedown(dev); 260 mutex_unlock(&dev->struct_mutex); 261 262 /* XXX Synchronize with drm_legacy_dev_reinit. */ 263 if (!drm_core_check_feature(dev, DRIVER_MODESET)) { 264 dev->sigdata.lock = NULL; 265 dev->context_flag = 0; 266 dev->last_context = 0; 267 dev->if_version = 0; 268 } 269 270 return 0; 271 } 272 273 static int 274 drm_read(struct file *fp, off_t *off, struct uio *uio, kauth_cred_t cred, 275 int flags) 276 { 277 struct drm_file *const file = fp->f_data; 278 struct drm_pending_event *event; 279 bool first; 280 int error = 0; 281 282 for (first = true; ; first = false) { 283 int f = 0; 284 285 if (!first || ISSET(fp->f_flag, FNONBLOCK)) 286 f |= FNONBLOCK; 287 288 /* XXX errno Linux->NetBSD */ 289 error = -drm_dequeue_event(file, uio->uio_resid, &event, f); 290 if (error) { 291 if ((error == EWOULDBLOCK) && !first) 292 error = 0; 293 break; 294 } 295 if (event == NULL) 296 break; 297 error = uiomove(event->event, event->event->length, uio); 298 if (error) /* XXX Requeue the event? */ 299 break; 300 (*event->destroy)(event); 301 } 302 303 /* Success! */ 304 if (error == ERESTARTSYS) 305 error = ERESTART; 306 return error; 307 } 308 309 static int 310 drm_dequeue_event(struct drm_file *file, size_t max_length, 311 struct drm_pending_event **eventp, int flags) 312 { 313 struct drm_device *const dev = file->minor->dev; 314 struct drm_pending_event *event = NULL; 315 unsigned long irqflags; 316 int ret = 0; 317 318 spin_lock_irqsave(&dev->event_lock, irqflags); 319 320 if (ISSET(flags, FNONBLOCK)) { 321 if (list_empty(&file->event_list)) 322 ret = -EWOULDBLOCK; 323 } else { 324 DRM_SPIN_WAIT_UNTIL(ret, &file->event_wait, &dev->event_lock, 325 !list_empty(&file->event_list)); 326 } 327 if (ret) 328 goto out; 329 330 event = list_first_entry(&file->event_list, struct drm_pending_event, 331 link); 332 if (event->event->length > max_length) { 333 /* Event is too large, can't return it. */ 334 event = NULL; 335 ret = 0; 336 goto out; 337 } 338 339 file->event_space += event->event->length; 340 list_del(&event->link); 341 342 out: spin_unlock_irqrestore(&dev->event_lock, irqflags); 343 *eventp = event; 344 return ret; 345 } 346 347 static int 348 drm_ioctl_shim(struct file *fp, unsigned long cmd, void *data) 349 { 350 struct drm_file *file = fp->f_data; 351 struct drm_driver *driver = file->minor->dev->driver; 352 int error; 353 354 if (driver->ioctl_override) 355 error = driver->ioctl_override(fp, cmd, data); 356 else 357 error = drm_ioctl(fp, cmd, data); 358 if (error == ERESTARTSYS) 359 error = ERESTART; 360 361 return error; 362 } 363 364 static int 365 drm_poll(struct file *fp __unused, int events __unused) 366 { 367 struct drm_file *const file = fp->f_data; 368 struct drm_device *const dev = file->minor->dev; 369 int revents = 0; 370 unsigned long irqflags; 371 372 if (!ISSET(events, (POLLIN | POLLRDNORM))) 373 return 0; 374 375 spin_lock_irqsave(&dev->event_lock, irqflags); 376 if (list_empty(&file->event_list)) 377 selrecord(curlwp, &file->event_selq); 378 else 379 revents |= (events & (POLLIN | POLLRDNORM)); 380 spin_unlock_irqrestore(&dev->event_lock, irqflags); 381 382 return revents; 383 } 384 385 static void filt_drm_detach(struct knote *); 386 static int filt_drm_event(struct knote *, long); 387 388 static const struct filterops drm_filtops = { 389 .f_isfd = 1, 390 .f_attach = NULL, 391 .f_detach = filt_drm_detach, 392 .f_event = filt_drm_event, 393 }; 394 395 static int 396 drm_kqfilter(struct file *fp, struct knote *kn) 397 { 398 struct drm_file *const file = fp->f_data; 399 struct drm_device *const dev = file->minor->dev; 400 unsigned long irqflags; 401 402 switch (kn->kn_filter) { 403 case EVFILT_READ: 404 kn->kn_fop = &drm_filtops; 405 kn->kn_hook = file; 406 spin_lock_irqsave(&dev->event_lock, irqflags); 407 selrecord_knote(&file->event_selq, kn); 408 spin_unlock_irqrestore(&dev->event_lock, irqflags); 409 return 0; 410 case EVFILT_WRITE: 411 default: 412 return EINVAL; 413 } 414 } 415 416 static void 417 filt_drm_detach(struct knote *kn) 418 { 419 struct drm_file *const file = kn->kn_hook; 420 struct drm_device *const dev = file->minor->dev; 421 unsigned long irqflags; 422 423 spin_lock_irqsave(&dev->event_lock, irqflags); 424 selremove_knote(&file->event_selq, kn); 425 spin_unlock_irqrestore(&dev->event_lock, irqflags); 426 } 427 428 static int 429 filt_drm_event(struct knote *kn, long hint) 430 { 431 struct drm_file *const file = kn->kn_hook; 432 struct drm_device *const dev = file->minor->dev; 433 unsigned long irqflags; 434 int ret; 435 436 if (hint == NOTE_SUBMIT) 437 KASSERT(spin_is_locked(&dev->event_lock)); 438 else 439 spin_lock_irqsave(&dev->event_lock, irqflags); 440 if (list_empty(&file->event_list)) { 441 ret = 0; 442 } else { 443 struct drm_pending_event *const event = 444 list_first_entry(&file->event_list, 445 struct drm_pending_event, link); 446 kn->kn_data = event->event->length; 447 ret = 1; 448 } 449 if (hint == NOTE_SUBMIT) 450 KASSERT(spin_is_locked(&dev->event_lock)); 451 else 452 spin_unlock_irqrestore(&dev->event_lock, irqflags); 453 454 return ret; 455 } 456 457 static int 458 drm_stat(struct file *fp, struct stat *st) 459 { 460 struct drm_file *const file = fp->f_data; 461 struct drm_minor *const dminor = file->minor; 462 const dev_t devno = makedev(cdevsw_lookup_major(&drm_cdevsw), 463 64*dminor->type + dminor->index); 464 465 (void)memset(st, 0, sizeof(*st)); 466 467 st->st_dev = devno; 468 st->st_ino = 0; /* XXX (dev,ino) uniqueness bleh */ 469 st->st_uid = kauth_cred_geteuid(fp->f_cred); 470 st->st_gid = kauth_cred_getegid(fp->f_cred); 471 st->st_mode = S_IFCHR; /* XXX what? */ 472 st->st_rdev = devno; 473 /* XXX what else? */ 474 475 return 0; 476 } 477 478 static int 479 drm_fop_mmap(struct file *fp, off_t *offp, size_t len, int prot, int *flagsp, 480 int *advicep, struct uvm_object **uobjp, int *maxprotp) 481 { 482 struct drm_file *const file = fp->f_data; 483 struct drm_device *const dev = file->minor->dev; 484 int error; 485 486 KASSERT(fp == file->filp); 487 /* XXX errno Linux->NetBSD */ 488 error = -(*dev->driver->mmap_object)(dev, *offp, len, prot, uobjp, 489 offp, file->filp); 490 *maxprotp = prot; 491 *advicep = UVM_ADV_RANDOM; 492 if (error == ERESTARTSYS) 493 error = ERESTART; 494 return error; 495 } 496 497 static paddr_t 498 drm_legacy_mmap(dev_t d, off_t offset, int prot) 499 { 500 struct drm_minor *dminor; 501 paddr_t paddr; 502 503 dminor = drm_minor_acquire(minor(d)); 504 if (IS_ERR(dminor)) 505 return (paddr_t)-1; 506 507 paddr = drm_legacy_mmap_paddr(dminor->dev, offset, prot); 508 509 drm_minor_release(dminor); 510 return paddr; 511 } 512