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 particularly for managing boot parameters as well as examining
various EFI status for debugging. 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 config CMD_EXCEPTION
bool "exception - raise exception" bool "exception - raise exception"
depends on ARM || RISCV || SANDBOX || X86 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_CMD_EEPROM) += eeprom.o
obj-$(CONFIG_EFI) += efi.o obj-$(CONFIG_EFI) += efi.o
obj-$(CONFIG_CMD_EFIDEBUG) += efidebug.o obj-$(CONFIG_CMD_EFIDEBUG) += efidebug.o
obj-$(CONFIG_CMD_EFICONFIG) += eficonfig.o
obj-$(CONFIG_CMD_ELF) += elf.o obj-$(CONFIG_CMD_ELF) += elf.o
obj-$(CONFIG_CMD_EROFS) += erofs.o obj-$(CONFIG_CMD_EROFS) += erofs.o
obj-$(CONFIG_HUSH_PARSER) += exit.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, \ EFI_GUID(0x63293792, 0xadf5, 0x9325, \
0xb9, 0x9f, 0x4e, 0x0e, 0x45, 0x5c, 0x1b, 0x1e) 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 */ /* Use internal device tree when starting UEFI application */
#define EFI_FDT_USE_INTERNAL NULL #define EFI_FDT_USE_INTERNAL NULL
@ -226,6 +231,9 @@ const char *__efi_nesting_dec(void);
#define EFI_CACHELINE_SIZE 128 #define EFI_CACHELINE_SIZE 128
#endif #endif
/* max bootmenu title size for volume selection */
#define BOOTMENU_DEVICE_NAME_MAX 16
/* Key identifying current memory map */ /* Key identifying current memory map */
extern efi_uintn_t efi_memory_map_key; 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); 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 */ /* GUID of the U-Boot root node */
extern const efi_guid_t efi_u_boot_guid; extern const efi_guid_t efi_u_boot_guid;
#ifdef CONFIG_SANDBOX #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; extern const efi_guid_t efi_esrt_guid;
/* GUID of the SMBIOS table */ /* GUID of the SMBIOS table */
extern const efi_guid_t smbios_guid; 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_start[], __efi_runtime_stop[];
extern char __efi_runtime_rel_start[], __efi_runtime_rel_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 efi_load_capsule_drivers(void);
efi_status_t platform_get_eventlog(struct udevice *dev, u64 *addr, u32 *sz); 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 */ #endif /* _EFI_LOADER_H */

View file

@ -19,6 +19,9 @@
static const struct efi_boot_services *bs; static const struct efi_boot_services *bs;
static const struct efi_runtime_services *rs; 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 * bootmgr implements the logic of trying to find a payload to boot
* based on the BootOrder + BootXXXX variables, and then loading it. * 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); 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 * efi_locate_handle_buffer() - locate handles implementing a protocol
* @search_type: selection criterion * @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_uintn_t *no_handles, efi_handle_t **buffer)
{ {
efi_status_t r; efi_status_t r;
efi_uintn_t buffer_size = 0;
EFI_ENTRY("%d, %pUs, %p, %p, %p", search_type, protocol, search_key, EFI_ENTRY("%d, %pUs, %p, %p, %p", search_type, protocol, search_key,
no_handles, buffer); no_handles, buffer);
if (!no_handles || !buffer) { r = efi_locate_handle_buffer_int(search_type, protocol, search_key,
r = EFI_INVALID_PARAMETER; no_handles, buffer);
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 EFI_EXIT(r); return EFI_EXIT(r);
} }

View file

@ -7,6 +7,7 @@
#define LOG_CATEGORY LOGC_EFI #define LOG_CATEGORY LOGC_EFI
#include <ansi.h>
#include <common.h> #include <common.h>
#include <charset.h> #include <charset.h>
#include <malloc.h> #include <malloc.h>
@ -1318,3 +1319,72 @@ out_of_memory:
printf("ERROR: Out of memory\n"); printf("ERROR: Out of memory\n");
return r; 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; 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,7 +246,7 @@ error:
return NULL; return NULL;
} }
static efi_status_t efi_file_open_int(struct efi_file_handle *this, efi_status_t efi_file_open_int(struct efi_file_handle *this,
struct efi_file_handle **new_handle, struct efi_file_handle **new_handle,
u16 *file_name, u64 open_mode, u16 *file_name, u64 open_mode,
u64 attributes) u64 attributes)
@ -369,11 +369,17 @@ static efi_status_t file_close(struct file_handle *fh)
return EFI_SUCCESS; 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); 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); 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) static efi_status_t EFIAPI efi_file_delete(struct efi_file_handle *file)
@ -562,7 +568,7 @@ static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size,
return EFI_SUCCESS; return EFI_SUCCESS;
} }
static efi_status_t efi_file_read_int(struct efi_file_handle *this, efi_status_t efi_file_read_int(struct efi_file_handle *this,
efi_uintn_t *buffer_size, void *buffer) efi_uintn_t *buffer_size, void *buffer)
{ {
struct file_handle *fh = to_fh(this); struct file_handle *fh = to_fh(this);
@ -773,24 +779,11 @@ out:
return EFI_EXIT(ret); return EFI_EXIT(ret);
} }
/** efi_status_t efi_file_setpos_int(struct efi_file_handle *file, u64 pos)
* 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)
{ {
struct file_handle *fh = to_fh(file); struct file_handle *fh = to_fh(file);
efi_status_t ret = EFI_SUCCESS; efi_status_t ret = EFI_SUCCESS;
EFI_ENTRY("%p, %llu", file, pos);
if (fh->isdir) { if (fh->isdir) {
if (pos != 0) { if (pos != 0) {
ret = EFI_UNSUPPORTED; ret = EFI_UNSUPPORTED;
@ -812,6 +805,28 @@ static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file,
fh->offset = pos; fh->offset = pos;
error: 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); return EFI_EXIT(ret);
} }
@ -1138,17 +1153,23 @@ struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp)
return f; return f;
} }
static efi_status_t EFIAPI efi_status_t efi_open_volume_int(struct efi_simple_file_system_protocol *this,
efi_open_volume(struct efi_simple_file_system_protocol *this,
struct efi_file_handle **root) struct efi_file_handle **root)
{ {
struct file_system *fs = to_fs(this); struct file_system *fs = to_fs(this);
EFI_ENTRY("%p, %p", this, root);
*root = file_open(fs, NULL, NULL, 0, 0); *root = file_open(fs, NULL, NULL, 0, 0);
return EFI_EXIT(EFI_SUCCESS); return EFI_SUCCESS;
}
static efi_status_t EFIAPI
efi_open_volume(struct efi_simple_file_system_protocol *this,
struct efi_file_handle **root)
{
EFI_ENTRY("%p, %p", this, root);
return EFI_EXIT(efi_open_volume_int(this, root));
} }
struct efi_simple_file_system_protocol * struct efi_simple_file_system_protocol *