Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.1-rc3 665 lines 15 kB view raw
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 52struct 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 60static int 61vcs_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 85static void 86vcs_poll_data_free(struct vcs_poll_data *poll) 87{ 88 unregister_vt_notifier(&poll->notifier); 89 kfree(poll); 90} 91 92static struct vcs_poll_data * 93vcs_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 */ 136static struct vc_data* 137vcs_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 */ 159static int 160vcs_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 179static 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 207static ssize_t 208vcs_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; 379unlock_out: 380 console_unlock(); 381 free_page((unsigned long) con_buf); 382 return ret; 383} 384 385static ssize_t 386vcs_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 567unlock_out: 568 console_unlock(); 569 free_page((unsigned long) con_buf); 570 return ret; 571} 572 573static unsigned int 574vcs_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 587static int 588vcs_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 604static int 605vcs_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 617static 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 626static 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 636static struct class *vc_class; 637 638void 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 646void 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 652int __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}