Index: sys/amd64/include/xen/hypercall.h =================================================================== --- sys/amd64/include/xen/hypercall.h +++ sys/amd64/include/xen/hypercall.h @@ -308,7 +308,7 @@ static inline int __must_check HYPERVISOR_console_io( - int cmd, unsigned int count, char *str) + int cmd, unsigned int count, const char *str) { return _hypercall3(int, console_io, cmd, count, str); } Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -2853,8 +2853,7 @@ dev/xen/balloon/balloon.c optional xenhvm dev/xen/blkfront/blkfront.c optional xenhvm dev/xen/blkback/blkback.c optional xenhvm -dev/xen/console/console.c optional xenhvm -dev/xen/console/xencons_ring.c optional xenhvm +dev/xen/console/xen_console.c optional xenhvm dev/xen/control/control.c optional xenhvm dev/xen/grant_table/grant_table.c optional xenhvm dev/xen/netback/netback.c optional xenhvm Index: sys/dev/xen/console/console.c =================================================================== --- sys/dev/xen/console/console.c +++ /dev/null @@ -1,512 +0,0 @@ -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - - -#include "opt_ddb.h" -#include "opt_printf.h" - -#ifdef DDB -#include -#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; -bool cnsl_evt_reg; -static unsigned int wc, wp; /* write_cons, write_prod */ -xen_intr_handle_t xen_intr_handle; -device_t xencons_dev; - -/* Virtual address of the shared console page */ -char *console_page; - -#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, -}; - -/*----------------------------- Debug function -------------------------------*/ -struct putchar_arg { - char *buf; - size_t size; - size_t n_next; -}; - -static void -putchar(int c, void *arg) -{ - struct putchar_arg *pca; - - pca = (struct putchar_arg *)arg; - - if (pca->buf == NULL) { - /* - * We have no buffer, output directly to the - * console char by char. - */ - HYPERVISOR_console_write((char *)&c, 1); - } else { - pca->buf[pca->n_next++] = c; - if ((pca->size == pca->n_next) || (c = '\0')) { - /* Flush the buffer */ - HYPERVISOR_console_write(pca->buf, pca->n_next); - pca->n_next = 0; - } - } -} - -void -xc_printf(const char *fmt, ...) -{ - va_list ap; - struct putchar_arg pca; -#ifdef PRINTF_BUFR_SIZE - char buf[PRINTF_BUFR_SIZE]; - - pca.buf = buf; - pca.size = sizeof(buf); - pca.n_next = 0; -#else - pca.buf = NULL; - pca.size = 0; -#endif - - KASSERT((xen_domain()), ("call to xc_printf from non Xen guest")); - - va_start(ap, fmt); - kvprintf(fmt, putchar, &pca, 10, ap); - va_end(ap); - -#ifdef PRINTF_BUFR_SIZE - if (pca.n_next != 0) - HYPERVISOR_console_write(buf, pca.n_next); -#endif -} - -static void -xc_cnprobe(struct consdev *cp) -{ - if (!xen_pv_domain()) - return; - - 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_initial_domain()) - xc_cnputc_dom0(dev, c); - else - xc_cnputc_domu(dev, c); -} - -static boolean_t -xcons_putc(int c) -{ - int force_flush = xc_mute || -#ifdef DDB - kdb_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 - } - __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; - - if (!xen_pv_domain()) - return; - - 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 (BUS_PROBE_NOWILDCARD); -} - -static int -xc_attach(device_t dev) -{ - int error; - - xencons_dev = dev; - xccons = tty_alloc(&xc_ttydevsw, NULL); - tty_makedev(xccons, NULL, "xc%r", 0); - - callout_init(&xc_callout, 0); - - xencons_ring_init(); - - cnsl_evt_reg = true; - callout_reset(&xc_callout, XC_POLLTIME, xc_timeout, xccons); - - if (xen_initial_domain()) { - error = xen_intr_bind_virq(dev, VIRQ_CONSOLE, 0, NULL, - xencons_priv_interrupt, NULL, - INTR_TYPE_TTY, &xen_intr_handle); - 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_initial_domain()) { - 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; -} - -#if 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; -} -#endif - - -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), - - DEVMETHOD_END -}; - -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_initial_domain()) - 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, xenpv, xc_driver, xc_devclass, 0, 0); Index: sys/dev/xen/console/xen_console.c =================================================================== --- /dev/null +++ sys/dev/xen/console/xen_console.c @@ -0,0 +1,764 @@ +/* + * Copyright (c) 2015 Julien Grall + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "opt_ddb.h" +#include "opt_printf.h" + +#ifdef DDB +#include +#endif + +static char driver_name[] = "xc"; + +struct xencons_priv; + +typedef void xencons_early_init_t(struct xencons_priv *cons); +typedef int xencons_init_t(device_t dev, struct tty *tp, + driver_intr_t intr_handler); +typedef int xencons_read_t(struct xencons_priv *cons, char *buffer, + unsigned int size); +typedef int xencons_write_t(struct xencons_priv *cons, const char *buffer, + unsigned int size); + +struct xencons_ops { + /* + * Called by the low-level driver during early boot. + * Only the minimal set up to get a console should be done here. + */ + xencons_early_init_t *early_init; + /* Prepare the console to be fully use */ + xencons_init_t *init; + /* Read/write helpers */ + xencons_read_t *read; + xencons_write_t *write; +}; + +struct xencons_priv { + /* Mutex to protect the shared ring and the internal buffers */ + struct mtx mtx; + /* Interrupt handler used for notify the backend */ + xen_intr_handle_t intr_handle; + /* KDB internal state */ +#ifdef KDB + int altbrk; +#endif + /* Status of the tty */ + bool opened; + /* Callout used when the write buffer is full */ + struct callout callout; + + /* Internal buffers must be used with mtx locked */ +#define WBUF_SIZE 4096 +#define WBUF_MASK(_i) ((_i)&(WBUF_SIZE-1)) + char wbuf[WBUF_SIZE]; + unsigned int wc, wp; /* Consumer/producer wbuf */ + +#define RBUF_SIZE 1024 +#define RBUF_MASK(_i) ((_i)&(RBUF_SIZE-1)) + char rbuf[RBUF_SIZE]; + unsigned int rc, rp; /* Consumer/producer rbuf */ + + /* Pointer to the console operations */ + const struct xencons_ops *ops; + + /* + * Ring specific fields + * XXX: make an union? + */ + /* Event channel number for early notification (PV only) */ + uint32_t evtchn; + /* Console shared page */ + struct xencons_interface *intf; +}; + +/* + * Data for the main console + * Necessary to support low-level console driver + */ +static struct xencons_priv main_cons; + +#define XC_POLLTIME (hz/10) + +/* + * Virtual address of the shared console page (only for PV guest) + * TODO: Introduce a function to set it + */ +char *console_page; + +/*----------------------------- Debug function ------------------------------*/ +struct putchar_arg { + char *buf; + size_t size; + size_t n_next; +}; + +static void +putchar(int c, void *arg) +{ + struct putchar_arg *pca; + + pca = (struct putchar_arg *)arg; + + if (pca->buf == NULL) { + /* + * We have no buffer, output directly to the + * console char by char. + */ + HYPERVISOR_console_write((char *)&c, 1); + } else { + pca->buf[pca->n_next++] = c; + if ((pca->size == pca->n_next) || (c = '\0')) { + /* Flush the buffer */ + HYPERVISOR_console_write(pca->buf, pca->n_next); + pca->n_next = 0; + } + } +} + +void +xc_printf(const char *fmt, ...) +{ + va_list ap; + struct putchar_arg pca; +#ifdef PRINTF_BUFR_SIZE + char buf[PRINTF_BUFR_SIZE]; + + pca.buf = buf; + pca.size = sizeof(buf); + pca.n_next = 0; +#else + pca.buf = NULL; + pca.size = 0; +#endif + + KASSERT((xen_domain()), ("call to xc_printf from non Xen guest")); + + va_start(ap, fmt); + kvprintf(fmt, putchar, &pca, 10, ap); + va_end(ap); + +#ifdef PRINTF_BUFR_SIZE + if (pca.n_next != 0) + HYPERVISOR_console_write(buf, pca.n_next); +#endif +} + +/*---------------------- Helpers for the console lock -----------------------*/ +/* + * The lock is not used when the kernel is panicing as it will never recover + * and we want to output no matter what it costs. + */ +static inline void xencons_lock(struct xencons_priv *cons) +{ + if (panicstr == NULL) + mtx_lock_spin(&cons->mtx); + +} + +static inline void xencons_unlock(struct xencons_priv *cons) +{ + if (panicstr == NULL) + mtx_unlock_spin(&cons->mtx); +} + +#define xencons_lock_assert(cons) mtx_assert(&(cons)->mtx, MA_OWNED) + +/*------------------ Helpers for the hypervisor console ---------------------*/ +static void +xencons_early_init_hypervisor(struct xencons_priv *cons) +{ + /* + * Nothing to setup for the low-level console when using + * the hypervisor console. + */ +} + +static int +xencons_init_hypervisor(device_t dev, struct tty *tp, + driver_intr_t intr_handler) +{ + struct xencons_priv *cons; + int err; + + cons = tty_softc(tp); + + err = xen_intr_bind_virq(dev, VIRQ_CONSOLE, 0, NULL, + intr_handler, tp, INTR_TYPE_TTY | INTR_MPSAFE, &cons->intr_handle); + if (err != 0) + device_printf(dev, "Can't register console interrupt\n"); + + return (err); +} + +static int +xencons_write_hypervisor(struct xencons_priv *cons, const char *buffer, + unsigned int size) +{ + HYPERVISOR_console_io(CONSOLEIO_write, size, buffer); + + return (size); +} + +static int +xencons_read_hypervisor(struct xencons_priv *cons, char *buffer, + unsigned int size) +{ + xencons_lock_assert(cons); + + return (HYPERVISOR_console_io(CONSOLEIO_read, size, buffer)); +} + +static const struct xencons_ops xencons_hypervisor_ops = { + .early_init = xencons_early_init_hypervisor, + .init = xencons_init_hypervisor, + .read = xencons_read_hypervisor, + .write = xencons_write_hypervisor, +}; + +/*------------------ Helpers for the ring console ---------------------------*/ +static void +xencons_early_init_ring(struct xencons_priv *cons) +{ + /* The shared page for PV is already mapped by the boot code */ + cons->intf = (struct xencons_interface *)console_page; + cons->evtchn = HYPERVISOR_start_info->console.domU.evtchn; +} + +static int +xencons_init_ring(device_t dev, struct tty *tp, driver_intr_t intr_handler) +{ + struct xencons_priv *cons; + int err; + + cons = tty_softc(tp); + + if (cons->evtchn == 0) + return (ENODEV); + + err = xen_intr_bind_local_port(dev, cons->evtchn, NULL, + intr_handler, tp, INTR_TYPE_TTY | INTR_MPSAFE, &cons->intr_handle); + if (err != 0) + return (err); + + return (0); +} + +static void +xencons_notify_ring(struct xencons_priv *cons) +{ + /* + * The console may be used before the ring interrupt is properly + * initialized. + * If so, fallback to directly use the event channel hypercall. + */ + if (__predict_true(cons->intr_handle != NULL)) + xen_intr_signal(cons->intr_handle); + else { + struct evtchn_send send = { + .port = cons->evtchn + }; + + HYPERVISOR_event_channel_op(EVTCHNOP_send, &send); + } +} + +static int +xencons_write_ring(struct xencons_priv *cons, const char *buffer, + unsigned int size) +{ + struct xencons_interface *intf; + XENCONS_RING_IDX wcons, wprod; + int sent; + + intf = cons->intf; + + xencons_lock_assert(cons); + + wcons = intf->out_cons; + wprod = intf->out_prod; + + mb(); + KASSERT((wprod - wcons) <= sizeof(intf->out), + ("console send ring inconsistent")); + + for (sent = 0; sent < size; sent++, wprod++) { + if ((wprod - wcons) >= sizeof(intf->out)) + break; + intf->out[MASK_XENCONS_IDX(wprod, intf->out)] = buffer[sent]; + } + + wmb(); + intf->out_prod = wprod; + + xencons_notify_ring(cons); + + return (sent); +} + +static int +xencons_read_ring(struct xencons_priv *cons, char *buffer, unsigned int size) +{ + struct xencons_interface *intf; + XENCONS_RING_IDX rcons, rprod; + unsigned int rsz; + + intf = cons->intf; + + xencons_lock_assert(cons); + + rcons = intf->in_cons; + rprod = intf->in_prod; + rmb(); + + for (rsz = 0; rsz < size; rsz++, rcons++) { + if (rprod == rcons) + break; + buffer[rsz] = intf->in[MASK_XENCONS_IDX(rcons, intf->in)]; + } + + wmb(); + intf->in_cons = rcons; + + /* No need to notify the backend if nothing has been read */ + if (rsz != 0) + xencons_notify_ring(cons); + + return (rsz); +} + +static const struct xencons_ops xencons_ring_ops = { + .early_init = xencons_early_init_ring, + .init = xencons_init_ring, + .read = xencons_read_ring, + .write = xencons_write_ring, +}; + +/*------------------ Common implementation of the console -------------------*/ + +/* + * Called by the low-level driver during early boot to initialize the + * main console driver. + * Only the minimal set up to get a console should be done here. + */ +static void +xencons_early_init(void) +{ + mtx_init(&main_cons.mtx, "XCONS LOCK", NULL, MTX_SPIN); + + if (xen_initial_domain()) + main_cons.ops = &xencons_hypervisor_ops; + else + main_cons.ops = &xencons_ring_ops; + + main_cons.ops->early_init(&main_cons); +} + +/* + * Receive character from the console and put them in the internal buffer + * XXX: Handle overflow of the internal buffer + */ +static void +xencons_rx(struct xencons_priv *cons) +{ + char buf[16]; + int sz; + + xencons_lock(cons); + while ((sz = cons->ops->read(cons, buf, sizeof(buf))) > 0) { + int i; + + for (i = 0; i < sz; i++) + cons->rbuf[RBUF_MASK(cons->rp++)] = buf[i]; + } + xencons_unlock(cons); +} + +/* Return true if the write buffer is full */ +static bool +xencons_tx_full(struct xencons_priv *cons) +{ + unsigned int used; + + xencons_lock(cons); + used = cons->wp - cons->wc; + xencons_unlock(cons); + + return (used >= WBUF_SIZE); +} + +static void +xencons_tx_flush(struct xencons_priv *cons, int force) +{ + int sz; + + xencons_lock(cons); + while (cons->wc != cons->wp) { + int sent; + sz = cons->wp - cons->wc; + if (sz > (WBUF_SIZE - WBUF_MASK(cons->wc))) + sz = WBUF_SIZE - WBUF_MASK(cons->wc); + sent = cons->ops->write(cons, &cons->wbuf[WBUF_MASK(cons->wc)], + sz); + + /* + * The other end may not have been initialized. Ignore + * the force. + */ + if (__predict_false(sent < 0)) + break; + + /* + * If force is set, spin until the console data is + * flushed through the domain controller. + */ + if (sent == 0 && __predict_true(!force)) + break; + + cons->wc += sent; + } + xencons_unlock(cons); +} + +static bool +xencons_putc(struct xencons_priv *cons, int c, bool force_flush) +{ + xencons_lock(cons); + if ((cons->wp - cons->wc) < WBUF_SIZE) + cons->wbuf[WBUF_MASK(cons->wp++)] = c; + xencons_unlock(cons); + + xencons_tx_flush(cons, force_flush); + + return (xencons_tx_full(cons)); +} + +static int +xencons_getc(struct xencons_priv *cons) +{ + int ret; + + xencons_lock(cons); + if (cons->rp != cons->rc) { + /* We need to return only one char */ + ret = (int)cons->rbuf[RBUF_MASK(cons->rc)]; + cons->rc++; + } else { + ret = -1; + } + + xencons_unlock(cons); + + return (ret); +} + +static bool +xencons_tx(struct tty *tp) +{ + bool cons_full; + char c; + struct xencons_priv *cons; + + cons = tty_softc(tp); + + tty_lock_assert(tp, MA_OWNED); + + /* + * Don't transmit any character if the buffer is full. Otherwise, + * characters may be lost + */ + if (xencons_tx_full(cons)) + return (false); + + cons_full = false; + while (!cons_full && ttydisc_getc(tp, &c, 1) == 1) + cons_full = xencons_putc(cons, c, false); + + return (!cons_full); +} + +static void +xencons_intr(void *arg) +{ + struct tty *tp; + struct xencons_priv *cons; + int ret; + + tp = arg; + cons = tty_softc(tp); + + /* + * The input will be used by the low-level console when KDB is active + */ + if (kdb_active) + return; + + /* + * It's not necessary to retrieve input when the tty is not opened + */ + if (!cons->opened) + return; + + xencons_rx(cons); + + tty_lock(tp); + while ((ret = xencons_getc(cons)) != -1) { +#ifdef KDB + kdb_alt_break(ret, &cons->altbrk); +#endif + ttydisc_rint(tp, ret, 0); + } + ttydisc_rint_done(tp); + tty_unlock(tp); + + /* Try to flush remaining characters if necessary */ + xencons_tx_flush(cons, 0); +} + +/* + * Helpers to call while shutting down: + * - Force flush all output + */ +static void +xencons_shutdown(void *arg, int howto) +{ + struct tty *tp; + + tp = arg; + + xencons_tx_flush(tty_softc(tp), 1); +} + +/*---------------------- Low-level console driver ---------------------------*/ +static void +xencons_cnprobe(struct consdev *cp) +{ + if (!xen_pv_domain()) + return; + + cp->cn_pri = CN_REMOTE; + sprintf(cp->cn_name, "%s0", driver_name); +} + +static void +xencons_cninit(struct consdev *cp) +{ + xencons_early_init(); +} + +static void +xencons_cnterm(struct consdev *cp) +{ +} + +static void +xencons_cngrab(struct consdev *cp) +{ +} + +static void +xencons_cnungrab(struct consdev *cp) +{ +} + +static int +xencons_cngetc(struct consdev *dev) +{ + xencons_rx(&main_cons); + + return (xencons_getc(&main_cons)); +} + +static void +xencons_cnputc(struct consdev *dev, int c) +{ + /* + * The low-level console is used by KDB and panic. We have to ensure + * that any character sent will be seen by the backend. + */ + xencons_putc(&main_cons, c, true); +} + +CONSOLE_DRIVER(xencons); + +/*----------------------------- TTY driver ---------------------------------*/ + +static int +xencons_tty_open(struct tty *tp) +{ + struct xencons_priv *cons; + + cons = tty_softc(tp); + + cons->opened = true; + + return (0); +} + +static void +xencons_tty_close(struct tty *tp) +{ + struct xencons_priv *cons; + + cons = tty_softc(tp); + + cons->opened = false; +} + +static void +xencons_timeout(void *v) +{ + struct tty *tp; + struct xencons_priv *cons; + + tp = v; + cons = tty_softc(tp); + + if (!xencons_tx(tp)) + callout_reset(&cons->callout, XC_POLLTIME, + xencons_timeout, tp); +} + +static void +xencons_tty_outwakeup(struct tty *tp) +{ + struct xencons_priv *cons; + + cons = tty_softc(tp); + + callout_stop(&cons->callout); + + if (!xencons_tx(tp)) + callout_reset(&cons->callout, XC_POLLTIME, + xencons_timeout, tp); +} + +static struct ttydevsw xencons_ttydevsw = { + .tsw_flags = TF_NOPREFIX, + .tsw_open = xencons_tty_open, + .tsw_close = xencons_tty_close, + .tsw_outwakeup = xencons_tty_outwakeup, +}; + +/*------------------------ Main console driver ------------------------------*/ +static void +xencons_identify(driver_t *driver, device_t parent) +{ + device_t child; + +#if defined(__arm__) || defined(__aarch64__) + if (!xen_domain()) + return; +#else + if (!xen_pv_domain()) + return; +#endif + + child = BUS_ADD_CHILD(parent, 0, driver_name, 0); +} + +static int +xencons_probe(device_t dev) +{ + device_set_desc(dev, "Xen Console"); + return (BUS_PROBE_NOWILDCARD); +} + +static int +xencons_attach(device_t dev) +{ + struct tty *tp; + /* + * The main console is already allocated statically in order to + * support low-level console + */ + struct xencons_priv *cons; + int err; + + cons = &main_cons; + + tp = tty_alloc(&xencons_ttydevsw, cons); + tty_makedev(tp, NULL, "%s%r", driver_name, 0); + + callout_init_mtx(&cons->callout, tty_getlock(tp), 0); + + err = cons->ops->init(dev, tp, xencons_intr); + if (err != 0) { + device_printf(dev, "Unable to initialize the console (%d)\n", + err); + return (err); + } + + /* register handler to flush console on shutdown */ + if ((EVENTHANDLER_REGISTER(shutdown_post_sync, xencons_shutdown, + tp, SHUTDOWN_PRI_DEFAULT)) == NULL) + device_printf(dev, "shutdown event registration failed!\n"); + + return (0); +} + +static devclass_t xencons_devclass; + +static device_method_t xencons_methods[] = { + DEVMETHOD(device_identify, xencons_identify), + DEVMETHOD(device_probe, xencons_probe), + DEVMETHOD(device_attach, xencons_attach), + + DEVMETHOD_END +}; + +static driver_t xencons_driver = { + driver_name, + xencons_methods, + 0, +}; + +DRIVER_MODULE(xc, xenpv, xencons_driver, xencons_devclass, 0, 0); Index: sys/dev/xen/console/xencons_ring.h =================================================================== --- sys/dev/xen/console/xencons_ring.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * $FreeBSD$ - * - */ -#ifndef _XENCONS_RING_H -#define _XENCONS_RING_H - -#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) - -int xencons_ring_init(void); -int xencons_ring_send(const char *data, unsigned len); -void xencons_rx(char *buf, unsigned len); -void xencons_tx(void); - - -typedef void (xencons_receiver_func)(char *buf, unsigned len); -void xencons_ring_register_receiver(xencons_receiver_func *f); - -void xencons_handle_input(void *unused); -int xencons_has_input(void); - -#endif /* _XENCONS_RING_H */ Index: sys/dev/xen/console/xencons_ring.c =================================================================== --- sys/dev/xen/console/xencons_ring.c +++ /dev/null @@ -1,201 +0,0 @@ -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#define console_evtchn console.domU.evtchn -xen_intr_handle_t console_handle; -extern struct mtx cn_mtx; -extern device_t xencons_dev; -extern bool cnsl_evt_reg; -#define DOM0_BUFFER_SIZE 16 - -static inline struct xencons_interface * -xencons_interface(void) -{ - return (struct xencons_interface *)console_page; -} - - -int -xencons_has_input(void) -{ - struct xencons_interface *intf; - - if (xen_initial_domain()) { - /* - * Since the Dom0 console works with hypercalls - * there's no way to know if there's input unless - * we actually try to retrieve it, so always return - * like there's pending data. Then if the hypercall - * returns no input, we can handle it without problems - * in xencons_handle_input(). - */ - return 1; - } - - intf = xencons_interface(); - - return (intf->in_cons != intf->in_prod); -} - - -int -xencons_ring_send(const char *data, unsigned len) -{ - struct xencons_interface *intf; - XENCONS_RING_IDX cons, prod; - int sent; - struct evtchn_send send = { - .port = HYPERVISOR_start_info->console_evtchn - }; - - intf = xencons_interface(); - cons = intf->out_cons; - prod = intf->out_prod; - sent = 0; - - mb(); - KASSERT((prod - cons) <= sizeof(intf->out), - ("console send ring inconsistent")); - - while ((sent < len) && ((prod - cons) < sizeof(intf->out))) - intf->out[MASK_XENCONS_IDX(prod++, intf->out)] = data[sent++]; - - wmb(); - intf->out_prod = prod; - - if (cnsl_evt_reg) - xen_intr_signal(console_handle); - else - HYPERVISOR_event_channel_op(EVTCHNOP_send, &send); - - return sent; - -} - - -static xencons_receiver_func *xencons_receiver; - -void -xencons_handle_input(void *unused) -{ - struct xencons_interface *intf; - XENCONS_RING_IDX cons, prod; - - CN_LOCK(cn_mtx); - - if (xen_initial_domain()) { - static char rbuf[DOM0_BUFFER_SIZE]; - int l; - - while ((l = HYPERVISOR_console_io(CONSOLEIO_read, - DOM0_BUFFER_SIZE, rbuf)) > 0) - xencons_rx(rbuf, l); - - CN_UNLOCK(cn_mtx); - return; - } - - intf = xencons_interface(); - - cons = intf->in_cons; - prod = intf->in_prod; - CN_UNLOCK(cn_mtx); - - /* XXX needs locking */ - while (cons != prod) { - xencons_rx(intf->in + MASK_XENCONS_IDX(cons, intf->in), 1); - cons++; - } - - mb(); - intf->in_cons = cons; - - CN_LOCK(cn_mtx); - xen_intr_signal(console_handle); - - xencons_tx(); - CN_UNLOCK(cn_mtx); -} - -void -xencons_ring_register_receiver(xencons_receiver_func *f) -{ - xencons_receiver = f; -} - -int -xencons_ring_init(void) -{ - int err; - - if (HYPERVISOR_start_info->console_evtchn == 0) - return 0; - - err = xen_intr_bind_local_port(xencons_dev, - HYPERVISOR_start_info->console_evtchn, NULL, xencons_handle_input, NULL, - INTR_TYPE_MISC | INTR_MPSAFE, &console_handle); - if (err) { - return err; - } - - return 0; -} - -extern void xencons_suspend(void); -extern void xencons_resume(void); - -void -xencons_suspend(void) -{ - - if (HYPERVISOR_start_info->console_evtchn == 0) - return; - - xen_intr_unbind(&console_handle); -} - -void -xencons_resume(void) -{ - - (void)xencons_ring_init(); -} - -/* - * Local variables: - * mode: C - * c-set-style: "BSD" - * c-basic-offset: 8 - * tab-width: 4 - * indent-tabs-mode: t - * End: - */ Index: sys/i386/include/xen/hypercall.h =================================================================== --- sys/i386/include/xen/hypercall.h +++ sys/i386/include/xen/hypercall.h @@ -293,7 +293,7 @@ static inline int HYPERVISOR_console_io( - int cmd, int count, char *str) + int cmd, int count, const char *str) { return _hypercall3(int, console_io, cmd, count, str); } Index: sys/xen/hypervisor.h =================================================================== --- sys/xen/hypervisor.h +++ sys/xen/hypervisor.h @@ -57,7 +57,7 @@ extern uint64_t get_system_time(int ticks); static inline int -HYPERVISOR_console_write(char *str, int count) +HYPERVISOR_console_write(const char *str, int count) { return HYPERVISOR_console_io(CONSOLEIO_write, count, str); }