summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/amd64/include/xen/hypercall.h2
-rw-r--r--sys/conf/files3
-rw-r--r--sys/dev/xen/console/console.c512
-rw-r--r--sys/dev/xen/console/xen_console.c797
-rw-r--r--sys/dev/xen/console/xencons_ring.c201
-rw-r--r--sys/dev/xen/console/xencons_ring.h31
-rw-r--r--sys/i386/include/xen/hypercall.h2
-rw-r--r--sys/xen/hypervisor.h2
8 files changed, 801 insertions, 749 deletions
diff --git a/sys/amd64/include/xen/hypercall.h b/sys/amd64/include/xen/hypercall.h
index 594ffd2..e427bbf 100644
--- a/sys/amd64/include/xen/hypercall.h
+++ b/sys/amd64/include/xen/hypercall.h
@@ -308,7 +308,7 @@ HYPERVISOR_xen_version(
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);
}
diff --git a/sys/conf/files b/sys/conf/files
index 4f13269..3ddf412 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -2853,8 +2853,7 @@ dev/xe/if_xe_pccard.c optional xe pccard
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
diff --git a/sys/dev/xen/console/console.c b/sys/dev/xen/console/console.c
deleted file mode 100644
index 5019ec9..0000000
--- a/sys/dev/xen/console/console.c
+++ /dev/null
@@ -1,512 +0,0 @@
-#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 <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"
-#include "opt_printf.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;
-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);
diff --git a/sys/dev/xen/console/xen_console.c b/sys/dev/xen/console/xen_console.c
new file mode 100644
index 0000000..94f08b7
--- /dev/null
+++ b/sys/dev/xen/console/xen_console.c
@@ -0,0 +1,797 @@
+/*
+ * Copyright (c) 2015 Julien Grall <julien.grall@citrix.com>
+ * 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 <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 <sys/cons.h>
+#include <sys/kdb.h>
+#include <sys/proc.h>
+
+#include <machine/stdarg.h>
+
+#include <xen/xen-os.h>
+#include <xen/hypervisor.h>
+#include <xen/xen_intr.h>
+#include <xen/interface/io/console.h>
+
+#include "opt_ddb.h"
+#include "opt_printf.h"
+
+#ifdef DDB
+#include <ddb/ddb.h>
+#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);
+ device_set_softc(dev, tp);
+
+ 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 int
+xencons_resume(device_t dev)
+{
+ struct xencons_priv *cons;
+ struct tty *tp;
+ int err;
+
+ tp = device_get_softc(dev);
+ cons = tty_softc(tp);
+ xen_intr_unbind(&cons->intr_handle);
+
+ err = cons->ops->init(dev, tp, xencons_intr);
+ if (err != 0) {
+ device_printf(dev, "Unable to resume the console (%d)\n", err);
+ return (err);
+ }
+
+ 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(device_resume, xencons_resume),
+
+ DEVMETHOD_END
+};
+
+static driver_t xencons_driver = {
+ driver_name,
+ xencons_methods,
+ 0,
+};
+
+DRIVER_MODULE(xc, xenpv, xencons_driver, xencons_devclass, 0, 0);
diff --git a/sys/dev/xen/console/xencons_ring.c b/sys/dev/xen/console/xencons_ring.c
deleted file mode 100644
index 65d05ce..0000000
--- a/sys/dev/xen/console/xencons_ring.c
+++ /dev/null
@@ -1,201 +0,0 @@
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include <sys/param.h>
-#include <sys/module.h>
-#include <sys/systm.h>
-#include <sys/consio.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 <sys/cons.h>
-
-#include <machine/stdarg.h>
-
-#include <xen/xen-os.h>
-#include <xen/hypervisor.h>
-#include <xen/xen_intr.h>
-#include <sys/cons.h>
-
-#include <xen/xen_intr.h>
-#include <xen/evtchn.h>
-#include <xen/interface/io/console.h>
-
-#include <dev/xen/console/xencons_ring.h>
-#include <xen/evtchn.h>
-#include <xen/interface/io/console.h>
-
-#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:
- */
diff --git a/sys/dev/xen/console/xencons_ring.h b/sys/dev/xen/console/xencons_ring.h
deleted file mode 100644
index 7e3a64c..0000000
--- a/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 */
diff --git a/sys/i386/include/xen/hypercall.h b/sys/i386/include/xen/hypercall.h
index 1c4d529..7e18f9e 100644
--- a/sys/i386/include/xen/hypercall.h
+++ b/sys/i386/include/xen/hypercall.h
@@ -293,7 +293,7 @@ HYPERVISOR_xen_version(
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);
}
diff --git a/sys/xen/hypervisor.h b/sys/xen/hypervisor.h
index 0d93f66..42cdb3b 100644
--- a/sys/xen/hypervisor.h
+++ b/sys/xen/hypervisor.h
@@ -57,7 +57,7 @@ extern start_info_t *xen_start_info;
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);
}
OpenPOWER on IntegriCloud