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