[U-Boot] [PATCH 0/8] efi_loader: EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL

Fix bugs for the EFI_SIMPLE_TEXT_INPUT__PROTOCOL. Implement the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL. Provide a unit test. Fix a U-Boot binary size problem.
Heinrich Schuchardt (8): test/py: rework test_efi_selftest_text_input() efi_loader: console input ESC a - ESC z efi_loader: EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL efi_selftest: test EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL test/py: test EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL efi_loader: implement key notify functions efi_selftest: test key notification functions efi_loader: unset CONFIG_EFI_UNICODE_CAPITALIZATION
configs/vf610twr_defconfig | 1 + configs/vf610twr_nand_defconfig | 1 + include/efi_api.h | 56 +++ lib/efi_loader/efi_console.c | 355 ++++++++++++++++++-- lib/efi_selftest/Makefile | 1 + lib/efi_selftest/efi_selftest_textinputex.c | 198 +++++++++++ test/py/tests/test_efi_selftest.py | 96 +++++- 7 files changed, 678 insertions(+), 30 deletions(-) create mode 100644 lib/efi_selftest/efi_selftest_textinputex.c

Use more precise regular expressions.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- test/py/tests/test_efi_selftest.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/test/py/tests/test_efi_selftest.py b/test/py/tests/test_efi_selftest.py index 5d0dba6f4e..3e70abdafc 100644 --- a/test/py/tests/test_efi_selftest.py +++ b/test/py/tests/test_efi_selftest.py @@ -68,42 +68,47 @@ def test_efi_selftest_text_input(u_boot_console): # EOT u_boot_console.run_command(cmd=chr(4), wait_for_echo=False, send_nl=False, wait_for_prompt=False) - m = u_boot_console.p.expect(['Unicode char 4']) + m = u_boot_console.p.expect( + ['Unicode char 4 (unknown), scan code 0 (Null)']) if m != 0: raise Exception('EOT failed in 'text input' test') u_boot_console.drain_console() # BS u_boot_console.run_command(cmd=chr(8), wait_for_echo=False, send_nl=False, wait_for_prompt=False) - m = u_boot_console.p.expect(['(BS)']) + m = u_boot_console.p.expect( + ['Unicode char 8 (BS), scan code 0 (Null)']) if m != 0: raise Exception('BS failed in 'text input' test') u_boot_console.drain_console() # TAB u_boot_console.run_command(cmd=chr(9), wait_for_echo=False, send_nl=False, wait_for_prompt=False) - m = u_boot_console.p.expect(['(TAB)']) + m = u_boot_console.p.expect( + ['Unicode char 9 (TAB), scan code 0 (Null)']) if m != 0: raise Exception('BS failed in 'text input' test') u_boot_console.drain_console() # a u_boot_console.run_command(cmd='a', wait_for_echo=False, send_nl=False, wait_for_prompt=False) - m = u_boot_console.p.expect(['('a')']) + m = u_boot_console.p.expect( + ['Unicode char 97 ('a'), scan code 0 (Null)']) if m != 0: raise Exception(''a' failed in 'text input' test') u_boot_console.drain_console() # UP escape sequence u_boot_console.run_command(cmd=chr(27) + '[A', wait_for_echo=False, send_nl=False, wait_for_prompt=False) - m = u_boot_console.p.expect(['(Up)']) + m = u_boot_console.p.expect( + ['Unicode char 0 (Null), scan code 1 (Up)']) if m != 0: raise Exception('UP failed in 'text input' test') u_boot_console.drain_console() # Euro sign u_boot_console.run_command(cmd='\xe2\x82\xac', wait_for_echo=False, send_nl=False, wait_for_prompt=False) - m = u_boot_console.p.expect(['Unicode char 8364']) + m = u_boot_console.p.expect(['Unicode char 8364 ('']) if m != 0: raise Exception('Euro sign failed in 'text input' test') u_boot_console.drain_console()

We have foreseen a work around for entering control codes in the EFI_SIMPLE_TEXT_INPUT_PROTOCOL. But currently we have an offset of one.
ESC a should translate to 0x01 (CTRL+a). ESC z should translate to 0x1a (CTRL+z).
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- lib/efi_loader/efi_console.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index ee84640b7c..ca68c8fc1e 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -479,7 +479,12 @@ static efi_status_t efi_cin_read_key(struct efi_input_key *key) pressed_key.scan_code = ch - 'P' + 11; break; case 'a'...'z': - ch = ch - 'a'; + /* + * Workaround for entering CTRL+a (0x01) - CTRL+z (0x1a) + * as escape sequence if the terminal does not allow + * direct entry. These are not Xterm control sequences. + */ + ch = ch - 'a' + 1; break; case '[': ch = getc();

This patch implements the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
The implementation of notification functions is postponed to a later patch.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- include/efi_api.h | 56 ++++++++ lib/efi_loader/efi_console.c | 251 ++++++++++++++++++++++++++++++++--- 2 files changed, 285 insertions(+), 22 deletions(-)
diff --git a/include/efi_api.h b/include/efi_api.h index ae840d1bb4..5004f520ff 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -588,11 +588,67 @@ struct efi_simple_text_output_protocol { struct simple_text_output_mode *mode; };
+#define EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID \ + EFI_GUID(0xdd9e7534, 0x7762, 0x4698, \ + 0x8c, 0x14, 0xf5, 0x85, 0x17, 0xa6, 0x25, 0xaa) + struct efi_input_key { u16 scan_code; s16 unicode_char; };
+#define EFI_SHIFT_STATE_INVALID 0x00000000 +#define EFI_RIGHT_SHIFT_PRESSED 0x00000001 +#define EFI_LEFT_SHIFT_PRESSED 0x00000002 +#define EFI_RIGHT_CONTROL_PRESSED 0x00000004 +#define EFI_LEFT_CONTROL_PRESSED 0x00000008 +#define EFI_RIGHT_ALT_PRESSED 0x00000010 +#define EFI_LEFT_ALT_PRESSED 0x00000020 +#define EFI_RIGHT_LOGO_PRESSED 0x00000040 +#define EFI_LEFT_LOGO_PRESSED 0x00000080 +#define EFI_MENU_KEY_PRESSED 0x00000100 +#define EFI_SYS_REQ_PRESSED 0x00000200 +#define EFI_SHIFT_STATE_VALID 0x80000000 + +#define EFI_TOGGLE_STATE_INVALID 0x00 +#define EFI_SCROLL_LOCK_ACTIVE 0x01 +#define EFI_NUM_LOCK_ACTIVE 0x02 +#define EFI_CAPS_LOCK_ACTIVE 0x04 +#define EFI_KEY_STATE_EXPOSED 0x40 +#define EFI_TOGGLE_STATE_VALID 0x80 + +struct efi_key_state { + u32 key_shift_state; + u8 key_toggle_state; +}; + +struct efi_key_data { + struct efi_input_key key; + struct efi_key_state key_state; +}; + +struct efi_simple_text_input_ex_protocol { + efi_status_t (EFIAPI *reset) ( + struct efi_simple_text_input_ex_protocol *this, + bool extended_verification); + efi_status_t (EFIAPI *read_key_stroke_ex) ( + struct efi_simple_text_input_ex_protocol *this, + struct efi_key_data *key_data); + struct efi_event *wait_for_key_ex; + efi_status_t (EFIAPI *set_state) ( + struct efi_simple_text_input_ex_protocol *this, + u8 key_toggle_state); + efi_status_t (EFIAPI *register_key_notify) ( + struct efi_simple_text_input_ex_protocol *this, + struct efi_key_data *key_data, + efi_status_t (EFIAPI *key_notify_function)( + struct efi_key_data *key_data), + void **notify_handle); + efi_status_t (EFIAPI *unregister_key_notify) ( + struct efi_simple_text_input_ex_protocol *this, + void *notification_handle); +}; + #define EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID \ EFI_GUID(0x387477c1, 0x69c7, 0x11d2, \ 0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b) diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index ca68c8fc1e..32f8ca4480 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -48,10 +48,12 @@ static struct cout_mode efi_cout_modes[] = { }, };
-const efi_guid_t efi_guid_text_output_protocol = - EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID; +const efi_guid_t efi_guid_text_input_ex_protocol = + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID; const efi_guid_t efi_guid_text_input_protocol = EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID; +const efi_guid_t efi_guid_text_output_protocol = + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID;
#define cESC '\x1b' #define ESC "\x1b" @@ -397,19 +399,19 @@ struct efi_simple_text_output_protocol efi_con_out = { };
static bool key_available; -static struct efi_input_key next_key; +static struct efi_key_data next_key;
/** - * skip_modifiers() - analyze modifiers (shift, alt, ctrl) for function keys + * analyze_modifiers() - analyze modifiers (shift, alt, ctrl) for function keys * * This gets called when we have already parsed CSI. * * @modifiers: bitmask (shift, alt, ctrl) * @return: the unmodified code */ -static char skip_modifiers(int *modifiers) +static int analyze_modifiers(struct efi_key_state *key_state) { - char c, mod = 0, ret = 0; + int c, mod = 0, ret = 0;
c = getc();
@@ -435,8 +437,17 @@ static char skip_modifiers(int *modifiers) out: if (mod) --mod; - if (modifiers) - *modifiers = mod; + key_state->key_shift_state = EFI_SHIFT_STATE_VALID; + if (mod) { + if (mod & 1) + key_state->key_shift_state |= EFI_LEFT_SHIFT_PRESSED; + if (mod & 2) + key_state->key_shift_state |= EFI_LEFT_ALT_PRESSED; + if (mod & 4) + key_state->key_shift_state |= EFI_LEFT_CONTROL_PRESSED; + if (mod & 8) + key_state->key_shift_state |= EFI_LEFT_LOGO_PRESSED; + } if (!ret) ret = c; return ret; @@ -448,7 +459,7 @@ out: * @key: - key received * Return: - status code */ -static efi_status_t efi_cin_read_key(struct efi_input_key *key) +static efi_status_t efi_cin_read_key(struct efi_key_data *key) { struct efi_input_key pressed_key = { .scan_code = 0, @@ -460,6 +471,8 @@ static efi_status_t efi_cin_read_key(struct efi_input_key *key) /* No key pressed */ goto error;
+ key->key_state.key_shift_state = EFI_SHIFT_STATE_INVALID; + key->key_state.key_toggle_state = EFI_TOGGLE_STATE_INVALID; ch = getc(); if (ch == cESC) { /* @@ -499,7 +512,7 @@ static efi_status_t efi_cin_read_key(struct efi_input_key *key) pressed_key.scan_code = 5; break; case '1': - ch = skip_modifiers(NULL); + ch = analyze_modifiers(&key->key_state); switch (ch) { case '1'...'5': /* F1 - F5 */ pressed_key.scan_code = ch - '1' + 11; @@ -519,7 +532,7 @@ static efi_status_t efi_cin_read_key(struct efi_input_key *key) } break; case '2': - ch = skip_modifiers(NULL); + ch = analyze_modifiers(&key->key_state); switch (ch) { case '0'...'1': /* F9 - F10 */ pressed_key.scan_code = ch - '0' + 19; @@ -534,15 +547,15 @@ static efi_status_t efi_cin_read_key(struct efi_input_key *key) break; case '3': /* DEL */ pressed_key.scan_code = 8; - skip_modifiers(NULL); + analyze_modifiers(&key->key_state); break; case '5': /* PG UP */ pressed_key.scan_code = 9; - skip_modifiers(NULL); + analyze_modifiers(&key->key_state); break; case '6': /* PG DOWN */ pressed_key.scan_code = 10; - skip_modifiers(NULL); + analyze_modifiers(&key->key_state); break; } break; @@ -600,9 +613,28 @@ static efi_status_t efi_cin_read_key(struct efi_input_key *key) } else if (ch >= 0x80) { goto error; } - if (!pressed_key.scan_code) + if (pressed_key.scan_code) { + key->key_state.key_shift_state |= EFI_SHIFT_STATE_VALID; + } else { pressed_key.unicode_char = ch; - *key = pressed_key; + + /* + * Assume left control key for control characters typically + * entered using the control key. + */ + if (ch >= 0x01 && ch <= 0x1f) { + key->key_state.key_shift_state = + EFI_SHIFT_STATE_VALID; + switch (ch) { + case 0x01 ... 0x07: + case 0x0b: + case 0x0e ... 0x1f: + key->key_state.key_shift_state |= + EFI_LEFT_CONTROL_PRESSED; + } + } + } + key->key = pressed_key;
return EFI_SUCCESS; error: @@ -648,6 +680,170 @@ static void efi_set_keymap(void) } }
+/** + * efi_cin_empty_buffer() - empty input buffer + */ +static void efi_cin_empty_buffer(void) +{ + while (tstc()) + getc(); + key_available = false; +} + +/** + * efi_cin_reset_ex() - reset console input + * + * @this: - the extended simple text input protocol + * @extended_verification: - extended verification + * + * This function implements the reset service of the + * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * Return: old value of the task priority level + */ +static efi_status_t EFIAPI efi_cin_reset_ex( + struct efi_simple_text_input_ex_protocol *this, + bool extended_verification) +{ + efi_status_t ret = EFI_SUCCESS; + + EFI_ENTRY("%p, %d", this, extended_verification); + + /* Check parameters */ + if (!this) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + efi_cin_empty_buffer(); +out: + return EFI_EXIT(ret); +} + +/** + * efi_cin_read_key_stroke_ex() - read key stroke + * + * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL + * @key_data: key read from console + * Return: status code + * + * This function implements the ReadKeyStrokeEx service of the + * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + */ +static efi_status_t EFIAPI efi_cin_read_key_stroke_ex( + struct efi_simple_text_input_ex_protocol *this, + struct efi_key_data *key_data) +{ + efi_status_t ret = EFI_SUCCESS; + + EFI_ENTRY("%p, %p", this, key_data); + + /* Check parameters */ + if (!this || !key_data) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + /* We don't do interrupts, so check for timers cooperatively */ + efi_timer_check(); + + /* Enable console input after ExitBootServices */ + efi_cin_check(); + + if (!key_available) { + ret = EFI_NOT_READY; + goto out; + } + *key_data = next_key; + key_available = false; + efi_con_in.wait_for_key->is_signaled = false; +out: + return EFI_EXIT(ret); +} + +/** + * efi_cin_set_state() - set toggle key state + * + * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL + * @key_toggle_state: key toggle state + * Return: status code + * + * This function implements the SetState service of the + * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + */ +static efi_status_t EFIAPI efi_cin_set_state( + struct efi_simple_text_input_ex_protocol *this, + u8 key_toggle_state) +{ + EFI_ENTRY("%p, %u", this, key_toggle_state); + /* + * U-Boot supports multiple console input sources like serial and + * net console for which a key toggle state cannot be set at all. + * + * According to the UEFI specification it is allowable to not implement + * this service. + */ + return EFI_EXIT(EFI_UNSUPPORTED); +} + +/** + * efi_cin_register_key_notify() - register key notification function + * + * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL + * @key_data: key to be notified + * @key_notify_function: function to be called if the key is pressed + * @notify_handle: handle for unregistering the notification + * Return: status code + * + * This function implements the SetState service of the + * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + */ +static efi_status_t EFIAPI efi_cin_register_key_notify( + struct efi_simple_text_input_ex_protocol *this, + struct efi_key_data *key_data, + efi_status_t (EFIAPI *key_notify_function)( + struct efi_key_data *key_data), + void **notify_handle) +{ + EFI_ENTRY("%p, %p, %p, %p", + this, key_data, key_notify_function, notify_handle); + return EFI_EXIT(EFI_OUT_OF_RESOURCES); +} + +/** + * efi_cin_unregister_key_notify() - unregister key notification function + * + * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL + * @notification_handle: handle received when registering + * Return: status code + * + * This function implements the SetState service of the + * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + */ +static efi_status_t EFIAPI efi_cin_unregister_key_notify( + struct efi_simple_text_input_ex_protocol *this, + void *notification_handle) +{ + EFI_ENTRY("%p, %p", this, notification_handle); + return EFI_EXIT(EFI_INVALID_PARAMETER); +} + + /** * efi_cin_reset() - drain the input buffer * @@ -675,16 +871,13 @@ static efi_status_t EFIAPI efi_cin_reset goto out; }
- /* Empty input buffer */ - while (tstc()) - getc(); - key_available = false; + efi_cin_empty_buffer(); out: return EFI_EXIT(ret); }
/** - * efi_cin_reset() - drain the input buffer + * efi_cin_read_key_stroke() - read key stroke * * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL * @key: key read from console @@ -720,13 +913,22 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke ret = EFI_NOT_READY; goto out; } - *key = next_key; + *key = next_key.key; key_available = false; efi_con_in.wait_for_key->is_signaled = false; out: return EFI_EXIT(ret); }
+static struct efi_simple_text_input_ex_protocol efi_con_in_ex = { + .reset = efi_cin_reset_ex, + .read_key_stroke_ex = efi_cin_read_key_stroke_ex, + .wait_for_key_ex = NULL, + .set_state = efi_cin_set_state, + .register_key_notify = efi_cin_register_key_notify, + .unregister_key_notify = efi_cin_unregister_key_notify, +}; + struct efi_simple_text_input_protocol efi_con_in = { .reset = efi_cin_reset, .read_key_stroke = efi_cin_read_key_stroke, @@ -800,6 +1002,10 @@ int efi_console_register(void) if (r != EFI_SUCCESS) goto out_of_memory; systab.con_in_handle = efi_console_input_obj->handle; + r = efi_add_protocol(efi_console_input_obj->handle, + &efi_guid_text_input_ex_protocol, &efi_con_in_ex); + if (r != EFI_SUCCESS) + goto out_of_memory;
/* Create console events */ r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK, efi_key_notify, @@ -808,6 +1014,7 @@ int efi_console_register(void) printf("ERROR: Failed to register WaitForKey event\n"); return r; } + efi_con_in_ex.wait_for_key_ex = efi_con_in.wait_for_key; r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, efi_console_timer_notify, NULL, NULL, &console_timer_event);

Provide a unit test for the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- lib/efi_selftest/Makefile | 1 + lib/efi_selftest/efi_selftest_textinputex.c | 139 ++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 lib/efi_selftest/efi_selftest_textinputex.c
diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index 80bc5d8a64..2f55d9d66f 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -29,6 +29,7 @@ efi_selftest_manageprotocols.o \ efi_selftest_rtc.o \ efi_selftest_snp.o \ efi_selftest_textinput.o \ +efi_selftest_textinputex.o \ efi_selftest_textoutput.o \ efi_selftest_tpl.o \ efi_selftest_unicode_collation.o \ diff --git a/lib/efi_selftest/efi_selftest_textinputex.c b/lib/efi_selftest/efi_selftest_textinputex.c new file mode 100644 index 0000000000..935bf065c4 --- /dev/null +++ b/lib/efi_selftest/efi_selftest_textinputex.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * efi_selftest_textinput + * + * Copyright (c) 2018 Heinrich Schuchardt xypron.glpk@gmx.de + * + * Provides a unit test for the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL. + * The unicode character and the scan code are printed for text + * input. To run the test: + * + * setenv efi_selftest extended text input + * bootefi selftest + */ + +#include <efi_selftest.h> + +static const efi_guid_t text_input_ex_protocol_guid = + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID; + +static struct efi_simple_text_input_ex_protocol *con_in_ex; + +static struct efi_boot_services *boottime; + +/* + * Setup unit test. + * + * @handle: handle of the loaded image + * @systable: system table + * @return: EFI_ST_SUCCESS for success + */ +static int setup(const efi_handle_t handle, + const struct efi_system_table *systable) +{ + efi_status_t ret; + + boottime = systable->boottime; + + ret = boottime->locate_protocol(&text_input_ex_protocol_guid, NULL, + (void **)&con_in_ex); + if (ret != EFI_SUCCESS) { + con_in_ex = NULL; + efi_st_error + ("Extended text input protocol is not available.\n"); + return EFI_ST_FAILURE; + } + + return EFI_ST_SUCCESS; +} + +/* + * Execute unit test. + * + * @return: EFI_ST_SUCCESS for success + */ +static int execute(void) +{ + struct efi_key_data input_key = {0,}; + efi_status_t ret; + efi_uintn_t index; + + if (!con_in_ex) { + efi_st_printf("Setup failed\n"); + return EFI_ST_FAILURE; + } + + /* Drain the console input */ + ret = con_in_ex->reset(con_in_ex, true); + if (ret != EFI_SUCCESS) { + efi_st_error("Reset failed\n"); + return EFI_ST_FAILURE; + } + ret = con_in_ex->read_key_stroke_ex(con_in_ex, &input_key); + if (ret != EFI_NOT_READY) { + efi_st_error("Empty buffer not reported\n"); + return EFI_ST_FAILURE; + } + + efi_st_printf("Waiting for your input\n"); + efi_st_printf("To terminate type 'x'\n"); + + for (;;) { + /* Wait for next key */ + ret = boottime->wait_for_event(1, &con_in_ex->wait_for_key_ex, + &index); + if (ret != EFI_ST_SUCCESS) { + efi_st_error("WaitForEvent failed\n"); + return EFI_ST_FAILURE; + } + ret = con_in_ex->read_key_stroke_ex(con_in_ex, &input_key); + if (ret != EFI_SUCCESS) { + efi_st_error("ReadKeyStroke failed\n"); + return EFI_ST_FAILURE; + } + + /* Allow 5 minutes until time out */ + boottime->set_watchdog_timer(300, 0, 0, NULL); + + efi_st_printf("Unicode char %u (%ps), scan code %u (", + (unsigned int)input_key.key.unicode_char, + efi_st_translate_char(input_key.key.unicode_char), + (unsigned int)input_key.key.scan_code); + if (input_key.key_state.key_shift_state & + EFI_SHIFT_STATE_VALID) { + if (input_key.key_state.key_shift_state & + (EFI_LEFT_SHIFT_PRESSED | EFI_RIGHT_SHIFT_PRESSED)) + efi_st_printf("SHIFT+"); + if (input_key.key_state.key_shift_state & + (EFI_LEFT_ALT_PRESSED | EFI_RIGHT_ALT_PRESSED)) + efi_st_printf("ALT+"); + if (input_key.key_state.key_shift_state & + (EFI_LEFT_CONTROL_PRESSED | + EFI_RIGHT_CONTROL_PRESSED)) + efi_st_printf("CTRL+"); + if (input_key.key_state.key_shift_state & + (EFI_LEFT_LOGO_PRESSED | EFI_RIGHT_LOGO_PRESSED)) + efi_st_printf("META+"); + if (input_key.key_state.key_shift_state == + EFI_SHIFT_STATE_VALID) + efi_st_printf("+"); + } + + efi_st_printf("%ps)\n", + efi_st_translate_code(input_key.key.scan_code)); + + switch (input_key.key.unicode_char) { + case 'x': + case 'X': + return EFI_ST_SUCCESS; + } + } +} + +EFI_UNIT_TEST(textinputex) = { + .name = "extended text input", + .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, + .setup = setup, + .execute = execute, + .on_request = true, +};

Add a unit test for the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- test/py/tests/test_efi_selftest.py | 79 ++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+)
diff --git a/test/py/tests/test_efi_selftest.py b/test/py/tests/test_efi_selftest.py index 3e70abdafc..f84aa4d706 100644 --- a/test/py/tests/test_efi_selftest.py +++ b/test/py/tests/test_efi_selftest.py @@ -118,3 +118,82 @@ def test_efi_selftest_text_input(u_boot_console): if m != 0: raise Exception('Failures occurred during the EFI selftest') u_boot_console.restart_uboot(); + +@pytest.mark.buildconfigspec('cmd_bootefi_selftest') +def test_efi_selftest_text_input_ex(u_boot_console): + """Test the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL + + :param u_boot_console: U-Boot console + + This function calls the extended text input EFI selftest. + """ + u_boot_console.run_command(cmd='setenv efi_selftest extended text input') + output = u_boot_console.run_command(cmd='bootefi selftest', + wait_for_prompt=False) + m = u_boot_console.p.expect(['To terminate type 'x'']) + if m != 0: + raise Exception('No prompt for 'text input' test') + u_boot_console.drain_console() + u_boot_console.p.timeout = 500 + # EOT + u_boot_console.run_command(cmd=chr(4), wait_for_echo=False, + send_nl=False, wait_for_prompt=False) + m = u_boot_console.p.expect( + ['Unicode char 4 (unknown), scan code 0 (CTRL+Null)']) + if m != 0: + raise Exception('EOT failed in 'text input' test') + u_boot_console.drain_console() + # BS + u_boot_console.run_command(cmd=chr(8), wait_for_echo=False, + send_nl=False, wait_for_prompt=False) + m = u_boot_console.p.expect( + ['Unicode char 8 (BS), scan code 0 (+Null)']) + if m != 0: + raise Exception('BS failed in 'text input' test') + u_boot_console.drain_console() + # TAB + u_boot_console.run_command(cmd=chr(9), wait_for_echo=False, + send_nl=False, wait_for_prompt=False) + m = u_boot_console.p.expect( + ['Unicode char 9 (TAB), scan code 0 (+Null)']) + if m != 0: + raise Exception('TAB failed in 'text input' test') + u_boot_console.drain_console() + # a + u_boot_console.run_command(cmd='a', wait_for_echo=False, send_nl=False, + wait_for_prompt=False) + m = u_boot_console.p.expect( + ['Unicode char 97 ('a'), scan code 0 (Null)']) + if m != 0: + raise Exception(''a' failed in 'text input' test') + u_boot_console.drain_console() + # UP escape sequence + u_boot_console.run_command(cmd=chr(27) + '[A', wait_for_echo=False, + send_nl=False, wait_for_prompt=False) + m = u_boot_console.p.expect( + ['Unicode char 0 (Null), scan code 1 (+Up)']) + if m != 0: + raise Exception('UP failed in 'text input' test') + u_boot_console.drain_console() + # Euro sign + u_boot_console.run_command(cmd='\xe2\x82\xac', wait_for_echo=False, + send_nl=False, wait_for_prompt=False) + m = u_boot_console.p.expect(['Unicode char 8364 ('']) + if m != 0: + raise Exception('Euro sign failed in 'text input' test') + u_boot_console.drain_console() + # SHIFT+ALT+FN 5 + u_boot_console.run_command(cmd='\x1b\x5b\x31\x35\x3b\x34\x7e', + wait_for_echo=False, send_nl=False, + wait_for_prompt=False) + m = u_boot_console.p.expect( + ['Unicode char 0 (Null), scan code 15 (SHIFT+ALT+FN 5)']) + if m != 0: + raise Exception('SHIFT+ALT+FN 5 failed in 'text input' test') + u_boot_console.drain_console() + u_boot_console.run_command(cmd='x', wait_for_echo=False, send_nl=False, + wait_for_prompt=False) + m = u_boot_console.p.expect(['Summary: 0 failures', 'Press any key']) + if m != 0: + raise Exception('Failures occurred during the EFI selftest') + u_boot_console.restart_uboot();

Implement registering and unregistreing key notify functions in the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- lib/efi_loader/efi_console.c | 101 +++++++++++++++++++++++++++++++++-- 1 file changed, 98 insertions(+), 3 deletions(-)
diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index 32f8ca4480..71c54a016f 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -398,8 +398,23 @@ struct efi_simple_text_output_protocol efi_con_out = { .mode = (void*)&efi_con_mode, };
+/** + * struct efi_cin_notify_function - registered console input notify function + * + * @link: link to list + * @data: key to notify + * @function: function to call + */ +struct efi_cin_notify_function { + struct list_head link; + struct efi_key_data key; + efi_status_t (EFIAPI *function) + (struct efi_key_data *key_data); +}; + static bool key_available; static struct efi_key_data next_key; +static LIST_HEAD(cin_notify_functions);
/** * analyze_modifiers() - analyze modifiers (shift, alt, ctrl) for function keys @@ -641,6 +656,34 @@ error: return EFI_NOT_READY; }
+/** + * efi_cin_notify() - notify registered functions + */ +static void efi_cin_notify(void) +{ + struct efi_cin_notify_function *item; + + list_for_each_entry(item, &cin_notify_functions, link) { + bool match = true; + + /* We do not support toggle states */ + if (item->key.key.unicode_char || item->key.key.scan_code) { + if (item->key.key.unicode_char != + next_key.key.unicode_char || + item->key.key.scan_code != next_key.key.scan_code) + match = false; + } + if (item->key.key_state.key_shift_state && + item->key.key_state.key_shift_state != + next_key.key_state.key_shift_state) + match = false; + + if (match) + /* We don't bother about the return code */ + EFI_CALL(item->function(&next_key)); + } +} + /** * efi_cin_check() - check if keyboard input is available */ @@ -658,8 +701,12 @@ static void efi_cin_check(void) if (ret == EFI_SUCCESS) { key_available = true;
+ /* Notify registered functions */ + efi_cin_notify(); + /* Queue the wait for key event */ - efi_signal_event(efi_con_in.wait_for_key, true); + if (key_available) + efi_signal_event(efi_con_in.wait_for_key, true); } } } @@ -817,9 +864,35 @@ static efi_status_t EFIAPI efi_cin_register_key_notify( struct efi_key_data *key_data), void **notify_handle) { + efi_status_t ret = EFI_SUCCESS; + struct efi_cin_notify_function *notify_function; + EFI_ENTRY("%p, %p, %p, %p", this, key_data, key_notify_function, notify_handle); - return EFI_EXIT(EFI_OUT_OF_RESOURCES); + + /* Check parameters */ + if (!this || !key_data || !key_notify_function || !notify_handle) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + EFI_PRINT("u+%04x, sc %04x, sh %08x, tg %02x\n", + key_data->key.unicode_char, + key_data->key.scan_code, + key_data->key_state.key_shift_state, + key_data->key_state.key_toggle_state); + + notify_function = calloc(1, sizeof(struct efi_cin_notify_function)); + if (!notify_function) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + notify_function->key = *key_data; + notify_function->function = key_notify_function; + list_add_tail(¬ify_function->link, &cin_notify_functions); + *notify_handle = notify_function; +out: + return EFI_EXIT(ret); }
/** @@ -839,8 +912,30 @@ static efi_status_t EFIAPI efi_cin_unregister_key_notify( struct efi_simple_text_input_ex_protocol *this, void *notification_handle) { + efi_status_t ret = EFI_INVALID_PARAMETER; + struct efi_cin_notify_function *item, *notify_function = + notification_handle; + EFI_ENTRY("%p, %p", this, notification_handle); - return EFI_EXIT(EFI_INVALID_PARAMETER); + + /* Check parameters */ + if (!this || !notification_handle) + goto out; + + list_for_each_entry(item, &cin_notify_functions, link) { + if (item == notify_function) { + ret = EFI_SUCCESS; + break; + } + } + if (ret != EFI_SUCCESS) + goto out; + + /* Remove the notify function */ + list_del(¬ify_function->link); + free(notify_function); +out: + return EFI_EXIT(ret); }

Use a key notification function to leave the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL test.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- lib/efi_selftest/efi_selftest_textinputex.c | 73 +++++++++++++++++++-- test/py/tests/test_efi_selftest.py | 4 +- 2 files changed, 68 insertions(+), 9 deletions(-)
diff --git a/lib/efi_selftest/efi_selftest_textinputex.c b/lib/efi_selftest/efi_selftest_textinputex.c index 935bf065c4..d20d8ad89d 100644 --- a/lib/efi_selftest/efi_selftest_textinputex.c +++ b/lib/efi_selftest/efi_selftest_textinputex.c @@ -21,6 +21,25 @@ static struct efi_simple_text_input_ex_protocol *con_in_ex;
static struct efi_boot_services *boottime;
+static void *efi_key_notify_handle; +static bool efi_running; + +/** + * efi_key_notify_function() - key notification function + * + * This function is called when the registered key is hit. + * + * @key_data: next key + * Return: status code + */ +static efi_status_t EFIAPI efi_key_notify_function + (struct efi_key_data *key_data) +{ + efi_running = false; + + return EFI_SUCCESS; +} + /* * Setup unit test. * @@ -32,6 +51,17 @@ static int setup(const efi_handle_t handle, const struct efi_system_table *systable) { efi_status_t ret; + struct efi_key_data key_data = { + .key = { + .scan_code = 0, + .unicode_char = 0x18 + }, + .key_state = { + .key_shift_state = EFI_SHIFT_STATE_VALID | + EFI_LEFT_CONTROL_PRESSED, + .key_toggle_state = EFI_TOGGLE_STATE_INVALID, + }, + };
boottime = systable->boottime;
@@ -44,9 +74,41 @@ static int setup(const efi_handle_t handle, return EFI_ST_FAILURE; }
+ ret = con_in_ex->register_key_notify(con_in_ex, &key_data, + efi_key_notify_function, + &efi_key_notify_handle); + if (ret != EFI_SUCCESS) { + efi_key_notify_handle = NULL; + efi_st_error + ("Notify function could not be registered.\n"); + return EFI_ST_FAILURE; + } + efi_running = true; + return EFI_ST_SUCCESS; }
+/* + * Tear down unit test. + * + * Unregister notify function. + * + * @return: EFI_ST_SUCCESS for success + */ +static int teardown(void) +{ + efi_status_t ret; + + ret = con_in_ex->unregister_key_notify + (con_in_ex, efi_key_notify_handle); + if (ret != EFI_SUCCESS) { + efi_st_error + ("Notify function could not be registered.\n"); + return EFI_ST_FAILURE; + } + + return EFI_ST_SUCCESS; +} /* * Execute unit test. * @@ -76,9 +138,9 @@ static int execute(void) }
efi_st_printf("Waiting for your input\n"); - efi_st_printf("To terminate type 'x'\n"); + efi_st_printf("To terminate type 'CTRL+x'\n");
- for (;;) { + while (efi_running) { /* Wait for next key */ ret = boottime->wait_for_event(1, &con_in_ex->wait_for_key_ex, &index); @@ -122,12 +184,8 @@ static int execute(void) efi_st_printf("%ps)\n", efi_st_translate_code(input_key.key.scan_code));
- switch (input_key.key.unicode_char) { - case 'x': - case 'X': - return EFI_ST_SUCCESS; - } } + return EFI_ST_SUCCESS; }
EFI_UNIT_TEST(textinputex) = { @@ -135,5 +193,6 @@ EFI_UNIT_TEST(textinputex) = { .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, .setup = setup, .execute = execute, + .teardown = teardown, .on_request = true, }; diff --git a/test/py/tests/test_efi_selftest.py b/test/py/tests/test_efi_selftest.py index f84aa4d706..e0833ffe22 100644 --- a/test/py/tests/test_efi_selftest.py +++ b/test/py/tests/test_efi_selftest.py @@ -130,7 +130,7 @@ def test_efi_selftest_text_input_ex(u_boot_console): u_boot_console.run_command(cmd='setenv efi_selftest extended text input') output = u_boot_console.run_command(cmd='bootefi selftest', wait_for_prompt=False) - m = u_boot_console.p.expect(['To terminate type 'x'']) + m = u_boot_console.p.expect(['To terminate type 'CTRL+x'']) if m != 0: raise Exception('No prompt for 'text input' test') u_boot_console.drain_console() @@ -191,7 +191,7 @@ def test_efi_selftest_text_input_ex(u_boot_console): if m != 0: raise Exception('SHIFT+ALT+FN 5 failed in 'text input' test') u_boot_console.drain_console() - u_boot_console.run_command(cmd='x', wait_for_echo=False, send_nl=False, + u_boot_console.run_command(cmd=chr(24), wait_for_echo=False, send_nl=False, wait_for_prompt=False) m = u_boot_console.p.expect(['Summary: 0 failures', 'Press any key']) if m != 0:

Unset CONFIG_EFI_UNICODE_CAPITALIZATION on boards with tough size restrictions.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- configs/vf610twr_defconfig | 1 + configs/vf610twr_nand_defconfig | 1 + 2 files changed, 2 insertions(+)
diff --git a/configs/vf610twr_defconfig b/configs/vf610twr_defconfig index 59066a36a9..3f38c8813b 100644 --- a/configs/vf610twr_defconfig +++ b/configs/vf610twr_defconfig @@ -39,3 +39,4 @@ CONFIG_PHY_MICREL=y CONFIG_MII=y CONFIG_DM_SERIAL=y CONFIG_FSL_LPUART=y +# CONFIG_EFI_UNICODE_CAPITALIZATION is not set diff --git a/configs/vf610twr_nand_defconfig b/configs/vf610twr_nand_defconfig index 5b269fdaf6..d6e318f58c 100644 --- a/configs/vf610twr_nand_defconfig +++ b/configs/vf610twr_nand_defconfig @@ -39,3 +39,4 @@ CONFIG_PHY_MICREL=y CONFIG_MII=y CONFIG_DM_SERIAL=y CONFIG_FSL_LPUART=y +# CONFIG_EFI_UNICODE_CAPITALIZATION is not set
participants (1)
-
Heinrich Schuchardt