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 COM_NPORTS 4 50 #define COM1_IOADDR 0x3f8 51 #define COM2_IOADDR 0x2f8 52 #define COM3_IOADDR 0x3e8 53 #define COM4_IOADDR 0x2e8 54 55 #define STOP1 0x00 56 #define STOP2 0x04 57 58 #define PARODD 0x00 59 #define PAREN 0x08 60 #define PAREVN 0x10 61 #define PARMARK 0x20 62 63 #define BITS5 0x00 /* 5 bits per char */ 64 #define BITS6 0x01 /* 6 bits per char */ 65 #define BITS7 0x02 /* 7 bits per char */ 66 #define BITS8 0x03 /* 8 bits per char */ 67 68 struct serial { 69 int speed; /* baud rate */ 70 uint8_t lcr; /* line control */ 71 uint8_t ignore_cd; /* boolean */ 72 uint8_t rtsdtr_off; /* boolean */ 73 int ioaddr; 74 uint32_t locator; 75 }; 76 77 static void comc_probe(struct console *); 78 static int comc_init(struct console *, int); 79 static void comc_putchar(struct console *, int); 80 static int comc_getchar(struct console *); 81 int comc_getspeed(int); 82 static int comc_ischar(struct console *); 83 static int comc_ioctl(struct console *, int, void *); 84 static uint32_t comc_parse_pcidev(const char *); 85 static int comc_pcidev_set(struct env_var *, int, const void *); 86 static int comc_pcidev_handle(struct console *, uint32_t); 87 static bool comc_setup(struct console *); 88 static char *comc_asprint_mode(struct serial *); 89 static int comc_parse_mode(struct serial *, const char *); 90 static int comc_mode_set(struct env_var *, int, const void *); 91 static int comc_cd_set(struct env_var *, int, const void *); 92 static int comc_rtsdtr_set(struct env_var *, int, const void *); 93 static void comc_devinfo(struct console *); 94 95 static void 96 comc_devinfo(struct console *cp) 97 { 98 struct serial *port = cp->c_private; 99 100 printf("\tport %#x", port->ioaddr); 101 } 102 103 static bool 104 comc_port_is_present(int ioaddr) 105 { 106 /* 107 * Write byte to scratch register and read it out. 108 */ 109 #define COMC_TEST 0xbb 110 outb(ioaddr + com_scr, COMC_TEST); 111 return (inb(ioaddr + com_scr) == COMC_TEST); 112 } 113 114 /* 115 * Set up list of possible serial consoles. 116 * This function is run very early, so we do not expect to 117 * run out of memory, and on error, we can not print output. 118 */ 119 void 120 comc_ini(void) 121 { 122 uint_t n = 0, c; 123 bool ports[COM_NPORTS]; 124 struct console **tmp; 125 struct console *tty; 126 struct serial *port; 127 128 /* 129 * Test the presence of 4 serial devices com1-com4 130 */ 131 ports[0] = comc_port_is_present(COM1_IOADDR); 132 ports[1] = comc_port_is_present(COM2_IOADDR); 133 ports[2] = comc_port_is_present(COM3_IOADDR); 134 ports[3] = comc_port_is_present(COM4_IOADDR); 135 136 for (uint_t i = 0; i < COM_NPORTS; i++) 137 if (ports[i]) 138 n++; 139 140 if (n == 0) /* there are no serial ports */ 141 return; 142 143 c = cons_array_size(); 144 if (c == 0) 145 n++; /* For NULL pointer */ 146 147 tmp = realloc(consoles, (c + n) * sizeof (*consoles)); 148 if (tmp == NULL) 149 return; 150 consoles = tmp; 151 if (c > 0) 152 c--; 153 154 for (uint_t i = 0; i < COM_NPORTS; i++) { 155 if (!ports[i]) 156 continue; 157 tty = malloc(sizeof (*tty)); 158 if (tty == NULL) { 159 /* Out of memory?! can not continue */ 160 consoles[c] = tty; 161 return; 162 } 163 if (asprintf(&tty->c_name, "tty%c", 'a' + i) < 0) { 164 free(tty); 165 consoles[c] = NULL; 166 return; 167 } 168 if (asprintf(&tty->c_desc, "serial port %c", 'a' + i) < 0) { 169 free(tty->c_name); 170 free(tty); 171 consoles[c] = NULL; 172 return; 173 } 174 tty->c_flags = 0; 175 tty->c_probe = comc_probe; 176 tty->c_init = comc_init; 177 tty->c_out = comc_putchar; 178 tty->c_in = comc_getchar; 179 tty->c_ready = comc_ischar; 180 tty->c_ioctl = comc_ioctl; 181 tty->c_devinfo = comc_devinfo; 182 port = malloc(sizeof (*port)); 183 if (port == NULL) { 184 free(tty->c_name); 185 free(tty->c_desc); 186 free(tty); 187 consoles[c] = NULL; 188 return; 189 } 190 port->speed = 0; /* Leave this for comc_probe */ 191 switch (i) { 192 case 0: 193 port->ioaddr = COM1_IOADDR; 194 break; 195 case 1: 196 port->ioaddr = COM2_IOADDR; 197 break; 198 case 2: 199 port->ioaddr = COM3_IOADDR; 200 break; 201 case 3: 202 port->ioaddr = COM4_IOADDR; 203 break; 204 } 205 port->speed = comc_getspeed(port->ioaddr); 206 port->lcr = BITS8; /* 8,n,1 */ 207 port->ignore_cd = 1; /* ignore cd */ 208 port->rtsdtr_off = 0; /* rts-dtr is on */ 209 210 tty->c_private = port; 211 consoles[c++] = tty; 212 213 /* Reset terminal to initial normal settings with ESC [ 0 m */ 214 comc_putchar(tty, 0x1b); 215 comc_putchar(tty, '['); 216 comc_putchar(tty, '0'); 217 comc_putchar(tty, 'm'); 218 /* drain input from random data */ 219 while (comc_getchar(tty) != -1) 220 ; 221 } 222 consoles[c] = NULL; 223 } 224 225 static void 226 comc_probe(struct console *cp) 227 { 228 struct serial *port; 229 char name[20]; 230 char value[20]; 231 char *env; 232 233 port = cp->c_private; 234 if (port->speed != 0) 235 return; 236 237 port->speed = COMSPEED; 238 239 /* 240 * Assume that the speed was set by an earlier boot loader if 241 * comconsole is already the preferred console. 242 */ 243 snprintf(name, sizeof (name), "%s-mode", cp->c_name); 244 env = getenv(name); 245 if (env != NULL) { 246 port->speed = comc_getspeed(port->ioaddr); 247 } 248 env = comc_asprint_mode(port); 249 250 if (env != NULL) { 251 unsetenv(name); 252 env_setenv(name, EV_VOLATILE, env, comc_mode_set, env_nounset); 253 free(env); 254 } 255 256 snprintf(name, sizeof (name), "%s-ignore-cd", cp->c_name); 257 env = getenv(name); 258 if (env != NULL) { 259 if (strcmp(env, "true") == 0) 260 port->ignore_cd = 1; 261 else if (strcmp(env, "false") == 0) 262 port->ignore_cd = 0; 263 } 264 265 snprintf(value, sizeof (value), "%s", 266 port->ignore_cd? "true" : "false"); 267 unsetenv(name); 268 env_setenv(name, EV_VOLATILE, value, comc_cd_set, env_nounset); 269 270 snprintf(name, sizeof (name), "%s-rts-dtr-off", cp->c_name); 271 env = getenv(name); 272 if (env != NULL) { 273 if (strcmp(env, "true") == 0) 274 port->rtsdtr_off = 1; 275 else if (strcmp(env, "false") == 0) 276 port->rtsdtr_off = 0; 277 } 278 279 snprintf(value, sizeof (value), "%s", 280 port->rtsdtr_off? "true" : "false"); 281 unsetenv(name); 282 env_setenv(name, EV_VOLATILE, value, comc_rtsdtr_set, env_nounset); 283 284 snprintf(name, sizeof (name), "%s-pcidev", cp->c_name); 285 env = getenv(name); 286 if (env != NULL) { 287 port->locator = comc_parse_pcidev(env); 288 if (port->locator != 0) 289 comc_pcidev_handle(cp, port->locator); 290 } 291 292 unsetenv(name); 293 env_setenv(name, EV_VOLATILE, env, comc_pcidev_set, env_nounset); 294 295 cp->c_flags = 0; 296 if (comc_setup(cp)) 297 cp->c_flags = C_PRESENTIN | C_PRESENTOUT; 298 } 299 300 static int 301 comc_init(struct console *cp, int arg __attribute((unused))) 302 { 303 304 if (comc_setup(cp)) 305 return (CMD_OK); 306 307 cp->c_flags = 0; 308 return (CMD_ERROR); 309 } 310 311 static void 312 comc_putchar(struct console *cp, int c) 313 { 314 int wait; 315 struct serial *sp = cp->c_private; 316 317 for (wait = COMC_TXWAIT; wait > 0; wait--) 318 if (inb(sp->ioaddr + com_lsr) & LSR_TXRDY) { 319 outb(sp->ioaddr + com_data, (uchar_t)c); 320 break; 321 } 322 } 323 324 static int 325 comc_getchar(struct console *cp) 326 { 327 struct serial *sp = cp->c_private; 328 return (comc_ischar(cp) ? inb(sp->ioaddr + com_data) : -1); 329 } 330 331 static int 332 comc_ischar(struct console *cp) 333 { 334 struct serial *sp = cp->c_private; 335 return (inb(sp->ioaddr + com_lsr) & LSR_RXRDY); 336 } 337 338 static int 339 comc_ioctl(struct console *cp __unused, int cmd __unused, void *data __unused) 340 { 341 return (ENOTTY); 342 } 343 344 static char * 345 comc_asprint_mode(struct serial *sp) 346 { 347 char par, *buf; 348 349 if (sp == NULL) 350 return (NULL); 351 352 if ((sp->lcr & (PAREN|PAREVN)) == (PAREN|PAREVN)) 353 par = 'e'; 354 else if ((sp->lcr & PAREN) == PAREN) 355 par = 'o'; 356 else 357 par = 'n'; 358 359 asprintf(&buf, "%d,%d,%c,%d,-", sp->speed, 360 (sp->lcr & BITS8) == BITS8? 8:7, 361 par, (sp->lcr & STOP2) == STOP2? 2:1); 362 return (buf); 363 } 364 365 static int 366 comc_parse_mode(struct serial *sp, const char *value) 367 { 368 unsigned long n; 369 int speed; 370 int lcr; 371 char *ep; 372 373 if (value == NULL || *value == '\0') 374 return (CMD_ERROR); 375 376 errno = 0; 377 n = strtoul(value, &ep, 10); 378 if (errno != 0 || *ep != ',') 379 return (CMD_ERROR); 380 speed = n; 381 382 ep++; 383 errno = 0; 384 n = strtoul(ep, &ep, 10); 385 if (errno != 0 || *ep != ',') 386 return (CMD_ERROR); 387 388 switch (n) { 389 case 7: lcr = BITS7; 390 break; 391 case 8: lcr = BITS8; 392 break; 393 default: 394 return (CMD_ERROR); 395 } 396 397 ep++; 398 switch (*ep++) { 399 case 'n': 400 break; 401 case 'e': lcr |= PAREN|PAREVN; 402 break; 403 case 'o': lcr |= PAREN|PARODD; 404 break; 405 default: 406 return (CMD_ERROR); 407 } 408 409 if (*ep == ',') 410 ep++; 411 else 412 return (CMD_ERROR); 413 414 switch (*ep++) { 415 case '1': 416 break; 417 case '2': lcr |= STOP2; 418 break; 419 default: 420 return (CMD_ERROR); 421 } 422 423 /* handshake is ignored, but we check syntax anyhow */ 424 if (*ep == ',') 425 ep++; 426 else 427 return (CMD_ERROR); 428 429 switch (*ep++) { 430 case '-': 431 case 'h': 432 case 's': 433 break; 434 default: 435 return (CMD_ERROR); 436 } 437 438 if (*ep != '\0') 439 return (CMD_ERROR); 440 441 sp->speed = speed; 442 sp->lcr = lcr; 443 return (CMD_OK); 444 } 445 446 static struct console * 447 get_console(const char *name) 448 { 449 char port[5]; 450 451 (void) strlcpy(port, name, sizeof (port)); 452 for (uint_t i = 0; consoles[i] != NULL; i++) { 453 if (strcmp(port, consoles[i]->c_name) == 0) 454 return (consoles[i]); 455 } 456 457 printf("No such port: %s\n", port); 458 return (NULL); 459 } 460 461 /* 462 * CMD_ERROR will cause set/setenv/setprop command to fail, 463 * when used in loader scripts (forth), this will cause processing 464 * of boot scripts to fail, rendering bootloading impossible. 465 * To prevent such unfortunate situation, we return CMD_OK when 466 * there is no such port, or there is invalid value in mode line. 467 */ 468 static int 469 comc_mode_set(struct env_var *ev, int flags, const void *value) 470 { 471 struct console *cp; 472 char name[15]; 473 474 if (value == NULL) 475 return (CMD_ERROR); 476 477 if ((cp = get_console(ev->ev_name)) == NULL) 478 return (CMD_OK); 479 480 /* Do not override serial setup from SPCR */ 481 snprintf(name, sizeof (name), "%s-spcr-mode", cp->c_name); 482 if (getenv(name) == NULL) { 483 if (comc_parse_mode(cp->c_private, value) == CMD_ERROR) { 484 printf("%s: invalid mode: %s\n", ev->ev_name, 485 (char *)value); 486 return (CMD_OK); 487 } 488 (void) comc_setup(cp); 489 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 490 } 491 492 return (CMD_OK); 493 } 494 495 /* 496 * CMD_ERROR will cause set/setenv/setprop command to fail, 497 * when used in loader scripts (forth), this will cause processing 498 * of boot scripts to fail, rendering bootloading impossible. 499 * To prevent such unfortunate situation, we return CMD_OK when 500 * there is no such port or invalid value was used. 501 */ 502 static int 503 comc_cd_set(struct env_var *ev, int flags, const void *value) 504 { 505 struct console *cp; 506 struct serial *sp; 507 508 if (value == NULL) 509 return (CMD_ERROR); 510 511 if ((cp = get_console(ev->ev_name)) == NULL) 512 return (CMD_OK); 513 514 sp = cp->c_private; 515 if (strcmp(value, "true") == 0) { 516 sp->ignore_cd = 1; 517 } else if (strcmp(value, "false") == 0) { 518 sp->ignore_cd = 0; 519 } else { 520 printf("%s: invalid value: %s\n", ev->ev_name, 521 (char *)value); 522 return (CMD_ERROR); 523 } 524 525 (void) comc_setup(cp); 526 527 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 528 529 return (CMD_OK); 530 } 531 532 /* 533 * CMD_ERROR will cause set/setenv/setprop command to fail, 534 * when used in loader scripts (forth), this will cause processing 535 * of boot scripts to fail, rendering bootloading impossible. 536 * To prevent such unfortunate situation, we return CMD_OK when 537 * there is no such port, or invalid value was used. 538 */ 539 static int 540 comc_rtsdtr_set(struct env_var *ev, int flags, const void *value) 541 { 542 struct console *cp; 543 struct serial *sp; 544 545 if (value == NULL) 546 return (CMD_ERROR); 547 548 if ((cp = get_console(ev->ev_name)) == NULL) 549 return (CMD_OK); 550 551 sp = cp->c_private; 552 if (strcmp(value, "true") == 0) { 553 sp->rtsdtr_off = 1; 554 } else if (strcmp(value, "false") == 0) { 555 sp->rtsdtr_off = 0; 556 } else { 557 printf("%s: invalid value: %s\n", ev->ev_name, 558 (char *)value); 559 return (CMD_ERROR); 560 } 561 562 (void) comc_setup(cp); 563 564 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 565 566 return (CMD_OK); 567 } 568 569 /* 570 * Input: bus:dev:func[:bar]. If bar is not specified, it is 0x10. 571 * Output: bar[24:16] bus[15:8] dev[7:3] func[2:0] 572 */ 573 static uint32_t 574 comc_parse_pcidev(const char *string) 575 { 576 #ifdef EFI 577 (void) string; 578 return (0); 579 #else 580 char *p, *p1; 581 uint8_t bus, dev, func, bar; 582 uint32_t locator; 583 int pres; 584 585 errno = 0; 586 pres = strtoul(string, &p, 10); 587 if (errno != 0 || p == string || *p != ':' || pres < 0) 588 return (0); 589 bus = pres; 590 p1 = ++p; 591 592 pres = strtoul(p1, &p, 10); 593 if (errno != 0 || p == string || *p != ':' || pres < 0) 594 return (0); 595 dev = pres; 596 p1 = ++p; 597 598 pres = strtoul(p1, &p, 10); 599 if (errno != 0 || p == string || (*p != ':' && *p != '\0') || pres < 0) 600 return (0); 601 func = pres; 602 603 if (*p == ':') { 604 p1 = ++p; 605 pres = strtoul(p1, &p, 10); 606 if (errno != 0 || p == string || *p != '\0' || pres <= 0) 607 return (0); 608 bar = pres; 609 } else 610 bar = 0x10; 611 612 locator = (bar << 16) | biospci_locator(bus, dev, func); 613 return (locator); 614 #endif 615 } 616 617 static int 618 comc_pcidev_handle(struct console *cp, uint32_t locator) 619 { 620 #ifdef EFI 621 (void) cp; 622 (void) locator; 623 return (CMD_ERROR); 624 #else 625 struct serial *sp = cp->c_private; 626 uint32_t port; 627 628 if (biospci_read_config(locator & 0xffff, 629 (locator & 0xff0000) >> 16, 2, &port) == -1) { 630 printf("Cannot read bar at 0x%x\n", locator); 631 return (CMD_ERROR); 632 } 633 if (!PCI_BAR_IO(port)) { 634 printf("Memory bar at 0x%x\n", locator); 635 return (CMD_ERROR); 636 } 637 port &= PCIM_BAR_IO_BASE; 638 639 (void) comc_setup(cp); 640 641 sp->locator = locator; 642 643 return (CMD_OK); 644 #endif 645 } 646 647 static int 648 comc_pcidev_set(struct env_var *ev, int flags, const void *value) 649 { 650 struct console *cp; 651 struct serial *sp; 652 uint32_t locator; 653 int error; 654 655 if ((cp = get_console(ev->ev_name)) == NULL) 656 return (CMD_ERROR); 657 sp = cp->c_private; 658 659 if (value == NULL || (locator = comc_parse_pcidev(value)) <= 0) { 660 printf("Invalid pcidev\n"); 661 return (CMD_ERROR); 662 } 663 if ((cp->c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) != 0 && 664 sp->locator != locator) { 665 error = comc_pcidev_handle(cp, locator); 666 if (error != CMD_OK) 667 return (error); 668 } 669 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 670 return (CMD_OK); 671 } 672 673 /* 674 * In case of error, we also reset ACTIVE flags, so the console 675 * framefork will try alternate consoles. 676 */ 677 static bool 678 comc_setup(struct console *cp) 679 { 680 struct serial *sp = cp->c_private; 681 static int TRY_COUNT = 1000000; 682 int tries; 683 684 outb(sp->ioaddr + com_cfcr, CFCR_DLAB | sp->lcr); 685 outb(sp->ioaddr + com_dlbl, COMC_BPS(sp->speed) & 0xff); 686 outb(sp->ioaddr + com_dlbh, COMC_BPS(sp->speed) >> 8); 687 outb(sp->ioaddr + com_cfcr, sp->lcr); 688 outb(sp->ioaddr + com_mcr, 689 sp->rtsdtr_off? ~(MCR_RTS | MCR_DTR) : MCR_RTS | MCR_DTR); 690 691 tries = 0; 692 do { 693 inb(sp->ioaddr + com_data); 694 } while (inb(sp->ioaddr + com_lsr) & LSR_RXRDY && ++tries < TRY_COUNT); 695 696 if (tries == TRY_COUNT) 697 return (false); 698 /* Mark this port usable. */ 699 cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT); 700 return (true); 701 } 702 703 int 704 comc_getspeed(int ioaddr) 705 { 706 uint_t divisor; 707 uchar_t dlbh; 708 uchar_t dlbl; 709 uchar_t cfcr; 710 711 cfcr = inb(ioaddr + com_cfcr); 712 outb(ioaddr + com_cfcr, CFCR_DLAB | cfcr); 713 714 dlbl = inb(ioaddr + com_dlbl); 715 dlbh = inb(ioaddr + com_dlbh); 716 717 outb(ioaddr + com_cfcr, cfcr); 718 719 divisor = dlbh << 8 | dlbl; 720 721 /* XXX there should be more sanity checking. */ 722 if (divisor == 0) 723 return (COMSPEED); 724 return (COMC_DIV2BPS(divisor)); 725 } 726