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 static void 258 comc_probe(struct console *sc) 259 { 260 EFI_STATUS status; 261 EFI_HANDLE handle; 262 char name[20]; 263 char value[20]; 264 unsigned val; 265 char *env, *buf, *ep; 266 size_t sz; 267 268 if (comc_port == NULL) { 269 comc_port = calloc(1, sizeof (struct serial)); 270 if (comc_port == NULL) 271 return; 272 } 273 274 /* Use defaults from firmware */ 275 comc_port->databits = 8; 276 comc_port->parity = DefaultParity; 277 comc_port->stopbits = DefaultStopBits; 278 279 handle = NULL; 280 env = getenv("efi_com_port"); 281 if (comc_parse_intval(env, &val) == CMD_OK) { 282 comc_port->ioaddr = val; 283 } else { 284 /* 285 * efi_com_port is not set, we need to select default. 286 * First, we consult ConOut variable to see if 287 * we have serial port redirection. If not, we just 288 * pick first device. 289 */ 290 handle = comc_get_con_serial_handle("ConOut"); 291 comc_port->condev = handle; 292 } 293 294 handle = efi_serial_get_handle(comc_port->ioaddr, handle); 295 if (handle != NULL) { 296 comc_port->currdev = handle; 297 status = BS->OpenProtocol(handle, &serial, 298 (void**)&comc_port->sio, IH, NULL, 299 EFI_OPEN_PROTOCOL_GET_PROTOCOL); 300 301 if (EFI_ERROR(status)) { 302 comc_port->sio = NULL; 303 } else { 304 comc_port->newbaudrate = 305 comc_port->baudrate = comc_port->sio->Mode->BaudRate; 306 comc_port->timeout = comc_port->sio->Mode->Timeout; 307 comc_port->receivefifodepth = 308 comc_port->sio->Mode->ReceiveFifoDepth; 309 comc_port->databits = comc_port->sio->Mode->DataBits; 310 comc_port->parity = comc_port->sio->Mode->Parity; 311 comc_port->stopbits = comc_port->sio->Mode->StopBits; 312 } 313 } 314 315 if (env != NULL) 316 unsetenv("efi_com_port"); 317 snprintf(value, sizeof (value), "%u", comc_port->ioaddr); 318 env_setenv("efi_com_port", EV_VOLATILE, value, 319 comc_port_set, env_nounset); 320 321 env = getenv("efi_com_speed"); 322 if (env == NULL) 323 /* fallback to comconsole setting */ 324 env = getenv("comconsole_speed"); 325 326 if (comc_parse_intval(env, &val) == CMD_OK) 327 comc_port->newbaudrate = val; 328 329 if (env != NULL) 330 unsetenv("efi_com_speed"); 331 snprintf(value, sizeof (value), "%ju", (uintmax_t)comc_port->baudrate); 332 env_setenv("efi_com_speed", EV_VOLATILE, value, 333 comc_speed_set, env_nounset); 334 335 eficom.c_flags = 0; 336 if (comc_setup()) { 337 sc->c_flags = C_PRESENTIN | C_PRESENTOUT; 338 } 339 } 340 341 #if defined(__aarch64__) && __FreeBSD_version < 1500000 342 static void 343 comc_probe_compat(struct console *sc) 344 { 345 comc_probe(sc); 346 if (sc->c_flags & (C_PRESENTIN | C_PRESENTOUT)) { 347 printf("comconsole: comconsole device name is deprecated, switch to eficom\n"); 348 } 349 } 350 #endif 351 352 static int 353 comc_init(int arg __unused) 354 { 355 356 if (comc_setup()) 357 return (CMD_OK); 358 359 eficom.c_flags = 0; 360 return (CMD_ERROR); 361 } 362 363 static void 364 comc_putchar(int c) 365 { 366 int wait; 367 EFI_STATUS status; 368 UINTN bufsz = 1; 369 char cb = c; 370 371 if (comc_port->sio == NULL) 372 return; 373 374 for (wait = COMC_TXWAIT; wait > 0; wait--) { 375 status = comc_port->sio->Write(comc_port->sio, &bufsz, &cb); 376 if (status != EFI_TIMEOUT) 377 break; 378 } 379 } 380 381 static int 382 comc_getchar(void) 383 { 384 EFI_STATUS status; 385 UINTN bufsz = 1; 386 char c; 387 388 389 /* 390 * if this device is also used as ConIn, some firmwares 391 * fail to return all input via SIO protocol. 392 */ 393 if (comc_port->currdev == comc_port->condev) { 394 if ((efi_console.c_flags & C_ACTIVEIN) == 0) 395 return (efi_console.c_in()); 396 return (-1); 397 } 398 399 if (comc_port->sio == NULL) 400 return (-1); 401 402 status = comc_port->sio->Read(comc_port->sio, &bufsz, &c); 403 if (EFI_ERROR(status) || bufsz == 0) 404 return (-1); 405 406 return (c); 407 } 408 409 static int 410 comc_ischar(void) 411 { 412 EFI_STATUS status; 413 uint32_t control; 414 415 /* 416 * if this device is also used as ConIn, some firmwares 417 * fail to return all input via SIO protocol. 418 */ 419 if (comc_port->currdev == comc_port->condev) { 420 if ((efi_console.c_flags & C_ACTIVEIN) == 0) 421 return (efi_console.c_ready()); 422 return (0); 423 } 424 425 if (comc_port->sio == NULL) 426 return (0); 427 428 status = comc_port->sio->GetControl(comc_port->sio, &control); 429 if (EFI_ERROR(status)) 430 return (0); 431 432 return (!(control & EFI_SERIAL_INPUT_BUFFER_EMPTY)); 433 } 434 435 static int 436 comc_parse_intval(const char *value, unsigned *valp) 437 { 438 unsigned n; 439 char *ep; 440 441 if (value == NULL || *value == '\0') 442 return (CMD_ERROR); 443 444 errno = 0; 445 n = strtoul(value, &ep, 10); 446 if (errno != 0 || *ep != '\0') 447 return (CMD_ERROR); 448 *valp = n; 449 450 return (CMD_OK); 451 } 452 453 static int 454 comc_port_set(struct env_var *ev, int flags, const void *value) 455 { 456 unsigned port; 457 SERIAL_IO_INTERFACE *sio; 458 EFI_HANDLE handle; 459 EFI_STATUS status; 460 461 if (value == NULL) 462 return (CMD_ERROR); 463 464 if (comc_parse_intval(value, &port) != CMD_OK) 465 return (CMD_ERROR); 466 467 handle = efi_serial_get_handle(port, NULL); 468 if (handle == NULL) { 469 printf("no handle\n"); 470 return (CMD_ERROR); 471 } 472 473 status = BS->OpenProtocol(handle, &serial, 474 (void**)&sio, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); 475 476 if (EFI_ERROR(status)) { 477 printf("OpenProtocol: %lu\n", EFI_ERROR_CODE(status)); 478 return (CMD_ERROR); 479 } 480 481 comc_port->currdev = handle; 482 comc_port->ioaddr = port; 483 comc_port->sio = sio; 484 485 (void) comc_setup(); 486 487 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 488 return (CMD_OK); 489 } 490 491 static int 492 comc_speed_set(struct env_var *ev, int flags, const void *value) 493 { 494 unsigned speed; 495 496 if (value == NULL) 497 return (CMD_ERROR); 498 499 if (comc_parse_intval(value, &speed) != CMD_OK) 500 return (CMD_ERROR); 501 502 comc_port->newbaudrate = speed; 503 if (comc_setup()) 504 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 505 506 return (CMD_OK); 507 } 508 509 /* 510 * In case of error, we also reset ACTIVE flags, so the console 511 * framefork will try alternate consoles. 512 */ 513 static bool 514 comc_setup(void) 515 { 516 EFI_STATUS status; 517 char *ev; 518 519 /* port is not usable */ 520 if (comc_port->sio == NULL) 521 return (false); 522 523 if (comc_port->sio->Reset != NULL) { 524 status = comc_port->sio->Reset(comc_port->sio); 525 if (EFI_ERROR(status)) 526 return (false); 527 } 528 529 /* 530 * Avoid setting the baud rate on Hyper-V. Also, only set the baud rate 531 * if the baud rate has changed from the default. And pass in '0' or 532 * DefaultFoo when we're not changing those values. Some EFI 533 * implementations get cranky when you set things to the values reported 534 * back even when they are unchanged. 535 */ 536 if (comc_port->sio->SetAttributes != NULL && 537 comc_port->newbaudrate != comc_port->baudrate) { 538 ev = getenv("smbios.bios.version"); 539 if (ev != NULL && strncmp(ev, "Hyper-V", 7) != 0) { 540 status = comc_port->sio->SetAttributes(comc_port->sio, 541 comc_port->newbaudrate, 0, 0, DefaultParity, 0, 542 DefaultStopBits); 543 if (EFI_ERROR(status)) 544 return (false); 545 comc_port->baudrate = comc_port->newbaudrate; 546 } 547 } 548 549 #ifdef EFI_FORCE_RTS 550 if (comc_port->sio->GetControl != NULL && comc_port->sio->SetControl != NULL) { 551 UINT32 control; 552 553 status = comc_port->sio->GetControl(comc_port->sio, &control); 554 if (EFI_ERROR(status)) 555 return (false); 556 control |= EFI_SERIAL_REQUEST_TO_SEND; 557 (void) comc_port->sio->SetControl(comc_port->sio, control); 558 } 559 #endif 560 /* Mark this port usable. */ 561 eficom.c_flags |= (C_PRESENTIN | C_PRESENTOUT); 562 return (true); 563 } 564