1 /* 2 * Copyright (c) 1998 Michael Smith (msmith@freebsd.org) 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 /* 27 * This code is shared on BIOS and UEFI systems on x86 because 28 * we can access io ports on both platforms and the UEFI Serial IO protocol 29 * is not giving us reliable port order and we see issues with input. 30 */ 31 #include <sys/cdefs.h> 32 33 #include <stand.h> 34 #include <bootstrap.h> 35 #include <stdbool.h> 36 #include <machine/cpufunc.h> 37 #include <dev/ic/ns16550.h> 38 #include <dev/pci/pcireg.h> 39 #include "libi386.h" 40 41 #define COMC_TXWAIT 0x40000 /* transmit timeout */ 42 #define COMC_BPS(x) (115200 / (x)) /* speed to DLAB divisor */ 43 #define COMC_DIV2BPS(x) (115200 / (x)) /* DLAB divisor to speed */ 44 45 #ifndef COMSPEED 46 #define COMSPEED 9600 47 #endif 48 49 #define COM1_IOADDR 0x3f8 50 #define COM2_IOADDR 0x2f8 51 #define COM3_IOADDR 0x3e8 52 #define COM4_IOADDR 0x2e8 53 54 #define STOP1 0x00 55 #define STOP2 0x04 56 57 #define PARODD 0x00 58 #define PAREN 0x08 59 #define PAREVN 0x10 60 #define PARMARK 0x20 61 62 #define BITS5 0x00 /* 5 bits per char */ 63 #define BITS6 0x01 /* 6 bits per char */ 64 #define BITS7 0x02 /* 7 bits per char */ 65 #define BITS8 0x03 /* 8 bits per char */ 66 67 struct serial { 68 int speed; /* baud rate */ 69 uint8_t lcr; /* line control */ 70 uint8_t ignore_cd; /* boolean */ 71 uint8_t rtsdtr_off; /* boolean */ 72 int ioaddr; 73 uint32_t locator; 74 }; 75 76 static void comc_probe(struct console *); 77 static int comc_init(struct console *, int); 78 static void comc_putchar(struct console *, int); 79 static int comc_getchar(struct console *); 80 static int comc_getspeed(struct serial *); 81 static int comc_ischar(struct console *); 82 static int comc_ioctl(struct console *, int, void *); 83 static uint32_t comc_parse_pcidev(const char *); 84 static int comc_pcidev_set(struct env_var *, int, const void *); 85 static int comc_pcidev_handle(struct console *, uint32_t); 86 static bool comc_setup(struct console *); 87 static char *comc_asprint_mode(struct serial *); 88 static int comc_parse_mode(struct serial *, const char *); 89 static int comc_mode_set(struct env_var *, int, const void *); 90 static int comc_cd_set(struct env_var *, int, const void *); 91 static int comc_rtsdtr_set(struct env_var *, int, const void *); 92 static void comc_devinfo(struct console *); 93 94 struct console ttya = { 95 .c_name = "ttya", 96 .c_desc = "serial port a", 97 .c_flags = 0, 98 .c_probe = comc_probe, 99 .c_init = comc_init, 100 .c_out = comc_putchar, 101 .c_in = comc_getchar, 102 .c_ready = comc_ischar, 103 .c_ioctl = comc_ioctl, 104 .c_devinfo = comc_devinfo, 105 .c_private = NULL 106 }; 107 108 struct console ttyb = { 109 .c_name = "ttyb", 110 .c_desc = "serial port b", 111 .c_flags = 0, 112 .c_probe = comc_probe, 113 .c_init = comc_init, 114 .c_out = comc_putchar, 115 .c_in = comc_getchar, 116 .c_ready = comc_ischar, 117 .c_ioctl = comc_ioctl, 118 .c_devinfo = comc_devinfo, 119 .c_private = NULL 120 }; 121 122 struct console ttyc = { 123 .c_name = "ttyc", 124 .c_desc = "serial port c", 125 .c_flags = 0, 126 .c_probe = comc_probe, 127 .c_init = comc_init, 128 .c_out = comc_putchar, 129 .c_in = comc_getchar, 130 .c_ready = comc_ischar, 131 .c_ioctl = comc_ioctl, 132 .c_devinfo = comc_devinfo, 133 .c_private = NULL 134 }; 135 136 struct console ttyd = { 137 .c_name = "ttyd", 138 .c_desc = "serial port d", 139 .c_flags = 0, 140 .c_probe = comc_probe, 141 .c_init = comc_init, 142 .c_out = comc_putchar, 143 .c_in = comc_getchar, 144 .c_ready = comc_ischar, 145 .c_ioctl = comc_ioctl, 146 .c_devinfo = comc_devinfo, 147 .c_private = NULL 148 }; 149 150 static void 151 comc_devinfo(struct console *cp) 152 { 153 struct serial *port = cp->c_private; 154 155 if (cp->c_flags != 0) 156 printf("\tport %#x", port->ioaddr); 157 else 158 printf("\tdevice is not present"); 159 } 160 161 static void 162 comc_probe(struct console *cp) 163 { 164 struct serial *port; 165 char name[20]; 166 char value[20]; 167 char *cons, *env; 168 169 if (cp->c_private != NULL) 170 return; 171 172 cp->c_private = malloc(sizeof (struct serial)); 173 port = cp->c_private; 174 port->speed = COMSPEED; 175 176 if (strcmp(cp->c_name, "ttya") == 0) 177 port->ioaddr = COM1_IOADDR; 178 else if (strcmp(cp->c_name, "ttyb") == 0) 179 port->ioaddr = COM2_IOADDR; 180 else if (strcmp(cp->c_name, "ttyc") == 0) 181 port->ioaddr = COM3_IOADDR; 182 else if (strcmp(cp->c_name, "ttyd") == 0) 183 port->ioaddr = COM4_IOADDR; 184 185 port->lcr = BITS8; /* 8,n,1 */ 186 port->ignore_cd = 1; /* ignore cd */ 187 port->rtsdtr_off = 0; /* rts-dtr is on */ 188 189 /* 190 * Assume that the speed was set by an earlier boot loader if 191 * comconsole is already the preferred console. 192 */ 193 cons = getenv("console"); 194 if ((cons != NULL && strcmp(cons, cp->c_name) == 0) || 195 getenv("boot_multicons") != NULL) { 196 port->speed = comc_getspeed(port); 197 } 198 199 snprintf(name, sizeof (name), "%s-mode", cp->c_name); 200 env = getenv(name); 201 202 if (env != NULL) { 203 (void) comc_parse_mode(port, env); 204 } 205 env = comc_asprint_mode(port); 206 207 if (env != NULL) { 208 unsetenv(name); 209 env_setenv(name, EV_VOLATILE, env, comc_mode_set, env_nounset); 210 free(env); 211 } 212 213 snprintf(name, sizeof (name), "%s-ignore-cd", cp->c_name); 214 env = getenv(name); 215 if (env != NULL) { 216 if (strcmp(env, "true") == 0) 217 port->ignore_cd = 1; 218 else if (strcmp(env, "false") == 0) 219 port->ignore_cd = 0; 220 } 221 222 snprintf(value, sizeof (value), "%s", 223 port->ignore_cd? "true" : "false"); 224 unsetenv(name); 225 env_setenv(name, EV_VOLATILE, value, comc_cd_set, env_nounset); 226 227 snprintf(name, sizeof (name), "%s-rts-dtr-off", cp->c_name); 228 env = getenv(name); 229 if (env != NULL) { 230 if (strcmp(env, "true") == 0) 231 port->rtsdtr_off = 1; 232 else if (strcmp(env, "false") == 0) 233 port->rtsdtr_off = 0; 234 } 235 236 snprintf(value, sizeof (value), "%s", 237 port->rtsdtr_off? "true" : "false"); 238 unsetenv(name); 239 env_setenv(name, EV_VOLATILE, value, comc_rtsdtr_set, env_nounset); 240 241 snprintf(name, sizeof (name), "%s-pcidev", cp->c_name); 242 env = getenv(name); 243 if (env != NULL) { 244 port->locator = comc_parse_pcidev(env); 245 if (port->locator != 0) 246 comc_pcidev_handle(cp, port->locator); 247 } 248 249 unsetenv(name); 250 env_setenv(name, EV_VOLATILE, env, comc_pcidev_set, env_nounset); 251 252 cp->c_flags = 0; 253 if (comc_setup(cp)) 254 cp->c_flags = C_PRESENTIN | C_PRESENTOUT; 255 } 256 257 static int 258 comc_init(struct console *cp, int arg __attribute((unused))) 259 { 260 261 if (comc_setup(cp)) 262 return (CMD_OK); 263 264 cp->c_flags = 0; 265 return (CMD_ERROR); 266 } 267 268 static void 269 comc_putchar(struct console *cp, int c) 270 { 271 int wait; 272 struct serial *sp = cp->c_private; 273 274 for (wait = COMC_TXWAIT; wait > 0; wait--) 275 if (inb(sp->ioaddr + com_lsr) & LSR_TXRDY) { 276 outb(sp->ioaddr + com_data, (uchar_t)c); 277 break; 278 } 279 } 280 281 static int 282 comc_getchar(struct console *cp) 283 { 284 struct serial *sp = cp->c_private; 285 return (comc_ischar(cp) ? inb(sp->ioaddr + com_data) : -1); 286 } 287 288 static int 289 comc_ischar(struct console *cp) 290 { 291 struct serial *sp = cp->c_private; 292 return (inb(sp->ioaddr + com_lsr) & LSR_RXRDY); 293 } 294 295 static int 296 comc_ioctl(struct console *cp __unused, int cmd __unused, void *data __unused) 297 { 298 return (ENOTTY); 299 } 300 301 static char * 302 comc_asprint_mode(struct serial *sp) 303 { 304 char par, *buf; 305 306 if (sp == NULL) 307 return (NULL); 308 309 if ((sp->lcr & (PAREN|PAREVN)) == (PAREN|PAREVN)) 310 par = 'e'; 311 else if ((sp->lcr & PAREN) == PAREN) 312 par = 'o'; 313 else 314 par = 'n'; 315 316 asprintf(&buf, "%d,%d,%c,%d,-", sp->speed, 317 (sp->lcr & BITS8) == BITS8? 8:7, 318 par, (sp->lcr & STOP2) == STOP2? 2:1); 319 return (buf); 320 } 321 322 static int 323 comc_parse_mode(struct serial *sp, const char *value) 324 { 325 unsigned long n; 326 int speed; 327 int lcr; 328 char *ep; 329 330 if (value == NULL || *value == '\0') 331 return (CMD_ERROR); 332 333 errno = 0; 334 n = strtoul(value, &ep, 10); 335 if (errno != 0 || *ep != ',') 336 return (CMD_ERROR); 337 speed = n; 338 339 ep++; 340 errno = 0; 341 n = strtoul(ep, &ep, 10); 342 if (errno != 0 || *ep != ',') 343 return (CMD_ERROR); 344 345 switch (n) { 346 case 7: lcr = BITS7; 347 break; 348 case 8: lcr = BITS8; 349 break; 350 default: 351 return (CMD_ERROR); 352 } 353 354 ep++; 355 switch (*ep++) { 356 case 'n': 357 break; 358 case 'e': lcr |= PAREN|PAREVN; 359 break; 360 case 'o': lcr |= PAREN|PARODD; 361 break; 362 default: 363 return (CMD_ERROR); 364 } 365 366 if (*ep == ',') 367 ep++; 368 else 369 return (CMD_ERROR); 370 371 switch (*ep++) { 372 case '1': 373 break; 374 case '2': lcr |= STOP2; 375 break; 376 default: 377 return (CMD_ERROR); 378 } 379 380 /* handshake is ignored, but we check syntax anyhow */ 381 if (*ep == ',') 382 ep++; 383 else 384 return (CMD_ERROR); 385 386 switch (*ep++) { 387 case '-': 388 case 'h': 389 case 's': 390 break; 391 default: 392 return (CMD_ERROR); 393 } 394 395 if (*ep != '\0') 396 return (CMD_ERROR); 397 398 sp->speed = speed; 399 sp->lcr = lcr; 400 return (CMD_OK); 401 } 402 403 static struct console * 404 get_console(char *name) 405 { 406 struct console *cp = NULL; 407 408 switch (name[3]) { 409 case 'a': cp = &ttya; 410 break; 411 case 'b': cp = &ttyb; 412 break; 413 case 'c': cp = &ttyc; 414 break; 415 case 'd': cp = &ttyd; 416 break; 417 } 418 return (cp); 419 } 420 421 static int 422 comc_mode_set(struct env_var *ev, int flags, const void *value) 423 { 424 struct console *cp; 425 426 if (value == NULL) 427 return (CMD_ERROR); 428 429 if ((cp = get_console(ev->ev_name)) == NULL) 430 return (CMD_ERROR); 431 432 if (comc_parse_mode(cp->c_private, value) == CMD_ERROR) 433 return (CMD_ERROR); 434 435 (void) comc_setup(cp); 436 437 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 438 439 return (CMD_OK); 440 } 441 442 static int 443 comc_cd_set(struct env_var *ev, int flags, const void *value) 444 { 445 struct console *cp; 446 struct serial *sp; 447 448 if (value == NULL) 449 return (CMD_ERROR); 450 451 if ((cp = get_console(ev->ev_name)) == NULL) 452 return (CMD_ERROR); 453 454 sp = cp->c_private; 455 if (strcmp(value, "true") == 0) 456 sp->ignore_cd = 1; 457 else if (strcmp(value, "false") == 0) 458 sp->ignore_cd = 0; 459 else 460 return (CMD_ERROR); 461 462 (void) comc_setup(cp); 463 464 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 465 466 return (CMD_OK); 467 } 468 469 static int 470 comc_rtsdtr_set(struct env_var *ev, int flags, const void *value) 471 { 472 struct console *cp; 473 struct serial *sp; 474 475 if (value == NULL) 476 return (CMD_ERROR); 477 478 if ((cp = get_console(ev->ev_name)) == NULL) 479 return (CMD_ERROR); 480 481 sp = cp->c_private; 482 if (strcmp(value, "true") == 0) 483 sp->rtsdtr_off = 1; 484 else if (strcmp(value, "false") == 0) 485 sp->rtsdtr_off = 0; 486 else 487 return (CMD_ERROR); 488 489 (void) comc_setup(cp); 490 491 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 492 493 return (CMD_OK); 494 } 495 496 /* 497 * Input: bus:dev:func[:bar]. If bar is not specified, it is 0x10. 498 * Output: bar[24:16] bus[15:8] dev[7:3] func[2:0] 499 */ 500 static uint32_t 501 comc_parse_pcidev(const char *string) 502 { 503 #ifdef EFI 504 (void) string; 505 return (0); 506 #else 507 char *p, *p1; 508 uint8_t bus, dev, func, bar; 509 uint32_t locator; 510 int pres; 511 512 errno = 0; 513 pres = strtoul(string, &p, 10); 514 if (errno != 0 || p == string || *p != ':' || pres < 0) 515 return (0); 516 bus = pres; 517 p1 = ++p; 518 519 pres = strtoul(p1, &p, 10); 520 if (errno != 0 || p == string || *p != ':' || pres < 0) 521 return (0); 522 dev = pres; 523 p1 = ++p; 524 525 pres = strtoul(p1, &p, 10); 526 if (errno != 0 || p == string || (*p != ':' && *p != '\0') || pres < 0) 527 return (0); 528 func = pres; 529 530 if (*p == ':') { 531 p1 = ++p; 532 pres = strtoul(p1, &p, 10); 533 if (errno != 0 || p == string || *p != '\0' || pres <= 0) 534 return (0); 535 bar = pres; 536 } else 537 bar = 0x10; 538 539 locator = (bar << 16) | biospci_locator(bus, dev, func); 540 return (locator); 541 #endif 542 } 543 544 static int 545 comc_pcidev_handle(struct console *cp, uint32_t locator) 546 { 547 #ifdef EFI 548 (void) cp; 549 (void) locator; 550 return (CMD_ERROR); 551 #else 552 struct serial *sp = cp->c_private; 553 uint32_t port; 554 555 if (biospci_read_config(locator & 0xffff, 556 (locator & 0xff0000) >> 16, 2, &port) == -1) { 557 printf("Cannot read bar at 0x%x\n", locator); 558 return (CMD_ERROR); 559 } 560 if (!PCI_BAR_IO(port)) { 561 printf("Memory bar at 0x%x\n", locator); 562 return (CMD_ERROR); 563 } 564 port &= PCIM_BAR_IO_BASE; 565 566 (void) comc_setup(cp); 567 568 sp->locator = locator; 569 570 return (CMD_OK); 571 #endif 572 } 573 574 static int 575 comc_pcidev_set(struct env_var *ev, int flags, const void *value) 576 { 577 struct console *cp; 578 struct serial *sp; 579 uint32_t locator; 580 int error; 581 582 if ((cp = get_console(ev->ev_name)) == NULL) 583 return (CMD_ERROR); 584 sp = cp->c_private; 585 586 if (value == NULL || (locator = comc_parse_pcidev(value)) <= 0) { 587 printf("Invalid pcidev\n"); 588 return (CMD_ERROR); 589 } 590 if ((cp->c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) != 0 && 591 sp->locator != locator) { 592 error = comc_pcidev_handle(cp, locator); 593 if (error != CMD_OK) 594 return (error); 595 } 596 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 597 return (CMD_OK); 598 } 599 600 /* 601 * In case of error, we also reset ACTIVE flags, so the console 602 * framefork will try alternate consoles. 603 */ 604 static bool 605 comc_setup(struct console *cp) 606 { 607 struct serial *sp = cp->c_private; 608 static int TRY_COUNT = 1000000; 609 int tries; 610 611 #define COMC_TEST 0xbb 612 /* 613 * Write byte to scratch register and read it out. 614 */ 615 outb(sp->ioaddr + com_scr, COMC_TEST); 616 if (inb(sp->ioaddr + com_scr) != COMC_TEST) 617 return (false); 618 619 outb(sp->ioaddr + com_cfcr, CFCR_DLAB | sp->lcr); 620 outb(sp->ioaddr + com_dlbl, COMC_BPS(sp->speed) & 0xff); 621 outb(sp->ioaddr + com_dlbh, COMC_BPS(sp->speed) >> 8); 622 outb(sp->ioaddr + com_cfcr, sp->lcr); 623 outb(sp->ioaddr + com_mcr, 624 sp->rtsdtr_off? ~(MCR_RTS | MCR_DTR) : MCR_RTS | MCR_DTR); 625 626 tries = 0; 627 do { 628 inb(sp->ioaddr + com_data); 629 } while (inb(sp->ioaddr + com_lsr) & LSR_RXRDY && ++tries < TRY_COUNT); 630 631 if (tries == TRY_COUNT) 632 return (false); 633 /* Mark this port usable. */ 634 cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT); 635 return (true); 636 } 637 638 static int 639 comc_getspeed(struct serial *sp) 640 { 641 uint_t divisor; 642 uchar_t dlbh; 643 uchar_t dlbl; 644 uchar_t cfcr; 645 646 cfcr = inb(sp->ioaddr + com_cfcr); 647 outb(sp->ioaddr + com_cfcr, CFCR_DLAB | cfcr); 648 649 dlbl = inb(sp->ioaddr + com_dlbl); 650 dlbh = inb(sp->ioaddr + com_dlbh); 651 652 outb(sp->ioaddr + com_cfcr, cfcr); 653 654 divisor = dlbh << 8 | dlbl; 655 656 /* XXX there should be more sanity checking. */ 657 if (divisor == 0) 658 return (COMSPEED); 659 return (COMC_DIV2BPS(divisor)); 660 } 661