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