1#include <sys/cdefs.h> 2__FBSDID("$FreeBSD$"); 3 4#include <sys/param.h> 5#include <sys/module.h> 6#include <sys/systm.h> 7#include <sys/consio.h> 8#include <sys/priv.h> 9#include <sys/proc.h> 10#include <sys/uio.h> 11#include <sys/tty.h> 12#include <sys/systm.h> 13#include <sys/taskqueue.h> 14#include <sys/conf.h> 15#include <sys/kernel.h> 16#include <sys/bus.h> 17#include <machine/stdarg.h> 18#include <machine/xen/xen-os.h> 19#include <xen/hypervisor.h> 20#include <xen/xen_intr.h> 21#include <sys/cons.h> 22#include <sys/kdb.h> 23#include <sys/proc.h> 24 25#include <dev/xen/console/xencons_ring.h> 26#include <xen/interface/io/console.h> 27 28 29#include "opt_ddb.h" 30#ifdef DDB 31#include <ddb/ddb.h> 32#endif 33 34static char driver_name[] = "xc"; 35devclass_t xc_devclass; /* do not make static */ 36static void xcoutwakeup(struct tty *); 37static void xc_timeout(void *); 38static void __xencons_tx_flush(void); 39static boolean_t xcons_putc(int c); 40 41/* switch console so that shutdown can occur gracefully */ 42static void xc_shutdown(void *arg, int howto); 43static int xc_mute; 44 45static void xcons_force_flush(void); 46static void xencons_priv_interrupt(void *); 47 48static cn_probe_t xc_cnprobe; 49static cn_init_t xc_cninit; 50static cn_term_t xc_cnterm; 51static cn_getc_t xc_cngetc; 52static cn_putc_t xc_cnputc; 53static cn_grab_t xc_cngrab; 54static cn_ungrab_t xc_cnungrab; 55 56#define XC_POLLTIME (hz/10) 57 58CONSOLE_DRIVER(xc); 59 60static int xen_console_up; 61static boolean_t xc_start_needed; 62static struct callout xc_callout; 63struct mtx cn_mtx; 64 65#define RBUF_SIZE 1024 66#define RBUF_MASK(_i) ((_i)&(RBUF_SIZE-1)) 67#define WBUF_SIZE 4096 68#define WBUF_MASK(_i) ((_i)&(WBUF_SIZE-1)) 69static char wbuf[WBUF_SIZE]; 70static char rbuf[RBUF_SIZE]; 71static int rc, rp; 72static unsigned int cnsl_evt_reg; 73static unsigned int wc, wp; /* write_cons, write_prod */ 74 75#ifdef KDB 76static int xc_altbrk; 77#endif 78 79#define CDEV_MAJOR 12 80#define XCUNIT(x) (dev2unit(x)) 81#define ISTTYOPEN(tp) ((tp) && ((tp)->t_state & TS_ISOPEN)) 82#define CN_LOCK_INIT(x, _name) \ 83 mtx_init(&x, _name, NULL, MTX_SPIN|MTX_RECURSE) 84 85#define CN_LOCK(l) \ 86 do { \ 87 if (panicstr == NULL) \ 88 mtx_lock_spin(&(l)); \ 89 } while (0) 90#define CN_UNLOCK(l) \ 91 do { \ 92 if (panicstr == NULL) \ 93 mtx_unlock_spin(&(l)); \ 94 } while (0) 95#define CN_LOCK_ASSERT(x) mtx_assert(&x, MA_OWNED) 96#define CN_LOCK_DESTROY(x) mtx_destroy(&x) 97 98 99static struct tty *xccons; 100 101static tsw_open_t xcopen; 102static tsw_close_t xcclose; 103 104static struct ttydevsw xc_ttydevsw = { 105 .tsw_flags = TF_NOPREFIX, 106 .tsw_open = xcopen, 107 .tsw_close = xcclose, 108 .tsw_outwakeup = xcoutwakeup, 109}; 110 111static void 112xc_cnprobe(struct consdev *cp) 113{ 114 cp->cn_pri = CN_REMOTE; 115 sprintf(cp->cn_name, "%s0", driver_name); 116} 117 118 119static void 120xc_cninit(struct consdev *cp) 121{ 122 CN_LOCK_INIT(cn_mtx,"XCONS LOCK"); 123 124} 125 126static void 127xc_cnterm(struct consdev *cp) 128{ 129} 130 131static void 132xc_cngrab(struct consdev *cp) 133{ 134} 135 136static void 137xc_cnungrab(struct consdev *cp) 138{ 139} 140 141static int 142xc_cngetc(struct consdev *dev) 143{ 144 int ret; 145 146 if (xencons_has_input()) 147 xencons_handle_input(NULL); 148 149 CN_LOCK(cn_mtx); 150 if ((rp - rc) && !xc_mute) { 151 /* we need to return only one char */ 152 ret = (int)rbuf[RBUF_MASK(rc)]; 153 rc++; 154 } else 155 ret = -1; 156 CN_UNLOCK(cn_mtx); 157 return(ret); 158} 159 160static void 161xc_cnputc_domu(struct consdev *dev, int c) 162{ 163 xcons_putc(c); 164} 165 166static void 167xc_cnputc_dom0(struct consdev *dev, int c) 168{ 169 HYPERVISOR_console_io(CONSOLEIO_write, 1, (char *)&c); 170} 171 172static void 173xc_cnputc(struct consdev *dev, int c) 174{ 175 176 if (xen_start_info->flags & SIF_INITDOMAIN) 177 xc_cnputc_dom0(dev, c); 178 else 179 xc_cnputc_domu(dev, c); 180} 181 182extern int db_active; 183static boolean_t 184xcons_putc(int c) 185{ 186 int force_flush = xc_mute || 187#ifdef DDB 188 db_active || 189#endif 190 panicstr; /* we're not gonna recover, so force 191 * flush 192 */ 193 194 if ((wp-wc) < (WBUF_SIZE-1)) { 195 if ((wbuf[WBUF_MASK(wp++)] = c) == '\n') { 196 wbuf[WBUF_MASK(wp++)] = '\r'; 197#ifdef notyet 198 if (force_flush) 199 xcons_force_flush(); 200#endif 201 } 202 } else if (force_flush) { 203#ifdef notyet 204 xcons_force_flush(); 205#endif 206 } 207 if (cnsl_evt_reg) 208 __xencons_tx_flush(); 209 210 /* inform start path that we're pretty full */ 211 return ((wp - wc) >= WBUF_SIZE - 100) ? TRUE : FALSE; 212} 213 214static void 215xc_identify(driver_t *driver, device_t parent) 216{ 217 device_t child; 218 child = BUS_ADD_CHILD(parent, 0, driver_name, 0); 219 device_set_driver(child, driver); 220 device_set_desc(child, "Xen Console"); 221} 222 223static int 224xc_probe(device_t dev) 225{ 226 227 return (0); 228} 229 230static int 231xc_attach(device_t dev) 232{ 233 int error; 234 235 xccons = tty_alloc(&xc_ttydevsw, NULL); 236 tty_makedev(xccons, NULL, "xc%r", 0); 237 238 callout_init(&xc_callout, 0); 239 240 xencons_ring_init(); 241 242 cnsl_evt_reg = 1; 243 callout_reset(&xc_callout, XC_POLLTIME, xc_timeout, xccons); 244 245 if (xen_start_info->flags & SIF_INITDOMAIN) { 246 error = bind_virq_to_irqhandler( 247 VIRQ_CONSOLE, 248 0, 249 "console", 250 NULL, 251 xencons_priv_interrupt, NULL, 252 INTR_TYPE_TTY, NULL); 253 254 KASSERT(error >= 0, ("can't register console interrupt")); 255 } 256 257 /* register handler to flush console on shutdown */ 258 if ((EVENTHANDLER_REGISTER(shutdown_post_sync, xc_shutdown, 259 NULL, SHUTDOWN_PRI_DEFAULT)) == NULL) 260 printf("xencons: shutdown event registration failed!\n"); 261 262 return (0); 263} 264 265/* 266 * return 0 for all console input, force flush all output. 267 */ 268static void 269xc_shutdown(void *arg, int howto) 270{ 271 xc_mute = 1; 272 xcons_force_flush(); 273} 274 275void 276xencons_rx(char *buf, unsigned len) 277{ 278 int i; 279 struct tty *tp = xccons; 280 281 if (xen_console_up 282#ifdef DDB 283 && !kdb_active 284#endif 285 ) { 286 tty_lock(tp); 287 for (i = 0; i < len; i++) { 288#ifdef KDB 289 kdb_alt_break(buf[i], &xc_altbrk); 290#endif 291 ttydisc_rint(tp, buf[i], 0); 292 } 293 ttydisc_rint_done(tp); 294 tty_unlock(tp); 295 } else { 296 CN_LOCK(cn_mtx); 297 for (i = 0; i < len; i++) 298 rbuf[RBUF_MASK(rp++)] = buf[i]; 299 CN_UNLOCK(cn_mtx); 300 } 301} 302 303static void 304__xencons_tx_flush(void) 305{ 306 int sz; 307 308 CN_LOCK(cn_mtx); 309 while (wc != wp) { 310 int sent; 311 sz = wp - wc; 312 if (sz > (WBUF_SIZE - WBUF_MASK(wc))) 313 sz = WBUF_SIZE - WBUF_MASK(wc); 314 if (xen_start_info->flags & SIF_INITDOMAIN) { 315 HYPERVISOR_console_io(CONSOLEIO_write, sz, &wbuf[WBUF_MASK(wc)]); 316 wc += sz; 317 } else { 318 sent = xencons_ring_send(&wbuf[WBUF_MASK(wc)], sz); 319 if (sent == 0) 320 break; 321 wc += sent; 322 } 323 } 324 CN_UNLOCK(cn_mtx); 325} 326 327void 328xencons_tx(void) 329{ 330 __xencons_tx_flush(); 331} 332 333static void 334xencons_priv_interrupt(void *arg) 335{ 336 337 static char rbuf[16]; 338 int l; 339 340 while ((l = HYPERVISOR_console_io(CONSOLEIO_read, 16, rbuf)) > 0) 341 xencons_rx(rbuf, l); 342 343 xencons_tx(); 344} 345 346static int 347xcopen(struct tty *tp) 348{ 349 350 xen_console_up = 1; 351 return (0); 352} 353 354static void 355xcclose(struct tty *tp) 356{ 357 358 xen_console_up = 0; 359} 360 361#if 0 362static inline int 363__xencons_put_char(int ch) 364{ 365 char _ch = (char)ch; 366 if ((wp - wc) == WBUF_SIZE) 367 return 0; 368 wbuf[WBUF_MASK(wp++)] = _ch; 369 return 1; 370} 371#endif 372 373 374static void 375xcoutwakeup(struct tty *tp) 376{ 377 boolean_t cons_full = FALSE; 378 char c; 379 380 while (ttydisc_getc(tp, &c, 1) == 1 && !cons_full) 381 cons_full = xcons_putc(c); 382 383 if (cons_full) { 384 /* let the timeout kick us in a bit */ 385 xc_start_needed = TRUE; 386 } 387 388} 389 390static void 391xc_timeout(void *v) 392{ 393 struct tty *tp; 394 int c; 395 396 tp = (struct tty *)v; 397 398 tty_lock(tp); 399 while ((c = xc_cngetc(NULL)) != -1) 400 ttydisc_rint(tp, c, 0); 401 402 if (xc_start_needed) { 403 xc_start_needed = FALSE; 404 xcoutwakeup(tp); 405 } 406 tty_unlock(tp); 407 408 callout_reset(&xc_callout, XC_POLLTIME, xc_timeout, tp); 409} 410 411static device_method_t xc_methods[] = { 412 DEVMETHOD(device_identify, xc_identify), 413 DEVMETHOD(device_probe, xc_probe), 414 DEVMETHOD(device_attach, xc_attach), 415 {0, 0} 416}; 417 418static driver_t xc_driver = { 419 driver_name, 420 xc_methods, 421 0, 422}; 423 424/*** Forcibly flush console data before dying. ***/ 425void 426xcons_force_flush(void) 427{ 428 int sz; 429 430 if (xen_start_info->flags & SIF_INITDOMAIN) 431 return; 432 433 /* Spin until console data is flushed through to the domain controller. */ 434 while (wc != wp) { 435 int sent = 0; 436 if ((sz = wp - wc) == 0) 437 continue; 438 439 sent = xencons_ring_send(&wbuf[WBUF_MASK(wc)], sz); 440 if (sent > 0) 441 wc += sent; 442 } 443} 444 445DRIVER_MODULE(xc, nexus, xc_driver, xc_devclass, 0, 0); 446