// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2001 Anton Blanchard <anton@au.ibm.com>, IBM
* Copyright (C) 2001 Paul Mackerras <paulus@au.ibm.com>, IBM
* Copyright (C) 2004 Benjamin Herrenschmidt <benh@kernel.crashing.org>, IBM Corp.
* Copyright (C) 2004 IBM Corporation
*
* Additional Author(s):
* Ryan S. Arnold <rsa@us.ibm.com>
*/
#include <linux/console.h>
#include <linux/cpumask.h>
#include <linux/init.h>
#include <linux/kbd_kern.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/list.h>
#include <linux/major.h>
#include <linux/atomic.h>
#include <linux/sysrq.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/freezer.h>
#include <linux/slab.h>
#include <linux/serial_core.h>
#include <linux/uaccess.h>
#include "hvc_console.h"
#define HVC_MAJOR 229
#define HVC_MINOR 0
/*
* Wait this long per iteration while trying to push buffered data to the
* hypervisor before allowing the tty to complete a close operation.
*/
#define HVC_CLOSE_WAIT (HZ/100) /* 1/10 of a second */
/*
* These sizes are most efficient for vio, because they are the
* native transfer size. We could make them selectable in the
* future to better deal with backends that want other buffer sizes.
*/
#define N_OUTBUF 16
#define N_INBUF 16
#define __ALIGNED__ __attribute__((__aligned__(sizeof(long))))
static struct tty_driver *hvc_driver;
static struct task_struct *hvc_task;
/* Picks up late kicks after list walk but before schedule() */
static int hvc_kicked;
/* hvc_init is triggered from hvc_alloc, i.e. only when actually used */
static atomic_t hvc_needs_init __read_mostly = ATOMIC_INIT(-1);
static int hvc_init(void);
#ifdef CONFIG_MAGIC_SYSRQ
static int sysrq_pressed;
#endif
/* dynamic list of hvc_struct instances */
static LIST_HEAD(hvc_structs);
/*
* Protect the list of hvc_struct instances from inserts and removals during
* list traversal.
*/
static DEFINE_MUTEX(hvc_structs_mutex);
/*
* This value is used to assign a tty->index value to a hvc_struct based
* upon order of exposure via hvc_probe(), when we can not match it to
* a console candidate registered with hvc_instantiate().
*/
static int last_hvc = -1;
/*
* Do not call this function with either the hvc_structs_mutex or the hvc_struct
* lock held. If successful, this function increments the kref reference
* count against the target hvc_struct so it should be released when finished.
*/
static struct hvc_struct *hvc_get_by_index(int index)
{
struct hvc_struct *hp;
unsigned long flags;
mutex_lock(&hvc_structs_mutex);
list_for_each_entry(hp, &hvc_structs, next) {
spin_lock_irqsave(&hp->lock, flags);
if (hp->index == index) {
tty_port_get(&hp->port);
spin_unlock_irqrestore(&hp->lock, flags);
mutex_unlock(&hvc_structs_mutex);
return hp;
}
spin_unlock_irqrestore(&hp->lock, flags);
}
hp = NULL;
mutex_unlock(&hvc_structs_mutex);
return hp;
}
static int __hvc_flush(const struct hv_ops *ops, uint32_t vtermno, bool wait)
{
if (wait)
might_sleep();
if (ops->flush)
return ops->flush(vtermno, wait);
return 0;
}
static int hvc_console_flush(const struct hv_ops *ops, uint32_t vtermno)
{
return __hvc_flush(ops, vtermno, false);
}
/*
* Wait for the console to flush before writing more to it. This sleeps.
*/
static int hvc_flush(struct hvc_struct *hp)
{
return __hvc_flush(hp->ops, hp->vtermno, true);
}
/*
* Initial console vtermnos for console API usage prior to full console
* initialization. Any vty adapter outside this range will not have usable
* console interfaces but can still be used as a tty device. This has to be
* static because kmalloc will not work during early console init.
*/
static const struct hv_ops *cons_ops[MAX_NR_HVC_CONSOLES];
static uint32_t vtermnos[MAX_NR_HVC_CONSOLES] =
{[0 ... MAX_NR_HVC_CONSOLES - 1] = -1};
/*
* Console APIs, NOT TTY. These APIs are available immediately when
* hvc_console_setup() finds adapters.
*/
static void hvc_console_print(struct console *co, const char *b,
unsigned count)
{
char c[N_OUTBUF] __ALIGNED__;
unsigned i = 0, n = 0;
int r, donecr = 0, index = co->index;
/* Console access attempt outside of acceptable console range. */
if (index >= MAX_NR_HVC_CONSOLES)
return;
/* This console adapter was removed so it is not usable. */
if (vtermnos[index] == -1)
return;
while (count > 0 || i > 0) {
if (count > 0 && i < sizeof(c)) {
if (b[n] == '\n' && !donecr) {
c