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