2 * Copyright (c) 2015 Julien Grall <julien.grall@citrix.com>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
30 #include <sys/param.h>
31 #include <sys/module.h>
32 #include <sys/systm.h>
33 #include <sys/eventhandler.h>
34 #include <sys/consio.h>
39 #include <sys/systm.h>
40 #include <sys/taskqueue.h>
42 #include <sys/kernel.h>
48 #include <machine/stdarg.h>
53 #include <xen/xen-os.h>
54 #include <xen/hypervisor.h>
55 #include <xen/xen_intr.h>
56 #include <xen/interface/io/console.h>
59 #include "opt_printf.h"
65 static char driver_name[] = "xc";
69 typedef void xencons_early_init_t(struct xencons_priv *cons);
70 typedef int xencons_init_t(device_t dev, struct tty *tp,
71 driver_intr_t intr_handler);
72 typedef int xencons_read_t(struct xencons_priv *cons, char *buffer,
74 typedef int xencons_write_t(struct xencons_priv *cons, const char *buffer,
79 * Called by the low-level driver during early boot.
80 * Only the minimal set up to get a console should be done here.
82 xencons_early_init_t *early_init;
83 /* Prepare the console to be fully use */
85 /* Read/write helpers */
87 xencons_write_t *write;
91 /* Mutex to protect the shared ring and the internal buffers */
93 /* Interrupt handler used for notify the backend */
94 xen_intr_handle_t intr_handle;
95 /* KDB internal state */
99 /* Status of the tty */
101 /* Callout used when the write buffer is full */
102 struct callout callout;
104 /* Internal buffers must be used with mtx locked */
105 #define WBUF_SIZE 4096
106 #define WBUF_MASK(_i) ((_i)&(WBUF_SIZE-1))
107 char wbuf[WBUF_SIZE];
108 unsigned int wc, wp; /* Consumer/producer wbuf */
110 #define RBUF_SIZE 1024
111 #define RBUF_MASK(_i) ((_i)&(RBUF_SIZE-1))
112 char rbuf[RBUF_SIZE];
113 unsigned int rc, rp; /* Consumer/producer rbuf */
115 /* Pointer to the console operations */
116 const struct xencons_ops *ops;
119 * Ring specific fields
120 * XXX: make an union?
122 /* Event channel number for early notification (PV only) */
124 /* Console shared page */
125 struct xencons_interface *intf;
129 * Data for the main console
130 * Necessary to support low-level console driver
132 static struct xencons_priv main_cons;
134 #define XC_POLLTIME (hz/10)
136 /*----------------------------- Debug function ------------------------------*/
144 putchar(int c, void *arg)
146 struct putchar_arg *pca;
148 pca = (struct putchar_arg *)arg;
150 if (pca->buf == NULL) {
152 * We have no buffer, output directly to the
153 * console char by char.
155 HYPERVISOR_console_write((char *)&c, 1);
157 pca->buf[pca->n_next++] = c;
158 if ((pca->size == pca->n_next) || (c = '\0')) {
159 /* Flush the buffer */
160 HYPERVISOR_console_write(pca->buf, pca->n_next);
167 xc_printf(const char *fmt, ...)
170 struct putchar_arg pca;
171 #ifdef PRINTF_BUFR_SIZE
172 char buf[PRINTF_BUFR_SIZE];
175 pca.size = sizeof(buf);
182 KASSERT((xen_domain()), ("call to xc_printf from non Xen guest"));
185 kvprintf(fmt, putchar, &pca, 10, ap);
188 #ifdef PRINTF_BUFR_SIZE
190 HYPERVISOR_console_write(buf, pca.n_next);
194 /*---------------------- Helpers for the console lock -----------------------*/
196 * The lock is not used when the kernel is panicing as it will never recover
197 * and we want to output no matter what it costs.
199 static inline void xencons_lock(struct xencons_priv *cons)
202 if (!KERNEL_PANICKED())
203 mtx_lock_spin(&cons->mtx);
207 static inline void xencons_unlock(struct xencons_priv *cons)
210 if (!KERNEL_PANICKED())
211 mtx_unlock_spin(&cons->mtx);
214 #define xencons_lock_assert(cons) mtx_assert(&(cons)->mtx, MA_OWNED)
216 /*------------------ Helpers for the hypervisor console ---------------------*/
218 xencons_early_init_hypervisor(struct xencons_priv *cons)
221 * Nothing to setup for the low-level console when using
222 * the hypervisor console.
227 xencons_init_hypervisor(device_t dev, struct tty *tp,
228 driver_intr_t intr_handler)
230 struct xencons_priv *cons;
233 cons = tty_softc(tp);
235 err = xen_intr_bind_virq(dev, VIRQ_CONSOLE, 0, NULL,
236 intr_handler, tp, INTR_TYPE_TTY | INTR_MPSAFE, &cons->intr_handle);
238 device_printf(dev, "Can't register console interrupt\n");
244 xencons_write_hypervisor(struct xencons_priv *cons, const char *buffer,
248 HYPERVISOR_console_io(CONSOLEIO_write, size, buffer);
254 xencons_read_hypervisor(struct xencons_priv *cons, char *buffer,
258 xencons_lock_assert(cons);
260 return (HYPERVISOR_console_io(CONSOLEIO_read, size, buffer));
263 static const struct xencons_ops xencons_hypervisor_ops = {
264 .early_init = xencons_early_init_hypervisor,
265 .init = xencons_init_hypervisor,
266 .read = xencons_read_hypervisor,
267 .write = xencons_write_hypervisor,
270 /*------------------ Helpers for the ring console ---------------------------*/
272 xencons_early_init_ring(struct xencons_priv *cons)
274 cons->intf = pmap_mapdev_attr(ptoa(xen_get_console_mfn()), PAGE_SIZE,
276 cons->evtchn = xen_get_console_evtchn();
280 xencons_init_ring(device_t dev, struct tty *tp, driver_intr_t intr_handler)
282 struct xencons_priv *cons;
285 cons = tty_softc(tp);
287 if (cons->evtchn == 0)
290 err = xen_intr_bind_local_port(dev, cons->evtchn, NULL,
291 intr_handler, tp, INTR_TYPE_TTY | INTR_MPSAFE, &cons->intr_handle);
299 xencons_notify_ring(struct xencons_priv *cons)
302 * The console may be used before the ring interrupt is properly
304 * If so, fallback to directly use the event channel hypercall.
306 if (__predict_true(cons->intr_handle != NULL))
307 xen_intr_signal(cons->intr_handle);
309 struct evtchn_send send = {
313 HYPERVISOR_event_channel_op(EVTCHNOP_send, &send);
318 xencons_write_ring(struct xencons_priv *cons, const char *buffer,
321 struct xencons_interface *intf;
322 XENCONS_RING_IDX wcons, wprod;
327 xencons_lock_assert(cons);
329 wcons = intf->out_cons;
330 wprod = intf->out_prod;
333 KASSERT((wprod - wcons) <= sizeof(intf->out),
334 ("console send ring inconsistent"));
336 for (sent = 0; sent < size; sent++, wprod++) {
337 if ((wprod - wcons) >= sizeof(intf->out))
339 intf->out[MASK_XENCONS_IDX(wprod, intf->out)] = buffer[sent];
343 intf->out_prod = wprod;
345 xencons_notify_ring(cons);
351 xencons_read_ring(struct xencons_priv *cons, char *buffer, unsigned int size)
353 struct xencons_interface *intf;
354 XENCONS_RING_IDX rcons, rprod;
359 xencons_lock_assert(cons);
361 rcons = intf->in_cons;
362 rprod = intf->in_prod;
365 for (rsz = 0; rsz < size; rsz++, rcons++) {
368 buffer[rsz] = intf->in[MASK_XENCONS_IDX(rcons, intf->in)];
372 intf->in_cons = rcons;
374 /* No need to notify the backend if nothing has been read */
376 xencons_notify_ring(cons);
381 static const struct xencons_ops xencons_ring_ops = {
382 .early_init = xencons_early_init_ring,
383 .init = xencons_init_ring,
384 .read = xencons_read_ring,
385 .write = xencons_write_ring,
388 /*------------------ Common implementation of the console -------------------*/
391 * Called by the low-level driver during early boot to initialize the
392 * main console driver.
393 * Only the minimal set up to get a console should be done here.
396 xencons_early_init(void)
399 mtx_init(&main_cons.mtx, "XCONS LOCK", NULL, MTX_SPIN);
401 if (xen_get_console_evtchn() == 0)
402 main_cons.ops = &xencons_hypervisor_ops;
404 main_cons.ops = &xencons_ring_ops;
406 main_cons.ops->early_init(&main_cons);
410 * Receive character from the console and put them in the internal buffer
411 * XXX: Handle overflow of the internal buffer
414 xencons_rx(struct xencons_priv *cons)
420 while ((sz = cons->ops->read(cons, buf, sizeof(buf))) > 0) {
423 for (i = 0; i < sz; i++)
424 cons->rbuf[RBUF_MASK(cons->rp++)] = buf[i];
426 xencons_unlock(cons);
429 /* Return true if the write buffer is full */
431 xencons_tx_full(struct xencons_priv *cons)
436 used = cons->wp - cons->wc;
437 xencons_unlock(cons);
439 return (used >= WBUF_SIZE);
443 xencons_tx_flush(struct xencons_priv *cons, int force)
448 while (cons->wc != cons->wp) {
450 sz = cons->wp - cons->wc;
451 if (sz > (WBUF_SIZE - WBUF_MASK(cons->wc)))
452 sz = WBUF_SIZE - WBUF_MASK(cons->wc);
453 sent = cons->ops->write(cons, &cons->wbuf[WBUF_MASK(cons->wc)],
457 * The other end may not have been initialized. Ignore
460 if (__predict_false(sent < 0))
464 * If force is set, spin until the console data is
465 * flushed through the domain controller.
467 if (sent == 0 && __predict_true(!force))
472 xencons_unlock(cons);
476 xencons_putc(struct xencons_priv *cons, int c, bool force_flush)
480 if ((cons->wp - cons->wc) < WBUF_SIZE)
481 cons->wbuf[WBUF_MASK(cons->wp++)] = c;
482 xencons_unlock(cons);
484 xencons_tx_flush(cons, force_flush);
486 return (xencons_tx_full(cons));
490 xencons_getc(struct xencons_priv *cons)
495 if (cons->rp != cons->rc) {
496 /* We need to return only one char */
497 ret = (int)cons->rbuf[RBUF_MASK(cons->rc)];
503 xencons_unlock(cons);
509 xencons_tx(struct tty *tp)
513 struct xencons_priv *cons;
515 cons = tty_softc(tp);
517 tty_lock_assert(tp, MA_OWNED);
520 * Don't transmit any character if the buffer is full. Otherwise,
521 * characters may be lost
523 if (xencons_tx_full(cons))
527 while (!cons_full && ttydisc_getc(tp, &c, 1) == 1)
528 cons_full = xencons_putc(cons, c, false);
534 xencons_intr(void *arg)
537 struct xencons_priv *cons;
541 cons = tty_softc(tp);
544 * The input will be used by the low-level console when KDB is active
550 * It's not necessary to retrieve input when the tty is not opened
558 while ((ret = xencons_getc(cons)) != -1) {
560 kdb_alt_break(ret, &cons->altbrk);
562 ttydisc_rint(tp, ret, 0);
564 ttydisc_rint_done(tp);
567 /* Try to flush remaining characters if necessary */
568 xencons_tx_flush(cons, 0);
572 * Helpers to call while shutting down:
573 * - Force flush all output
576 xencons_shutdown(void *arg, int howto)
582 xencons_tx_flush(tty_softc(tp), 1);
585 /*---------------------- Low-level console driver ---------------------------*/
587 xencons_cnprobe(struct consdev *cp)
593 cp->cn_pri = CN_REMOTE;
594 sprintf(cp->cn_name, "%s0", driver_name);
598 xencons_cninit(struct consdev *cp)
601 xencons_early_init();
605 xencons_cnterm(struct consdev *cp)
610 xencons_cngrab(struct consdev *cp)
615 xencons_cnungrab(struct consdev *cp)
620 xencons_cngetc(struct consdev *dev)
623 xencons_rx(&main_cons);
625 return (xencons_getc(&main_cons));
629 xencons_cnputc(struct consdev *dev, int c)
632 * The low-level console is used by KDB and panic. We have to ensure
633 * that any character sent will be seen by the backend.
635 xencons_putc(&main_cons, c, true);
638 CONSOLE_DRIVER(xencons);
640 /*----------------------------- TTY driver ---------------------------------*/
643 xencons_tty_open(struct tty *tp)
645 struct xencons_priv *cons;
647 cons = tty_softc(tp);
655 xencons_tty_close(struct tty *tp)
657 struct xencons_priv *cons;
659 cons = tty_softc(tp);
661 cons->opened = false;
665 xencons_timeout(void *v)
668 struct xencons_priv *cons;
671 cons = tty_softc(tp);
674 callout_reset(&cons->callout, XC_POLLTIME,
675 xencons_timeout, tp);
679 xencons_tty_outwakeup(struct tty *tp)
681 struct xencons_priv *cons;
683 cons = tty_softc(tp);
685 callout_stop(&cons->callout);
688 callout_reset(&cons->callout, XC_POLLTIME,
689 xencons_timeout, tp);
692 static struct ttydevsw xencons_ttydevsw = {
693 .tsw_flags = TF_NOPREFIX,
694 .tsw_open = xencons_tty_open,
695 .tsw_close = xencons_tty_close,
696 .tsw_outwakeup = xencons_tty_outwakeup,
699 /*------------------------ Main console driver ------------------------------*/
701 xencons_identify(driver_t *driver, device_t parent)
705 if (main_cons.ops == NULL)
708 child = BUS_ADD_CHILD(parent, 0, driver_name, 0);
712 xencons_probe(device_t dev)
715 device_set_desc(dev, "Xen Console");
716 return (BUS_PROBE_NOWILDCARD);
720 xencons_attach(device_t dev)
724 * The main console is already allocated statically in order to
725 * support low-level console
727 struct xencons_priv *cons;
732 tp = tty_alloc(&xencons_ttydevsw, cons);
733 tty_makedev(tp, NULL, "%s%r", driver_name, 0);
734 device_set_softc(dev, tp);
736 callout_init_mtx(&cons->callout, tty_getlock(tp), 0);
738 err = cons->ops->init(dev, tp, xencons_intr);
740 device_printf(dev, "Unable to initialize the console (%d)\n",
745 /* register handler to flush console on shutdown */
746 if ((EVENTHANDLER_REGISTER(shutdown_post_sync, xencons_shutdown,
747 tp, SHUTDOWN_PRI_DEFAULT)) == NULL)
748 device_printf(dev, "shutdown event registration failed!\n");
754 xencons_resume(device_t dev)
756 struct xencons_priv *cons;
760 tp = device_get_softc(dev);
761 cons = tty_softc(tp);
762 xen_intr_unbind(&cons->intr_handle);
764 err = cons->ops->init(dev, tp, xencons_intr);
766 device_printf(dev, "Unable to resume the console (%d)\n", err);
773 static devclass_t xencons_devclass;
775 static device_method_t xencons_methods[] = {
776 DEVMETHOD(device_identify, xencons_identify),
777 DEVMETHOD(device_probe, xencons_probe),
778 DEVMETHOD(device_attach, xencons_attach),
779 DEVMETHOD(device_resume, xencons_resume),
784 static driver_t xencons_driver = {
790 DRIVER_MODULE(xc, xenpv, xencons_driver, xencons_devclass, 0, 0);