Index: stand/efi/loader/bootinfo.c =================================================================== --- stand/efi/loader/bootinfo.c +++ stand/efi/loader/bootinfo.c @@ -67,10 +67,11 @@ 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 @@ 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); Index: stand/efi/loader/main.c =================================================================== --- stand/efi/loader/main.c +++ stand/efi/loader/main.c @@ -400,8 +400,8 @@ 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 @@ 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 @@ */ 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 +#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);