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