18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 1992 obz under the linux copyright 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Dynamic diacritical handling - aeb@cwi.nl - Dec 1993 68c2ecf20Sopenharmony_ci * Dynamic keymap and string allocation - aeb@cwi.nl - May 1994 78c2ecf20Sopenharmony_ci * Restrict VT switching via ioctl() - grif@cs.ucr.edu - Dec 1995 88c2ecf20Sopenharmony_ci * Some code moved for less code duplication - Andi Kleen - Mar 1997 98c2ecf20Sopenharmony_ci * Check put/get_user, cleanups - acme@conectiva.com.br - Jun 2001 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/types.h> 138c2ecf20Sopenharmony_ci#include <linux/errno.h> 148c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 158c2ecf20Sopenharmony_ci#include <linux/tty.h> 168c2ecf20Sopenharmony_ci#include <linux/timer.h> 178c2ecf20Sopenharmony_ci#include <linux/kernel.h> 188c2ecf20Sopenharmony_ci#include <linux/compat.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/kd.h> 218c2ecf20Sopenharmony_ci#include <linux/vt.h> 228c2ecf20Sopenharmony_ci#include <linux/string.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci#include <linux/major.h> 258c2ecf20Sopenharmony_ci#include <linux/fs.h> 268c2ecf20Sopenharmony_ci#include <linux/console.h> 278c2ecf20Sopenharmony_ci#include <linux/consolemap.h> 288c2ecf20Sopenharmony_ci#include <linux/signal.h> 298c2ecf20Sopenharmony_ci#include <linux/suspend.h> 308c2ecf20Sopenharmony_ci#include <linux/timex.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include <asm/io.h> 338c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#include <linux/nospec.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include <linux/kbd_kern.h> 388c2ecf20Sopenharmony_ci#include <linux/vt_kern.h> 398c2ecf20Sopenharmony_ci#include <linux/kbd_diacr.h> 408c2ecf20Sopenharmony_ci#include <linux/selection.h> 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cibool vt_dont_switch; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic inline bool vt_in_use(unsigned int i) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci const struct vc_data *vc = vc_cons[i].d; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci /* 498c2ecf20Sopenharmony_ci * console_lock must be held to prevent the vc from being deallocated 508c2ecf20Sopenharmony_ci * while we're checking whether it's in-use. 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_ci WARN_CONSOLE_UNLOCKED(); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci return vc && kref_read(&vc->port.kref) > 1; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic inline bool vt_busy(int i) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci if (vt_in_use(i)) 608c2ecf20Sopenharmony_ci return true; 618c2ecf20Sopenharmony_ci if (i == fg_console) 628c2ecf20Sopenharmony_ci return true; 638c2ecf20Sopenharmony_ci if (vc_is_sel(vc_cons[i].d)) 648c2ecf20Sopenharmony_ci return true; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci return false; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* 708c2ecf20Sopenharmony_ci * Console (vt and kd) routines, as defined by USL SVR4 manual, and by 718c2ecf20Sopenharmony_ci * experimentation and study of X386 SYSV handling. 728c2ecf20Sopenharmony_ci * 738c2ecf20Sopenharmony_ci * One point of difference: SYSV vt's are /dev/vtX, which X >= 0, and 748c2ecf20Sopenharmony_ci * /dev/console is a separate ttyp. Under Linux, /dev/tty0 is /dev/console, 758c2ecf20Sopenharmony_ci * and the vc start at /dev/ttyX, X >= 1. We maintain that here, so we will 768c2ecf20Sopenharmony_ci * always treat our set of vt as numbered 1..MAX_NR_CONSOLES (corresponding to 778c2ecf20Sopenharmony_ci * ttys 0..MAX_NR_CONSOLES-1). Explicitly naming VT 0 is illegal, but using 788c2ecf20Sopenharmony_ci * /dev/tty0 (fg_console) as a target is legal, since an implicit aliasing 798c2ecf20Sopenharmony_ci * to the current console is done by the main ioctl code. 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci#ifdef CONFIG_X86 838c2ecf20Sopenharmony_ci#include <asm/syscalls.h> 848c2ecf20Sopenharmony_ci#endif 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic void complete_change_console(struct vc_data *vc); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* 898c2ecf20Sopenharmony_ci * User space VT_EVENT handlers 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistruct vt_event_wait { 938c2ecf20Sopenharmony_ci struct list_head list; 948c2ecf20Sopenharmony_ci struct vt_event event; 958c2ecf20Sopenharmony_ci int done; 968c2ecf20Sopenharmony_ci}; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic LIST_HEAD(vt_events); 998c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(vt_event_lock); 1008c2ecf20Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(vt_event_waitqueue); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/** 1038c2ecf20Sopenharmony_ci * vt_event_post 1048c2ecf20Sopenharmony_ci * @event: the event that occurred 1058c2ecf20Sopenharmony_ci * @old: old console 1068c2ecf20Sopenharmony_ci * @new: new console 1078c2ecf20Sopenharmony_ci * 1088c2ecf20Sopenharmony_ci * Post an VT event to interested VT handlers 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_civoid vt_event_post(unsigned int event, unsigned int old, unsigned int new) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci struct list_head *pos, *head; 1148c2ecf20Sopenharmony_ci unsigned long flags; 1158c2ecf20Sopenharmony_ci int wake = 0; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci spin_lock_irqsave(&vt_event_lock, flags); 1188c2ecf20Sopenharmony_ci head = &vt_events; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci list_for_each(pos, head) { 1218c2ecf20Sopenharmony_ci struct vt_event_wait *ve = list_entry(pos, 1228c2ecf20Sopenharmony_ci struct vt_event_wait, list); 1238c2ecf20Sopenharmony_ci if (!(ve->event.event & event)) 1248c2ecf20Sopenharmony_ci continue; 1258c2ecf20Sopenharmony_ci ve->event.event = event; 1268c2ecf20Sopenharmony_ci /* kernel view is consoles 0..n-1, user space view is 1278c2ecf20Sopenharmony_ci console 1..n with 0 meaning current, so we must bias */ 1288c2ecf20Sopenharmony_ci ve->event.oldev = old + 1; 1298c2ecf20Sopenharmony_ci ve->event.newev = new + 1; 1308c2ecf20Sopenharmony_ci wake = 1; 1318c2ecf20Sopenharmony_ci ve->done = 1; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vt_event_lock, flags); 1348c2ecf20Sopenharmony_ci if (wake) 1358c2ecf20Sopenharmony_ci wake_up_interruptible(&vt_event_waitqueue); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic void __vt_event_queue(struct vt_event_wait *vw) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci unsigned long flags; 1418c2ecf20Sopenharmony_ci /* Prepare the event */ 1428c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&vw->list); 1438c2ecf20Sopenharmony_ci vw->done = 0; 1448c2ecf20Sopenharmony_ci /* Queue our event */ 1458c2ecf20Sopenharmony_ci spin_lock_irqsave(&vt_event_lock, flags); 1468c2ecf20Sopenharmony_ci list_add(&vw->list, &vt_events); 1478c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vt_event_lock, flags); 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic void __vt_event_wait(struct vt_event_wait *vw) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci /* Wait for it to pass */ 1538c2ecf20Sopenharmony_ci wait_event_interruptible(vt_event_waitqueue, vw->done); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic void __vt_event_dequeue(struct vt_event_wait *vw) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci unsigned long flags; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* Dequeue it */ 1618c2ecf20Sopenharmony_ci spin_lock_irqsave(&vt_event_lock, flags); 1628c2ecf20Sopenharmony_ci list_del(&vw->list); 1638c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vt_event_lock, flags); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci/** 1678c2ecf20Sopenharmony_ci * vt_event_wait - wait for an event 1688c2ecf20Sopenharmony_ci * @vw: our event 1698c2ecf20Sopenharmony_ci * 1708c2ecf20Sopenharmony_ci * Waits for an event to occur which completes our vt_event_wait 1718c2ecf20Sopenharmony_ci * structure. On return the structure has wv->done set to 1 for success 1728c2ecf20Sopenharmony_ci * or 0 if some event such as a signal ended the wait. 1738c2ecf20Sopenharmony_ci */ 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic void vt_event_wait(struct vt_event_wait *vw) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci __vt_event_queue(vw); 1788c2ecf20Sopenharmony_ci __vt_event_wait(vw); 1798c2ecf20Sopenharmony_ci __vt_event_dequeue(vw); 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci/** 1838c2ecf20Sopenharmony_ci * vt_event_wait_ioctl - event ioctl handler 1848c2ecf20Sopenharmony_ci * @event: argument to ioctl (the event) 1858c2ecf20Sopenharmony_ci * 1868c2ecf20Sopenharmony_ci * Implement the VT_WAITEVENT ioctl using the VT event interface 1878c2ecf20Sopenharmony_ci */ 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic int vt_event_wait_ioctl(struct vt_event __user *event) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci struct vt_event_wait vw; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (copy_from_user(&vw.event, event, sizeof(struct vt_event))) 1948c2ecf20Sopenharmony_ci return -EFAULT; 1958c2ecf20Sopenharmony_ci /* Highest supported event for now */ 1968c2ecf20Sopenharmony_ci if (vw.event.event & ~VT_MAX_EVENT) 1978c2ecf20Sopenharmony_ci return -EINVAL; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci vt_event_wait(&vw); 2008c2ecf20Sopenharmony_ci /* If it occurred report it */ 2018c2ecf20Sopenharmony_ci if (vw.done) { 2028c2ecf20Sopenharmony_ci if (copy_to_user(event, &vw.event, sizeof(struct vt_event))) 2038c2ecf20Sopenharmony_ci return -EFAULT; 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci return -EINTR; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci/** 2108c2ecf20Sopenharmony_ci * vt_waitactive - active console wait 2118c2ecf20Sopenharmony_ci * @n: new console 2128c2ecf20Sopenharmony_ci * 2138c2ecf20Sopenharmony_ci * Helper for event waits. Used to implement the legacy 2148c2ecf20Sopenharmony_ci * event waiting ioctls in terms of events 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ciint vt_waitactive(int n) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci struct vt_event_wait vw; 2208c2ecf20Sopenharmony_ci do { 2218c2ecf20Sopenharmony_ci vw.event.event = VT_EVENT_SWITCH; 2228c2ecf20Sopenharmony_ci __vt_event_queue(&vw); 2238c2ecf20Sopenharmony_ci if (n == fg_console + 1) { 2248c2ecf20Sopenharmony_ci __vt_event_dequeue(&vw); 2258c2ecf20Sopenharmony_ci break; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci __vt_event_wait(&vw); 2288c2ecf20Sopenharmony_ci __vt_event_dequeue(&vw); 2298c2ecf20Sopenharmony_ci if (vw.done == 0) 2308c2ecf20Sopenharmony_ci return -EINTR; 2318c2ecf20Sopenharmony_ci } while (vw.event.newev != n); 2328c2ecf20Sopenharmony_ci return 0; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci/* 2368c2ecf20Sopenharmony_ci * these are the valid i/o ports we're allowed to change. they map all the 2378c2ecf20Sopenharmony_ci * video ports 2388c2ecf20Sopenharmony_ci */ 2398c2ecf20Sopenharmony_ci#define GPFIRST 0x3b4 2408c2ecf20Sopenharmony_ci#define GPLAST 0x3df 2418c2ecf20Sopenharmony_ci#define GPNUM (GPLAST - GPFIRST + 1) 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci/* 2448c2ecf20Sopenharmony_ci * currently, setting the mode from KD_TEXT to KD_GRAPHICS doesn't do a whole 2458c2ecf20Sopenharmony_ci * lot. i'm not sure if it should do any restoration of modes or what... 2468c2ecf20Sopenharmony_ci * 2478c2ecf20Sopenharmony_ci * XXX It should at least call into the driver, fbdev's definitely need to 2488c2ecf20Sopenharmony_ci * restore their engine state. --BenH 2498c2ecf20Sopenharmony_ci * 2508c2ecf20Sopenharmony_ci * Called with the console lock held. 2518c2ecf20Sopenharmony_ci */ 2528c2ecf20Sopenharmony_cistatic int vt_kdsetmode(struct vc_data *vc, unsigned long mode) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci switch (mode) { 2558c2ecf20Sopenharmony_ci case KD_GRAPHICS: 2568c2ecf20Sopenharmony_ci break; 2578c2ecf20Sopenharmony_ci case KD_TEXT0: 2588c2ecf20Sopenharmony_ci case KD_TEXT1: 2598c2ecf20Sopenharmony_ci mode = KD_TEXT; 2608c2ecf20Sopenharmony_ci fallthrough; 2618c2ecf20Sopenharmony_ci case KD_TEXT: 2628c2ecf20Sopenharmony_ci break; 2638c2ecf20Sopenharmony_ci default: 2648c2ecf20Sopenharmony_ci return -EINVAL; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci if (vc->vc_mode == mode) 2688c2ecf20Sopenharmony_ci return 0; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci vc->vc_mode = mode; 2718c2ecf20Sopenharmony_ci if (vc->vc_num != fg_console) 2728c2ecf20Sopenharmony_ci return 0; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* explicitly blank/unblank the screen if switching modes */ 2758c2ecf20Sopenharmony_ci if (mode == KD_TEXT) 2768c2ecf20Sopenharmony_ci do_unblank_screen(1); 2778c2ecf20Sopenharmony_ci else 2788c2ecf20Sopenharmony_ci do_blank_screen(1); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci return 0; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic int vt_k_ioctl(struct tty_struct *tty, unsigned int cmd, 2848c2ecf20Sopenharmony_ci unsigned long arg, bool perm) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci struct vc_data *vc = tty->driver_data; 2878c2ecf20Sopenharmony_ci void __user *up = (void __user *)arg; 2888c2ecf20Sopenharmony_ci unsigned int console = vc->vc_num; 2898c2ecf20Sopenharmony_ci int ret; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci switch (cmd) { 2928c2ecf20Sopenharmony_ci case KIOCSOUND: 2938c2ecf20Sopenharmony_ci if (!perm) 2948c2ecf20Sopenharmony_ci return -EPERM; 2958c2ecf20Sopenharmony_ci /* 2968c2ecf20Sopenharmony_ci * The use of PIT_TICK_RATE is historic, it used to be 2978c2ecf20Sopenharmony_ci * the platform-dependent CLOCK_TICK_RATE between 2.6.12 2988c2ecf20Sopenharmony_ci * and 2.6.36, which was a minor but unfortunate ABI 2998c2ecf20Sopenharmony_ci * change. kd_mksound is locked by the input layer. 3008c2ecf20Sopenharmony_ci */ 3018c2ecf20Sopenharmony_ci if (arg) 3028c2ecf20Sopenharmony_ci arg = PIT_TICK_RATE / arg; 3038c2ecf20Sopenharmony_ci kd_mksound(arg, 0); 3048c2ecf20Sopenharmony_ci break; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci case KDMKTONE: 3078c2ecf20Sopenharmony_ci if (!perm) 3088c2ecf20Sopenharmony_ci return -EPERM; 3098c2ecf20Sopenharmony_ci { 3108c2ecf20Sopenharmony_ci unsigned int ticks, count; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci /* 3138c2ecf20Sopenharmony_ci * Generate the tone for the appropriate number of ticks. 3148c2ecf20Sopenharmony_ci * If the time is zero, turn off sound ourselves. 3158c2ecf20Sopenharmony_ci */ 3168c2ecf20Sopenharmony_ci ticks = msecs_to_jiffies((arg >> 16) & 0xffff); 3178c2ecf20Sopenharmony_ci count = ticks ? (arg & 0xffff) : 0; 3188c2ecf20Sopenharmony_ci if (count) 3198c2ecf20Sopenharmony_ci count = PIT_TICK_RATE / count; 3208c2ecf20Sopenharmony_ci kd_mksound(count, ticks); 3218c2ecf20Sopenharmony_ci break; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci case KDGKBTYPE: 3258c2ecf20Sopenharmony_ci /* 3268c2ecf20Sopenharmony_ci * this is naïve. 3278c2ecf20Sopenharmony_ci */ 3288c2ecf20Sopenharmony_ci return put_user(KB_101, (char __user *)arg); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* 3318c2ecf20Sopenharmony_ci * These cannot be implemented on any machine that implements 3328c2ecf20Sopenharmony_ci * ioperm() in user level (such as Alpha PCs) or not at all. 3338c2ecf20Sopenharmony_ci * 3348c2ecf20Sopenharmony_ci * XXX: you should never use these, just call ioperm directly.. 3358c2ecf20Sopenharmony_ci */ 3368c2ecf20Sopenharmony_ci#ifdef CONFIG_X86 3378c2ecf20Sopenharmony_ci case KDADDIO: 3388c2ecf20Sopenharmony_ci case KDDELIO: 3398c2ecf20Sopenharmony_ci /* 3408c2ecf20Sopenharmony_ci * KDADDIO and KDDELIO may be able to add ports beyond what 3418c2ecf20Sopenharmony_ci * we reject here, but to be safe... 3428c2ecf20Sopenharmony_ci * 3438c2ecf20Sopenharmony_ci * These are locked internally via sys_ioperm 3448c2ecf20Sopenharmony_ci */ 3458c2ecf20Sopenharmony_ci if (arg < GPFIRST || arg > GPLAST) 3468c2ecf20Sopenharmony_ci return -EINVAL; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci return ksys_ioperm(arg, 1, (cmd == KDADDIO)) ? -ENXIO : 0; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci case KDENABIO: 3518c2ecf20Sopenharmony_ci case KDDISABIO: 3528c2ecf20Sopenharmony_ci return ksys_ioperm(GPFIRST, GPNUM, 3538c2ecf20Sopenharmony_ci (cmd == KDENABIO)) ? -ENXIO : 0; 3548c2ecf20Sopenharmony_ci#endif 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci /* Linux m68k/i386 interface for setting the keyboard delay/repeat rate */ 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci case KDKBDREP: 3598c2ecf20Sopenharmony_ci { 3608c2ecf20Sopenharmony_ci struct kbd_repeat kbrep; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_TTY_CONFIG)) 3638c2ecf20Sopenharmony_ci return -EPERM; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci if (copy_from_user(&kbrep, up, sizeof(struct kbd_repeat))) 3668c2ecf20Sopenharmony_ci return -EFAULT; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci ret = kbd_rate(&kbrep); 3698c2ecf20Sopenharmony_ci if (ret) 3708c2ecf20Sopenharmony_ci return ret; 3718c2ecf20Sopenharmony_ci if (copy_to_user(up, &kbrep, sizeof(struct kbd_repeat))) 3728c2ecf20Sopenharmony_ci return -EFAULT; 3738c2ecf20Sopenharmony_ci break; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci case KDSETMODE: 3778c2ecf20Sopenharmony_ci if (!perm) 3788c2ecf20Sopenharmony_ci return -EPERM; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci console_lock(); 3818c2ecf20Sopenharmony_ci ret = vt_kdsetmode(vc, arg); 3828c2ecf20Sopenharmony_ci console_unlock(); 3838c2ecf20Sopenharmony_ci return ret; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci case KDGETMODE: 3868c2ecf20Sopenharmony_ci return put_user(vc->vc_mode, (int __user *)arg); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci case KDMAPDISP: 3898c2ecf20Sopenharmony_ci case KDUNMAPDISP: 3908c2ecf20Sopenharmony_ci /* 3918c2ecf20Sopenharmony_ci * these work like a combination of mmap and KDENABIO. 3928c2ecf20Sopenharmony_ci * this could be easily finished. 3938c2ecf20Sopenharmony_ci */ 3948c2ecf20Sopenharmony_ci return -EINVAL; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci case KDSKBMODE: 3978c2ecf20Sopenharmony_ci if (!perm) 3988c2ecf20Sopenharmony_ci return -EPERM; 3998c2ecf20Sopenharmony_ci ret = vt_do_kdskbmode(console, arg); 4008c2ecf20Sopenharmony_ci if (ret) 4018c2ecf20Sopenharmony_ci return ret; 4028c2ecf20Sopenharmony_ci tty_ldisc_flush(tty); 4038c2ecf20Sopenharmony_ci break; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci case KDGKBMODE: 4068c2ecf20Sopenharmony_ci return put_user(vt_do_kdgkbmode(console), (int __user *)arg); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* this could be folded into KDSKBMODE, but for compatibility 4098c2ecf20Sopenharmony_ci reasons it is not so easy to fold KDGKBMETA into KDGKBMODE */ 4108c2ecf20Sopenharmony_ci case KDSKBMETA: 4118c2ecf20Sopenharmony_ci return vt_do_kdskbmeta(console, arg); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci case KDGKBMETA: 4148c2ecf20Sopenharmony_ci /* FIXME: should review whether this is worth locking */ 4158c2ecf20Sopenharmony_ci return put_user(vt_do_kdgkbmeta(console), (int __user *)arg); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci case KDGETKEYCODE: 4188c2ecf20Sopenharmony_ci case KDSETKEYCODE: 4198c2ecf20Sopenharmony_ci if(!capable(CAP_SYS_TTY_CONFIG)) 4208c2ecf20Sopenharmony_ci perm = 0; 4218c2ecf20Sopenharmony_ci return vt_do_kbkeycode_ioctl(cmd, up, perm); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci case KDGKBENT: 4248c2ecf20Sopenharmony_ci case KDSKBENT: 4258c2ecf20Sopenharmony_ci return vt_do_kdsk_ioctl(cmd, up, perm, console); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci case KDGKBSENT: 4288c2ecf20Sopenharmony_ci case KDSKBSENT: 4298c2ecf20Sopenharmony_ci return vt_do_kdgkb_ioctl(cmd, up, perm); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* Diacritical processing. Handled in keyboard.c as it has 4328c2ecf20Sopenharmony_ci to operate on the keyboard locks and structures */ 4338c2ecf20Sopenharmony_ci case KDGKBDIACR: 4348c2ecf20Sopenharmony_ci case KDGKBDIACRUC: 4358c2ecf20Sopenharmony_ci case KDSKBDIACR: 4368c2ecf20Sopenharmony_ci case KDSKBDIACRUC: 4378c2ecf20Sopenharmony_ci return vt_do_diacrit(cmd, up, perm); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci /* the ioctls below read/set the flags usually shown in the leds */ 4408c2ecf20Sopenharmony_ci /* don't use them - they will go away without warning */ 4418c2ecf20Sopenharmony_ci case KDGKBLED: 4428c2ecf20Sopenharmony_ci case KDSKBLED: 4438c2ecf20Sopenharmony_ci case KDGETLED: 4448c2ecf20Sopenharmony_ci case KDSETLED: 4458c2ecf20Sopenharmony_ci return vt_do_kdskled(console, cmd, arg, perm); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci /* 4488c2ecf20Sopenharmony_ci * A process can indicate its willingness to accept signals 4498c2ecf20Sopenharmony_ci * generated by pressing an appropriate key combination. 4508c2ecf20Sopenharmony_ci * Thus, one can have a daemon that e.g. spawns a new console 4518c2ecf20Sopenharmony_ci * upon a keypress and then changes to it. 4528c2ecf20Sopenharmony_ci * See also the kbrequest field of inittab(5). 4538c2ecf20Sopenharmony_ci */ 4548c2ecf20Sopenharmony_ci case KDSIGACCEPT: 4558c2ecf20Sopenharmony_ci if (!perm || !capable(CAP_KILL)) 4568c2ecf20Sopenharmony_ci return -EPERM; 4578c2ecf20Sopenharmony_ci if (!valid_signal(arg) || arg < 1 || arg == SIGKILL) 4588c2ecf20Sopenharmony_ci return -EINVAL; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci spin_lock_irq(&vt_spawn_con.lock); 4618c2ecf20Sopenharmony_ci put_pid(vt_spawn_con.pid); 4628c2ecf20Sopenharmony_ci vt_spawn_con.pid = get_pid(task_pid(current)); 4638c2ecf20Sopenharmony_ci vt_spawn_con.sig = arg; 4648c2ecf20Sopenharmony_ci spin_unlock_irq(&vt_spawn_con.lock); 4658c2ecf20Sopenharmony_ci break; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci case KDFONTOP: { 4688c2ecf20Sopenharmony_ci struct console_font_op op; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if (copy_from_user(&op, up, sizeof(op))) 4718c2ecf20Sopenharmony_ci return -EFAULT; 4728c2ecf20Sopenharmony_ci if (!perm && op.op != KD_FONT_OP_GET) 4738c2ecf20Sopenharmony_ci return -EPERM; 4748c2ecf20Sopenharmony_ci ret = con_font_op(vc, &op); 4758c2ecf20Sopenharmony_ci if (ret) 4768c2ecf20Sopenharmony_ci return ret; 4778c2ecf20Sopenharmony_ci if (copy_to_user(up, &op, sizeof(op))) 4788c2ecf20Sopenharmony_ci return -EFAULT; 4798c2ecf20Sopenharmony_ci break; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci default: 4838c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci return 0; 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic inline int do_unimap_ioctl(int cmd, struct unimapdesc __user *user_ud, 4908c2ecf20Sopenharmony_ci bool perm, struct vc_data *vc) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci struct unimapdesc tmp; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci if (copy_from_user(&tmp, user_ud, sizeof tmp)) 4958c2ecf20Sopenharmony_ci return -EFAULT; 4968c2ecf20Sopenharmony_ci switch (cmd) { 4978c2ecf20Sopenharmony_ci case PIO_UNIMAP: 4988c2ecf20Sopenharmony_ci if (!perm) 4998c2ecf20Sopenharmony_ci return -EPERM; 5008c2ecf20Sopenharmony_ci return con_set_unimap(vc, tmp.entry_ct, tmp.entries); 5018c2ecf20Sopenharmony_ci case GIO_UNIMAP: 5028c2ecf20Sopenharmony_ci if (!perm && fg_console != vc->vc_num) 5038c2ecf20Sopenharmony_ci return -EPERM; 5048c2ecf20Sopenharmony_ci return con_get_unimap(vc, tmp.entry_ct, &(user_ud->entry_ct), 5058c2ecf20Sopenharmony_ci tmp.entries); 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci return 0; 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic int vt_io_ioctl(struct vc_data *vc, unsigned int cmd, void __user *up, 5118c2ecf20Sopenharmony_ci bool perm) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci switch (cmd) { 5148c2ecf20Sopenharmony_ci case PIO_CMAP: 5158c2ecf20Sopenharmony_ci if (!perm) 5168c2ecf20Sopenharmony_ci return -EPERM; 5178c2ecf20Sopenharmony_ci return con_set_cmap(up); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci case GIO_CMAP: 5208c2ecf20Sopenharmony_ci return con_get_cmap(up); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci case PIO_SCRNMAP: 5238c2ecf20Sopenharmony_ci if (!perm) 5248c2ecf20Sopenharmony_ci return -EPERM; 5258c2ecf20Sopenharmony_ci return con_set_trans_old(up); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci case GIO_SCRNMAP: 5288c2ecf20Sopenharmony_ci return con_get_trans_old(up); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci case PIO_UNISCRNMAP: 5318c2ecf20Sopenharmony_ci if (!perm) 5328c2ecf20Sopenharmony_ci return -EPERM; 5338c2ecf20Sopenharmony_ci return con_set_trans_new(up); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci case GIO_UNISCRNMAP: 5368c2ecf20Sopenharmony_ci return con_get_trans_new(up); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci case PIO_UNIMAPCLR: 5398c2ecf20Sopenharmony_ci if (!perm) 5408c2ecf20Sopenharmony_ci return -EPERM; 5418c2ecf20Sopenharmony_ci con_clear_unimap(vc); 5428c2ecf20Sopenharmony_ci break; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci case PIO_UNIMAP: 5458c2ecf20Sopenharmony_ci case GIO_UNIMAP: 5468c2ecf20Sopenharmony_ci return do_unimap_ioctl(cmd, up, perm, vc); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci default: 5498c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci return 0; 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistatic int vt_reldisp(struct vc_data *vc, unsigned int swtch) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci int newvt, ret; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci if (vc->vt_mode.mode != VT_PROCESS) 5608c2ecf20Sopenharmony_ci return -EINVAL; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci /* Switched-to response */ 5638c2ecf20Sopenharmony_ci if (vc->vt_newvt < 0) { 5648c2ecf20Sopenharmony_ci /* If it's just an ACK, ignore it */ 5658c2ecf20Sopenharmony_ci return swtch == VT_ACKACQ ? 0 : -EINVAL; 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci /* Switching-from response */ 5698c2ecf20Sopenharmony_ci if (swtch == 0) { 5708c2ecf20Sopenharmony_ci /* Switch disallowed, so forget we were trying to do it. */ 5718c2ecf20Sopenharmony_ci vc->vt_newvt = -1; 5728c2ecf20Sopenharmony_ci return 0; 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci /* The current vt has been released, so complete the switch. */ 5768c2ecf20Sopenharmony_ci newvt = vc->vt_newvt; 5778c2ecf20Sopenharmony_ci vc->vt_newvt = -1; 5788c2ecf20Sopenharmony_ci ret = vc_allocate(newvt); 5798c2ecf20Sopenharmony_ci if (ret) 5808c2ecf20Sopenharmony_ci return ret; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci /* 5838c2ecf20Sopenharmony_ci * When we actually do the console switch, make sure we are atomic with 5848c2ecf20Sopenharmony_ci * respect to other console switches.. 5858c2ecf20Sopenharmony_ci */ 5868c2ecf20Sopenharmony_ci complete_change_console(vc_cons[newvt].d); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci return 0; 5898c2ecf20Sopenharmony_ci} 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_cistatic int vt_setactivate(struct vt_setactivate __user *sa) 5928c2ecf20Sopenharmony_ci{ 5938c2ecf20Sopenharmony_ci struct vt_setactivate vsa; 5948c2ecf20Sopenharmony_ci struct vc_data *nvc; 5958c2ecf20Sopenharmony_ci int ret; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (copy_from_user(&vsa, sa, sizeof(vsa))) 5988c2ecf20Sopenharmony_ci return -EFAULT; 5998c2ecf20Sopenharmony_ci if (vsa.console == 0 || vsa.console > MAX_NR_CONSOLES) 6008c2ecf20Sopenharmony_ci return -ENXIO; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci vsa.console--; 6038c2ecf20Sopenharmony_ci vsa.console = array_index_nospec(vsa.console, MAX_NR_CONSOLES); 6048c2ecf20Sopenharmony_ci console_lock(); 6058c2ecf20Sopenharmony_ci ret = vc_allocate(vsa.console); 6068c2ecf20Sopenharmony_ci if (ret) { 6078c2ecf20Sopenharmony_ci console_unlock(); 6088c2ecf20Sopenharmony_ci return ret; 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci /* 6128c2ecf20Sopenharmony_ci * This is safe providing we don't drop the console sem between 6138c2ecf20Sopenharmony_ci * vc_allocate and finishing referencing nvc. 6148c2ecf20Sopenharmony_ci */ 6158c2ecf20Sopenharmony_ci nvc = vc_cons[vsa.console].d; 6168c2ecf20Sopenharmony_ci nvc->vt_mode = vsa.mode; 6178c2ecf20Sopenharmony_ci nvc->vt_mode.frsig = 0; 6188c2ecf20Sopenharmony_ci put_pid(nvc->vt_pid); 6198c2ecf20Sopenharmony_ci nvc->vt_pid = get_pid(task_pid(current)); 6208c2ecf20Sopenharmony_ci console_unlock(); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci /* Commence switch and lock */ 6238c2ecf20Sopenharmony_ci /* Review set_console locks */ 6248c2ecf20Sopenharmony_ci set_console(vsa.console); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci return 0; 6278c2ecf20Sopenharmony_ci} 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci/* deallocate a single console, if possible (leave 0) */ 6308c2ecf20Sopenharmony_cistatic int vt_disallocate(unsigned int vc_num) 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci struct vc_data *vc = NULL; 6338c2ecf20Sopenharmony_ci int ret = 0; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci console_lock(); 6368c2ecf20Sopenharmony_ci if (vt_busy(vc_num)) 6378c2ecf20Sopenharmony_ci ret = -EBUSY; 6388c2ecf20Sopenharmony_ci else if (vc_num) 6398c2ecf20Sopenharmony_ci vc = vc_deallocate(vc_num); 6408c2ecf20Sopenharmony_ci console_unlock(); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci if (vc && vc_num >= MIN_NR_CONSOLES) 6438c2ecf20Sopenharmony_ci tty_port_put(&vc->port); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci return ret; 6468c2ecf20Sopenharmony_ci} 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci/* deallocate all unused consoles, but leave 0 */ 6498c2ecf20Sopenharmony_cistatic void vt_disallocate_all(void) 6508c2ecf20Sopenharmony_ci{ 6518c2ecf20Sopenharmony_ci struct vc_data *vc[MAX_NR_CONSOLES]; 6528c2ecf20Sopenharmony_ci int i; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci console_lock(); 6558c2ecf20Sopenharmony_ci for (i = 1; i < MAX_NR_CONSOLES; i++) 6568c2ecf20Sopenharmony_ci if (!vt_busy(i)) 6578c2ecf20Sopenharmony_ci vc[i] = vc_deallocate(i); 6588c2ecf20Sopenharmony_ci else 6598c2ecf20Sopenharmony_ci vc[i] = NULL; 6608c2ecf20Sopenharmony_ci console_unlock(); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci for (i = 1; i < MAX_NR_CONSOLES; i++) { 6638c2ecf20Sopenharmony_ci if (vc[i] && i >= MIN_NR_CONSOLES) 6648c2ecf20Sopenharmony_ci tty_port_put(&vc[i]->port); 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci} 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cistatic int vt_resizex(struct vc_data *vc, struct vt_consize __user *cs) 6698c2ecf20Sopenharmony_ci{ 6708c2ecf20Sopenharmony_ci struct vt_consize v; 6718c2ecf20Sopenharmony_ci int i; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci if (copy_from_user(&v, cs, sizeof(struct vt_consize))) 6748c2ecf20Sopenharmony_ci return -EFAULT; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci /* FIXME: Should check the copies properly */ 6778c2ecf20Sopenharmony_ci if (!v.v_vlin) 6788c2ecf20Sopenharmony_ci v.v_vlin = vc->vc_scan_lines; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci if (v.v_clin) { 6818c2ecf20Sopenharmony_ci int rows = v.v_vlin / v.v_clin; 6828c2ecf20Sopenharmony_ci if (v.v_rows != rows) { 6838c2ecf20Sopenharmony_ci if (v.v_rows) /* Parameters don't add up */ 6848c2ecf20Sopenharmony_ci return -EINVAL; 6858c2ecf20Sopenharmony_ci v.v_rows = rows; 6868c2ecf20Sopenharmony_ci } 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci if (v.v_vcol && v.v_ccol) { 6908c2ecf20Sopenharmony_ci int cols = v.v_vcol / v.v_ccol; 6918c2ecf20Sopenharmony_ci if (v.v_cols != cols) { 6928c2ecf20Sopenharmony_ci if (v.v_cols) 6938c2ecf20Sopenharmony_ci return -EINVAL; 6948c2ecf20Sopenharmony_ci v.v_cols = cols; 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci if (v.v_clin > 32) 6998c2ecf20Sopenharmony_ci return -EINVAL; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci for (i = 0; i < MAX_NR_CONSOLES; i++) { 7028c2ecf20Sopenharmony_ci struct vc_data *vcp; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci if (!vc_cons[i].d) 7058c2ecf20Sopenharmony_ci continue; 7068c2ecf20Sopenharmony_ci console_lock(); 7078c2ecf20Sopenharmony_ci vcp = vc_cons[i].d; 7088c2ecf20Sopenharmony_ci if (vcp) { 7098c2ecf20Sopenharmony_ci int ret; 7108c2ecf20Sopenharmony_ci int save_scan_lines = vcp->vc_scan_lines; 7118c2ecf20Sopenharmony_ci int save_cell_height = vcp->vc_cell_height; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci if (v.v_vlin) 7148c2ecf20Sopenharmony_ci vcp->vc_scan_lines = v.v_vlin; 7158c2ecf20Sopenharmony_ci if (v.v_clin) 7168c2ecf20Sopenharmony_ci vcp->vc_cell_height = v.v_clin; 7178c2ecf20Sopenharmony_ci vcp->vc_resize_user = 1; 7188c2ecf20Sopenharmony_ci ret = vc_resize(vcp, v.v_cols, v.v_rows); 7198c2ecf20Sopenharmony_ci if (ret) { 7208c2ecf20Sopenharmony_ci vcp->vc_scan_lines = save_scan_lines; 7218c2ecf20Sopenharmony_ci vcp->vc_cell_height = save_cell_height; 7228c2ecf20Sopenharmony_ci console_unlock(); 7238c2ecf20Sopenharmony_ci return ret; 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci console_unlock(); 7278c2ecf20Sopenharmony_ci } 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci return 0; 7308c2ecf20Sopenharmony_ci} 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci/* 7338c2ecf20Sopenharmony_ci * We handle the console-specific ioctl's here. We allow the 7348c2ecf20Sopenharmony_ci * capability to modify any console, not just the fg_console. 7358c2ecf20Sopenharmony_ci */ 7368c2ecf20Sopenharmony_ciint vt_ioctl(struct tty_struct *tty, 7378c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 7388c2ecf20Sopenharmony_ci{ 7398c2ecf20Sopenharmony_ci struct vc_data *vc = tty->driver_data; 7408c2ecf20Sopenharmony_ci void __user *up = (void __user *)arg; 7418c2ecf20Sopenharmony_ci int i, perm; 7428c2ecf20Sopenharmony_ci int ret; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci /* 7458c2ecf20Sopenharmony_ci * To have permissions to do most of the vt ioctls, we either have 7468c2ecf20Sopenharmony_ci * to be the owner of the tty, or have CAP_SYS_TTY_CONFIG. 7478c2ecf20Sopenharmony_ci */ 7488c2ecf20Sopenharmony_ci perm = 0; 7498c2ecf20Sopenharmony_ci if (current->signal->tty == tty || capable(CAP_SYS_TTY_CONFIG)) 7508c2ecf20Sopenharmony_ci perm = 1; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci ret = vt_k_ioctl(tty, cmd, arg, perm); 7538c2ecf20Sopenharmony_ci if (ret != -ENOIOCTLCMD) 7548c2ecf20Sopenharmony_ci return ret; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci ret = vt_io_ioctl(vc, cmd, up, perm); 7578c2ecf20Sopenharmony_ci if (ret != -ENOIOCTLCMD) 7588c2ecf20Sopenharmony_ci return ret; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci switch (cmd) { 7618c2ecf20Sopenharmony_ci case TIOCLINUX: 7628c2ecf20Sopenharmony_ci return tioclinux(tty, arg); 7638c2ecf20Sopenharmony_ci case VT_SETMODE: 7648c2ecf20Sopenharmony_ci { 7658c2ecf20Sopenharmony_ci struct vt_mode tmp; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci if (!perm) 7688c2ecf20Sopenharmony_ci return -EPERM; 7698c2ecf20Sopenharmony_ci if (copy_from_user(&tmp, up, sizeof(struct vt_mode))) 7708c2ecf20Sopenharmony_ci return -EFAULT; 7718c2ecf20Sopenharmony_ci if (tmp.mode != VT_AUTO && tmp.mode != VT_PROCESS) 7728c2ecf20Sopenharmony_ci return -EINVAL; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci console_lock(); 7758c2ecf20Sopenharmony_ci vc->vt_mode = tmp; 7768c2ecf20Sopenharmony_ci /* the frsig is ignored, so we set it to 0 */ 7778c2ecf20Sopenharmony_ci vc->vt_mode.frsig = 0; 7788c2ecf20Sopenharmony_ci put_pid(vc->vt_pid); 7798c2ecf20Sopenharmony_ci vc->vt_pid = get_pid(task_pid(current)); 7808c2ecf20Sopenharmony_ci /* no switch is required -- saw@shade.msu.ru */ 7818c2ecf20Sopenharmony_ci vc->vt_newvt = -1; 7828c2ecf20Sopenharmony_ci console_unlock(); 7838c2ecf20Sopenharmony_ci break; 7848c2ecf20Sopenharmony_ci } 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci case VT_GETMODE: 7878c2ecf20Sopenharmony_ci { 7888c2ecf20Sopenharmony_ci struct vt_mode tmp; 7898c2ecf20Sopenharmony_ci int rc; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci console_lock(); 7928c2ecf20Sopenharmony_ci memcpy(&tmp, &vc->vt_mode, sizeof(struct vt_mode)); 7938c2ecf20Sopenharmony_ci console_unlock(); 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci rc = copy_to_user(up, &tmp, sizeof(struct vt_mode)); 7968c2ecf20Sopenharmony_ci if (rc) 7978c2ecf20Sopenharmony_ci return -EFAULT; 7988c2ecf20Sopenharmony_ci break; 7998c2ecf20Sopenharmony_ci } 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci /* 8028c2ecf20Sopenharmony_ci * Returns global vt state. Note that VT 0 is always open, since 8038c2ecf20Sopenharmony_ci * it's an alias for the current VT, and people can't use it here. 8048c2ecf20Sopenharmony_ci * We cannot return state for more than 16 VTs, since v_state is short. 8058c2ecf20Sopenharmony_ci */ 8068c2ecf20Sopenharmony_ci case VT_GETSTATE: 8078c2ecf20Sopenharmony_ci { 8088c2ecf20Sopenharmony_ci struct vt_stat __user *vtstat = up; 8098c2ecf20Sopenharmony_ci unsigned short state, mask; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci if (put_user(fg_console + 1, &vtstat->v_active)) 8128c2ecf20Sopenharmony_ci return -EFAULT; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci state = 1; /* /dev/tty0 is always open */ 8158c2ecf20Sopenharmony_ci console_lock(); /* required by vt_in_use() */ 8168c2ecf20Sopenharmony_ci for (i = 0, mask = 2; i < MAX_NR_CONSOLES && mask; 8178c2ecf20Sopenharmony_ci ++i, mask <<= 1) 8188c2ecf20Sopenharmony_ci if (vt_in_use(i)) 8198c2ecf20Sopenharmony_ci state |= mask; 8208c2ecf20Sopenharmony_ci console_unlock(); 8218c2ecf20Sopenharmony_ci return put_user(state, &vtstat->v_state); 8228c2ecf20Sopenharmony_ci } 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci /* 8258c2ecf20Sopenharmony_ci * Returns the first available (non-opened) console. 8268c2ecf20Sopenharmony_ci */ 8278c2ecf20Sopenharmony_ci case VT_OPENQRY: 8288c2ecf20Sopenharmony_ci console_lock(); /* required by vt_in_use() */ 8298c2ecf20Sopenharmony_ci for (i = 0; i < MAX_NR_CONSOLES; ++i) 8308c2ecf20Sopenharmony_ci if (!vt_in_use(i)) 8318c2ecf20Sopenharmony_ci break; 8328c2ecf20Sopenharmony_ci console_unlock(); 8338c2ecf20Sopenharmony_ci i = i < MAX_NR_CONSOLES ? (i+1) : -1; 8348c2ecf20Sopenharmony_ci return put_user(i, (int __user *)arg); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci /* 8378c2ecf20Sopenharmony_ci * ioctl(fd, VT_ACTIVATE, num) will cause us to switch to vt # num, 8388c2ecf20Sopenharmony_ci * with num >= 1 (switches to vt 0, our console, are not allowed, just 8398c2ecf20Sopenharmony_ci * to preserve sanity). 8408c2ecf20Sopenharmony_ci */ 8418c2ecf20Sopenharmony_ci case VT_ACTIVATE: 8428c2ecf20Sopenharmony_ci if (!perm) 8438c2ecf20Sopenharmony_ci return -EPERM; 8448c2ecf20Sopenharmony_ci if (arg == 0 || arg > MAX_NR_CONSOLES) 8458c2ecf20Sopenharmony_ci return -ENXIO; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci arg--; 8488c2ecf20Sopenharmony_ci arg = array_index_nospec(arg, MAX_NR_CONSOLES); 8498c2ecf20Sopenharmony_ci console_lock(); 8508c2ecf20Sopenharmony_ci ret = vc_allocate(arg); 8518c2ecf20Sopenharmony_ci console_unlock(); 8528c2ecf20Sopenharmony_ci if (ret) 8538c2ecf20Sopenharmony_ci return ret; 8548c2ecf20Sopenharmony_ci set_console(arg); 8558c2ecf20Sopenharmony_ci break; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci case VT_SETACTIVATE: 8588c2ecf20Sopenharmony_ci if (!perm) 8598c2ecf20Sopenharmony_ci return -EPERM; 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci return vt_setactivate(up); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci /* 8648c2ecf20Sopenharmony_ci * wait until the specified VT has been activated 8658c2ecf20Sopenharmony_ci */ 8668c2ecf20Sopenharmony_ci case VT_WAITACTIVE: 8678c2ecf20Sopenharmony_ci if (!perm) 8688c2ecf20Sopenharmony_ci return -EPERM; 8698c2ecf20Sopenharmony_ci if (arg == 0 || arg > MAX_NR_CONSOLES) 8708c2ecf20Sopenharmony_ci return -ENXIO; 8718c2ecf20Sopenharmony_ci return vt_waitactive(arg); 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci /* 8748c2ecf20Sopenharmony_ci * If a vt is under process control, the kernel will not switch to it 8758c2ecf20Sopenharmony_ci * immediately, but postpone the operation until the process calls this 8768c2ecf20Sopenharmony_ci * ioctl, allowing the switch to complete. 8778c2ecf20Sopenharmony_ci * 8788c2ecf20Sopenharmony_ci * According to the X sources this is the behavior: 8798c2ecf20Sopenharmony_ci * 0: pending switch-from not OK 8808c2ecf20Sopenharmony_ci * 1: pending switch-from OK 8818c2ecf20Sopenharmony_ci * 2: completed switch-to OK 8828c2ecf20Sopenharmony_ci */ 8838c2ecf20Sopenharmony_ci case VT_RELDISP: 8848c2ecf20Sopenharmony_ci if (!perm) 8858c2ecf20Sopenharmony_ci return -EPERM; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci console_lock(); 8888c2ecf20Sopenharmony_ci ret = vt_reldisp(vc, arg); 8898c2ecf20Sopenharmony_ci console_unlock(); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci return ret; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci /* 8958c2ecf20Sopenharmony_ci * Disallocate memory associated to VT (but leave VT1) 8968c2ecf20Sopenharmony_ci */ 8978c2ecf20Sopenharmony_ci case VT_DISALLOCATE: 8988c2ecf20Sopenharmony_ci if (arg > MAX_NR_CONSOLES) 8998c2ecf20Sopenharmony_ci return -ENXIO; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci if (arg == 0) 9028c2ecf20Sopenharmony_ci vt_disallocate_all(); 9038c2ecf20Sopenharmony_ci else 9048c2ecf20Sopenharmony_ci return vt_disallocate(--arg); 9058c2ecf20Sopenharmony_ci break; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci case VT_RESIZE: 9088c2ecf20Sopenharmony_ci { 9098c2ecf20Sopenharmony_ci struct vt_sizes __user *vtsizes = up; 9108c2ecf20Sopenharmony_ci struct vc_data *vc; 9118c2ecf20Sopenharmony_ci ushort ll,cc; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci if (!perm) 9148c2ecf20Sopenharmony_ci return -EPERM; 9158c2ecf20Sopenharmony_ci if (get_user(ll, &vtsizes->v_rows) || 9168c2ecf20Sopenharmony_ci get_user(cc, &vtsizes->v_cols)) 9178c2ecf20Sopenharmony_ci return -EFAULT; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci console_lock(); 9208c2ecf20Sopenharmony_ci for (i = 0; i < MAX_NR_CONSOLES; i++) { 9218c2ecf20Sopenharmony_ci vc = vc_cons[i].d; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci if (vc) { 9248c2ecf20Sopenharmony_ci vc->vc_resize_user = 1; 9258c2ecf20Sopenharmony_ci /* FIXME: review v tty lock */ 9268c2ecf20Sopenharmony_ci vc_resize(vc_cons[i].d, cc, ll); 9278c2ecf20Sopenharmony_ci } 9288c2ecf20Sopenharmony_ci } 9298c2ecf20Sopenharmony_ci console_unlock(); 9308c2ecf20Sopenharmony_ci break; 9318c2ecf20Sopenharmony_ci } 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci case VT_RESIZEX: 9348c2ecf20Sopenharmony_ci if (!perm) 9358c2ecf20Sopenharmony_ci return -EPERM; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci return vt_resizex(vc, up); 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci case VT_LOCKSWITCH: 9408c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_TTY_CONFIG)) 9418c2ecf20Sopenharmony_ci return -EPERM; 9428c2ecf20Sopenharmony_ci vt_dont_switch = true; 9438c2ecf20Sopenharmony_ci break; 9448c2ecf20Sopenharmony_ci case VT_UNLOCKSWITCH: 9458c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_TTY_CONFIG)) 9468c2ecf20Sopenharmony_ci return -EPERM; 9478c2ecf20Sopenharmony_ci vt_dont_switch = false; 9488c2ecf20Sopenharmony_ci break; 9498c2ecf20Sopenharmony_ci case VT_GETHIFONTMASK: 9508c2ecf20Sopenharmony_ci return put_user(vc->vc_hi_font_mask, 9518c2ecf20Sopenharmony_ci (unsigned short __user *)arg); 9528c2ecf20Sopenharmony_ci case VT_WAITEVENT: 9538c2ecf20Sopenharmony_ci return vt_event_wait_ioctl((struct vt_event __user *)arg); 9548c2ecf20Sopenharmony_ci default: 9558c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 9568c2ecf20Sopenharmony_ci } 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci return 0; 9598c2ecf20Sopenharmony_ci} 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_civoid reset_vc(struct vc_data *vc) 9628c2ecf20Sopenharmony_ci{ 9638c2ecf20Sopenharmony_ci vc->vc_mode = KD_TEXT; 9648c2ecf20Sopenharmony_ci vt_reset_unicode(vc->vc_num); 9658c2ecf20Sopenharmony_ci vc->vt_mode.mode = VT_AUTO; 9668c2ecf20Sopenharmony_ci vc->vt_mode.waitv = 0; 9678c2ecf20Sopenharmony_ci vc->vt_mode.relsig = 0; 9688c2ecf20Sopenharmony_ci vc->vt_mode.acqsig = 0; 9698c2ecf20Sopenharmony_ci vc->vt_mode.frsig = 0; 9708c2ecf20Sopenharmony_ci put_pid(vc->vt_pid); 9718c2ecf20Sopenharmony_ci vc->vt_pid = NULL; 9728c2ecf20Sopenharmony_ci vc->vt_newvt = -1; 9738c2ecf20Sopenharmony_ci if (!in_interrupt()) /* Via keyboard.c:SAK() - akpm */ 9748c2ecf20Sopenharmony_ci reset_palette(vc); 9758c2ecf20Sopenharmony_ci} 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_civoid vc_SAK(struct work_struct *work) 9788c2ecf20Sopenharmony_ci{ 9798c2ecf20Sopenharmony_ci struct vc *vc_con = 9808c2ecf20Sopenharmony_ci container_of(work, struct vc, SAK_work); 9818c2ecf20Sopenharmony_ci struct vc_data *vc; 9828c2ecf20Sopenharmony_ci struct tty_struct *tty; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci console_lock(); 9858c2ecf20Sopenharmony_ci vc = vc_con->d; 9868c2ecf20Sopenharmony_ci if (vc) { 9878c2ecf20Sopenharmony_ci /* FIXME: review tty ref counting */ 9888c2ecf20Sopenharmony_ci tty = vc->port.tty; 9898c2ecf20Sopenharmony_ci /* 9908c2ecf20Sopenharmony_ci * SAK should also work in all raw modes and reset 9918c2ecf20Sopenharmony_ci * them properly. 9928c2ecf20Sopenharmony_ci */ 9938c2ecf20Sopenharmony_ci if (tty) 9948c2ecf20Sopenharmony_ci __do_SAK(tty); 9958c2ecf20Sopenharmony_ci reset_vc(vc); 9968c2ecf20Sopenharmony_ci } 9978c2ecf20Sopenharmony_ci console_unlock(); 9988c2ecf20Sopenharmony_ci} 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_cistruct compat_console_font_op { 10038c2ecf20Sopenharmony_ci compat_uint_t op; /* operation code KD_FONT_OP_* */ 10048c2ecf20Sopenharmony_ci compat_uint_t flags; /* KD_FONT_FLAG_* */ 10058c2ecf20Sopenharmony_ci compat_uint_t width, height; /* font size */ 10068c2ecf20Sopenharmony_ci compat_uint_t charcount; 10078c2ecf20Sopenharmony_ci compat_caddr_t data; /* font data with height fixed to 32 */ 10088c2ecf20Sopenharmony_ci}; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_cistatic inline int 10118c2ecf20Sopenharmony_cicompat_kdfontop_ioctl(struct compat_console_font_op __user *fontop, 10128c2ecf20Sopenharmony_ci int perm, struct console_font_op *op, struct vc_data *vc) 10138c2ecf20Sopenharmony_ci{ 10148c2ecf20Sopenharmony_ci int i; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci if (copy_from_user(op, fontop, sizeof(struct compat_console_font_op))) 10178c2ecf20Sopenharmony_ci return -EFAULT; 10188c2ecf20Sopenharmony_ci if (!perm && op->op != KD_FONT_OP_GET) 10198c2ecf20Sopenharmony_ci return -EPERM; 10208c2ecf20Sopenharmony_ci op->data = compat_ptr(((struct compat_console_font_op *)op)->data); 10218c2ecf20Sopenharmony_ci i = con_font_op(vc, op); 10228c2ecf20Sopenharmony_ci if (i) 10238c2ecf20Sopenharmony_ci return i; 10248c2ecf20Sopenharmony_ci ((struct compat_console_font_op *)op)->data = (unsigned long)op->data; 10258c2ecf20Sopenharmony_ci if (copy_to_user(fontop, op, sizeof(struct compat_console_font_op))) 10268c2ecf20Sopenharmony_ci return -EFAULT; 10278c2ecf20Sopenharmony_ci return 0; 10288c2ecf20Sopenharmony_ci} 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_cistruct compat_unimapdesc { 10318c2ecf20Sopenharmony_ci unsigned short entry_ct; 10328c2ecf20Sopenharmony_ci compat_caddr_t entries; 10338c2ecf20Sopenharmony_ci}; 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_cistatic inline int 10368c2ecf20Sopenharmony_cicompat_unimap_ioctl(unsigned int cmd, struct compat_unimapdesc __user *user_ud, 10378c2ecf20Sopenharmony_ci int perm, struct vc_data *vc) 10388c2ecf20Sopenharmony_ci{ 10398c2ecf20Sopenharmony_ci struct compat_unimapdesc tmp; 10408c2ecf20Sopenharmony_ci struct unipair __user *tmp_entries; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci if (copy_from_user(&tmp, user_ud, sizeof tmp)) 10438c2ecf20Sopenharmony_ci return -EFAULT; 10448c2ecf20Sopenharmony_ci tmp_entries = compat_ptr(tmp.entries); 10458c2ecf20Sopenharmony_ci switch (cmd) { 10468c2ecf20Sopenharmony_ci case PIO_UNIMAP: 10478c2ecf20Sopenharmony_ci if (!perm) 10488c2ecf20Sopenharmony_ci return -EPERM; 10498c2ecf20Sopenharmony_ci return con_set_unimap(vc, tmp.entry_ct, tmp_entries); 10508c2ecf20Sopenharmony_ci case GIO_UNIMAP: 10518c2ecf20Sopenharmony_ci if (!perm && fg_console != vc->vc_num) 10528c2ecf20Sopenharmony_ci return -EPERM; 10538c2ecf20Sopenharmony_ci return con_get_unimap(vc, tmp.entry_ct, &(user_ud->entry_ct), tmp_entries); 10548c2ecf20Sopenharmony_ci } 10558c2ecf20Sopenharmony_ci return 0; 10568c2ecf20Sopenharmony_ci} 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_cilong vt_compat_ioctl(struct tty_struct *tty, 10598c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 10608c2ecf20Sopenharmony_ci{ 10618c2ecf20Sopenharmony_ci struct vc_data *vc = tty->driver_data; 10628c2ecf20Sopenharmony_ci struct console_font_op op; /* used in multiple places here */ 10638c2ecf20Sopenharmony_ci void __user *up = compat_ptr(arg); 10648c2ecf20Sopenharmony_ci int perm; 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci /* 10678c2ecf20Sopenharmony_ci * To have permissions to do most of the vt ioctls, we either have 10688c2ecf20Sopenharmony_ci * to be the owner of the tty, or have CAP_SYS_TTY_CONFIG. 10698c2ecf20Sopenharmony_ci */ 10708c2ecf20Sopenharmony_ci perm = 0; 10718c2ecf20Sopenharmony_ci if (current->signal->tty == tty || capable(CAP_SYS_TTY_CONFIG)) 10728c2ecf20Sopenharmony_ci perm = 1; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci switch (cmd) { 10758c2ecf20Sopenharmony_ci /* 10768c2ecf20Sopenharmony_ci * these need special handlers for incompatible data structures 10778c2ecf20Sopenharmony_ci */ 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci case KDFONTOP: 10808c2ecf20Sopenharmony_ci return compat_kdfontop_ioctl(up, perm, &op, vc); 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci case PIO_UNIMAP: 10838c2ecf20Sopenharmony_ci case GIO_UNIMAP: 10848c2ecf20Sopenharmony_ci return compat_unimap_ioctl(cmd, up, perm, vc); 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci /* 10878c2ecf20Sopenharmony_ci * all these treat 'arg' as an integer 10888c2ecf20Sopenharmony_ci */ 10898c2ecf20Sopenharmony_ci case KIOCSOUND: 10908c2ecf20Sopenharmony_ci case KDMKTONE: 10918c2ecf20Sopenharmony_ci#ifdef CONFIG_X86 10928c2ecf20Sopenharmony_ci case KDADDIO: 10938c2ecf20Sopenharmony_ci case KDDELIO: 10948c2ecf20Sopenharmony_ci#endif 10958c2ecf20Sopenharmony_ci case KDSETMODE: 10968c2ecf20Sopenharmony_ci case KDMAPDISP: 10978c2ecf20Sopenharmony_ci case KDUNMAPDISP: 10988c2ecf20Sopenharmony_ci case KDSKBMODE: 10998c2ecf20Sopenharmony_ci case KDSKBMETA: 11008c2ecf20Sopenharmony_ci case KDSKBLED: 11018c2ecf20Sopenharmony_ci case KDSETLED: 11028c2ecf20Sopenharmony_ci case KDSIGACCEPT: 11038c2ecf20Sopenharmony_ci case VT_ACTIVATE: 11048c2ecf20Sopenharmony_ci case VT_WAITACTIVE: 11058c2ecf20Sopenharmony_ci case VT_RELDISP: 11068c2ecf20Sopenharmony_ci case VT_DISALLOCATE: 11078c2ecf20Sopenharmony_ci case VT_RESIZE: 11088c2ecf20Sopenharmony_ci case VT_RESIZEX: 11098c2ecf20Sopenharmony_ci return vt_ioctl(tty, cmd, arg); 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci /* 11128c2ecf20Sopenharmony_ci * the rest has a compatible data structure behind arg, 11138c2ecf20Sopenharmony_ci * but we have to convert it to a proper 64 bit pointer. 11148c2ecf20Sopenharmony_ci */ 11158c2ecf20Sopenharmony_ci default: 11168c2ecf20Sopenharmony_ci return vt_ioctl(tty, cmd, (unsigned long)up); 11178c2ecf20Sopenharmony_ci } 11188c2ecf20Sopenharmony_ci} 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci#endif /* CONFIG_COMPAT */ 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci/* 11258c2ecf20Sopenharmony_ci * Performs the back end of a vt switch. Called under the console 11268c2ecf20Sopenharmony_ci * semaphore. 11278c2ecf20Sopenharmony_ci */ 11288c2ecf20Sopenharmony_cistatic void complete_change_console(struct vc_data *vc) 11298c2ecf20Sopenharmony_ci{ 11308c2ecf20Sopenharmony_ci unsigned char old_vc_mode; 11318c2ecf20Sopenharmony_ci int old = fg_console; 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci last_console = fg_console; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci /* 11368c2ecf20Sopenharmony_ci * If we're switching, we could be going from KD_GRAPHICS to 11378c2ecf20Sopenharmony_ci * KD_TEXT mode or vice versa, which means we need to blank or 11388c2ecf20Sopenharmony_ci * unblank the screen later. 11398c2ecf20Sopenharmony_ci */ 11408c2ecf20Sopenharmony_ci old_vc_mode = vc_cons[fg_console].d->vc_mode; 11418c2ecf20Sopenharmony_ci switch_screen(vc); 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci /* 11448c2ecf20Sopenharmony_ci * This can't appear below a successful kill_pid(). If it did, 11458c2ecf20Sopenharmony_ci * then the *blank_screen operation could occur while X, having 11468c2ecf20Sopenharmony_ci * received acqsig, is waking up on another processor. This 11478c2ecf20Sopenharmony_ci * condition can lead to overlapping accesses to the VGA range 11488c2ecf20Sopenharmony_ci * and the framebuffer (causing system lockups). 11498c2ecf20Sopenharmony_ci * 11508c2ecf20Sopenharmony_ci * To account for this we duplicate this code below only if the 11518c2ecf20Sopenharmony_ci * controlling process is gone and we've called reset_vc. 11528c2ecf20Sopenharmony_ci */ 11538c2ecf20Sopenharmony_ci if (old_vc_mode != vc->vc_mode) { 11548c2ecf20Sopenharmony_ci if (vc->vc_mode == KD_TEXT) 11558c2ecf20Sopenharmony_ci do_unblank_screen(1); 11568c2ecf20Sopenharmony_ci else 11578c2ecf20Sopenharmony_ci do_blank_screen(1); 11588c2ecf20Sopenharmony_ci } 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci /* 11618c2ecf20Sopenharmony_ci * If this new console is under process control, send it a signal 11628c2ecf20Sopenharmony_ci * telling it that it has acquired. Also check if it has died and 11638c2ecf20Sopenharmony_ci * clean up (similar to logic employed in change_console()) 11648c2ecf20Sopenharmony_ci */ 11658c2ecf20Sopenharmony_ci if (vc->vt_mode.mode == VT_PROCESS) { 11668c2ecf20Sopenharmony_ci /* 11678c2ecf20Sopenharmony_ci * Send the signal as privileged - kill_pid() will 11688c2ecf20Sopenharmony_ci * tell us if the process has gone or something else 11698c2ecf20Sopenharmony_ci * is awry 11708c2ecf20Sopenharmony_ci */ 11718c2ecf20Sopenharmony_ci if (kill_pid(vc->vt_pid, vc->vt_mode.acqsig, 1) != 0) { 11728c2ecf20Sopenharmony_ci /* 11738c2ecf20Sopenharmony_ci * The controlling process has died, so we revert back to 11748c2ecf20Sopenharmony_ci * normal operation. In this case, we'll also change back 11758c2ecf20Sopenharmony_ci * to KD_TEXT mode. I'm not sure if this is strictly correct 11768c2ecf20Sopenharmony_ci * but it saves the agony when the X server dies and the screen 11778c2ecf20Sopenharmony_ci * remains blanked due to KD_GRAPHICS! It would be nice to do 11788c2ecf20Sopenharmony_ci * this outside of VT_PROCESS but there is no single process 11798c2ecf20Sopenharmony_ci * to account for and tracking tty count may be undesirable. 11808c2ecf20Sopenharmony_ci */ 11818c2ecf20Sopenharmony_ci reset_vc(vc); 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci if (old_vc_mode != vc->vc_mode) { 11848c2ecf20Sopenharmony_ci if (vc->vc_mode == KD_TEXT) 11858c2ecf20Sopenharmony_ci do_unblank_screen(1); 11868c2ecf20Sopenharmony_ci else 11878c2ecf20Sopenharmony_ci do_blank_screen(1); 11888c2ecf20Sopenharmony_ci } 11898c2ecf20Sopenharmony_ci } 11908c2ecf20Sopenharmony_ci } 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci /* 11938c2ecf20Sopenharmony_ci * Wake anyone waiting for their VT to activate 11948c2ecf20Sopenharmony_ci */ 11958c2ecf20Sopenharmony_ci vt_event_post(VT_EVENT_SWITCH, old, vc->vc_num); 11968c2ecf20Sopenharmony_ci return; 11978c2ecf20Sopenharmony_ci} 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci/* 12008c2ecf20Sopenharmony_ci * Performs the front-end of a vt switch 12018c2ecf20Sopenharmony_ci */ 12028c2ecf20Sopenharmony_civoid change_console(struct vc_data *new_vc) 12038c2ecf20Sopenharmony_ci{ 12048c2ecf20Sopenharmony_ci struct vc_data *vc; 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci if (!new_vc || new_vc->vc_num == fg_console || vt_dont_switch) 12078c2ecf20Sopenharmony_ci return; 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci /* 12108c2ecf20Sopenharmony_ci * If this vt is in process mode, then we need to handshake with 12118c2ecf20Sopenharmony_ci * that process before switching. Essentially, we store where that 12128c2ecf20Sopenharmony_ci * vt wants to switch to and wait for it to tell us when it's done 12138c2ecf20Sopenharmony_ci * (via VT_RELDISP ioctl). 12148c2ecf20Sopenharmony_ci * 12158c2ecf20Sopenharmony_ci * We also check to see if the controlling process still exists. 12168c2ecf20Sopenharmony_ci * If it doesn't, we reset this vt to auto mode and continue. 12178c2ecf20Sopenharmony_ci * This is a cheap way to track process control. The worst thing 12188c2ecf20Sopenharmony_ci * that can happen is: we send a signal to a process, it dies, and 12198c2ecf20Sopenharmony_ci * the switch gets "lost" waiting for a response; hopefully, the 12208c2ecf20Sopenharmony_ci * user will try again, we'll detect the process is gone (unless 12218c2ecf20Sopenharmony_ci * the user waits just the right amount of time :-) and revert the 12228c2ecf20Sopenharmony_ci * vt to auto control. 12238c2ecf20Sopenharmony_ci */ 12248c2ecf20Sopenharmony_ci vc = vc_cons[fg_console].d; 12258c2ecf20Sopenharmony_ci if (vc->vt_mode.mode == VT_PROCESS) { 12268c2ecf20Sopenharmony_ci /* 12278c2ecf20Sopenharmony_ci * Send the signal as privileged - kill_pid() will 12288c2ecf20Sopenharmony_ci * tell us if the process has gone or something else 12298c2ecf20Sopenharmony_ci * is awry. 12308c2ecf20Sopenharmony_ci * 12318c2ecf20Sopenharmony_ci * We need to set vt_newvt *before* sending the signal or we 12328c2ecf20Sopenharmony_ci * have a race. 12338c2ecf20Sopenharmony_ci */ 12348c2ecf20Sopenharmony_ci vc->vt_newvt = new_vc->vc_num; 12358c2ecf20Sopenharmony_ci if (kill_pid(vc->vt_pid, vc->vt_mode.relsig, 1) == 0) { 12368c2ecf20Sopenharmony_ci /* 12378c2ecf20Sopenharmony_ci * It worked. Mark the vt to switch to and 12388c2ecf20Sopenharmony_ci * return. The process needs to send us a 12398c2ecf20Sopenharmony_ci * VT_RELDISP ioctl to complete the switch. 12408c2ecf20Sopenharmony_ci */ 12418c2ecf20Sopenharmony_ci return; 12428c2ecf20Sopenharmony_ci } 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci /* 12458c2ecf20Sopenharmony_ci * The controlling process has died, so we revert back to 12468c2ecf20Sopenharmony_ci * normal operation. In this case, we'll also change back 12478c2ecf20Sopenharmony_ci * to KD_TEXT mode. I'm not sure if this is strictly correct 12488c2ecf20Sopenharmony_ci * but it saves the agony when the X server dies and the screen 12498c2ecf20Sopenharmony_ci * remains blanked due to KD_GRAPHICS! It would be nice to do 12508c2ecf20Sopenharmony_ci * this outside of VT_PROCESS but there is no single process 12518c2ecf20Sopenharmony_ci * to account for and tracking tty count may be undesirable. 12528c2ecf20Sopenharmony_ci */ 12538c2ecf20Sopenharmony_ci reset_vc(vc); 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci /* 12568c2ecf20Sopenharmony_ci * Fall through to normal (VT_AUTO) handling of the switch... 12578c2ecf20Sopenharmony_ci */ 12588c2ecf20Sopenharmony_ci } 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci /* 12618c2ecf20Sopenharmony_ci * Ignore all switches in KD_GRAPHICS+VT_AUTO mode 12628c2ecf20Sopenharmony_ci */ 12638c2ecf20Sopenharmony_ci if (vc->vc_mode == KD_GRAPHICS) 12648c2ecf20Sopenharmony_ci return; 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci complete_change_console(new_vc); 12678c2ecf20Sopenharmony_ci} 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci/* Perform a kernel triggered VT switch for suspend/resume */ 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_cistatic int disable_vt_switch; 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ciint vt_move_to_console(unsigned int vt, int alloc) 12748c2ecf20Sopenharmony_ci{ 12758c2ecf20Sopenharmony_ci int prev; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci console_lock(); 12788c2ecf20Sopenharmony_ci /* Graphics mode - up to X */ 12798c2ecf20Sopenharmony_ci if (disable_vt_switch) { 12808c2ecf20Sopenharmony_ci console_unlock(); 12818c2ecf20Sopenharmony_ci return 0; 12828c2ecf20Sopenharmony_ci } 12838c2ecf20Sopenharmony_ci prev = fg_console; 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci if (alloc && vc_allocate(vt)) { 12868c2ecf20Sopenharmony_ci /* we can't have a free VC for now. Too bad, 12878c2ecf20Sopenharmony_ci * we don't want to mess the screen for now. */ 12888c2ecf20Sopenharmony_ci console_unlock(); 12898c2ecf20Sopenharmony_ci return -ENOSPC; 12908c2ecf20Sopenharmony_ci } 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci if (set_console(vt)) { 12938c2ecf20Sopenharmony_ci /* 12948c2ecf20Sopenharmony_ci * We're unable to switch to the SUSPEND_CONSOLE. 12958c2ecf20Sopenharmony_ci * Let the calling function know so it can decide 12968c2ecf20Sopenharmony_ci * what to do. 12978c2ecf20Sopenharmony_ci */ 12988c2ecf20Sopenharmony_ci console_unlock(); 12998c2ecf20Sopenharmony_ci return -EIO; 13008c2ecf20Sopenharmony_ci } 13018c2ecf20Sopenharmony_ci console_unlock(); 13028c2ecf20Sopenharmony_ci if (vt_waitactive(vt + 1)) { 13038c2ecf20Sopenharmony_ci pr_debug("Suspend: Can't switch VCs."); 13048c2ecf20Sopenharmony_ci return -EINTR; 13058c2ecf20Sopenharmony_ci } 13068c2ecf20Sopenharmony_ci return prev; 13078c2ecf20Sopenharmony_ci} 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci/* 13108c2ecf20Sopenharmony_ci * Normally during a suspend, we allocate a new console and switch to it. 13118c2ecf20Sopenharmony_ci * When we resume, we switch back to the original console. This switch 13128c2ecf20Sopenharmony_ci * can be slow, so on systems where the framebuffer can handle restoration 13138c2ecf20Sopenharmony_ci * of video registers anyways, there's little point in doing the console 13148c2ecf20Sopenharmony_ci * switch. This function allows you to disable it by passing it '0'. 13158c2ecf20Sopenharmony_ci */ 13168c2ecf20Sopenharmony_civoid pm_set_vt_switch(int do_switch) 13178c2ecf20Sopenharmony_ci{ 13188c2ecf20Sopenharmony_ci console_lock(); 13198c2ecf20Sopenharmony_ci disable_vt_switch = !do_switch; 13208c2ecf20Sopenharmony_ci console_unlock(); 13218c2ecf20Sopenharmony_ci} 13228c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pm_set_vt_switch); 1323