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 #include <stand.h> 27 #include <sys/errno.h> 28 #include <bootstrap.h> 29 #include <stdbool.h> 30 31 #include <efi.h> 32 #include <efilib.h> 33 34 static EFI_GUID serial = SERIAL_IO_PROTOCOL; 35 36 #define COMC_TXWAIT 0x40000 /* transmit timeout */ 37 38 #define PNP0501 0x501 /* 16550A-compatible COM port */ 39 40 struct serial { 41 uint64_t newbaudrate; 42 uint64_t baudrate; 43 uint32_t timeout; 44 uint32_t receivefifodepth; 45 uint32_t databits; 46 EFI_PARITY_TYPE parity; 47 EFI_STOP_BITS_TYPE stopbits; 48 int ioaddr; /* index in handles array */ 49 EFI_HANDLE currdev; /* current serial device */ 50 EFI_HANDLE condev; /* EFI Console device */ 51 SERIAL_IO_INTERFACE *sio; 52 }; 53 54 static void comc_probe(struct console *); 55 static int comc_init(int); 56 static void comc_putchar(int); 57 static int comc_getchar(void); 58 static int comc_ischar(void); 59 static bool comc_setup(void); 60 static int comc_parse_intval(const char *, unsigned *); 61 static int comc_port_set(struct env_var *, int, const void *); 62 static int comc_speed_set(struct env_var *, int, const void *); 63 64 static struct serial *comc_port; 65 extern struct console efi_console; 66 67 struct console eficom = { 68 .c_name = "eficom", 69 .c_desc = "serial port", 70 .c_flags = 0, 71 .c_probe = comc_probe, 72 .c_init = comc_init, 73 .c_out = comc_putchar, 74 .c_in = comc_getchar, 75 .c_ready = comc_ischar, 76 }; 77 78 #if defined(__aarch64__) && __FreeBSD_version < 1500000 79 static void comc_probe_compat(struct console *); 80 struct console comconsole = { 81 .c_name = "comconsole", 82 .c_desc = "serial port", 83 .c_flags = 0, 84 .c_probe = comc_probe_compat, 85 .c_init = comc_init, 86 .c_out = comc_putchar, 87 .c_in = comc_getchar, 88 .c_ready = comc_ischar, 89 }; 90 #endif 91 92 static EFI_STATUS 93 efi_serial_init(EFI_HANDLE **handlep, int *nhandles) 94 { 95 UINTN bufsz = 0; 96 EFI_STATUS status; 97 EFI_HANDLE *handles; 98 99 /* 100 * get buffer size 101 */ 102 *nhandles = 0; 103 handles = NULL; 104 status = BS->LocateHandle(ByProtocol, &serial, NULL, &bufsz, handles); 105 if (status != EFI_BUFFER_TOO_SMALL) 106 return (status); 107 108 if ((handles = malloc(bufsz)) == NULL) 109 return (ENOMEM); 110 111 *nhandles = (int)(bufsz / sizeof (EFI_HANDLE)); 112 /* 113 * get handle array 114 */ 115 status = BS->LocateHandle(ByProtocol, &serial, NULL, &bufsz, handles); 116 if (EFI_ERROR(status)) { 117 free(handles); 118 *nhandles = 0; 119 } else 120 *handlep = handles; 121 return (status); 122 } 123 124 /* 125 * Find serial device number from device path. 126 * Return -1 if not found. 127 */ 128 static int 129 efi_serial_get_index(EFI_DEVICE_PATH *devpath, int idx) 130 { 131 ACPI_HID_DEVICE_PATH *acpi; 132 CHAR16 *text; 133 134 while (!IsDevicePathEnd(devpath)) { 135 if (DevicePathType(devpath) == MESSAGING_DEVICE_PATH && 136 DevicePathSubType(devpath) == MSG_UART_DP) 137 return (idx); 138 139 if (DevicePathType(devpath) == ACPI_DEVICE_PATH && 140 (DevicePathSubType(devpath) == ACPI_DP || 141 DevicePathSubType(devpath) == ACPI_EXTENDED_DP)) { 142 143 acpi = (ACPI_HID_DEVICE_PATH *)devpath; 144 if (acpi->HID == EISA_PNP_ID(PNP0501)) { 145 return (acpi->UID); 146 } 147 } 148 149 devpath = NextDevicePathNode(devpath); 150 } 151 return (-1); 152 } 153 154 /* 155 * The order of handles from LocateHandle() is not known, we need to 156 * iterate handles, pick device path for handle, and check the device 157 * number. 158 */ 159 static EFI_HANDLE 160 efi_serial_get_handle(int port, EFI_HANDLE condev) 161 { 162 EFI_STATUS status; 163 EFI_HANDLE *handles, handle; 164 EFI_DEVICE_PATH *devpath; 165 int index, nhandles; 166 167 if (port == -1) 168 return (NULL); 169 170 handles = NULL; 171 nhandles = 0; 172 status = efi_serial_init(&handles, &nhandles); 173 if (EFI_ERROR(status)) 174 return (NULL); 175 176 /* 177 * We have console handle, set ioaddr for it. 178 */ 179 if (condev != NULL) { 180 for (index = 0; index < nhandles; index++) { 181 if (condev == handles[index]) { 182 devpath = efi_lookup_devpath(condev); 183 comc_port->ioaddr = 184 efi_serial_get_index(devpath, index); 185 efi_close_devpath(condev); 186 free(handles); 187 return (condev); 188 } 189 } 190 } 191 192 handle = NULL; 193 for (index = 0; handle == NULL && index < nhandles; index++) { 194 devpath = efi_lookup_devpath(handles[index]); 195 if (port == efi_serial_get_index(devpath, index)) 196 handle = (handles[index]); 197 efi_close_devpath(handles[index]); 198 } 199 200 /* 201 * In case we did fail to identify the device by path, use port as 202 * array index. Note, we did check port == -1 above. 203 */ 204 if (port < nhandles && handle == NULL) 205 handle = handles[port]; 206 207 free(handles); 208 return (handle); 209 } 210 211 static EFI_HANDLE 212 comc_get_con_serial_handle(const char *name) 213 { 214 EFI_HANDLE handle; 215 EFI_DEVICE_PATH *node; 216 EFI_STATUS status; 217 char *buf, *ep; 218 size_t sz; 219 220 buf = NULL; 221 sz = 0; 222 status = efi_global_getenv(name, buf, &sz); 223 if (status == EFI_BUFFER_TOO_SMALL) { 224 buf = malloc(sz); 225 if (buf == NULL) 226 return (NULL); 227 status = efi_global_getenv(name, buf, &sz); 228 } 229 if (status != EFI_SUCCESS) { 230 free(buf); 231 return (NULL); 232 } 233 234 ep = buf + sz; 235 node = (EFI_DEVICE_PATH *)buf; 236 while ((char *)node < ep) { 237 status = BS->LocateDevicePath(&serial, &node, &handle); 238 if (status == EFI_SUCCESS) { 239 free(buf); 240 return (handle); 241 } 242 243 /* Sanity check the node before moving to the next node. */ 244 if (DevicePathNodeLength(node) < sizeof(*node)) 245 break; 246 247 /* Start of next device path in list. */ 248 node = NextDevicePathNode(node); 249 } 250 free(buf); 251 return (NULL); 252 } 253 254 /* 255 * Called from cons_probe() to see if this device is available. 256 * Return immediately on x86, except for hyperv, since it interferes with 257 * common configurations otherwise (yes, this is just firewalling the bug). 258 */ 259 static void 260 comc_probe(struct console *sc) 261 { 262 EFI_STATUS status; 263 EFI_HANDLE handle; 264 char name[20]; 265 char value[20]; 266 unsigned val; 267 char *env, *buf, *ep; 268 size_t sz; 269 270 #ifdef __amd64__ 271 /* 272 * This driver tickles issues on a number of different firmware loads. 273 * It is only required for HyperV, and is only known to work on HyperV, 274 * so only allow it on HyperV. 275 */ 276 env = getenv("smbios.bios.version"); 277 if (env == NULL || strncmp(env, "Hyper-V", 7) != 0) { 278 return; 279 } 280 #endif 281 282 if (comc_port == NULL) { 283 comc_port = calloc(1, sizeof (struct serial)); 284 if (comc_port == NULL) 285 return; 286 } 287 288 /* Use defaults from firmware */ 289 comc_port->databits = 8; 290 comc_port->parity = DefaultParity; 291 comc_port->stopbits = DefaultStopBits; 292 293 handle = NULL; 294 env = getenv("efi_com_port"); 295 if (comc_parse_intval(env, &val) == CMD_OK) { 296 comc_port->ioaddr = val; 297 } else { 298 /* 299 * efi_com_port is not set, we need to select default. 300 * First, we consult ConOut variable to see if 301 * we have serial port redirection. If not, we just 302 * pick first device. 303 */ 304 handle = comc_get_con_serial_handle("ConOut"); 305 comc_port->condev = handle; 306 } 307 308 handle = efi_serial_get_handle(comc_port->ioaddr, handle); 309 if (handle != NULL) { 310 comc_port->currdev = handle; 311 status = BS->OpenProtocol(handle, &serial, 312 (void**)&comc_port->sio, IH, NULL, 313 EFI_OPEN_PROTOCOL_GET_PROTOCOL); 314 315 if (EFI_ERROR(status)) { 316 comc_port->sio = NULL; 317 } else { 318 comc_port->newbaudrate = 319 comc_port->baudrate = comc_port->sio->Mode->BaudRate; 320 comc_port->timeout = comc_port->sio->Mode->Timeout; 321 comc_port->receivefifodepth = 322 comc_port->sio->Mode->ReceiveFifoDepth; 323 comc_port->databits = comc_port->sio->Mode->DataBits; 324 comc_port->parity = comc_port->sio->Mode->Parity; 325 comc_port->stopbits = comc_port->sio->Mode->StopBits; 326 } 327 } 328 329 /* 330 * If there's no sio, then the device isn't there, so just return since 331 * the present flags aren't yet set. 332 */ 333 if (comc_port->sio == NULL) { 334 free(comc_port); 335 comc_port = NULL; 336 return; 337 } 338 339 if (env != NULL) 340 unsetenv("efi_com_port"); 341 snprintf(value, sizeof (value), "%u", comc_port->ioaddr); 342 env_setenv("efi_com_port", EV_VOLATILE, value, 343 comc_port_set, env_nounset); 344 345 env = getenv("efi_com_speed"); 346 if (env == NULL) 347 /* fallback to comconsole setting */ 348 env = getenv("comconsole_speed"); 349 350 if (comc_parse_intval(env, &val) == CMD_OK) 351 comc_port->newbaudrate = val; 352 353 if (env != NULL) 354 unsetenv("efi_com_speed"); 355 snprintf(value, sizeof (value), "%ju", (uintmax_t)comc_port->baudrate); 356 env_setenv("efi_com_speed", EV_VOLATILE, value, 357 comc_speed_set, env_nounset); 358 359 if (comc_setup()) { 360 sc->c_flags = C_PRESENTIN | C_PRESENTOUT; 361 } else { 362 sc->c_flags &= ~(C_PRESENTIN | C_PRESENTOUT); 363 free(comc_port); 364 comc_port = NULL; 365 } 366 } 367 368 #if defined(__aarch64__) && __FreeBSD_version < 1500000 369 static void 370 comc_probe_compat(struct console *sc) 371 { 372 comc_probe(&eficom); 373 if (eficom.c_flags & (C_PRESENTIN | C_PRESENTOUT)) { 374 printf("comconsole: comconsole device name is deprecated, switch to eficom\n"); 375 } 376 /* 377 * Note: We leave the present bits unset in sc to avoid ghosting. 378 */ 379 } 380 #endif 381 382 /* 383 * Called when the console is selected in cons_change. If we didn't detect the 384 * device, comc_port will be NULL, and comc_setup will fail. It may be called 385 * even when the device isn't present as a 'fallback' console or when listed 386 * specifically in console env, so we have to reset the c_flags in those case to 387 * say it's not present. 388 */ 389 static int 390 comc_init(int arg __unused) 391 { 392 if (comc_setup()) 393 return (CMD_OK); 394 395 eficom.c_flags &= ~(C_ACTIVEIN | C_ACTIVEOUT); 396 return (CMD_ERROR); 397 } 398 399 static void 400 comc_putchar(int c) 401 { 402 int wait; 403 EFI_STATUS status; 404 UINTN bufsz = 1; 405 char cb = c; 406 407 if (comc_port->sio == NULL) 408 return; 409 410 for (wait = COMC_TXWAIT; wait > 0; wait--) { 411 status = comc_port->sio->Write(comc_port->sio, &bufsz, &cb); 412 if (status != EFI_TIMEOUT) 413 break; 414 } 415 } 416 417 static int 418 comc_getchar(void) 419 { 420 EFI_STATUS status; 421 UINTN bufsz = 1; 422 char c; 423 424 425 /* 426 * if this device is also used as ConIn, some firmwares 427 * fail to return all input via SIO protocol. 428 */ 429 if (comc_port->currdev == comc_port->condev) { 430 if ((efi_console.c_flags & C_ACTIVEIN) == 0) 431 return (efi_console.c_in()); 432 return (-1); 433 } 434 435 if (comc_port->sio == NULL) 436 return (-1); 437 438 status = comc_port->sio->Read(comc_port->sio, &bufsz, &c); 439 if (EFI_ERROR(status) || bufsz == 0) 440 return (-1); 441 442 return (c); 443 } 444 445 static int 446 comc_ischar(void) 447 { 448 EFI_STATUS status; 449 uint32_t control; 450 451 /* 452 * if this device is also used as ConIn, some firmwares 453 * fail to return all input via SIO protocol. 454 */ 455 if (comc_port->currdev == comc_port->condev) { 456 if ((efi_console.c_flags & C_ACTIVEIN) == 0) 457 return (efi_console.c_ready()); 458 return (0); 459 } 460 461 if (comc_port->sio == NULL) 462 return (0); 463 464 status = comc_port->sio->GetControl(comc_port->sio, &control); 465 if (EFI_ERROR(status)) 466 return (0); 467 468 return (!(control & EFI_SERIAL_INPUT_BUFFER_EMPTY)); 469 } 470 471 static int 472 comc_parse_intval(const char *value, unsigned *valp) 473 { 474 unsigned n; 475 char *ep; 476 477 if (value == NULL || *value == '\0') 478 return (CMD_ERROR); 479 480 errno = 0; 481 n = strtoul(value, &ep, 10); 482 if (errno != 0 || *ep != '\0') 483 return (CMD_ERROR); 484 *valp = n; 485 486 return (CMD_OK); 487 } 488 489 static int 490 comc_port_set(struct env_var *ev, int flags, const void *value) 491 { 492 unsigned port; 493 SERIAL_IO_INTERFACE *sio; 494 EFI_HANDLE handle; 495 EFI_STATUS status; 496 497 if (value == NULL || comc_port == NULL) 498 return (CMD_ERROR); 499 500 if (comc_parse_intval(value, &port) != CMD_OK) 501 return (CMD_ERROR); 502 503 handle = efi_serial_get_handle(port, NULL); 504 if (handle == NULL) { 505 printf("no handle\n"); 506 return (CMD_ERROR); 507 } 508 509 status = BS->OpenProtocol(handle, &serial, 510 (void**)&sio, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); 511 512 if (EFI_ERROR(status)) { 513 printf("OpenProtocol: %lu\n", EFI_ERROR_CODE(status)); 514 return (CMD_ERROR); 515 } 516 517 comc_port->currdev = handle; 518 comc_port->ioaddr = port; 519 comc_port->sio = sio; 520 521 (void) comc_setup(); 522 523 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 524 return (CMD_OK); 525 } 526 527 static int 528 comc_speed_set(struct env_var *ev, int flags, const void *value) 529 { 530 unsigned speed; 531 532 if (value == NULL || comc_port == NULL) 533 return (CMD_ERROR); 534 535 if (comc_parse_intval(value, &speed) != CMD_OK) 536 return (CMD_ERROR); 537 538 comc_port->newbaudrate = speed; 539 if (comc_setup()) 540 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 541 542 return (CMD_OK); 543 } 544 545 /* 546 * In case of error, we also reset ACTIVE flags, so the console 547 * framefork will try alternate consoles. 548 */ 549 static bool 550 comc_setup(void) 551 { 552 EFI_STATUS status; 553 char *ev; 554 555 /* 556 * If the device isn't active, or there's no port present. 557 */ 558 if ((eficom.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) == 0 || comc_port == NULL) 559 return (false); 560 561 if (comc_port->sio->Reset != NULL) { 562 status = comc_port->sio->Reset(comc_port->sio); 563 if (EFI_ERROR(status)) 564 return (false); 565 } 566 567 /* 568 * Avoid setting the baud rate on Hyper-V. Also, only set the baud rate 569 * if the baud rate has changed from the default. And pass in '0' or 570 * DefaultFoo when we're not changing those values. Some EFI 571 * implementations get cranky when you set things to the values reported 572 * back even when they are unchanged. 573 */ 574 if (comc_port->sio->SetAttributes != NULL && 575 comc_port->newbaudrate != comc_port->baudrate) { 576 ev = getenv("smbios.bios.version"); 577 if (ev != NULL && strncmp(ev, "Hyper-V", 7) != 0) { 578 status = comc_port->sio->SetAttributes(comc_port->sio, 579 comc_port->newbaudrate, 0, 0, DefaultParity, 0, 580 DefaultStopBits); 581 if (EFI_ERROR(status)) 582 return (false); 583 comc_port->baudrate = comc_port->newbaudrate; 584 } 585 } 586 587 #ifdef EFI_FORCE_RTS 588 if (comc_port->sio->GetControl != NULL && comc_port->sio->SetControl != NULL) { 589 UINT32 control; 590 591 status = comc_port->sio->GetControl(comc_port->sio, &control); 592 if (EFI_ERROR(status)) 593 return (false); 594 control |= EFI_SERIAL_REQUEST_TO_SEND; 595 (void) comc_port->sio->SetControl(comc_port->sio, control); 596 } 597 #endif 598 /* Mark this port usable. */ 599 eficom.c_flags |= (C_PRESENTIN | C_PRESENTOUT); 600 return (true); 601 } 602