diff options
author | Toomas Soome <tsoome@me.com> | 2019-07-13 19:32:11 +0300 |
---|---|---|
committer | Toomas Soome <tsoome@me.com> | 2022-07-11 10:42:55 +0300 |
commit | e27085df90712f99e5ea3d44ab0b83c73ac1bf52 (patch) | |
tree | aeb15583612ac9db5e65f5885a8d2534c83dc8bb | |
parent | 92ee55c7e1c76d6edfc89c4ad988922d56888580 (diff) | |
download | illumos-gate-e27085df90712f99e5ea3d44ab0b83c73ac1bf52.tar.gz |
14580 loader.efi: detect console from ConOut/ConOutDev
Reviewed by: Garrett D'Amore <garrett@damore.org>
Approved by: Richard Lowe <richlowe@richlowe.net>
-rw-r--r-- | usr/src/boot/Makefile.version | 2 | ||||
-rw-r--r-- | usr/src/boot/efi/include/efidevp.h | 41 | ||||
-rw-r--r-- | usr/src/boot/efi/loader/main.c | 245 |
3 files changed, 275 insertions, 13 deletions
diff --git a/usr/src/boot/Makefile.version b/usr/src/boot/Makefile.version index 6ec72af057..3f11cd86eb 100644 --- a/usr/src/boot/Makefile.version +++ b/usr/src/boot/Makefile.version @@ -34,4 +34,4 @@ LOADER_VERSION = 1.1 # Use date like formatting here, YYYY.MM.DD.XX, without leading zeroes. # The version is processed from left to right, the version number can only # be increased. -BOOT_VERSION = $(LOADER_VERSION)-2022.06.27.1 +BOOT_VERSION = $(LOADER_VERSION)-2022.07.04.1 diff --git a/usr/src/boot/efi/include/efidevp.h b/usr/src/boot/efi/include/efidevp.h index bd8f304922..ed2f55fd73 100644 --- a/usr/src/boot/efi/include/efidevp.h +++ b/usr/src/boot/efi/include/efidevp.h @@ -154,9 +154,48 @@ typedef struct _ACPI_EXTENDED_HID_DEVICE_PATH { #define PNP_EISA_ID_MASK 0xffff #define EISA_ID_TO_NUM(_Id) ((_Id) >> 16) + /* - * + * ACPI _ADR Device Path SubType. + */ +#define ACPI_ADR_DP 0x03 + +/* + * The _ADR device path is used to contain video output device attributes to + * support the Graphics Output Protocol. + * The device path can contain multiple _ADR entries if multiple video output + * devices are displaying the same output. */ +typedef struct { + EFI_DEVICE_PATH Header; + /* + * _ADR value. For video output devices the value of this + * field comes from Table B-2 of the ACPI 3.0 specification. At + * least one _ADR value is required. + */ + UINT32 ADR; + /* + * This device path may optionally contain more than one _ADR entry. + */ +} ACPI_ADR_DEVICE_PATH; + +#define ACPI_ADR_DISPLAY_TYPE_OTHER 0 +#define ACPI_ADR_DISPLAY_TYPE_VGA 1 +#define ACPI_ADR_DISPLAY_TYPE_TV 2 +#define ACPI_ADR_DISPLAY_TYPE_EXTERNAL_DIGITAL 3 +#define ACPI_ADR_DISPLAY_TYPE_INTERNAL_DIGITAL 4 + +#define ACPI_DISPLAY_ADR(_DeviceIdScheme, _HeadId, _NonVgaOutput, \ + _BiosCanDetect, _VendorInfo, _Type, _Port, _Index) \ + ((UINT32)( ((UINT32)((_DeviceIdScheme) & 0x1) << 31) | \ + (((_HeadId) & 0x7) << 18) | \ + (((_NonVgaOutput) & 0x1) << 17) | \ + (((_BiosCanDetect) & 0x1) << 16) | \ + (((_VendorInfo) & 0xf) << 12) | \ + (((_Type) & 0xf) << 8) | \ + (((_Port) & 0xf) << 4) | \ + ((_Index) & 0xf) )) + #define MESSAGING_DEVICE_PATH 0x03 #define MSG_ATAPI_DP 0x01 diff --git a/usr/src/boot/efi/loader/main.c b/usr/src/boot/efi/loader/main.c index 82b0936cde..9c3a6a2c46 100644 --- a/usr/src/boot/efi/loader/main.c +++ b/usr/src/boot/efi/loader/main.c @@ -61,6 +61,7 @@ EFI_GUID imgid = LOADED_IMAGE_PROTOCOL; EFI_GUID smbios = SMBIOS_TABLE_GUID; EFI_GUID smbios3 = SMBIOS3_TABLE_GUID; EFI_GUID inputid = SIMPLE_TEXT_INPUT_PROTOCOL; +EFI_GUID serialio = SERIAL_IO_PROTOCOL; extern void acpi_detect(void); extern void efi_getsmap(void); @@ -489,12 +490,214 @@ interactive_interrupt(const char *msg) return (false); } +static void +setenv_int(const char *key, int val) +{ + char buf[20]; + + (void) snprintf(buf, sizeof (buf), "%d", val); + (void) setenv(key, buf, 1); +} + +/* + * 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 = false; + + how = 0; + sz = sizeof (buf); + rv = efi_global_getenv("ConOut", buf, &sz); + if (rv != EFI_SUCCESS) + rv = efi_global_getenv("ConOutDev", buf, &sz); + if (rv != EFI_SUCCESS) { + /* + * If we don't have any ConOut default to video. + * non-server systems may not have serial. + */ + goto out; + } + ep = buf + sz; + node = (EFI_DEVICE_PATH *)buf; + while ((char *)node < ep) { + if (IsDevicePathEndType(node)) { + if (pci_pending && vid_seen == 0) + vid_seen = ++seen; + } + pci_pending = false; + if (DevicePathType(node) == ACPI_DEVICE_PATH && + (DevicePathSubType(node) == ACPI_DP || + DevicePathSubType(node) == ACPI_EXTENDED_DP)) { + /* Check for Serial node */ + acpi = (void *)node; + if (EISA_ID_TO_NUM(acpi->HID) == 0x501) { + setenv_int("efi_8250_uid", acpi->UID); + com_seen = ++seen; + } + } else if (DevicePathType(node) == MESSAGING_DEVICE_PATH && + DevicePathSubType(node) == MSG_UART_DP) { + com_seen = ++seen; + uart = (void *)node; + setenv_int("efi_com_speed", 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 */ + } + + /* + * 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); +} + caddr_t ptov(uintptr_t x) { return ((caddr_t)x); } +static int +efi_serial_get_uid(EFI_DEVICE_PATH *devpath) +{ + ACPI_HID_DEVICE_PATH *acpi; + + while (!IsDevicePathEnd(devpath)) { + if (DevicePathType(devpath) == ACPI_DEVICE_PATH && + (DevicePathSubType(devpath) == ACPI_DP || + DevicePathSubType(devpath) == ACPI_EXTENDED_DP)) { + acpi = (ACPI_HID_DEVICE_PATH *)devpath; + if (EISA_ID_TO_NUM(acpi->HID) == 0x501) { + return (acpi->UID); + } + } + + devpath = NextDevicePathNode(devpath); + } + return (-1); +} + +/* + * Walk serialio protocol handle array and find index for serial console + * device. The problem is, we check for acpi UID value, but we can not be sure, + * if it will start from 0 or 1. + */ +static const char * +uefi_serial_console(void) +{ + UINTN bufsz; + EFI_STATUS status; + EFI_HANDLE *handles; + int i, nhandles; + unsigned long uid, lowest; + char *env, *ep; + extern struct console ttya; + extern struct console ttyb; + extern struct console ttyc; + extern struct console ttyd; + + env = getenv("efi_8250_uid"); + if (env == NULL) + return (NULL); + (void) unsetenv("efi_8250_uid"); + errno = 0; + uid = strtoul(env, &ep, 10); + if (errno != 0 || *ep != '\0') + return (NULL); + + /* if uid is 0, this is first serial port */ + if (uid == 0) + return (ttya.c_name); + + /* + * get buffer size + */ + bufsz = 0; + handles = NULL; + status = BS->LocateHandle(ByProtocol, &serialio, NULL, &bufsz, handles); + if (status != EFI_BUFFER_TOO_SMALL) + return (NULL); + if ((handles = malloc(bufsz)) == NULL) + return (NULL); + + /* + * get handle array + */ + status = BS->LocateHandle(ByProtocol, &serialio, NULL, &bufsz, handles); + if (EFI_ERROR(status)) { + free(handles); + return (NULL); + } + nhandles = (int)(bufsz / sizeof (EFI_HANDLE)); + + lowest = 255; /* high enough value */ + for (i = 0; i < nhandles; i++) { + EFI_DEVICE_PATH *devpath; + unsigned long _uid; + + devpath = efi_lookup_devpath(handles[i]); + _uid = efi_serial_get_uid(devpath); + if (_uid < lowest) + lowest = _uid; + } + free(handles); + switch (uid - lowest) { + case 0: + return (ttya.c_name); + case 1: + return (ttyb.c_name); + case 2: + return (ttyc.c_name); + case 3: + return (ttyd.c_name); + } + return (NULL); +} + EFI_STATUS main(int argc, CHAR16 *argv[]) { @@ -504,6 +707,7 @@ main(int argc, CHAR16 *argv[]) void *ptr; bool has_kbd; char *s; + const char *serial; EFI_DEVICE_PATH *imgpath; CHAR16 *text; EFI_STATUS status; @@ -527,25 +731,43 @@ main(int argc, CHAR16 *argv[]) /* Get our loaded image protocol interface structure. */ (void) OpenProtocolByHandle(IH, &imgid, (void **)&img); - /* Init the time source */ - efi_time_init(); - - has_kbd = has_keyboard(); - /* * XXX Chicken-and-egg problem; we want to have console output * early, but some console attributes may depend on reading from * eg. the boot device, which we can't do yet. We can use * printf() etc. once this is done. */ + setenv("console", "text", 1); + howto = parse_uefi_con_out(); + serial = uefi_serial_console(); cons_probe(); efi_getsmap(); + if ((s = getenv("efi_com_speed")) != NULL) { + char *name; + + (void) snprintf(var, sizeof (var), "%s,8,n,1,-", s); + if (asprintf(&name, "%s-mode", serial) > 0) { + (void) setenv (name, var, 1); + free(name); + } + if (asprintf(&name, "%s-spcr-mode", serial) > 0) { + (void) setenv (name, var, 1); + free(name); + } + (void) unsetenv("efi_com_speed"); + } + + /* Init the time source */ + efi_time_init(); + /* * Initialise the block cache. Set the upper limit. */ bcache_init(32768, 512); + has_kbd = has_keyboard(); + /* * Parse the args to set the console settings, etc * iPXE may be setup to pass these in. Or the optional argument in the @@ -558,7 +780,6 @@ main(int argc, CHAR16 *argv[]) * args from UCS-2 to ASCII (16 to 8 bit) as they are copied (though * this method is flawed for non-ASCII characters). */ - howto = 0; for (i = 1; i < argc; i++) { if (argv[i][0] == '-') { for (j = 1; argv[i][j] != 0; j++) { @@ -647,13 +868,15 @@ main(int argc, CHAR16 *argv[]) */ if (howto & RB_MULTIPLE) { if (howto & RB_SERIAL) - setenv("console", "ttya text", 1); + (void) snprintf(var, sizeof (var), "%s text", serial); else - setenv("console", "text ttya", 1); + (void) snprintf(var, sizeof (var), "text %s", serial); } else if (howto & RB_SERIAL) { - setenv("console", "ttya", 1); - } else - setenv("console", "text", 1); + (void) snprintf(var, sizeof (var), "%s", serial); + } else { + (void) snprintf(var, sizeof (var), "text"); + } + (void) setenv("console", var, 1); if ((s = getenv("fail_timeout")) != NULL) fail_timeout = strtol(s, NULL, 10); |