// SPDX-License-Identifier: GPL-2.0 /* * Provide access to virtual console memory. * /dev/vcs: the screen as it is being viewed right now (possibly scrolled) * /dev/vcsN: the screen of /dev/ttyN (1 <= N <= 63) * [minor: N] * * /dev/vcsaN: idem, but including attributes, and prefixed with * the 4 bytes lines,columns,x,y (as screendump used to give). * Attribute/character pair is in native endianity. * [minor: N+128] * * /dev/vcsuN: similar to /dev/vcsaN but using 4-byte unicode values * instead of 1-byte screen glyph values. * [minor: N+64] * * /dev/vcsuaN: same idea as /dev/vcsaN for unicode (not yet implemented). * * This replaces screendump and part of selection, so that the system * administrator can control access using file system permissions. * * aeb@cwi.nl - efter Friedas begravelse - 950211 * * machek@k332.feld.cvut.cz - modified not to send characters to wrong console * - fixed some fatal off-by-one bugs (0-- no longer == -1 -> looping and looping and looping...) * - making it shorter - scr_readw are macros which expand in PRETTY long code
*/
/* * Our minor space: * * 0 ... 63 glyph mode without attributes * 64 ... 127 unicode mode without attributes * 128 ... 191 glyph mode with attributes * 192 ... 255 unused (reserved for unicode with attributes) * * This relies on MAX_NR_CONSOLES being <= 63, meaning 63 actual consoles * with minors 0, 64, 128 and 192 being proxies for the foreground console.
*/ #if MAX_NR_CONSOLES > 63 #warning"/dev/vcs* devices may not accommodate more than 63 consoles" #endif
poll = kzalloc(sizeof(*poll), GFP_KERNEL); if (!poll) return NULL;
poll->cons_num = console(file_inode(file));
init_waitqueue_head(&poll->waitq);
poll->notifier.notifier_call = vcs_notifier; /* * In order not to lose any update event, we must pretend one might * have occurred before we have a chance to register our notifier. * This is also how user space has come to detect which kernels * support POLLPRI on /dev/vcs* devices i.e. using poll() with * POLLPRI and a zero timeout.
*/
poll->event = VT_UPDATE;
if (register_vt_notifier(&poll->notifier) != 0) {
kfree(poll); return NULL;
}
/* * This code may be called either through ->poll() or ->fasync(). * If we have two threads using the same file descriptor, they could * both enter this function, both notice that the structure hasn't * been allocated yet and go ahead allocating it in parallel, but * only one of them must survive and be shared otherwise we'd leak * memory with a dangling notifier callback.
*/
spin_lock(&file->f_lock); if (!file->private_data) {
file->private_data = poll;
} else { /* someone else raced ahead of us */
kill = poll;
poll = file->private_data;
}
spin_unlock(&file->f_lock); if (kill)
vcs_poll_data_free(kill);
return poll;
}
/** * vcs_vc - return VC for @inode * @inode: inode for which to return a VC * @viewed: returns whether this console is currently foreground (viewed) * * Must be called with console_lock.
*/ staticstruct vc_data *vcs_vc(struct inode *inode, bool *viewed)
{ unsignedint currcons = console(inode);
WARN_CONSOLE_UNLOCKED();
if (currcons == 0) {
currcons = fg_console; if (viewed)
*viewed = true;
} else {
currcons--; if (viewed)
*viewed = false;
} return vc_cons[currcons].d;
}
/** * vcs_size - return size for a VC in @vc * @vc: which VC * @attr: does it use attributes? * @unicode: is it unicode? * * Must be called with console_lock.
*/ staticint vcs_size(conststruct vc_data *vc, bool attr, bool unicode)
{ int size;
/* Advance state pointers and move on. */
count -= min(HEADER_SIZE, count);
pos = HEADER_SIZE;
con_buf += HEADER_SIZE; /* If count >= 0, then pos is even... */
} elseif (pos & 1) { /* * Skip first byte for output if start address is odd. Update * region sizes up/down depending on free space in buffer.
*/
(*skip)++; if (count < CON_BUF_SIZE)
count++; else
filled--;
}
/* * Buffer has even length, so we can always copy character + attribute. * We do not copy last byte to userspace if count is odd.
*/
count = (count + 1) / 2;
con_buf16 = (u16 *)con_buf;
while (count) {
*con_buf16++ = vcs_scr_readw(vc, org++);
count--; if (++col == maxcol) {
org = screen_pos(vc, pos, viewed);
col = 0;
pos += maxcol;
}
}
ret = -EINVAL; if (pos < 0) goto unlock_out; /* we enforce 32-bit alignment for pos and count in unicode mode */ if (uni_mode && (pos | count) & 3) goto unlock_out;
poll = file->private_data; if (count && poll)
poll->event = 0;
read = 0;
ret = 0; while (count) { unsignedint this_round, skip = 0; int size;
vc = vcs_vc(inode, &viewed); if (!vc) {
ret = -ENXIO; break;
}
/* Check whether we are above size each round, * as copy_to_user at the end of this loop * could sleep.
*/
size = vcs_size(vc, attr, uni_mode); if (size < 0) {
ret = size; break;
} if (pos >= size) break; if (count > size - pos)
count = size - pos;
this_round = count; if (this_round > CON_BUF_SIZE)
this_round = CON_BUF_SIZE;
/* Perform the whole read into the local con_buf. * Then we can drop the console spinlock and safely * attempt to move it to userspace.
*/
/* Finally, release the console semaphore while we push * all the data to userspace from our temporary buffer. * * AKPM: Even though it's a semaphore, we should drop it because * the pagefault handling code may want to call printk().
*/
console_unlock();
ret = copy_to_user(buf, con_buf + skip, this_round);
console_lock();
/* * Compilers (gcc 10) are unable to optimize the swap in cpu_to_le16. So do it * the poor man way.
*/ staticinline u16 vc_compile_le16(u8 hi, u8 lo)
{ #ifdef __BIG_ENDIAN return (lo << 8u) | hi; #else return (hi << 8u) | lo; #endif
}
/* odd pos -- the first single character */ if (pos & 1) {
count--;
c = *con_buf++;
vcs_scr_writew(vc, vc_compile_le16(c, vcs_scr_readw(vc, org)),
org);
org++;
pos++; if (++col == maxcol) {
org = screen_pos(vc, pos/2, viewed);
col = 0;
}
}
pos /= 2;
pos += maxcol - col;
/* even pos -- handle attr+character pairs */ while (count > 1) { unsignedshort w;
con_buf = (char *) __get_free_page(GFP_KERNEL); if (!con_buf) return -ENOMEM;
pos = *ppos;
/* Select the proper current console and verify * sanity of the situation under the console lock.
*/
console_lock();
attr = use_attributes(inode);
ret = -ENXIO;
vc = vcs_vc(inode, &viewed); if (!vc) goto unlock_out;
size = vcs_size(vc, attr, false); if (size < 0) {
ret = size; goto unlock_out;
}
ret = -EINVAL; if (pos < 0 || pos > size) goto unlock_out; if (count > size - pos)
count = size - pos;
written = 0; while (count) { unsignedint this_round = count;
if (this_round > CON_BUF_SIZE)
this_round = CON_BUF_SIZE;
/* Temporarily drop the console lock so that we can read * in the write data from userspace safely.
*/
console_unlock();
ret = copy_from_user(con_buf, buf, this_round);
console_lock();
if (ret) {
this_round -= ret; if (!this_round) { /* Abort loop if no data were copied. Otherwise * fail with -EFAULT.
*/ if (written) break;
ret = -EFAULT; goto unlock_out;
}
}
/* The vc might have been freed or vcs_size might have changed * while we slept to grab the user buffer, so recheck. * Return data written up to now on failure.
*/
vc = vcs_vc(inode, &viewed); if (!vc) { if (written) break;
ret = -ENXIO; goto unlock_out;
}
size = vcs_size(vc, attr, false); if (size < 0) { if (written) break;
ret = size; goto unlock_out;
} if (pos >= size) break; if (this_round > size - pos)
this_round = size - pos;
/* OK, now actually push the write to the console * under the lock using the local kernel buffer.
*/
if (poll) {
poll_wait(file, &poll->waitq, wait); switch (poll->event) { case VT_UPDATE:
ret = DEFAULT_POLLMASK|EPOLLPRI; break; case VT_DEALLOCATE:
ret = DEFAULT_POLLMASK|EPOLLHUP|EPOLLERR; break; case 0:
ret = DEFAULT_POLLMASK; break;
}
} return ret;
}
if (!poll) { /* don't allocate anything if all we want is disable fasync */ if (!on) return 0;
poll = vcs_poll_data_get(file); if (!poll) return -ENOMEM;
}
return fasync_helper(fd, file, on, &poll->fasync);
}
void vcs_make_sysfs(int index)
{
device_create(&vc_class, NULL, MKDEV(VCS_MAJOR, index + 1), NULL, "vcs%u", index + 1);
device_create(&vc_class, NULL, MKDEV(VCS_MAJOR, index + 65), NULL, "vcsu%u", index + 1);
device_create(&vc_class, NULL, MKDEV(VCS_MAJOR, index + 129), NULL, "vcsa%u", index + 1);
}
void vcs_remove_sysfs(int index)
{
device_destroy(&vc_class, MKDEV(VCS_MAJOR, index + 1));
device_destroy(&vc_class, MKDEV(VCS_MAJOR, index + 65));
device_destroy(&vc_class, MKDEV(VCS_MAJOR, index + 129));
}
int __init vcs_init(void)
{ unsignedint i;
if (register_chrdev(VCS_MAJOR, "vcs", &vcs_fops))
panic("unable to get major %d for vcs device", VCS_MAJOR); if (class_register(&vc_class))
panic("unable to create vc_class");
device_create(&vc_class, NULL, MKDEV(VCS_MAJOR, 0), NULL, "vcs");
device_create(&vc_class, NULL, MKDEV(VCS_MAJOR, 64), NULL, "vcsu");
device_create(&vc_class, NULL, MKDEV(VCS_MAJOR, 128), NULL, "vcsa"); for (i = 0; i < MIN_NR_CONSOLES; i++)
vcs_make_sysfs(i); return 0;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.