1181643Skmacy#include <sys/cdefs.h> 2181643Skmacy__FBSDID("$FreeBSD$"); 3181643Skmacy 4181643Skmacy#include <sys/param.h> 5181643Skmacy#include <sys/module.h> 6181643Skmacy#include <sys/systm.h> 7181643Skmacy#include <sys/consio.h> 8189699Sdfr#include <sys/priv.h> 9181643Skmacy#include <sys/proc.h> 10181643Skmacy#include <sys/uio.h> 11181643Skmacy#include <sys/tty.h> 12181643Skmacy#include <sys/systm.h> 13181643Skmacy#include <sys/taskqueue.h> 14181643Skmacy#include <sys/conf.h> 15181643Skmacy#include <sys/kernel.h> 16181643Skmacy#include <sys/bus.h> 17181643Skmacy#include <machine/stdarg.h> 18255040Sgibbs#include <xen/xen-os.h> 19186557Skmacy#include <xen/hypervisor.h> 20186557Skmacy#include <xen/xen_intr.h> 21181643Skmacy#include <sys/cons.h> 22189699Sdfr#include <sys/kdb.h> 23181643Skmacy#include <sys/proc.h> 24181643Skmacy 25181643Skmacy#include <dev/xen/console/xencons_ring.h> 26181643Skmacy#include <xen/interface/io/console.h> 27181643Skmacy 28181643Skmacy 29181643Skmacy#include "opt_ddb.h" 30181643Skmacy#ifdef DDB 31181643Skmacy#include <ddb/ddb.h> 32181643Skmacy#endif 33181643Skmacy 34181643Skmacystatic char driver_name[] = "xc"; 35181643Skmacydevclass_t xc_devclass; /* do not make static */ 36181908Sedstatic void xcoutwakeup(struct tty *); 37181643Skmacystatic void xc_timeout(void *); 38181643Skmacystatic void __xencons_tx_flush(void); 39181643Skmacystatic boolean_t xcons_putc(int c); 40181643Skmacy 41181643Skmacy/* switch console so that shutdown can occur gracefully */ 42181643Skmacystatic void xc_shutdown(void *arg, int howto); 43181643Skmacystatic int xc_mute; 44181643Skmacy 45181643Skmacystatic void xcons_force_flush(void); 46181643Skmacystatic void xencons_priv_interrupt(void *); 47181643Skmacy 48196499Sedstatic cn_probe_t xc_cnprobe; 49196499Sedstatic cn_init_t xc_cninit; 50196499Sedstatic cn_term_t xc_cnterm; 51196499Sedstatic cn_getc_t xc_cngetc; 52196499Sedstatic cn_putc_t xc_cnputc; 53228631Savgstatic cn_grab_t xc_cngrab; 54228631Savgstatic cn_ungrab_t xc_cnungrab; 55181643Skmacy 56181643Skmacy#define XC_POLLTIME (hz/10) 57181643Skmacy 58196499SedCONSOLE_DRIVER(xc); 59181643Skmacy 60181643Skmacystatic int xen_console_up; 61181643Skmacystatic boolean_t xc_start_needed; 62181643Skmacystatic struct callout xc_callout; 63181643Skmacystruct mtx cn_mtx; 64181643Skmacy 65181643Skmacy#define RBUF_SIZE 1024 66181643Skmacy#define RBUF_MASK(_i) ((_i)&(RBUF_SIZE-1)) 67181643Skmacy#define WBUF_SIZE 4096 68181643Skmacy#define WBUF_MASK(_i) ((_i)&(WBUF_SIZE-1)) 69181643Skmacystatic char wbuf[WBUF_SIZE]; 70181643Skmacystatic char rbuf[RBUF_SIZE]; 71181643Skmacystatic int rc, rp; 72181643Skmacystatic unsigned int cnsl_evt_reg; 73181643Skmacystatic unsigned int wc, wp; /* write_cons, write_prod */ 74255040Sgibbsxen_intr_handle_t xen_intr_handle; 75255040Sgibbsdevice_t xencons_dev; 76181643Skmacy 77225343Srwatson#ifdef KDB 78225343Srwatsonstatic int xc_altbrk; 79225343Srwatson#endif 80225343Srwatson 81181643Skmacy#define CDEV_MAJOR 12 82183397Sed#define XCUNIT(x) (dev2unit(x)) 83181643Skmacy#define ISTTYOPEN(tp) ((tp) && ((tp)->t_state & TS_ISOPEN)) 84181643Skmacy#define CN_LOCK_INIT(x, _name) \ 85192003Skmacy mtx_init(&x, _name, NULL, MTX_SPIN|MTX_RECURSE) 86181643Skmacy 87181643Skmacy#define CN_LOCK(l) \ 88181643Skmacy do { \ 89181643Skmacy if (panicstr == NULL) \ 90192003Skmacy mtx_lock_spin(&(l)); \ 91181643Skmacy } while (0) 92181643Skmacy#define CN_UNLOCK(l) \ 93181643Skmacy do { \ 94181643Skmacy if (panicstr == NULL) \ 95192003Skmacy mtx_unlock_spin(&(l)); \ 96181643Skmacy } while (0) 97181643Skmacy#define CN_LOCK_ASSERT(x) mtx_assert(&x, MA_OWNED) 98181643Skmacy#define CN_LOCK_DESTROY(x) mtx_destroy(&x) 99181643Skmacy 100181643Skmacy 101181643Skmacystatic struct tty *xccons; 102181643Skmacy 103181908Sedstatic tsw_open_t xcopen; 104181908Sedstatic tsw_close_t xcclose; 105181643Skmacy 106181908Sedstatic struct ttydevsw xc_ttydevsw = { 107181908Sed .tsw_flags = TF_NOPREFIX, 108181908Sed .tsw_open = xcopen, 109181908Sed .tsw_close = xcclose, 110181908Sed .tsw_outwakeup = xcoutwakeup, 111181643Skmacy}; 112181643Skmacy 113181643Skmacystatic void 114196499Sedxc_cnprobe(struct consdev *cp) 115181643Skmacy{ 116181643Skmacy cp->cn_pri = CN_REMOTE; 117181643Skmacy sprintf(cp->cn_name, "%s0", driver_name); 118181643Skmacy} 119181643Skmacy 120181643Skmacy 121181643Skmacystatic void 122196499Sedxc_cninit(struct consdev *cp) 123181643Skmacy{ 124181643Skmacy CN_LOCK_INIT(cn_mtx,"XCONS LOCK"); 125181643Skmacy 126181643Skmacy} 127196499Sed 128196499Sedstatic void 129196499Sedxc_cnterm(struct consdev *cp) 130196499Sed{ 131181643Skmacy} 132181643Skmacy 133228631Savgstatic void 134228631Savgxc_cngrab(struct consdev *cp) 135228631Savg{ 136228631Savg} 137228631Savg 138228631Savgstatic void 139228631Savgxc_cnungrab(struct consdev *cp) 140228631Savg{ 141228631Savg} 142228631Savg 143196499Sedstatic int 144196499Sedxc_cngetc(struct consdev *dev) 145181643Skmacy{ 146216790Scperciva int ret; 147189699Sdfr 148189699Sdfr if (xencons_has_input()) 149189699Sdfr xencons_handle_input(NULL); 150181643Skmacy 151181643Skmacy CN_LOCK(cn_mtx); 152216790Scperciva if ((rp - rc) && !xc_mute) { 153181643Skmacy /* we need to return only one char */ 154181643Skmacy ret = (int)rbuf[RBUF_MASK(rc)]; 155181643Skmacy rc++; 156216790Scperciva } else 157216790Scperciva ret = -1; 158181643Skmacy CN_UNLOCK(cn_mtx); 159181643Skmacy return(ret); 160181643Skmacy} 161181643Skmacy 162181643Skmacystatic void 163196499Sedxc_cnputc_domu(struct consdev *dev, int c) 164181643Skmacy{ 165181643Skmacy xcons_putc(c); 166181643Skmacy} 167181643Skmacy 168181643Skmacystatic void 169196499Sedxc_cnputc_dom0(struct consdev *dev, int c) 170181643Skmacy{ 171181643Skmacy HYPERVISOR_console_io(CONSOLEIO_write, 1, (char *)&c); 172181643Skmacy} 173181643Skmacy 174196499Sedstatic void 175196499Sedxc_cnputc(struct consdev *dev, int c) 176196499Sed{ 177196499Sed 178196499Sed if (xen_start_info->flags & SIF_INITDOMAIN) 179196499Sed xc_cnputc_dom0(dev, c); 180196499Sed else 181196499Sed xc_cnputc_domu(dev, c); 182196499Sed} 183196499Sed 184181643Skmacyextern int db_active; 185181643Skmacystatic boolean_t 186181643Skmacyxcons_putc(int c) 187181643Skmacy{ 188181643Skmacy int force_flush = xc_mute || 189181643Skmacy#ifdef DDB 190181643Skmacy db_active || 191181643Skmacy#endif 192181643Skmacy panicstr; /* we're not gonna recover, so force 193181643Skmacy * flush 194181643Skmacy */ 195181643Skmacy 196181643Skmacy if ((wp-wc) < (WBUF_SIZE-1)) { 197181643Skmacy if ((wbuf[WBUF_MASK(wp++)] = c) == '\n') { 198181643Skmacy wbuf[WBUF_MASK(wp++)] = '\r'; 199181643Skmacy#ifdef notyet 200181643Skmacy if (force_flush) 201181643Skmacy xcons_force_flush(); 202181643Skmacy#endif 203181643Skmacy } 204181643Skmacy } else if (force_flush) { 205181643Skmacy#ifdef notyet 206181643Skmacy xcons_force_flush(); 207181643Skmacy#endif 208181643Skmacy } 209181643Skmacy if (cnsl_evt_reg) 210181643Skmacy __xencons_tx_flush(); 211181643Skmacy 212181643Skmacy /* inform start path that we're pretty full */ 213181643Skmacy return ((wp - wc) >= WBUF_SIZE - 100) ? TRUE : FALSE; 214181643Skmacy} 215181643Skmacy 216181643Skmacystatic void 217181643Skmacyxc_identify(driver_t *driver, device_t parent) 218181643Skmacy{ 219181643Skmacy device_t child; 220181643Skmacy child = BUS_ADD_CHILD(parent, 0, driver_name, 0); 221181643Skmacy device_set_driver(child, driver); 222181643Skmacy device_set_desc(child, "Xen Console"); 223181643Skmacy} 224181643Skmacy 225181643Skmacystatic int 226181643Skmacyxc_probe(device_t dev) 227181643Skmacy{ 228181643Skmacy 229181643Skmacy return (0); 230181643Skmacy} 231181643Skmacy 232181643Skmacystatic int 233181643Skmacyxc_attach(device_t dev) 234181643Skmacy{ 235186557Skmacy int error; 236181643Skmacy 237255040Sgibbs xencons_dev = dev; 238193018Sed xccons = tty_alloc(&xc_ttydevsw, NULL); 239181908Sed tty_makedev(xccons, NULL, "xc%r", 0); 240181643Skmacy 241181643Skmacy callout_init(&xc_callout, 0); 242181643Skmacy 243181643Skmacy xencons_ring_init(); 244181643Skmacy 245181643Skmacy cnsl_evt_reg = 1; 246181643Skmacy callout_reset(&xc_callout, XC_POLLTIME, xc_timeout, xccons); 247181643Skmacy 248181643Skmacy if (xen_start_info->flags & SIF_INITDOMAIN) { 249255040Sgibbs error = xen_intr_bind_virq(dev, VIRQ_CONSOLE, 0, NULL, 250255040Sgibbs xencons_priv_interrupt, NULL, 251255040Sgibbs INTR_TYPE_TTY, &xen_intr_handle); 252255040Sgibbs KASSERT(error >= 0, ("can't register console interrupt")); 253181643Skmacy } 254181643Skmacy 255181643Skmacy /* register handler to flush console on shutdown */ 256181643Skmacy if ((EVENTHANDLER_REGISTER(shutdown_post_sync, xc_shutdown, 257181643Skmacy NULL, SHUTDOWN_PRI_DEFAULT)) == NULL) 258181643Skmacy printf("xencons: shutdown event registration failed!\n"); 259181643Skmacy 260181643Skmacy return (0); 261181643Skmacy} 262181643Skmacy 263181643Skmacy/* 264181643Skmacy * return 0 for all console input, force flush all output. 265181643Skmacy */ 266181643Skmacystatic void 267181643Skmacyxc_shutdown(void *arg, int howto) 268181643Skmacy{ 269181643Skmacy xc_mute = 1; 270181643Skmacy xcons_force_flush(); 271181643Skmacy} 272181643Skmacy 273181643Skmacyvoid 274181643Skmacyxencons_rx(char *buf, unsigned len) 275181643Skmacy{ 276181643Skmacy int i; 277181643Skmacy struct tty *tp = xccons; 278181908Sed 279189699Sdfr if (xen_console_up 280189699Sdfr#ifdef DDB 281189699Sdfr && !kdb_active 282189699Sdfr#endif 283189699Sdfr ) { 284181908Sed tty_lock(tp); 285225343Srwatson for (i = 0; i < len; i++) { 286225343Srwatson#ifdef KDB 287225343Srwatson kdb_alt_break(buf[i], &xc_altbrk); 288225343Srwatson#endif 289181908Sed ttydisc_rint(tp, buf[i], 0); 290225343Srwatson } 291181908Sed ttydisc_rint_done(tp); 292181908Sed tty_unlock(tp); 293181908Sed } else { 294192004Skmacy CN_LOCK(cn_mtx); 295181908Sed for (i = 0; i < len; i++) 296181643Skmacy rbuf[RBUF_MASK(rp++)] = buf[i]; 297192004Skmacy CN_UNLOCK(cn_mtx); 298181643Skmacy } 299181643Skmacy} 300181643Skmacy 301181643Skmacystatic void 302181643Skmacy__xencons_tx_flush(void) 303181643Skmacy{ 304181908Sed int sz; 305181643Skmacy 306181643Skmacy CN_LOCK(cn_mtx); 307181643Skmacy while (wc != wp) { 308181643Skmacy int sent; 309181643Skmacy sz = wp - wc; 310181643Skmacy if (sz > (WBUF_SIZE - WBUF_MASK(wc))) 311181643Skmacy sz = WBUF_SIZE - WBUF_MASK(wc); 312181643Skmacy if (xen_start_info->flags & SIF_INITDOMAIN) { 313181643Skmacy HYPERVISOR_console_io(CONSOLEIO_write, sz, &wbuf[WBUF_MASK(wc)]); 314181643Skmacy wc += sz; 315181643Skmacy } else { 316181643Skmacy sent = xencons_ring_send(&wbuf[WBUF_MASK(wc)], sz); 317181643Skmacy if (sent == 0) 318181643Skmacy break; 319181643Skmacy wc += sent; 320181643Skmacy } 321181643Skmacy } 322181643Skmacy CN_UNLOCK(cn_mtx); 323181643Skmacy} 324181643Skmacy 325181643Skmacyvoid 326181643Skmacyxencons_tx(void) 327181643Skmacy{ 328181643Skmacy __xencons_tx_flush(); 329181643Skmacy} 330181643Skmacy 331181643Skmacystatic void 332181643Skmacyxencons_priv_interrupt(void *arg) 333181643Skmacy{ 334181643Skmacy 335181643Skmacy static char rbuf[16]; 336181643Skmacy int l; 337181643Skmacy 338181643Skmacy while ((l = HYPERVISOR_console_io(CONSOLEIO_read, 16, rbuf)) > 0) 339181643Skmacy xencons_rx(rbuf, l); 340181643Skmacy 341181643Skmacy xencons_tx(); 342181643Skmacy} 343181643Skmacy 344181908Sedstatic int 345181908Sedxcopen(struct tty *tp) 346181643Skmacy{ 347181643Skmacy 348181643Skmacy xen_console_up = 1; 349181643Skmacy return (0); 350181643Skmacy} 351181643Skmacy 352181908Sedstatic void 353181908Sedxcclose(struct tty *tp) 354181643Skmacy{ 355181643Skmacy 356181908Sed xen_console_up = 0; 357181643Skmacy} 358181643Skmacy 359181643Skmacystatic inline int 360181643Skmacy__xencons_put_char(int ch) 361181643Skmacy{ 362181643Skmacy char _ch = (char)ch; 363181643Skmacy if ((wp - wc) == WBUF_SIZE) 364181643Skmacy return 0; 365181643Skmacy wbuf[WBUF_MASK(wp++)] = _ch; 366181643Skmacy return 1; 367181643Skmacy} 368181643Skmacy 369181643Skmacy 370181643Skmacystatic void 371181908Sedxcoutwakeup(struct tty *tp) 372181643Skmacy{ 373181643Skmacy boolean_t cons_full = FALSE; 374181908Sed char c; 375181643Skmacy 376181908Sed while (ttydisc_getc(tp, &c, 1) == 1 && !cons_full) 377181908Sed cons_full = xcons_putc(c); 378181643Skmacy 379181908Sed if (cons_full) { 380181643Skmacy /* let the timeout kick us in a bit */ 381181643Skmacy xc_start_needed = TRUE; 382181643Skmacy } 383181643Skmacy 384181643Skmacy} 385181643Skmacy 386181643Skmacystatic void 387181643Skmacyxc_timeout(void *v) 388181643Skmacy{ 389181643Skmacy struct tty *tp; 390181643Skmacy int c; 391181643Skmacy 392181643Skmacy tp = (struct tty *)v; 393181643Skmacy 394181908Sed tty_lock(tp); 395196499Sed while ((c = xc_cngetc(NULL)) != -1) 396181908Sed ttydisc_rint(tp, c, 0); 397181643Skmacy 398181643Skmacy if (xc_start_needed) { 399181643Skmacy xc_start_needed = FALSE; 400181908Sed xcoutwakeup(tp); 401181643Skmacy } 402181916Skmacy tty_unlock(tp); 403181643Skmacy 404181643Skmacy callout_reset(&xc_callout, XC_POLLTIME, xc_timeout, tp); 405181643Skmacy} 406181643Skmacy 407181643Skmacystatic device_method_t xc_methods[] = { 408181643Skmacy DEVMETHOD(device_identify, xc_identify), 409181643Skmacy DEVMETHOD(device_probe, xc_probe), 410181643Skmacy DEVMETHOD(device_attach, xc_attach), 411246128Ssbz 412246128Ssbz DEVMETHOD_END 413181643Skmacy}; 414181643Skmacy 415181643Skmacystatic driver_t xc_driver = { 416181643Skmacy driver_name, 417181643Skmacy xc_methods, 418181908Sed 0, 419181643Skmacy}; 420181643Skmacy 421181643Skmacy/*** Forcibly flush console data before dying. ***/ 422181643Skmacyvoid 423181643Skmacyxcons_force_flush(void) 424181643Skmacy{ 425181643Skmacy int sz; 426181643Skmacy 427181643Skmacy if (xen_start_info->flags & SIF_INITDOMAIN) 428181643Skmacy return; 429181643Skmacy 430181643Skmacy /* Spin until console data is flushed through to the domain controller. */ 431181643Skmacy while (wc != wp) { 432181643Skmacy int sent = 0; 433181643Skmacy if ((sz = wp - wc) == 0) 434181643Skmacy continue; 435181643Skmacy 436181643Skmacy sent = xencons_ring_send(&wbuf[WBUF_MASK(wc)], sz); 437181643Skmacy if (sent > 0) 438181643Skmacy wc += sent; 439181643Skmacy } 440181643Skmacy} 441181643Skmacy 442181643SkmacyDRIVER_MODULE(xc, nexus, xc_driver, xc_devclass, 0, 0); 443