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