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