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