Date: Sat, 14 Jul 2018 00:40:39 +0000 (UTC) From: Warner Losh <imp@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r336270 - head/stand/efi/loader Message-ID: <201807140040.w6E0eddV033638@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: imp Date: Sat Jul 14 00:40:38 2018 New Revision: 336270 URL: https://svnweb.freebsd.org/changeset/base/336270 Log: uefi stand: Guess the console better For server machines, ComOut is set to the set of devices that the efi console suppots. Parse it to see if we have serial, video or both. Make that take precidence over the command line args. boot1.efi parses them, but loader.efi doesn't. It's not clear where to read boot.conf from, so we don't do that. The command line args can still be set via efibootmgr, which is more inline with the UEFI boot manager to replace that. These args are typically used only to set serial vs video and the com speed line. We can infer that from ComOut, so do so. Remember the com speed and hw.uart.console to match. RelNotes: yes Sponsored by: Netflix Differential Revision: https://reviews.freebsd.org/D15917 Modified: head/stand/efi/loader/bootinfo.c head/stand/efi/loader/main.c Modified: head/stand/efi/loader/bootinfo.c ============================================================================== --- head/stand/efi/loader/bootinfo.c Fri Jul 13 23:46:07 2018 (r336269) +++ head/stand/efi/loader/bootinfo.c Sat Jul 14 00:40:38 2018 (r336270) @@ -67,10 +67,11 @@ extern EFI_SYSTEM_TABLE *ST; static int bi_getboothowto(char *kargs) { - const char *sw; + const char *sw, *tmp; char *opts; char *console; - int howto; + int howto, speed, port; + char buf[50]; howto = boot_parse_cmdline(kargs); howto |= boot_env_to_howto(); @@ -81,6 +82,35 @@ bi_getboothowto(char *kargs) howto |= RB_SERIAL; if (strcmp(console, "nullconsole") == 0) howto |= RB_MUTE; + if (strcmp(console, "efi") == 0) { + /* + * If we found a com port and com speed, we need to tell + * the kernel where the serial port is, and how + * fast. Ideally, we'd get the port from ACPI, but that + * isn't running in the loader. Do the next best thing + * by allowing it to be set by a loader.conf variable, + * either a EFI specific one, or the compatible + * comconsole_port if not. PCI support is needed, but + * for that we'd ideally refactor the + * libi386/comconsole.c code to have identical behavior. + */ + tmp = getenv("efi_com_speed"); + if (tmp != NULL) { + speed = strtol(tmp, NULL, 0); + tmp = getenv("efi_com_port"); + if (tmp == NULL) + tmp = getenv("comconsole_port"); + /* XXX fallback to EFI variable set in rc.d? */ + if (tmp != NULL) + port = strtol(tmp, NULL, 0); + else + port = 0x3f8; + snprintf(buf, sizeof(buf), "io:%d,br:%d", port, + speed); + env_setenv("hw.uart.console", EV_VOLATILE, buf, + NULL, NULL); + } + } } return (howto); Modified: head/stand/efi/loader/main.c ============================================================================== --- head/stand/efi/loader/main.c Fri Jul 13 23:46:07 2018 (r336269) +++ head/stand/efi/loader/main.c Sat Jul 14 00:40:38 2018 (r336270) @@ -400,8 +400,8 @@ interactive_interrupt(const char *msg) return (false); } -int -parse_args(int argc, CHAR16 *argv[], bool has_kbd) +static int +parse_args(int argc, CHAR16 *argv[]) { int i, j, howto; bool vargood; @@ -429,12 +429,98 @@ parse_args(int argc, CHAR16 *argv[], bool has_kbd) return (howto); } +/* + * Parse ConOut (the list of consoles active) and see if we can find a + * serial port and/or a video port. It would be nice to also walk the + * ACPI name space to map the UID for the serial port to a port. The + * latter is especially hard. + */ +static int +parse_uefi_con_out(void) +{ + int how, rv; + int vid_seen = 0, com_seen = 0, seen = 0; + size_t sz; + char buf[4096], *ep; + EFI_DEVICE_PATH *node; + ACPI_HID_DEVICE_PATH *acpi; + UART_DEVICE_PATH *uart; + bool pci_pending; + how = 0; + sz = sizeof(buf); + rv = efi_global_getenv("ConOut", buf, &sz); + if (rv != EFI_SUCCESS) + goto out; + ep = buf + sz; + node = (EFI_DEVICE_PATH *)buf; + while ((char *)node < ep && !IsDevicePathEndType(node)) { + pci_pending = false; + if (DevicePathType(node) == ACPI_DEVICE_PATH && + DevicePathSubType(node) == ACPI_DP) { + /* Check for Serial node */ + acpi = (void *)node; + if (EISA_ID_TO_NUM(acpi->HID) == 0x501) + com_seen = ++seen; + } else if (DevicePathType(node) == MESSAGING_DEVICE_PATH && + DevicePathSubType(node) == MSG_UART_DP) { + char bd[16]; + + uart = (void *)node; + snprintf(bd, sizeof(bd), "%d", uart->BaudRate); + setenv("efi_com_speed", bd, 1); + printf("Console speed is %d\n", uart->BaudRate); + } else if (DevicePathType(node) == ACPI_DEVICE_PATH && + DevicePathSubType(node) == ACPI_ADR_DP) { + /* Check for AcpiAdr() Node for video */ + vid_seen = ++seen; + } else if (DevicePathType(node) == HARDWARE_DEVICE_PATH && + DevicePathSubType(node) == HW_PCI_DP) { + /* + * Note, vmware fusion has a funky console device + * PciRoot(0x0)/Pci(0xf,0x0) + * which we can only detect at the end since we also + * have to cope with: + * PciRoot(0x0)/Pci(0x1f,0x0)/Serial(0x1) + * so only match it if it's last. + */ + pci_pending = true; + } + node = NextDevicePathNode(node); /* Skip the end node */ + } + if (pci_pending && vid_seen == 0) + vid_seen = ++seen; + + /* + * Truth table for RB_MULTIPLE | RB_SERIAL + * Value Result + * 0 Use only video console + * RB_SERIAL Use only serial console + * RB_MULTIPLE Use both video and serial console + * (but video is primary so gets rc messages) + * both Use both video and serial console + * (but serial is primary so gets rc messages) + * + * Try to honor this as best we can. If only one of serial / video + * found, then use that. Otherwise, use the first one we found. + * This also implies if we found nothing, default to video. + */ + how = 0; + if (vid_seen && com_seen) { + how |= RB_MULTIPLE; + if (com_seen < vid_seen) + how |= RB_SERIAL; + } else if (com_seen) + how |= RB_SERIAL; +out: + return (how); +} + EFI_STATUS main(int argc, CHAR16 *argv[]) { EFI_GUID *guid; - int howto, i; + int howto, i, uhowto; UINTN k; bool has_kbd; char *s; @@ -481,23 +567,61 @@ main(int argc, CHAR16 *argv[]) */ bcache_init(32768, 512); - howto = parse_args(argc, argv, has_kbd); + howto = parse_args(argc, argv); + if (!has_kbd && (howto & RB_PROBE)) + howto |= RB_SERIAL | RB_MULTIPLE; + howto &= ~RB_PROBE; - boot_howto_to_env(howto); + uhowto = parse_uefi_con_out(); /* - * XXX we need fallback to this stuff after looking at the ConIn, ConOut and ConErr variables + * We now have two notions of console. howto should be viewed as + * overrides. */ - if (howto & RB_MULTIPLE) { - if (howto & RB_SERIAL) - setenv("console", "comconsole efi" , 1); - else - setenv("console", "efi comconsole" , 1); - } else if (howto & RB_SERIAL) { - setenv("console", "comconsole" , 1); - } else - setenv("console", "efi", 1); +#define VIDEO_ONLY 0 +#define SERIAL_ONLY RB_SERIAL +#define VID_SER_BOTH RB_MULTIPLE +#define SER_VID_BOTH (RB_SERIAL | RB_MULTIPLE) +#define CON_MASK (RB_SERIAL | RB_MULTIPLE) + if ((howto & CON_MASK) == 0) { + /* No override, uhowto is controlling and efi cons is perfect */ + howto = howto | (uhowto & CON_MASK); + setenv("console", "efi", 1); + } else if ((howto & CON_MASK) == (uhowto & CON_MASK)) { + /* override matches what UEFI told us, efi console is perfect */ + setenv("console", "efi", 1); + } else if ((uhowto & (CON_MASK)) != 0) { + /* + * We detected a serial console on ConOut. All possible + * overrides include serial. We can't really override what efi + * gives us, so we use it knowing it's the best choice. + */ + setenv("console", "efi", 1); + } else { + /* + * We detected some kind of serial in the override, but ConOut + * has no serial, so we have to sort out which case it really is. + */ + switch (howto & CON_MASK) { + case SERIAL_ONLY: + setenv("console", "comconsole", 1); + break; + case VID_SER_BOTH: + setenv("console", "efi comconsole", 1); + break; + case SER_VID_BOTH: + setenv("console", "comconsole efi", 1); + break; + /* case VIDEO_ONLY can't happen -- it's the first if above */ + } + } + /* + * howto is set now how we want to export the flags to the kernel, so + * set the env based on it. + */ + boot_howto_to_env(howto); + if (efi_copy_init()) { printf("failed to allocate staging area\n"); return (EFI_BUFFER_TOO_SMALL);
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201807140040.w6E0eddV033638>