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