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