1 /* 2 * This file contains routines to perform character device operations. 3 * Character drivers may suspend I/O requests on their devices (read, write, 4 * ioctl), as well as select requests. These requests will therefore suspend 5 * their calling process, freeing up the associated VFS worker thread for other 6 * tasks. The I/O requests may later be cancelled as a result of the suspended 7 * process receiving a signal (which it either catches or dies from), in which 8 * case there will be a worker thread associated with the cancellation. Open 9 * and close requests may not suspend and will thus block the calling thread. 10 * 11 * The entry points in this file are: 12 * cdev_map: map a character device to its actual device number 13 * cdev_open: open a character device 14 * cdev_close: close a character device 15 * cdev_io: initiate a read, write, or ioctl to a character device 16 * cdev_select: initiate a select call on a device 17 * cdev_cancel: cancel an I/O request, blocking until it has been cancelled 18 * cdev_reply: process the result of a character driver request 19 */ 20 21 #include "fs.h" 22 #include "vnode.h" 23 #include "file.h" 24 #include <string.h> 25 #include <fcntl.h> 26 #include <sys/ttycom.h> 27 #include <assert.h> 28 29 /* 30 * Map the given device number to a real device number, remapping /dev/tty to 31 * the given process's controlling terminal if it has one. Perform a bounds 32 * check on the resulting device's major number, and return NO_DEV on failure. 33 * This function is idempotent but not used that way. 34 */ 35 dev_t 36 cdev_map(dev_t dev, struct fproc * rfp) 37 { 38 devmajor_t major; 39 40 /* 41 * First cover one special case: /dev/tty, the magic device that 42 * translates to the controlling TTY. 43 */ 44 if ((major = major(dev)) == CTTY_MAJOR) { 45 /* No controlling terminal? Fail the request. */ 46 if (rfp->fp_tty == NO_DEV) return NO_DEV; 47 48 /* Substitute the controlling terminal device. */ 49 dev = rfp->fp_tty; 50 major = major(dev); 51 } 52 53 if (major < 0 || major >= NR_DEVICES) return NO_DEV; 54 55 return dev; 56 } 57 58 /* 59 * Obtain the dmap structure for the given device, if a valid driver exists for 60 * the major device. Perform redirection for CTTY_MAJOR. 61 */ 62 static struct dmap * 63 cdev_get(dev_t dev, devminor_t * minor_dev) 64 { 65 struct dmap *dp; 66 int slot; 67 68 /* 69 * Remap /dev/tty as needed. Perform a bounds check on the major 70 * number. 71 */ 72 if ((dev = cdev_map(dev, fp)) == NO_DEV) 73 return NULL; 74 75 /* Determine the driver endpoint. */ 76 dp = &dmap[major(dev)]; 77 78 /* See if driver is roughly valid. */ 79 if (dp->dmap_driver == NONE) return NULL; 80 81 if (isokendpt(dp->dmap_driver, &slot) != OK) { 82 printf("VFS: cdev_get: old driver for major %x (%d)\n", 83 major(dev), dp->dmap_driver); 84 return NULL; 85 } 86 87 /* Also return the (possibly redirected) minor number. */ 88 *minor_dev = minor(dev); 89 return dp; 90 } 91 92 /* 93 * A new minor device number has been returned. Request PFS to create a 94 * temporary device file to hold it. 95 */ 96 static int 97 cdev_clone(int fd, dev_t dev, devminor_t new_minor) 98 { 99 struct vnode *vp; 100 struct node_details res; 101 int r; 102 103 assert(fd != -1); 104 105 /* Device number of the new device. */ 106 dev = makedev(major(dev), new_minor); 107 108 /* Create a new file system node on PFS for the cloned device. */ 109 r = req_newnode(PFS_PROC_NR, fp->fp_effuid, fp->fp_effgid, 110 RWX_MODES | I_CHAR_SPECIAL, dev, &res); 111 if (r != OK) { 112 (void)cdev_close(dev); 113 return r; 114 } 115 116 /* Drop the old node and use the new values. */ 117 if ((vp = get_free_vnode()) == NULL) { 118 req_putnode(PFS_PROC_NR, res.inode_nr, 1); /* is this right? */ 119 (void)cdev_close(dev); 120 return err_code; 121 } 122 lock_vnode(vp, VNODE_OPCL); 123 124 assert(fp->fp_filp[fd] != NULL); 125 unlock_vnode(fp->fp_filp[fd]->filp_vno); 126 put_vnode(fp->fp_filp[fd]->filp_vno); 127 128 vp->v_fs_e = res.fs_e; 129 vp->v_vmnt = NULL; 130 vp->v_dev = NO_DEV; 131 vp->v_inode_nr = res.inode_nr; 132 vp->v_mode = res.fmode; 133 vp->v_sdev = dev; 134 vp->v_fs_count = 1; 135 vp->v_ref_count = 1; 136 fp->fp_filp[fd]->filp_vno = vp; 137 138 return OK; 139 } 140 141 /* 142 * Open or close a character device. The given operation must be either 143 * CDEV_OPEN or CDEV_CLOSE. For CDEV_OPEN, 'fd' must be the file descriptor 144 * for the file being opened; for CDEV_CLOSE, it is ignored. For CDEV_OPEN, 145 * 'flags' identifies a bitwise combination of R_BIT, W_BIT, and/or O_NOCTTY; 146 * for CDEV_CLOSE, it too is ignored. 147 */ 148 static int 149 cdev_opcl(int op, dev_t dev, int fd, int flags) 150 { 151 devminor_t minor_dev, new_minor; 152 struct dmap *dp; 153 struct fproc *rfp; 154 message dev_mess; 155 int r, r2, acc; 156 157 /* 158 * We need the a descriptor for CDEV_OPEN, because if the driver 159 * returns a cloned device, we need to replace what the fd points to. 160 * For CDEV_CLOSE however, we may be closing a device for which the 161 * calling process has no file descriptor, and thus we expect no 162 * meaningful fd value in that case. 163 */ 164 assert(op == CDEV_OPEN || op == CDEV_CLOSE); 165 assert(fd != -1 || op == CDEV_CLOSE); 166 167 /* Determine task dmap. */ 168 if ((dp = cdev_get(dev, &minor_dev)) == NULL) 169 return ENXIO; 170 171 /* 172 * CTTY exception: do not actually send the open/close request for 173 * /dev/tty to the driver. This avoids the case that the actual device 174 * will remain open forever if the process calls setsid() after opening 175 * /dev/tty. 176 */ 177 if (major(dev) == CTTY_MAJOR) return OK; 178 179 /* 180 * Add O_NOCTTY to the access flags if this process is not a session 181 * leader, or if it already has a controlling tty, or if it is someone 182 * else's controlling tty. For performance reasons, only search the 183 * full process table if this driver has set controlling TTYs before. 184 */ 185 if (!(fp->fp_flags & FP_SESLDR) || fp->fp_tty != 0) { 186 flags |= O_NOCTTY; 187 } else if (!(flags & O_NOCTTY) && dp->dmap_seen_tty) { 188 for (rfp = &fproc[0]; rfp < &fproc[NR_PROCS]; rfp++) 189 if (rfp->fp_pid != PID_FREE && rfp->fp_tty == dev) 190 flags |= O_NOCTTY; 191 } 192 193 /* Prepare the request message. */ 194 memset(&dev_mess, 0, sizeof(dev_mess)); 195 dev_mess.m_type = op; 196 dev_mess.m_vfs_lchardriver_openclose.minor = minor_dev; 197 dev_mess.m_vfs_lchardriver_openclose.id = who_e; 198 if (op == CDEV_OPEN) { 199 acc = 0; 200 if (flags & R_BIT) acc |= CDEV_R_BIT; 201 if (flags & W_BIT) acc |= CDEV_W_BIT; 202 if (flags & O_NOCTTY) acc |= CDEV_NOCTTY; 203 dev_mess.m_vfs_lchardriver_openclose.user = who_e; 204 dev_mess.m_vfs_lchardriver_openclose.access = acc; 205 } 206 207 /* Send the request to the driver. */ 208 if ((r = asynsend3(dp->dmap_driver, &dev_mess, AMF_NOREPLY)) != OK) 209 panic("VFS: asynsend in cdev_opcl failed: %d", r); 210 211 /* Block the thread waiting for a reply. */ 212 self->w_task = dp->dmap_driver; 213 self->w_drv_sendrec = &dev_mess; 214 215 worker_wait(); 216 217 self->w_task = NONE; 218 assert(self->w_drv_sendrec == NULL); 219 220 /* Process the reply. */ 221 r = dev_mess.m_lchardriver_vfs_reply.status; 222 223 if (op == CDEV_OPEN && r >= 0) { 224 /* 225 * Some devices need special processing upon open. Such a 226 * device is "cloned", i.e., on a succesful open it is replaced 227 * by a new device with a new unique minor device number. This 228 * new device number identifies a new object that has been 229 * allocated within a driver. 230 */ 231 if (r & CDEV_CLONED) { 232 new_minor = r & ~(CDEV_CLONED | CDEV_CTTY); 233 if ((r2 = cdev_clone(fd, dev, new_minor)) < 0) 234 return r2; 235 } 236 237 /* Did this call make the TTY the controlling TTY? */ 238 if (r & CDEV_CTTY) { 239 fp->fp_tty = dev; 240 dp->dmap_seen_tty = TRUE; 241 } 242 243 r = OK; 244 } 245 246 /* Return the result from the driver. */ 247 return r; 248 } 249 250 /* 251 * Open a character device. 252 */ 253 int 254 cdev_open(int fd, dev_t dev, int flags) 255 { 256 257 return cdev_opcl(CDEV_OPEN, dev, fd, flags); 258 } 259 260 /* 261 * Close a character device. 262 */ 263 int 264 cdev_close(dev_t dev) 265 { 266 267 return cdev_opcl(CDEV_CLOSE, dev, -1, 0); 268 } 269 270 /* 271 * Initiate a read, write, or ioctl to a character device. The given operation 272 * must be CDEV_READ, CDEV_WRITE, or CDEV_IOCTL. The call is made on behalf of 273 * user process 'proc_e'. For read/write requests, 'bytes' is the number of 274 * bytes to read into 'buf' at file position 'pos'. For ioctl requests, 275 * 'bytes' is actually an IOCTL request code, which implies the size of the 276 * buffer 'buf' if needed for the request at all ('pos' is ignored here). The 277 * 'flags' field contains file pointer flags, from which O_NONBLOCK is tested. 278 */ 279 int 280 cdev_io(int op, dev_t dev, endpoint_t proc_e, vir_bytes buf, off_t pos, 281 unsigned long bytes, int flags) 282 { 283 devminor_t minor_dev; 284 struct dmap *dp; 285 message dev_mess; 286 cp_grant_id_t gid; 287 int r; 288 289 assert(op == CDEV_READ || op == CDEV_WRITE || op == CDEV_IOCTL); 290 291 /* Determine task map. */ 292 if ((dp = cdev_get(dev, &minor_dev)) == NULL) 293 return EIO; 294 295 /* 296 * Handle TIOCSCTTY ioctl: set controlling TTY. FIXME: this should not 297 * hardcode major device numbers, and not assume that the IOCTL request 298 * succeeds! 299 */ 300 if (op == CDEV_IOCTL && bytes == TIOCSCTTY && 301 (major(dev) == TTY_MAJOR || major(dev) == PTY_MAJOR)) { 302 fp->fp_tty = dev; 303 } 304 305 /* Create a grant for the buffer provided by the user process. */ 306 if (op != CDEV_IOCTL) { 307 gid = cpf_grant_magic(dp->dmap_driver, proc_e, buf, 308 (size_t)bytes, (op == CDEV_READ) ? CPF_WRITE : CPF_READ); 309 if (!GRANT_VALID(gid)) 310 panic("VFS: cpf_grant_magic failed"); 311 } else 312 gid = make_ioctl_grant(dp->dmap_driver, proc_e, buf, bytes); 313 314 /* Set up the message that will be sent to the driver. */ 315 memset(&dev_mess, 0, sizeof(dev_mess)); 316 dev_mess.m_type = op; 317 dev_mess.m_vfs_lchardriver_readwrite.minor = minor_dev; 318 if (op == CDEV_IOCTL) { 319 dev_mess.m_vfs_lchardriver_readwrite.request = bytes; 320 dev_mess.m_vfs_lchardriver_readwrite.user = proc_e; 321 } else { 322 dev_mess.m_vfs_lchardriver_readwrite.pos = pos; 323 dev_mess.m_vfs_lchardriver_readwrite.count = bytes; 324 } 325 dev_mess.m_vfs_lchardriver_readwrite.id = proc_e; 326 dev_mess.m_vfs_lchardriver_readwrite.grant = gid; 327 dev_mess.m_vfs_lchardriver_readwrite.flags = 0; 328 if (flags & O_NONBLOCK) 329 dev_mess.m_vfs_lchardriver_readwrite.flags |= CDEV_NONBLOCK; 330 331 /* Send the request to the driver. */ 332 if ((r = asynsend3(dp->dmap_driver, &dev_mess, AMF_NOREPLY)) != OK) 333 panic("VFS: asynsend in cdev_io failed: %d", r); 334 335 /* Suspend the calling process until a reply arrives. */ 336 fp->fp_cdev.dev = dev; 337 fp->fp_cdev.endpt = dp->dmap_driver; 338 fp->fp_cdev.grant = gid; /* revoke this when unsuspended */ 339 suspend(FP_BLOCKED_ON_CDEV); 340 341 return SUSPEND; 342 } 343 344 /* 345 * Initiate a select call on a device. Return OK iff the request was sent. 346 * This function explicitly bypasses cdev_get() since it must not do CTTY 347 * mapping, because a) the caller already has done that, b) "fp" may be wrong. 348 */ 349 int 350 cdev_select(dev_t dev, int ops) 351 { 352 devmajor_t major; 353 message dev_mess; 354 struct dmap *dp; 355 int r; 356 357 /* Determine task dmap, without CTTY mapping. */ 358 assert(dev != NO_DEV); 359 major = major(dev); 360 assert(major >= 0 && major < NR_DEVICES); 361 assert(major != CTTY_MAJOR); 362 dp = &dmap[major]; 363 364 /* Prepare the request message. */ 365 memset(&dev_mess, 0, sizeof(dev_mess)); 366 dev_mess.m_type = CDEV_SELECT; 367 dev_mess.m_vfs_lchardriver_select.minor = minor(dev); 368 dev_mess.m_vfs_lchardriver_select.ops = ops; 369 370 /* Send the request to the driver. */ 371 if ((r = asynsend3(dp->dmap_driver, &dev_mess, AMF_NOREPLY)) != OK) 372 panic("VFS: asynsend in cdev_select failed: %d", r); 373 374 return OK; 375 } 376 377 /* 378 * Cancel an I/O request, blocking until it has been cancelled. 379 */ 380 int 381 cdev_cancel(dev_t dev, endpoint_t endpt __unused, cp_grant_id_t grant) 382 { 383 devminor_t minor_dev; 384 message dev_mess; 385 struct dmap *dp; 386 int r; 387 388 /* Determine task dmap. */ 389 if ((dp = cdev_get(dev, &minor_dev)) == NULL) 390 return EIO; 391 392 /* Prepare the request message. */ 393 memset(&dev_mess, 0, sizeof(dev_mess)); 394 dev_mess.m_type = CDEV_CANCEL; 395 dev_mess.m_vfs_lchardriver_cancel.minor = minor_dev; 396 dev_mess.m_vfs_lchardriver_cancel.id = fp->fp_endpoint; 397 398 /* Send the request to the driver. */ 399 if ((r = asynsend3(dp->dmap_driver, &dev_mess, AMF_NOREPLY)) != OK) 400 panic("VFS: asynsend in cdev_cancel failed: %d", r); 401 402 /* Suspend this thread until we have received the response. */ 403 self->w_task = dp->dmap_driver; 404 self->w_drv_sendrec = &dev_mess; 405 406 worker_wait(); 407 408 self->w_task = NONE; 409 assert(self->w_drv_sendrec == NULL); 410 411 /* Clean up. */ 412 if (GRANT_VALID(grant)) 413 (void)cpf_revoke(grant); 414 415 /* Return the result. Note that the request may have completed. */ 416 r = dev_mess.m_lchardriver_vfs_reply.status; 417 418 return (r == EAGAIN) ? EINTR : r; /* see below regarding error codes */ 419 } 420 421 /* 422 * A character driver has results for an open, close, read, write, or ioctl 423 * call (i.e., everything except select). There may be a thread waiting for 424 * these results as part of an ongoing open, close, or (for read/write/ioctl) 425 * cancel call. If so, wake up that thread; if not, send a reply to the 426 * requesting process. This function MUST NOT block its calling thread. 427 */ 428 static void 429 cdev_generic_reply(message * m_ptr) 430 { 431 struct fproc *rfp; 432 struct worker_thread *wp; 433 endpoint_t proc_e; 434 int r, slot; 435 436 proc_e = m_ptr->m_lchardriver_vfs_reply.id; 437 438 if (m_ptr->m_lchardriver_vfs_reply.status == SUSPEND) { 439 printf("VFS: ignoring SUSPEND status from %d\n", 440 m_ptr->m_source); 441 return; 442 } 443 444 if (isokendpt(proc_e, &slot) != OK) { 445 printf("VFS: proc %d from %d not found\n", 446 proc_e, m_ptr->m_source); 447 return; 448 } 449 rfp = &fproc[slot]; 450 wp = rfp->fp_worker; 451 if (wp != NULL && wp->w_task == who_e && wp->w_drv_sendrec != NULL) { 452 assert(!fp_is_blocked(rfp)); 453 *wp->w_drv_sendrec = *m_ptr; 454 wp->w_drv_sendrec = NULL; 455 worker_signal(wp); /* continue open/close/cancel */ 456 } else if (rfp->fp_blocked_on != FP_BLOCKED_ON_CDEV || 457 rfp->fp_cdev.endpt != m_ptr->m_source) { 458 /* 459 * This would typically be caused by a protocol error, i.e., a 460 * driver not properly following the character driver protocol. 461 */ 462 printf("VFS: proc %d not blocked on %d\n", 463 proc_e, m_ptr->m_source); 464 } else { 465 /* 466 * Some services (e.g., inet) use the same infrastructure for 467 * nonblocking and cancelled requests, resulting in one of 468 * EINTR or EAGAIN when the other is really the appropriate 469 * code. Thus, cdev_cancel converts EAGAIN into EINTR, and we 470 * convert EINTR into EAGAIN here. 471 */ 472 r = m_ptr->m_lchardriver_vfs_reply.status; 473 revive(proc_e, (r == EINTR) ? EAGAIN : r); 474 } 475 } 476 477 /* 478 * A character driver has results for us. 479 */ 480 void 481 cdev_reply(void) 482 { 483 484 if (get_dmap_by_endpt(who_e) == NULL) { 485 printf("VFS: ignoring char dev reply from unknown driver %d\n", 486 who_e); 487 return; 488 } 489 490 switch (call_nr) { 491 case CDEV_REPLY: 492 cdev_generic_reply(&m_in); 493 break; 494 case CDEV_SEL1_REPLY: 495 select_cdev_reply1(m_in.m_source, 496 m_in.m_lchardriver_vfs_sel1.minor, 497 m_in.m_lchardriver_vfs_sel1.status); 498 break; 499 case CDEV_SEL2_REPLY: 500 select_cdev_reply2(m_in.m_source, 501 m_in.m_lchardriver_vfs_sel2.minor, 502 m_in.m_lchardriver_vfs_sel2.status); 503 break; 504 default: 505 printf("VFS: char driver %u sent unknown reply %x\n", 506 who_e, call_nr); 507 } 508 } 509