1 /* 2 * (MPSAFE) 3 * 4 * Copyright (C) 2003,2004 5 * Hidetoshi Shimokawa. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * 18 * This product includes software developed by Hidetoshi Shimokawa. 19 * 20 * 4. Neither the name of the author nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * $FreeBSD: src/sys/dev/dcons/dcons_os.c,v 1.4 2004/10/24 12:41:04 simokawa Exp $ 37 */ 38 39 #include <sys/param.h> 40 #include <sys/kernel.h> 41 #include <sys/module.h> 42 #include <sys/systm.h> 43 #include <sys/types.h> 44 #include <sys/conf.h> 45 #include <sys/cons.h> 46 #include <sys/consio.h> 47 #include <sys/tty.h> 48 #include <sys/ttydefaults.h> /* for TTYDEF_* */ 49 #include <sys/malloc.h> 50 #include <sys/proc.h> 51 #include <sys/priv.h> 52 #include <sys/ucred.h> 53 #include <sys/bus.h> 54 55 #include "dcons.h" 56 #include "dcons_os.h" 57 58 #include <ddb/ddb.h> 59 #include <sys/reboot.h> 60 61 #include <sys/sysctl.h> 62 63 #include <vm/vm.h> 64 #include <vm/vm_param.h> 65 #include <vm/pmap.h> 66 67 #include "opt_ddb.h" 68 #include "opt_comconsole.h" 69 #include "opt_dcons.h" 70 71 #ifndef DCONS_POLL_HZ 72 #define DCONS_POLL_HZ 100 73 #endif 74 75 #ifndef DCONS_BUF_SIZE 76 #define DCONS_BUF_SIZE (16*1024) 77 #endif 78 79 #ifndef DCONS_FORCE_CONSOLE 80 #define DCONS_FORCE_CONSOLE 0 /* Mostly for FreeBSD-4/DragonFly */ 81 #endif 82 83 #ifndef DCONS_FORCE_GDB 84 #define DCONS_FORCE_GDB 1 85 #endif 86 87 static d_open_t dcons_open; 88 static d_close_t dcons_close; 89 static d_ioctl_t dcons_ioctl; 90 91 static struct dev_ops dcons_ops = { 92 { "dcons", 0, D_TTY }, 93 .d_open = dcons_open, 94 .d_close = dcons_close, 95 .d_read = ttyread, 96 .d_write = ttywrite, 97 .d_ioctl = dcons_ioctl, 98 .d_kqfilter = ttykqfilter, 99 .d_revoke = ttyrevoke 100 }; 101 102 #ifndef KLD_MODULE 103 static char bssbuf[DCONS_BUF_SIZE]; /* buf in bss */ 104 #endif 105 106 /* global data */ 107 static struct dcons_global dg; 108 struct dcons_global *dcons_conf; 109 static int poll_hz = DCONS_POLL_HZ; 110 111 static struct dcons_softc sc[DCONS_NPORT]; 112 113 SYSCTL_NODE(_kern, OID_AUTO, dcons, CTLFLAG_RD, 0, "Dumb Console"); 114 SYSCTL_INT(_kern_dcons, OID_AUTO, poll_hz, CTLFLAG_RW, &poll_hz, 0, 115 "dcons polling rate"); 116 117 static int drv_init = 0; 118 static struct callout dcons_callout; 119 struct dcons_buf *dcons_buf; /* for local dconschat */ 120 121 #define DEV cdev_t 122 #define THREAD d_thread_t 123 124 static void dcons_tty_start(struct tty *); 125 static int dcons_tty_param(struct tty *, struct termios *); 126 static void dcons_timeout(void *); 127 static int dcons_drv_init(int); 128 129 static cn_probe_t dcons_cnprobe; 130 static cn_init_t dcons_cninit; 131 static cn_init_fini_t dcons_cninit_fini; 132 static cn_getc_t dcons_cngetc; 133 static cn_checkc_t dcons_cncheckc; 134 static cn_putc_t dcons_cnputc; 135 136 CONS_DRIVER(dcons, dcons_cnprobe, dcons_cninit, dcons_cninit_fini, 137 NULL, dcons_cngetc, dcons_cncheckc, dcons_cnputc, NULL, NULL); 138 139 #if defined(DDB) && defined(ALT_BREAK_TO_DEBUGGER) 140 static int 141 dcons_check_break(struct dcons_softc *dc, int c) 142 { 143 if (c < 0) 144 return (c); 145 146 switch (dc->brk_state) { 147 case STATE1: 148 if (c == KEY_TILDE) 149 dc->brk_state = STATE2; 150 else 151 dc->brk_state = STATE0; 152 break; 153 case STATE2: 154 dc->brk_state = STATE0; 155 if (c == KEY_CTRLB) { 156 #if DCONS_FORCE_GDB 157 if (dc->flags & DC_GDB) 158 boothowto |= RB_GDB; 159 #endif 160 breakpoint(); 161 } 162 } 163 if (c == KEY_CR) 164 dc->brk_state = STATE1; 165 return (c); 166 } 167 #else 168 #define dcons_check_break(dc, c) (c) 169 #endif 170 171 static int 172 dcons_os_checkc(struct dcons_softc *dc) 173 { 174 int c; 175 176 if (dg.dma_tag != NULL) 177 bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_POSTREAD); 178 179 c = dcons_check_break(dc, dcons_checkc(dc)); 180 181 if (dg.dma_tag != NULL) 182 bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_PREREAD); 183 184 return (c); 185 } 186 187 static int 188 dcons_os_getc(struct dcons_softc *dc) 189 { 190 int c; 191 192 while ((c = dcons_os_checkc(dc)) == -1); 193 194 return (c & 0xff); 195 } 196 197 static void 198 dcons_os_putc(struct dcons_softc *dc, int c) 199 { 200 if (dg.dma_tag != NULL) 201 bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_POSTWRITE); 202 203 dcons_putc(dc, c); 204 205 if (dg.dma_tag != NULL) 206 bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_PREWRITE); 207 } 208 static int 209 dcons_open(struct dev_open_args *ap) 210 { 211 cdev_t dev = ap->a_head.a_dev; 212 struct tty *tp; 213 int unit, error; 214 215 unit = minor(dev); 216 if (unit != 0) 217 return (ENXIO); 218 219 tp = ttymalloc(&dev->si_tty); 220 lwkt_gettoken(&tp->t_token); 221 tp->t_oproc = dcons_tty_start; 222 tp->t_param = dcons_tty_param; 223 tp->t_stop = nottystop; 224 tp->t_dev = dev; 225 226 error = 0; 227 228 if ((tp->t_state & TS_ISOPEN) == 0) { 229 tp->t_state |= TS_CARR_ON; 230 ttychars(tp); 231 tp->t_iflag = TTYDEF_IFLAG; 232 tp->t_oflag = TTYDEF_OFLAG; 233 tp->t_cflag = TTYDEF_CFLAG|CLOCAL; 234 tp->t_lflag = TTYDEF_LFLAG; 235 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 236 ttsetwater(tp); 237 } else if ((tp->t_state & TS_XCLUDE) && priv_check_cred(ap->a_cred, PRIV_ROOT, 0)) { 238 lwkt_reltoken(&tp->t_token); 239 240 return (EBUSY); 241 } 242 243 error = (*linesw[tp->t_line].l_open)(dev, tp); 244 lwkt_reltoken(&tp->t_token); 245 246 return (error); 247 } 248 249 static int 250 dcons_close(struct dev_close_args *ap) 251 { 252 cdev_t dev = ap->a_head.a_dev; 253 int unit; 254 struct tty *tp; 255 256 unit = minor(dev); 257 if (unit != 0) 258 return (ENXIO); 259 260 tp = dev->si_tty; 261 lwkt_gettoken(&tp->t_token); 262 if (tp->t_state & TS_ISOPEN) { 263 (*linesw[tp->t_line].l_close)(tp, ap->a_fflag); 264 ttyclose(tp); 265 } 266 lwkt_reltoken(&tp->t_token); 267 268 return (0); 269 } 270 271 static int 272 dcons_ioctl(struct dev_ioctl_args *ap) 273 { 274 cdev_t dev = ap->a_head.a_dev; 275 int unit; 276 struct tty *tp; 277 int error; 278 279 unit = minor(dev); 280 if (unit != 0) 281 return (ENXIO); 282 283 tp = dev->si_tty; 284 lwkt_gettoken(&tp->t_token); 285 error = (*linesw[tp->t_line].l_ioctl)(tp, ap->a_cmd, ap->a_data, ap->a_fflag, ap->a_cred); 286 if (error != ENOIOCTL) { 287 lwkt_reltoken(&tp->t_token); 288 return (error); 289 } 290 291 error = ttioctl(tp, ap->a_cmd, ap->a_data, ap->a_fflag); 292 if (error != ENOIOCTL) { 293 lwkt_reltoken(&tp->t_token); 294 return (error); 295 } 296 297 lwkt_reltoken(&tp->t_token); 298 return (ENOTTY); 299 } 300 301 static int 302 dcons_tty_param(struct tty *tp, struct termios *t) 303 { 304 lwkt_gettoken(&tp->t_token); 305 tp->t_ispeed = t->c_ispeed; 306 tp->t_ospeed = t->c_ospeed; 307 tp->t_cflag = t->c_cflag; 308 lwkt_reltoken(&tp->t_token); 309 return 0; 310 } 311 312 static void 313 dcons_tty_start(struct tty *tp) 314 { 315 struct dcons_softc *dc; 316 317 lwkt_gettoken(&tp->t_token); 318 dc = (struct dcons_softc *)tp->t_dev->si_drv1; 319 if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { 320 ttwwakeup(tp); 321 lwkt_reltoken(&tp->t_token); 322 return; 323 } 324 325 tp->t_state |= TS_BUSY; 326 while (tp->t_outq.c_cc != 0) 327 dcons_os_putc(dc, clist_getc(&tp->t_outq)); 328 tp->t_state &= ~TS_BUSY; 329 330 ttwwakeup(tp); 331 lwkt_reltoken(&tp->t_token); 332 } 333 334 static void 335 dcons_timeout(void *dummy __unused) 336 { 337 struct tty *tp; 338 struct dcons_softc *dc; 339 int i, c, polltime; 340 341 for (i = 0; i < DCONS_NPORT; i ++) { 342 dc = &sc[i]; 343 tp = ((DEV)dc->dev)->si_tty; 344 lwkt_gettoken(&tp->t_token); 345 while ((c = dcons_os_checkc(dc)) != -1) { 346 if (tp->t_state & TS_ISOPEN) 347 (*linesw[tp->t_line].l_rint)(c, tp); 348 } 349 lwkt_reltoken(&tp->t_token); 350 } 351 polltime = hz / poll_hz; 352 if (polltime < 1) 353 polltime = 1; 354 callout_reset(&dcons_callout, polltime, dcons_timeout, NULL); 355 } 356 357 static void 358 dcons_cnprobe(struct consdev *cp) 359 { 360 cp->cn_probegood = 1; 361 #if DCONS_FORCE_CONSOLE 362 cp->cn_pri = CN_REMOTE; 363 #else 364 cp->cn_pri = CN_NORMAL; 365 #endif 366 } 367 368 static void 369 dcons_cninit(struct consdev *cp) 370 { 371 dcons_drv_init(0); 372 #ifdef CONS_NODEV 373 cp->cn_arg 374 #else 375 cp->cn_private 376 #endif 377 = (void *)&sc[DCONS_CON]; /* share port0 with unit0 */ 378 } 379 380 static void 381 dcons_cninit_fini(struct consdev *cp) 382 { 383 cp->cn_dev = make_dev(&dcons_ops, DCONS_CON, 384 UID_ROOT, GID_WHEEL, 0600, "dcons"); 385 } 386 387 #ifdef CONS_NODEV 388 static int 389 dcons_cngetc(struct consdev *cp) 390 { 391 struct dcons_softc *dc = (struct dcons_softc *)cp->cn_arg; 392 return (dcons_os_getc(dc)); 393 } 394 static int 395 dcons_cncheckc(struct consdev *cp) 396 { 397 struct dcons_softc *dc = (struct dcons_softc *)cp->cn_arg; 398 return (dcons_os_checkc(dc)); 399 } 400 static void 401 dcons_cnputc(struct consdev *cp, int c) 402 { 403 struct dcons_softc *dc = (struct dcons_softc *)cp->cn_arg; 404 dcons_os_putc(dc, c); 405 } 406 #else 407 static int 408 dcons_cngetc(void *private) 409 { 410 struct dcons_softc *dc = (struct dcons_softc *)private; 411 return (dcons_os_getc(dc)); 412 } 413 static int 414 dcons_cncheckc(void *private) 415 { 416 struct dcons_softc *dc = (struct dcons_softc *)private; 417 return (dcons_os_checkc(dc)); 418 } 419 static void 420 dcons_cnputc(void *private, int c) 421 { 422 struct dcons_softc *dc = (struct dcons_softc *)private; 423 dcons_os_putc(dc, c); 424 } 425 #endif 426 427 static int 428 dcons_drv_init(int stage) 429 { 430 #ifdef __i386__ 431 quad_t addr, size; 432 #endif 433 434 if (drv_init) 435 return(drv_init); 436 437 drv_init = -1; 438 439 bzero(&dg, sizeof(dg)); 440 dcons_conf = &dg; 441 dg.cdev = &dcons_consdev; 442 dg.buf = NULL; 443 dg.size = DCONS_BUF_SIZE; 444 445 #ifdef __i386__ 446 if (kgetenv_quad("dcons.addr", &addr) > 0 && 447 kgetenv_quad("dcons.size", &size) > 0) { 448 vm_paddr_t pa; 449 /* 450 * Allow read/write access to dcons buffer. 451 */ 452 for (pa = trunc_page(addr); pa < addr + size; pa += PAGE_SIZE) 453 pmap_kmodify_rw((vm_offset_t)(KERNBASE + pa)); 454 cpu_invltlb(); 455 /* XXX P to V */ 456 dg.buf = (struct dcons_buf *)(vm_offset_t)(KERNBASE + addr); 457 dg.size = size; 458 if (dcons_load_buffer(dg.buf, dg.size, sc) < 0) 459 dg.buf = NULL; 460 } 461 #endif 462 if (dg.buf != NULL) 463 goto ok; 464 465 #ifndef KLD_MODULE 466 if (stage == 0) { /* XXX or cold */ 467 /* 468 * DCONS_FORCE_CONSOLE == 1 and statically linked. 469 * called from cninit(). can't use contigmalloc yet . 470 */ 471 dg.buf = (struct dcons_buf *) bssbuf; 472 dcons_init(dg.buf, dg.size, sc); 473 } else 474 #endif 475 { 476 /* 477 * DCONS_FORCE_CONSOLE == 0 or kernel module case. 478 * if the module is loaded after boot, 479 * bssbuf could be non-continuous. 480 */ 481 dg.buf = (struct dcons_buf *) contigmalloc(dg.size, 482 M_DEVBUF, 0, 0x10000, 0xffffffff, PAGE_SIZE, 0ul); 483 dcons_init(dg.buf, dg.size, sc); 484 } 485 486 ok: 487 dcons_buf = dg.buf; 488 489 #if defined(DDB) && DCONS_FORCE_GDB 490 if (gdb_tab == NULL) { 491 gdb_tab = &dcons_consdev; 492 dcons_consdev.cn_gdbprivate = &sc[DCONS_GDB]; 493 } 494 #endif 495 drv_init = 1; 496 497 return 0; 498 } 499 500 static int 501 dcons_attach_port(int port, char *name, int flags) 502 { 503 struct dcons_softc *dc; 504 struct tty *tp; 505 DEV dev; 506 507 dc = &sc[port]; 508 dc->flags = flags; 509 dev = make_dev(&dcons_ops, port, UID_ROOT, GID_WHEEL, 0600, 510 "%s", name); 511 dc->dev = (void *)dev; 512 dev->si_drv1 = (void *)dc; 513 514 tp = ttymalloc(&dev->si_tty); 515 tp->t_oproc = dcons_tty_start; 516 tp->t_param = dcons_tty_param; 517 tp->t_stop = nottystop; 518 tp->t_dev = dc->dev; 519 520 return(0); 521 } 522 523 static int 524 dcons_attach(void) 525 { 526 int polltime; 527 528 dcons_attach_port(DCONS_CON, "dcons", 0); 529 dcons_attach_port(DCONS_GDB, "dgdb", DC_GDB); 530 callout_init_mp(&dcons_callout); 531 polltime = hz / poll_hz; 532 if (polltime < 1) 533 polltime = 1; 534 callout_reset(&dcons_callout, polltime, dcons_timeout, NULL); 535 return(0); 536 } 537 538 static int 539 dcons_detach(int port) 540 { 541 struct tty *tp; 542 struct dcons_softc *dc; 543 544 dc = &sc[port]; 545 546 tp = ((DEV)dc->dev)->si_tty; 547 lwkt_gettoken(&tp->t_token); 548 549 if (tp->t_state & TS_ISOPEN) { 550 kprintf("dcons: still opened\n"); 551 (*linesw[tp->t_line].l_close)(tp, 0); 552 ttyclose(tp); 553 } 554 /* XXX 555 * must wait until all device are closed. 556 */ 557 #ifdef __DragonFly__ 558 tsleep((void *)dc, 0, "dcodtc", hz/4); 559 #else 560 tsleep((void *)dc, PWAIT, "dcodtc", hz/4); 561 #endif 562 lwkt_reltoken(&tp->t_token); 563 destroy_dev(dc->dev); 564 565 return(0); 566 } 567 568 569 /* cnXXX works only for FreeBSD-5 */ 570 static int 571 dcons_modevent(module_t mode, int type, void *data) 572 { 573 int err = 0, ret; 574 575 switch (type) { 576 case MOD_LOAD: 577 ret = dcons_drv_init(1); 578 dcons_attach(); 579 #if 0 /* XXX __FreeBSD_version >= 500000 */ 580 if (ret == 0) { 581 dcons_cnprobe(&dcons_consdev); 582 dcons_cninit(&dcons_consdev); 583 cnadd(&dcons_consdev); 584 } 585 #endif 586 break; 587 case MOD_UNLOAD: 588 kprintf("dcons: unload\n"); 589 callout_stop(&dcons_callout); 590 #if defined(DDB) && DCONS_FORCE_GDB 591 #ifdef CONS_NODEV 592 gdb_arg = NULL; 593 #else 594 if (gdb_tab == &dcons_consdev) 595 gdb_tab = NULL; 596 #endif 597 #endif 598 #if 0 /* XXX __FreeBSD_version >= 500000 */ 599 cnremove(&dcons_consdev); 600 #endif 601 dcons_detach(DCONS_CON); 602 dcons_detach(DCONS_GDB); 603 dg.buf->magic = 0; 604 605 contigfree(dg.buf, DCONS_BUF_SIZE, M_DEVBUF); 606 607 break; 608 case MOD_SHUTDOWN: 609 dg.buf->magic = 0; 610 break; 611 default: 612 err = EOPNOTSUPP; 613 break; 614 } 615 return(err); 616 } 617 618 DEV_MODULE(dcons, dcons_modevent, NULL); 619 MODULE_VERSION(dcons, DCONS_VERSION); 620