]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[efi] Check only the non-extended WaitForKey event
authorMichael Brown <mcb30@ipxe.org>
Tue, 15 Jul 2025 15:56:11 +0000 (16:56 +0100)
committerMichael Brown <mcb30@ipxe.org>
Wed, 16 Jul 2025 12:20:29 +0000 (13:20 +0100)
The WaitForKeyEx event in EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL is
redundant: by definition it has to signal under exactly the same
conditions as the WaitForKey event in EFI_SIMPLE_TEXT_INPUT_PROTOCOL
and cannot provide any "extended" information since EFI events do not
convey any information beyond their own occurrence.

UEFI keyboard drivers such as Ps2KeyboardDxe and UsbKbDxe invariably
use a single notification function to implement both events.  The
console multiplexer driver ConSplitterDxe uses a single notification
function for both events, which ends up checking only the WaitForKey
event on the underlying console devices.  (Since all console input is
routed through the console multiplexer, this means that in practice
nothing will ever check the underlying devices' WaitForKeyEx events.)

UEFI console consumers such as the UEFI shell tend to use only the
EFI_SIMPLE_TEXT_INPUT_PROTOCOL instance provided as ConIn in the EFI
system table.  With the exception of the UEFI text editor (the "edit"
command in the UEFI shell), almost nothing bothers to open the
EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL instance on the same handle.

The Lenovo ThinkPad T14s Gen 5 has a very peculiar firmware bug.
Enabling the "UEFI Wi-Fi Network Boot" feature in the BIOS setup will
cause the completely unrelated WaitForKeyEx event pointer to be
overwritten with a pointer to a FAT_DIRENT structure representing the
"BOOT" directory in the EFI system partition.  This happens with 100%
repeatability.  It is not necessary to attempt to boot from Wi-Fi: it
is only necessary to have the feature enabled.  The root cause is
unknown, but is presumably an uninitialised pointer or similar
memory-related bug in Lenovo's UEFI Wi-Fi driver.

Work around this Lenovo firmware bug by checking only the WaitForKey
event, ignoring the WaitForKeyEx event even if we will subsequently
use ReadKeyStrokeEx() to read the keypress.  Since almost all other
UEFI console consumers use only WaitForKey, this ensures that we will
be using code paths that the firmware vendor is likely to have tested
at least once.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/interface/efi/efi_console.c

index c5a3f24cafb4efc8a898957039aab58c94ba4e63..e896c5d88ec2568ec225b2d64b55605f1b1cc6d4 100644 (file)
@@ -386,8 +386,6 @@ static int efi_getchar ( void ) {
 static int efi_iskey ( void ) {
        EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
        EFI_SIMPLE_TEXT_INPUT_PROTOCOL *conin = efi_systab->ConIn;
-       EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *conin_ex = efi_conin_ex;
-       EFI_EVENT event;
        EFI_STATUS efirc;
 
        /* If we are mid-sequence, we are always ready */
@@ -395,8 +393,7 @@ static int efi_iskey ( void ) {
                return 1;
 
        /* Check to see if the WaitForKey event has fired */
-       event = ( conin_ex ? conin_ex->WaitForKeyEx : conin->WaitForKey );
-       if ( ( efirc = bs->CheckEvent ( event ) ) == 0 )
+       if ( ( efirc = bs->CheckEvent ( conin->WaitForKey ) ) == 0 )
                return 1;
 
        return 0;