diff --git a/sys/boot/efi/loader/main.c b/sys/boot/efi/loader/main.c index 4c3bc7aa3ef6..361d3bbd473a 100644 --- a/sys/boot/efi/loader/main.c +++ b/sys/boot/efi/loader/main.c @@ -66,6 +66,7 @@ EFI_GUID hoblist = HOB_LIST_TABLE_GUID; EFI_GUID memtype = MEMORY_TYPE_INFORMATION_TABLE_GUID; EFI_GUID debugimg = DEBUG_IMAGE_INFO_TABLE_GUID; EFI_GUID fdtdtb = FDT_TABLE_GUID; +EFI_GUID inputid = SIMPLE_TEXT_INPUT_PROTOCOL; #ifdef EFI_ZFS_BOOT static void efi_zfs_probe(void); @@ -94,6 +95,88 @@ cp16to8(const CHAR16 *src, char *dst, size_t len) dst[i] = (char)src[i]; } +static int +has_keyboard(void) +{ + EFI_STATUS status; + EFI_DEVICE_PATH *path; + EFI_HANDLE *hin, *hin_end, *walker; + UINTN sz; + int retval = 0; + + /* + * Find all the handles that support the SIMPLE_TEXT_INPUT_PROTOCOL and + * do the typical dance to get the right sized buffer. + */ + sz = 0; + hin = NULL; + status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz, 0); + if (status == EFI_BUFFER_TOO_SMALL) { + hin = (EFI_HANDLE *)malloc(sz); + status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz, + hin); + if (EFI_ERROR(status)) + free(hin); + } + if (EFI_ERROR(status)) + return retval; + + /* + * Look at each of the handles. If it supports the device path protocol, + * use it to get the device path for this handle. Then see if that + * device path matches either the USB device path for keyboards or the + * legacy device path for keyboards. + */ + hin_end = &hin[sz / sizeof(*hin)]; + for (walker = hin; walker < hin_end; walker++) { + status = BS->HandleProtocol(*walker, &devid, (VOID **)&path); + if (EFI_ERROR(status)) + continue; + + while (!IsDevicePathEnd(path)) { + /* + * Check for the ACPI keyboard node. All PNP3xx nodes + * are keyboards of different flavors. Note: It is + * unclear of there's always a keyboard node when + * there's a keyboard controller, or if there's only one + * when a keyboard is detected at boot. + */ + if (DevicePathType(path) == ACPI_DEVICE_PATH && + (DevicePathSubType(path) == ACPI_DP || + DevicePathSubType(path) == ACPI_EXTENDED_DP)) { + ACPI_HID_DEVICE_PATH *acpi; + + acpi = (ACPI_HID_DEVICE_PATH *)(void *)path; + if ((EISA_ID_TO_NUM(acpi->HID) & 0xff00) == 0x300 && + (acpi->HID & 0xffff) == PNP_EISA_ID_CONST) { + retval = 1; + goto out; + } + /* + * Check for USB keyboard node, if present. Unlike a + * PS/2 keyboard, these definitely only appear when + * connected to the system. + */ + } else if (DevicePathType(path) == MESSAGING_DEVICE_PATH && + DevicePathSubType(path) == MSG_USB_CLASS_DP) { + USB_CLASS_DEVICE_PATH *usb; + + usb = (USB_CLASS_DEVICE_PATH *)(void *)path; + if (usb->DeviceClass == 3 && /* HID */ + usb->DeviceSubClass == 1 && /* Boot devices */ + usb->DeviceProtocol == 1) { /* Boot keyboards */ + retval = 1; + goto out; + } + } + path = NextDevicePathNode(path); + } + } +out: + free(hin); + return retval; +} + EFI_STATUS main(int argc, CHAR16 *argv[]) { @@ -104,6 +187,7 @@ main(int argc, CHAR16 *argv[]) struct devsw *dev; uint64_t pool_guid; UINTN k; + int has_kbd; archsw.arch_autoload = efi_autoload; archsw.arch_getdev = efi_getdev; @@ -115,6 +199,8 @@ main(int argc, CHAR16 *argv[]) archsw.arch_zfs_probe = efi_zfs_probe; #endif + 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 @@ -150,15 +236,19 @@ main(int argc, CHAR16 *argv[]) case 'D': howto |= RB_MULTIPLE; break; - case 'm': - howto |= RB_MUTE; - break; case 'h': howto |= RB_SERIAL; break; + case 'm': + howto |= RB_MUTE; + break; case 'p': howto |= RB_PAUSE; break; + case 'P': + if (!has_kbd) + howto |= RB_SERIAL | RB_MULTIPLE; + break; case 'r': howto |= RB_DFLTROOT; break;