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