Pull request for efi-2022-07-rc2-2
* Test Unit test for 'bootmenu' command * UEFI Preparatory patches for implementing a UEFI boot options based menu -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEbcT5xx8ppvoGt20zxIHbvCwFGsQFAmJyoMcACgkQxIHbvCwF GsTbjA/+J+mQg5Rl8AkWwxnBynshNbOsQjsekJjoQTKN9XdvA4Rest2cP7AZ7LP7 yi2jks4qMKex5NyGSt2wV51snwZJ6ikZshYpEl59wzoOa4DNlBktH/emOVPNhHqm n+By+m1Uer7I1NBATfGLjgp7Pwk910OkX9sXdFBtg3OlQwBnoCgNARmxxz7ifWwa BxZP2w7l8BfejeZyS/HBzvUc8tPHljukidZoXANXJ/wbJmiU1JR+MBU4+iJmAwCi rt4loje8ynIiPd0NsfDdasqsNGXNtKf1ikY9xH7UBmv8lmkM1BLE+SSufb+8ihUV 5/hn4Q/nXze4klNM/owkglfsiBzs1tGIh1EBmuD9BBjBKKTHMUqSCU1f30cqc+oh 80heH8J6GicqlQlaN5WyYbimH7WvGz48dr8VrWM5AuFf8FjZlNfIlkt86h4yQBjc v6aTvNOXyLriFUh1p9DaQi1+U2ZdB2UO7wpEuWdVbgTE/yH9ZqmBNCB8kGTH1TPk pOwMDZPIFTvHp3WirztpxZRcPus/Eo7s1noMGzM/gdQjmIMRlkEbP8T617hoXWdZ /T4xwRyArQFCkx5xhd5nNqpLd3Lgq4ezpw5ZFAmULJZaGYpp6Iaf+IWiTYghrZ2/ z5cfeQZVL7Sb7vb/yajtVdaZzKg8Eq1FeZdDnoBayvL9UdFTBWA= =bl7A -----END PGP SIGNATURE----- Merge tag 'efi-2022-07-rc2-2' of https://source.denx.de/u-boot/custodians/u-boot-efi Pull request for efi-2022-07-rc2-2 * Test Unit test for 'bootmenu' command * UEFI Preparatory patches for implementing a UEFI boot options based menu
This commit is contained in:
commit
1739a6db54
11
cmd/Kconfig
11
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
|
||||
|
345
cmd/bootmenu.c
345
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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
*
|
||||
|
45
include/efi_default_filename.h
Normal file
45
include/efi_default_filename.h
Normal file
@ -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
|
@ -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,
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
46
test/py/tests/test_bootmenu.py
Normal file
46
test/py/tests/test_bootmenu.py
Normal file
@ -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')
|
@ -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'):
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user