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 }; 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 int comc_getspeed(int); 81 static int comc_ischar(struct console *); 82 static int comc_ioctl(struct console *, int, void *); 83 static bool comc_setup(struct console *); 84 static char *comc_asprint_mode(struct serial *); 85 static int comc_parse_mode(struct serial *, const char *); 86 static int comc_mode_set(struct env_var *, int, const void *); 87 static int comc_cd_set(struct env_var *, int, const void *); 88 static int comc_rtsdtr_set(struct env_var *, int, const void *); 89 static void comc_devinfo(struct console *); 90 91 static void 92 comc_devinfo(struct console *cp) 93 { 94 struct serial *port = cp->c_private; 95 96 printf("\tport %#x", port->ioaddr); 97 } 98 99 static bool 100 comc_port_is_present(int ioaddr) 101 { 102 /* 103 * Write byte to scratch register and read it out. 104 */ 105 #define COMC_TEST 0xbb 106 outb(ioaddr + com_scr, COMC_TEST); 107 return (inb(ioaddr + com_scr) == COMC_TEST); 108 } 109 110 /* 111 * Set up following environment variables: 112 * ttyX-mode 113 * ttyX-rts-dtr-off 114 * ttyX-ignore-cd 115 */ 116 static void 117 comc_setup_env(struct console *tty) 118 { 119 struct serial *port; 120 char name[20]; 121 char value[20]; 122 char *env; 123 124 port = tty->c_private; 125 snprintf(name, sizeof (name), "%s-mode", tty->c_name); 126 env = comc_asprint_mode(port); 127 if (env != NULL) { 128 unsetenv(name); 129 env_setenv(name, EV_VOLATILE, env, comc_mode_set, env_nounset); 130 free(env); 131 } 132 133 snprintf(name, sizeof (name), "%s-rts-dtr-off", tty->c_name); 134 snprintf(value, sizeof (value), "%s", 135 port->rtsdtr_off? "true" : "false"); 136 unsetenv(name); 137 env_setenv(name, EV_VOLATILE, value, comc_rtsdtr_set, env_nounset); 138 139 snprintf(name, sizeof (name), "%s-ignore-cd", tty->c_name); 140 snprintf(value, sizeof (value), "%s", 141 port->ignore_cd? "true" : "false"); 142 unsetenv(name); 143 env_setenv(name, EV_VOLATILE, value, comc_cd_set, env_nounset); 144 } 145 146 /* 147 * Set up list of possible serial consoles. 148 * This function is run very early, so we do not expect to 149 * run out of memory, and on error, we can not print output. 150 */ 151 void 152 comc_ini(void) 153 { 154 uint_t n = 0, c; 155 bool ports[COM_NPORTS]; 156 struct console **tmp; 157 struct console *tty; 158 struct serial *port; 159 160 /* 161 * Test the presence of 4 serial devices com1-com4 162 */ 163 ports[0] = comc_port_is_present(COM1_IOADDR); 164 ports[1] = comc_port_is_present(COM2_IOADDR); 165 ports[2] = comc_port_is_present(COM3_IOADDR); 166 ports[3] = comc_port_is_present(COM4_IOADDR); 167 168 for (uint_t i = 0; i < COM_NPORTS; i++) 169 if (ports[i]) 170 n++; 171 172 if (n == 0) /* there are no serial ports */ 173 return; 174 175 c = cons_array_size(); 176 if (c == 0) 177 n++; /* For NULL pointer */ 178 179 tmp = realloc(consoles, (c + n) * sizeof (*consoles)); 180 if (tmp == NULL) 181 return; 182 consoles = tmp; 183 if (c > 0) 184 c--; 185 186 for (uint_t i = 0; i < COM_NPORTS; i++) { 187 if (!ports[i]) 188 continue; 189 tty = malloc(sizeof (*tty)); 190 if (tty == NULL) { 191 /* Out of memory?! can not continue */ 192 consoles[c] = tty; 193 return; 194 } 195 if (asprintf(&tty->c_name, "tty%c", 'a' + i) < 0) { 196 free(tty); 197 consoles[c] = NULL; 198 return; 199 } 200 if (asprintf(&tty->c_desc, "serial port %c", 'a' + i) < 0) { 201 free(tty->c_name); 202 free(tty); 203 consoles[c] = NULL; 204 return; 205 } 206 tty->c_flags = 0; 207 tty->c_probe = comc_probe; 208 tty->c_init = comc_init; 209 tty->c_out = comc_putchar; 210 tty->c_in = comc_getchar; 211 tty->c_ready = comc_ischar; 212 tty->c_ioctl = comc_ioctl; 213 tty->c_devinfo = comc_devinfo; 214 port = malloc(sizeof (*port)); 215 if (port == NULL) { 216 free(tty->c_name); 217 free(tty->c_desc); 218 free(tty); 219 consoles[c] = NULL; 220 return; 221 } 222 port->speed = 0; /* Leave this for comc_probe */ 223 switch (i) { 224 case 0: 225 port->ioaddr = COM1_IOADDR; 226 break; 227 case 1: 228 port->ioaddr = COM2_IOADDR; 229 break; 230 case 2: 231 port->ioaddr = COM3_IOADDR; 232 break; 233 case 3: 234 port->ioaddr = COM4_IOADDR; 235 break; 236 } 237 port->speed = comc_getspeed(port->ioaddr); 238 port->lcr = BITS8; /* 8,n,1 */ 239 port->ignore_cd = 1; /* ignore cd */ 240 port->rtsdtr_off = 0; /* rts-dtr is on */ 241 242 tty->c_private = port; 243 consoles[c++] = tty; 244 comc_setup_env(tty); 245 246 /* Reset terminal to initial normal settings with ESC [ 0 m */ 247 comc_putchar(tty, 0x1b); 248 comc_putchar(tty, '['); 249 comc_putchar(tty, '0'); 250 comc_putchar(tty, 'm'); 251 /* drain input from random data */ 252 while (comc_getchar(tty) != -1) 253 ; 254 } 255 consoles[c] = NULL; 256 } 257 258 static void 259 comc_probe(struct console *cp) 260 { 261 cp->c_flags = 0; 262 if (comc_setup(cp)) 263 cp->c_flags = C_PRESENTIN | C_PRESENTOUT; 264 } 265 266 static int 267 comc_init(struct console *cp, int arg __attribute((unused))) 268 { 269 270 if (comc_setup(cp)) 271 return (CMD_OK); 272 273 cp->c_flags = 0; 274 return (CMD_ERROR); 275 } 276 277 static void 278 comc_putchar(struct console *cp, int c) 279 { 280 int wait; 281 struct serial *sp = cp->c_private; 282 283 for (wait = COMC_TXWAIT; wait > 0; wait--) 284 if (inb(sp->ioaddr + com_lsr) & LSR_TXRDY) { 285 outb(sp->ioaddr + com_data, (uchar_t)c); 286 break; 287 } 288 } 289 290 static int 291 comc_getchar(struct console *cp) 292 { 293 struct serial *sp = cp->c_private; 294 return (comc_ischar(cp) ? inb(sp->ioaddr + com_data) : -1); 295 } 296 297 static int 298 comc_ischar(struct console *cp) 299 { 300 struct serial *sp = cp->c_private; 301 return (inb(sp->ioaddr + com_lsr) & LSR_RXRDY); 302 } 303 304 static int 305 comc_ioctl(struct console *cp __unused, int cmd __unused, void *data __unused) 306 { 307 return (ENOTTY); 308 } 309 310 static char * 311 comc_asprint_mode(struct serial *sp) 312 { 313 char par, *buf; 314 315 if (sp == NULL) 316 return (NULL); 317 318 if ((sp->lcr & (PAREN|PAREVN)) == (PAREN|PAREVN)) 319 par = 'e'; 320 else if ((sp->lcr & PAREN) == PAREN) 321 par = 'o'; 322 else 323 par = 'n'; 324 325 asprintf(&buf, "%d,%d,%c,%d,-", sp->speed, 326 (sp->lcr & BITS8) == BITS8? 8:7, 327 par, (sp->lcr & STOP2) == STOP2? 2:1); 328 return (buf); 329 } 330 331 static int 332 comc_parse_mode(struct serial *sp, const char *value) 333 { 334 unsigned long n; 335 int speed; 336 int lcr; 337 char *ep; 338 339 if (value == NULL || *value == '\0') 340 return (CMD_ERROR); 341 342 errno = 0; 343 n = strtoul(value, &ep, 10); 344 if (errno != 0 || *ep != ',') 345 return (CMD_ERROR); 346 speed = n; 347 348 ep++; 349 errno = 0; 350 n = strtoul(ep, &ep, 10); 351 if (errno != 0 || *ep != ',') 352 return (CMD_ERROR); 353 354 switch (n) { 355 case 7: lcr = BITS7; 356 break; 357 case 8: lcr = BITS8; 358 break; 359 default: 360 return (CMD_ERROR); 361 } 362 363 ep++; 364 switch (*ep++) { 365 case 'n': 366 break; 367 case 'e': lcr |= PAREN|PAREVN; 368 break; 369 case 'o': lcr |= PAREN|PARODD; 370 break; 371 default: 372 return (CMD_ERROR); 373 } 374 375 if (*ep == ',') 376 ep++; 377 else 378 return (CMD_ERROR); 379 380 switch (*ep++) { 381 case '1': 382 break; 383 case '2': lcr |= STOP2; 384 break; 385 default: 386 return (CMD_ERROR); 387 } 388 389 /* handshake is ignored, but we check syntax anyhow */ 390 if (*ep == ',') 391 ep++; 392 else 393 return (CMD_ERROR); 394 395 switch (*ep++) { 396 case '-': 397 case 'h': 398 case 's': 399 break; 400 default: 401 return (CMD_ERROR); 402 } 403 404 if (*ep != '\0') 405 return (CMD_ERROR); 406 407 sp->speed = speed; 408 sp->lcr = lcr; 409 return (CMD_OK); 410 } 411 412 /* 413 * CMD_ERROR will cause set/setenv/setprop command to fail, 414 * when used in loader scripts (forth), this will cause processing 415 * of boot scripts to fail, rendering bootloading impossible. 416 * To prevent such unfortunate situation, we return CMD_OK when 417 * there is no such port, or there is invalid value in mode line. 418 */ 419 static int 420 comc_mode_set(struct env_var *ev, int flags, const void *value) 421 { 422 struct console *cp; 423 char name[15]; 424 425 if (value == NULL) 426 return (CMD_ERROR); 427 428 if ((cp = cons_get_console(ev->ev_name)) == NULL) 429 return (CMD_OK); 430 431 /* Do not override serial setup from SPCR */ 432 snprintf(name, sizeof (name), "%s-spcr-mode", cp->c_name); 433 if (getenv(name) == NULL) { 434 if (comc_parse_mode(cp->c_private, value) == CMD_ERROR) { 435 printf("%s: invalid mode: %s\n", ev->ev_name, 436 (char *)value); 437 return (CMD_OK); 438 } 439 (void) comc_setup(cp); 440 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 441 } 442 443 return (CMD_OK); 444 } 445 446 /* 447 * CMD_ERROR will cause set/setenv/setprop command to fail, 448 * when used in loader scripts (forth), this will cause processing 449 * of boot scripts to fail, rendering bootloading impossible. 450 * To prevent such unfortunate situation, we return CMD_OK when 451 * there is no such port or invalid value was used. 452 */ 453 static int 454 comc_cd_set(struct env_var *ev, int flags, const void *value) 455 { 456 struct console *cp; 457 struct serial *sp; 458 459 if (value == NULL) 460 return (CMD_OK); 461 462 if ((cp = cons_get_console(ev->ev_name)) == NULL) 463 return (CMD_OK); 464 465 sp = cp->c_private; 466 if (strcmp(value, "true") == 0) { 467 sp->ignore_cd = 1; 468 } else if (strcmp(value, "false") == 0) { 469 sp->ignore_cd = 0; 470 } else { 471 printf("%s: invalid value: %s\n", ev->ev_name, 472 (char *)value); 473 return (CMD_OK); 474 } 475 476 (void) comc_setup(cp); 477 478 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 479 480 return (CMD_OK); 481 } 482 483 /* 484 * CMD_ERROR will cause set/setenv/setprop command to fail, 485 * when used in loader scripts (forth), this will cause processing 486 * of boot scripts to fail, rendering bootloading impossible. 487 * To prevent such unfortunate situation, we return CMD_OK when 488 * there is no such port, or invalid value was used. 489 */ 490 static int 491 comc_rtsdtr_set(struct env_var *ev, int flags, const void *value) 492 { 493 struct console *cp; 494 struct serial *sp; 495 496 if (value == NULL) 497 return (CMD_OK); 498 499 if ((cp = cons_get_console(ev->ev_name)) == NULL) 500 return (CMD_OK); 501 502 sp = cp->c_private; 503 if (strcmp(value, "true") == 0) { 504 sp->rtsdtr_off = 1; 505 } else if (strcmp(value, "false") == 0) { 506 sp->rtsdtr_off = 0; 507 } else { 508 printf("%s: invalid value: %s\n", ev->ev_name, 509 (char *)value); 510 return (CMD_OK); 511 } 512 513 (void) comc_setup(cp); 514 515 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 516 517 return (CMD_OK); 518 } 519 520 /* 521 * In case of error, we also reset ACTIVE flags, so the console 522 * framefork will try alternate consoles. 523 */ 524 static bool 525 comc_setup(struct console *cp) 526 { 527 struct serial *sp = cp->c_private; 528 static int TRY_COUNT = 1000000; 529 int tries; 530 531 outb(sp->ioaddr + com_cfcr, CFCR_DLAB | sp->lcr); 532 outb(sp->ioaddr + com_dlbl, COMC_BPS(sp->speed) & 0xff); 533 outb(sp->ioaddr + com_dlbh, COMC_BPS(sp->speed) >> 8); 534 outb(sp->ioaddr + com_cfcr, sp->lcr); 535 outb(sp->ioaddr + com_mcr, 536 sp->rtsdtr_off? ~(MCR_RTS | MCR_DTR) : MCR_RTS | MCR_DTR); 537 538 tries = 0; 539 do { 540 inb(sp->ioaddr + com_data); 541 } while (inb(sp->ioaddr + com_lsr) & LSR_RXRDY && ++tries < TRY_COUNT); 542 543 if (tries == TRY_COUNT) 544 return (false); 545 /* Mark this port usable. */ 546 cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT); 547 return (true); 548 } 549 550 int 551 comc_getspeed(int ioaddr) 552 { 553 uint_t divisor; 554 uchar_t dlbh; 555 uchar_t dlbl; 556 uchar_t cfcr; 557 558 cfcr = inb(ioaddr + com_cfcr); 559 outb(ioaddr + com_cfcr, CFCR_DLAB | cfcr); 560 561 dlbl = inb(ioaddr + com_dlbl); 562 dlbh = inb(ioaddr + com_dlbh); 563 564 outb(ioaddr + com_cfcr, cfcr); 565 566 divisor = dlbh << 8 | dlbl; 567 568 /* XXX there should be more sanity checking. */ 569 if (divisor == 0) 570 return (COMSPEED); 571 return (COMC_DIV2BPS(divisor)); 572 } 573