Pull request for efi next

UEFI:
 
 Implement a command eficonfig to maintain Load Options and boot order via
 menus.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEbcT5xx8ppvoGt20zxIHbvCwFGsQFAmMh/kQACgkQxIHbvCwF
 GsSYVhAAlhyCiAQudBJtrOyAA0HDPMio/Fx9F3YV1Laay//6+xHhEh5H94fMr7jP
 BHOrpE+IDKQTiQ8X3FpJpm3FzgQBzu0s4gQ+8fCKwkXCkaPTtWhs3s5SdHN0TWmv
 qGlNbuPdoU+4i7PeBWw87EvOCzh4+snn+l3t+npMxUlZJInGy+6xj0irnSEXTvXV
 lpDRw2UDq3/smAS8tAcR+zriTH5o1Lx+0qPIO9Hw8BSDHOcvZD+S9d6cd6j+M+Wz
 rWk1Bshol6anIZWS5Gnx14ddKF8SQcSiPsDXg2Uj2DvFg7lWepKxAbDJD5CVF9R9
 aEu6THCurc+769A/IHAS9M0EDNnWoUGFSxe0JuEHdYVuGj30RRfzSaciKkNxsxUZ
 UGvDnQ+zdRoj6bTVi4Zf8SeZUTm/04lWMoIF05uhZFDiwxidbMowoNsk9FeKria/
 4vXybfj/IhRwbiAhOUqyDjGuoLj8iVeOAyzin4FnDvBwdshIkSsB+4y1sX1aQv02
 HQbP6WXkiYoYNkw5i9E9ZChioxkzMrn2za3GxWhkTBMJArDw6xSuHMdQsgwWWoqU
 fCKjwe+D96lHq5cKt3DmGTvGxQtx9ckSJGK5r+US5y1udzotjOKc1jqMkMvS3Jyg
 ureTj0wvLYSx+/UirVAZ4IDviDMBVEar79VS2xi+SYBMLeQPy3I=
 =P89S
 -----END PGP SIGNATURE-----

Merge tag 'efi-next-2022-09-14' of https://source.denx.de/u-boot/custodians/u-boot-efi into next

Pull request for efi next

UEFI:

Implement a command eficonfig to maintain Load Options and boot order via
menus.
This commit is contained in:
Tom Rini 2022-09-14 12:31:44 -04:00
commit 1520af3f84
19 changed files with 3437 additions and 53 deletions

View file

@ -1928,6 +1928,13 @@ config CMD_EFIDEBUG
particularly for managing boot parameters as well as examining
various EFI status for debugging.
config CMD_EFICONFIG
bool "eficonfig - provide menu-driven uefi variables maintenance interface"
depends on CMD_BOOTEFI_BOOTMGR
help
Enable the 'eficonfig' command which provides the menu-driven UEFI
variable maintenance interface.
config CMD_EXCEPTION
bool "exception - raise exception"
depends on ARM || RISCV || SANDBOX || X86

View file

@ -64,6 +64,7 @@ obj-$(CONFIG_ENV_IS_IN_EEPROM) += eeprom.o
obj-$(CONFIG_CMD_EEPROM) += eeprom.o
obj-$(CONFIG_EFI) += efi.o
obj-$(CONFIG_CMD_EFIDEBUG) += efidebug.o
obj-$(CONFIG_CMD_EFICONFIG) += eficonfig.o
obj-$(CONFIG_CMD_ELF) += elf.o
obj-$(CONFIG_CMD_EROFS) += erofs.o
obj-$(CONFIG_HUSH_PARSER) += exit.o

View file

@ -7,7 +7,7 @@
#include <common.h>
#include <command.h>
#include <ansi.h>
#include <efi_loader.h>
#include <efi_config.h>
#include <efi_variable.h>
#include <env.h>
#include <log.h>
@ -220,7 +220,7 @@ static int prepare_bootmenu_entry(struct bootmenu_data *menu,
return 1;
}
#if (CONFIG_IS_ENABLED(CMD_BOOTEFI_BOOTMGR))
#if (CONFIG_IS_ENABLED(CMD_BOOTEFI_BOOTMGR)) && (CONFIG_IS_ENABLED(CMD_EFICONFIG))
/**
* prepare_uefi_bootorder_entry() - generate the uefi bootmenu entries
*
@ -340,11 +340,21 @@ static struct bootmenu_data *bootmenu_create(int delay)
if (ret < 0)
goto cleanup;
#if (CONFIG_IS_ENABLED(CMD_BOOTEFI_BOOTMGR))
#if (CONFIG_IS_ENABLED(CMD_BOOTEFI_BOOTMGR)) && (CONFIG_IS_ENABLED(CMD_EFICONFIG))
if (i < MAX_COUNT - 1) {
ret = prepare_uefi_bootorder_entry(menu, &iter, &i);
if (ret < 0 && ret != -ENOENT)
goto cleanup;
efi_status_t efi_ret;
/*
* UEFI specification requires booting from removal media using
* a architecture-specific default image name such as BOOTAA64.EFI.
*/
efi_ret = eficonfig_generate_media_device_boot_option();
if (efi_ret != EFI_SUCCESS && efi_ret != EFI_NOT_FOUND)
goto cleanup;
ret = prepare_uefi_bootorder_entry(menu, &iter, &i);
if (ret < 0 && ret != -ENOENT)
goto cleanup;
}
#endif

2502
cmd/eficonfig.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -548,4 +548,13 @@ void bootmenu_loop(struct bootmenu_data *menu,
/* ^C was pressed */
if (c == 0x3)
*key = KEY_QUIT;
if (c == '+')
*key = KEY_PLUS;
if (c == '-')
*key = KEY_MINUS;
if (c == ' ')
*key = KEY_SPACE;
}

View file

@ -93,6 +93,7 @@ CONFIG_CMD_LINK_LOCAL=y
CONFIG_CMD_ETHSW=y
CONFIG_CMD_BMP=y
CONFIG_CMD_BOOTCOUNT=y
CONFIG_CMD_EFICONFIG=y
CONFIG_CMD_EFIDEBUG=y
CONFIG_CMD_RTC=y
CONFIG_CMD_TIME=y

View file

@ -4,6 +4,15 @@
bootmenu command
================
Synopsis
--------
::
bootmenu [delay]
Description
-----------
The "bootmenu" command uses U-Boot menu interfaces and provides
a simple mechanism for creating menus with different boot items.
The cursor keys "Up" and "Down" are used for navigation through
@ -79,6 +88,55 @@ The above example will be rendered as below::
The selected menu entry will be highlighted - it will have inverted
background and text colors.
UEFI boot variable enumeration
''''''''''''''''''''''''''''''
If enabled, the bootmenu command will automatically generate and add
UEFI-related boot menu entries for the following items.
* possible bootable media with default file names
* user-defined UEFI boot options
The bootmenu automatically enumerates the possible bootable
media devices supporting EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.
This auto generated entry is named as "<interface> <devnum>:<part>" format.
(e.g. "usb 0:1")
The bootmenu displays the UEFI-related menu entries in order of "BootOrder".
When the user selects the UEFI boot menu entry, the bootmenu sets
the selected boot variable index to "BootNext" without non-volatile attribute,
then call the uefi boot manager with the command "bootefi bootmgr".
Example bootmenu is as below::
*** U-Boot Boot Menu ***
mmc 0:1
mmc 0:2
debian
nvme 0:1
ubuntu
nvme 0:2
usb 0:2
U-Boot console
Default behavior when user exits from the bootmenu
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
User can exit from bootmenu by selecting the last entry
"U-Boot console"/"Quit" or ESC/CTRL+C key.
When the CONFIG_BOOTMENU_DISABLE_UBOOT_CONSOLE is disabled,
user exits from the bootmenu and returns to the U-Boot console.
When the CONFIG_BOOTMENU_DISABLE_UBOOT_CONSOLE is enabled, user can not
enter the U-Boot console. When the user exits from the bootmenu,
the bootmenu invokes the following default behavior.
* if CONFIG_CMD_BOOTEFI_BOOTMGR is enabled, execute "bootefi bootmgr" command
* "bootefi bootmgr" fails or is not enabled, then execute "run bootcmd" command.
Configuration
-------------
The "bootmenu" command is enabled by::
CONFIG_CMD_BOOTMENU=y
@ -88,3 +146,19 @@ To run the bootmenu at startup add these additional settings::
CONFIG_AUTOBOOT_KEYED=y
CONFIG_BOOTDELAY=30
CONFIG_AUTOBOOT_MENU_SHOW=y
UEFI boot variable enumeration is enabled by::
CONFIG_CMD_BOOTEFI_BOOTMGR=y
To improve the product security, entering U-Boot console from bootmenu
can be disabled by::
CONFIG_BOOTMENU_DISABLE_UBOOT_CONSOLE=y
To scan the discoverable devices connected to the buses such as
USB and PCIe prior to bootmenu showing up, CONFIG_PREBOOT can be
used to run the command before showing the bootmenu, i.e.::
CONFIG_USE_PREBOOT=y
CONFIG_PREBOOT="pci enum; usb start; scsi scan; nvme scan; virtio scan"

View file

@ -0,0 +1,71 @@
.. SPDX-License-Identifier: GPL-2.0+
.. (C) Copyright 2022, Masahisa Kojima <masahisa.kojima@linaro.org>
eficonfig command
=================
Synopsis
--------
::
eficonfig
Description
-----------
The "eficonfig" command uses U-Boot menu interface and provides
a menu-driven UEFI variable maintenance feature.
The "eficonfig" has the following menu entries.
Add Boot Option
Add new UEFI Boot Option.
User can edit description, file path, and optional_data.
Edit Boot Option
Edit the existing UEFI Boot Option
User can edit description, file path, and optional_data.
Change Boot Order
Change the order of UEFI BootOrder variable.
Delete Boot Option
Delete the UEFI Boot Option
Configuration
-------------
The "eficonfig" command is enabled by::
CONFIG_CMD_EFICONFIG=y
If CONFIG_BOOTMENU_DISABLE_UBOOT_CONSOLE is enabled, user can not enter
U-Boot console. In this case, bootmenu can be used to invoke "eficonfig"::
CONFIG_USE_PREBOOT=y
CONFIG_PREBOOT="setenv bootmenu_0 UEFI Maintenance Menu=eficonfig"
How to boot the system with newly added UEFI Boot Option
''''''''''''''''''''''''''''''''''''''''''''''''''''''''
"eficonfig" command is responsible for configuring the UEFI variables,
not directly handle the system boot.
The new Boot Option added by "eficonfig" is appended at the last entry
of UEFI BootOrder variable, user may want to change the boot order
through "Change Boot Order".
If the bootmenu is enabled, CONFIG_BOOTMENU_DISABLE_UBOOT_CONSOLE is enabled,
and "eficonfig" is configured as preboot command, the newly added Boot Options
are enumerated in the bootmenu when user exits from the eficonfig menu.
User may select the entry in the bootmenu to boot the system, or follow
the U-Boot configuration the system already has.
Auto boot with the UEFI Boot Option
'''''''''''''''''''''''''''''''''''
To do auto boot according to the UEFI BootOrder variable,
add "bootefi bootmgr" entry as a default or first bootmenu entry::
CONFIG_PREBOOT="setenv bootmenu_0 UEFI Boot Manager=bootefi bootmgr; setenv bootmenu_1 UEFI Maintenance Menu=eficonfig"
See also
--------
* :doc:`bootmenu<bootmenu>` provides a simple mechanism for creating menus with different boot items

View file

@ -36,6 +36,7 @@ Shell commands
cmd/cyclic
cmd/dm
cmd/echo
cmd/eficonfig
cmd/env
cmd/event
cmd/exception

98
include/efi_config.h Normal file
View file

@ -0,0 +1,98 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Menu-driven UEFI Variable maintenance
*
* Copyright (c) 2022 Masahisa Kojima, Linaro Limited
*/
#ifndef _EFI_CONFIG_H
#define _EFI_CONFIG_H
#include <efi_loader.h>
#define EFICONFIG_ENTRY_NUM_MAX 99
#define EFICONFIG_VOLUME_PATH_MAX 512
#define EFICONFIG_FILE_PATH_MAX 512
#define EFICONFIG_FILE_PATH_BUF_SIZE (EFICONFIG_FILE_PATH_MAX * sizeof(u16))
typedef efi_status_t (*eficonfig_entry_func)(void *data);
/**
* struct eficonfig_entry - menu entry structure
*
* @num: menu entry index
* @title: title of entry
* @key: unique key
* @efi_menu: pointer to the menu structure
* @func: callback function to be called when this entry is selected
* @data: data to be passed to the callback function, caller must free() this pointer
* @list: list structure
*/
struct eficonfig_entry {
u32 num;
char *title;
char key[3];
struct efimenu *efi_menu;
eficonfig_entry_func func;
void *data;
struct list_head list;
};
/**
* struct efimenu - efi menu structure
*
* @delay: delay for autoboot
* @active: active menu entry index
* @count: total count of menu entry
* @menu_header: menu header string
* @list: menu entry list structure
*/
struct efimenu {
int delay;
int active;
int count;
char *menu_header;
struct list_head list;
};
/**
* struct eficonfig_item - structure to construct eficonfig_entry
*
* @title: title of entry
* @func: callback function to be called when this entry is selected
* @data: data to be passed to the callback function
*/
struct eficonfig_item {
char *title;
eficonfig_entry_func func;
void *data;
};
/**
* struct eficonfig_select_file_info - structure to be used for file selection
*
* @current_volume: pointer to the efi_simple_file_system_protocol
* @dp_volume: pointer to device path of the selected device
* @current_path: pointer to the selected file path string
* @filepath_list: list_head structure for file path list
* @file_selectred: flag indicates file selecting status
*/
struct eficonfig_select_file_info {
struct efi_simple_file_system_protocol *current_volume;
struct efi_device_path *dp_volume;
u16 *current_path;
struct list_head filepath_list;
bool file_selected;
};
void eficonfig_print_msg(char *msg);
void eficonfig_destroy(struct efimenu *efi_menu);
efi_status_t eficonfig_process_quit(void *data);
efi_status_t eficonfig_process_common(struct efimenu *efi_menu, char *menu_header);
efi_status_t eficonfig_select_file_handler(void *data);
efi_status_t eficonfig_get_unused_bootoption(u16 *buf,
efi_uintn_t buf_size, u32 *index);
efi_status_t eficonfig_append_bootorder(u16 index);
efi_status_t eficonfig_generate_media_device_boot_option(void);
#endif

View file

@ -142,6 +142,11 @@ static inline efi_status_t efi_launch_capsules(void)
EFI_GUID(0x63293792, 0xadf5, 0x9325, \
0xb9, 0x9f, 0x4e, 0x0e, 0x45, 0x5c, 0x1b, 0x1e)
/* GUID for the auto generated boot menu entry */
#define EFICONFIG_AUTO_GENERATED_ENTRY_GUID \
EFI_GUID(0x38c1acc1, 0x9fc0, 0x41f0, \
0xb9, 0x01, 0xfa, 0x74, 0xd6, 0xd6, 0xe4, 0xde)
/* Use internal device tree when starting UEFI application */
#define EFI_FDT_USE_INTERNAL NULL
@ -226,6 +231,9 @@ const char *__efi_nesting_dec(void);
#define EFI_CACHELINE_SIZE 128
#endif
/* max bootmenu title size for volume selection */
#define BOOTMENU_DEVICE_NAME_MAX 16
/* Key identifying current memory map */
extern efi_uintn_t efi_memory_map_key;
@ -249,6 +257,9 @@ extern const struct efi_hii_string_protocol efi_hii_string;
uint16_t *efi_dp_str(struct efi_device_path *dp);
/* GUID for the auto generated boot menu entry */
extern const efi_guid_t efi_guid_bootmenu_auto_generated;
/* GUID of the U-Boot root node */
extern const efi_guid_t efi_u_boot_guid;
#ifdef CONFIG_SANDBOX
@ -314,6 +325,8 @@ extern const efi_guid_t efi_guid_firmware_management_protocol;
extern const efi_guid_t efi_esrt_guid;
/* GUID of the SMBIOS table */
extern const efi_guid_t smbios_guid;
/*GUID of console */
extern const efi_guid_t efi_guid_text_input_protocol;
extern char __efi_runtime_start[], __efi_runtime_stop[];
extern char __efi_runtime_rel_start[], __efi_runtime_rel_stop[];
@ -940,6 +953,22 @@ struct efi_signature_store {
struct x509_certificate;
struct pkcs7_message;
/**
* struct eficonfig_media_boot_option - boot option for (removable) media device
*
* This structure is used to enumerate possible boot option
*
* @lo: Serialized load option data
* @size: Size of serialized load option data
* @exist: Flag to indicate the load option already exists
* in Non-volatile load option
*/
struct eficonfig_media_boot_option {
void *lo;
efi_uintn_t size;
bool exist;
};
bool efi_hash_regions(struct image_region *regs, int count,
void **hash, const char *hash_algo, int *len);
bool efi_signature_lookup_digest(struct efi_image_regions *regs,
@ -1064,4 +1093,28 @@ efi_status_t efi_esrt_populate(void);
efi_status_t efi_load_capsule_drivers(void);
efi_status_t platform_get_eventlog(struct udevice *dev, u64 *addr, u32 *sz);
efi_status_t efi_locate_handle_buffer_int(enum efi_locate_search_type search_type,
const efi_guid_t *protocol, void *search_key,
efi_uintn_t *no_handles, efi_handle_t **buffer);
efi_status_t efi_open_volume_int(struct efi_simple_file_system_protocol *this,
struct efi_file_handle **root);
efi_status_t efi_file_open_int(struct efi_file_handle *this,
struct efi_file_handle **new_handle,
u16 *file_name, u64 open_mode,
u64 attributes);
efi_status_t efi_file_close_int(struct efi_file_handle *file);
efi_status_t efi_file_read_int(struct efi_file_handle *this,
efi_uintn_t *buffer_size, void *buffer);
efi_status_t efi_file_setpos_int(struct efi_file_handle *file, u64 pos);
typedef efi_status_t (*efi_console_filter_func)(struct efi_input_key *key);
efi_status_t efi_console_get_u16_string
(struct efi_simple_text_input_protocol *cin,
u16 *buf, efi_uintn_t count, efi_console_filter_func filer_func,
int row, int col);
efi_status_t efi_disk_get_device_name(const efi_handle_t handle, char *buf, int size);
#endif /* _EFI_LOADER_H */

View file

@ -48,6 +48,9 @@ enum bootmenu_key {
KEY_DOWN,
KEY_SELECT,
KEY_QUIT,
KEY_PLUS,
KEY_MINUS,
KEY_SPACE,
};
void bootmenu_autoboot_loop(struct bootmenu_data *menu,

View file

@ -19,6 +19,9 @@
static const struct efi_boot_services *bs;
static const struct efi_runtime_services *rs;
const efi_guid_t efi_guid_bootmenu_auto_generated =
EFICONFIG_AUTO_GENERATED_ENTRY_GUID;
/*
* bootmgr implements the logic of trying to find a payload to boot
* based on the BootOrder + BootXXXX variables, and then loading it.
@ -243,6 +246,10 @@ static efi_status_t try_load_entry(u16 n, efi_handle_t *handle,
}
/* Set load options */
if (size >= sizeof(efi_guid_t) &&
!guidcmp(lo.optional_data, &efi_guid_bootmenu_auto_generated))
size = 0;
if (size) {
*load_options = malloc(size);
if (!*load_options) {

View file

@ -2453,6 +2453,35 @@ static efi_status_t EFIAPI efi_protocols_per_handle(
return EFI_EXIT(EFI_SUCCESS);
}
efi_status_t efi_locate_handle_buffer_int(enum efi_locate_search_type search_type,
const efi_guid_t *protocol, void *search_key,
efi_uintn_t *no_handles, efi_handle_t **buffer)
{
efi_status_t r;
efi_uintn_t buffer_size = 0;
if (!no_handles || !buffer) {
r = EFI_INVALID_PARAMETER;
goto out;
}
*no_handles = 0;
*buffer = NULL;
r = efi_locate_handle(search_type, protocol, search_key, &buffer_size,
*buffer);
if (r != EFI_BUFFER_TOO_SMALL)
goto out;
r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
(void **)buffer);
if (r != EFI_SUCCESS)
goto out;
r = efi_locate_handle(search_type, protocol, search_key, &buffer_size,
*buffer);
if (r == EFI_SUCCESS)
*no_handles = buffer_size / sizeof(efi_handle_t);
out:
return r;
}
/**
* efi_locate_handle_buffer() - locate handles implementing a protocol
* @search_type: selection criterion
@ -2474,30 +2503,13 @@ efi_status_t EFIAPI efi_locate_handle_buffer(
efi_uintn_t *no_handles, efi_handle_t **buffer)
{
efi_status_t r;
efi_uintn_t buffer_size = 0;
EFI_ENTRY("%d, %pUs, %p, %p, %p", search_type, protocol, search_key,
no_handles, buffer);
if (!no_handles || !buffer) {
r = EFI_INVALID_PARAMETER;
goto out;
}
*no_handles = 0;
*buffer = NULL;
r = efi_locate_handle(search_type, protocol, search_key, &buffer_size,
*buffer);
if (r != EFI_BUFFER_TOO_SMALL)
goto out;
r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
(void **)buffer);
if (r != EFI_SUCCESS)
goto out;
r = efi_locate_handle(search_type, protocol, search_key, &buffer_size,
*buffer);
if (r == EFI_SUCCESS)
*no_handles = buffer_size / sizeof(efi_handle_t);
out:
r = efi_locate_handle_buffer_int(search_type, protocol, search_key,
no_handles, buffer);
return EFI_EXIT(r);
}

View file

@ -7,6 +7,7 @@
#define LOG_CATEGORY LOGC_EFI
#include <ansi.h>
#include <common.h>
#include <charset.h>
#include <malloc.h>
@ -1318,3 +1319,72 @@ out_of_memory:
printf("ERROR: Out of memory\n");
return r;
}
/**
* efi_console_get_u16_string() - get user input string
*
* @cin: protocol interface to EFI_SIMPLE_TEXT_INPUT_PROTOCOL
* @buf: buffer to store user input string in UTF16
* @count: number of u16 string including NULL terminator that buf has
* @filter_func: callback to filter user input
* @row: row number to locate user input form
* @col: column number to locate user input form
* Return: status code
*/
efi_status_t efi_console_get_u16_string(struct efi_simple_text_input_protocol *cin,
u16 *buf, efi_uintn_t count,
efi_console_filter_func filter_func,
int row, int col)
{
efi_status_t ret;
efi_uintn_t len = 0;
struct efi_input_key key;
printf(ANSI_CURSOR_POSITION
ANSI_CLEAR_LINE_TO_END
ANSI_CURSOR_SHOW, row, col);
ret = EFI_CALL(cin->reset(cin, false));
if (ret != EFI_SUCCESS)
return ret;
for (;;) {
do {
ret = EFI_CALL(cin->read_key_stroke(cin, &key));
mdelay(10);
} while (ret == EFI_NOT_READY);
if (key.unicode_char == u'\b') {
if (len > 0)
buf[--len] = u'\0';
printf(ANSI_CURSOR_POSITION
"%ls"
ANSI_CLEAR_LINE_TO_END, row, col, buf);
continue;
} else if (key.unicode_char == u'\r') {
buf[len] = u'\0';
return EFI_SUCCESS;
} else if (key.unicode_char == 0x3 || key.scan_code == 23) {
return EFI_ABORTED;
} else if (key.unicode_char < 0x20) {
/* ignore control codes other than Ctrl+C, '\r' and '\b' */
continue;
} else if (key.scan_code != 0) {
/* only accept single ESC press for cancel */
continue;
}
if (filter_func) {
if (filter_func(&key) != EFI_SUCCESS)
continue;
}
if (len >= (count - 1))
continue;
buf[len] = key.unicode_char;
len++;
printf(ANSI_CURSOR_POSITION "%ls", row, col, buf);
}
}

View file

@ -760,3 +760,53 @@ efi_status_t efi_disk_init(void)
return EFI_SUCCESS;
}
/**
* efi_disk_get_device_name() - get U-Boot device name associated with EFI handle
*
* @handle: pointer to the EFI handle
* @buf: pointer to the buffer to store the string
* @size: size of buffer
* Return: status code
*/
efi_status_t efi_disk_get_device_name(const efi_handle_t handle, char *buf, int size)
{
int count;
int diskid;
enum uclass_id id;
unsigned int part;
struct udevice *dev;
struct blk_desc *desc;
const char *if_typename;
bool is_partition = false;
struct disk_part *part_data;
if (!handle || !buf || !size)
return EFI_INVALID_PARAMETER;
dev = handle->dev;
id = device_get_uclass_id(dev);
if (id == UCLASS_BLK) {
desc = dev_get_uclass_plat(dev);
} else if (id == UCLASS_PARTITION) {
desc = dev_get_uclass_plat(dev_get_parent(dev));
is_partition = true;
} else {
return EFI_INVALID_PARAMETER;
}
if_typename = blk_get_if_type_name(desc->if_type);
diskid = desc->devnum;
if (is_partition) {
part_data = dev_get_uclass_plat(dev);
part = part_data->partnum;
count = snprintf(buf, size, "%s %d:%d", if_typename, diskid, part);
} else {
count = snprintf(buf, size, "%s %d", if_typename, diskid);
}
if (count < 0 || (count + 1) > size)
return EFI_INVALID_PARAMETER;
return EFI_SUCCESS;
}

View file

@ -246,10 +246,10 @@ error:
return NULL;
}
static efi_status_t efi_file_open_int(struct efi_file_handle *this,
struct efi_file_handle **new_handle,
u16 *file_name, u64 open_mode,
u64 attributes)
efi_status_t efi_file_open_int(struct efi_file_handle *this,
struct efi_file_handle **new_handle,
u16 *file_name, u64 open_mode,
u64 attributes)
{
struct file_handle *fh = to_fh(this);
efi_status_t ret;
@ -369,11 +369,17 @@ static efi_status_t file_close(struct file_handle *fh)
return EFI_SUCCESS;
}
static efi_status_t EFIAPI efi_file_close(struct efi_file_handle *file)
efi_status_t efi_file_close_int(struct efi_file_handle *file)
{
struct file_handle *fh = to_fh(file);
return file_close(fh);
}
static efi_status_t EFIAPI efi_file_close(struct efi_file_handle *file)
{
EFI_ENTRY("%p", file);
return EFI_EXIT(file_close(fh));
return EFI_EXIT(efi_file_close_int(file));
}
static efi_status_t EFIAPI efi_file_delete(struct efi_file_handle *file)
@ -562,8 +568,8 @@ static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size,
return EFI_SUCCESS;
}
static efi_status_t efi_file_read_int(struct efi_file_handle *this,
efi_uintn_t *buffer_size, void *buffer)
efi_status_t efi_file_read_int(struct efi_file_handle *this,
efi_uintn_t *buffer_size, void *buffer)
{
struct file_handle *fh = to_fh(this);
efi_status_t ret = EFI_SUCCESS;
@ -773,24 +779,11 @@ out:
return EFI_EXIT(ret);
}
/**
* efi_file_setpos() - set current position in file
*
* This function implements the SetPosition service of the EFI file protocol.
* See the UEFI spec for details.
*
* @file: file handle
* @pos: new file position
* Return: status code
*/
static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file,
u64 pos)
efi_status_t efi_file_setpos_int(struct efi_file_handle *file, u64 pos)
{
struct file_handle *fh = to_fh(file);
efi_status_t ret = EFI_SUCCESS;
EFI_ENTRY("%p, %llu", file, pos);
if (fh->isdir) {
if (pos != 0) {
ret = EFI_UNSUPPORTED;
@ -812,6 +805,28 @@ static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file,
fh->offset = pos;
error:
return ret;
}
/**
* efi_file_setpos() - set current position in file
*
* This function implements the SetPosition service of the EFI file protocol.
* See the UEFI spec for details.
*
* @file: file handle
* @pos: new file position
* Return: status code
*/
static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file,
u64 pos)
{
efi_status_t ret = EFI_SUCCESS;
EFI_ENTRY("%p, %llu", file, pos);
ret = efi_file_setpos_int(file, pos);
return EFI_EXIT(ret);
}
@ -1138,17 +1153,23 @@ struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp)
return f;
}
efi_status_t efi_open_volume_int(struct efi_simple_file_system_protocol *this,
struct efi_file_handle **root)
{
struct file_system *fs = to_fs(this);
*root = file_open(fs, NULL, NULL, 0, 0);
return EFI_SUCCESS;
}
static efi_status_t EFIAPI
efi_open_volume(struct efi_simple_file_system_protocol *this,
struct efi_file_handle **root)
{
struct file_system *fs = to_fs(this);
EFI_ENTRY("%p, %p", this, root);
*root = file_open(fs, NULL, NULL, 0, 0);
return EFI_EXIT(EFI_SUCCESS);
return EFI_EXIT(efi_open_volume_int(this, root));
}
struct efi_simple_file_system_protocol *

View file

@ -0,0 +1,40 @@
# SPDX-License-Identifier: GPL-2.0+
"""Fixture for UEFI eficonfig test
"""
import os
import shutil
from subprocess import check_call
import pytest
@pytest.fixture(scope='session')
def efi_eficonfig_data(u_boot_config):
"""Set up a file system to be used in UEFI "eficonfig" command
tests
Args:
u_boot_config -- U-boot configuration.
Return:
A path to disk image to be used for testing
"""
mnt_point = u_boot_config.persistent_data_dir + '/test_efi_eficonfig'
image_path = u_boot_config.persistent_data_dir + '/efi_eficonfig.img'
shutil.rmtree(mnt_point, ignore_errors=True)
os.mkdir(mnt_point, mode = 0o755)
with open(mnt_point + '/initrd-1.img', 'w', encoding = 'ascii') as file:
file.write("initrd 1")
with open(mnt_point + '/initrd-2.img', 'w', encoding = 'ascii') as file:
file.write("initrd 2")
shutil.copyfile(u_boot_config.build_dir + '/lib/efi_loader/initrddump.efi',
mnt_point + '/initrddump.efi')
check_call(f'virt-make-fs --partition=gpt --size=+1M --type=vfat {mnt_point} {image_path}',
shell=True)
return image_path

View file

@ -0,0 +1,354 @@
# SPDX-License-Identifier: GPL-2.0+
""" Unit test for UEFI menu-driven configuration
"""
import pytest
import time
@pytest.mark.boardspec('sandbox')
@pytest.mark.buildconfigspec('cmd_eficonfig')
@pytest.mark.buildconfigspec('cmd_bootefi_bootmgr')
def test_efi_eficonfig(u_boot_console, efi_eficonfig_data):
def send_user_input_and_wait(user_str, expect_str):
time.sleep(0.1) # TODO: does not work correctly without sleep
u_boot_console.run_command(cmd=user_str, wait_for_prompt=False,
wait_for_echo=True, send_nl=False)
u_boot_console.run_command(cmd='\x0d', wait_for_prompt=False,
wait_for_echo=False, send_nl=False)
if expect_str is not None:
for i in expect_str:
u_boot_console.p.expect([i])
def press_up_down_enter_and_wait(up_count, down_count, enter, expect_str):
# press UP key
for i in range(up_count):
u_boot_console.run_command(cmd='\x1b\x5b\x41', wait_for_prompt=False,
wait_for_echo=False, send_nl=False)
# press DOWN key
for i in range(down_count):
u_boot_console.run_command(cmd='\x1b\x5b\x42', wait_for_prompt=False,
wait_for_echo=False, send_nl=False)
# press ENTER if requested
if enter:
u_boot_console.run_command(cmd='\x0d', wait_for_prompt=False,
wait_for_echo=False, send_nl=False)
# wait expected output
if expect_str is not None:
for i in expect_str:
u_boot_console.p.expect([i])
def press_escape_key(wait_prompt):
u_boot_console.run_command(cmd='\x1b', wait_for_prompt=wait_prompt, wait_for_echo=False, send_nl=False)
def press_enter_key(wait_prompt):
u_boot_console.run_command(cmd='\x0d', wait_for_prompt=wait_prompt,
wait_for_echo=False, send_nl=False)
def check_current_is_maintenance_menu():
for i in ('UEFI Maintenance Menu', 'Add Boot Option', 'Edit Boot Option',
'Change Boot Order', 'Delete Boot Option', 'Quit'):
u_boot_console.p.expect([i])
""" Unit test for "eficonfig" command
The menu-driven interface is used to set up UEFI load options.
The bootefi bootmgr loads initrddump.efi as a payload.
The crc32 of the loaded initrd.img is checked
Args:
u_boot_console -- U-Boot console
efi__data -- Path to the disk image used for testing.
Test disk image has following files.
initrd-1.img
initrd-2.img
initrddump.efi
"""
# Restart the system to clean the previous state
u_boot_console.restart_uboot()
with u_boot_console.temporary_timeout(500):
#
# Test Case 1: Check the menu is displayed
#
u_boot_console.run_command('eficonfig', wait_for_prompt=False)
for i in ('UEFI Maintenance Menu', 'Add Boot Option', 'Edit Boot Option',
'Change Boot Order', 'Delete Boot Option', 'Quit'):
u_boot_console.p.expect([i])
# Select "Add Boot Option"
press_enter_key(False)
for i in ('Add Boot Option', 'Description:', 'File', 'Initrd File', 'Optional Data',
'Save', 'Quit'):
u_boot_console.p.expect([i])
press_escape_key(False)
check_current_is_maintenance_menu()
# return to U-Boot console
press_escape_key(True)
#
# Test Case 2: check auto generated media device entry
#
# bind the test disk image for succeeding tests
u_boot_console.run_command(cmd = f'host bind 0 {efi_eficonfig_data}')
u_boot_console.run_command('eficonfig', wait_for_prompt=False)
# Change the Boot Order
press_up_down_enter_and_wait(0, 2, True, 'Quit')
for i in ('host 0:1', 'Save', 'Quit'):
u_boot_console.p.expect([i])
# disable auto generated boot option for succeeding test
u_boot_console.run_command(cmd=' ', wait_for_prompt=False,
wait_for_echo=False, send_nl=False)
# Save the BootOrder
press_up_down_enter_and_wait(0, 1, True, None)
check_current_is_maintenance_menu()
#
# Test Case 3: Add first Boot Option and load it
#
# Select 'Add Boot Option'
press_up_down_enter_and_wait(0, 0, True, 'Quit')
# Press the enter key to select 'Description:' entry, then enter Description
press_up_down_enter_and_wait(0, 0, True, 'enter description:')
# Send Description user input, press ENTER key to complete
send_user_input_and_wait('test 1', 'Quit')
# Set EFI image(initrddump.efi)
press_up_down_enter_and_wait(0, 1, True, 'Quit')
press_up_down_enter_and_wait(0, 0, True, 'host 0:1')
# Select 'host 0:1'
press_up_down_enter_and_wait(0, 0, True, 'Quit')
# Press down key to select "initrddump.efi" entry followed by the enter key
press_up_down_enter_and_wait(0, 2, True, 'Quit')
# Set Initrd file(initrd-1.img)
press_up_down_enter_and_wait(0, 2, True, 'Quit')
press_up_down_enter_and_wait(0, 0, True, 'host 0:1')
# Select 'host 0:1'
press_up_down_enter_and_wait(0, 0, True, 'Quit')
# Press down key to select "initrd-1.img" entry followed by the enter key
press_up_down_enter_and_wait(0, 0, True, 'Quit')
# Set optional_data
press_up_down_enter_and_wait(0, 3, True, 'Optional Data:')
# Send Description user input, press ENTER key to complete
send_user_input_and_wait('nocolor', None)
for i in ('Description: test 1', 'File: host 0:1/initrddump.efi',
'Initrd File: host 0:1/initrd-1.img', 'Optional Data: nocolor', 'Save', 'Quit'):
u_boot_console.p.expect([i])
# Save the Boot Option
press_up_down_enter_and_wait(0, 4, True, None)
check_current_is_maintenance_menu()
# Check the newly added Boot Option is handled correctly
# Return to U-Boot console
press_escape_key(True)
u_boot_console.run_command(cmd = 'bootefi bootmgr')
response = u_boot_console.run_command(cmd = 'load', wait_for_echo=False)
assert 'crc32: 0x181464af' in response
u_boot_console.run_command(cmd = 'exit', wait_for_echo=False)
#
# Test Case 4: Add second Boot Option and load it
#
u_boot_console.run_command('eficonfig', wait_for_prompt=False)
# Select 'Add Boot Option'
press_up_down_enter_and_wait(0, 0, True, 'Quit')
# Press the enter key to select 'Description:' entry, then enter Description
press_up_down_enter_and_wait(0, 0, True, 'enter description:')
# Send Description user input, press ENTER key to complete
send_user_input_and_wait('test 2', 'Quit')
# Set EFI image(initrddump.efi)
press_up_down_enter_and_wait(0, 1, True, 'Quit')
press_up_down_enter_and_wait(0, 0, True, 'host 0:1')
# Select 'host 0:1'
press_up_down_enter_and_wait(0, 0, True, 'Quit')
# Press down key to select "initrddump.efi" entry followed by the enter key
press_up_down_enter_and_wait(0, 2, True, 'Quit')
# Set Initrd file(initrd-2.img)
press_up_down_enter_and_wait(0, 2, True, 'Quit')
press_up_down_enter_and_wait(0, 0, True, 'host 0:1')
# Select 'host 0:1'
press_up_down_enter_and_wait(0, 0, True, 'Quit')
# Press down key to select "initrd-2.img" entry followed by the enter key
press_up_down_enter_and_wait(0, 1, True, 'Quit')
# Set optional_data
press_up_down_enter_and_wait(0, 3, True, 'Optional Data:')
# Send Description user input, press ENTER key to complete
send_user_input_and_wait('nocolor', None)
for i in ('Description: test 2', 'File: host 0:1/initrddump.efi',
'Initrd File: host 0:1/initrd-2.img', 'Optional Data: nocolor', 'Save', 'Quit'):
u_boot_console.p.expect([i])
# Save the Boot Option
press_up_down_enter_and_wait(0, 4, True, 'Quit')
# Change the Boot Order
press_up_down_enter_and_wait(0, 2, True, 'Quit')
press_up_down_enter_and_wait(0, 1, False, 'Quit')
# move 'test 1' to the second entry
u_boot_console.run_command(cmd='+', wait_for_prompt=False,
wait_for_echo=False, send_nl=False)
for i in ('test 2', 'test 1', 'host 0:1', 'Save', 'Quit'):
u_boot_console.p.expect([i])
# Save the BootOrder
press_up_down_enter_and_wait(0, 3, True, None)
check_current_is_maintenance_menu()
# Check the newly added Boot Option is handled correctly
# Return to U-Boot console
press_escape_key(True)
u_boot_console.run_command(cmd = 'bootefi bootmgr')
response = u_boot_console.run_command(cmd = 'load', wait_for_echo=False)
assert 'crc32: 0x811d3515' in response
u_boot_console.run_command(cmd = 'exit', wait_for_echo=False)
#
# Test Case 5: Change BootOrder and load it
#
u_boot_console.run_command('eficonfig', wait_for_prompt=False)
# Change the Boot Order
press_up_down_enter_and_wait(0, 2, True, None)
# Check the curren BootOrder
for i in ('test 2', 'test 1', 'host 0:1', 'Save', 'Quit'):
u_boot_console.p.expect([i])
# move 'test 2' to the second entry
u_boot_console.run_command(cmd='-', wait_for_prompt=False,
wait_for_echo=False, send_nl=False)
for i in ('test 1', 'test 2', 'host 0:1', 'Save', 'Quit'):
u_boot_console.p.expect([i])
# Save the BootOrder
press_up_down_enter_and_wait(0, 2, True, None)
check_current_is_maintenance_menu()
# Return to U-Boot console
press_escape_key(True)
u_boot_console.run_command(cmd = 'bootefi bootmgr')
response = u_boot_console.run_command(cmd = 'load', wait_for_echo=False)
assert 'crc32: 0x181464af' in response
u_boot_console.run_command(cmd = 'exit', wait_for_echo=False)
#
# Test Case 6: Delete Boot Option(label:test 2)
#
u_boot_console.run_command('eficonfig', wait_for_prompt=False)
# Select 'Delete Boot Option'
press_up_down_enter_and_wait(0, 3, True, None)
# Check the current BootOrder
for i in ('test 1', 'test 2', 'Quit'):
u_boot_console.p.expect([i])
# Delete 'test 2'
press_up_down_enter_and_wait(0, 1, True, None)
for i in ('test 1', 'Quit'):
u_boot_console.p.expect([i])
press_escape_key(False)
check_current_is_maintenance_menu()
# Return to U-Boot console
press_escape_key(True)
#
# Test Case 7: Edit Boot Option
#
u_boot_console.run_command('eficonfig', wait_for_prompt=False)
# Select 'Edit Boot Option'
press_up_down_enter_and_wait(0, 1, True, None)
# Check the curren BootOrder
for i in ('test 1', 'Quit'):
u_boot_console.p.expect([i])
press_up_down_enter_and_wait(0, 0, True, None)
for i in ('Description: test 1', 'File: host 0:1/initrddump.efi',
'Initrd File: host 0:1/initrd-1.img', 'Optional Data: nocolor', 'Save', 'Quit'):
u_boot_console.p.expect([i])
# Press the enter key to select 'Description:' entry, then enter Description
press_up_down_enter_and_wait(0, 0, True, 'enter description:')
# Send Description user input, press ENTER key to complete
send_user_input_and_wait('test 3', 'Quit')
# Set EFI image(initrddump.efi)
press_up_down_enter_and_wait(0, 1, True, 'Quit')
press_up_down_enter_and_wait(0, 0, True, 'host 0:1')
# Select 'host 0:1'
press_up_down_enter_and_wait(0, 0, True, 'Quit')
# Press down key to select "initrddump.efi" entry followed by the enter key
press_up_down_enter_and_wait(0, 2, True, 'Quit')
# Set Initrd file(initrd-2.img)
press_up_down_enter_and_wait(0, 2, True, 'Quit')
press_up_down_enter_and_wait(0, 0, True, 'host 0:1')
# Select 'host 0:1'
press_up_down_enter_and_wait(0, 0, True, 'Quit')
# Press down key to select "initrd-1.img" entry followed by the enter key
press_up_down_enter_and_wait(0, 1, True, 'Quit')
# Set optional_data
press_up_down_enter_and_wait(0, 3, True, 'Optional Data:')
# Send Description user input, press ENTER key to complete
send_user_input_and_wait('', None)
for i in ('Description: test 3', 'File: host 0:1/initrddump.efi',
'Initrd File: host 0:1/initrd-2.img', 'Optional Data:', 'Save', 'Quit'):
u_boot_console.p.expect([i])
# Save the Boot Option
press_up_down_enter_and_wait(0, 4, True, 'Quit')
press_escape_key(False)
check_current_is_maintenance_menu()
# Check the updated Boot Option is handled correctly
# Return to U-Boot console
press_escape_key(True)
u_boot_console.run_command(cmd = 'bootefi bootmgr')
response = u_boot_console.run_command(cmd = 'load', wait_for_echo=False)
assert 'crc32: 0x811d3515' in response
u_boot_console.run_command(cmd = 'exit', wait_for_echo=False)
#
# Test Case 8: Delete Boot Option(label:test 3)
#
u_boot_console.run_command('eficonfig', wait_for_prompt=False)
# Select 'Delete Boot Option'
press_up_down_enter_and_wait(0, 3, True, None)
# Check the curren BootOrder
for i in ('test 3', 'Quit'):
u_boot_console.p.expect([i])
# Delete 'test 3'
press_up_down_enter_and_wait(0, 0, True, 'Quit')
press_escape_key(False)
check_current_is_maintenance_menu()
# Return to U-Boot console
press_escape_key(True)
# remove the host device
u_boot_console.run_command(cmd = f'host bind -r 0')
#
# Test Case 9: No block device found
#
u_boot_console.run_command('eficonfig', wait_for_prompt=False)
# Select 'Add Boot Option'
press_up_down_enter_and_wait(0, 0, True, 'Quit')
# Set EFI image
press_up_down_enter_and_wait(0, 1, True, 'Quit')
press_up_down_enter_and_wait(0, 0, True, 'No block device found!')
press_escape_key(False)
check_current_is_maintenance_menu()
# Return to U-Boot console
press_escape_key(True)