eficonfig: menu-driven addition of UEFI boot option

This commit add the "eficonfig" command.
The "eficonfig" command implements the menu-driven UEFI boot option
maintenance feature. This commit implements the addition of
new boot option. User can select the block device volume having
efi_simple_file_system_protocol and select the file corresponding
to the Boot#### variable. User can also enter the description and
optional_data of the BOOT#### variable in utf8.

This commit adds "include/efi_config.h", it contains the common
definition to be used from other menus such as UEFI Secure Boot
key management.

Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
This commit is contained in:
Masahisa Kojima 2022-09-12 17:33:50 +09:00 committed by Heinrich Schuchardt
parent c2238fcf0c
commit 87d791423a
10 changed files with 1952 additions and 47 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

@ -63,6 +63,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

1608
cmd/eficonfig.c Normal file

File diff suppressed because it is too large Load Diff

96
include/efi_config.h Normal file
View File

@ -0,0 +1,96 @@
/* 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_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);
#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[];
@ -1064,4 +1077,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

@ -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.

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 *