LCOV - code coverage report
Current view: top level - drivers/tty/vt - vc_screen.c (source / functions) Hit Total Coverage
Test: landlock.info Lines: 15 382 3.9 %
Date: 2021-04-22 12:43:58 Functions: 2 19 10.5 %

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

Generated by: LCOV version 1.14