1 /* 2 * Provide access to virtual console memory. 3 * /dev/vcs0: the screen as it is being viewed right now (possibly scrolled) 4 * /dev/vcsN: the screen of /dev/ttyN (1 <= N <= 63) 5 * [minor: N] 6 * 7 * /dev/vcsaN: idem, but including attributes, and prefixed with 8 * the 4 bytes lines,columns,x,y (as screendump used to give). 9 * Attribute/character pair is in native endianity. 10 * [minor: N+128] 11 * 12 * This replaces screendump and part of selection, so that the system 13 * administrator can control access using file system permissions. 14 * 15 * aeb@cwi.nl - efter Friedas begravelse - 950211 16 * 17 * machek@k332.feld.cvut.cz - modified not to send characters to wrong console 18 * - fixed some fatal off-by-one bugs (0-- no longer == -1 -> looping and looping and looping...) 19 * - making it shorter - scr_readw are macros which expand in PRETTY long code 20 */ 21 22 #include <linux/kernel.h> 23 #include <linux/major.h> 24 #include <linux/errno.h> 25 #include <linux/tty.h> 26 #include <linux/interrupt.h> 27 #include <linux/mm.h> 28 #include <linux/init.h> 29 #include <linux/vt_kern.h> 30 #include <linux/selection.h> 31 #include <linux/kbd_kern.h> 32 #include <linux/console.h> 33 #include <linux/device.h> 34 #include <linux/sched.h> 35 #include <linux/fs.h> 36 #include <linux/poll.h> 37 #include <linux/signal.h> 38 #include <linux/slab.h> 39 #include <linux/notifier.h> 40 41 #include <asm/uaccess.h> 42 #include <asm/byteorder.h> 43 #include <asm/unaligned.h> 44 45 #undef attr 46 #undef org 47 #undef addr 48 #define HEADER_SIZE 4 49 50 #define CON_BUF_SIZE (CONFIG_BASE_SMALL ? 256 : PAGE_SIZE) 51 52 struct vcs_poll_data { 53 struct notifier_block notifier; 54 unsigned int cons_num; 55 bool seen_last_update; 56 wait_queue_head_t waitq; 57 struct fasync_struct *fasync; 58 }; 59 60 static int 61 vcs_notifier(struct notifier_block *nb, unsigned long code, void *_param) 62 { 63 struct vt_notifier_param *param = _param; 64 struct vc_data *vc = param->vc; 65 struct vcs_poll_data *poll = 66 container_of(nb, struct vcs_poll_data, notifier); 67 int currcons = poll->cons_num; 68 69 if (code != VT_UPDATE) 70 return NOTIFY_DONE; 71 72 if (currcons == 0) 73 currcons = fg_console; 74 else 75 currcons--; 76 if (currcons != vc->vc_num) 77 return NOTIFY_DONE; 78 79 poll->seen_last_update = false; 80 wake_up_interruptible(&poll->waitq); 81 kill_fasync(&poll->fasync, SIGIO, POLL_IN); 82 return NOTIFY_OK; 83 } 84 85 static void 86 vcs_poll_data_free(struct vcs_poll_data *poll) 87 { 88 unregister_vt_notifier(&poll->notifier); 89 kfree(poll); 90 } 91 92 static struct vcs_poll_data * 93 vcs_poll_data_get(struct file *file) 94 { 95 struct vcs_poll_data *poll = file->private_data; 96 97 if (poll) 98 return poll; 99 100 poll = kzalloc(sizeof(*poll), GFP_KERNEL); 101 if (!poll) 102 return NULL; 103 poll->cons_num = iminor(file->f_path.dentry->d_inode) & 127; 104 init_waitqueue_head(&poll->waitq); 105 poll->notifier.notifier_call = vcs_notifier; 106 if (register_vt_notifier(&poll->notifier) != 0) { 107 kfree(poll); 108 return NULL; 109 } 110 111 /* 112 * This code may be called either through ->poll() or ->fasync(). 113 * If we have two threads using the same file descriptor, they could 114 * both enter this function, both notice that the structure hasn't 115 * been allocated yet and go ahead allocating it in parallel, but 116 * only one of them must survive and be shared otherwise we'd leak 117 * memory with a dangling notifier callback. 118 */ 119 spin_lock(&file->f_lock); 120 if (!file->private_data) { 121 file->private_data = poll; 122 } else { 123 /* someone else raced ahead of us */ 124 vcs_poll_data_free(poll); 125 poll = file->private_data; 126 } 127 spin_unlock(&file->f_lock); 128 129 return poll; 130 } 131 132 /* 133 * Returns VC for inode. 134 * Must be called with console_lock. 135 */ 136 static struct vc_data* 137 vcs_vc(struct inode *inode, int *viewed) 138 { 139 unsigned int currcons = iminor(inode) & 127; 140 141 WARN_CONSOLE_UNLOCKED(); 142 143 if (currcons == 0) { 144 currcons = fg_console; 145 if (viewed) 146 *viewed = 1; 147 } else { 148 currcons--; 149 if (viewed) 150 *viewed = 0; 151 } 152 return vc_cons[currcons].d; 153 } 154 155 /* 156 * Returns size for VC carried by inode. 157 * Must be called with console_lock. 158 */ 159 static int 160 vcs_size(struct inode *inode) 161 { 162 int size; 163 int minor = iminor(inode); 164 struct vc_data *vc; 165 166 WARN_CONSOLE_UNLOCKED(); 167 168 vc = vcs_vc(inode, NULL); 169 if (!vc) 170 return -ENXIO; 171 172 size = vc->vc_rows * vc->vc_cols; 173 174 if (minor & 128) 175 size = 2*size + HEADER_SIZE; 176 return size; 177 } 178 179 static loff_t vcs_lseek(struct file *file, loff_t offset, int orig) 180 { 181 int size; 182 183 console_lock(); 184 size = vcs_size(file->f_path.dentry->d_inode); 185 console_unlock(); 186 if (size < 0) 187 return size; 188 switch (orig) { 189 default: 190 return -EINVAL; 191 case 2: 192 offset += size; 193 break; 194 case 1: 195 offset += file->f_pos; 196 case 0: 197 break; 198 } 199 if (offset < 0 || offset > size) { 200 return -EINVAL; 201 } 202 file->f_pos = offset; 203 return file->f_pos; 204 } 205 206 207 static ssize_t 208 vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) 209 { 210 struct inode *inode = file->f_path.dentry->d_inode; 211 unsigned int currcons = iminor(inode); 212 struct vc_data *vc; 213 struct vcs_poll_data *poll; 214 long pos; 215 long attr, read; 216 int col, maxcol, viewed; 217 unsigned short *org = NULL; 218 ssize_t ret; 219 char *con_buf; 220 221 con_buf = (char *) __get_free_page(GFP_KERNEL); 222 if (!con_buf) 223 return -ENOMEM; 224 225 pos = *ppos; 226 227 /* Select the proper current console and verify 228 * sanity of the situation under the console lock. 229 */ 230 console_lock(); 231 232 attr = (currcons & 128); 233 ret = -ENXIO; 234 vc = vcs_vc(inode, &viewed); 235 if (!vc) 236 goto unlock_out; 237 238 ret = -EINVAL; 239 if (pos < 0) 240 goto unlock_out; 241 poll = file->private_data; 242 if (count && poll) 243 poll->seen_last_update = true; 244 read = 0; 245 ret = 0; 246 while (count) { 247 char *con_buf0, *con_buf_start; 248 long this_round, size; 249 ssize_t orig_count; 250 long p = pos; 251 252 /* Check whether we are above size each round, 253 * as copy_to_user at the end of this loop 254 * could sleep. 255 */ 256 size = vcs_size(inode); 257 if (size < 0) { 258 if (read) 259 break; 260 ret = size; 261 goto unlock_out; 262 } 263 if (pos >= size) 264 break; 265 if (count > size - pos) 266 count = size - pos; 267 268 this_round = count; 269 if (this_round > CON_BUF_SIZE) 270 this_round = CON_BUF_SIZE; 271 272 /* Perform the whole read into the local con_buf. 273 * Then we can drop the console spinlock and safely 274 * attempt to move it to userspace. 275 */ 276 277 con_buf_start = con_buf0 = con_buf; 278 orig_count = this_round; 279 maxcol = vc->vc_cols; 280 if (!attr) { 281 org = screen_pos(vc, p, viewed); 282 col = p % maxcol; 283 p += maxcol - col; 284 while (this_round-- > 0) { 285 *con_buf0++ = (vcs_scr_readw(vc, org++) & 0xff); 286 if (++col == maxcol) { 287 org = screen_pos(vc, p, viewed); 288 col = 0; 289 p += maxcol; 290 } 291 } 292 } else { 293 if (p < HEADER_SIZE) { 294 size_t tmp_count; 295 296 con_buf0[0] = (char)vc->vc_rows; 297 con_buf0[1] = (char)vc->vc_cols; 298 getconsxy(vc, con_buf0 + 2); 299 300 con_buf_start += p; 301 this_round += p; 302 if (this_round > CON_BUF_SIZE) { 303 this_round = CON_BUF_SIZE; 304 orig_count = this_round - p; 305 } 306 307 tmp_count = HEADER_SIZE; 308 if (tmp_count > this_round) 309 tmp_count = this_round; 310 311 /* Advance state pointers and move on. */ 312 this_round -= tmp_count; 313 p = HEADER_SIZE; 314 con_buf0 = con_buf + HEADER_SIZE; 315 /* If this_round >= 0, then p is even... */ 316 } else if (p & 1) { 317 /* Skip first byte for output if start address is odd 318 * Update region sizes up/down depending on free 319 * space in buffer. 320 */ 321 con_buf_start++; 322 if (this_round < CON_BUF_SIZE) 323 this_round++; 324 else 325 orig_count--; 326 } 327 if (this_round > 0) { 328 unsigned short *tmp_buf = (unsigned short *)con_buf0; 329 330 p -= HEADER_SIZE; 331 p /= 2; 332 col = p % maxcol; 333 334 org = screen_pos(vc, p, viewed); 335 p += maxcol - col; 336 337 /* Buffer has even length, so we can always copy 338 * character + attribute. We do not copy last byte 339 * to userspace if this_round is odd. 340 */ 341 this_round = (this_round + 1) >> 1; 342 343 while (this_round) { 344 *tmp_buf++ = vcs_scr_readw(vc, org++); 345 this_round --; 346 if (++col == maxcol) { 347 org = screen_pos(vc, p, viewed); 348 col = 0; 349 p += maxcol; 350 } 351 } 352 } 353 } 354 355 /* Finally, release the console semaphore while we push 356 * all the data to userspace from our temporary buffer. 357 * 358 * AKPM: Even though it's a semaphore, we should drop it because 359 * the pagefault handling code may want to call printk(). 360 */ 361 362 console_unlock(); 363 ret = copy_to_user(buf, con_buf_start, orig_count); 364 console_lock(); 365 366 if (ret) { 367 read += (orig_count - ret); 368 ret = -EFAULT; 369 break; 370 } 371 buf += orig_count; 372 pos += orig_count; 373 read += orig_count; 374 count -= orig_count; 375 } 376 *ppos += read; 377 if (read) 378 ret = read; 379 unlock_out: 380 console_unlock(); 381 free_page((unsigned long) con_buf); 382 return ret; 383 } 384 385 static ssize_t 386 vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) 387 { 388 struct inode *inode = file->f_path.dentry->d_inode; 389 unsigned int currcons = iminor(inode); 390 struct vc_data *vc; 391 long pos; 392 long attr, size, written; 393 char *con_buf0; 394 int col, maxcol, viewed; 395 u16 *org0 = NULL, *org = NULL; 396 size_t ret; 397 char *con_buf; 398 399 con_buf = (char *) __get_free_page(GFP_KERNEL); 400 if (!con_buf) 401 return -ENOMEM; 402 403 pos = *ppos; 404 405 /* Select the proper current console and verify 406 * sanity of the situation under the console lock. 407 */ 408 console_lock(); 409 410 attr = (currcons & 128); 411 ret = -ENXIO; 412 vc = vcs_vc(inode, &viewed); 413 if (!vc) 414 goto unlock_out; 415 416 size = vcs_size(inode); 417 ret = -EINVAL; 418 if (pos < 0 || pos > size) 419 goto unlock_out; 420 if (count > size - pos) 421 count = size - pos; 422 written = 0; 423 while (count) { 424 long this_round = count; 425 size_t orig_count; 426 long p; 427 428 if (this_round > CON_BUF_SIZE) 429 this_round = CON_BUF_SIZE; 430 431 /* Temporarily drop the console lock so that we can read 432 * in the write data from userspace safely. 433 */ 434 console_unlock(); 435 ret = copy_from_user(con_buf, buf, this_round); 436 console_lock(); 437 438 if (ret) { 439 this_round -= ret; 440 if (!this_round) { 441 /* Abort loop if no data were copied. Otherwise 442 * fail with -EFAULT. 443 */ 444 if (written) 445 break; 446 ret = -EFAULT; 447 goto unlock_out; 448 } 449 } 450 451 /* The vcs_size might have changed while we slept to grab 452 * the user buffer, so recheck. 453 * Return data written up to now on failure. 454 */ 455 size = vcs_size(inode); 456 if (size < 0) { 457 if (written) 458 break; 459 ret = size; 460 goto unlock_out; 461 } 462 if (pos >= size) 463 break; 464 if (this_round > size - pos) 465 this_round = size - pos; 466 467 /* OK, now actually push the write to the console 468 * under the lock using the local kernel buffer. 469 */ 470 471 con_buf0 = con_buf; 472 orig_count = this_round; 473 maxcol = vc->vc_cols; 474 p = pos; 475 if (!attr) { 476 org0 = org = screen_pos(vc, p, viewed); 477 col = p % maxcol; 478 p += maxcol - col; 479 480 while (this_round > 0) { 481 unsigned char c = *con_buf0++; 482 483 this_round--; 484 vcs_scr_writew(vc, 485 (vcs_scr_readw(vc, org) & 0xff00) | c, org); 486 org++; 487 if (++col == maxcol) { 488 org = screen_pos(vc, p, viewed); 489 col = 0; 490 p += maxcol; 491 } 492 } 493 } else { 494 if (p < HEADER_SIZE) { 495 char header[HEADER_SIZE]; 496 497 getconsxy(vc, header + 2); 498 while (p < HEADER_SIZE && this_round > 0) { 499 this_round--; 500 header[p++] = *con_buf0++; 501 } 502 if (!viewed) 503 putconsxy(vc, header + 2); 504 } 505 p -= HEADER_SIZE; 506 col = (p/2) % maxcol; 507 if (this_round > 0) { 508 org0 = org = screen_pos(vc, p/2, viewed); 509 if ((p & 1) && this_round > 0) { 510 char c; 511 512 this_round--; 513 c = *con_buf0++; 514 #ifdef __BIG_ENDIAN 515 vcs_scr_writew(vc, c | 516 (vcs_scr_readw(vc, org) & 0xff00), org); 517 #else 518 vcs_scr_writew(vc, (c << 8) | 519 (vcs_scr_readw(vc, org) & 0xff), org); 520 #endif 521 org++; 522 p++; 523 if (++col == maxcol) { 524 org = screen_pos(vc, p/2, viewed); 525 col = 0; 526 } 527 } 528 p /= 2; 529 p += maxcol - col; 530 } 531 while (this_round > 1) { 532 unsigned short w; 533 534 w = get_unaligned(((unsigned short *)con_buf0)); 535 vcs_scr_writew(vc, w, org++); 536 con_buf0 += 2; 537 this_round -= 2; 538 if (++col == maxcol) { 539 org = screen_pos(vc, p, viewed); 540 col = 0; 541 p += maxcol; 542 } 543 } 544 if (this_round > 0) { 545 unsigned char c; 546 547 c = *con_buf0++; 548 #ifdef __BIG_ENDIAN 549 vcs_scr_writew(vc, (vcs_scr_readw(vc, org) & 0xff) | (c << 8), org); 550 #else 551 vcs_scr_writew(vc, (vcs_scr_readw(vc, org) & 0xff00) | c, org); 552 #endif 553 } 554 } 555 count -= orig_count; 556 written += orig_count; 557 buf += orig_count; 558 pos += orig_count; 559 if (org0) 560 update_region(vc, (unsigned long)(org0), org - org0); 561 } 562 *ppos += written; 563 ret = written; 564 if (written) 565 vcs_scr_updated(vc); 566 567 unlock_out: 568 console_unlock(); 569 free_page((unsigned long) con_buf); 570 return ret; 571 } 572 573 static unsigned int 574 vcs_poll(struct file *file, poll_table *wait) 575 { 576 struct vcs_poll_data *poll = vcs_poll_data_get(file); 577 int ret = DEFAULT_POLLMASK|POLLERR|POLLPRI; 578 579 if (poll) { 580 poll_wait(file, &poll->waitq, wait); 581 if (poll->seen_last_update) 582 ret = DEFAULT_POLLMASK; 583 } 584 return ret; 585 } 586 587 static int 588 vcs_fasync(int fd, struct file *file, int on) 589 { 590 struct vcs_poll_data *poll = file->private_data; 591 592 if (!poll) { 593 /* don't allocate anything if all we want is disable fasync */ 594 if (!on) 595 return 0; 596 poll = vcs_poll_data_get(file); 597 if (!poll) 598 return -ENOMEM; 599 } 600 601 return fasync_helper(fd, file, on, &poll->fasync); 602 } 603 604 static int 605 vcs_open(struct inode *inode, struct file *filp) 606 { 607 unsigned int currcons = iminor(inode) & 127; 608 int ret = 0; 609 610 tty_lock(); 611 if(currcons && !vc_cons_allocated(currcons-1)) 612 ret = -ENXIO; 613 tty_unlock(); 614 return ret; 615 } 616 617 static int vcs_release(struct inode *inode, struct file *file) 618 { 619 struct vcs_poll_data *poll = file->private_data; 620 621 if (poll) 622 vcs_poll_data_free(poll); 623 return 0; 624 } 625 626 static const struct file_operations vcs_fops = { 627 .llseek = vcs_lseek, 628 .read = vcs_read, 629 .write = vcs_write, 630 .poll = vcs_poll, 631 .fasync = vcs_fasync, 632 .open = vcs_open, 633 .release = vcs_release, 634 }; 635 636 static struct class *vc_class; 637 638 void vcs_make_sysfs(int index) 639 { 640 device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 1), NULL, 641 "vcs%u", index + 1); 642 device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 129), NULL, 643 "vcsa%u", index + 1); 644 } 645 646 void vcs_remove_sysfs(int index) 647 { 648 device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 1)); 649 device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 129)); 650 } 651 652 int __init vcs_init(void) 653 { 654 unsigned int i; 655 656 if (register_chrdev(VCS_MAJOR, "vcs", &vcs_fops)) 657 panic("unable to get major %d for vcs device", VCS_MAJOR); 658 vc_class = class_create(THIS_MODULE, "vc"); 659 660 device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 0), NULL, "vcs"); 661 device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 128), NULL, "vcsa"); 662 for (i = 0; i < MIN_NR_CONSOLES; i++) 663 vcs_make_sysfs(i); 664 return 0; 665 } 666