summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Brown <mcb30@ipxe.org>2021-01-26 20:46:57 +0000
committerMichael Brown <mcb30@ipxe.org>2021-01-26 21:05:37 +0000
commitb5d8ed7e285029d4e91e96122dba076cf9ebeebf (patch)
tree6f0f7153a1722f94b54ffa502a24563d7dcbf951
parentf4c3a01470289d450dbe437fdfb1e85309459374 (diff)
downloadqemu-ipxe-simpletextinputex.tar.gz
[efi] Use EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL if availablesimpletextinputex
The original EFI_SIMPLE_TEXT_INPUT_PROTOCOL is not technically required to handle the use of the Ctrl key, and the long-obsolete EFI 1.10 specification lists only backspace, tab, linefeed, and carriage return as required. Some particularly brain-dead vendor UEFI firmware implementations dutifully put in the extra effort of ensuring that all other control characters (such as Ctrl-C) are impossible to type via EFI_SIMPLE_TEXT_INPUT_PROTOCOL. Current versions of the UEFI specification mandate that the console input handle must support both EFI_SIMPLE_TEXT_INPUT_PROTOCOL and EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL, the latter of which at least provides access to modifier key state. Unlike EFI_SIMPLE_TEXT_INPUT_PROTOCOL, the pointer to the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL instance does not appear within the EFI system table and must therefore be opened explicitly. The UEFI specification provides no safe way to do so, since we cannot open the handle BY_DRIVER or BY_CHILD_CONTROLLER and so nothing guarantees that this pointer will remain valid for the lifetime of iPXE. We must simply hope that no UEFI firmware implementation ever discovers a motivation for reinstalling the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL instance. Use EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL if available, falling back to the existing EFI_SIMPLE_TEXT_PROTOCOL otherwise. Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r--src/interface/efi/efi_console.c77
1 files changed, 66 insertions, 11 deletions
diff --git a/src/interface/efi/efi_console.c b/src/interface/efi/efi_console.c
index 047baed4..98ebbf3a 100644
--- a/src/interface/efi/efi_console.c
+++ b/src/interface/efi/efi_console.c
@@ -54,6 +54,8 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define ATTR_DEFAULT ATTR_FCOL_WHITE
+#define CTRL_MASK 0x1f
+
/* Set default console usage if applicable */
#if ! ( defined ( CONSOLE_EFI ) && CONSOLE_EXPLICIT ( CONSOLE_EFI ) )
#undef CONSOLE_EFI
@@ -67,6 +69,9 @@ static unsigned int efi_attr = ATTR_DEFAULT;
static EFI_CONSOLE_CONTROL_PROTOCOL *conctrl;
EFI_REQUEST_PROTOCOL ( EFI_CONSOLE_CONTROL_PROTOCOL, &conctrl );
+/** Extended simple text input protocol, if present */
+static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *efi_conin_ex;
+
/**
* Handle ANSI CUP (cursor position)
*
@@ -278,8 +283,9 @@ static const char * scancode_to_ansi_seq ( unsigned int scancode ) {
*/
static int efi_getchar ( void ) {
EFI_SIMPLE_TEXT_INPUT_PROTOCOL *conin = efi_systab->ConIn;
+ EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *conin_ex = efi_conin_ex;
const char *ansi_seq;
- EFI_INPUT_KEY key;
+ EFI_KEY_DATA key;
EFI_STATUS efirc;
int rc;
@@ -288,20 +294,42 @@ static int efi_getchar ( void ) {
return *(ansi_input++);
/* Read key from real EFI console */
- if ( ( efirc = conin->ReadKeyStroke ( conin, &key ) ) != 0 ) {
- rc = -EEFI ( efirc );
- DBG ( "EFI could not read keystroke: %s\n", strerror ( rc ) );
- return 0;
+ memset ( &key, 0, sizeof ( key ) );
+ if ( conin_ex ) {
+ if ( ( efirc = conin_ex->ReadKeyStrokeEx ( conin_ex,
+ &key ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBG ( "EFI could not read extended keystroke: %s\n",
+ strerror ( rc ) );
+ return 0;
+ }
+ } else {
+ if ( ( efirc = conin->ReadKeyStroke ( conin,
+ &key.Key ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBG ( "EFI could not read keystroke: %s\n",
+ strerror ( rc ) );
+ return 0;
+ }
+ }
+ DBG2 ( "EFI read key stroke shift %08x toggle %02x unicode %04x "
+ "scancode %04x\n", key.KeyState.KeyShiftState,
+ key.KeyState.KeyToggleState, key.Key.UnicodeChar,
+ key.Key.ScanCode );
+
+ /* Translate Ctrl-<key> */
+ if ( ( key.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID ) &&
+ ( key.KeyState.KeyShiftState & ( EFI_LEFT_CONTROL_PRESSED |
+ EFI_RIGHT_CONTROL_PRESSED ) ) ) {
+ key.Key.UnicodeChar &= CTRL_MASK;
}
- DBG2 ( "EFI read key stroke with unicode %04x scancode %04x\n",
- key.UnicodeChar, key.ScanCode );
/* If key has a Unicode representation, return it */
- if ( key.UnicodeChar )
- return key.UnicodeChar;
+ if ( key.Key.UnicodeChar )
+ return key.Key.UnicodeChar;
/* Otherwise, check for a special key that we know about */
- if ( ( ansi_seq = scancode_to_ansi_seq ( key.ScanCode ) ) ) {
+ if ( ( ansi_seq = scancode_to_ansi_seq ( key.Key.ScanCode ) ) ) {
/* Start of escape sequence: return ESC (0x1b) */
ansi_input = ansi_seq;
return 0x1b;
@@ -319,6 +347,8 @@ 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 */
@@ -326,7 +356,8 @@ static int efi_iskey ( void ) {
return 1;
/* Check to see if the WaitForKey event has fired */
- if ( ( efirc = bs->CheckEvent ( conin->WaitForKey ) ) == 0 )
+ event = ( conin_ex ? conin_ex->WaitForKeyEx : conin->WaitForKey );
+ if ( ( efirc = bs->CheckEvent ( event ) ) == 0 )
return 1;
return 0;
@@ -345,7 +376,14 @@ struct console_driver efi_console __console_driver = {
*
*/
static void efi_console_init ( void ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
EFI_CONSOLE_CONTROL_SCREEN_MODE mode;
+ union {
+ void *interface;
+ EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *wtf;
+ } u;
+ EFI_STATUS efirc;
+ int rc;
/* On some older EFI 1.10 implementations, we must use the
* (now obsolete) EFI_CONSOLE_CONTROL_PROTOCOL to switch the
@@ -358,6 +396,23 @@ static void efi_console_init ( void ) {
EfiConsoleControlScreenText );
}
}
+
+ /* Attempt to open the Simple Text Input Ex protocol on the
+ * console input handle. This is provably unsafe, but is
+ * apparently the expected behaviour for all UEFI
+ * applications. Don't ask.
+ */
+ if ( ( efirc = bs->OpenProtocol ( efi_systab->ConsoleInHandle,
+ &efi_simple_text_input_ex_protocol_guid,
+ &u.interface, efi_image_handle,
+ efi_systab->ConsoleInHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) == 0 ) {
+ efi_conin_ex = u.wtf;
+ DBG ( "EFI using SimpleTextInputEx\n" );
+ } else {
+ rc = -EEFI ( efirc );
+ DBG ( "EFI has no SimpleTextInputEx: %s\n", strerror ( rc ) );
+ }
}
/**