LTP GCOV extension - code coverage report
Current view: directory - drivers/char - vc_screen.c
Test: 2.6.14_rebootonly_gcov.info
Date: 2006-05-22 Instrumented lines: 281
Code covered: 6.4 % Executed lines: 18

       1                 : /*
       2                 :  * linux/drivers/char/vc_screen.c
       3                 :  *
       4                 :  * Provide access to virtual console memory.
       5                 :  * /dev/vcs0: the screen as it is being viewed right now (possibly scrolled)
       6                 :  * /dev/vcsN: the screen of /dev/ttyN (1 <= N <= 63)
       7                 :  *            [minor: N]
       8                 :  *
       9                 :  * /dev/vcsaN: idem, but including attributes, and prefixed with
      10                 :  *      the 4 bytes lines,columns,x,y (as screendump used to give).
      11                 :  *      Attribute/character pair is in native endianity.
      12                 :  *            [minor: N+128]
      13                 :  *
      14                 :  * This replaces screendump and part of selection, so that the system
      15                 :  * administrator can control access using file system permissions.
      16                 :  *
      17                 :  * aeb@cwi.nl - efter Friedas begravelse - 950211
      18                 :  *
      19                 :  * machek@k332.feld.cvut.cz - modified not to send characters to wrong console
      20                 :  *       - fixed some fatal off-by-one bugs (0-- no longer == -1 -> looping and looping and looping...)
      21                 :  *       - making it shorter - scr_readw are macros which expand in PRETTY long code
      22                 :  */
      23                 : 
      24                 : #include <linux/config.h>
      25                 : #include <linux/kernel.h>
      26                 : #include <linux/major.h>
      27                 : #include <linux/errno.h>
      28                 : #include <linux/tty.h>
      29                 : #include <linux/devfs_fs_kernel.h>
      30                 : #include <linux/sched.h>
      31                 : #include <linux/interrupt.h>
      32                 : #include <linux/mm.h>
      33                 : #include <linux/init.h>
      34                 : #include <linux/vt_kern.h>
      35                 : #include <linux/selection.h>
      36                 : #include <linux/kbd_kern.h>
      37                 : #include <linux/console.h>
      38                 : #include <linux/smp_lock.h>
      39                 : #include <linux/device.h>
      40                 : #include <asm/uaccess.h>
      41                 : #include <asm/byteorder.h>
      42                 : #include <asm/unaligned.h>
      43                 : 
      44                 : #undef attr
      45                 : #undef org
      46                 : #undef addr
      47                 : #define HEADER_SIZE     4
      48                 : 
      49                 : static int
      50                 : vcs_size(struct inode *inode)
      51               0 : {
      52               0 :         int size;
      53               0 :         int minor = iminor(inode);
      54               0 :         int currcons = minor & 127;
      55               0 :         struct vc_data *vc;
      56                 : 
      57               0 :         if (currcons == 0)
      58               0 :                 currcons = fg_console;
      59                 :         else
      60               0 :                 currcons--;
      61               0 :         if (!vc_cons_allocated(currcons))
      62               0 :                 return -ENXIO;
      63               0 :         vc = vc_cons[currcons].d;
      64                 : 
      65               0 :         size = vc->vc_rows * vc->vc_cols;
      66                 : 
      67               0 :         if (minor & 128)
      68               0 :                 size = 2*size + HEADER_SIZE;
      69               0 :         return size;
      70                 : }
      71                 : 
      72                 : static loff_t vcs_lseek(struct file *file, loff_t offset, int orig)
      73               0 : {
      74               0 :         int size;
      75                 : 
      76               0 :         down(&con_buf_sem);
      77               0 :         size = vcs_size(file->f_dentry->d_inode);
      78               0 :         switch (orig) {
      79                 :                 default:
      80               0 :                         up(&con_buf_sem);
      81               0 :                         return -EINVAL;
      82                 :                 case 2:
      83               0 :                         offset += size;
      84               0 :                         break;
      85                 :                 case 1:
      86               0 :                         offset += file->f_pos;
      87                 :                 case 0:
      88               0 :                         break;
      89                 :         }
      90               0 :         if (offset < 0 || offset > size) {
      91               0 :                 up(&con_buf_sem);
      92               0 :                 return -EINVAL;
      93                 :         }
      94               0 :         file->f_pos = offset;
      95               0 :         up(&con_buf_sem);
      96               0 :         return file->f_pos;
      97                 : }
      98                 : 
      99                 : 
     100                 : static ssize_t
     101                 : vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
     102               0 : {
     103               0 :         struct inode *inode = file->f_dentry->d_inode;
     104               0 :         unsigned int currcons = iminor(inode);
     105               0 :         struct vc_data *vc;
     106               0 :         long pos;
     107               0 :         long viewed, attr, read;
     108               0 :         int col, maxcol;
     109               0 :         unsigned short *org = NULL;
     110               0 :         ssize_t ret;
     111                 : 
     112               0 :         down(&con_buf_sem);
     113                 : 
     114               0 :         pos = *ppos;
     115                 : 
     116                 :         /* Select the proper current console and verify
     117                 :          * sanity of the situation under the console lock.
     118                 :          */
     119               0 :         acquire_console_sem();
     120                 : 
     121               0 :         attr = (currcons & 128);
     122               0 :         currcons = (currcons & 127);
     123               0 :         if (currcons == 0) {
     124               0 :                 currcons = fg_console;
     125               0 :                 viewed = 1;
     126                 :         } else {
     127               0 :                 currcons--;
     128               0 :                 viewed = 0;
     129                 :         }
     130               0 :         ret = -ENXIO;
     131               0 :         if (!vc_cons_allocated(currcons))
     132               0 :                 goto unlock_out;
     133               0 :         vc = vc_cons[currcons].d;
     134                 : 
     135               0 :         ret = -EINVAL;
     136               0 :         if (pos < 0)
     137               0 :                 goto unlock_out;
     138               0 :         read = 0;
     139               0 :         ret = 0;
     140               0 :         while (count) {
     141               0 :                 char *con_buf0, *con_buf_start;
     142               0 :                 long this_round, size;
     143               0 :                 ssize_t orig_count;
     144               0 :                 long p = pos;
     145                 : 
     146                 :                 /* Check whether we are above size each round,
     147                 :                  * as copy_to_user at the end of this loop
     148                 :                  * could sleep.
     149                 :                  */
     150               0 :                 size = vcs_size(inode);
     151               0 :                 if (pos >= size)
     152               0 :                         break;
     153               0 :                 if (count > size - pos)
     154               0 :                         count = size - pos;
     155                 : 
     156               0 :                 this_round = count;
     157               0 :                 if (this_round > CON_BUF_SIZE)
     158               0 :                         this_round = CON_BUF_SIZE;
     159                 : 
     160                 :                 /* Perform the whole read into the local con_buf.
     161                 :                  * Then we can drop the console spinlock and safely
     162                 :                  * attempt to move it to userspace.
     163                 :                  */
     164                 : 
     165               0 :                 con_buf_start = con_buf0 = con_buf;
     166               0 :                 orig_count = this_round;
     167               0 :                 maxcol = vc->vc_cols;
     168               0 :                 if (!attr) {
     169               0 :                         org = screen_pos(vc, p, viewed);
     170               0 :                         col = p % maxcol;
     171               0 :                         p += maxcol - col;
     172               0 :                         while (this_round-- > 0) {
     173               0 :                                 *con_buf0++ = (vcs_scr_readw(vc, org++) & 0xff);
     174               0 :                                 if (++col == maxcol) {
     175               0 :                                         org = screen_pos(vc, p, viewed);
     176               0 :                                         col = 0;
     177               0 :                                         p += maxcol;
     178                 :                                 }
     179                 :                         }
     180                 :                 } else {
     181               0 :                         if (p < HEADER_SIZE) {
     182               0 :                                 size_t tmp_count;
     183                 : 
     184               0 :                                 con_buf0[0] = (char)vc->vc_rows;
     185               0 :                                 con_buf0[1] = (char)vc->vc_cols;
     186               0 :                                 getconsxy(vc, con_buf0 + 2);
     187                 : 
     188               0 :                                 con_buf_start += p;
     189               0 :                                 this_round += p;
     190               0 :                                 if (this_round > CON_BUF_SIZE) {
     191               0 :                                         this_round = CON_BUF_SIZE;
     192               0 :                                         orig_count = this_round - p;
     193                 :                                 }
     194                 : 
     195               0 :                                 tmp_count = HEADER_SIZE;
     196               0 :                                 if (tmp_count > this_round)
     197               0 :                                         tmp_count = this_round;
     198                 : 
     199                 :                                 /* Advance state pointers and move on. */
     200               0 :                                 this_round -= tmp_count;
     201               0 :                                 p = HEADER_SIZE;
     202               0 :                                 con_buf0 = con_buf + HEADER_SIZE;
     203                 :                                 /* If this_round >= 0, then p is even... */
     204               0 :                         } else if (p & 1) {
     205                 :                                 /* Skip first byte for output if start address is odd
     206                 :                                  * Update region sizes up/down depending on free
     207                 :                                  * space in buffer.
     208                 :                                  */
     209               0 :                                 con_buf_start++;
     210               0 :                                 if (this_round < CON_BUF_SIZE)
     211               0 :                                         this_round++;
     212                 :                                 else
     213               0 :                                         orig_count--;
     214                 :                         }
     215               0 :                         if (this_round > 0) {
     216               0 :                                 unsigned short *tmp_buf = (unsigned short *)con_buf0;
     217                 : 
     218               0 :                                 p -= HEADER_SIZE;
     219               0 :                                 p /= 2;
     220               0 :                                 col = p % maxcol;
     221                 : 
     222               0 :                                 org = screen_pos(vc, p, viewed);
     223               0 :                                 p += maxcol - col;
     224                 : 
     225                 :                                 /* Buffer has even length, so we can always copy
     226                 :                                  * character + attribute. We do not copy last byte
     227                 :                                  * to userspace if this_round is odd.
     228                 :                                  */
     229               0 :                                 this_round = (this_round + 1) >> 1;
     230                 : 
     231               0 :                                 while (this_round) {
     232               0 :                                         *tmp_buf++ = vcs_scr_readw(vc, org++);
     233               0 :                                         this_round --;
     234               0 :                                         if (++col == maxcol) {
     235               0 :                                                 org = screen_pos(vc, p, viewed);
     236               0 :                                                 col = 0;
     237               0 :                                                 p += maxcol;
     238                 :                                         }
     239                 :                                 }
     240                 :                         }
     241                 :                 }
     242                 : 
     243                 :                 /* Finally, release the console semaphore while we push
     244                 :                  * all the data to userspace from our temporary buffer.
     245                 :                  *
     246                 :                  * AKPM: Even though it's a semaphore, we should drop it because
     247                 :                  * the pagefault handling code may want to call printk().
     248                 :                  */
     249                 : 
     250               0 :                 release_console_sem();
     251               0 :                 ret = copy_to_user(buf, con_buf_start, orig_count);
     252               0 :                 acquire_console_sem();
     253                 : 
     254               0 :                 if (ret) {
     255               0 :                         read += (orig_count - ret);
     256               0 :                         ret = -EFAULT;
     257               0 :                         break;
     258                 :                 }
     259               0 :                 buf += orig_count;
     260               0 :                 pos += orig_count;
     261               0 :                 read += orig_count;
     262               0 :                 count -= orig_count;
     263                 :         }
     264               0 :         *ppos += read;
     265               0 :         if (read)
     266               0 :                 ret = read;
     267                 : unlock_out:
     268               0 :         release_console_sem();
     269               0 :         up(&con_buf_sem);
     270               0 :         return ret;
     271                 : }
     272                 : 
     273                 : static ssize_t
     274                 : vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
     275               0 : {
     276               0 :         struct inode *inode = file->f_dentry->d_inode;
     277               0 :         unsigned int currcons = iminor(inode);
     278               0 :         struct vc_data *vc;
     279               0 :         long pos;
     280               0 :         long viewed, attr, size, written;
     281               0 :         char *con_buf0;
     282               0 :         int col, maxcol;
     283               0 :         u16 *org0 = NULL, *org = NULL;
     284               0 :         size_t ret;
     285                 : 
     286               0 :         down(&con_buf_sem);
     287                 : 
     288               0 :         pos = *ppos;
     289                 : 
     290                 :         /* Select the proper current console and verify
     291                 :          * sanity of the situation under the console lock.
     292                 :          */
     293               0 :         acquire_console_sem();
     294                 : 
     295               0 :         attr = (currcons & 128);
     296               0 :         currcons = (currcons & 127);
     297                 : 
     298               0 :         if (currcons == 0) {
     299               0 :                 currcons = fg_console;
     300               0 :                 viewed = 1;
     301                 :         } else {
     302               0 :                 currcons--;
     303               0 :                 viewed = 0;
     304                 :         }
     305               0 :         ret = -ENXIO;
     306               0 :         if (!vc_cons_allocated(currcons))
     307               0 :                 goto unlock_out;
     308               0 :         vc = vc_cons[currcons].d;
     309                 : 
     310               0 :         size = vcs_size(inode);
     311               0 :         ret = -EINVAL;
     312               0 :         if (pos < 0 || pos > size)
     313               0 :                 goto unlock_out;
     314               0 :         if (count > size - pos)
     315               0 :                 count = size - pos;
     316               0 :         written = 0;
     317               0 :         while (count) {
     318               0 :                 long this_round = count;
     319               0 :                 size_t orig_count;
     320               0 :                 long p;
     321                 : 
     322               0 :                 if (this_round > CON_BUF_SIZE)
     323               0 :                         this_round = CON_BUF_SIZE;
     324                 : 
     325                 :                 /* Temporarily drop the console lock so that we can read
     326                 :                  * in the write data from userspace safely.
     327                 :                  */
     328               0 :                 release_console_sem();
     329               0 :                 ret = copy_from_user(con_buf, buf, this_round);
     330               0 :                 acquire_console_sem();
     331                 : 
     332               0 :                 if (ret) {
     333               0 :                         this_round -= ret;
     334               0 :                         if (!this_round) {
     335                 :                                 /* Abort loop if no data were copied. Otherwise
     336                 :                                  * fail with -EFAULT.
     337                 :                                  */
     338               0 :                                 if (written)
     339               0 :                                         break;
     340               0 :                                 ret = -EFAULT;
     341               0 :                                 goto unlock_out;
     342                 :                         }
     343                 :                 }
     344                 : 
     345                 :                 /* The vcs_size might have changed while we slept to grab
     346                 :                  * the user buffer, so recheck.
     347                 :                  * Return data written up to now on failure.
     348                 :                  */
     349               0 :                 size = vcs_size(inode);
     350               0 :                 if (pos >= size)
     351               0 :                         break;
     352               0 :                 if (this_round > size - pos)
     353               0 :                         this_round = size - pos;
     354                 : 
     355                 :                 /* OK, now actually push the write to the console
     356                 :                  * under the lock using the local kernel buffer.
     357                 :                  */
     358                 : 
     359               0 :                 con_buf0 = con_buf;
     360               0 :                 orig_count = this_round;
     361               0 :                 maxcol = vc->vc_cols;
     362               0 :                 p = pos;
     363               0 :                 if (!attr) {
     364               0 :                         org0 = org = screen_pos(vc, p, viewed);
     365               0 :                         col = p % maxcol;
     366               0 :                         p += maxcol - col;
     367                 : 
     368               0 :                         while (this_round > 0) {
     369               0 :                                 unsigned char c = *con_buf0++;
     370                 : 
     371               0 :                                 this_round--;
     372               0 :                                 vcs_scr_writew(vc,
     373                 :                                                (vcs_scr_readw(vc, org) & 0xff00) | c, org);
     374               0 :                                 org++;
     375               0 :                                 if (++col == maxcol) {
     376               0 :                                         org = screen_pos(vc, p, viewed);
     377               0 :                                         col = 0;
     378               0 :                                         p += maxcol;
     379                 :                                 }
     380                 :                         }
     381                 :                 } else {
     382               0 :                         if (p < HEADER_SIZE) {
     383               0 :                                 char header[HEADER_SIZE];
     384                 : 
     385               0 :                                 getconsxy(vc, header + 2);
     386               0 :                                 while (p < HEADER_SIZE && this_round > 0) {
     387               0 :                                         this_round--;
     388               0 :                                         header[p++] = *con_buf0++;
     389                 :                                 }
     390               0 :                                 if (!viewed)
     391               0 :                                         putconsxy(vc, header + 2);
     392                 :                         }
     393               0 :                         p -= HEADER_SIZE;
     394               0 :                         col = (p/2) % maxcol;
     395               0 :                         if (this_round > 0) {
     396               0 :                                 org0 = org = screen_pos(vc, p/2, viewed);
     397               0 :                                 if ((p & 1) && this_round > 0) {
     398               0 :                                         char c;
     399                 : 
     400               0 :                                         this_round--;
     401               0 :                                         c = *con_buf0++;
     402                 : #ifdef __BIG_ENDIAN
     403                 :                                         vcs_scr_writew(vc, c |
     404                 :                                              (vcs_scr_readw(vc, org) & 0xff00), org);
     405                 : #else
     406               0 :                                         vcs_scr_writew(vc, (c << 8) |
     407                 :                                              (vcs_scr_readw(vc, org) & 0xff), org);
     408                 : #endif
     409               0 :                                         org++;
     410               0 :                                         p++;
     411               0 :                                         if (++col == maxcol) {
     412               0 :                                                 org = screen_pos(vc, p/2, viewed);
     413               0 :                                                 col = 0;
     414                 :                                         }
     415                 :                                 }
     416               0 :                                 p /= 2;
     417               0 :                                 p += maxcol - col;
     418                 :                         }
     419               0 :                         while (this_round > 1) {
     420               0 :                                 unsigned short w;
     421                 : 
     422               0 :                                 w = get_unaligned(((const unsigned short *)con_buf0));
     423               0 :                                 vcs_scr_writew(vc, w, org++);
     424               0 :                                 con_buf0 += 2;
     425               0 :                                 this_round -= 2;
     426               0 :                                 if (++col == maxcol) {
     427               0 :                                         org = screen_pos(vc, p, viewed);
     428               0 :                                         col = 0;
     429               0 :                                         p += maxcol;
     430                 :                                 }
     431                 :                         }
     432               0 :                         if (this_round > 0) {
     433               0 :                                 unsigned char c;
     434                 : 
     435               0 :                                 c = *con_buf0++;
     436                 : #ifdef __BIG_ENDIAN
     437                 :                                 vcs_scr_writew(vc, (vcs_scr_readw(vc, org) & 0xff) | (c << 8), org);
     438                 : #else
     439               0 :                                 vcs_scr_writew(vc, (vcs_scr_readw(vc, org) & 0xff00) | c, org);
     440                 : #endif
     441                 :                         }
     442                 :                 }
     443               0 :                 count -= orig_count;
     444               0 :                 written += orig_count;
     445               0 :                 buf += orig_count;
     446               0 :                 pos += orig_count;
     447               0 :                 if (org0)
     448               0 :                         update_region(vc, (unsigned long)(org0), org - org0);
     449                 :         }
     450               0 :         *ppos += written;
     451               0 :         ret = written;
     452                 : 
     453                 : unlock_out:
     454               0 :         release_console_sem();
     455                 : 
     456               0 :         up(&con_buf_sem);
     457                 : 
     458               0 :         return ret;
     459                 : }
     460                 : 
     461                 : static int
     462                 : vcs_open(struct inode *inode, struct file *filp)
     463               0 : {
     464               0 :         unsigned int currcons = iminor(inode) & 127;
     465               0 :         if(currcons && !vc_cons_allocated(currcons-1))
     466               0 :                 return -ENXIO;
     467               0 :         return 0;
     468                 : }
     469                 : 
     470                 : static struct file_operations vcs_fops = {
     471                 :         .llseek         = vcs_lseek,
     472                 :         .read           = vcs_read,
     473                 :         .write          = vcs_write,
     474                 :         .open           = vcs_open,
     475                 : };
     476                 : 
     477                 : static struct class *vc_class;
     478                 : 
     479                 : void vcs_make_devfs(struct tty_struct *tty)
     480              36 : {
     481              36 :         devfs_mk_cdev(MKDEV(VCS_MAJOR, tty->index + 1),
     482                 :                         S_IFCHR|S_IRUSR|S_IWUSR,
     483                 :                         "vcc/%u", tty->index + 1);
     484              36 :         devfs_mk_cdev(MKDEV(VCS_MAJOR, tty->index + 129),
     485                 :                         S_IFCHR|S_IRUSR|S_IWUSR,
     486                 :                         "vcc/a%u", tty->index + 1);
     487              36 :         class_device_create(vc_class, MKDEV(VCS_MAJOR, tty->index + 1), NULL, "vcs%u", tty->index + 1);
     488              36 :         class_device_create(vc_class, MKDEV(VCS_MAJOR, tty->index + 129), NULL, "vcsa%u", tty->index + 1);
     489                 : }
     490                 : void vcs_remove_devfs(struct tty_struct *tty)
     491              30 : {
     492              30 :         devfs_remove("vcc/%u", tty->index + 1);
     493              30 :         devfs_remove("vcc/a%u", tty->index + 1);
     494              30 :         class_device_destroy(vc_class, MKDEV(VCS_MAJOR, tty->index + 1));
     495              30 :         class_device_destroy(vc_class, MKDEV(VCS_MAJOR, tty->index + 129));
     496                 : }
     497                 : 
     498                 : int __init vcs_init(void)
     499               1 : {
     500               1 :         if (register_chrdev(VCS_MAJOR, "vcs", &vcs_fops))
     501               0 :                 panic("unable to get major %d for vcs device", VCS_MAJOR);
     502               1 :         vc_class = class_create(THIS_MODULE, "vc");
     503                 : 
     504               1 :         devfs_mk_cdev(MKDEV(VCS_MAJOR, 0), S_IFCHR|S_IRUSR|S_IWUSR, "vcc/0");
     505               1 :         devfs_mk_cdev(MKDEV(VCS_MAJOR, 128), S_IFCHR|S_IRUSR|S_IWUSR, "vcc/a0");
     506               1 :         class_device_create(vc_class, MKDEV(VCS_MAJOR, 0), NULL, "vcs");
     507               1 :         class_device_create(vc_class, MKDEV(VCS_MAJOR, 128), NULL, "vcsa");
     508               1 :         return 0;
     509                 : }

Generated by: LTP GCOV extension version 1.4