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