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