1/* $NetBSD: xencons.c,v 1.53 2023/02/25 00:35:40 riastradh Exp $ */ 2 3/* 4 * Copyright (c) 2006 Manuel Bouyer. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * 26 */ 27 28/* 29 * 30 * Copyright (c) 2004 Christian Limpach. 31 * All rights reserved. 32 * 33 * Redistribution and use in source and binary forms, with or without 34 * modification, are permitted provided that the following conditions 35 * are met: 36 * 1. Redistributions of source code must retain the above copyright 37 * notice, this list of conditions and the following disclaimer. 38 * 2. Redistributions in binary form must reproduce the above copyright 39 * notice, this list of conditions and the following disclaimer in the 40 * documentation and/or other materials provided with the distribution. 41 * 42 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 43 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 44 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 45 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 46 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 47 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 48 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 49 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 50 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 51 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 52 */ 53 54 55#include <sys/cdefs.h> 56__KERNEL_RCSID(0, "$NetBSD: xencons.c,v 1.53 2023/02/25 00:35:40 riastradh Exp $"); 57 58#include "opt_xen.h" 59 60#include <sys/param.h> 61#include <sys/ioctl.h> 62#include <sys/proc.h> 63#include <sys/tty.h> 64#include <sys/systm.h> 65#include <sys/device.h> 66#include <sys/conf.h> 67#include <sys/kauth.h> 68#include <sys/kernel.h> 69 70#include <xen/intr.h> 71#include <xen/xen.h> 72#include <xen/hypervisor.h> 73#include <xen/evtchn.h> 74#include <uvm/uvm.h> 75#include <machine/pmap.h> 76#include <xen/include/public/io/console.h> 77 78#include <dev/cons.h> 79 80#ifdef DDB 81#include <ddb/db_output.h> /* XXX for db_max_line */ 82#endif 83 84#undef XENDEBUG 85 86#ifdef XENDEBUG 87#define XENPRINTK(x) printk x 88#else 89#define XENPRINTK(x) 90#endif 91 92static int xencons_isconsole = 0; 93static struct xencons_softc *xencons_console_device = NULL; 94static struct intrhand *ih; 95 96#define XENCONS_UNIT(x) (minor(x)) 97#define XENCONS_BURST 128 98 99static int xencons_match(device_t, cfdata_t, void *); 100static void xencons_attach(device_t, device_t, void *); 101static int xencons_intr(void *); 102static void xencons_tty_input(struct xencons_softc *, char*, int); 103 104struct xencons_softc { 105 device_t sc_dev; 106 struct tty *sc_tty; 107 int polling; 108}; 109volatile struct xencons_interface *xencons_interface; 110 111CFATTACH_DECL_NEW(xencons, sizeof(struct xencons_softc), 112 xencons_match, xencons_attach, NULL, NULL); 113 114extern struct cfdriver xencons_cd; 115 116static dev_type_open(xencons_open); 117static dev_type_close(xencons_close); 118static dev_type_read(xencons_read); 119static dev_type_write(xencons_write); 120static dev_type_ioctl(xencons_ioctl); 121static dev_type_stop(xencons_stop); 122static dev_type_tty(xencons_tty); 123static dev_type_poll(xencons_poll); 124 125const struct cdevsw xencons_cdevsw = { 126 .d_open = xencons_open, 127 .d_close = xencons_close, 128 .d_read = xencons_read, 129 .d_write = xencons_write, 130 .d_ioctl = xencons_ioctl, 131 .d_stop = xencons_stop, 132 .d_tty = xencons_tty, 133 .d_poll = xencons_poll, 134 .d_mmap = NULL, /* XXX: is this safe? - dholland 20140315 */ 135 .d_kqfilter = ttykqfilter, 136 .d_discard = nodiscard, 137 .d_flag = D_TTY 138}; 139 140static int xencons_handler(void *); 141static int xenconscn_getc(dev_t); 142static void xenconscn_putc(dev_t, int); 143static void xenconscn_pollc(dev_t, int); 144 145static bool xencons_suspend(device_t, const pmf_qual_t *); 146static bool xencons_resume(device_t, const pmf_qual_t *); 147 148static struct consdev xencons = { 149 NULL, NULL, xenconscn_getc, xenconscn_putc, xenconscn_pollc, 150 NULL, NULL, NULL, NODEV, CN_NORMAL 151}; 152 153static struct cnm_state xencons_cnm_state; 154 155static void xencons_start(struct tty *); 156static int xencons_param(struct tty *, struct termios *); 157 158static int 159xencons_match(device_t parent, cfdata_t match, void *aux) 160{ 161 struct xencons_attach_args *xa = (struct xencons_attach_args *)aux; 162 163 if (strcmp(xa->xa_device, "xencons") == 0) 164 return 1; 165 return 0; 166} 167 168static void 169xencons_attach(device_t parent, device_t self, void *aux) 170{ 171 struct xencons_softc *sc = device_private(self); 172 173 aprint_normal(": Xen Virtual Console Driver\n"); 174 175 sc->sc_dev = self; 176 sc->sc_tty = tty_alloc(); 177 tty_attach(sc->sc_tty); 178 sc->sc_tty->t_oproc = xencons_start; 179 sc->sc_tty->t_param = xencons_param; 180 181 if (xencons_isconsole) { 182 int maj; 183 184 /* Locate the major number. */ 185 maj = cdevsw_lookup_major(&xencons_cdevsw); 186 187 /* There can be only one, but it can have any unit number. */ 188 cn_tab->cn_dev = makedev(maj, device_unit(self)); 189 190 aprint_verbose_dev(self, "console major %d, unit %d\n", 191 maj, device_unit(self)); 192 193 sc->sc_tty->t_dev = cn_tab->cn_dev; 194 195#ifdef DDB 196 /* Set db_max_line to avoid paging. */ 197 db_max_line = 0x7fffffff; 198#endif 199 200 xencons_console_device = sc; 201 202 xencons_resume(self, PMF_Q_NONE); 203 } 204 sc->polling = 0; 205 206 if (!pmf_device_register(self, xencons_suspend, xencons_resume)) 207 aprint_error_dev(self, "couldn't establish power handler\n"); 208} 209 210static bool 211xencons_suspend(device_t dev, const pmf_qual_t *qual) { 212 213 int evtch; 214 215 /* dom0 console should not be suspended */ 216 if (!xendomain_is_dom0()) { 217 evtch = xen_start_info.console_evtchn; 218 hypervisor_mask_event(evtch); 219 xen_intr_disestablish(ih); 220 aprint_verbose_dev(dev, "removed event channel %d\n", ih->ih_pin); 221 } 222 223 return true; 224} 225 226static bool 227xencons_resume(device_t dev, const pmf_qual_t *qual) { 228 229 int evtch = -1; 230 231 if (xendomain_is_dom0()) { 232 /* dom0 console resume is required only during first start-up */ 233 if (cold) { 234 evtch = bind_virq_to_evtch(VIRQ_CONSOLE); 235 ih = xen_intr_establish_xname(-1, &xen_pic, evtch, 236 IST_LEVEL, IPL_TTY, xencons_intr, 237 xencons_console_device, false, 238 device_xname(dev)); 239 KASSERT(ih != NULL); 240 } 241 } else { 242 evtch = xen_start_info.console_evtchn; 243 ih = xen_intr_establish_xname(-1, &xen_pic, evtch, 244 IST_LEVEL, IPL_TTY, xencons_handler, 245 xencons_console_device, false, device_xname(dev)); 246 KASSERT(ih != NULL); 247 } 248 249 if (evtch != -1) { 250 aprint_verbose_dev(dev, "using event channel %d\n", evtch); 251 hypervisor_unmask_event(evtch); 252 } 253 254 return true; 255} 256 257static int 258xencons_open(dev_t dev, int flag, int mode, struct lwp *l) 259{ 260 struct xencons_softc *sc; 261 struct tty *tp; 262 263 sc = device_lookup_private(&xencons_cd, XENCONS_UNIT(dev)); 264 if (sc == NULL) 265 return (ENXIO); 266 267 tp = sc->sc_tty; 268 269 if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp)) 270 return (EBUSY); 271 272 if ((tp->t_state & TS_ISOPEN) == 0 && tp->t_wopen == 0) { 273 tp->t_dev = dev; 274 ttychars(tp); 275 tp->t_iflag = TTYDEF_IFLAG; 276 tp->t_oflag = TTYDEF_OFLAG; 277 tp->t_cflag = TTYDEF_CFLAG; 278 tp->t_lflag = TTYDEF_LFLAG; 279 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 280 xencons_param(tp, &tp->t_termios); 281 ttsetwater(tp); 282 } 283 tp->t_state |= TS_CARR_ON; 284 285 return ((*tp->t_linesw->l_open)(dev, tp)); 286} 287 288static int 289xencons_close(dev_t dev, int flag, int mode, struct lwp *l) 290{ 291 struct xencons_softc *sc = device_lookup_private(&xencons_cd, 292 XENCONS_UNIT(dev)); 293 struct tty *tp = sc->sc_tty; 294 295 if (tp == NULL) 296 return (0); 297 (*tp->t_linesw->l_close)(tp, flag); 298 ttyclose(tp); 299#ifdef notyet /* XXX */ 300 tty_free(tp); 301#endif 302 return (0); 303} 304 305static int 306xencons_read(dev_t dev, struct uio *uio, int flag) 307{ 308 struct xencons_softc *sc = device_lookup_private(&xencons_cd, 309 XENCONS_UNIT(dev)); 310 struct tty *tp = sc->sc_tty; 311 312 return ((*tp->t_linesw->l_read)(tp, uio, flag)); 313} 314 315static int 316xencons_write(dev_t dev, struct uio *uio, int flag) 317{ 318 struct xencons_softc *sc = device_lookup_private(&xencons_cd, 319 XENCONS_UNIT(dev)); 320 struct tty *tp = sc->sc_tty; 321 322 return ((*tp->t_linesw->l_write)(tp, uio, flag)); 323} 324 325static int 326xencons_poll(dev_t dev, int events, struct lwp *l) 327{ 328 struct xencons_softc *sc = device_lookup_private(&xencons_cd, 329 XENCONS_UNIT(dev)); 330 struct tty *tp = sc->sc_tty; 331 332 return ((*tp->t_linesw->l_poll)(tp, events, l)); 333} 334 335static struct tty * 336xencons_tty(dev_t dev) 337{ 338 struct xencons_softc *sc = device_lookup_private(&xencons_cd, 339 XENCONS_UNIT(dev)); 340 struct tty *tp = sc->sc_tty; 341 342 return (tp); 343} 344 345static int 346xencons_ioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 347{ 348 struct xencons_softc *sc = device_lookup_private(&xencons_cd, 349 XENCONS_UNIT(dev)); 350 struct tty *tp = sc->sc_tty; 351 int error; 352 353 error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l); 354 if (error != EPASSTHROUGH) 355 return (error); 356 357 error = ttioctl(tp, cmd, data, flag, l); 358 if (error != EPASSTHROUGH) 359 return (error); 360 361 switch (cmd) { 362 default: 363 return (EPASSTHROUGH); 364 } 365 366#ifdef DIAGNOSTIC 367 panic("xencons_ioctl: impossible"); 368#endif 369} 370 371static void 372xencons_start(struct tty *tp) 373{ 374 struct clist *cl; 375 int s; 376 377 s = spltty(); 378 if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) 379 goto out; 380 tp->t_state |= TS_BUSY; 381 splx(s); 382 383 /* 384 * We need to do this outside spl since it could be fairly 385 * expensive and we don't want our serial ports to overflow. 386 */ 387 cl = &tp->t_outq; 388 if (xendomain_is_dom0()) { 389 int len, r; 390 u_char buf[XENCONS_BURST+1]; 391 392 len = q_to_b(cl, buf, XENCONS_BURST); 393 while (len > 0) { 394 r = HYPERVISOR_console_io(CONSOLEIO_write, len, buf); 395 if (r <= 0) 396 break; 397 len -= r; 398 } 399 } else { 400 XENCONS_RING_IDX cons, prod, len; 401 402#define XNC_OUT (xencons_interface->out) 403 cons = xencons_interface->out_cons; 404 prod = xencons_interface->out_prod; 405 xen_rmb(); 406 while (prod != cons + sizeof(xencons_interface->out)) { 407 if (MASK_XENCONS_IDX(prod, XNC_OUT) < 408 MASK_XENCONS_IDX(cons, XNC_OUT)) { 409 len = MASK_XENCONS_IDX(cons, XNC_OUT) - 410 MASK_XENCONS_IDX(prod, XNC_OUT); 411 } else { 412 len = sizeof(XNC_OUT) - 413 MASK_XENCONS_IDX(prod, XNC_OUT); 414 } 415 len = q_to_b(cl, __UNVOLATILE( 416 &XNC_OUT[MASK_XENCONS_IDX(prod, XNC_OUT)]), len); 417 if (len == 0) 418 break; 419 prod = prod + len; 420 } 421 xen_wmb(); 422 xencons_interface->out_prod = prod; 423 xen_wmb(); 424 hypervisor_notify_via_evtchn(xen_start_info.console.domU.evtchn); 425#undef XNC_OUT 426 } 427 428 s = spltty(); 429 tp->t_state &= ~TS_BUSY; 430 if (ttypull(tp)) { 431 tp->t_state |= TS_TIMEOUT; 432 callout_schedule(&tp->t_rstrt_ch, 1); 433 } 434out: 435 splx(s); 436} 437 438static void 439xencons_stop(struct tty *tp, int flag) 440{ 441 442} 443 444/* Non-privileged console interrupt routine */ 445static int 446xencons_handler(void *arg) 447{ 448 struct xencons_softc *sc = arg; 449 XENCONS_RING_IDX cons, prod, len; 450 int s = spltty(); 451 452 if (sc->polling) { 453 splx(s); 454 return 1; 455 } 456 457 458#define XNC_IN (xencons_interface->in) 459 460 cons = xencons_interface->in_cons; 461 prod = xencons_interface->in_prod; 462 xen_rmb(); 463 while (cons != prod) { 464 if (MASK_XENCONS_IDX(cons, XNC_IN) < 465 MASK_XENCONS_IDX(prod, XNC_IN)) 466 len = MASK_XENCONS_IDX(prod, XNC_IN) - 467 MASK_XENCONS_IDX(cons, XNC_IN); 468 else 469 len = sizeof(XNC_IN) - MASK_XENCONS_IDX(cons, XNC_IN); 470 471 xencons_tty_input(sc, __UNVOLATILE( 472 &XNC_IN[MASK_XENCONS_IDX(cons, XNC_IN)]), len); 473 if (__predict_false(xencons_interface->in_cons != cons)) { 474 /* catch up with xenconscn_getc() */ 475 cons = xencons_interface->in_cons; 476 prod = xencons_interface->in_prod; 477 xen_rmb(); 478 } else { 479 cons += len; 480 xen_wmb(); 481 xencons_interface->in_cons = cons; 482 } 483 } 484 xen_wmb(); 485 hypervisor_notify_via_evtchn(xen_start_info.console.domU.evtchn); 486 splx(s); 487 return 1; 488#undef XNC_IN 489} 490 491static void 492xencons_tty_input(struct xencons_softc *sc, char* buf, int len) 493{ 494 struct tty *tp; 495 int i; 496 497 tp = sc->sc_tty; 498 if (tp == NULL) 499 return; 500 501 for (i = 0; i < len; i++) { 502 cn_check_magic(sc->sc_tty->t_dev, buf[i], xencons_cnm_state); 503 (*tp->t_linesw->l_rint)(buf[i], tp); 504 } 505} 506 507/* privileged receive callback */ 508static int 509xencons_intr(void *p) 510{ 511 static char rbuf[16]; 512 int len; 513 struct xencons_softc *sc = p; 514 515 if (sc == NULL) 516 /* Interrupt may happen during resume */ 517 return 1; 518 519 if (sc->polling) 520 return 1; 521 522 while ((len = 523 HYPERVISOR_console_io(CONSOLEIO_read, sizeof(rbuf), rbuf)) > 0) { 524 xencons_tty_input(sc, rbuf, len); 525 } 526 return 1; 527} 528 529void 530xenconscn_attach(void) 531{ 532 533 cn_tab = &xencons; 534 535 /* console ring mapped in locore.S */ 536 537 cn_init_magic(&xencons_cnm_state); 538 cn_set_magic("+++++"); 539 540 xencons_isconsole = 1; 541} 542 543static int 544xenconscn_getc(dev_t dev) 545{ 546 char c; 547 int s = spltty(); 548 XENCONS_RING_IDX cons, prod; 549 550 if (xencons_console_device && xencons_console_device->polling == 0) { 551 printf("xenconscn_getc() but not polling\n"); 552 splx(s); 553 return 0; 554 } 555 if (xendomain_is_dom0()) { 556 while (HYPERVISOR_console_io(CONSOLEIO_read, 1, &c) == 0) 557 ; 558 cn_check_magic(dev, c, xencons_cnm_state); 559 splx(s); 560 return c; 561 } 562 if (xencons_console_device == NULL) { 563 printf("xenconscn_getc(): not console\n"); 564 while (1) 565 ; /* loop here instead of in ddb */ 566 splx(s); 567 return 0; 568 } 569 570 if (xencons_console_device->polling == 0) { 571 printf("xenconscn_getc() but not polling\n"); 572 splx(s); 573 return 0; 574 } 575 576 cons = xencons_interface->in_cons; 577 prod = xencons_interface->in_prod; 578 while (cons == prod) { 579 HYPERVISOR_yield(); 580 prod = xencons_interface->in_prod; 581 } 582 xen_rmb(); 583 c = xencons_interface->in[MASK_XENCONS_IDX(xencons_interface->in_cons, 584 xencons_interface->in)]; 585 xen_wmb(); 586 xencons_interface->in_cons = cons + 1; 587 cn_check_magic(dev, c, xencons_cnm_state); 588 splx(s); 589 return c; 590} 591 592static void 593xenconscn_putc(dev_t dev, int c) 594{ 595 int s = spltty(); 596 XENCONS_RING_IDX cons, prod; 597 598 if (xendomain_is_dom0()) { 599 u_char buf[1]; 600 601 buf[0] = c; 602 (void)HYPERVISOR_console_io(CONSOLEIO_write, 1, buf); 603 } else { 604 XENPRINTK(("xenconscn_putc(%c)\n", c)); 605 606 cons = xencons_interface->out_cons; 607 prod = xencons_interface->out_prod; 608 xen_rmb(); 609 while (prod == cons + sizeof(xencons_interface->out)) { 610 cons = xencons_interface->out_cons; 611 prod = xencons_interface->out_prod; 612 xen_rmb(); 613 } 614 xencons_interface->out[MASK_XENCONS_IDX(xencons_interface->out_prod, 615 xencons_interface->out)] = c; 616 xen_wmb(); 617 xencons_interface->out_prod++; 618 xen_wmb(); 619 hypervisor_notify_via_evtchn(xen_start_info.console.domU.evtchn); 620 splx(s); 621 } 622} 623 624static void 625xenconscn_pollc(dev_t dev, int on) 626{ 627 if (xencons_console_device) 628 xencons_console_device->polling = on; 629} 630 631/* 632 * Set line parameters. 633 */ 634static int 635xencons_param(struct tty *tp, struct termios *t) 636{ 637 638 tp->t_ispeed = t->c_ispeed; 639 tp->t_ospeed = t->c_ospeed; 640 tp->t_cflag = t->c_cflag; 641 return (0); 642} 643