From 29a02185cde20c1504b5b7da075591b605140a62 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 30 Apr 2022 07:42:38 +0200 Subject: [PATCH 01/19] sandbox: enable bootmenu command Provide the bootmenu command on the sandbox for testing. Signed-off-by: Heinrich Schuchardt --- configs/sandbox_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index fe8ea4626d9..c509a924e6b 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -39,6 +39,7 @@ CONFIG_CMD_LICENSE=y CONFIG_CMD_BOOTM_PRE_LOAD=y CONFIG_CMD_BOOTZ=y CONFIG_CMD_BOOTEFI_HELLO=y +CONFIG_CMD_BOOTMENU=y CONFIG_CMD_ABOOTIMG=y # CONFIG_CMD_ELF is not set CONFIG_CMD_ASKENV=y From f2c534d354a6f4156f3e476f9bc83a7250206eab Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 30 Apr 2022 11:13:55 +0200 Subject: [PATCH 02/19] test: unit test for bootmenu Provide a unit test for the bootmenu command Signed-off-by: Heinrich Schuchardt --- scripts/pylint.base | 1 + test/py/tests/test_bootmenu.py | 48 ++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 test/py/tests/test_bootmenu.py diff --git a/scripts/pylint.base b/scripts/pylint.base index 50e9989e879..9ebebf47ab9 100644 --- a/scripts/pylint.base +++ b/scripts/pylint.base @@ -6,6 +6,7 @@ test_tests_test_android_test_ab.py 6.50 test_tests_test_android_test_abootimg.py 6.09 test_tests_test_android_test_avb.py 5.52 test_tests_test_bind.py -2.99 +test_tests_test_bootmenu.py 10.00 test_tests_test_button.py 3.33 test_tests_test_dfu.py 5.45 test_tests_test_dm.py 9.52 diff --git a/test/py/tests/test_bootmenu.py b/test/py/tests/test_bootmenu.py new file mode 100644 index 00000000000..fb03fa417b7 --- /dev/null +++ b/test/py/tests/test_bootmenu.py @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: GPL-2.0+ + +"""Test bootmenu""" + +import pytest + +@pytest.mark.buildconfigspec('cmd_bootmenu') +def test_bootmenu(u_boot_console): + """Test bootmenu + + u_boot_console -- U-Boot console + """ + + u_boot_console.p.timeout = 500 + u_boot_console.run_command('setenv bootmenu_default 1') + u_boot_console.run_command('setenv bootmenu_0 test 1=echo ok 1') + u_boot_console.run_command('setenv bootmenu_1 test 2=echo ok 2') + u_boot_console.run_command('setenv bootmenu_2 test 3=echo ok 3') + u_boot_console.run_command('bootmenu 2', wait_for_prompt=False) + for i in ('U-Boot Boot Menu', 'test 1', 'test 2', 'test 3', 'autoboot'): + u_boot_console.p.expect([i]) + # Press enter key to execute default entry + response = u_boot_console.run_command(cmd='\x0d', wait_for_echo=False, send_nl=False) + assert 'ok 2' in response + u_boot_console.run_command('bootmenu 2', wait_for_prompt=False) + u_boot_console.p.expect(['autoboot']) + # Press up key to select prior entry followed by the enter key + response = u_boot_console.run_command(cmd='\x1b\x5b\x41\x0d', wait_for_echo=False, + send_nl=False) + assert 'ok 1' in response + u_boot_console.run_command('bootmenu 2', wait_for_prompt=False) + u_boot_console.p.expect(['autoboot']) + # Press down key to select next entry followed by the enter key + response = u_boot_console.run_command(cmd='\x1b\x5b\x42\x0d', wait_for_echo=False, + send_nl=False) + assert 'ok 3' in response + u_boot_console.run_command('bootmenu 2; echo rc:$?', wait_for_prompt=False) + u_boot_console.p.expect(['autoboot']) + # Press the escape key + response = u_boot_console.run_command(cmd='\x1b', wait_for_echo=False, send_nl=False) + assert 'ok' not in response + assert 'rc:0' in response + u_boot_console.run_command('setenv bootmenu_default') + u_boot_console.run_command('setenv bootmenu_0') + # Without bootmenu_0 no menu should be shown. + u_boot_console.run_command('bootmenu 2') + u_boot_console.run_command('setenv bootmenu_1') + u_boot_console.run_command('setenv bootmenu_2') From d1d7ed7b1eb99089cf027cafaa134b75a9cf3348 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 1 May 2022 23:17:18 +0200 Subject: [PATCH 03/19] cmd: simplify bootmenu * correct output for timeout > 99 s * don't use spaces to advance to the output column Signed-off-by: Heinrich Schuchardt --- cmd/bootmenu.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c index d5734872720..fe356074728 100644 --- a/cmd/bootmenu.c +++ b/cmd/bootmenu.c @@ -68,9 +68,7 @@ static void bootmenu_print_entry(void *data) * Move cursor to line where the entry will be drown (entry->num) * First 3 lines contain bootmenu header + 1 empty line */ - printf(ANSI_CURSOR_POSITION, entry->num + 4, 1); - - puts(" "); + printf(ANSI_CURSOR_POSITION, entry->num + 4, 7); if (reverse) puts(ANSI_COLOR_REVERSE); @@ -86,12 +84,9 @@ static void bootmenu_autoboot_loop(struct bootmenu_data *menu, { int i, c; - if (menu->delay > 0) { - printf(ANSI_CURSOR_POSITION, menu->count + 5, 1); - printf(" Hit any key to stop autoboot: %2d ", menu->delay); - } - while (menu->delay > 0) { + printf(ANSI_CURSOR_POSITION, menu->count + 5, 3); + printf("Hit any key to stop autoboot: %d ", menu->delay); for (i = 0; i < 100; ++i) { if (!tstc()) { WATCHDOG_RESET(); @@ -125,7 +120,6 @@ static void bootmenu_autoboot_loop(struct bootmenu_data *menu, break; --menu->delay; - printf("\b\b\b%2d ", menu->delay); } printf(ANSI_CURSOR_POSITION, menu->count + 5, 1); @@ -407,8 +401,8 @@ static void menu_display_statusline(struct menu *m) printf(ANSI_CURSOR_POSITION, 1, 1); puts(ANSI_CLEAR_LINE); - printf(ANSI_CURSOR_POSITION, 2, 1); - puts(" *** U-Boot Boot Menu ***"); + printf(ANSI_CURSOR_POSITION, 2, 3); + puts("*** U-Boot Boot Menu ***"); puts(ANSI_CLEAR_LINE_TO_END); printf(ANSI_CURSOR_POSITION, 3, 1); puts(ANSI_CLEAR_LINE); @@ -416,8 +410,8 @@ static void menu_display_statusline(struct menu *m) /* First 3 lines are bootmenu header + 2 empty lines between entries */ printf(ANSI_CURSOR_POSITION, menu->count + 5, 1); puts(ANSI_CLEAR_LINE); - printf(ANSI_CURSOR_POSITION, menu->count + 6, 1); - puts(" Press UP/DOWN to move, ENTER to select, ESC/CTRL+C to quit"); + printf(ANSI_CURSOR_POSITION, menu->count + 6, 3); + puts("Press UP/DOWN to move, ENTER to select, ESC/CTRL+C to quit"); puts(ANSI_CLEAR_LINE_TO_END); printf(ANSI_CURSOR_POSITION, menu->count + 7, 1); puts(ANSI_CLEAR_LINE); From 100de5e37d130dcf8a2bef4e0b33d4e59179d061 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 29 Apr 2022 22:01:54 +0200 Subject: [PATCH 04/19] doc: fix typos in bootmenu.rst %s/cammand/command/ %s/sequencies/sequences/ Signed-off-by: Heinrich Schuchardt Reviewed-by: Bin Meng --- doc/usage/cmd/bootmenu.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/usage/cmd/bootmenu.rst b/doc/usage/cmd/bootmenu.rst index 1016ac8cebd..9430f8c9aa1 100644 --- a/doc/usage/cmd/bootmenu.rst +++ b/doc/usage/cmd/bootmenu.rst @@ -12,7 +12,7 @@ selected using the "Enter" key. The selection of the highlighted menu entry invokes an U-Boot command (or a list of commands) associated with this menu entry. -The "bootmenu" command interprets ANSI escape sequencies, so +The "bootmenu" command interprets ANSI escape sequences, so an ANSI terminal is required for proper menu rendering and item selection. @@ -79,7 +79,7 @@ The above example will be rendered as below:: The selected menu entry will be highlighted - it will have inverted background and text colors. -The "bootmenu" cammand is enabled by:: +The "bootmenu" command is enabled by:: CONFIG_CMD_BOOTMENU=y From 5c1037d57835eb1cbeb9c78069839a445f8ab86b Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 30 Apr 2022 09:05:04 +0200 Subject: [PATCH 05/19] efi_loader: reset colors before clearing screen When resetting the text console the colors have to be set before clearing the screen. Otherwise the background color may be wrong. Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_console.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index ba68a150172..60a3fc85ac4 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -522,11 +522,11 @@ static efi_status_t EFIAPI efi_cout_reset( { EFI_ENTRY("%p, %d", this, extended_verification); - /* Clear screen */ - EFI_CALL(efi_cout_clear_screen(this)); /* Set default colors */ efi_con_mode.attribute = 0x07; printf(ESC "[0;37;40m"); + /* Clear screen */ + EFI_CALL(efi_cout_clear_screen(this)); return EFI_EXIT(EFI_SUCCESS); } From 1979404263f5b40521a513ab510d789d26386ba1 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 30 Apr 2022 15:24:31 +0200 Subject: [PATCH 06/19] test: fix some pylint problems in test_capsule_firmware_raw.py * improve doc-string formatting * remove unused imports Signed-off-by: Heinrich Schuchardt --- .../test_capsule_firmware_raw.py | 42 +++++++++---------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware_raw.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_raw.py index ae99f080ff4..c8c647d0b10 100644 --- a/test/py/tests/test_efi_capsule/test_capsule_firmware_raw.py +++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_raw.py @@ -1,17 +1,13 @@ # SPDX-License-Identifier: GPL-2.0+ # Copyright (c) 2020, Linaro Limited # Author: AKASHI Takahiro -# -# U-Boot UEFI: Firmware Update Test -""" +""" U-Boot UEFI: Firmware Update Test This test verifies capsule-on-disk firmware update for raw images """ -from subprocess import check_call, check_output, CalledProcessError import pytest -from capsule_defs import * - +from capsule_defs import CAPSULE_DATA_DIR, CAPSULE_INSTALL_DIR @pytest.mark.boardspec('sandbox') @pytest.mark.buildconfigspec('efi_capsule_firmware_raw') @@ -24,15 +20,18 @@ from capsule_defs import * @pytest.mark.buildconfigspec('cmd_nvedit_efi') @pytest.mark.buildconfigspec('cmd_sf') @pytest.mark.slow -class TestEfiCapsuleFirmwareRaw(object): +class TestEfiCapsuleFirmwareRaw: + """ Tests verifying capsule-on-disk firmware update for raw images + """ + def test_efi_capsule_fw1( self, u_boot_config, u_boot_console, efi_capsule_data): - """ - Test Case 1 - Update U-Boot and U-Boot environment on SPI Flash - but with an incorrect GUID value in the capsule - No update should happen - 0x100000-0x150000: U-Boot binary (but dummy) - 0x150000-0x200000: U-Boot environment (but dummy) + """ Test Case 1 + Update U-Boot and U-Boot environment on SPI Flash + but with an incorrect GUID value in the capsule + No update should happen + 0x100000-0x150000: U-Boot binary (but dummy) + 0x150000-0x200000: U-Boot environment (but dummy) """ # other tests might have run and the @@ -106,12 +105,11 @@ class TestEfiCapsuleFirmwareRaw(object): def test_efi_capsule_fw2( self, u_boot_config, u_boot_console, efi_capsule_data): - """ - Test Case 2 - Update U-Boot and U-Boot environment on SPI Flash - but with OsIndications unset - No update should happen - 0x100000-0x150000: U-Boot binary (but dummy) - 0x150000-0x200000: U-Boot environment (but dummy) + """ Test Case 2 + Update U-Boot and U-Boot environment on SPI Flash but with OsIndications unset + No update should happen + 0x100000-0x150000: U-Boot binary (but dummy) + 0x150000-0x200000: U-Boot environment (but dummy) """ disk_img = efi_capsule_data with u_boot_console.log.section('Test Case 2-a, before reboot'): @@ -191,9 +189,9 @@ class TestEfiCapsuleFirmwareRaw(object): def test_efi_capsule_fw3( self, u_boot_config, u_boot_console, efi_capsule_data): - """ - Test Case 3 - Update U-Boot on SPI Flash, raw image format - 0x100000-0x150000: U-Boot binary (but dummy) + """ Test Case 3 + Update U-Boot on SPI Flash, raw image format + 0x100000-0x150000: U-Boot binary (but dummy) """ disk_img = efi_capsule_data with u_boot_console.log.section('Test Case 3-a, before reboot'): From 1b2c3e543ca7fe70ffbe4444a5e90c037c782fbb Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 1 May 2022 11:18:03 +0200 Subject: [PATCH 07/19] efi_selftest: buildefi_selftest_unaligned.c The unit test has not been built since CPU_V7 was rename CPU_V7A. Fixes: acf1500138bb ("arm: v7: Kconfig: Rename CPU_V7 as CPU_V7A") Signed-off-by: Heinrich Schuchardt --- lib/efi_selftest/Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index be8040d1c7e..33536c9ec02 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -55,7 +55,9 @@ obj-$(CONFIG_EFI_DEVICE_PATH_TO_TEXT) += efi_selftest_devicepath.o obj-$(CONFIG_EFI_UNICODE_COLLATION_PROTOCOL2) += \ efi_selftest_unicode_collation.o -obj-$(CONFIG_CPU_V7) += efi_selftest_unaligned.o +ifeq ($(CONFIG_CPU_V7A)$(CONFIG_CPU_V7M)$(CONFIG_CPU_V7R),y) +obj-y += efi_selftest_unaligned.o +endif obj-$(CONFIG_EFI_LOADER_HII) += efi_selftest_hii.o obj-$(CONFIG_EFI_RNG_PROTOCOL) += efi_selftest_rng.o obj-$(CONFIG_EFI_GET_TIME) += efi_selftest_rtc.o From 0db8221f323efe9c92e569b2d741229e020f3e08 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 1 May 2022 11:24:22 +0200 Subject: [PATCH 08/19] efi_selftest: clean up unaligned unit test * fix typo %s/give/given/ * don't use void * in pointer arithmetic Signed-off-by: Heinrich Schuchardt --- lib/efi_selftest/efi_selftest_unaligned.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/efi_selftest/efi_selftest_unaligned.c b/lib/efi_selftest/efi_selftest_unaligned.c index 6fce110b76f..7c6bf2d6e8d 100644 --- a/lib/efi_selftest/efi_selftest_unaligned.c +++ b/lib/efi_selftest/efi_selftest_unaligned.c @@ -14,14 +14,14 @@ struct aligned_buffer { }; /* - * Return an u32 at a give address. + * Return an u32 at a given address. * If the address is not four byte aligned, an unaligned memory access * occurs. * * @addr: address to read * Return: value at the address */ -static inline u32 deref(u32 *addr) +static inline u32 deref(void *addr) { int ret; @@ -43,12 +43,11 @@ static int execute(void) { struct aligned_buffer buf = { {0, 1, 2, 3, 4, 5, 6, 7}, - }; - void *v = &buf; + }; u32 r = 0; /* Read an unaligned address */ - r = deref(v + 1); + r = deref(&buf.a[1]); /* UEFI only supports low endian systems */ if (r != 0x04030201) { From c900a42eb0a82d3d7cd18f3255acf92dd9894b92 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 1 May 2022 11:34:15 +0200 Subject: [PATCH 09/19] efi_selftest: error handling in efi_selftest_tcg2 If memory allocation fails, write an error message. Signed-off-by: Heinrich Schuchardt --- lib/efi_selftest/efi_selftest_tcg2.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/efi_selftest/efi_selftest_tcg2.c b/lib/efi_selftest/efi_selftest_tcg2.c index a2b4a79e9b2..67a886efaa8 100644 --- a/lib/efi_selftest/efi_selftest_tcg2.c +++ b/lib/efi_selftest/efi_selftest_tcg2.c @@ -631,8 +631,10 @@ static int efi_st_tcg2_setup(const efi_handle_t img_handle, sizeof(struct efi_tcg2_event) + sizeof(struct uefi_image_load_event), (void **)&efi_tcg2_event); - if (!efi_tcg2_event) + if (ret != EFI_SUCCESS) { + efi_st_error("Out of memory\n"); return EFI_ST_FAILURE; + } efi_tcg2_event->size = sizeof(struct efi_tcg2_event) + sizeof(struct uefi_image_load_event); @@ -659,8 +661,10 @@ static int efi_st_tcg2_setup(const efi_handle_t img_handle, (EFI_TCG2_MAX_PCR_INDEX + 1) * TPM2_SHA256_DIGEST_SIZE, (void **)&pcrs); - if (!pcrs) + if (ret != EFI_SUCCESS) { + efi_st_error("Out of memory\n"); return EFI_ST_FAILURE; + } boottime->set_mem(pcrs, (EFI_TCG2_MAX_PCR_INDEX + 1) * TPM2_SHA256_DIGEST_SIZE, 0); From d30924f16bdceb4c34bfa1f230b04e91e28d5666 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Mon, 2 May 2022 06:27:00 +0200 Subject: [PATCH 10/19] lib: fix selection of CONFIG_CHARSET lib/charset.c is not optional for EFI_APP || EFI_LOADER || UFS || UT_UNICODE. These must select CONFIG_CHARSET. Fixes: 726cd9836db0 ("efi: Make unicode printf available to the app") Signed-off-by: Heinrich Schuchardt --- drivers/ufs/Kconfig | 1 + lib/Kconfig | 5 ----- lib/efi/Kconfig | 1 + lib/efi_loader/Kconfig | 1 + test/Kconfig | 1 + 5 files changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/ufs/Kconfig b/drivers/ufs/Kconfig index c2aafd3020c..69ea18edf8d 100644 --- a/drivers/ufs/Kconfig +++ b/drivers/ufs/Kconfig @@ -3,6 +3,7 @@ menu "UFS Host Controller Support" config UFS bool "Support UFS controllers" depends on DM_SCSI + select CHARSET help This selects support for Universal Flash Subsystem (UFS). Say Y here if you want UFS Support. diff --git a/lib/Kconfig b/lib/Kconfig index e2697ab2ceb..acc0ac081a4 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -52,11 +52,6 @@ config CC_OPTIMIZE_LIBS_FOR_SPEED config CHARSET bool - default y if UT_UNICODE || EFI_LOADER || UFS || EFI_APP - help - Enables support for various conversions between different - character sets, such as between unicode representations and - different 'code pages'. config DYNAMIC_CRC_TABLE bool "Enable Dynamic tables for CRC" diff --git a/lib/efi/Kconfig b/lib/efi/Kconfig index 15ce99e1a74..c2b9bb73f71 100644 --- a/lib/efi/Kconfig +++ b/lib/efi/Kconfig @@ -14,6 +14,7 @@ choice config EFI_APP bool "Support running as an EFI application" + select CHARSET help Build U-Boot as an application which can be started from EFI. This is useful for examining a platform in the early stages of porting diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 6b245f50a72..eb2d6fddc4f 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -14,6 +14,7 @@ config EFI_LOADER depends on DM_ETH || !NET depends on !EFI_APP default y if !ARM || SYS_CPU = armv7 || SYS_CPU = armv8 + select CHARSET select DM_EVENT select EVENT_DYNAMIC select LIB_UUID diff --git a/test/Kconfig b/test/Kconfig index e15ba239eb3..7f3447ae5ac 100644 --- a/test/Kconfig +++ b/test/Kconfig @@ -91,6 +91,7 @@ config UT_UNICODE bool "Unit tests for Unicode functions" depends on UNIT_TEST default y + select CHARSET help Enables the 'ut unicode' command which tests that the functions for manipulating Unicode strings work correctly. From eca08ce94ceb72358c5fb00e82506c0f74e65e3f Mon Sep 17 00:00:00 2001 From: Masahisa Kojima Date: Thu, 28 Apr 2022 17:09:34 +0900 Subject: [PATCH 11/19] lib/charset: add u16_strlcat() function Provide u16 string version of strlcat(). Signed-off-by: Masahisa Kojima Reviewed-by: Heinrich Schuchardt --- include/charset.h | 14 ++++++++++++++ lib/charset.c | 16 ++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/include/charset.h b/include/charset.h index 38908e08f0d..20abfbe7524 100644 --- a/include/charset.h +++ b/include/charset.h @@ -261,6 +261,20 @@ u16 *u16_strcpy(u16 *dest, const u16 *src); */ u16 *u16_strdup(const void *src); +/** + * u16_strlcat() - Append a length-limited, %NUL-terminated string to another + * + * Append the source string @src to the destination string @dest, overwriting + * null word at the end of @dest adding a terminating null word. + * + * @dest: zero terminated u16 destination string + * @src: zero terminated u16 source string + * @count: size of buffer in u16 words including taling 0x0000 + * Return: required size including trailing 0x0000 in u16 words + * If return value >= count, truncation occurred. + */ +size_t u16_strlcat(u16 *dest, const u16 *src, size_t size); + /** * utf16_to_utf8() - Convert an utf16 string to utf8 * diff --git a/lib/charset.c b/lib/charset.c index de201cf3b9b..bece4985bfc 100644 --- a/lib/charset.c +++ b/lib/charset.c @@ -416,6 +416,22 @@ u16 *u16_strdup(const void *src) return new; } +size_t u16_strlcat(u16 *dest, const u16 *src, size_t count) +{ + size_t destlen = u16_strlen(dest); + size_t srclen = u16_strlen(src); + size_t ret = destlen + srclen + 1; + + if (destlen >= count) + return ret; + if (ret > count) + srclen -= ret - count; + memcpy(&dest[destlen], src, 2 * srclen); + dest[destlen + srclen] = 0x0000; + + return ret; +} + /* Convert UTF-16 to UTF-8. */ uint8_t *utf16_to_utf8(uint8_t *dest, const uint16_t *src, size_t size) { From b8cd1e7fc2802fb4a9f42caf5d8f8e194eb8266f Mon Sep 17 00:00:00 2001 From: Masahisa Kojima Date: Thu, 28 Apr 2022 17:09:35 +0900 Subject: [PATCH 12/19] test: unit test for u16_strlcat() Provide a unit test for function u16_strlcat(). Signed-off-by: Masahisa Kojima Reviewed-by: Heinrich Schuchardt --- test/unicode_ut.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/test/unicode_ut.c b/test/unicode_ut.c index f2f63d5367b..d104bd5997f 100644 --- a/test/unicode_ut.c +++ b/test/unicode_ut.c @@ -758,6 +758,56 @@ static int unicode_test_efi_create_indexed_name(struct unit_test_state *uts) UNICODE_TEST(unicode_test_efi_create_indexed_name); #endif +static int unicode_test_u16_strlcat(struct unit_test_state *uts) +{ + u16 buf[40]; + u16 dest[] = {0x3053, 0x3093, 0x306b, 0x3061, 0x306f, 0}; + u16 src[] = {0x03B1, 0x2172, 0x6F5C, 0x8247, 0}; + u16 concat_str[] = {0x3053, 0x3093, 0x306b, 0x3061, 0x306f, + 0x03B1, 0x2172, 0x6F5C, 0x8247, 0}; + u16 null_src = u'\0'; + size_t ret, expected; + int i; + + /* dest and src are empty string */ + memset(buf, 0, sizeof(buf)); + ret = u16_strlcat(buf, &null_src, sizeof(buf)); + ut_asserteq(1, ret); + + /* dest is empty string */ + memset(buf, 0, sizeof(buf)); + ret = u16_strlcat(buf, src, sizeof(buf)); + ut_asserteq(5, ret); + ut_assert(!unicode_test_u16_strcmp(buf, src, 40)); + + /* src is empty string */ + memset(buf, 0xCD, (sizeof(buf) - sizeof(u16))); + buf[39] = 0; + memcpy(buf, dest, sizeof(dest)); + ret = u16_strlcat(buf, &null_src, sizeof(buf)); + ut_asserteq(6, ret); + ut_assert(!unicode_test_u16_strcmp(buf, dest, 40)); + + for (i = 0; i <= 40; i++) { + memset(buf, 0xCD, (sizeof(buf) - sizeof(u16))); + buf[39] = 0; + memcpy(buf, dest, sizeof(dest)); + expected = 10; + ret = u16_strlcat(buf, src, i); + ut_asserteq(expected, ret); + if (i <= 6) { + ut_assert(!unicode_test_u16_strcmp(buf, dest, 40)); + } else if (i < 10) { + ut_assert(!unicode_test_u16_strcmp(buf, concat_str, i - 1)); + } else { + ut_assert(!unicode_test_u16_strcmp(buf, concat_str, 40)); + } + } + + return 0; +} +UNICODE_TEST(unicode_test_u16_strlcat); + int do_ut_unicode(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { struct unit_test *tests = UNIT_TEST_SUITE_START(unicode_test); From c23bb03465c1107495b90c4b396d37a0bbe90bfc Mon Sep 17 00:00:00 2001 From: Masahisa Kojima Date: Thu, 28 Apr 2022 17:09:36 +0900 Subject: [PATCH 13/19] menu: always show the menu regardless of the number of entry To make user aware of the menu entry selection, menu always appears regardless of the number of entry. Signed-off-by: Masahisa Kojima Adjust test/py/tests/test_bootmenu.py Reviewed-by: Heinrich Schuchardt --- common/menu.c | 2 +- test/py/tests/test_bootmenu.py | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/common/menu.c b/common/menu.c index 5fb2ffbd065..b577d80b4ff 100644 --- a/common/menu.c +++ b/common/menu.c @@ -271,7 +271,7 @@ int menu_get_choice(struct menu *m, void **choice) if (!m || !choice) return -EINVAL; - if (!m->prompt || m->item_cnt == 1) + if (!m->prompt) return menu_default_choice(m, choice); return menu_interactive_choice(m, choice); diff --git a/test/py/tests/test_bootmenu.py b/test/py/tests/test_bootmenu.py index fb03fa417b7..b4baa534aa2 100644 --- a/test/py/tests/test_bootmenu.py +++ b/test/py/tests/test_bootmenu.py @@ -42,7 +42,5 @@ def test_bootmenu(u_boot_console): assert 'rc:0' in response u_boot_console.run_command('setenv bootmenu_default') u_boot_console.run_command('setenv bootmenu_0') - # Without bootmenu_0 no menu should be shown. - u_boot_console.run_command('bootmenu 2') u_boot_console.run_command('setenv bootmenu_1') u_boot_console.run_command('setenv bootmenu_2') From 7f67525f99d8158f668af8198cc1d089a3dc4ee8 Mon Sep 17 00:00:00 2001 From: Masahisa Kojima Date: Thu, 28 Apr 2022 17:09:37 +0900 Subject: [PATCH 14/19] menu: menu_get_choice() return -ENOENT if menu item is empty menu_get_choice() needs to handle the case that menu item is empty. In this case, menu_get_choice() returns -ENOENT. Signed-off-by: Masahisa Kojima Reviewed-by: Heinrich Schuchardt Signed-off-by: Heinrich Schuchardt --- common/menu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common/menu.c b/common/menu.c index b577d80b4ff..f5fc6930a2c 100644 --- a/common/menu.c +++ b/common/menu.c @@ -271,6 +271,9 @@ int menu_get_choice(struct menu *m, void **choice) if (!m || !choice) return -EINVAL; + if (!m->item_cnt) + return -ENOENT; + if (!m->prompt) return menu_default_choice(m, choice); From d8465ffc019a742ae6c8770179f359317c5f101a Mon Sep 17 00:00:00 2001 From: AKASHI Takahiro Date: Thu, 28 Apr 2022 17:09:38 +0900 Subject: [PATCH 15/19] efi_loader: export efi_locate_device_handle() This function will be used in the next commit where some behavior of EFI boot manager will be expanded. Signed-off-by: AKASHI Takahiro Reviewed-by: Ilias Apalodimas Reviewed-by: Heinrich Schuchardt --- include/efi_loader.h | 4 ++++ lib/efi_loader/efi_boottime.c | 7 +++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/include/efi_loader.h b/include/efi_loader.h index ba79a9afb40..effb43369d9 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -595,6 +595,10 @@ efi_status_t efi_create_handle(efi_handle_t *handle); void efi_delete_handle(efi_handle_t obj); /* Call this to validate a handle and find the EFI object for it */ struct efi_object *efi_search_obj(const efi_handle_t handle); +/* Locate device_path handle */ +efi_status_t EFIAPI efi_locate_device_path(const efi_guid_t *protocol, + struct efi_device_path **device_path, + efi_handle_t *device); /* Load image */ efi_status_t EFIAPI efi_load_image(bool boot_policy, efi_handle_t parent_image, diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 5bcb8253edb..4da64b5d296 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1799,10 +1799,9 @@ failure: * * Return: status code */ -static efi_status_t EFIAPI efi_locate_device_path( - const efi_guid_t *protocol, - struct efi_device_path **device_path, - efi_handle_t *device) +efi_status_t EFIAPI efi_locate_device_path(const efi_guid_t *protocol, + struct efi_device_path **device_path, + efi_handle_t *device) { struct efi_device_path *dp; size_t i; From 4e65ca00f3a30791752529fe9ffe8c02da104c62 Mon Sep 17 00:00:00 2001 From: AKASHI Takahiro Date: Thu, 28 Apr 2022 17:09:39 +0900 Subject: [PATCH 16/19] efi_loader: bootmgr: add booting from removable media Under the current implementation, booting from removable media using a architecture-specific default image name, say BOOTAA64.EFI, is supported only in distro_bootcmd script. See the commit 74522c898b35 ("efi_loader: Add distro boot script for removable media"). This is, however, half-baked implementation because 1) UEFI specification requires this feature to be implemented as part of Boot Manager's responsibility: 3 - Boot Manager 3.5.1 Boot via the Simple File Protocol When booting via the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL, the FilePath will start with a device path that points to the device that implements the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL or the EFI_BLOCK_IO_PROTOCOL. The next part of the FilePath may point to the file name, including subdirectories, which contain the bootable image. If the file name is a null device path, the file name must be generated from the rules defined below. ... 3.5.1.1 Removable Media Boot Behavior To generate a file name when none is present in the FilePath, the firmware must append a default file name in the form \EFI\BOOT\BOOT{machine type short-name}.EFI ... 2) So (1) entails the hehavior that the user's preference of boot media order should be determined by Boot#### and BootOrder variables. With this patch, the semantics mentioned above is fully implemented. For example, if you want to boot the system from USB and SCSI in this order, * define Boot0001 which contains only a device path to the USB device (without any file path/name) * define Boot0002 which contains only a device path to the SCSI device, and * set BootOrder to Boot0001:Boot0002 To avoid build error for sandbox, default file name "BOOTSANDBOX.efi" is defined even if it is out of scope of UEFI specification. Signed-off-by: AKASHI Takahiro Signed-off-by: Masahisa Kojima On sandbox use binary name corresponding to host architecture. Reviewed-by: Heinrich Schuchardt --- include/efi_default_filename.h | 45 ++++++++++++++++++++++++++++++ lib/efi_loader/efi_bootmgr.c | 51 +++++++++++++++++++++++++++++++++- 2 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 include/efi_default_filename.h diff --git a/include/efi_default_filename.h b/include/efi_default_filename.h new file mode 100644 index 00000000000..13b9de8754a --- /dev/null +++ b/include/efi_default_filename.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * When a boot option does not provide a file path the EFI file to be + * booted is \EFI\BOOT\$(BOOTEFI_NAME).EFI. The architecture specific + * file name is defined in this include. + * + * Copyright (c) 2022, Heinrich Schuchardt + */ + +#ifndef _EFI_DEFAULT_FILENAME_H +#define _EFI_DEFAULT_FILENAME_H + +#include + +#undef BOOTEFI_NAME + +#if HOST_ARCH == HOST_ARCH_X86_64 +#define BOOTEFI_NAME "BOOTX64.EFI" +#endif + +#if HOST_ARCH == HOST_ARCH_X86 +#define BOOTEFI_NAME "BOOTIA32.EFI" +#endif + +#if HOST_ARCH == HOST_ARCH_AARCH64 +#define BOOTEFI_NAME "BOOTAA64.EFI" +#endif + +#if HOST_ARCH == HOST_ARCH_ARM +#define BOOTEFI_NAME "BOOTARM.EFI" +#endif + +#if HOST_ARCH == HOST_ARCH_RISCV32 +#define BOOTEFI_NAME "BOOTRISCV32.EFI" +#endif + +#if HOST_ARCH == HOST_ARCH_RISCV64 +#define BOOTEFI_NAME "BOOTRISCV64.EFI" +#endif + +#ifndef BOOTEFI_NAME +#error Unsupported UEFI architecture +#endif + +#endif diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c index 52bea4d5411..631a25d76e9 100644 --- a/lib/efi_loader/efi_bootmgr.c +++ b/lib/efi_loader/efi_bootmgr.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +31,51 @@ static const struct efi_runtime_services *rs; * should do normal or recovery boot. */ +/** + * expand_media_path() - expand a device path for default file name + * @device_path: device path to check against + * + * If @device_path is a media or disk partition which houses a file + * system, this function returns a full device path which contains + * an architecture-specific default file name for removable media. + * + * Return: a newly allocated device path + */ +static +struct efi_device_path *expand_media_path(struct efi_device_path *device_path) +{ + struct efi_device_path *dp, *full_path; + efi_handle_t handle; + efi_status_t ret; + + if (!device_path) + return NULL; + + /* + * If device_path is a (removable) media or partition which provides + * simple file system protocol, append a default file name to support + * booting from removable media. + */ + dp = device_path; + ret = EFI_CALL(efi_locate_device_path( + &efi_simple_file_system_protocol_guid, + &dp, &handle)); + if (ret == EFI_SUCCESS) { + if (dp->type == DEVICE_PATH_TYPE_END) { + dp = efi_dp_from_file(NULL, 0, + "/EFI/BOOT/" BOOTEFI_NAME); + full_path = efi_dp_append(device_path, dp); + efi_free_pool(dp); + } else { + full_path = efi_dp_dup(device_path); + } + } else { + full_path = efi_dp_dup(device_path); + } + + return full_path; +} + /** * try_load_entry() - try to load image for boot option * @@ -64,13 +110,16 @@ static efi_status_t try_load_entry(u16 n, efi_handle_t *handle, } if (lo.attributes & LOAD_OPTION_ACTIVE) { + struct efi_device_path *file_path; u32 attributes; log_debug("trying to load \"%ls\" from %pD\n", lo.label, lo.file_path); - ret = EFI_CALL(efi_load_image(true, efi_root, lo.file_path, + file_path = expand_media_path(lo.file_path); + ret = EFI_CALL(efi_load_image(true, efi_root, file_path, NULL, 0, handle)); + efi_free_pool(file_path); if (ret != EFI_SUCCESS) { log_warning("Loading %ls '%ls' failed\n", varname, lo.label); From a3d0aa87acbec4b983f54c24a6e7fd8e9e7b9728 Mon Sep 17 00:00:00 2001 From: Masahisa Kojima Date: Thu, 28 Apr 2022 17:09:41 +0900 Subject: [PATCH 17/19] bootmenu: update bootmenu_entry structure This is a preparation for succeeding addition of uefi boot and distro boot menu entries into bootmenu. The bootmenu_entry title is updated to u16 string because uefi use u16 string. This commit also factors out the function to prepare the entries generated by "bootmenu_x" U-Boot environment variable. Signed-off-by: Masahisa Kojima Reviewed-by: Heinrich Schuchardt --- cmd/Kconfig | 1 + cmd/bootmenu.c | 107 ++++++++++++++++++++++++++++++++++--------------- 2 files changed, 76 insertions(+), 32 deletions(-) diff --git a/cmd/Kconfig b/cmd/Kconfig index 2b575a2b42d..09193b61b95 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -353,6 +353,7 @@ source lib/efi_selftest/Kconfig config CMD_BOOTMENU bool "bootmenu" select MENU + select CHARSET help Add an ANSI terminal boot menu command. diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c index fe356074728..4707559af30 100644 --- a/cmd/bootmenu.c +++ b/cmd/bootmenu.c @@ -3,6 +3,7 @@ * (C) Copyright 2011-2013 Pali Rohár */ +#include #include #include #include @@ -24,11 +25,18 @@ */ #define MAX_ENV_SIZE (9 + 2 + 1) +enum boot_type { + BOOTMENU_TYPE_NONE = 0, + BOOTMENU_TYPE_BOOTMENU, +}; + struct bootmenu_entry { unsigned short int num; /* unique number 0 .. MAX_COUNT */ char key[3]; /* key identifier of number */ - char *title; /* title of entry */ + u16 *title; /* title of entry */ char *command; /* hush command of entry */ + enum boot_type type; /* boot type of entry */ + u16 bootorder; /* order for each boot type */ struct bootmenu_data *menu; /* this bootmenu */ struct bootmenu_entry *next; /* next menu entry (num+1) */ }; @@ -73,7 +81,7 @@ static void bootmenu_print_entry(void *data) if (reverse) puts(ANSI_COLOR_REVERSE); - puts(entry->title); + printf("%ls", entry->title); if (reverse) puts(ANSI_COLOR_RESET); @@ -269,31 +277,32 @@ static void bootmenu_destroy(struct bootmenu_data *menu) free(menu); } -static struct bootmenu_data *bootmenu_create(int delay) +/** + * prepare_bootmenu_entry() - generate the bootmenu_xx entries + * + * This function read the "bootmenu_x" U-Boot environment variable + * and generate the bootmenu entries. + * + * @menu: pointer to the bootmenu structure + * @current: pointer to the last bootmenu entry list + * @index: pointer to the index of the last bootmenu entry, + * the number of bootmenu entry is added by this function + * Return: 1 on success, negative value on error + */ +static int prepare_bootmenu_entry(struct bootmenu_data *menu, + struct bootmenu_entry **current, + unsigned short int *index) { - unsigned short int i = 0; - const char *option; - struct bootmenu_data *menu; - struct bootmenu_entry *iter = NULL; - int len; char *sep; - char *default_str; - struct bootmenu_entry *entry; - - menu = malloc(sizeof(struct bootmenu_data)); - if (!menu) - return NULL; - - menu->delay = delay; - menu->active = 0; - menu->first = NULL; - - default_str = env_get("bootmenu_default"); - if (default_str) - menu->active = (int)simple_strtol(default_str, NULL, 10); + const char *option; + unsigned short int i = *index; + struct bootmenu_entry *entry = NULL; + struct bootmenu_entry *iter = *current; while ((option = bootmenu_getoption(i))) { + u16 *buf; + sep = strchr(option, '='); if (!sep) { printf("Invalid bootmenu entry: %s\n", option); @@ -302,23 +311,23 @@ static struct bootmenu_data *bootmenu_create(int delay) entry = malloc(sizeof(struct bootmenu_entry)); if (!entry) - goto cleanup; + return -ENOMEM; len = sep-option; - entry->title = malloc(len + 1); + buf = calloc(1, (len + 1) * sizeof(u16)); + entry->title = buf; if (!entry->title) { free(entry); - goto cleanup; + return -ENOMEM; } - memcpy(entry->title, option, len); - entry->title[len] = 0; + utf8_utf16_strncpy(&buf, option, len); len = strlen(sep + 1); entry->command = malloc(len + 1); if (!entry->command) { free(entry->title); free(entry); - goto cleanup; + return -ENOMEM; } memcpy(entry->command, sep + 1, len); entry->command[len] = 0; @@ -327,6 +336,8 @@ static struct bootmenu_data *bootmenu_create(int delay) entry->num = i; entry->menu = menu; + entry->type = BOOTMENU_TYPE_BOOTMENU; + entry->bootorder = i; entry->next = NULL; if (!iter) @@ -341,13 +352,44 @@ static struct bootmenu_data *bootmenu_create(int delay) break; } + *index = i; + *current = iter; + + return 1; +} + +static struct bootmenu_data *bootmenu_create(int delay) +{ + int ret; + unsigned short int i = 0; + struct bootmenu_data *menu; + struct bootmenu_entry *iter = NULL; + struct bootmenu_entry *entry; + char *default_str; + + menu = malloc(sizeof(struct bootmenu_data)); + if (!menu) + return NULL; + + menu->delay = delay; + menu->active = 0; + menu->first = NULL; + + default_str = env_get("bootmenu_default"); + if (default_str) + menu->active = (int)simple_strtol(default_str, NULL, 10); + + ret = prepare_bootmenu_entry(menu, &iter, &i); + if (ret < 0) + goto cleanup; + /* Add U-Boot console entry at the end */ if (i <= MAX_COUNT - 1) { entry = malloc(sizeof(struct bootmenu_entry)); if (!entry) goto cleanup; - entry->title = strdup("U-Boot console"); + entry->title = u16_strdup(u"U-Boot console"); if (!entry->title) { free(entry); goto cleanup; @@ -364,6 +406,7 @@ static struct bootmenu_data *bootmenu_create(int delay) entry->num = i; entry->menu = menu; + entry->type = BOOTMENU_TYPE_NONE; entry->next = NULL; if (!iter) @@ -421,7 +464,7 @@ static void bootmenu_show(int delay) { int init = 0; void *choice = NULL; - char *title = NULL; + u16 *title = NULL; char *command = NULL; struct menu *menu; struct bootmenu_data *bootmenu; @@ -472,7 +515,7 @@ static void bootmenu_show(int delay) if (menu_get_choice(menu, &choice) == 1) { iter = choice; - title = strdup(iter->title); + title = u16_strdup(iter->title); command = strdup(iter->command); } @@ -487,7 +530,7 @@ cleanup: } if (title && command) { - debug("Starting entry '%s'\n", title); + debug("Starting entry '%ls'\n", title); free(title); run_command(command, 0); free(command); From c606c0059f7f81d06ed316e9cd654ffbbad84ad5 Mon Sep 17 00:00:00 2001 From: Masahisa Kojima Date: Thu, 28 Apr 2022 17:09:42 +0900 Subject: [PATCH 18/19] bootmenu: add UEFI boot entry into bootmenu This commit adds the UEFI related menu entries into the bootmenu. User can select which UEFI "Boot####" option to execute from bootmenu, then bootmenu sets the "BootNext" UEFI variable and invoke efi bootmgr. The efi bootmgr will handle the "BootNext" UEFI variable. If the "BootNext" UEFI variable is preset and efi bootmgr is enabled, bootmenu invokes efi bootmgr to handle "BootNext" as first priority. The UEFI boot entry has the "UEFI BOOTXXXX" prefix as below. *** U-Boot Boot Menu *** UEFI BOOT0000 : debian UEFI BOOT0001 : ubuntu Signed-off-by: Masahisa Kojima --- cmd/bootmenu.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 152 insertions(+), 1 deletion(-) diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c index 4707559af30..2244a0faf2a 100644 --- a/cmd/bootmenu.c +++ b/cmd/bootmenu.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include #include @@ -28,6 +30,7 @@ enum boot_type { BOOTMENU_TYPE_NONE = 0, BOOTMENU_TYPE_BOOTMENU, + BOOTMENU_TYPE_UEFI_BOOT_OPTION, }; struct bootmenu_entry { @@ -358,6 +361,95 @@ static int prepare_bootmenu_entry(struct bootmenu_data *menu, return 1; } +/** + * prepare_uefi_bootorder_entry() - generate the uefi bootmenu entries + * + * This function read the "BootOrder" UEFI variable + * and generate the bootmenu entries in the order of "BootOrder". + * + * @menu: pointer to the bootmenu structure + * @current: pointer to the last bootmenu entry list + * @index: pointer to the index of the last bootmenu entry, + * the number of uefi entry is added by this function + * Return: 1 on success, negative value on error + */ +static int prepare_uefi_bootorder_entry(struct bootmenu_data *menu, + struct bootmenu_entry **current, + unsigned short int *index) +{ + u16 *bootorder; + efi_status_t ret; + unsigned short j; + efi_uintn_t num, size; + void *load_option; + struct efi_load_option lo; + u16 varname[] = u"Boot####"; + unsigned short int i = *index; + struct bootmenu_entry *entry = NULL; + struct bootmenu_entry *iter = *current; + + bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size); + if (!bootorder) + return -ENOENT; + + num = size / sizeof(u16); + for (j = 0; j < num; j++) { + entry = malloc(sizeof(struct bootmenu_entry)); + if (!entry) + return -ENOMEM; + + efi_create_indexed_name(varname, sizeof(varname), + "Boot", bootorder[j]); + load_option = efi_get_var(varname, &efi_global_variable_guid, &size); + if (!load_option) + continue; + + ret = efi_deserialize_load_option(&lo, load_option, &size); + if (ret != EFI_SUCCESS) { + log_warning("Invalid load option for %ls\n", varname); + free(load_option); + free(entry); + continue; + } + + if (lo.attributes & LOAD_OPTION_ACTIVE) { + entry->title = u16_strdup(lo.label); + if (!entry->title) { + free(load_option); + free(entry); + free(bootorder); + return -ENOMEM; + } + entry->command = strdup("bootefi bootmgr"); + sprintf(entry->key, "%d", i); + entry->num = i; + entry->menu = menu; + entry->type = BOOTMENU_TYPE_UEFI_BOOT_OPTION; + entry->bootorder = bootorder[j]; + entry->next = NULL; + + if (!iter) + menu->first = entry; + else + iter->next = entry; + + iter = entry; + i++; + } + + free(load_option); + + if (i == MAX_COUNT - 1) + break; + } + + free(bootorder); + *index = i; + *current = iter; + + return 1; +} + static struct bootmenu_data *bootmenu_create(int delay) { int ret; @@ -383,6 +475,14 @@ static struct bootmenu_data *bootmenu_create(int delay) if (ret < 0) goto cleanup; + if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) { + if (i < MAX_COUNT - 1) { + ret = prepare_uefi_bootorder_entry(menu, &iter, &i); + if (ret < 0 && ret != -ENOENT) + goto cleanup; + } + } + /* Add U-Boot console entry at the end */ if (i <= MAX_COUNT - 1) { entry = malloc(sizeof(struct bootmenu_entry)); @@ -460,6 +560,31 @@ static void menu_display_statusline(struct menu *m) puts(ANSI_CLEAR_LINE); } +static void handle_uefi_bootnext(void) +{ + u16 bootnext; + efi_status_t ret; + efi_uintn_t size; + + /* Initialize EFI drivers */ + ret = efi_init_obj_list(); + if (ret != EFI_SUCCESS) { + log_err("Error: Cannot initialize UEFI sub-system, r = %lu\n", + ret & ~EFI_ERROR_MASK); + + return; + } + + /* If UEFI BootNext variable is set, boot the BootNext load option */ + size = sizeof(u16); + ret = efi_get_variable_int(u"BootNext", + &efi_global_variable_guid, + NULL, &size, &bootnext, NULL); + if (ret == EFI_SUCCESS) + /* BootNext does exist here, try to boot */ + run_command("bootefi bootmgr", 0); +} + static void bootmenu_show(int delay) { int init = 0; @@ -469,8 +594,12 @@ static void bootmenu_show(int delay) struct menu *menu; struct bootmenu_data *bootmenu; struct bootmenu_entry *iter; + efi_status_t efi_ret = EFI_SUCCESS; char *option, *sep; + if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) + handle_uefi_bootnext(); + /* If delay is 0 do not create menu, just run first entry */ if (delay == 0) { option = bootmenu_getoption(0); @@ -519,6 +648,27 @@ static void bootmenu_show(int delay) command = strdup(iter->command); } + /* + * If the selected entry is UEFI BOOT####, set the BootNext variable. + * Then uefi bootmgr is invoked by the preset command in iter->command. + */ + if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) { + if (iter->type == BOOTMENU_TYPE_UEFI_BOOT_OPTION) { + /* + * UEFI specification requires BootNext variable needs non-volatile + * attribute, but this BootNext is only used inside of U-Boot and + * removed by efi bootmgr once BootNext is processed. + * So this BootNext can be volatile. + */ + efi_ret = efi_set_variable_int(u"BootNext", &efi_global_variable_guid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + sizeof(u16), &iter->bootorder, false); + if (efi_ret != EFI_SUCCESS) + goto cleanup; + } + } + cleanup: menu_destroy(menu); bootmenu_destroy(bootmenu); @@ -532,7 +682,8 @@ cleanup: if (title && command) { debug("Starting entry '%ls'\n", title); free(title); - run_command(command, 0); + if (efi_ret == EFI_SUCCESS) + run_command(command, 0); free(command); } From 2158b0da220ccbe969bc18668263141d9a89f13e Mon Sep 17 00:00:00 2001 From: Masahisa Kojima Date: Thu, 28 Apr 2022 17:09:44 +0900 Subject: [PATCH 19/19] bootmenu: add Kconfig option not to enter U-Boot console This commit adds the Kconfig option to disable to enter the U-Boot console from bootmenu. If CMD_BOOTMENU_ENTER_UBOOT_CONSOLE is enabled, "U-Boot console" entry is appeared as the last entry in the bootmenu, then user can enter U-Boot console. If CMD_BOOTMENU_ENTER_UBOOT_CONSOLE is disabled, "Quit" entry is appeared as the last entry instead of "U-Boot console". When user chooses "Quit" from bootmenu, the following default commands are invoked. - "bootefi bootmgr" (if efi bootmgr is enabled) - "run bootcmd" If the both commands are executed and returns to the bootmenu, the bootmenu will appears again. Signed-off-by: Masahisa Kojima --- cmd/Kconfig | 10 ++++++++ cmd/bootmenu.c | 69 ++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 68 insertions(+), 11 deletions(-) diff --git a/cmd/Kconfig b/cmd/Kconfig index 09193b61b95..69c1814d24a 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -357,6 +357,16 @@ config CMD_BOOTMENU help Add an ANSI terminal boot menu command. +config CMD_BOOTMENU_ENTER_UBOOT_CONSOLE + bool "Allow Bootmenu to enter the U-Boot console" + depends on CMD_BOOTMENU + default n + help + Add an entry to enter U-Boot console in bootmenu. + If this option is disabled, user can not enter + the U-Boot console from bootmenu. It increases + the system security. + config CMD_ADTIMG bool "adtimg" help diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c index 2244a0faf2a..ac857672466 100644 --- a/cmd/bootmenu.c +++ b/cmd/bootmenu.c @@ -27,6 +27,13 @@ */ #define MAX_ENV_SIZE (9 + 2 + 1) +enum bootmenu_ret { + BOOTMENU_RET_SUCCESS = 0, + BOOTMENU_RET_FAIL, + BOOTMENU_RET_QUIT, + BOOTMENU_RET_UPDATED, +}; + enum boot_type { BOOTMENU_TYPE_NONE = 0, BOOTMENU_TYPE_BOOTMENU, @@ -489,7 +496,12 @@ static struct bootmenu_data *bootmenu_create(int delay) if (!entry) goto cleanup; - entry->title = u16_strdup(u"U-Boot console"); + /* Add Quit entry if entering U-Boot console is disabled */ + if (IS_ENABLED(CONFIG_CMD_BOOTMENU_ENTER_UBOOT_CONSOLE)) + entry->title = u16_strdup(u"U-Boot console"); + else + entry->title = u16_strdup(u"Quit"); + if (!entry->title) { free(entry); goto cleanup; @@ -585,15 +597,17 @@ static void handle_uefi_bootnext(void) run_command("bootefi bootmgr", 0); } -static void bootmenu_show(int delay) +static enum bootmenu_ret bootmenu_show(int delay) { + int cmd_ret; int init = 0; void *choice = NULL; u16 *title = NULL; char *command = NULL; struct menu *menu; - struct bootmenu_data *bootmenu; struct bootmenu_entry *iter; + int ret = BOOTMENU_RET_SUCCESS; + struct bootmenu_data *bootmenu; efi_status_t efi_ret = EFI_SUCCESS; char *option, *sep; @@ -605,27 +619,27 @@ static void bootmenu_show(int delay) option = bootmenu_getoption(0); if (!option) { puts("bootmenu option 0 was not found\n"); - return; + return BOOTMENU_RET_FAIL; } sep = strchr(option, '='); if (!sep) { puts("bootmenu option 0 is invalid\n"); - return; + return BOOTMENU_RET_FAIL; } - run_command(sep+1, 0); - return; + cmd_ret = run_command(sep + 1, 0); + return (cmd_ret == CMD_RET_SUCCESS ? BOOTMENU_RET_SUCCESS : BOOTMENU_RET_FAIL); } bootmenu = bootmenu_create(delay); if (!bootmenu) - return; + return BOOTMENU_RET_FAIL; menu = menu_create(NULL, bootmenu->delay, 1, menu_display_statusline, bootmenu_print_entry, bootmenu_choice_entry, bootmenu); if (!menu) { bootmenu_destroy(bootmenu); - return; + return BOOTMENU_RET_FAIL; } for (iter = bootmenu->first; iter; iter = iter->next) { @@ -646,6 +660,14 @@ static void bootmenu_show(int delay) iter = choice; title = u16_strdup(iter->title); command = strdup(iter->command); + + /* last entry is U-Boot console or Quit */ + if (iter->num == iter->menu->count - 1) { + ret = BOOTMENU_RET_QUIT; + goto cleanup; + } + } else { + goto cleanup; } /* @@ -683,19 +705,44 @@ cleanup: debug("Starting entry '%ls'\n", title); free(title); if (efi_ret == EFI_SUCCESS) - run_command(command, 0); + cmd_ret = run_command(command, 0); free(command); } #ifdef CONFIG_POSTBOOTMENU run_command(CONFIG_POSTBOOTMENU, 0); #endif + + if (efi_ret != EFI_SUCCESS || cmd_ret != CMD_RET_SUCCESS) + ret = BOOTMENU_RET_FAIL; + + return ret; } #ifdef CONFIG_AUTOBOOT_MENU_SHOW int menu_show(int bootdelay) { - bootmenu_show(bootdelay); + int ret; + + while (1) { + ret = bootmenu_show(bootdelay); + bootdelay = -1; + if (ret == BOOTMENU_RET_UPDATED) + continue; + + if (!IS_ENABLED(CONFIG_CMD_BOOTMENU_ENTER_UBOOT_CONSOLE)) { + if (ret == BOOTMENU_RET_QUIT) { + /* default boot process */ + if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) + run_command("bootefi bootmgr", 0); + + run_command("run bootcmd", 0); + } + } else { + break; + } + } + return -1; /* -1 - abort boot and run monitor code */ } #endif