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 * We do not use this implementation with x86 till we can fix two issues: 28 * 1. Reliably identify the serial ports in correct order. 29 * 2. Ensure we get properly working reads from serial io. 30 */ 31 32 #include <sys/cdefs.h> 33 34 #include <stand.h> 35 #include <sys/errno.h> 36 #include <bootstrap.h> 37 #include <stdbool.h> 38 39 #include <efi.h> 40 #include <efilib.h> 41 42 #include "loader_efi.h" 43 44 static EFI_GUID serial = SERIAL_IO_PROTOCOL; 45 46 #define COMC_TXWAIT 0x40000 /* transmit timeout */ 47 48 #ifndef COMSPEED 49 #define COMSPEED 9600 50 #endif 51 52 #define PNP0501 0x501 /* 16550A-compatible COM port */ 53 54 struct serial { 55 uint64_t baudrate; 56 uint8_t databits; 57 EFI_PARITY_TYPE parity; 58 EFI_STOP_BITS_TYPE stopbits; 59 uint8_t ignore_cd; /* boolean */ 60 uint8_t rtsdtr_off; /* boolean */ 61 int ioaddr; /* index in handles array */ 62 SERIAL_IO_INTERFACE *sio; 63 }; 64 65 static void comc_probe(struct console *); 66 static int comc_init(struct console *, int); 67 static void comc_putchar(struct console *, int); 68 static int comc_getchar(struct console *); 69 static int comc_ischar(struct console *); 70 static int comc_ioctl(struct console *, int, void *); 71 static void comc_devinfo(struct console *); 72 static bool comc_setup(struct console *); 73 static char *comc_asprint_mode(struct serial *); 74 static int comc_parse_mode(struct serial *, const char *); 75 static int comc_mode_set(struct env_var *, int, const void *); 76 static int comc_cd_set(struct env_var *, int, const void *); 77 static int comc_rtsdtr_set(struct env_var *, int, const void *); 78 79 struct console ttya = { 80 .c_name = "ttya", 81 .c_desc = "serial port a", 82 .c_flags = 0, 83 .c_probe = comc_probe, 84 .c_init = comc_init, 85 .c_out = comc_putchar, 86 .c_in = comc_getchar, 87 .c_ready = comc_ischar, 88 .c_ioctl = comc_ioctl, 89 .c_devinfo = comc_devinfo, 90 .c_private = NULL 91 }; 92 93 struct console ttyb = { 94 .c_name = "ttyb", 95 .c_desc = "serial port b", 96 .c_flags = 0, 97 .c_probe = comc_probe, 98 .c_init = comc_init, 99 .c_out = comc_putchar, 100 .c_in = comc_getchar, 101 .c_ready = comc_ischar, 102 .c_ioctl = comc_ioctl, 103 .c_devinfo = comc_devinfo, 104 .c_private = NULL 105 }; 106 107 struct console ttyc = { 108 .c_name = "ttyc", 109 .c_desc = "serial port c", 110 .c_flags = 0, 111 .c_probe = comc_probe, 112 .c_init = comc_init, 113 .c_out = comc_putchar, 114 .c_in = comc_getchar, 115 .c_ready = comc_ischar, 116 .c_ioctl = comc_ioctl, 117 .c_devinfo = comc_devinfo, 118 .c_private = NULL 119 }; 120 121 struct console ttyd = { 122 .c_name = "ttyd", 123 .c_desc = "serial port d", 124 .c_flags = 0, 125 .c_probe = comc_probe, 126 .c_init = comc_init, 127 .c_out = comc_putchar, 128 .c_in = comc_getchar, 129 .c_ready = comc_ischar, 130 .c_ioctl = comc_ioctl, 131 .c_devinfo = comc_devinfo, 132 .c_private = NULL 133 }; 134 135 /* 136 * Find serial device number from device path. 137 * Return -1 if not found. 138 */ 139 static int 140 efi_serial_get_index(EFI_DEVICE_PATH *devpath) 141 { 142 ACPI_HID_DEVICE_PATH *acpi; 143 144 while (!IsDevicePathEnd(devpath)) { 145 if (DevicePathType(devpath) == ACPI_DEVICE_PATH && 146 DevicePathSubType(devpath) == ACPI_DP) { 147 148 acpi = (ACPI_HID_DEVICE_PATH *)devpath; 149 if (acpi->HID == EISA_PNP_ID(PNP0501)) { 150 return (acpi->UID); 151 } 152 } 153 154 devpath = NextDevicePathNode(devpath); 155 } 156 return (-1); 157 } 158 159 /* 160 * The order of handles from LocateHandle() is not known, we need to 161 * iterate handles, pick device path for handle, and check the device 162 * number. 163 */ 164 static EFI_HANDLE 165 efi_serial_get_handle(int port) 166 { 167 EFI_STATUS status; 168 EFI_HANDLE *handles, handle; 169 EFI_DEVICE_PATH *devpath; 170 uint_t index, nhandles; 171 172 if (port == -1) 173 return (NULL); 174 175 status = efi_get_protocol_handles(&serial, &nhandles, &handles); 176 if (EFI_ERROR(status)) 177 return (NULL); 178 179 handle = NULL; 180 for (index = 0; index < nhandles; index++) { 181 devpath = efi_lookup_devpath(handles[index]); 182 if (port == efi_serial_get_index(devpath)) { 183 handle = (handles[index]); 184 break; 185 } 186 } 187 188 /* 189 * In case we did fail to identify the device by path, use port as 190 * array index. Note, we did check port == -1 above. 191 */ 192 if (port < nhandles && handle == NULL) 193 handle = handles[port]; 194 195 free(handles); 196 return (handle); 197 } 198 199 static void 200 comc_probe(struct console *cp) 201 { 202 EFI_STATUS status; 203 EFI_HANDLE handle; 204 struct serial *port; 205 char name[20]; 206 char value[20]; 207 char *env; 208 209 /* are we already set up? */ 210 if (cp->c_private != NULL) 211 return; 212 213 cp->c_private = malloc(sizeof (struct serial)); 214 port = cp->c_private; 215 port->baudrate = COMSPEED; 216 217 port->ioaddr = -1; /* invalid port */ 218 if (strcmp(cp->c_name, "ttya") == 0) 219 port->ioaddr = 0; 220 else if (strcmp(cp->c_name, "ttyb") == 0) 221 port->ioaddr = 1; 222 else if (strcmp(cp->c_name, "ttyc") == 0) 223 port->ioaddr = 2; 224 else if (strcmp(cp->c_name, "ttyd") == 0) 225 port->ioaddr = 3; 226 227 port->databits = 8; /* 8,n,1 */ 228 port->parity = NoParity; /* 8,n,1 */ 229 port->stopbits = OneStopBit; /* 8,n,1 */ 230 port->ignore_cd = 1; /* ignore cd */ 231 port->rtsdtr_off = 0; /* rts-dtr is on */ 232 port->sio = NULL; 233 234 handle = efi_serial_get_handle(port->ioaddr); 235 236 if (handle != NULL) { 237 status = BS->OpenProtocol(handle, &serial, 238 (void**)&port->sio, IH, NULL, 239 EFI_OPEN_PROTOCOL_GET_PROTOCOL); 240 241 if (EFI_ERROR(status)) 242 port->sio = NULL; 243 } 244 245 snprintf(name, sizeof (name), "%s-mode", cp->c_name); 246 env = getenv(name); 247 248 if (env != NULL) 249 (void) comc_parse_mode(port, env); 250 251 env = comc_asprint_mode(port); 252 253 if (env != NULL) { 254 unsetenv(name); 255 env_setenv(name, EV_VOLATILE, env, comc_mode_set, env_nounset); 256 free(env); 257 } 258 259 snprintf(name, sizeof (name), "%s-ignore-cd", cp->c_name); 260 env = getenv(name); 261 if (env != NULL) { 262 if (strcmp(env, "true") == 0) 263 port->ignore_cd = 1; 264 else if (strcmp(env, "false") == 0) 265 port->ignore_cd = 0; 266 } 267 268 snprintf(value, sizeof (value), "%s", 269 port->ignore_cd? "true" : "false"); 270 unsetenv(name); 271 env_setenv(name, EV_VOLATILE, value, comc_cd_set, env_nounset); 272 273 snprintf(name, sizeof (name), "%s-rts-dtr-off", cp->c_name); 274 env = getenv(name); 275 if (env != NULL) { 276 if (strcmp(env, "true") == 0) 277 port->rtsdtr_off = 1; 278 else if (strcmp(env, "false") == 0) 279 port->rtsdtr_off = 0; 280 } 281 282 snprintf(value, sizeof (value), "%s", 283 port->rtsdtr_off? "true" : "false"); 284 unsetenv(name); 285 env_setenv(name, EV_VOLATILE, value, comc_rtsdtr_set, env_nounset); 286 287 cp->c_flags = 0; 288 if (comc_setup(cp)) 289 cp->c_flags = C_PRESENTIN | C_PRESENTOUT; 290 } 291 292 static int 293 comc_init(struct console *cp, int arg __attribute((unused))) 294 { 295 296 if (comc_setup(cp)) 297 return (CMD_OK); 298 299 cp->c_flags = 0; 300 return (CMD_ERROR); 301 } 302 303 static void 304 comc_putchar(struct console *cp, int c) 305 { 306 int wait; 307 EFI_STATUS status; 308 UINTN bufsz = 1; 309 char cb = c; 310 struct serial *sp = cp->c_private; 311 312 if (sp->sio == NULL) 313 return; 314 315 for (wait = COMC_TXWAIT; wait > 0; wait--) { 316 status = sp->sio->Write(sp->sio, &bufsz, &cb); 317 if (status != EFI_TIMEOUT) 318 break; 319 } 320 } 321 322 static int 323 comc_getchar(struct console *cp) 324 { 325 EFI_STATUS status; 326 UINTN bufsz = 1; 327 char c; 328 struct serial *sp = cp->c_private; 329 330 if (sp->sio == NULL || !comc_ischar(cp)) 331 return (-1); 332 333 status = sp->sio->Read(sp->sio, &bufsz, &c); 334 if (EFI_ERROR(status) || bufsz == 0) 335 return (-1); 336 337 return (c); 338 } 339 340 static int 341 comc_ischar(struct console *cp) 342 { 343 EFI_STATUS status; 344 uint32_t control; 345 struct serial *sp = cp->c_private; 346 347 if (sp->sio == NULL) 348 return (0); 349 350 status = sp->sio->GetControl(sp->sio, &control); 351 if (EFI_ERROR(status)) 352 return (0); 353 354 return (!(control & EFI_SERIAL_INPUT_BUFFER_EMPTY)); 355 } 356 357 static int 358 comc_ioctl(struct console *cp __unused, int cmd __unused, void *data __unused) 359 { 360 return (ENOTTY); 361 } 362 363 static void 364 comc_devinfo(struct console *cp) 365 { 366 struct serial *port = cp->c_private; 367 EFI_HANDLE handle; 368 EFI_DEVICE_PATH *dp; 369 CHAR16 *text; 370 371 handle = efi_serial_get_handle(port->ioaddr); 372 if (handle == NULL) { 373 printf("\tdevice is not present"); 374 return; 375 } 376 377 dp = efi_lookup_devpath(handle); 378 if (dp == NULL) 379 return; 380 381 text = efi_devpath_name(dp); 382 if (text == NULL) 383 return; 384 385 printf("\t%S", text); 386 efi_free_devpath_name(text); 387 } 388 389 static char * 390 comc_asprint_mode(struct serial *sp) 391 { 392 char par, *buf; 393 char *stop; 394 395 if (sp == NULL) 396 return (NULL); 397 398 switch (sp->parity) { 399 case NoParity: 400 par = 'n'; 401 break; 402 case EvenParity: 403 par = 'e'; 404 break; 405 case OddParity: 406 par = 'o'; 407 break; 408 case MarkParity: 409 par = 'm'; 410 break; 411 case SpaceParity: 412 par = 's'; 413 break; 414 default: 415 par = 'n'; 416 break; 417 } 418 419 switch (sp->stopbits) { 420 case OneStopBit: 421 stop = "1"; 422 break; 423 case TwoStopBits: 424 stop = "2"; 425 break; 426 case OneFiveStopBits: 427 stop = "1.5"; 428 break; 429 default: 430 stop = "1"; 431 break; 432 } 433 434 asprintf(&buf, "%ju,%d,%c,%s,-", sp->baudrate, sp->databits, par, stop); 435 return (buf); 436 } 437 438 static int 439 comc_parse_mode(struct serial *sp, const char *value) 440 { 441 unsigned long n; 442 uint64_t baudrate; 443 uint8_t databits = 8; 444 int parity = NoParity; 445 int stopbits = OneStopBit; 446 char *ep; 447 448 if (value == NULL || *value == '\0') 449 return (CMD_ERROR); 450 451 errno = 0; 452 n = strtoul(value, &ep, 10); 453 if (errno != 0 || *ep != ',') 454 return (CMD_ERROR); 455 baudrate = n; 456 457 ep++; 458 n = strtoul(ep, &ep, 10); 459 if (errno != 0 || *ep != ',') 460 return (CMD_ERROR); 461 462 switch (n) { 463 case 5: databits = 5; 464 break; 465 case 6: databits = 6; 466 break; 467 case 7: databits = 7; 468 break; 469 case 8: databits = 8; 470 break; 471 default: 472 return (CMD_ERROR); 473 } 474 475 ep++; 476 switch (*ep++) { 477 case 'n': parity = NoParity; 478 break; 479 case 'e': parity = EvenParity; 480 break; 481 case 'o': parity = OddParity; 482 break; 483 case 'm': parity = MarkParity; 484 break; 485 case 's': parity = SpaceParity; 486 break; 487 default: 488 return (CMD_ERROR); 489 } 490 491 if (*ep == ',') 492 ep++; 493 else 494 return (CMD_ERROR); 495 496 switch (*ep++) { 497 case '1': stopbits = OneStopBit; 498 if (ep[0] == '.' && ep[1] == '5') { 499 ep += 2; 500 stopbits = OneFiveStopBits; 501 } 502 break; 503 case '2': stopbits = TwoStopBits; 504 break; 505 default: 506 return (CMD_ERROR); 507 } 508 509 /* handshake is ignored, but we check syntax anyhow */ 510 if (*ep == ',') 511 ep++; 512 else 513 return (CMD_ERROR); 514 515 switch (*ep++) { 516 case '-': 517 case 'h': 518 case 's': 519 break; 520 default: 521 return (CMD_ERROR); 522 } 523 524 if (*ep != '\0') 525 return (CMD_ERROR); 526 527 sp->baudrate = baudrate; 528 sp->databits = databits; 529 sp->parity = parity; 530 sp->stopbits = stopbits; 531 return (CMD_OK); 532 } 533 534 static struct console * 535 get_console(char *name) 536 { 537 struct console *cp = NULL; 538 539 switch (name[3]) { 540 case 'a': cp = &ttya; 541 break; 542 case 'b': cp = &ttyb; 543 break; 544 case 'c': cp = &ttyc; 545 break; 546 case 'd': cp = &ttyd; 547 break; 548 } 549 return (cp); 550 } 551 552 static int 553 comc_mode_set(struct env_var *ev, int flags, const void *value) 554 { 555 struct console *cp; 556 557 if (value == NULL) 558 return (CMD_ERROR); 559 560 if ((cp = get_console(ev->ev_name)) == NULL) 561 return (CMD_ERROR); 562 563 if (comc_parse_mode(cp->c_private, value) == CMD_ERROR) 564 return (CMD_ERROR); 565 566 (void) comc_setup(cp); 567 568 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 569 570 return (CMD_OK); 571 } 572 573 static int 574 comc_cd_set(struct env_var *ev, int flags, const void *value) 575 { 576 struct console *cp; 577 struct serial *sp; 578 579 if (value == NULL) 580 return (CMD_ERROR); 581 582 if ((cp = get_console(ev->ev_name)) == NULL) 583 return (CMD_ERROR); 584 585 sp = cp->c_private; 586 if (strcmp(value, "true") == 0) 587 sp->ignore_cd = 1; 588 else if (strcmp(value, "false") == 0) 589 sp->ignore_cd = 0; 590 else 591 return (CMD_ERROR); 592 593 (void) comc_setup(cp); 594 595 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 596 597 return (CMD_OK); 598 } 599 600 static int 601 comc_rtsdtr_set(struct env_var *ev, int flags, const void *value) 602 { 603 struct console *cp; 604 struct serial *sp; 605 606 if (value == NULL) 607 return (CMD_ERROR); 608 609 if ((cp = get_console(ev->ev_name)) == NULL) 610 return (CMD_ERROR); 611 612 sp = cp->c_private; 613 if (strcmp(value, "true") == 0) 614 sp->rtsdtr_off = 1; 615 else if (strcmp(value, "false") == 0) 616 sp->rtsdtr_off = 0; 617 else 618 return (CMD_ERROR); 619 620 (void) comc_setup(cp); 621 622 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 623 624 return (CMD_OK); 625 } 626 627 /* 628 * In case of error, we also reset ACTIVE flags, so the console 629 * framefork will try alternate consoles. 630 */ 631 static bool 632 comc_setup(struct console *cp) 633 { 634 EFI_STATUS status; 635 UINT32 control; 636 struct serial *sp = cp->c_private; 637 638 /* port is not usable */ 639 if (sp->sio == NULL) 640 return (false); 641 642 status = sp->sio->Reset(sp->sio); 643 if (EFI_ERROR(status)) 644 return (false); 645 646 status = sp->sio->SetAttributes(sp->sio, sp->baudrate, 0, 0, sp->parity, 647 sp->databits, sp->stopbits); 648 if (EFI_ERROR(status)) 649 return (false); 650 651 status = sp->sio->GetControl(sp->sio, &control); 652 if (EFI_ERROR(status)) 653 return (false); 654 if (sp->rtsdtr_off) { 655 control &= ~(EFI_SERIAL_REQUEST_TO_SEND | 656 EFI_SERIAL_DATA_TERMINAL_READY); 657 } else { 658 control |= EFI_SERIAL_REQUEST_TO_SEND; 659 } 660 661 (void) sp->sio->SetControl(sp->sio, control); 662 663 /* Mark this port usable. */ 664 cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT); 665 return (true); 666 } 667