diff options
-rw-r--r-- | cmd/Kconfig | 11 | ||||
-rw-r--r-- | cmd/bootmenu.c | 345 | ||||
-rw-r--r-- | common/menu.c | 5 | ||||
-rw-r--r-- | configs/sandbox_defconfig | 1 | ||||
-rw-r--r-- | doc/usage/cmd/bootmenu.rst | 4 | ||||
-rw-r--r-- | drivers/ufs/Kconfig | 1 | ||||
-rw-r--r-- | include/charset.h | 14 | ||||
-rw-r--r-- | include/efi_default_filename.h | 45 | ||||
-rw-r--r-- | include/efi_loader.h | 4 | ||||
-rw-r--r-- | lib/Kconfig | 5 | ||||
-rw-r--r-- | lib/charset.c | 16 | ||||
-rw-r--r-- | lib/efi/Kconfig | 1 | ||||
-rw-r--r-- | lib/efi_loader/Kconfig | 1 | ||||
-rw-r--r-- | lib/efi_loader/efi_bootmgr.c | 51 | ||||
-rw-r--r-- | lib/efi_loader/efi_boottime.c | 7 | ||||
-rw-r--r-- | lib/efi_loader/efi_console.c | 4 | ||||
-rw-r--r-- | lib/efi_selftest/Makefile | 4 | ||||
-rw-r--r-- | lib/efi_selftest/efi_selftest_tcg2.c | 8 | ||||
-rw-r--r-- | lib/efi_selftest/efi_selftest_unaligned.c | 9 | ||||
-rw-r--r-- | scripts/pylint.base | 1 | ||||
-rw-r--r-- | test/Kconfig | 1 | ||||
-rw-r--r-- | test/py/tests/test_bootmenu.py | 46 | ||||
-rw-r--r-- | test/py/tests/test_efi_capsule/test_capsule_firmware_raw.py | 42 | ||||
-rw-r--r-- | test/unicode_ut.c | 50 |
24 files changed, 576 insertions, 100 deletions
diff --git a/cmd/Kconfig b/cmd/Kconfig index 2b575a2b42..69c1814d24 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -353,9 +353,20 @@ source lib/efi_selftest/Kconfig config CMD_BOOTMENU bool "bootmenu" select MENU + select CHARSET 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 d573487272..ac85767246 100644 --- a/cmd/bootmenu.c +++ b/cmd/bootmenu.c @@ -3,9 +3,12 @@ * (C) Copyright 2011-2013 Pali Rohár <pali@kernel.org> */ +#include <charset.h> #include <common.h> #include <command.h> #include <ansi.h> +#include <efi_loader.h> +#include <efi_variable.h> #include <env.h> #include <log.h> #include <menu.h> @@ -24,11 +27,26 @@ */ #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, + BOOTMENU_TYPE_UEFI_BOOT_OPTION, +}; + 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) */ }; @@ -68,14 +86,12 @@ 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); - puts(entry->title); + printf("%ls", entry->title); if (reverse) puts(ANSI_COLOR_RESET); @@ -86,12 +102,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 +138,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); @@ -275,31 +287,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); @@ -308,23 +321,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; @@ -333,6 +346,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) @@ -347,13 +362,146 @@ static struct bootmenu_data *bootmenu_create(int delay) break; } + *index = i; + *current = iter; + + 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; + 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; + + 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)); if (!entry) goto cleanup; - entry->title = strdup("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; @@ -370,6 +518,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) @@ -407,8 +556,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,50 +565,81 @@ 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); } -static void bootmenu_show(int delay) +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 enum bootmenu_ret bootmenu_show(int delay) +{ + int cmd_ret; int init = 0; void *choice = NULL; - char *title = 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; + 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); 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) { @@ -478,8 +658,37 @@ 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); + + /* last entry is U-Boot console or Quit */ + if (iter->num == iter->menu->count - 1) { + ret = BOOTMENU_RET_QUIT; + goto cleanup; + } + } else { + goto cleanup; + } + + /* + * 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: @@ -493,21 +702,47 @@ cleanup: } if (title && command) { - debug("Starting entry '%s'\n", title); + debug("Starting entry '%ls'\n", title); free(title); - run_command(command, 0); + if (efi_ret == EFI_SUCCESS) + 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 diff --git a/common/menu.c b/common/menu.c index 5fb2ffbd06..f5fc6930a2 100644 --- a/common/menu.c +++ b/common/menu.c @@ -271,7 +271,10 @@ int menu_get_choice(struct menu *m, void **choice) if (!m || !choice) return -EINVAL; - if (!m->prompt || m->item_cnt == 1) + if (!m->item_cnt) + return -ENOENT; + + if (!m->prompt) return menu_default_choice(m, choice); return menu_interactive_choice(m, choice); diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index fe8ea4626d..c509a924e6 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 diff --git a/doc/usage/cmd/bootmenu.rst b/doc/usage/cmd/bootmenu.rst index 1016ac8ceb..9430f8c9aa 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 diff --git a/drivers/ufs/Kconfig b/drivers/ufs/Kconfig index c2aafd3020..69ea18edf8 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/include/charset.h b/include/charset.h index 38908e08f0..20abfbe752 100644 --- a/include/charset.h +++ b/include/charset.h @@ -262,6 +262,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 * * Converts 'size' characters of the utf16 string 'src' to utf8 diff --git a/include/efi_default_filename.h b/include/efi_default_filename.h new file mode 100644 index 0000000000..13b9de8754 --- /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 <xypron.glpk@gmx.de> + */ + +#ifndef _EFI_DEFAULT_FILENAME_H +#define _EFI_DEFAULT_FILENAME_H + +#include <host_arch.h> + +#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/include/efi_loader.h b/include/efi_loader.h index ba79a9afb4..effb43369d 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/Kconfig b/lib/Kconfig index e2697ab2ce..acc0ac081a 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/charset.c b/lib/charset.c index de201cf3b9..bece4985bf 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) { diff --git a/lib/efi/Kconfig b/lib/efi/Kconfig index 15ce99e1a7..c2b9bb73f7 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 6b245f50a7..eb2d6fddc4 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/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c index 52bea4d541..631a25d76e 100644 --- a/lib/efi_loader/efi_bootmgr.c +++ b/lib/efi_loader/efi_bootmgr.c @@ -11,6 +11,7 @@ #include <charset.h> #include <log.h> #include <malloc.h> +#include <efi_default_filename.h> #include <efi_loader.h> #include <efi_variable.h> #include <asm/unaligned.h> @@ -31,6 +32,51 @@ static const struct efi_runtime_services *rs; */ /** + * 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 * * Attempt to load load-option number 'n', returning device_path and file_path @@ -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); diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 5bcb8253ed..4da64b5d29 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; diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index ba68a15017..60a3fc85ac 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); } diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index be8040d1c7..33536c9ec0 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 diff --git a/lib/efi_selftest/efi_selftest_tcg2.c b/lib/efi_selftest/efi_selftest_tcg2.c index a2b4a79e9b..67a886efaa 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); diff --git a/lib/efi_selftest/efi_selftest_unaligned.c b/lib/efi_selftest/efi_selftest_unaligned.c index 6fce110b76..7c6bf2d6e8 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) { diff --git a/scripts/pylint.base b/scripts/pylint.base index 50e9989e87..9ebebf47ab 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/Kconfig b/test/Kconfig index e15ba239eb..7f3447ae5a 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. diff --git a/test/py/tests/test_bootmenu.py b/test/py/tests/test_bootmenu.py new file mode 100644 index 0000000000..b4baa534aa --- /dev/null +++ b/test/py/tests/test_bootmenu.py @@ -0,0 +1,46 @@ +# 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') + u_boot_console.run_command('setenv bootmenu_1') + u_boot_console.run_command('setenv bootmenu_2') 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 ae99f080ff..c8c647d0b1 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 <takahiro.akashi@linaro.org> -# -# 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'): diff --git a/test/unicode_ut.c b/test/unicode_ut.c index f2f63d5367..d104bd5997 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); |