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