f6def40e18
At the moment grab and ungrab methods of all console drivers are no-ops. Current intended meaning of the calls is that the kernel takes control of console input. In the future the semantics may be extended to mean that the calling thread takes full ownership of the console (e.g. console output from other threads could be suspended). Inspired by: bde MFC after: 2 months
444 lines
8.1 KiB
C
444 lines
8.1 KiB
C
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/module.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/consio.h>
|
|
#include <sys/priv.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/tty.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/taskqueue.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/bus.h>
|
|
#include <machine/stdarg.h>
|
|
#include <machine/xen/xen-os.h>
|
|
#include <xen/hypervisor.h>
|
|
#include <xen/xen_intr.h>
|
|
#include <sys/cons.h>
|
|
#include <sys/kdb.h>
|
|
#include <sys/proc.h>
|
|
|
|
#include <dev/xen/console/xencons_ring.h>
|
|
#include <xen/interface/io/console.h>
|
|
|
|
|
|
#include "opt_ddb.h"
|
|
#ifdef DDB
|
|
#include <ddb/ddb.h>
|
|
#endif
|
|
|
|
static char driver_name[] = "xc";
|
|
devclass_t xc_devclass; /* do not make static */
|
|
static void xcoutwakeup(struct tty *);
|
|
static void xc_timeout(void *);
|
|
static void __xencons_tx_flush(void);
|
|
static boolean_t xcons_putc(int c);
|
|
|
|
/* switch console so that shutdown can occur gracefully */
|
|
static void xc_shutdown(void *arg, int howto);
|
|
static int xc_mute;
|
|
|
|
static void xcons_force_flush(void);
|
|
static void xencons_priv_interrupt(void *);
|
|
|
|
static cn_probe_t xc_cnprobe;
|
|
static cn_init_t xc_cninit;
|
|
static cn_term_t xc_cnterm;
|
|
static cn_getc_t xc_cngetc;
|
|
static cn_putc_t xc_cnputc;
|
|
static cn_grab_t xc_cngrab;
|
|
static cn_ungrab_t xc_cnungrab;
|
|
|
|
#define XC_POLLTIME (hz/10)
|
|
|
|
CONSOLE_DRIVER(xc);
|
|
|
|
static int xen_console_up;
|
|
static boolean_t xc_start_needed;
|
|
static struct callout xc_callout;
|
|
struct mtx cn_mtx;
|
|
|
|
#define RBUF_SIZE 1024
|
|
#define RBUF_MASK(_i) ((_i)&(RBUF_SIZE-1))
|
|
#define WBUF_SIZE 4096
|
|
#define WBUF_MASK(_i) ((_i)&(WBUF_SIZE-1))
|
|
static char wbuf[WBUF_SIZE];
|
|
static char rbuf[RBUF_SIZE];
|
|
static int rc, rp;
|
|
static unsigned int cnsl_evt_reg;
|
|
static unsigned int wc, wp; /* write_cons, write_prod */
|
|
|
|
#ifdef KDB
|
|
static int xc_altbrk;
|
|
#endif
|
|
|
|
#define CDEV_MAJOR 12
|
|
#define XCUNIT(x) (dev2unit(x))
|
|
#define ISTTYOPEN(tp) ((tp) && ((tp)->t_state & TS_ISOPEN))
|
|
#define CN_LOCK_INIT(x, _name) \
|
|
mtx_init(&x, _name, NULL, MTX_SPIN|MTX_RECURSE)
|
|
|
|
#define CN_LOCK(l) \
|
|
do { \
|
|
if (panicstr == NULL) \
|
|
mtx_lock_spin(&(l)); \
|
|
} while (0)
|
|
#define CN_UNLOCK(l) \
|
|
do { \
|
|
if (panicstr == NULL) \
|
|
mtx_unlock_spin(&(l)); \
|
|
} while (0)
|
|
#define CN_LOCK_ASSERT(x) mtx_assert(&x, MA_OWNED)
|
|
#define CN_LOCK_DESTROY(x) mtx_destroy(&x)
|
|
|
|
|
|
static struct tty *xccons;
|
|
|
|
static tsw_open_t xcopen;
|
|
static tsw_close_t xcclose;
|
|
|
|
static struct ttydevsw xc_ttydevsw = {
|
|
.tsw_flags = TF_NOPREFIX,
|
|
.tsw_open = xcopen,
|
|
.tsw_close = xcclose,
|
|
.tsw_outwakeup = xcoutwakeup,
|
|
};
|
|
|
|
static void
|
|
xc_cnprobe(struct consdev *cp)
|
|
{
|
|
cp->cn_pri = CN_REMOTE;
|
|
sprintf(cp->cn_name, "%s0", driver_name);
|
|
}
|
|
|
|
|
|
static void
|
|
xc_cninit(struct consdev *cp)
|
|
{
|
|
CN_LOCK_INIT(cn_mtx,"XCONS LOCK");
|
|
|
|
}
|
|
|
|
static void
|
|
xc_cnterm(struct consdev *cp)
|
|
{
|
|
}
|
|
|
|
static void
|
|
xc_cngrab(struct consdev *cp)
|
|
{
|
|
}
|
|
|
|
static void
|
|
xc_cnungrab(struct consdev *cp)
|
|
{
|
|
}
|
|
|
|
static int
|
|
xc_cngetc(struct consdev *dev)
|
|
{
|
|
int ret;
|
|
|
|
if (xencons_has_input())
|
|
xencons_handle_input(NULL);
|
|
|
|
CN_LOCK(cn_mtx);
|
|
if ((rp - rc) && !xc_mute) {
|
|
/* we need to return only one char */
|
|
ret = (int)rbuf[RBUF_MASK(rc)];
|
|
rc++;
|
|
} else
|
|
ret = -1;
|
|
CN_UNLOCK(cn_mtx);
|
|
return(ret);
|
|
}
|
|
|
|
static void
|
|
xc_cnputc_domu(struct consdev *dev, int c)
|
|
{
|
|
xcons_putc(c);
|
|
}
|
|
|
|
static void
|
|
xc_cnputc_dom0(struct consdev *dev, int c)
|
|
{
|
|
HYPERVISOR_console_io(CONSOLEIO_write, 1, (char *)&c);
|
|
}
|
|
|
|
static void
|
|
xc_cnputc(struct consdev *dev, int c)
|
|
{
|
|
|
|
if (xen_start_info->flags & SIF_INITDOMAIN)
|
|
xc_cnputc_dom0(dev, c);
|
|
else
|
|
xc_cnputc_domu(dev, c);
|
|
}
|
|
|
|
extern int db_active;
|
|
static boolean_t
|
|
xcons_putc(int c)
|
|
{
|
|
int force_flush = xc_mute ||
|
|
#ifdef DDB
|
|
db_active ||
|
|
#endif
|
|
panicstr; /* we're not gonna recover, so force
|
|
* flush
|
|
*/
|
|
|
|
if ((wp-wc) < (WBUF_SIZE-1)) {
|
|
if ((wbuf[WBUF_MASK(wp++)] = c) == '\n') {
|
|
wbuf[WBUF_MASK(wp++)] = '\r';
|
|
#ifdef notyet
|
|
if (force_flush)
|
|
xcons_force_flush();
|
|
#endif
|
|
}
|
|
} else if (force_flush) {
|
|
#ifdef notyet
|
|
xcons_force_flush();
|
|
#endif
|
|
}
|
|
if (cnsl_evt_reg)
|
|
__xencons_tx_flush();
|
|
|
|
/* inform start path that we're pretty full */
|
|
return ((wp - wc) >= WBUF_SIZE - 100) ? TRUE : FALSE;
|
|
}
|
|
|
|
static void
|
|
xc_identify(driver_t *driver, device_t parent)
|
|
{
|
|
device_t child;
|
|
child = BUS_ADD_CHILD(parent, 0, driver_name, 0);
|
|
device_set_driver(child, driver);
|
|
device_set_desc(child, "Xen Console");
|
|
}
|
|
|
|
static int
|
|
xc_probe(device_t dev)
|
|
{
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
xc_attach(device_t dev)
|
|
{
|
|
int error;
|
|
|
|
xccons = tty_alloc(&xc_ttydevsw, NULL);
|
|
tty_makedev(xccons, NULL, "xc%r", 0);
|
|
|
|
callout_init(&xc_callout, 0);
|
|
|
|
xencons_ring_init();
|
|
|
|
cnsl_evt_reg = 1;
|
|
callout_reset(&xc_callout, XC_POLLTIME, xc_timeout, xccons);
|
|
|
|
if (xen_start_info->flags & SIF_INITDOMAIN) {
|
|
error = bind_virq_to_irqhandler(
|
|
VIRQ_CONSOLE,
|
|
0,
|
|
"console",
|
|
NULL,
|
|
xencons_priv_interrupt, NULL,
|
|
INTR_TYPE_TTY, NULL);
|
|
|
|
KASSERT(error >= 0, ("can't register console interrupt"));
|
|
}
|
|
|
|
/* register handler to flush console on shutdown */
|
|
if ((EVENTHANDLER_REGISTER(shutdown_post_sync, xc_shutdown,
|
|
NULL, SHUTDOWN_PRI_DEFAULT)) == NULL)
|
|
printf("xencons: shutdown event registration failed!\n");
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* return 0 for all console input, force flush all output.
|
|
*/
|
|
static void
|
|
xc_shutdown(void *arg, int howto)
|
|
{
|
|
xc_mute = 1;
|
|
xcons_force_flush();
|
|
}
|
|
|
|
void
|
|
xencons_rx(char *buf, unsigned len)
|
|
{
|
|
int i;
|
|
struct tty *tp = xccons;
|
|
|
|
if (xen_console_up
|
|
#ifdef DDB
|
|
&& !kdb_active
|
|
#endif
|
|
) {
|
|
tty_lock(tp);
|
|
for (i = 0; i < len; i++) {
|
|
#ifdef KDB
|
|
kdb_alt_break(buf[i], &xc_altbrk);
|
|
#endif
|
|
ttydisc_rint(tp, buf[i], 0);
|
|
}
|
|
ttydisc_rint_done(tp);
|
|
tty_unlock(tp);
|
|
} else {
|
|
CN_LOCK(cn_mtx);
|
|
for (i = 0; i < len; i++)
|
|
rbuf[RBUF_MASK(rp++)] = buf[i];
|
|
CN_UNLOCK(cn_mtx);
|
|
}
|
|
}
|
|
|
|
static void
|
|
__xencons_tx_flush(void)
|
|
{
|
|
int sz;
|
|
|
|
CN_LOCK(cn_mtx);
|
|
while (wc != wp) {
|
|
int sent;
|
|
sz = wp - wc;
|
|
if (sz > (WBUF_SIZE - WBUF_MASK(wc)))
|
|
sz = WBUF_SIZE - WBUF_MASK(wc);
|
|
if (xen_start_info->flags & SIF_INITDOMAIN) {
|
|
HYPERVISOR_console_io(CONSOLEIO_write, sz, &wbuf[WBUF_MASK(wc)]);
|
|
wc += sz;
|
|
} else {
|
|
sent = xencons_ring_send(&wbuf[WBUF_MASK(wc)], sz);
|
|
if (sent == 0)
|
|
break;
|
|
wc += sent;
|
|
}
|
|
}
|
|
CN_UNLOCK(cn_mtx);
|
|
}
|
|
|
|
void
|
|
xencons_tx(void)
|
|
{
|
|
__xencons_tx_flush();
|
|
}
|
|
|
|
static void
|
|
xencons_priv_interrupt(void *arg)
|
|
{
|
|
|
|
static char rbuf[16];
|
|
int l;
|
|
|
|
while ((l = HYPERVISOR_console_io(CONSOLEIO_read, 16, rbuf)) > 0)
|
|
xencons_rx(rbuf, l);
|
|
|
|
xencons_tx();
|
|
}
|
|
|
|
static int
|
|
xcopen(struct tty *tp)
|
|
{
|
|
|
|
xen_console_up = 1;
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
xcclose(struct tty *tp)
|
|
{
|
|
|
|
xen_console_up = 0;
|
|
}
|
|
|
|
static inline int
|
|
__xencons_put_char(int ch)
|
|
{
|
|
char _ch = (char)ch;
|
|
if ((wp - wc) == WBUF_SIZE)
|
|
return 0;
|
|
wbuf[WBUF_MASK(wp++)] = _ch;
|
|
return 1;
|
|
}
|
|
|
|
|
|
static void
|
|
xcoutwakeup(struct tty *tp)
|
|
{
|
|
boolean_t cons_full = FALSE;
|
|
char c;
|
|
|
|
while (ttydisc_getc(tp, &c, 1) == 1 && !cons_full)
|
|
cons_full = xcons_putc(c);
|
|
|
|
if (cons_full) {
|
|
/* let the timeout kick us in a bit */
|
|
xc_start_needed = TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
static void
|
|
xc_timeout(void *v)
|
|
{
|
|
struct tty *tp;
|
|
int c;
|
|
|
|
tp = (struct tty *)v;
|
|
|
|
tty_lock(tp);
|
|
while ((c = xc_cngetc(NULL)) != -1)
|
|
ttydisc_rint(tp, c, 0);
|
|
|
|
if (xc_start_needed) {
|
|
xc_start_needed = FALSE;
|
|
xcoutwakeup(tp);
|
|
}
|
|
tty_unlock(tp);
|
|
|
|
callout_reset(&xc_callout, XC_POLLTIME, xc_timeout, tp);
|
|
}
|
|
|
|
static device_method_t xc_methods[] = {
|
|
DEVMETHOD(device_identify, xc_identify),
|
|
DEVMETHOD(device_probe, xc_probe),
|
|
DEVMETHOD(device_attach, xc_attach),
|
|
{0, 0}
|
|
};
|
|
|
|
static driver_t xc_driver = {
|
|
driver_name,
|
|
xc_methods,
|
|
0,
|
|
};
|
|
|
|
/*** Forcibly flush console data before dying. ***/
|
|
void
|
|
xcons_force_flush(void)
|
|
{
|
|
int sz;
|
|
|
|
if (xen_start_info->flags & SIF_INITDOMAIN)
|
|
return;
|
|
|
|
/* Spin until console data is flushed through to the domain controller. */
|
|
while (wc != wp) {
|
|
int sent = 0;
|
|
if ((sz = wp - wc) == 0)
|
|
continue;
|
|
|
|
sent = xencons_ring_send(&wbuf[WBUF_MASK(wc)], sz);
|
|
if (sent > 0)
|
|
wc += sent;
|
|
}
|
|
}
|
|
|
|
DRIVER_MODULE(xc, nexus, xc_driver, xc_devclass, 0, 0);
|