Patch queue for efi - 2017-10-01

Lots of new things this time. High level highlights are:
 
   - Shim support (to boot Fedora)
   - Initial set of unit tests
   - Preparations to support UEFI Shell
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iQIcBAABAgAGBQJZ0KdpAAoJECszeR4D/txga+AQAL8ehBlkr3R6b/n00p3kuAVE
 cfM+o/16b+P/r3U2/wvlNtw7L+UPlGaQNk8o7n4xTu0DpVlIt7DhFrSBIcK6VbYB
 8BFJ/EL9NRj/sd/wb9B1Bc0SlXJcGxzRM+JxNseVmc7rz9sWdARvx/diQlP+YAX4
 qjp2YRzsErjUgix4eGDXvgFQdFmh4Pnp7UnHlSq4WkINo14navGLe/PwChxsL91M
 AZV7JulVcm7XtOPTON8udec3O15O8BExAM1cH2ITjtD99Je+4VcuI99nLm2aIE++
 U6vTuoogHAU+oCutD3qZU85uj2VJ2kYy2k5A/RK/i6Ug/QrTa+JA8PNWP78/TZ+8
 xmyDVzG5MQhMIddDjJlMXbxIwgKhr0SjUJ1iVaGI0bS/6Glc7h9m+hhIXKudqOvs
 lxqPhy7mahtBwETjoUlM1WJg+TG+vIGDJtT22njjOSTh0CVYw2H3xihyFKE3AjaW
 L26EOEsxArErtzF17GgF5KxFOGaYAmVmA3XnCcEZEIY5gzL65uok1GX/apf4sIsi
 j8HSbW3UH6QwjWKBhF+RVuK4CcTDUS9K2YmJlrdZ69elDZyg5PWbpMLNdYIP+VVG
 +j91c7e3O2q2AHAzTprpmkyeIeqZfFhgyu4H/jPQ8Yi8N/b7wbugcLm3fHuUNal4
 RB0KjbyJzJPQwbYGSLYi
 =9HkH
 -----END PGP SIGNATURE-----

Merge tag 'signed-efi-next' of git://github.com/agraf/u-boot

Patch queue for efi - 2017-10-01

Lots of new things this time. High level highlights are:

  - Shim support (to boot Fedora)
  - Initial set of unit tests
  - Preparations to support UEFI Shell
This commit is contained in:
Tom Rini 2017-10-01 08:48:38 -04:00
commit 958046fc78
38 changed files with 3802 additions and 405 deletions

View file

@ -259,8 +259,9 @@ EFI PAYLOAD
M: Alexander Graf <agraf@suse.de>
S: Maintained
T: git git://github.com/agraf/u-boot.git
F: include/efi_loader.h
F: lib/efi_loader/
F: include/efi*
F: lib/efi*
F: test/py/tests/test_efi*
F: cmd/bootefi.c
FLATTENED DEVICE TREE

View file

@ -222,6 +222,8 @@ config CMD_BOOTEFI_HELLO
for testing that EFI is working at a basic level, and for bringing
up EFI support on a new architecture.
source lib/efi_selftest/Kconfig
config CMD_BOOTMENU
bool "bootmenu"
select MENU

View file

@ -22,97 +22,14 @@ DECLARE_GLOBAL_DATA_PTR;
static uint8_t efi_obj_list_initalized;
/*
* When booting using the "bootefi" command, we don't know which
* physical device the file came from. So we create a pseudo-device
* called "bootefi" with the device path /bootefi.
*
* In addition to the originating device we also declare the file path
* of "bootefi" based loads to be /bootefi.
*/
static struct efi_device_path_file_path bootefi_image_path[] = {
{
.dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE,
.dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH,
.dp.length = sizeof(bootefi_image_path[0]),
.str = { 'b','o','o','t','e','f','i' },
}, {
.dp.type = DEVICE_PATH_TYPE_END,
.dp.sub_type = DEVICE_PATH_SUB_TYPE_END,
.dp.length = sizeof(bootefi_image_path[0]),
}
};
static struct efi_device_path_file_path bootefi_device_path[] = {
{
.dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE,
.dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH,
.dp.length = sizeof(bootefi_image_path[0]),
.str = { 'b','o','o','t','e','f','i' },
}, {
.dp.type = DEVICE_PATH_TYPE_END,
.dp.sub_type = DEVICE_PATH_SUB_TYPE_END,
.dp.length = sizeof(bootefi_image_path[0]),
}
};
/* The EFI loaded_image interface for the image executed via "bootefi" */
static struct efi_loaded_image loaded_image_info = {
.device_handle = bootefi_device_path,
.file_path = bootefi_image_path,
};
/* The EFI object struct for the image executed via "bootefi" */
static struct efi_object loaded_image_info_obj = {
.handle = &loaded_image_info,
.protocols = {
{
/*
* When asking for the loaded_image interface, just
* return handle which points to loaded_image_info
*/
.guid = &efi_guid_loaded_image,
.protocol_interface = &loaded_image_info,
},
{
/*
* When asking for the device path interface, return
* bootefi_device_path
*/
.guid = &efi_guid_device_path,
.protocol_interface = bootefi_device_path,
},
{
.guid = &efi_guid_console_control,
.protocol_interface = (void *) &efi_console_control
},
{
.guid = &efi_guid_device_path_to_text_protocol,
.protocol_interface = (void *) &efi_device_path_to_text
},
},
};
/* The EFI object struct for the device the "bootefi" image was loaded from */
static struct efi_object bootefi_device_obj = {
.handle = bootefi_device_path,
.protocols = {
{
/* When asking for the device path interface, return
* bootefi_device_path */
.guid = &efi_guid_device_path,
.protocol_interface = bootefi_device_path
}
},
};
static struct efi_device_path *bootefi_image_path;
static struct efi_device_path *bootefi_device_path;
/* Initialize and populate EFI object list */
static void efi_init_obj_list(void)
{
efi_obj_list_initalized = 1;
list_add_tail(&loaded_image_info_obj.link, &efi_obj_list);
list_add_tail(&bootefi_device_obj.link, &efi_obj_list);
efi_console_register();
#ifdef CONFIG_PARTITIONS
efi_disk_register();
@ -121,13 +38,7 @@ static void efi_init_obj_list(void)
efi_gop_register();
#endif
#ifdef CONFIG_NET
void *nethandle = loaded_image_info.device_handle;
efi_net_register(&nethandle);
if (!memcmp(bootefi_device_path[0].str, "N\0e\0t", 6))
loaded_image_info.device_handle = nethandle;
else
loaded_image_info.device_handle = bootefi_device_path;
efi_net_register();
#endif
#ifdef CONFIG_GENERATE_SMBIOS_TABLE
efi_smbios_register();
@ -210,14 +121,27 @@ static unsigned long efi_run_in_el2(asmlinkage ulong (*entry)(
* Load an EFI payload into a newly allocated piece of memory, register all
* EFI objects it would want to access and jump to it.
*/
static unsigned long do_bootefi_exec(void *efi, void *fdt)
static unsigned long do_bootefi_exec(void *efi, void *fdt,
struct efi_device_path *device_path,
struct efi_device_path *image_path)
{
struct efi_loaded_image loaded_image_info = {};
struct efi_object loaded_image_info_obj = {};
ulong ret;
ulong (*entry)(void *image_handle, struct efi_system_table *st)
asmlinkage;
ulong fdt_pages, fdt_size, fdt_start, fdt_end;
const efi_guid_t fdt_guid = EFI_FDT_GUID;
bootm_headers_t img = { 0 };
/* Initialize and populate EFI object list */
if (!efi_obj_list_initalized)
efi_init_obj_list();
efi_setup_loaded_image(&loaded_image_info, &loaded_image_info_obj,
device_path, image_path);
/*
* gd lives in a fixed register which may get clobbered while we execute
* the payload. So save it here and restore it on every callback entry
@ -252,18 +176,21 @@ static unsigned long do_bootefi_exec(void *efi, void *fdt)
/* Load the EFI payload */
entry = efi_load_pe(efi, &loaded_image_info);
if (!entry)
return -ENOENT;
if (!entry) {
ret = -ENOENT;
goto exit;
}
/* Initialize and populate EFI object list */
if (!efi_obj_list_initalized)
efi_init_obj_list();
/* we don't support much: */
env_set("efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_OsIndicationsSupported",
"{ro,boot}(blob)0000000000000000");
/* Call our payload! */
debug("%s:%d Jumping to 0x%lx\n", __func__, __LINE__, (long)entry);
if (setjmp(&loaded_image_info.exit_jmp)) {
return loaded_image_info.exit_status;
ret = loaded_image_info.exit_status;
goto exit;
}
#ifdef CONFIG_ARM64
@ -282,9 +209,45 @@ static unsigned long do_bootefi_exec(void *efi, void *fdt)
}
#endif
return efi_do_enter(&loaded_image_info, &systab, entry);
ret = efi_do_enter(&loaded_image_info, &systab, entry);
exit:
/* image has returned, loaded-image obj goes *poof*: */
list_del(&loaded_image_info_obj.link);
return ret;
}
static int do_bootefi_bootmgr_exec(unsigned long fdt_addr)
{
struct efi_device_path *device_path, *file_path;
void *addr;
efi_status_t r;
/* Initialize and populate EFI object list */
if (!efi_obj_list_initalized)
efi_init_obj_list();
/*
* gd lives in a fixed register which may get clobbered while we execute
* the payload. So save it here and restore it on every callback entry
*/
efi_save_gd();
addr = efi_bootmgr_load(&device_path, &file_path);
if (!addr)
return 1;
printf("## Starting EFI application at %p ...\n", addr);
r = do_bootefi_exec(addr, (void *)fdt_addr, device_path, file_path);
printf("## Application terminated, r = %lu\n",
r & ~EFI_ERROR_MASK);
if (r != EFI_SUCCESS)
return 1;
return 0;
}
/* Interpreter command to boot an arbitrary EFI image from memory */
static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
@ -297,13 +260,44 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
return CMD_RET_USAGE;
#ifdef CONFIG_CMD_BOOTEFI_HELLO
if (!strcmp(argv[1], "hello")) {
ulong size = __efi_hello_world_end - __efi_hello_world_begin;
ulong size = __efi_helloworld_end - __efi_helloworld_begin;
addr = CONFIG_SYS_LOAD_ADDR;
memcpy((char *)addr, __efi_hello_world_begin, size);
saddr = env_get("loadaddr");
if (saddr)
addr = simple_strtoul(saddr, NULL, 16);
else
addr = CONFIG_SYS_LOAD_ADDR;
memcpy((char *)addr, __efi_helloworld_begin, size);
} else
#endif
{
#ifdef CONFIG_CMD_BOOTEFI_SELFTEST
if (!strcmp(argv[1], "selftest")) {
struct efi_loaded_image loaded_image_info = {};
struct efi_object loaded_image_info_obj = {};
efi_setup_loaded_image(&loaded_image_info,
&loaded_image_info_obj,
bootefi_device_path, bootefi_image_path);
/*
* gd lives in a fixed register which may get clobbered while we
* execute the payload. So save it here and restore it on every
* callback entry
*/
efi_save_gd();
/* Initialize and populate EFI object list */
if (!efi_obj_list_initalized)
efi_init_obj_list();
return efi_selftest(&loaded_image_info, &systab);
} else
#endif
if (!strcmp(argv[1], "bootmgr")) {
unsigned long fdt_addr = 0;
if (argc > 2)
fdt_addr = simple_strtoul(argv[2], NULL, 16);
return do_bootefi_bootmgr_exec(fdt_addr);
} else {
saddr = argv[1];
addr = simple_strtoul(saddr, NULL, 16);
@ -315,7 +309,8 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
}
printf("## Starting EFI application at %08lx ...\n", addr);
r = do_bootefi_exec((void *)addr, (void*)fdt_addr);
r = do_bootefi_exec((void *)addr, (void *)fdt_addr,
bootefi_device_path, bootefi_image_path);
printf("## Application terminated, r = %lu\n",
r & ~EFI_ERROR_MASK);
@ -332,10 +327,18 @@ static char bootefi_help_text[] =
" If specified, the device tree located at <fdt address> gets\n"
" exposed as EFI configuration table.\n"
#ifdef CONFIG_CMD_BOOTEFI_HELLO
"hello\n"
" - boot a sample Hello World application stored within U-Boot"
"bootefi hello\n"
" - boot a sample Hello World application stored within U-Boot\n"
#endif
;
#ifdef CONFIG_CMD_BOOTEFI_SELFTEST
"bootefi selftest\n"
" - boot an EFI selftest application stored within U-Boot\n"
#endif
"bootmgr [fdt addr]\n"
" - load and boot EFI payload based on BootOrder/BootXXXX variables.\n"
"\n"
" If specified, the device tree located at <fdt address> gets\n"
" exposed as EFI configuration table.\n";
#endif
U_BOOT_CMD(
@ -344,58 +347,47 @@ U_BOOT_CMD(
bootefi_help_text
);
static int parse_partnum(const char *devnr)
{
const char *str = strchr(devnr, ':');
if (str) {
str++;
return simple_strtoul(str, NULL, 16);
}
return 0;
}
void efi_set_bootdev(const char *dev, const char *devnr, const char *path)
{
__maybe_unused struct blk_desc *desc;
char devname[32] = { 0 }; /* dp->str is u16[32] long */
char *colon, *s;
char filename[32] = { 0 }; /* dp->str is u16[32] long */
char *s;
#if defined(CONFIG_BLK) || CONFIG_IS_ENABLED(ISO_PARTITION)
desc = blk_get_dev(dev, simple_strtol(devnr, NULL, 10));
if (strcmp(dev, "Net")) {
struct blk_desc *desc;
int part;
desc = blk_get_dev(dev, simple_strtol(devnr, NULL, 10));
part = parse_partnum(devnr);
bootefi_device_path = efi_dp_from_part(desc, part);
} else {
#ifdef CONFIG_NET
bootefi_device_path = efi_dp_from_eth();
#endif
#ifdef CONFIG_BLK
if (desc) {
snprintf(devname, sizeof(devname), "%s", desc->bdev->name);
} else
#endif
{
/* Assemble the condensed device name we use in efi_disk.c */
snprintf(devname, sizeof(devname), "%s%s", dev, devnr);
}
colon = strchr(devname, ':');
if (!path)
return;
#if CONFIG_IS_ENABLED(ISO_PARTITION)
/* For ISOs we create partition block devices */
if (desc && (desc->type != DEV_TYPE_UNKNOWN) &&
(desc->part_type == PART_TYPE_ISO)) {
if (!colon)
snprintf(devname, sizeof(devname), "%s:1", devname);
colon = NULL;
}
#endif
if (colon)
*colon = '\0';
/* Patch bootefi_device_path to the target device */
memset(bootefi_device_path[0].str, 0, sizeof(bootefi_device_path[0].str));
ascii2unicode(bootefi_device_path[0].str, devname);
/* Patch bootefi_image_path to the target file path */
memset(bootefi_image_path[0].str, 0, sizeof(bootefi_image_path[0].str));
if (strcmp(dev, "Net")) {
/* Add leading / to fs paths, because they're absolute */
snprintf(devname, sizeof(devname), "/%s", path);
snprintf(filename, sizeof(filename), "/%s", path);
} else {
snprintf(devname, sizeof(devname), "%s", path);
snprintf(filename, sizeof(filename), "%s", path);
}
/* DOS style file path: */
s = devname;
s = filename;
while ((s = strchr(s, '/')))
*s++ = '\\';
ascii2unicode(bootefi_image_path[0].str, devname);
bootefi_image_path = efi_dp_from_file(NULL, 0, filename);
}

View file

@ -44,7 +44,7 @@ static inline int is_extended(int part_type)
static inline int is_bootable(dos_partition_t *p)
{
return p->boot_ind == 0x80;
return (p->sys_ind == 0xef) || (p->boot_ind == 0x80);
}
static void print_one_part(dos_partition_t *p, lbaint_t ext_part_sector,
@ -89,14 +89,20 @@ static int test_block_type(unsigned char *buffer)
static int part_test_dos(struct blk_desc *dev_desc)
{
ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz);
ALLOC_CACHE_ALIGN_BUFFER(legacy_mbr, mbr, dev_desc->blksz);
if (blk_dread(dev_desc, 0, 1, (ulong *)buffer) != 1)
if (blk_dread(dev_desc, 0, 1, (ulong *)mbr) != 1)
return -1;
if (test_block_type(buffer) != DOS_MBR)
if (test_block_type((unsigned char *)mbr) != DOS_MBR)
return -1;
if (dev_desc->sig_type == SIG_TYPE_NONE &&
mbr->unique_mbr_signature != 0) {
dev_desc->sig_type = SIG_TYPE_MBR;
dev_desc->mbr_sig = mbr->unique_mbr_signature;
}
return 0;
}

View file

@ -923,11 +923,19 @@ static int is_pmbr_valid(legacy_mbr * mbr)
static int is_gpt_valid(struct blk_desc *dev_desc, u64 lba,
gpt_header *pgpt_head, gpt_entry **pgpt_pte)
{
ALLOC_CACHE_ALIGN_BUFFER(legacy_mbr, mbr, dev_desc->blksz);
if (!dev_desc || !pgpt_head) {
printf("%s: Invalid Argument(s)\n", __func__);
return 0;
}
/* Read MBR Header from device */
if (blk_dread(dev_desc, 0, 1, (ulong *)mbr) != 1) {
printf("*** ERROR: Can't read MBR header ***\n");
return 0;
}
/* Read GPT Header from device */
if (blk_dread(dev_desc, (lbaint_t)lba, 1, pgpt_head) != 1) {
printf("*** ERROR: Can't read GPT header ***\n");
@ -937,6 +945,18 @@ static int is_gpt_valid(struct blk_desc *dev_desc, u64 lba,
if (validate_gpt_header(pgpt_head, (lbaint_t)lba, dev_desc->lba))
return 0;
if (dev_desc->sig_type == SIG_TYPE_NONE) {
efi_guid_t empty = {};
if (memcmp(&pgpt_head->disk_guid, &empty, sizeof(empty))) {
dev_desc->sig_type = SIG_TYPE_GUID;
memcpy(&dev_desc->guid_sig, &pgpt_head->disk_guid,
sizeof(empty));
} else if (mbr->unique_mbr_signature != 0) {
dev_desc->sig_type = SIG_TYPE_MBR;
dev_desc->mbr_sig = mbr->unique_mbr_signature;
}
}
/* Read and allocate Partition Table Entries */
*pgpt_pte = alloc_read_gpt_entries(dev_desc, pgpt_head);
if (*pgpt_pte == NULL) {

View file

@ -22,8 +22,8 @@ extern char __kprobes_text_start[], __kprobes_text_end[];
extern char __entry_text_start[], __entry_text_end[];
extern char __initdata_begin[], __initdata_end[];
extern char __start_rodata[], __end_rodata[];
extern char __efi_hello_world_begin[];
extern char __efi_hello_world_end[];
extern char __efi_helloworld_begin[];
extern char __efi_helloworld_end[];
/* Start and end of .ctors section - used for constructor calls. */
extern char __ctors_start[], __ctors_end[];

View file

@ -8,6 +8,8 @@
#ifndef BLK_H
#define BLK_H
#include <efi.h>
#ifdef CONFIG_SYS_64BIT_LBA
typedef uint64_t lbaint_t;
#define LBAFlength "ll"
@ -40,6 +42,17 @@ enum if_type {
#define BLK_PRD_SIZE 20
#define BLK_REV_SIZE 8
/*
* Identifies the partition table type (ie. MBR vs GPT GUID) signature
*/
enum sig_type {
SIG_TYPE_NONE,
SIG_TYPE_MBR,
SIG_TYPE_GUID,
SIG_TYPE_COUNT /* Number of signature types */
};
/*
* With driver model (CONFIG_BLK) this is uclass platform data, accessible
* with dev_get_uclass_platdata(dev)
@ -67,6 +80,11 @@ struct blk_desc {
char vendor[BLK_VEN_SIZE + 1]; /* device vendor string */
char product[BLK_PRD_SIZE + 1]; /* device product number */
char revision[BLK_REV_SIZE + 1]; /* firmware revision */
enum sig_type sig_type; /* Partition table signature type */
union {
uint32_t mbr_sig; /* MBR integer signature */
efi_guid_t guid_sig; /* GPT GUID Signature */
};
#if CONFIG_IS_ENABLED(BLK)
/*
* For now we have a few functions which take struct blk_desc as a

View file

@ -112,6 +112,11 @@
#define BOOTENV_SHARED_EFI \
"boot_efi_binary=" \
"if fdt addr ${fdt_addr_r}; then " \
"bootefi bootmgr ${fdt_addr_r};" \
"else " \
"bootefi bootmgr ${fdtcontroladdr};" \
"fi;" \
"load ${devtype} ${devnum}:${distro_bootpart} " \
"${kernel_addr_r} efi/boot/"BOOTEFI_NAME"; " \
"if fdt addr ${fdt_addr_r}; then " \

View file

@ -28,6 +28,10 @@
struct efi_device_path;
typedef struct {
u8 b[16];
} efi_guid_t;
#define EFI_BITS_PER_LONG BITS_PER_LONG
/*
@ -77,6 +81,8 @@ struct efi_device_path;
#define EFI_IP_ADDRESS_CONFLICT (EFI_ERROR_MASK | 34)
#define EFI_HTTP_ERROR (EFI_ERROR_MASK | 35)
#define EFI_WARN_DELETE_FAILURE 2
typedef unsigned long efi_status_t;
typedef u64 efi_physical_addr_t;
typedef u64 efi_virtual_addr_t;
@ -116,7 +122,7 @@ enum efi_mem_type {
/* The code portions of a loaded Boot Services Driver */
EFI_BOOT_SERVICES_CODE,
/*
* The data portions of a loaded Boot Serves Driver and
* The data portions of a loaded Boot Services Driver and
* the default data allocation type used by a Boot Services
* Driver to allocate pool memory.
*/
@ -318,6 +324,25 @@ extern char image_base[];
/* Start and end of U-Boot image (for payload) */
extern char _binary_u_boot_bin_start[], _binary_u_boot_bin_end[];
/*
* Variable Attributes
*/
#define EFI_VARIABLE_NON_VOLATILE 0x0000000000000001
#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002
#define EFI_VARIABLE_RUNTIME_ACCESS 0x0000000000000004
#define EFI_VARIABLE_HARDWARE_ERROR_RECORD 0x0000000000000008
#define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 0x0000000000000010
#define EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS 0x0000000000000020
#define EFI_VARIABLE_APPEND_WRITE 0x0000000000000040
#define EFI_VARIABLE_MASK (EFI_VARIABLE_NON_VOLATILE | \
EFI_VARIABLE_BOOTSERVICE_ACCESS | \
EFI_VARIABLE_RUNTIME_ACCESS | \
EFI_VARIABLE_HARDWARE_ERROR_RECORD | \
EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | \
EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | \
EFI_VARIABLE_APPEND_WRITE)
/**
* efi_get_sys_table() - Get access to the main EFI system table
*

View file

@ -29,6 +29,8 @@ enum efi_timer_delay {
};
#define UINTN size_t
typedef long INTN;
typedef uint16_t *efi_string_t;
#define EVT_TIMER 0x80000000
#define EVT_RUNTIME 0x40000000
@ -211,6 +213,10 @@ struct efi_runtime_services {
EFI_GUID(0x00000000, 0x0000, 0x0000, 0x00, 0x00, \
0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
#define EFI_GLOBAL_VARIABLE_GUID \
EFI_GUID(0x8be4df61, 0x93ca, 0x11d2, 0xaa, 0x0d, \
0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c)
#define LOADED_IMAGE_PROTOCOL_GUID \
EFI_GUID(0x5b1b31a1, 0x9562, 0x11d2, 0x8e, 0x3f, \
0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
@ -284,28 +290,93 @@ struct efi_device_path {
u8 type;
u8 sub_type;
u16 length;
};
} __packed;
struct efi_mac_addr {
u8 addr[32];
};
} __packed;
#define DEVICE_PATH_TYPE_HARDWARE_DEVICE 0x01
# define DEVICE_PATH_SUB_TYPE_VENDOR 0x04
struct efi_device_path_vendor {
struct efi_device_path dp;
efi_guid_t guid;
u8 vendor_data[];
} __packed;
#define DEVICE_PATH_TYPE_ACPI_DEVICE 0x02
# define DEVICE_PATH_SUB_TYPE_ACPI_DEVICE 0x01
#define EFI_PNP_ID(ID) (u32)(((ID) << 16) | 0x41D0)
#define EISA_PNP_ID(ID) EFI_PNP_ID(ID)
#define EISA_PNP_NUM(ID) ((ID) >> 16)
struct efi_device_path_acpi_path {
struct efi_device_path dp;
u32 hid;
u32 uid;
} __packed;
#define DEVICE_PATH_TYPE_MESSAGING_DEVICE 0x03
# define DEVICE_PATH_SUB_TYPE_MSG_USB 0x05
# define DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR 0x0b
# define DEVICE_PATH_SUB_TYPE_MSG_USB_CLASS 0x0f
# define DEVICE_PATH_SUB_TYPE_MSG_SD 0x1a
# define DEVICE_PATH_SUB_TYPE_MSG_MMC 0x1d
struct efi_device_path_usb {
struct efi_device_path dp;
u8 parent_port_number;
u8 usb_interface;
} __packed;
struct efi_device_path_mac_addr {
struct efi_device_path dp;
struct efi_mac_addr mac;
u8 if_type;
};
} __packed;
struct efi_device_path_usb_class {
struct efi_device_path dp;
u16 vendor_id;
u16 product_id;
u8 device_class;
u8 device_subclass;
u8 device_protocol;
} __packed;
struct efi_device_path_sd_mmc_path {
struct efi_device_path dp;
u8 slot_number;
} __packed;
#define DEVICE_PATH_TYPE_MEDIA_DEVICE 0x04
# define DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH 0x01
# define DEVICE_PATH_SUB_TYPE_CDROM_PATH 0x02
# define DEVICE_PATH_SUB_TYPE_FILE_PATH 0x04
struct efi_device_path_hard_drive_path {
struct efi_device_path dp;
u32 partition_number;
u64 partition_start;
u64 partition_end;
u8 partition_signature[16];
u8 partmap_type;
u8 signature_type;
} __packed;
struct efi_device_path_cdrom_path {
struct efi_device_path dp;
u32 boot_entry;
u64 partition_start;
u64 partition_end;
} __packed;
struct efi_device_path_file_path {
struct efi_device_path dp;
u16 str[32];
};
u16 str[];
} __packed;
#define BLOCK_IO_GUID \
EFI_GUID(0x964e5b21, 0x6459, 0x11d2, \
@ -358,10 +429,10 @@ struct efi_simple_text_output_protocol {
void *reset;
efi_status_t (EFIAPI *output_string)(
struct efi_simple_text_output_protocol *this,
const unsigned short *str);
const efi_string_t str);
efi_status_t (EFIAPI *test_string)(
struct efi_simple_text_output_protocol *this,
const unsigned short *str);
const efi_string_t str);
efi_status_t(EFIAPI *query_mode)(
struct efi_simple_text_output_protocol *this,
unsigned long mode_number, unsigned long *columns,
@ -423,22 +494,14 @@ struct efi_console_control_protocol
EFI_GUID(0x8b843e20, 0x8132, 0x4852, \
0x90, 0xcc, 0x55, 0x1a, 0x4e, 0x4a, 0x7f, 0x1c)
struct efi_device_path_protocol
{
uint8_t type;
uint8_t sub_type;
uint16_t length;
uint8_t data[];
};
struct efi_device_path_to_text_protocol
{
uint16_t *(EFIAPI *convert_device_node_to_text)(
struct efi_device_path_protocol *device_node,
struct efi_device_path *device_node,
bool display_only,
bool allow_shortcuts);
uint16_t *(EFIAPI *convert_device_path_to_text)(
struct efi_device_path_protocol *device_path,
struct efi_device_path *device_path,
bool display_only,
bool allow_shortcuts);
};
@ -609,4 +672,69 @@ struct efi_pxe {
struct efi_pxe_mode *mode;
};
#define EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID \
EFI_GUID(0x964e5b22, 0x6459, 0x11d2, \
0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
#define EFI_FILE_PROTOCOL_REVISION 0x00010000
struct efi_file_handle {
u64 rev;
efi_status_t (EFIAPI *open)(struct efi_file_handle *file,
struct efi_file_handle **new_handle,
s16 *file_name, u64 open_mode, u64 attributes);
efi_status_t (EFIAPI *close)(struct efi_file_handle *file);
efi_status_t (EFIAPI *delete)(struct efi_file_handle *file);
efi_status_t (EFIAPI *read)(struct efi_file_handle *file,
u64 *buffer_size, void *buffer);
efi_status_t (EFIAPI *write)(struct efi_file_handle *file,
u64 *buffer_size, void *buffer);
efi_status_t (EFIAPI *getpos)(struct efi_file_handle *file,
u64 *pos);
efi_status_t (EFIAPI *setpos)(struct efi_file_handle *file,
u64 pos);
efi_status_t (EFIAPI *getinfo)(struct efi_file_handle *file,
efi_guid_t *info_type, u64 *buffer_size, void *buffer);
efi_status_t (EFIAPI *setinfo)(struct efi_file_handle *file,
efi_guid_t *info_type, u64 buffer_size, void *buffer);
efi_status_t (EFIAPI *flush)(struct efi_file_handle *file);
};
#define EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID \
EFI_GUID(0x964e5b22, 0x6459, 0x11d2, \
0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
#define EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION 0x00010000
struct efi_simple_file_system_protocol {
u64 rev;
efi_status_t (EFIAPI *open_volume)(struct efi_simple_file_system_protocol *this,
struct efi_file_handle **root);
};
#define EFI_FILE_INFO_GUID \
EFI_GUID(0x9576e92, 0x6d3f, 0x11d2, \
0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
#define EFI_FILE_MODE_READ 0x0000000000000001
#define EFI_FILE_MODE_WRITE 0x0000000000000002
#define EFI_FILE_MODE_CREATE 0x8000000000000000
#define EFI_FILE_READ_ONLY 0x0000000000000001
#define EFI_FILE_HIDDEN 0x0000000000000002
#define EFI_FILE_SYSTEM 0x0000000000000004
#define EFI_FILE_RESERVED 0x0000000000000008
#define EFI_FILE_DIRECTORY 0x0000000000000010
#define EFI_FILE_ARCHIVE 0x0000000000000020
#define EFI_FILE_VALID_ATTR 0x0000000000000037
struct efi_file_info {
u64 size;
u64 file_size;
u64 physical_size;
struct efi_time create_time;
struct efi_time last_access_time;
struct efi_time modification_time;
u64 attribute;
s16 file_name[0];
};
#endif

View file

@ -17,6 +17,7 @@
int __efi_entry_check(void);
int __efi_exit_check(void);
const char *__efi_nesting(void);
const char *__efi_nesting_inc(void);
const char *__efi_nesting_dec(void);
@ -41,9 +42,22 @@ const char *__efi_nesting_dec(void);
})
/*
* Callback into UEFI world from u-boot:
* Call non-void UEFI function from u-boot and retrieve return value:
*/
#define EFI_CALL(exp) do { \
#define EFI_CALL(exp) ({ \
debug("%sEFI: Call: %s\n", __efi_nesting_inc(), #exp); \
assert(__efi_exit_check()); \
typeof(exp) _r = exp; \
assert(__efi_entry_check()); \
debug("%sEFI: %lu returned by %s\n", __efi_nesting_dec(), \
(unsigned long)((uintptr_t)_r & ~EFI_ERROR_MASK), #exp); \
_r; \
})
/*
* Call void UEFI function from u-boot:
*/
#define EFI_CALL_VOID(exp) do { \
debug("%sEFI: Call: %s\n", __efi_nesting_inc(), #exp); \
assert(__efi_exit_check()); \
exp; \
@ -51,6 +65,13 @@ const char *__efi_nesting_dec(void);
debug("%sEFI: Return From: %s\n", __efi_nesting_dec(), #exp); \
} while(0)
/*
* Write GUID
*/
#define EFI_PRINT_GUID(txt, guid) ({ \
debug("%sEFI: %s %pUl\n", __efi_nesting(), txt, guid); \
})
extern struct efi_runtime_services efi_runtime_services;
extern struct efi_system_table systab;
@ -59,10 +80,15 @@ extern struct efi_simple_input_interface efi_con_in;
extern const struct efi_console_control_protocol efi_console_control;
extern const struct efi_device_path_to_text_protocol efi_device_path_to_text;
uint16_t *efi_dp_str(struct efi_device_path *dp);
extern const efi_guid_t efi_global_variable_guid;
extern const efi_guid_t efi_guid_console_control;
extern const efi_guid_t efi_guid_device_path;
extern const efi_guid_t efi_guid_loaded_image;
extern const efi_guid_t efi_guid_device_path_to_text_protocol;
extern const efi_guid_t efi_simple_file_system_protocol_guid;
extern const efi_guid_t efi_file_info_guid;
extern unsigned int __efi_runtime_start, __efi_runtime_stop;
extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop;
@ -110,7 +136,8 @@ struct efi_object {
* @nofify_function: Function to call when the event is triggered
* @notify_context: Data to be passed to the notify function
* @trigger_type: Type of timer, see efi_set_timer
* @signaled: The notify function was already called
* @queued: The notification functionis queued
* @signaled: The event occured
*/
struct efi_event {
uint32_t type;
@ -120,6 +147,7 @@ struct efi_event {
u64 trigger_next;
u64 trigger_time;
enum efi_timer_delay trigger_type;
int queued;
int signaled;
};
@ -134,10 +162,13 @@ int efi_disk_register(void);
/* Called by bootefi to make GOP (graphical) interface available */
int efi_gop_register(void);
/* Called by bootefi to make the network interface available */
int efi_net_register(void **handle);
int efi_net_register(void);
/* Called by bootefi to make SMBIOS tables available */
void efi_smbios_register(void);
struct efi_simple_file_system_protocol *
efi_fs_from_path(struct efi_device_path *fp);
/* Called by networking code to memorize the dhcp ack package */
void efi_net_set_dhcp_ack(void *pkt, int len);
@ -166,6 +197,14 @@ efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type,
/* Call this to signal an event */
void efi_signal_event(struct efi_event *event);
/* open file system: */
struct efi_simple_file_system_protocol *efi_simple_file_system(
struct blk_desc *desc, int part, struct efi_device_path *dp);
/* open file from device-path: */
struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp);
/* Generic EFI memory allocator, call this to get memory */
void *efi_alloc(uint64_t len, int memory_type);
/* More specific EFI memory allocator, called by EFI payloads */
@ -191,12 +230,43 @@ uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type,
int efi_memory_init(void);
/* Adds new or overrides configuration table entry to the system table */
efi_status_t efi_install_configuration_table(const efi_guid_t *guid, void *table);
void efi_setup_loaded_image(struct efi_loaded_image *info, struct efi_object *obj,
struct efi_device_path *device_path,
struct efi_device_path *file_path);
efi_status_t efi_load_image_from_path(struct efi_device_path *file_path,
void **buffer);
#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
extern void *efi_bounce_buffer;
#define EFI_LOADER_BOUNCE_BUFFER_SIZE (64 * 1024 * 1024)
#endif
struct efi_device_path *efi_dp_next(const struct efi_device_path *dp);
int efi_dp_match(struct efi_device_path *a, struct efi_device_path *b);
struct efi_object *efi_dp_find_obj(struct efi_device_path *dp,
struct efi_device_path **rem);
unsigned efi_dp_size(const struct efi_device_path *dp);
struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp);
struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
const struct efi_device_path *dp2);
struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
const struct efi_device_path *node);
struct efi_device_path *efi_dp_from_dev(struct udevice *dev);
struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part);
struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part,
const char *path);
struct efi_device_path *efi_dp_from_eth(void);
void efi_dp_split_file_path(struct efi_device_path *full_path,
struct efi_device_path **device_path,
struct efi_device_path **file_path);
#define EFI_DP_TYPE(_dp, _type, _subtype) \
(((_dp)->type == DEVICE_PATH_TYPE_##_type) && \
((_dp)->sub_type == DEVICE_PATH_SUB_TYPE_##_subtype))
/* Convert strings from normal C strings to uEFI strings */
static inline void ascii2unicode(u16 *unicode, const char *ascii)
{
@ -233,6 +303,28 @@ efi_status_t __efi_runtime EFIAPI efi_get_time(
struct efi_time_cap *capabilities);
void efi_get_time_init(void);
#ifdef CONFIG_CMD_BOOTEFI_SELFTEST
/*
* Entry point for the tests of the EFI API.
* It is called by 'bootefi selftest'
*/
efi_status_t EFIAPI efi_selftest(efi_handle_t image_handle,
struct efi_system_table *systab);
#endif
efi_status_t EFIAPI efi_get_variable(s16 *variable_name,
efi_guid_t *vendor, u32 *attributes,
unsigned long *data_size, void *data);
efi_status_t EFIAPI efi_get_next_variable(
unsigned long *variable_name_size,
s16 *variable_name, efi_guid_t *vendor);
efi_status_t EFIAPI efi_set_variable(s16 *variable_name,
efi_guid_t *vendor, u32 attributes,
unsigned long data_size, void *data);
void *efi_bootmgr_load(struct efi_device_path **device_path,
struct efi_device_path **file_path);
#else /* defined(EFI_LOADER) && !defined(CONFIG_SPL_BUILD) */
/* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */

91
include/efi_selftest.h Normal file
View file

@ -0,0 +1,91 @@
/*
* EFI application loader
*
* Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef _EFI_SELFTEST_H
#define _EFI_SELFTEST_H
#include <common.h>
#include <efi.h>
#include <efi_api.h>
#include <linker_lists.h>
/*
* Prints an error message.
*
* @... format string followed by fields to print
*/
#define efi_st_error(...) \
efi_st_printf("%s(%u):\nERROR: ", __FILE__, __LINE__); \
efi_st_printf(__VA_ARGS__) \
/*
* A test may be setup and executed at boottime,
* it may be setup at boottime and executed at runtime,
* or it may be setup and executed at runtime.
*/
enum efi_test_phase {
EFI_EXECUTE_BEFORE_BOOTTIME_EXIT = 1,
EFI_SETUP_BEFORE_BOOTTIME_EXIT,
EFI_SETUP_AFTER_BOOTTIME_EXIT,
};
extern struct efi_simple_text_output_protocol *con_out;
extern struct efi_simple_input_interface *con_in;
/*
* Exit the boot services.
*
* The size of the memory map is determined.
* Pool memory is allocated to copy the memory map.
* The memory amp is copied and the map key is obtained.
* The map key is used to exit the boot services.
*/
void efi_st_exit_boot_services(void);
/*
* Print a pointer to an u16 string
*
* @pointer: pointer
* @buf: pointer to buffer address
* on return position of terminating zero word
*/
void efi_st_printf(const char *fmt, ...)
__attribute__ ((format (__printf__, 1, 2)));
/*
* Reads an Unicode character from the input device.
*
* @return: Unicode character
*/
u16 efi_st_get_key(void);
/**
* struct efi_unit_test - EFI unit test
*
* An efi_unit_test provides a interface to an EFI unit test.
*
* @name: name of unit test
* @phase: specifies when setup and execute are executed
* @setup: set up the unit test
* @teardown: tear down the unit test
* @execute: execute the unit test
*/
struct efi_unit_test {
const char *name;
const enum efi_test_phase phase;
int (*setup)(const efi_handle_t handle,
const struct efi_system_table *systable);
int (*execute)(void);
int (*teardown)(void);
};
/* Declare a new EFI unit test */
#define EFI_UNIT_TEST(__name) \
ll_entry_declare(struct efi_unit_test, __name, efi_unit_test)
#endif /* _EFI_SELFTEST_H */

View file

@ -280,8 +280,9 @@ struct part_driver {
#define U_BOOT_PART_TYPE(__name) \
ll_entry_declare(struct part_driver, __name, part_driver)
#if CONFIG_IS_ENABLED(EFI_PARTITION)
#include <part_efi.h>
#if CONFIG_IS_ENABLED(EFI_PARTITION)
/* disk/part_efi.c */
/**
* write_gpt_table() - Write the GUID Partition Table to disk

View file

@ -58,10 +58,6 @@
/* linux/include/efi.h */
typedef u16 efi_char16_t;
typedef struct {
u8 b[16];
} efi_guid_t;
/* based on linux/include/genhd.h */
struct partition {
u8 boot_ind; /* 0x80 - active */

View file

@ -62,6 +62,12 @@ typedef struct _IMAGE_DATA_DIRECTORY {
#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
/* PE32+ Subsystem type for EFI images */
#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10
#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11
#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12
#define IMAGE_SUBSYSTEM_SAL_RUNTIME_DRIVER 13
typedef struct _IMAGE_OPTIONAL_HEADER64 {
uint16_t Magic; /* 0x20b */
uint8_t MajorLinkerVersion;

View file

@ -9,6 +9,7 @@ ifndef CONFIG_SPL_BUILD
obj-$(CONFIG_EFI) += efi/
obj-$(CONFIG_EFI_LOADER) += efi_loader/
obj-$(CONFIG_EFI_LOADER) += efi_selftest/
obj-$(CONFIG_LZMA) += lzma/
obj-$(CONFIG_LZO) += lzo/
obj-$(CONFIG_BZIP2) += bzip2/

View file

@ -10,12 +10,14 @@
CFLAGS_helloworld.o := $(CFLAGS_EFI)
CFLAGS_REMOVE_helloworld.o := $(CFLAGS_NON_EFI)
efiprogs-$(CONFIG_CMD_BOOTEFI_HELLO_COMPILE) += helloworld.efi
always := $(efiprogs-y)
ifneq ($(CONFIG_CMD_BOOTEFI_HELLO_COMPILE),)
always += helloworld.efi
endif
obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o
obj-y += efi_image_loader.o efi_boottime.o efi_runtime.o efi_console.o
obj-y += efi_memory.o efi_device_path_to_text.o
obj-y += efi_memory.o efi_device_path_to_text.o efi_device_path.o
obj-y += efi_file.o efi_variable.o efi_bootmgr.o
obj-$(CONFIG_LCD) += efi_gop.o
obj-$(CONFIG_DM_VIDEO) += efi_gop.o
obj-$(CONFIG_PARTITIONS) += efi_disk.o

View file

@ -0,0 +1,180 @@
/*
* EFI utils
*
* Copyright (c) 2017 Rob Clark
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <charset.h>
#include <malloc.h>
#include <efi_loader.h>
static const struct efi_boot_services *bs;
static const struct efi_runtime_services *rs;
#define LOAD_OPTION_ACTIVE 0x00000001
#define LOAD_OPTION_FORCE_RECONNECT 0x00000002
#define LOAD_OPTION_HIDDEN 0x00000008
/*
* bootmgr implements the logic of trying to find a payload to boot
* based on the BootOrder + BootXXXX variables, and then loading it.
*
* TODO detecting a special key held (f9?) and displaying a boot menu
* like you would get on a PC would be clever.
*
* TODO if we had a way to write and persist variables after the OS
* has started, we'd also want to check OsIndications to see if we
* should do normal or recovery boot.
*/
/*
* See section 3.1.3 in the v2.7 UEFI spec for more details on
* the layout of EFI_LOAD_OPTION. In short it is:
*
* typedef struct _EFI_LOAD_OPTION {
* UINT32 Attributes;
* UINT16 FilePathListLength;
* // CHAR16 Description[]; <-- variable length, NULL terminated
* // EFI_DEVICE_PATH_PROTOCOL FilePathList[]; <-- FilePathListLength bytes
* // UINT8 OptionalData[];
* } EFI_LOAD_OPTION;
*/
struct load_option {
u32 attributes;
u16 file_path_length;
u16 *label;
struct efi_device_path *file_path;
u8 *optional_data;
};
/* parse an EFI_LOAD_OPTION, as described above */
static void parse_load_option(struct load_option *lo, void *ptr)
{
lo->attributes = *(u32 *)ptr;
ptr += sizeof(u32);
lo->file_path_length = *(u16 *)ptr;
ptr += sizeof(u16);
lo->label = ptr;
ptr += (utf16_strlen(lo->label) + 1) * 2;
lo->file_path = ptr;
ptr += lo->file_path_length;
lo->optional_data = ptr;
}
/* free() the result */
static void *get_var(u16 *name, const efi_guid_t *vendor,
unsigned long *size)
{
efi_guid_t *v = (efi_guid_t *)vendor;
efi_status_t ret;
void *buf = NULL;
*size = 0;
EFI_CALL(ret = rs->get_variable((s16 *)name, v, NULL, size, buf));
if (ret == EFI_BUFFER_TOO_SMALL) {
buf = malloc(*size);
EFI_CALL(ret = rs->get_variable((s16 *)name, v, NULL, size, buf));
}
if (ret != EFI_SUCCESS) {
free(buf);
*size = 0;
return NULL;
}
return buf;
}
/*
* Attempt to load load-option number 'n', returning device_path and file_path
* if successful. This checks that the EFI_LOAD_OPTION is active (enabled)
* and that the specified file to boot exists.
*/
static void *try_load_entry(uint16_t n, struct efi_device_path **device_path,
struct efi_device_path **file_path)
{
struct load_option lo;
u16 varname[] = L"Boot0000";
u16 hexmap[] = L"0123456789ABCDEF";
void *load_option, *image = NULL;
unsigned long size;
varname[4] = hexmap[(n & 0xf000) >> 12];
varname[5] = hexmap[(n & 0x0f00) >> 8];
varname[6] = hexmap[(n & 0x00f0) >> 4];
varname[7] = hexmap[(n & 0x000f) >> 0];
load_option = get_var(varname, &efi_global_variable_guid, &size);
if (!load_option)
return NULL;
parse_load_option(&lo, load_option);
if (lo.attributes & LOAD_OPTION_ACTIVE) {
efi_status_t ret;
u16 *str = NULL;
debug("%s: trying to load \"%ls\" from: %ls\n", __func__,
lo.label, (str = efi_dp_str(lo.file_path)));
efi_free_pool(str);
ret = efi_load_image_from_path(lo.file_path, &image);
if (ret != EFI_SUCCESS)
goto error;
printf("Booting: %ls\n", lo.label);
efi_dp_split_file_path(lo.file_path, device_path, file_path);
}
error:
free(load_option);
return image;
}
/*
* Attempt to load, in the order specified by BootOrder EFI variable, the
* available load-options, finding and returning the first one that can
* be loaded successfully.
*/
void *efi_bootmgr_load(struct efi_device_path **device_path,
struct efi_device_path **file_path)
{
uint16_t *bootorder;
unsigned long size;
void *image = NULL;
int i, num;
__efi_entry_check();
bs = systab.boottime;
rs = systab.runtime;
bootorder = get_var(L"BootOrder", &efi_global_variable_guid, &size);
if (!bootorder)
goto error;
num = size / sizeof(uint16_t);
for (i = 0; i < num; i++) {
debug("%s: trying to load Boot%04X\n", __func__, bootorder[i]);
image = try_load_entry(bootorder[i], device_path, file_path);
if (image)
break;
}
free(bootorder);
error:
__efi_exit_check();
return image;
}

View file

@ -8,6 +8,7 @@
#include <common.h>
#include <efi_loader.h>
#include <environment.h>
#include <malloc.h>
#include <asm/global_data.h>
#include <libfdt_env.h>
@ -18,6 +19,9 @@
DECLARE_GLOBAL_DATA_PTR;
/* Task priority level */
static UINTN efi_tpl = TPL_APPLICATION;
/* This list contains all the EFI objects our payload has access to */
LIST_HEAD(efi_obj_list);
@ -109,6 +113,11 @@ static const char *indent_string(int level)
return &indent[max - level];
}
const char *__efi_nesting(void)
{
return indent_string(nesting_level);
}
const char *__efi_nesting_inc(void)
{
return indent_string(nesting_level++);
@ -154,12 +163,15 @@ static u64 efi_div10(u64 a)
void efi_signal_event(struct efi_event *event)
{
if (event->signaled)
return;
event->signaled = 1;
if (event->type & EVT_NOTIFY_SIGNAL) {
EFI_CALL(event->notify_function(event, event->notify_context));
if (event->notify_function) {
event->queued = 1;
/* Check TPL */
if (efi_tpl >= event->notify_tpl)
return;
EFI_CALL_VOID(event->notify_function(event,
event->notify_context));
}
event->queued = 0;
}
static efi_status_t efi_unsupported(const char *funcname)
@ -170,14 +182,31 @@ static efi_status_t efi_unsupported(const char *funcname)
static unsigned long EFIAPI efi_raise_tpl(UINTN new_tpl)
{
UINTN old_tpl = efi_tpl;
EFI_ENTRY("0x%zx", new_tpl);
return EFI_EXIT(0);
if (new_tpl < efi_tpl)
debug("WARNING: new_tpl < current_tpl in %s\n", __func__);
efi_tpl = new_tpl;
if (efi_tpl > TPL_HIGH_LEVEL)
efi_tpl = TPL_HIGH_LEVEL;
EFI_EXIT(EFI_SUCCESS);
return old_tpl;
}
static void EFIAPI efi_restore_tpl(UINTN old_tpl)
{
EFI_ENTRY("0x%zx", old_tpl);
efi_unsupported(__func__);
if (old_tpl > efi_tpl)
debug("WARNING: old_tpl > current_tpl in %s\n", __func__);
efi_tpl = old_tpl;
if (efi_tpl > TPL_HIGH_LEVEL)
efi_tpl = TPL_HIGH_LEVEL;
EFI_EXIT(EFI_SUCCESS);
}
static efi_status_t EFIAPI efi_allocate_pages_ext(int type, int memory_type,
@ -270,6 +299,7 @@ efi_status_t efi_create_event(uint32_t type, UINTN notify_tpl,
efi_events[i].notify_context = notify_context;
/* Disable timers on bootup */
efi_events[i].trigger_next = -1ULL;
efi_events[i].queued = 0;
efi_events[i].signaled = 0;
*event = &efi_events[i];
return EFI_SUCCESS;
@ -301,16 +331,25 @@ void efi_timer_check(void)
u64 now = timer_get_us();
for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
if (!efi_events[i].type ||
!(efi_events[i].type & EVT_TIMER) ||
efi_events[i].trigger_type == EFI_TIMER_STOP ||
if (!efi_events[i].type)
continue;
if (efi_events[i].queued)
efi_signal_event(&efi_events[i]);
if (!(efi_events[i].type & EVT_TIMER) ||
now < efi_events[i].trigger_next)
continue;
if (efi_events[i].trigger_type == EFI_TIMER_PERIODIC) {
switch (efi_events[i].trigger_type) {
case EFI_TIMER_RELATIVE:
efi_events[i].trigger_type = EFI_TIMER_STOP;
break;
case EFI_TIMER_PERIODIC:
efi_events[i].trigger_next +=
efi_events[i].trigger_time;
efi_events[i].signaled = 0;
break;
default:
continue;
}
efi_events[i].signaled = 1;
efi_signal_event(&efi_events[i]);
}
WATCHDOG_RESET();
@ -347,6 +386,7 @@ efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type,
}
event->trigger_type = type;
event->trigger_time = trigger_time;
event->signaled = 0;
return EFI_SUCCESS;
}
return EFI_INVALID_PARAMETER;
@ -371,6 +411,9 @@ static efi_status_t EFIAPI efi_wait_for_event(unsigned long num_events,
/* Check parameters */
if (!num_events || !event)
return EFI_EXIT(EFI_INVALID_PARAMETER);
/* Check TPL */
if (efi_tpl != TPL_APPLICATION)
return EFI_EXIT(EFI_UNSUPPORTED);
for (i = 0; i < num_events; ++i) {
for (j = 0; j < ARRAY_SIZE(efi_events); ++j) {
if (event[i] == &efi_events[j])
@ -380,6 +423,8 @@ static efi_status_t EFIAPI efi_wait_for_event(unsigned long num_events,
known_event:
if (!event[i]->type || event[i]->type & EVT_NOTIFY_SIGNAL)
return EFI_EXIT(EFI_INVALID_PARAMETER);
if (!event[i]->signaled)
efi_signal_event(event[i]);
}
/* Wait for signal */
@ -412,7 +457,11 @@ static efi_status_t EFIAPI efi_signal_event_ext(struct efi_event *event)
for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
if (event != &efi_events[i])
continue;
efi_signal_event(event);
if (event->signaled)
break;
event->signaled = 1;
if (event->type & EVT_NOTIFY_SIGNAL)
efi_signal_event(event);
break;
}
return EFI_EXIT(EFI_SUCCESS);
@ -427,6 +476,7 @@ static efi_status_t EFIAPI efi_close_event(struct efi_event *event)
if (event == &efi_events[i]) {
event->type = 0;
event->trigger_next = -1ULL;
event->queued = 0;
event->signaled = 0;
return EFI_EXIT(EFI_SUCCESS);
}
@ -445,6 +495,8 @@ static efi_status_t EFIAPI efi_check_event(struct efi_event *event)
continue;
if (!event->type || event->type & EVT_NOTIFY_SIGNAL)
break;
if (!event->signaled)
efi_signal_event(event);
if (event->signaled)
return EFI_EXIT(EFI_SUCCESS);
return EFI_EXIT(EFI_NOT_READY);
@ -513,7 +565,7 @@ static efi_status_t EFIAPI efi_install_protocol_interface_ext(void **handle,
efi_guid_t *protocol, int protocol_interface_type,
void *protocol_interface)
{
EFI_ENTRY("%p, %p, %d, %p", handle, protocol, protocol_interface_type,
EFI_ENTRY("%p, %pUl, %d, %p", handle, protocol, protocol_interface_type,
protocol_interface);
return EFI_EXIT(efi_install_protocol_interface(handle, protocol,
@ -525,7 +577,7 @@ static efi_status_t EFIAPI efi_reinstall_protocol_interface(void *handle,
efi_guid_t *protocol, void *old_interface,
void *new_interface)
{
EFI_ENTRY("%p, %p, %p, %p", handle, protocol, old_interface,
EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, old_interface,
new_interface);
return EFI_EXIT(EFI_ACCESS_DENIED);
}
@ -574,7 +626,7 @@ out:
static efi_status_t EFIAPI efi_uninstall_protocol_interface_ext(void *handle,
efi_guid_t *protocol, void *protocol_interface)
{
EFI_ENTRY("%p, %p, %p", handle, protocol, protocol_interface);
EFI_ENTRY("%p, %pUl, %p", handle, protocol, protocol_interface);
return EFI_EXIT(efi_uninstall_protocol_interface(handle, protocol,
protocol_interface));
@ -584,7 +636,7 @@ static efi_status_t EFIAPI efi_register_protocol_notify(efi_guid_t *protocol,
struct efi_event *event,
void **registration)
{
EFI_ENTRY("%p, %p, %p", protocol, event, registration);
EFI_ENTRY("%pUl, %p, %p", protocol, event, registration);
return EFI_EXIT(EFI_OUT_OF_RESOURCES);
}
@ -654,7 +706,7 @@ static efi_status_t EFIAPI efi_locate_handle_ext(
efi_guid_t *protocol, void *search_key,
unsigned long *buffer_size, efi_handle_t *buffer)
{
EFI_ENTRY("%d, %p, %p, %p, %p", search_type, protocol, search_key,
EFI_ENTRY("%d, %pUl, %p, %p, %p", search_type, protocol, search_key,
buffer_size, buffer);
return EFI_EXIT(efi_locate_handle(search_type, protocol, search_key,
@ -665,8 +717,17 @@ static efi_status_t EFIAPI efi_locate_device_path(efi_guid_t *protocol,
struct efi_device_path **device_path,
efi_handle_t *device)
{
EFI_ENTRY("%p, %p, %p", protocol, device_path, device);
return EFI_EXIT(EFI_NOT_FOUND);
struct efi_object *efiobj;
EFI_ENTRY("%pUl, %p, %p", protocol, device_path, device);
efiobj = efi_dp_find_obj(*device_path, device_path);
if (!efiobj)
return EFI_EXIT(EFI_NOT_FOUND);
*device = efiobj->handle;
return EFI_EXIT(EFI_SUCCESS);
}
/* Collapses configuration table entries, removing index i */
@ -713,10 +774,87 @@ efi_status_t efi_install_configuration_table(const efi_guid_t *guid, void *table
static efi_status_t EFIAPI efi_install_configuration_table_ext(efi_guid_t *guid,
void *table)
{
EFI_ENTRY("%p, %p", guid, table);
EFI_ENTRY("%pUl, %p", guid, table);
return EFI_EXIT(efi_install_configuration_table(guid, table));
}
/* Initialize a loaded_image_info + loaded_image_info object with correct
* protocols, boot-device, etc.
*/
void efi_setup_loaded_image(struct efi_loaded_image *info, struct efi_object *obj,
struct efi_device_path *device_path,
struct efi_device_path *file_path)
{
obj->handle = info;
/*
* When asking for the device path interface, return
* bootefi_device_path
*/
obj->protocols[0].guid = &efi_guid_device_path;
obj->protocols[0].protocol_interface = device_path;
/*
* When asking for the loaded_image interface, just
* return handle which points to loaded_image_info
*/
obj->protocols[1].guid = &efi_guid_loaded_image;
obj->protocols[1].protocol_interface = info;
obj->protocols[2].guid = &efi_guid_console_control;
obj->protocols[2].protocol_interface = (void *)&efi_console_control;
obj->protocols[3].guid = &efi_guid_device_path_to_text_protocol;
obj->protocols[3].protocol_interface =
(void *)&efi_device_path_to_text;
info->file_path = file_path;
info->device_handle = efi_dp_find_obj(device_path, NULL);
list_add_tail(&obj->link, &efi_obj_list);
}
efi_status_t efi_load_image_from_path(struct efi_device_path *file_path,
void **buffer)
{
struct efi_file_info *info = NULL;
struct efi_file_handle *f;
static efi_status_t ret;
uint64_t bs;
f = efi_file_from_path(file_path);
if (!f)
return EFI_DEVICE_ERROR;
bs = 0;
EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid,
&bs, info));
if (ret == EFI_BUFFER_TOO_SMALL) {
info = malloc(bs);
EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid,
&bs, info));
}
if (ret != EFI_SUCCESS)
goto error;
ret = efi_allocate_pool(EFI_LOADER_DATA, info->file_size, buffer);
if (ret)
goto error;
EFI_CALL(ret = f->read(f, &info->file_size, *buffer));
error:
free(info);
EFI_CALL(f->close(f));
if (ret != EFI_SUCCESS) {
efi_free_pool(*buffer);
*buffer = NULL;
}
return ret;
}
static efi_status_t EFIAPI efi_load_image(bool boot_policy,
efi_handle_t parent_image,
struct efi_device_path *file_path,
@ -724,25 +862,40 @@ static efi_status_t EFIAPI efi_load_image(bool boot_policy,
unsigned long source_size,
efi_handle_t *image_handle)
{
static struct efi_object loaded_image_info_obj = {
.protocols = {
{
.guid = &efi_guid_loaded_image,
},
},
};
struct efi_loaded_image *info;
struct efi_object *obj;
EFI_ENTRY("%d, %p, %p, %p, %ld, %p", boot_policy, parent_image,
file_path, source_buffer, source_size, image_handle);
info = malloc(sizeof(*info));
loaded_image_info_obj.protocols[0].protocol_interface = info;
obj = malloc(sizeof(loaded_image_info_obj));
memset(info, 0, sizeof(*info));
memcpy(obj, &loaded_image_info_obj, sizeof(loaded_image_info_obj));
obj->handle = info;
info->file_path = file_path;
info = calloc(1, sizeof(*info));
obj = calloc(1, sizeof(*obj));
if (!source_buffer) {
struct efi_device_path *dp, *fp;
efi_status_t ret;
ret = efi_load_image_from_path(file_path, &source_buffer);
if (ret != EFI_SUCCESS) {
free(info);
free(obj);
return EFI_EXIT(ret);
}
/*
* split file_path which contains both the device and
* file parts:
*/
efi_dp_split_file_path(file_path, &dp, &fp);
efi_setup_loaded_image(info, obj, dp, fp);
} else {
/* In this case, file_path is the "device" path, ie.
* something like a HARDWARE_DEVICE:MEMORY_MAPPED
*/
efi_setup_loaded_image(info, obj, file_path, NULL);
}
info->reserved = efi_load_pe(source_buffer, info);
if (!info->reserved) {
free(info);
@ -751,7 +904,6 @@ static efi_status_t EFIAPI efi_load_image(bool boot_policy,
}
*image_handle = info;
list_add_tail(&obj->link, &efi_obj_list);
return EFI_EXIT(EFI_SUCCESS);
}
@ -793,6 +945,15 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
EFI_ENTRY("%p, %ld, %ld, %p", image_handle, exit_status,
exit_data_size, exit_data);
/* Make sure entry/exit counts for EFI world cross-overs match */
__efi_exit_check();
/*
* But longjmp out with the U-Boot gd, not the application's, as
* the other end is a setjmp call inside EFI context.
*/
efi_restore_gd();
loaded_image_info->exit_status = exit_status;
longjmp(&loaded_image_info->exit_jmp, 1);
@ -840,8 +1001,24 @@ static void efi_exit_caches(void)
static efi_status_t EFIAPI efi_exit_boot_services(void *image_handle,
unsigned long map_key)
{
int i;
EFI_ENTRY("%p, %ld", image_handle, map_key);
/* Notify that ExitBootServices is invoked. */
for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
if (efi_events[i].type != EVT_SIGNAL_EXIT_BOOT_SERVICES)
continue;
efi_signal_event(&efi_events[i]);
}
/* Make sure that notification functions are not called anymore */
efi_tpl = TPL_HIGH_LEVEL;
#if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_ENV_IS_NOWHERE)
/* save any EFI variables that have been written: */
env_save();
#endif
board_quiesce_devices();
/* Fix up caches for EFI payloads if necessary */
@ -906,7 +1083,7 @@ static efi_status_t EFIAPI efi_close_protocol(void *handle,
void *agent_handle,
void *controller_handle)
{
EFI_ENTRY("%p, %p, %p, %p", handle, protocol, agent_handle,
EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, agent_handle,
controller_handle);
return EFI_EXIT(EFI_NOT_FOUND);
}
@ -916,7 +1093,7 @@ static efi_status_t EFIAPI efi_open_protocol_information(efi_handle_t handle,
struct efi_open_protocol_info_entry **entry_buffer,
unsigned long *entry_count)
{
EFI_ENTRY("%p, %p, %p, %p", handle, protocol, entry_buffer,
EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, entry_buffer,
entry_count);
return EFI_EXIT(EFI_NOT_FOUND);
}
@ -982,7 +1159,7 @@ static efi_status_t EFIAPI efi_locate_handle_buffer(
efi_status_t r;
unsigned long buffer_size = 0;
EFI_ENTRY("%d, %p, %p, %p, %p", search_type, protocol, search_key,
EFI_ENTRY("%d, %pUl, %p, %p, %p", search_type, protocol, search_key,
no_handles, buffer);
if (!no_handles || !buffer) {
@ -1014,11 +1191,13 @@ static efi_status_t EFIAPI efi_locate_protocol(efi_guid_t *protocol,
struct list_head *lhandle;
int i;
EFI_ENTRY("%p, %p, %p", protocol, registration, protocol_interface);
EFI_ENTRY("%pUl, %p, %p", protocol, registration, protocol_interface);
if (!protocol || !protocol_interface)
return EFI_EXIT(EFI_INVALID_PARAMETER);
EFI_PRINT_GUID("protocol", protocol);
list_for_each(lhandle, &efi_obj_list) {
struct efi_object *efiobj;
@ -1122,7 +1301,7 @@ static efi_status_t EFIAPI efi_open_protocol(
int i;
efi_status_t r = EFI_INVALID_PARAMETER;
EFI_ENTRY("%p, %p, %p, %p, %p, 0x%x", handle, protocol,
EFI_ENTRY("%p, %pUl, %p, %p, %p, 0x%x", handle, protocol,
protocol_interface, agent_handle, controller_handle,
attributes);
@ -1132,6 +1311,8 @@ static efi_status_t EFIAPI efi_open_protocol(
goto out;
}
EFI_PRINT_GUID("protocol", protocol);
switch (attributes) {
case EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL:
case EFI_OPEN_PROTOCOL_GET_PROTOCOL:

View file

@ -8,7 +8,10 @@
#include <common.h>
#include <charset.h>
#include <dm/device.h>
#include <efi_loader.h>
#include <stdio_dev.h>
#include <video_console.h>
static bool console_size_queried;
@ -137,34 +140,46 @@ static efi_status_t EFIAPI efi_cout_reset(
return EFI_EXIT(EFI_UNSUPPORTED);
}
static void print_unicode_in_utf8(u16 c)
{
char utf8[MAX_UTF8_PER_UTF16] = { 0 };
utf16_to_utf8((u8 *)utf8, &c, 1);
puts(utf8);
}
static efi_status_t EFIAPI efi_cout_output_string(
struct efi_simple_text_output_protocol *this,
const unsigned short *string)
const efi_string_t string)
{
struct cout_mode *mode;
u16 ch;
struct simple_text_output_mode *con = &efi_con_mode;
struct cout_mode *mode = &efi_cout_modes[con->mode];
mode = &efi_cout_modes[efi_con_mode.mode];
EFI_ENTRY("%p, %p", this, string);
for (;(ch = *string); string++) {
print_unicode_in_utf8(ch);
efi_con_mode.cursor_column++;
if (ch == '\n') {
efi_con_mode.cursor_column = 1;
efi_con_mode.cursor_row++;
} else if (efi_con_mode.cursor_column > mode->columns) {
efi_con_mode.cursor_column = 1;
efi_con_mode.cursor_row++;
unsigned int n16 = utf16_strlen(string);
char buf[MAX_UTF8_PER_UTF16 * n16 + 1];
char *p;
*utf16_to_utf8((u8 *)buf, string, n16) = '\0';
fputs(stdout, buf);
for (p = buf; *p; p++) {
switch (*p) {
case '\r': /* carriage-return */
con->cursor_column = 0;
break;
case '\n': /* newline */
con->cursor_column = 0;
con->cursor_row++;
break;
case '\t': /* tab, assume 8 char align */
break;
case '\b': /* backspace */
con->cursor_column = max(0, con->cursor_column - 1);
break;
default:
con->cursor_column++;
break;
}
if (efi_con_mode.cursor_row > mode->rows)
efi_con_mode.cursor_row = mode->rows;
if (con->cursor_column >= mode->columns) {
con->cursor_column = 0;
con->cursor_row++;
}
con->cursor_row = min(con->cursor_row, (s32)mode->rows - 1);
}
return EFI_EXIT(EFI_SUCCESS);
@ -172,7 +187,7 @@ static efi_status_t EFIAPI efi_cout_output_string(
static efi_status_t EFIAPI efi_cout_test_string(
struct efi_simple_text_output_protocol *this,
const unsigned short *string)
const efi_string_t string)
{
EFI_ENTRY("%p, %p", this, string);
return EFI_EXIT(EFI_SUCCESS);
@ -186,6 +201,34 @@ static bool cout_mode_matches(struct cout_mode *mode, int rows, int cols)
return (mode->rows == rows) && (mode->columns == cols);
}
static int query_console_serial(int *rows, int *cols)
{
/* Ask the terminal about its size */
int n[3];
u64 timeout;
/* Empty input buffer */
while (tstc())
getc();
printf(ESC"[18t");
/* Check if we have a terminal that understands */
timeout = timer_get_us() + 1000000;
while (!tstc())
if (timer_get_us() > timeout)
return -1;
/* Read {depth,rows,cols} */
if (term_read_reply(n, 3, 't'))
return -1;
*cols = n[2];
*rows = n[1];
return 0;
}
static efi_status_t EFIAPI efi_cout_query_mode(
struct efi_simple_text_output_protocol *this,
unsigned long mode_number, unsigned long *columns,
@ -194,34 +237,24 @@ static efi_status_t EFIAPI efi_cout_query_mode(
EFI_ENTRY("%p, %ld, %p, %p", this, mode_number, columns, rows);
if (!console_size_queried) {
/* Ask the terminal about its size */
int n[3];
int cols;
int rows;
u64 timeout;
const char *stdout_name = env_get("stdout");
int rows, cols;
console_size_queried = true;
/* Empty input buffer */
while (tstc())
getc();
printf(ESC"[18t");
/* Check if we have a terminal that understands */
timeout = timer_get_us() + 1000000;
while (!tstc())
if (timer_get_us() > timeout)
goto out;
/* Read {depth,rows,cols} */
if (term_read_reply(n, 3, 't')) {
if (stdout_name && !strcmp(stdout_name, "vidconsole") &&
IS_ENABLED(CONFIG_DM_VIDEO)) {
struct stdio_dev *stdout_dev =
stdio_get_by_name("vidconsole");
struct udevice *dev = stdout_dev->priv;
struct vidconsole_priv *priv =
dev_get_uclass_priv(dev);
rows = priv->rows;
cols = priv->cols;
} else if (query_console_serial(&rows, &cols)) {
goto out;
}
cols = n[2];
rows = n[1];
/* Test if we can have Mode 1 */
if (cols >= 80 && rows >= 50) {
efi_cout_modes[1].present = 1;
@ -426,8 +459,10 @@ static void EFIAPI efi_console_timer_notify(struct efi_event *event,
void *context)
{
EFI_ENTRY("%p, %p", event, context);
if (tstc())
if (tstc()) {
efi_con_in.wait_for_key->signaled = 1;
efi_signal_event(efi_con_in.wait_for_key);
}
EFI_EXIT(EFI_SUCCESS);
}

View file

@ -0,0 +1,563 @@
/*
* EFI device path from u-boot device-model mapping
*
* (C) Copyright 2017 Rob Clark
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <blk.h>
#include <dm.h>
#include <usb.h>
#include <mmc.h>
#include <efi_loader.h>
#include <inttypes.h>
#include <part.h>
/* template END node: */
static const struct efi_device_path END = {
.type = DEVICE_PATH_TYPE_END,
.sub_type = DEVICE_PATH_SUB_TYPE_END,
.length = sizeof(END),
};
#define U_BOOT_GUID \
EFI_GUID(0xe61d73b9, 0xa384, 0x4acc, \
0xae, 0xab, 0x82, 0xe8, 0x28, 0xf3, 0x62, 0x8b)
/* template ROOT node: */
static const struct efi_device_path_vendor ROOT = {
.dp = {
.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE,
.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR,
.length = sizeof(ROOT),
},
.guid = U_BOOT_GUID,
};
static void *dp_alloc(size_t sz)
{
void *buf;
if (efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES, sz, &buf) != EFI_SUCCESS)
return NULL;
return buf;
}
/*
* Iterate to next block in device-path, terminating (returning NULL)
* at /End* node.
*/
struct efi_device_path *efi_dp_next(const struct efi_device_path *dp)
{
if (dp == NULL)
return NULL;
if (dp->type == DEVICE_PATH_TYPE_END)
return NULL;
dp = ((void *)dp) + dp->length;
if (dp->type == DEVICE_PATH_TYPE_END)
return NULL;
return (struct efi_device_path *)dp;
}
/*
* Compare two device-paths, stopping when the shorter of the two hits
* an End* node. This is useful to, for example, compare a device-path
* representing a device with one representing a file on the device, or
* a device with a parent device.
*/
int efi_dp_match(struct efi_device_path *a, struct efi_device_path *b)
{
while (1) {
int ret;
ret = memcmp(&a->length, &b->length, sizeof(a->length));
if (ret)
return ret;
ret = memcmp(a, b, a->length);
if (ret)
return ret;
a = efi_dp_next(a);
b = efi_dp_next(b);
if (!a || !b)
return 0;
}
}
/*
* See UEFI spec (section 3.1.2, about short-form device-paths..
* tl;dr: we can have a device-path that starts with a USB WWID
* or USB Class node, and a few other cases which don't encode
* the full device path with bus hierarchy:
*
* - MESSAGING:USB_WWID
* - MESSAGING:USB_CLASS
* - MEDIA:FILE_PATH
* - MEDIA:HARD_DRIVE
* - MESSAGING:URI
*/
static struct efi_device_path *shorten_path(struct efi_device_path *dp)
{
while (dp) {
/*
* TODO: Add MESSAGING:USB_WWID and MESSAGING:URI..
* in practice fallback.efi just uses MEDIA:HARD_DRIVE
* so not sure when we would see these other cases.
*/
if (EFI_DP_TYPE(dp, MESSAGING_DEVICE, MSG_USB_CLASS) ||
EFI_DP_TYPE(dp, MEDIA_DEVICE, HARD_DRIVE_PATH) ||
EFI_DP_TYPE(dp, MEDIA_DEVICE, FILE_PATH))
return dp;
dp = efi_dp_next(dp);
}
return dp;
}
static struct efi_object *find_obj(struct efi_device_path *dp, bool short_path,
struct efi_device_path **rem)
{
struct efi_object *efiobj;
list_for_each_entry(efiobj, &efi_obj_list, link) {
int i;
for (i = 0; i < ARRAY_SIZE(efiobj->protocols); i++) {
struct efi_handler *handler = &efiobj->protocols[i];
struct efi_device_path *obj_dp;
if (!handler->guid)
break;
if (guidcmp(handler->guid, &efi_guid_device_path))
continue;
obj_dp = handler->protocol_interface;
do {
if (efi_dp_match(dp, obj_dp) == 0) {
if (rem) {
*rem = ((void *)dp) +
efi_dp_size(obj_dp);
}
return efiobj;
}
obj_dp = shorten_path(efi_dp_next(obj_dp));
} while (short_path && obj_dp);
}
}
return NULL;
}
/*
* Find an efiobj from device-path, if 'rem' is not NULL, returns the
* remaining part of the device path after the matched object.
*/
struct efi_object *efi_dp_find_obj(struct efi_device_path *dp,
struct efi_device_path **rem)
{
struct efi_object *efiobj;
efiobj = find_obj(dp, false, rem);
if (!efiobj)
efiobj = find_obj(dp, true, rem);
return efiobj;
}
/* return size not including End node: */
unsigned efi_dp_size(const struct efi_device_path *dp)
{
unsigned sz = 0;
while (dp) {
sz += dp->length;
dp = efi_dp_next(dp);
}
return sz;
}
struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp)
{
struct efi_device_path *ndp;
unsigned sz = efi_dp_size(dp) + sizeof(END);
if (!dp)
return NULL;
ndp = dp_alloc(sz);
memcpy(ndp, dp, sz);
return ndp;
}
struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
const struct efi_device_path *dp2)
{
struct efi_device_path *ret;
if (!dp1) {
ret = efi_dp_dup(dp2);
} else if (!dp2) {
ret = efi_dp_dup(dp1);
} else {
/* both dp1 and dp2 are non-null */
unsigned sz1 = efi_dp_size(dp1);
unsigned sz2 = efi_dp_size(dp2);
void *p = dp_alloc(sz1 + sz2 + sizeof(END));
memcpy(p, dp1, sz1);
memcpy(p + sz1, dp2, sz2);
memcpy(p + sz1 + sz2, &END, sizeof(END));
ret = p;
}
return ret;
}
struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
const struct efi_device_path *node)
{
struct efi_device_path *ret;
if (!node && !dp) {
ret = efi_dp_dup(&END);
} else if (!node) {
ret = efi_dp_dup(dp);
} else if (!dp) {
unsigned sz = node->length;
void *p = dp_alloc(sz + sizeof(END));
memcpy(p, node, sz);
memcpy(p + sz, &END, sizeof(END));
ret = p;
} else {
/* both dp and node are non-null */
unsigned sz = efi_dp_size(dp);
void *p = dp_alloc(sz + node->length + sizeof(END));
memcpy(p, dp, sz);
memcpy(p + sz, node, node->length);
memcpy(p + sz + node->length, &END, sizeof(END));
ret = p;
}
return ret;
}
#ifdef CONFIG_DM
/* size of device-path not including END node for device and all parents
* up to the root device.
*/
static unsigned dp_size(struct udevice *dev)
{
if (!dev || !dev->driver)
return sizeof(ROOT);
switch (dev->driver->id) {
case UCLASS_ROOT:
case UCLASS_SIMPLE_BUS:
/* stop traversing parents at this point: */
return sizeof(ROOT);
case UCLASS_MMC:
return dp_size(dev->parent) +
sizeof(struct efi_device_path_sd_mmc_path);
case UCLASS_MASS_STORAGE:
case UCLASS_USB_HUB:
return dp_size(dev->parent) +
sizeof(struct efi_device_path_usb_class);
default:
/* just skip over unknown classes: */
return dp_size(dev->parent);
}
}
static void *dp_fill(void *buf, struct udevice *dev)
{
if (!dev || !dev->driver)
return buf;
switch (dev->driver->id) {
case UCLASS_ROOT:
case UCLASS_SIMPLE_BUS: {
/* stop traversing parents at this point: */
struct efi_device_path_vendor *vdp = buf;
*vdp = ROOT;
return &vdp[1];
}
#if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC)
case UCLASS_MMC: {
struct efi_device_path_sd_mmc_path *sddp =
dp_fill(buf, dev->parent);
struct mmc *mmc = mmc_get_mmc_dev(dev);
struct blk_desc *desc = mmc_get_blk_desc(mmc);
sddp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
sddp->dp.sub_type = (desc->if_type == IF_TYPE_MMC) ?
DEVICE_PATH_SUB_TYPE_MSG_MMC :
DEVICE_PATH_SUB_TYPE_MSG_SD;
sddp->dp.length = sizeof(*sddp);
sddp->slot_number = dev->seq;
return &sddp[1];
}
#endif
case UCLASS_MASS_STORAGE:
case UCLASS_USB_HUB: {
struct efi_device_path_usb_class *udp =
dp_fill(buf, dev->parent);
struct usb_device *udev = dev_get_parent_priv(dev);
struct usb_device_descriptor *desc = &udev->descriptor;
udp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
udp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_USB_CLASS;
udp->dp.length = sizeof(*udp);
udp->vendor_id = desc->idVendor;
udp->product_id = desc->idProduct;
udp->device_class = desc->bDeviceClass;
udp->device_subclass = desc->bDeviceSubClass;
udp->device_protocol = desc->bDeviceProtocol;
return &udp[1];
}
default:
debug("unhandled device class: %s (%u)\n",
dev->name, dev->driver->id);
return dp_fill(buf, dev->parent);
}
}
/* Construct a device-path from a device: */
struct efi_device_path *efi_dp_from_dev(struct udevice *dev)
{
void *buf, *start;
start = buf = dp_alloc(dp_size(dev) + sizeof(END));
buf = dp_fill(buf, dev);
*((struct efi_device_path *)buf) = END;
return start;
}
#endif
static unsigned dp_part_size(struct blk_desc *desc, int part)
{
unsigned dpsize;
#ifdef CONFIG_BLK
dpsize = dp_size(desc->bdev->parent);
#else
dpsize = sizeof(ROOT) + sizeof(struct efi_device_path_usb);
#endif
if (part == 0) /* the actual disk, not a partition */
return dpsize;
if (desc->part_type == PART_TYPE_ISO)
dpsize += sizeof(struct efi_device_path_cdrom_path);
else
dpsize += sizeof(struct efi_device_path_hard_drive_path);
return dpsize;
}
static void *dp_part_fill(void *buf, struct blk_desc *desc, int part)
{
disk_partition_t info;
#ifdef CONFIG_BLK
buf = dp_fill(buf, desc->bdev->parent);
#else
/*
* We *could* make a more accurate path, by looking at if_type
* and handling all the different cases like we do for non-
* legacy (ie CONFIG_BLK=y) case. But most important thing
* is just to have a unique device-path for if_type+devnum.
* So map things to a fictional USB device:
*/
struct efi_device_path_usb *udp;
memcpy(buf, &ROOT, sizeof(ROOT));
buf += sizeof(ROOT);
udp = buf;
udp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
udp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_USB;
udp->dp.length = sizeof(*udp);
udp->parent_port_number = desc->if_type;
udp->usb_interface = desc->devnum;
buf = &udp[1];
#endif
if (part == 0) /* the actual disk, not a partition */
return buf;
part_get_info(desc, part, &info);
if (desc->part_type == PART_TYPE_ISO) {
struct efi_device_path_cdrom_path *cddp = buf;
cddp->boot_entry = part - 1;
cddp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
cddp->dp.sub_type = DEVICE_PATH_SUB_TYPE_CDROM_PATH;
cddp->dp.length = sizeof(*cddp);
cddp->partition_start = info.start;
cddp->partition_end = info.size;
buf = &cddp[1];
} else {
struct efi_device_path_hard_drive_path *hddp = buf;
hddp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
hddp->dp.sub_type = DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH;
hddp->dp.length = sizeof(*hddp);
hddp->partition_number = part - 1;
hddp->partition_start = info.start;
hddp->partition_end = info.size;
if (desc->part_type == PART_TYPE_EFI)
hddp->partmap_type = 2;
else
hddp->partmap_type = 1;
hddp->signature_type = desc->sig_type;
if (hddp->signature_type != 0)
memcpy(hddp->partition_signature, &desc->guid_sig,
sizeof(hddp->partition_signature));
buf = &hddp[1];
}
return buf;
}
/* Construct a device-path from a partition on a blk device: */
struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part)
{
void *buf, *start;
start = buf = dp_alloc(dp_part_size(desc, part) + sizeof(END));
buf = dp_part_fill(buf, desc, part);
*((struct efi_device_path *)buf) = END;
return start;
}
/* convert path to an UEFI style path (ie. DOS style backslashes and utf16) */
static void path_to_uefi(u16 *uefi, const char *path)
{
while (*path) {
char c = *(path++);
if (c == '/')
c = '\\';
*(uefi++) = c;
}
*uefi = '\0';
}
/*
* If desc is NULL, this creates a path with only the file component,
* otherwise it creates a full path with both device and file components
*/
struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part,
const char *path)
{
struct efi_device_path_file_path *fp;
void *buf, *start;
unsigned dpsize = 0, fpsize;
if (desc)
dpsize = dp_part_size(desc, part);
fpsize = sizeof(struct efi_device_path) + 2 * (strlen(path) + 1);
dpsize += fpsize;
start = buf = dp_alloc(dpsize + sizeof(END));
if (desc)
buf = dp_part_fill(buf, desc, part);
/* add file-path: */
fp = buf;
fp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
fp->dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH;
fp->dp.length = fpsize;
path_to_uefi(fp->str, path);
buf += fpsize;
*((struct efi_device_path *)buf) = END;
return start;
}
#ifdef CONFIG_NET
struct efi_device_path *efi_dp_from_eth(void)
{
struct efi_device_path_mac_addr *ndp;
void *buf, *start;
unsigned dpsize = 0;
assert(eth_get_dev());
#ifdef CONFIG_DM_ETH
dpsize += dp_size(eth_get_dev());
#else
dpsize += sizeof(ROOT);
#endif
dpsize += sizeof(*ndp);
start = buf = dp_alloc(dpsize + sizeof(END));
#ifdef CONFIG_DM_ETH
buf = dp_fill(buf, eth_get_dev());
#else
memcpy(buf, &ROOT, sizeof(ROOT));
buf += sizeof(ROOT);
#endif
ndp = buf;
ndp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
ndp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR;
ndp->dp.length = sizeof(*ndp);
memcpy(ndp->mac.addr, eth_get_ethaddr(), ARP_HLEN);
buf = &ndp[1];
*((struct efi_device_path *)buf) = END;
return start;
}
#endif
/*
* Helper to split a full device path (containing both device and file
* parts) into it's constituent parts.
*/
void efi_dp_split_file_path(struct efi_device_path *full_path,
struct efi_device_path **device_path,
struct efi_device_path **file_path)
{
struct efi_device_path *p, *dp, *fp;
dp = efi_dp_dup(full_path);
p = dp;
while (!EFI_DP_TYPE(p, MEDIA_DEVICE, FILE_PATH))
p = efi_dp_next(p);
fp = efi_dp_dup(p);
p->type = DEVICE_PATH_TYPE_END;
p->sub_type = DEVICE_PATH_SUB_TYPE_END;
p->length = sizeof(*p);
*device_path = dp;
*file_path = fp;
}

View file

@ -15,81 +15,199 @@
const efi_guid_t efi_guid_device_path_to_text_protocol =
EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID;
static char *dp_unknown(char *s, struct efi_device_path *dp)
{
s += sprintf(s, "/UNKNOWN(%04x,%04x)", dp->type, dp->sub_type);
return s;
}
static char *dp_hardware(char *s, struct efi_device_path *dp)
{
switch (dp->sub_type) {
case DEVICE_PATH_SUB_TYPE_VENDOR: {
struct efi_device_path_vendor *vdp =
(struct efi_device_path_vendor *)dp;
s += sprintf(s, "/VenHw(%pUl)", &vdp->guid);
break;
}
default:
s = dp_unknown(s, dp);
break;
}
return s;
}
static char *dp_acpi(char *s, struct efi_device_path *dp)
{
switch (dp->sub_type) {
case DEVICE_PATH_SUB_TYPE_ACPI_DEVICE: {
struct efi_device_path_acpi_path *adp =
(struct efi_device_path_acpi_path *)dp;
s += sprintf(s, "/Acpi(PNP%04x", EISA_PNP_NUM(adp->hid));
if (adp->uid)
s += sprintf(s, ",%d", adp->uid);
s += sprintf(s, ")");
break;
}
default:
s = dp_unknown(s, dp);
break;
}
return s;
}
static char *dp_msging(char *s, struct efi_device_path *dp)
{
switch (dp->sub_type) {
case DEVICE_PATH_SUB_TYPE_MSG_USB: {
struct efi_device_path_usb *udp =
(struct efi_device_path_usb *)dp;
s += sprintf(s, "/Usb(0x%x,0x%x)", udp->parent_port_number,
udp->usb_interface);
break;
}
case DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR: {
struct efi_device_path_mac_addr *mdp =
(struct efi_device_path_mac_addr *)dp;
if (mdp->if_type != 0 && mdp->if_type != 1)
break;
s += sprintf(s, "/MAC(%02x%02x%02x%02x%02x%02x,0x%1x)",
mdp->mac.addr[0], mdp->mac.addr[1],
mdp->mac.addr[2], mdp->mac.addr[3],
mdp->mac.addr[4], mdp->mac.addr[5],
mdp->if_type);
break;
}
case DEVICE_PATH_SUB_TYPE_MSG_USB_CLASS: {
struct efi_device_path_usb_class *ucdp =
(struct efi_device_path_usb_class *)dp;
s += sprintf(s, "/USBClass(%x,%x,%x,%x,%x)",
ucdp->vendor_id, ucdp->product_id,
ucdp->device_class, ucdp->device_subclass,
ucdp->device_protocol);
break;
}
case DEVICE_PATH_SUB_TYPE_MSG_SD:
case DEVICE_PATH_SUB_TYPE_MSG_MMC: {
const char *typename =
(dp->sub_type == DEVICE_PATH_SUB_TYPE_MSG_SD) ?
"SDCard" : "MMC";
struct efi_device_path_sd_mmc_path *sddp =
(struct efi_device_path_sd_mmc_path *)dp;
s += sprintf(s, "/%s(Slot%u)", typename, sddp->slot_number);
break;
}
default:
s = dp_unknown(s, dp);
break;
}
return s;
}
static char *dp_media(char *s, struct efi_device_path *dp)
{
switch (dp->sub_type) {
case DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH: {
struct efi_device_path_hard_drive_path *hddp =
(struct efi_device_path_hard_drive_path *)dp;
void *sig = hddp->partition_signature;
switch (hddp->signature_type) {
case SIG_TYPE_MBR:
s += sprintf(s, "/HD(Part%d,Sig%08x)",
hddp->partition_number,
*(uint32_t *)sig);
break;
case SIG_TYPE_GUID:
s += sprintf(s, "/HD(Part%d,Sig%pUl)",
hddp->partition_number, sig);
default:
s += sprintf(s, "/HD(Part%d,MBRType=%02x,SigType=%02x)",
hddp->partition_number, hddp->partmap_type,
hddp->signature_type);
}
break;
}
case DEVICE_PATH_SUB_TYPE_CDROM_PATH: {
struct efi_device_path_cdrom_path *cddp =
(struct efi_device_path_cdrom_path *)dp;
s += sprintf(s, "/CDROM(0x%x)", cddp->boot_entry);
break;
}
case DEVICE_PATH_SUB_TYPE_FILE_PATH: {
struct efi_device_path_file_path *fp =
(struct efi_device_path_file_path *)dp;
int slen = (dp->length - sizeof(*dp)) / 2;
s += sprintf(s, "/%-*ls", slen, fp->str);
break;
}
default:
s = dp_unknown(s, dp);
break;
}
return s;
}
static uint16_t *efi_convert_device_node_to_text(
struct efi_device_path_protocol *device_node,
struct efi_device_path *dp,
bool display_only,
bool allow_shortcuts)
{
unsigned long buffer_size;
unsigned long len;
efi_status_t r;
uint16_t *buffer = NULL;
int i;
char buf[512]; /* this ought be be big enough for worst case */
char *str = buf;
uint16_t *out;
switch (device_node->type) {
case DEVICE_PATH_TYPE_END:
while (dp) {
switch (dp->type) {
case DEVICE_PATH_TYPE_HARDWARE_DEVICE:
str = dp_hardware(str, dp);
break;
case DEVICE_PATH_TYPE_ACPI_DEVICE:
str = dp_acpi(str, dp);
break;
case DEVICE_PATH_TYPE_MESSAGING_DEVICE:
str = dp_msging(str, dp);
break;
case DEVICE_PATH_TYPE_MEDIA_DEVICE:
str = dp_media(str, dp);
break;
default:
str = dp_unknown(str, dp);
}
dp = efi_dp_next(dp);
}
*str++ = '\0';
len = str - buf;
r = efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES, 2 * len, (void **)&out);
if (r != EFI_SUCCESS)
return NULL;
case DEVICE_PATH_TYPE_MESSAGING_DEVICE:
switch (device_node->sub_type) {
case DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR: {
struct efi_device_path_mac_addr *dp =
(struct efi_device_path_mac_addr *)device_node;
if (dp->if_type != 0 && dp->if_type != 1)
break;
r = efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES,
2 * MAC_OUTPUT_LEN,
(void **)&buffer);
if (r != EFI_SUCCESS)
return NULL;
sprintf((char *)buffer,
"MAC(%02x%02x%02x%02x%02x%02x,0x%1x)",
dp->mac.addr[0], dp->mac.addr[1],
dp->mac.addr[2], dp->mac.addr[3],
dp->mac.addr[4], dp->mac.addr[5],
dp->if_type);
for (i = MAC_OUTPUT_LEN - 1; i >= 0; --i)
buffer[i] = ((uint8_t *)buffer)[i];
break;
}
}
break;
case DEVICE_PATH_TYPE_MEDIA_DEVICE:
switch (device_node->sub_type) {
case DEVICE_PATH_SUB_TYPE_FILE_PATH:
buffer_size = device_node->length - 4;
r = efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES,
buffer_size, (void **) &buffer);
if (r != EFI_SUCCESS)
return NULL;
memcpy(buffer, device_node->data, buffer_size);
break;
}
break;
}
ascii2unicode(out, buf);
out[len - 1] = 0;
/*
* For all node types that we do not yet support return
* 'UNKNOWN(type,subtype)'.
*/
if (!buffer) {
r = efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES,
2 * UNKNOWN_OUTPUT_LEN,
(void **)&buffer);
if (r != EFI_SUCCESS)
return NULL;
sprintf((char *)buffer,
"UNKNOWN(%04x,%04x)",
device_node->type,
device_node->sub_type);
for (i = UNKNOWN_OUTPUT_LEN - 1; i >= 0; --i)
buffer[i] = ((uint8_t *)buffer)[i];
}
return buffer;
return out;
}
/* helper for debug prints.. efi_free_pool() the result. */
uint16_t *efi_dp_str(struct efi_device_path *dp)
{
return efi_convert_device_node_to_text(dp, true, true);
}
static uint16_t EFIAPI *efi_convert_device_node_to_text_ext(
struct efi_device_path_protocol *device_node,
struct efi_device_path *device_node,
bool display_only,
bool allow_shortcuts)
{
@ -105,7 +223,7 @@ static uint16_t EFIAPI *efi_convert_device_node_to_text_ext(
}
static uint16_t EFIAPI *efi_convert_device_path_to_text(
struct efi_device_path_protocol *device_path,
struct efi_device_path *device_path,
bool display_only,
bool allow_shortcuts)
{

View file

@ -28,11 +28,15 @@ struct efi_disk_obj {
/* EFI Interface Media descriptor struct, referenced by ops */
struct efi_block_io_media media;
/* EFI device path to this block device */
struct efi_device_path_file_path *dp;
struct efi_device_path *dp;
/* partition # */
unsigned int part;
/* handle to filesys proto (for partition objects) */
struct efi_simple_file_system_protocol *volume;
/* Offset into disk for simple partitions */
lbaint_t offset;
/* Internal block device */
const struct blk_desc *desc;
struct blk_desc *desc;
};
static efi_status_t EFIAPI efi_disk_reset(struct efi_block_io *this,
@ -47,7 +51,7 @@ enum efi_disk_direction {
EFI_DISK_WRITE,
};
static efi_status_t EFIAPI efi_disk_rw_blocks(struct efi_block_io *this,
static efi_status_t efi_disk_rw_blocks(struct efi_block_io *this,
u32 media_id, u64 lba, unsigned long buffer_size,
void *buffer, enum efi_disk_direction direction)
{
@ -170,28 +174,58 @@ static const struct efi_block_io block_io_disk_template = {
.flush_blocks = &efi_disk_flush_blocks,
};
/*
* Find filesystem from a device-path. The passed in path 'p' probably
* contains one or more /File(name) nodes, so the comparison stops at
* the first /File() node, and returns the pointer to that via 'rp'.
* This is mostly intended to be a helper to map a device-path to an
* efi_file_handle object.
*/
struct efi_simple_file_system_protocol *
efi_fs_from_path(struct efi_device_path *fp)
{
struct efi_object *efiobj;
struct efi_disk_obj *diskobj;
efiobj = efi_dp_find_obj(fp, NULL);
if (!efiobj)
return NULL;
diskobj = container_of(efiobj, struct efi_disk_obj, parent);
return diskobj->volume;
}
static void efi_disk_add_dev(const char *name,
const char *if_typename,
const struct blk_desc *desc,
struct blk_desc *desc,
int dev_index,
lbaint_t offset)
lbaint_t offset,
unsigned int part)
{
struct efi_disk_obj *diskobj;
struct efi_device_path_file_path *dp;
int objlen = sizeof(*diskobj) + (sizeof(*dp) * 2);
/* Don't add empty devices */
if (!desc->lba)
return;
diskobj = calloc(1, objlen);
diskobj = calloc(1, sizeof(*diskobj));
/* Fill in object data */
dp = (void *)&diskobj[1];
diskobj->dp = efi_dp_from_part(desc, part);
diskobj->part = part;
diskobj->parent.protocols[0].guid = &efi_block_io_guid;
diskobj->parent.protocols[0].protocol_interface = &diskobj->ops;
diskobj->parent.protocols[1].guid = &efi_guid_device_path;
diskobj->parent.protocols[1].protocol_interface = dp;
diskobj->parent.protocols[1].protocol_interface = diskobj->dp;
if (part >= 1) {
diskobj->volume = efi_simple_file_system(desc, part,
diskobj->dp);
diskobj->parent.protocols[2].guid =
&efi_simple_file_system_protocol_guid;
diskobj->parent.protocols[2].protocol_interface =
diskobj->volume;
}
diskobj->parent.handle = diskobj;
diskobj->ops = block_io_disk_template;
diskobj->ifname = if_typename;
@ -207,17 +241,6 @@ static void efi_disk_add_dev(const char *name,
diskobj->media.last_block = desc->lba - offset;
diskobj->ops.media = &diskobj->media;
/* Fill in device path */
diskobj->dp = dp;
dp[0].dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
dp[0].dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH;
dp[0].dp.length = sizeof(*dp);
ascii2unicode(dp[0].str, name);
dp[1].dp.type = DEVICE_PATH_TYPE_END;
dp[1].dp.sub_type = DEVICE_PATH_SUB_TYPE_END;
dp[1].dp.length = sizeof(*dp);
/* Hook up to the device list */
list_add_tail(&diskobj->parent.link, &efi_obj_list);
}
@ -236,14 +259,18 @@ static int efi_disk_create_eltorito(struct blk_desc *desc,
if (desc->part_type != PART_TYPE_ISO)
return 0;
/* and devices for each partition: */
while (!part_get_info(desc, part, &info)) {
snprintf(devname, sizeof(devname), "%s:%d", pdevname,
part);
efi_disk_add_dev(devname, if_typename, desc, diskid,
info.start);
info.start, part);
part++;
disks++;
}
/* ... and add block device: */
efi_disk_add_dev(devname, if_typename, desc, diskid, 0, 0);
#endif
return disks;
@ -271,9 +298,22 @@ int efi_disk_register(void)
uclass_next_device_check(&dev)) {
struct blk_desc *desc = dev_get_uclass_platdata(dev);
const char *if_typename = dev->driver->name;
disk_partition_t info;
int part = 1;
printf("Scanning disk %s...\n", dev->name);
efi_disk_add_dev(dev->name, if_typename, desc, desc->devnum, 0);
/* add devices for each partition: */
while (!part_get_info(desc, part, &info)) {
efi_disk_add_dev(dev->name, if_typename, desc,
desc->devnum, 0, part);
part++;
}
/* ... and add block device: */
efi_disk_add_dev(dev->name, if_typename, desc,
desc->devnum, 0, 0);
disks++;
/*
@ -309,7 +349,7 @@ int efi_disk_register(void)
snprintf(devname, sizeof(devname), "%s%d",
if_typename, i);
efi_disk_add_dev(devname, if_typename, desc, i, 0);
efi_disk_add_dev(devname, if_typename, desc, i, 0, 0);
disks++;
/*

560
lib/efi_loader/efi_file.c Normal file
View file

@ -0,0 +1,560 @@
/*
* EFI utils
*
* Copyright (c) 2017 Rob Clark
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <charset.h>
#include <efi_loader.h>
#include <malloc.h>
#include <fs.h>
struct file_system {
struct efi_simple_file_system_protocol base;
struct efi_device_path *dp;
struct blk_desc *desc;
int part;
};
#define to_fs(x) container_of(x, struct file_system, base)
struct file_handle {
struct efi_file_handle base;
struct file_system *fs;
loff_t offset; /* current file position/cursor */
int isdir;
/* for reading a directory: */
struct fs_dir_stream *dirs;
struct fs_dirent *dent;
char path[0];
};
#define to_fh(x) container_of(x, struct file_handle, base)
static const struct efi_file_handle efi_file_handle_protocol;
static char *basename(struct file_handle *fh)
{
char *s = strrchr(fh->path, '/');
if (s)
return s + 1;
return fh->path;
}
static int set_blk_dev(struct file_handle *fh)
{
return fs_set_blk_dev_with_part(fh->fs->desc, fh->fs->part);
}
static int is_dir(struct file_handle *fh)
{
struct fs_dir_stream *dirs;
set_blk_dev(fh);
dirs = fs_opendir(fh->path);
if (!dirs)
return 0;
fs_closedir(dirs);
return 1;
}
/*
* Normalize a path which may include either back or fwd slashes,
* double slashes, . or .. entries in the path, etc.
*/
static int sanitize_path(char *path)
{
char *p;
/* backslash to slash: */
p = path;
while ((p = strchr(p, '\\')))
*p++ = '/';
/* handle double-slashes: */
p = path;
while ((p = strstr(p, "//"))) {
char *src = p + 1;
memmove(p, src, strlen(src) + 1);
}
/* handle extra /.'s */
p = path;
while ((p = strstr(p, "/."))) {
/*
* You'd be tempted to do this *after* handling ".."s
* below to avoid having to check if "/." is start of
* a "/..", but that won't have the correct results..
* for example, "/foo/./../bar" would get resolved to
* "/foo/bar" if you did these two passes in the other
* order
*/
if (p[2] == '.') {
p += 2;
continue;
}
char *src = p + 2;
memmove(p, src, strlen(src) + 1);
}
/* handle extra /..'s: */
p = path;
while ((p = strstr(p, "/.."))) {
char *src = p + 3;
p--;
/* find beginning of previous path entry: */
while (true) {
if (p < path)
return -1;
if (*p == '/')
break;
p--;
}
memmove(p, src, strlen(src) + 1);
}
return 0;
}
/* NOTE: despite what you would expect, 'file_name' is actually a path.
* With windoze style backlashes, ofc.
*/
static struct efi_file_handle *file_open(struct file_system *fs,
struct file_handle *parent, s16 *file_name, u64 mode)
{
struct file_handle *fh;
char f0[MAX_UTF8_PER_UTF16] = {0};
int plen = 0;
int flen = 0;
if (file_name) {
utf16_to_utf8((u8 *)f0, (u16 *)file_name, 1);
flen = utf16_strlen((u16 *)file_name);
}
/* we could have a parent, but also an absolute path: */
if (f0[0] == '\\') {
plen = 0;
} else if (parent) {
plen = strlen(parent->path) + 1;
}
/* +2 is for null and '/' */
fh = calloc(1, sizeof(*fh) + plen + (flen * MAX_UTF8_PER_UTF16) + 2);
fh->base = efi_file_handle_protocol;
fh->fs = fs;
if (parent) {
char *p = fh->path;
if (plen > 0) {
strcpy(p, parent->path);
p += plen - 1;
*p++ = '/';
}
utf16_to_utf8((u8 *)p, (u16 *)file_name, flen);
if (sanitize_path(fh->path))
goto error;
/* check if file exists: */
if (set_blk_dev(fh))
goto error;
if (!((mode & EFI_FILE_MODE_CREATE) || fs_exists(fh->path)))
goto error;
/* figure out if file is a directory: */
fh->isdir = is_dir(fh);
} else {
fh->isdir = 1;
strcpy(fh->path, "");
}
return &fh->base;
error:
free(fh);
return NULL;
}
static efi_status_t EFIAPI efi_file_open(struct efi_file_handle *file,
struct efi_file_handle **new_handle,
s16 *file_name, u64 open_mode, u64 attributes)
{
struct file_handle *fh = to_fh(file);
EFI_ENTRY("%p, %p, \"%ls\", %llx, %llu", file, new_handle, file_name,
open_mode, attributes);
*new_handle = file_open(fh->fs, fh, file_name, open_mode);
if (!*new_handle)
return EFI_EXIT(EFI_NOT_FOUND);
return EFI_EXIT(EFI_SUCCESS);
}
static efi_status_t file_close(struct file_handle *fh)
{
fs_closedir(fh->dirs);
free(fh);
return EFI_SUCCESS;
}
static efi_status_t EFIAPI efi_file_close(struct efi_file_handle *file)
{
struct file_handle *fh = to_fh(file);
EFI_ENTRY("%p", file);
return EFI_EXIT(file_close(fh));
}
static efi_status_t EFIAPI efi_file_delete(struct efi_file_handle *file)
{
struct file_handle *fh = to_fh(file);
EFI_ENTRY("%p", file);
file_close(fh);
return EFI_EXIT(EFI_WARN_DELETE_FAILURE);
}
static efi_status_t file_read(struct file_handle *fh, u64 *buffer_size,
void *buffer)
{
loff_t actread;
if (fs_read(fh->path, (ulong)buffer, fh->offset,
*buffer_size, &actread))
return EFI_DEVICE_ERROR;
*buffer_size = actread;
fh->offset += actread;
return EFI_SUCCESS;
}
static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size,
void *buffer)
{
struct efi_file_info *info = buffer;
struct fs_dirent *dent;
unsigned int required_size;
if (!fh->dirs) {
assert(fh->offset == 0);
fh->dirs = fs_opendir(fh->path);
if (!fh->dirs)
return EFI_DEVICE_ERROR;
}
/*
* So this is a bit awkward. Since fs layer is stateful and we
* can't rewind an entry, in the EFI_BUFFER_TOO_SMALL case below
* we might have to return without consuming the dent.. so we
* have to stash it for next call.
*/
if (fh->dent) {
dent = fh->dent;
fh->dent = NULL;
} else {
dent = fs_readdir(fh->dirs);
}
if (!dent) {
/* no more files in directory: */
/* workaround shim.efi bug/quirk.. as find_boot_csv()
* loops through directory contents, it initially calls
* read w/ zero length buffer to find out how much mem
* to allocate for the EFI_FILE_INFO, then allocates,
* and then calls a 2nd time. If we return size of
* zero the first time, it happily passes that to
* AllocateZeroPool(), and when that returns NULL it
* thinks it is EFI_OUT_OF_RESOURCES. So on first
* call return a non-zero size:
*/
if (*buffer_size == 0)
*buffer_size = sizeof(*info);
else
*buffer_size = 0;
return EFI_SUCCESS;
}
/* check buffer size: */
required_size = sizeof(*info) + 2 * (strlen(dent->name) + 1);
if (*buffer_size < required_size) {
*buffer_size = required_size;
fh->dent = dent;
return EFI_BUFFER_TOO_SMALL;
}
*buffer_size = required_size;
memset(info, 0, required_size);
info->size = required_size;
info->file_size = dent->size;
info->physical_size = dent->size;
if (dent->type == FS_DT_DIR)
info->attribute |= EFI_FILE_DIRECTORY;
ascii2unicode((u16 *)info->file_name, dent->name);
fh->offset++;
return EFI_SUCCESS;
}
static efi_status_t EFIAPI efi_file_read(struct efi_file_handle *file,
u64 *buffer_size, void *buffer)
{
struct file_handle *fh = to_fh(file);
efi_status_t ret = EFI_SUCCESS;
EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer);
if (set_blk_dev(fh)) {
ret = EFI_DEVICE_ERROR;
goto error;
}
if (fh->isdir)
ret = dir_read(fh, buffer_size, buffer);
else
ret = file_read(fh, buffer_size, buffer);
error:
return EFI_EXIT(ret);
}
static efi_status_t EFIAPI efi_file_write(struct efi_file_handle *file,
u64 *buffer_size, void *buffer)
{
struct file_handle *fh = to_fh(file);
efi_status_t ret = EFI_SUCCESS;
loff_t actwrite;
EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer);
if (set_blk_dev(fh)) {
ret = EFI_DEVICE_ERROR;
goto error;
}
if (fs_write(fh->path, (ulong)buffer, fh->offset, *buffer_size,
&actwrite)) {
ret = EFI_DEVICE_ERROR;
goto error;
}
*buffer_size = actwrite;
fh->offset += actwrite;
error:
return EFI_EXIT(ret);
}
static efi_status_t EFIAPI efi_file_getpos(struct efi_file_handle *file,
u64 *pos)
{
struct file_handle *fh = to_fh(file);
EFI_ENTRY("%p, %p", file, pos);
*pos = fh->offset;
return EFI_EXIT(EFI_SUCCESS);
}
static efi_status_t EFIAPI efi_file_setpos(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;
goto error;
}
fs_closedir(fh->dirs);
fh->dirs = NULL;
}
if (pos == ~0ULL) {
loff_t file_size;
if (set_blk_dev(fh)) {
ret = EFI_DEVICE_ERROR;
goto error;
}
if (fs_size(fh->path, &file_size)) {
ret = EFI_DEVICE_ERROR;
goto error;
}
pos = file_size;
}
fh->offset = pos;
error:
return EFI_EXIT(ret);
}
static efi_status_t EFIAPI efi_file_getinfo(struct efi_file_handle *file,
efi_guid_t *info_type, u64 *buffer_size, void *buffer)
{
struct file_handle *fh = to_fh(file);
efi_status_t ret = EFI_SUCCESS;
EFI_ENTRY("%p, %p, %p, %p", file, info_type, buffer_size, buffer);
if (!guidcmp(info_type, &efi_file_info_guid)) {
struct efi_file_info *info = buffer;
char *filename = basename(fh);
unsigned int required_size;
loff_t file_size;
/* check buffer size: */
required_size = sizeof(*info) + 2 * (strlen(filename) + 1);
if (*buffer_size < required_size) {
*buffer_size = required_size;
ret = EFI_BUFFER_TOO_SMALL;
goto error;
}
if (set_blk_dev(fh)) {
ret = EFI_DEVICE_ERROR;
goto error;
}
if (fs_size(fh->path, &file_size)) {
ret = EFI_DEVICE_ERROR;
goto error;
}
memset(info, 0, required_size);
info->size = required_size;
info->file_size = file_size;
info->physical_size = file_size;
if (fh->isdir)
info->attribute |= EFI_FILE_DIRECTORY;
ascii2unicode((u16 *)info->file_name, filename);
} else {
ret = EFI_UNSUPPORTED;
}
error:
return EFI_EXIT(ret);
}
static efi_status_t EFIAPI efi_file_setinfo(struct efi_file_handle *file,
efi_guid_t *info_type, u64 buffer_size, void *buffer)
{
EFI_ENTRY("%p, %p, %llu, %p", file, info_type, buffer_size, buffer);
return EFI_EXIT(EFI_UNSUPPORTED);
}
static efi_status_t EFIAPI efi_file_flush(struct efi_file_handle *file)
{
EFI_ENTRY("%p", file);
return EFI_EXIT(EFI_SUCCESS);
}
static const struct efi_file_handle efi_file_handle_protocol = {
.rev = EFI_FILE_PROTOCOL_REVISION,
.open = efi_file_open,
.close = efi_file_close,
.delete = efi_file_delete,
.read = efi_file_read,
.write = efi_file_write,
.getpos = efi_file_getpos,
.setpos = efi_file_setpos,
.getinfo = efi_file_getinfo,
.setinfo = efi_file_setinfo,
.flush = efi_file_flush,
};
struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp)
{
struct efi_simple_file_system_protocol *v;
struct efi_file_handle *f;
efi_status_t ret;
v = efi_fs_from_path(fp);
if (!v)
return NULL;
EFI_CALL(ret = v->open_volume(v, &f));
if (ret != EFI_SUCCESS)
return NULL;
/* skip over device-path nodes before the file path: */
while (fp && !EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH))
fp = efi_dp_next(fp);
while (fp) {
struct efi_device_path_file_path *fdp =
container_of(fp, struct efi_device_path_file_path, dp);
struct efi_file_handle *f2;
if (!EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH)) {
printf("bad file path!\n");
f->close(f);
return NULL;
}
EFI_CALL(ret = f->open(f, &f2, (s16 *)fdp->str,
EFI_FILE_MODE_READ, 0));
if (ret != EFI_SUCCESS)
return NULL;
fp = efi_dp_next(fp);
EFI_CALL(f->close(f));
f = f2;
}
return f;
}
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);
return EFI_EXIT(EFI_SUCCESS);
}
struct efi_simple_file_system_protocol *
efi_simple_file_system(struct blk_desc *desc, int part,
struct efi_device_path *dp)
{
struct file_system *fs;
fs = calloc(1, sizeof(*fs));
fs->base.rev = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;
fs->base.open_volume = efi_open_volume;
fs->desc = desc;
fs->part = part;
fs->dp = dp;
return &fs->base;
}

View file

@ -15,8 +15,12 @@
DECLARE_GLOBAL_DATA_PTR;
const efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
const efi_guid_t efi_guid_device_path = DEVICE_PATH_GUID;
const efi_guid_t efi_guid_loaded_image = LOADED_IMAGE_GUID;
const efi_guid_t efi_simple_file_system_protocol_guid =
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
const efi_guid_t efi_file_info_guid = EFI_FILE_INFO_GUID;
static efi_status_t efi_loader_relocate(const IMAGE_BASE_RELOCATION *rel,
unsigned long rel_size, void *efi_reloc)
@ -90,6 +94,7 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info)
unsigned long virt_size = 0;
bool can_run_nt64 = true;
bool can_run_nt32 = true;
uint16_t image_type;
#if defined(CONFIG_ARM64)
can_run_nt32 = false;
@ -135,6 +140,7 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info)
entry = efi_reloc + opt->AddressOfEntryPoint;
rel_size = opt->DataDirectory[rel_idx].Size;
rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress;
image_type = opt->Subsystem;
} else if (can_run_nt32 &&
(nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)) {
IMAGE_OPTIONAL_HEADER32 *opt = &nt->OptionalHeader;
@ -148,12 +154,32 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info)
entry = efi_reloc + opt->AddressOfEntryPoint;
rel_size = opt->DataDirectory[rel_idx].Size;
rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress;
image_type = opt->Subsystem;
} else {
printf("%s: Invalid optional header magic %x\n", __func__,
nt->OptionalHeader.Magic);
return NULL;
}
switch (image_type) {
case IMAGE_SUBSYSTEM_EFI_APPLICATION:
loaded_image_info->image_code_type = EFI_LOADER_CODE;
loaded_image_info->image_data_type = EFI_LOADER_DATA;
break;
case IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER:
loaded_image_info->image_code_type = EFI_BOOT_SERVICES_CODE;
loaded_image_info->image_data_type = EFI_BOOT_SERVICES_DATA;
break;
case IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER:
case IMAGE_SUBSYSTEM_SAL_RUNTIME_DRIVER:
loaded_image_info->image_code_type = EFI_RUNTIME_SERVICES_CODE;
loaded_image_info->image_data_type = EFI_RUNTIME_SERVICES_DATA;
break;
default:
printf("%s: invalid image type: %u\n", __func__, image_type);
break;
}
/* Load sections into RAM */
for (i = num_sections - 1; i >= 0; i--) {
IMAGE_SECTION_HEADER *sec = &sections[i];

View file

@ -43,7 +43,7 @@ void *efi_bounce_buffer;
*/
struct efi_pool_allocation {
u64 num_pages;
char data[];
char data[] __aligned(ARCH_DMA_MINALIGN);
};
/*
@ -356,7 +356,8 @@ efi_status_t efi_allocate_pool(int pool_type, unsigned long size,
{
efi_status_t r;
efi_physical_addr_t t;
u64 num_pages = (size + sizeof(u64) + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT;
u64 num_pages = (size + sizeof(struct efi_pool_allocation) +
EFI_PAGE_MASK) >> EFI_PAGE_SHIFT;
if (size == 0) {
*buffer = NULL;

View file

@ -26,9 +26,6 @@ struct efi_net_obj {
/* EFI Interface callback struct for network */
struct efi_simple_network net;
struct efi_simple_network_mode net_mode;
/* Device path to the network adapter */
struct efi_device_path_mac_addr dp_mac;
struct efi_device_path_file_path dp_end;
/* PXE struct to transmit dhcp data */
struct efi_pxe pxe;
struct efi_pxe_mode pxe_mode;
@ -210,19 +207,9 @@ void efi_net_set_dhcp_ack(void *pkt, int len)
}
/* This gets called from do_bootefi_exec(). */
int efi_net_register(void **handle)
int efi_net_register(void)
{
struct efi_net_obj *netobj;
struct efi_device_path_mac_addr dp_net = {
.dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE,
.dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR,
.dp.length = sizeof(dp_net),
};
struct efi_device_path_file_path dp_end = {
.dp.type = DEVICE_PATH_TYPE_END,
.dp.sub_type = DEVICE_PATH_SUB_TYPE_END,
.dp.length = sizeof(dp_end),
};
if (!eth_get_dev()) {
/* No eth device active, don't expose any */
@ -236,7 +223,8 @@ int efi_net_register(void **handle)
netobj->parent.protocols[0].guid = &efi_net_guid;
netobj->parent.protocols[0].protocol_interface = &netobj->net;
netobj->parent.protocols[1].guid = &efi_guid_device_path;
netobj->parent.protocols[1].protocol_interface = &netobj->dp_mac;
netobj->parent.protocols[1].protocol_interface =
efi_dp_from_eth();
netobj->parent.protocols[2].guid = &efi_pxe_guid;
netobj->parent.protocols[2].protocol_interface = &netobj->pxe;
netobj->parent.handle = &netobj->net;
@ -255,9 +243,6 @@ int efi_net_register(void **handle)
netobj->net.receive = efi_net_receive;
netobj->net.mode = &netobj->net_mode;
netobj->net_mode.state = EFI_NETWORK_STARTED;
netobj->dp_mac = dp_net;
netobj->dp_end = dp_end;
memcpy(netobj->dp_mac.mac.addr, eth_get_ethaddr(), 6);
memcpy(netobj->net_mode.current_address.mac_addr, eth_get_ethaddr(), 6);
netobj->net_mode.max_packet_size = PKTSIZE;
@ -268,8 +253,5 @@ int efi_net_register(void **handle)
/* Hook net up to the device list */
list_add_tail(&netobj->parent.link, &efi_obj_list);
if (handle)
*handle = &netobj->net;
return 0;
}

View file

@ -184,7 +184,16 @@ static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
/* Clean up system table */
.ptr = &systab.boottime,
.patchto = NULL,
},
}, {
.ptr = &efi_runtime_services.get_variable,
.patchto = &efi_device_error,
}, {
.ptr = &efi_runtime_services.get_next_variable,
.patchto = &efi_device_error,
}, {
.ptr = &efi_runtime_services.set_variable,
.patchto = &efi_device_error,
}
};
static bool efi_runtime_tobedetached(void *p)
@ -243,7 +252,8 @@ void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map)
/* Check if the relocation is inside bounds */
if (map && ((newaddr < map->virtual_start) ||
newaddr > (map->virtual_start + (map->num_pages << 12)))) {
newaddr > (map->virtual_start +
(map->num_pages << EFI_PAGE_SHIFT)))) {
if (!efi_runtime_tobedetached(p))
printf("U-Boot EFI: Relocation at %p is out of "
"range (%lx)\n", p, newaddr);
@ -269,7 +279,8 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
uint32_t descriptor_version,
struct efi_mem_desc *virtmap)
{
ulong runtime_start = (ulong)&__efi_runtime_start & ~0xfffULL;
ulong runtime_start = (ulong)&__efi_runtime_start &
~(ulong)EFI_PAGE_MASK;
int n = memory_map_size / descriptor_size;
int i;
@ -382,9 +393,9 @@ struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
.set_wakeup_time = (void *)&efi_unimplemented,
.set_virtual_address_map = &efi_set_virtual_address_map,
.convert_pointer = (void *)&efi_invalid_parameter,
.get_variable = (void *)&efi_device_error,
.get_next_variable = (void *)&efi_device_error,
.set_variable = (void *)&efi_device_error,
.get_variable = efi_get_variable,
.get_next_variable = efi_get_next_variable,
.set_variable = efi_set_variable,
.get_next_high_mono_count = (void *)&efi_device_error,
.reset_system = &efi_reset_system_boottime,
};

View file

@ -0,0 +1,335 @@
/*
* EFI utils
*
* Copyright (c) 2017 Rob Clark
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <malloc.h>
#include <charset.h>
#include <efi_loader.h>
#define READ_ONLY BIT(31)
/*
* Mapping between EFI variables and u-boot variables:
*
* efi_$guid_$varname = {attributes}(type)value
*
* For example:
*
* efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_OsIndicationsSupported=
* "{ro,boot,run}(blob)0000000000000000"
* efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_BootOrder=
* "(blob)00010000"
*
* The attributes are a comma separated list of these possible
* attributes:
*
* + ro - read-only
* + boot - boot-services access
* + run - runtime access
*
* NOTE: with current implementation, no variables are available after
* ExitBootServices, and all are persisted (if possible).
*
* If not specified, the attributes default to "{boot}".
*
* The required type is one of:
*
* + utf8 - raw utf8 string
* + blob - arbitrary length hex string
*
* Maybe a utf16 type would be useful to for a string value to be auto
* converted to utf16?
*/
#define MAX_VAR_NAME 31
#define MAX_NATIVE_VAR_NAME \
(strlen("efi_xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx_") + \
(MAX_VAR_NAME * MAX_UTF8_PER_UTF16))
static int hex(unsigned char ch)
{
if (ch >= 'a' && ch <= 'f')
return ch-'a'+10;
if (ch >= '0' && ch <= '9')
return ch-'0';
if (ch >= 'A' && ch <= 'F')
return ch-'A'+10;
return -1;
}
static const char *hex2mem(u8 *mem, const char *hexstr, int count)
{
memset(mem, 0, count/2);
do {
int nibble;
*mem = 0;
if (!count || !*hexstr)
break;
nibble = hex(*hexstr);
if (nibble < 0)
break;
*mem = nibble;
count--;
hexstr++;
if (!count || !*hexstr)
break;
nibble = hex(*hexstr);
if (nibble < 0)
break;
*mem = (*mem << 4) | nibble;
count--;
hexstr++;
mem++;
} while (1);
if (*hexstr)
return hexstr;
return NULL;
}
static char *mem2hex(char *hexstr, const u8 *mem, int count)
{
static const char hexchars[] = "0123456789abcdef";
while (count-- > 0) {
u8 ch = *mem++;
*hexstr++ = hexchars[ch >> 4];
*hexstr++ = hexchars[ch & 0xf];
}
return hexstr;
}
static efi_status_t efi_to_native(char *native, s16 *variable_name,
efi_guid_t *vendor)
{
size_t len;
len = utf16_strlen((u16 *)variable_name);
if (len >= MAX_VAR_NAME)
return EFI_DEVICE_ERROR;
native += sprintf(native, "efi_%pUl_", vendor);
native = (char *)utf16_to_utf8((u8 *)native, (u16 *)variable_name, len);
*native = '\0';
return EFI_SUCCESS;
}
static const char *prefix(const char *str, const char *prefix)
{
size_t n = strlen(prefix);
if (!strncmp(prefix, str, n))
return str + n;
return NULL;
}
/* parse attributes part of variable value, if present: */
static const char *parse_attr(const char *str, u32 *attrp)
{
u32 attr = 0;
char sep = '{';
if (*str != '{') {
*attrp = EFI_VARIABLE_BOOTSERVICE_ACCESS;
return str;
}
while (*str == sep) {
const char *s;
str++;
if ((s = prefix(str, "ro"))) {
attr |= READ_ONLY;
} else if ((s = prefix(str, "boot"))) {
attr |= EFI_VARIABLE_BOOTSERVICE_ACCESS;
} else if ((s = prefix(str, "run"))) {
attr |= EFI_VARIABLE_RUNTIME_ACCESS;
} else {
printf("invalid attribute: %s\n", str);
break;
}
str = s;
sep = ',';
}
str++;
*attrp = attr;
return str;
}
/* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#GetVariable.28.29 */
efi_status_t EFIAPI efi_get_variable(s16 *variable_name,
efi_guid_t *vendor, u32 *attributes,
unsigned long *data_size, void *data)
{
char native_name[MAX_NATIVE_VAR_NAME + 1];
efi_status_t ret;
unsigned long in_size;
const char *val, *s;
u32 attr;
EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes,
data_size, data);
if (!variable_name || !vendor || !data_size)
return EFI_EXIT(EFI_INVALID_PARAMETER);
ret = efi_to_native(native_name, variable_name, vendor);
if (ret)
return EFI_EXIT(ret);
debug("%s: get '%s'\n", __func__, native_name);
val = env_get(native_name);
if (!val)
return EFI_EXIT(EFI_NOT_FOUND);
val = parse_attr(val, &attr);
in_size = *data_size;
if ((s = prefix(val, "(blob)"))) {
unsigned len = strlen(s);
/* two characters per byte: */
len = DIV_ROUND_UP(len, 2);
*data_size = len;
if (in_size < len)
return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
if (!data)
return EFI_EXIT(EFI_INVALID_PARAMETER);
if (hex2mem(data, s, len * 2))
return EFI_EXIT(EFI_DEVICE_ERROR);
debug("%s: got value: \"%s\"\n", __func__, s);
} else if ((s = prefix(val, "(utf8)"))) {
unsigned len = strlen(s) + 1;
*data_size = len;
if (in_size < len)
return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
if (!data)
return EFI_EXIT(EFI_INVALID_PARAMETER);
memcpy(data, s, len);
((char *)data)[len] = '\0';
debug("%s: got value: \"%s\"\n", __func__, (char *)data);
} else {
debug("%s: invalid value: '%s'\n", __func__, val);
return EFI_EXIT(EFI_DEVICE_ERROR);
}
if (attributes)
*attributes = attr & EFI_VARIABLE_MASK;
return EFI_EXIT(EFI_SUCCESS);
}
/* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#GetNextVariableName.28.29 */
efi_status_t EFIAPI efi_get_next_variable(
unsigned long *variable_name_size,
s16 *variable_name, efi_guid_t *vendor)
{
EFI_ENTRY("%p \"%ls\" %pUl", variable_name_size, variable_name, vendor);
return EFI_EXIT(EFI_DEVICE_ERROR);
}
/* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#SetVariable.28.29 */
efi_status_t EFIAPI efi_set_variable(s16 *variable_name,
efi_guid_t *vendor, u32 attributes,
unsigned long data_size, void *data)
{
char native_name[MAX_NATIVE_VAR_NAME + 1];
efi_status_t ret = EFI_SUCCESS;
char *val, *s;
u32 attr;
EFI_ENTRY("\"%ls\" %pUl %x %lu %p", variable_name, vendor, attributes,
data_size, data);
if (!variable_name || !vendor)
return EFI_EXIT(EFI_INVALID_PARAMETER);
ret = efi_to_native(native_name, variable_name, vendor);
if (ret)
return EFI_EXIT(ret);
#define ACCESS_ATTR (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)
if ((data_size == 0) || !(attributes & ACCESS_ATTR)) {
/* delete the variable: */
env_set(native_name, NULL);
return EFI_EXIT(EFI_SUCCESS);
}
val = env_get(native_name);
if (val) {
parse_attr(val, &attr);
if (attr & READ_ONLY)
return EFI_EXIT(EFI_WRITE_PROTECTED);
}
val = malloc(2 * data_size + strlen("{ro,run,boot}(blob)") + 1);
if (!val)
return EFI_EXIT(EFI_OUT_OF_RESOURCES);
s = val;
/* store attributes: */
attributes &= (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS);
s += sprintf(s, "{");
while (attributes) {
u32 attr = 1 << (ffs(attributes) - 1);
if (attr == EFI_VARIABLE_BOOTSERVICE_ACCESS)
s += sprintf(s, "boot");
else if (attr == EFI_VARIABLE_RUNTIME_ACCESS)
s += sprintf(s, "run");
attributes &= ~attr;
if (attributes)
s += sprintf(s, ",");
}
s += sprintf(s, "}");
/* store payload: */
s += sprintf(s, "(blob)");
s = mem2hex(s, data, data_size);
*s = '\0';
debug("%s: setting: %s=%s\n", __func__, native_name, val);
if (env_set(native_name, val))
ret = EFI_DEVICE_ERROR;
free(val);
return EFI_EXIT(ret);
}

7
lib/efi_selftest/Kconfig Normal file
View file

@ -0,0 +1,7 @@
config CMD_BOOTEFI_SELFTEST
bool "Allow booting an EFI efi_selftest"
depends on CMD_BOOTEFI
help
This adds an EFI test application to U-Boot that can be executed
with the 'bootefi selftest' command. It provides extended tests of
the EFI API implementation.

26
lib/efi_selftest/Makefile Normal file
View file

@ -0,0 +1,26 @@
:
# (C) Copyright 2017, Heinrich Schuchardt <xypron.glpk@gmx.de>
#
# SPDX-License-Identifier: GPL-2.0+
#
# This file only gets included with CONFIG_EFI_LOADER set, so all
# object inclusion implicitly depends on it
CFLAGS_efi_selftest.o := $(CFLAGS_EFI)
CFLAGS_REMOVE_efi_selftest.o := $(CFLAGS_NON_EFI)
CFLAGS_efi_selftest_console.o := $(CFLAGS_EFI)
CFLAGS_REMOVE_efi_selftest_console.o := $(CFLAGS_NON_EFI)
CFLAGS_efi_selftest_events.o := $(CFLAGS_EFI)
CFLAGS_REMOVE_efi_selftest_events.o := $(CFLAGS_NON_EFI)
CFLAGS_efi_selftest_exitbootservices.o := $(CFLAGS_EFI)
CFLAGS_REMOVE_efi_selftest_exitbootservices.o := $(CFLAGS_NON_EFI)
CFLAGS_efi_selftest_tpl.o := $(CFLAGS_EFI)
CFLAGS_REMOVE_efi_selftest_tpl.o := $(CFLAGS_NON_EFI)
obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += \
efi_selftest.o \
efi_selftest_console.o \
efi_selftest_events.o \
efi_selftest_exitbootservices.o \
efi_selftest_tpl.o

View file

@ -0,0 +1,219 @@
/*
* EFI efi_selftest
*
* Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <efi_selftest.h>
#include <vsprintf.h>
static const struct efi_system_table *systable;
static const struct efi_boot_services *boottime;
static const struct efi_runtime_services *runtime;
static efi_handle_t handle;
static u16 reset_message[] = L"Selftest completed";
/*
* Exit the boot services.
*
* The size of the memory map is determined.
* Pool memory is allocated to copy the memory map.
* The memory amp is copied and the map key is obtained.
* The map key is used to exit the boot services.
*/
void efi_st_exit_boot_services(void)
{
unsigned long map_size = 0;
unsigned long map_key;
unsigned long desc_size;
u32 desc_version;
efi_status_t ret;
struct efi_mem_desc *memory_map;
ret = boottime->get_memory_map(&map_size, NULL, &map_key, &desc_size,
&desc_version);
if (ret != EFI_BUFFER_TOO_SMALL) {
efi_st_printf("ERROR: GetMemoryMap did not return "
"EFI_BUFFER_TOO_SMALL\n");
return;
}
/* Allocate extra space for newly allocated memory */
map_size += sizeof(struct efi_mem_desc);
ret = boottime->allocate_pool(EFI_BOOT_SERVICES_DATA, map_size,
(void **)&memory_map);
if (ret != EFI_SUCCESS) {
efi_st_printf("ERROR: AllocatePool did not return "
"EFI_SUCCESS\n");
return;
}
ret = boottime->get_memory_map(&map_size, memory_map, &map_key,
&desc_size, &desc_version);
if (ret != EFI_SUCCESS) {
efi_st_printf("ERROR: GetMemoryMap did not return "
"EFI_SUCCESS\n");
return;
}
ret = boottime->exit_boot_services(handle, map_key);
if (ret != EFI_SUCCESS) {
efi_st_printf("ERROR: ExitBootServices did not return "
"EFI_SUCCESS\n");
return;
}
efi_st_printf("\nBoot services terminated\n");
}
/*
* Set up a test.
*
* @test the test to be executed
* @failures counter that will be incremented if a failure occurs
*/
static int setup(struct efi_unit_test *test, unsigned int *failures)
{
int ret;
if (!test->setup)
return 0;
efi_st_printf("\nSetting up '%s'\n", test->name);
ret = test->setup(handle, systable);
if (ret) {
efi_st_printf("ERROR: Setting up '%s' failed\n", test->name);
++*failures;
} else {
efi_st_printf("Setting up '%s' succeeded\n", test->name);
}
return ret;
}
/*
* Execute a test.
*
* @test the test to be executed
* @failures counter that will be incremented if a failure occurs
*/
static int execute(struct efi_unit_test *test, unsigned int *failures)
{
int ret;
if (!test->execute)
return 0;
efi_st_printf("\nExecuting '%s'\n", test->name);
ret = test->execute();
if (ret) {
efi_st_printf("ERROR: Executing '%s' failed\n", test->name);
++*failures;
} else {
efi_st_printf("Executing '%s' succeeded\n", test->name);
}
return ret;
}
/*
* Tear down a test.
*
* @test the test to be torn down
* @failures counter that will be incremented if a failure occurs
*/
static int teardown(struct efi_unit_test *test, unsigned int *failures)
{
int ret;
if (!test->teardown)
return 0;
efi_st_printf("\nTearing down '%s'\n", test->name);
ret = test->teardown();
if (ret) {
efi_st_printf("ERROR: Tearing down '%s' failed\n", test->name);
++*failures;
} else {
efi_st_printf("Tearing down '%s' succeeded\n", test->name);
}
return ret;
}
/*
* Execute selftest of the EFI API
*
* This is the main entry point of the EFI selftest application.
*
* All tests use a driver model and are run in three phases:
* setup, execute, teardown.
*
* A test may be setup and executed at boottime,
* it may be setup at boottime and executed at runtime,
* or it may be setup and executed at runtime.
*
* After executing all tests the system is reset.
*
* @image_handle: handle of the loaded EFI image
* @systab: EFI system table
*/
efi_status_t EFIAPI efi_selftest(efi_handle_t image_handle,
struct efi_system_table *systab)
{
struct efi_unit_test *test;
unsigned int failures = 0;
systable = systab;
boottime = systable->boottime;
runtime = systable->runtime;
handle = image_handle;
con_out = systable->con_out;
con_in = systable->con_in;
efi_st_printf("\nTesting EFI API implementation\n");
efi_st_printf("\nNumber of tests to execute: %u\n",
ll_entry_count(struct efi_unit_test, efi_unit_test));
/* Execute boottime tests */
for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
if (test->phase == EFI_EXECUTE_BEFORE_BOOTTIME_EXIT) {
setup(test, &failures);
execute(test, &failures);
teardown(test, &failures);
}
}
/* Execute mixed tests */
for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
if (test->phase == EFI_SETUP_BEFORE_BOOTTIME_EXIT)
setup(test, &failures);
}
efi_st_exit_boot_services();
for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
if (test->phase == EFI_SETUP_BEFORE_BOOTTIME_EXIT) {
execute(test, &failures);
teardown(test, &failures);
}
}
/* Execute runtime tests */
for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
if (test->phase == EFI_SETUP_AFTER_BOOTTIME_EXIT) {
setup(test, &failures);
execute(test, &failures);
teardown(test, &failures);
}
}
/* Give feedback */
efi_st_printf("\nSummary: %u failures\n\n", failures);
/* Reset system */
efi_st_printf("Preparing for reset. Press any key.\n");
efi_st_get_key();
runtime->reset_system(EFI_RESET_WARM, EFI_NOT_READY,
sizeof(reset_message), reset_message);
efi_st_printf("\nERROR: reset failed.\n");
return EFI_UNSUPPORTED;
}

View file

@ -0,0 +1,187 @@
/*
* EFI efi_selftest
*
* Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <efi_selftest.h>
#include <vsprintf.h>
struct efi_simple_text_output_protocol *con_out;
struct efi_simple_input_interface *con_in;
/*
* Print a pointer to an u16 string
*
* @pointer: pointer
* @buf: pointer to buffer address
* on return position of terminating zero word
*/
static void pointer(void *pointer, u16 **buf)
{
int i;
u16 c;
uintptr_t p = (uintptr_t)pointer;
u16 *pos = *buf;
for (i = 8 * sizeof(p) - 4; i >= 0; i -= 4) {
c = (p >> i) & 0x0f;
c += '0';
if (c > '9')
c += 'a' - '9' - 1;
*pos++ = c;
}
*pos = 0;
*buf = pos;
}
/*
* Print an unsigned 32bit value as decimal number to an u16 string
*
* @value: value to be printed
* @buf: pointer to buffer address
* on return position of terminating zero word
*/
static void uint2dec(u32 value, u16 **buf)
{
u16 *pos = *buf;
int i;
u16 c;
u64 f;
/*
* Increment by .5 and multiply with
* (2 << 60) / 1,000,000,000 = 0x44B82FA0.9B5A52CC
* to move the first digit to bit 60-63.
*/
f = 0x225C17D0;
f += (0x9B5A52DULL * value) >> 28;
f += 0x44B82FA0ULL * value;
for (i = 0; i < 10; ++i) {
/* Write current digit */
c = f >> 60;
if (c || pos != *buf)
*pos++ = c + '0';
/* Eliminate current digit */
f &= 0xfffffffffffffff;
/* Get next digit */
f *= 0xaULL;
}
if (pos == *buf)
*pos++ = '0';
*pos = 0;
*buf = pos;
}
/*
* Print a signed 32bit value as decimal number to an u16 string
*
* @value: value to be printed
* @buf: pointer to buffer address
* on return position of terminating zero word
*/
static void int2dec(s32 value, u16 **buf)
{
u32 u;
u16 *pos = *buf;
if (value < 0) {
*pos++ = '-';
u = -value;
} else {
u = value;
}
uint2dec(u, &pos);
*buf = pos;
}
/*
* Print a formatted string to the EFI console
*
* @fmt: format string
* @...: optional arguments
*/
void efi_st_printf(const char *fmt, ...)
{
va_list args;
u16 buf[160];
const char *c;
u16 *pos = buf;
const char *s;
va_start(args, fmt);
c = fmt;
for (; *c; ++c) {
switch (*c) {
case '\\':
++c;
switch (*c) {
case '\0':
--c;
break;
case 'n':
*pos++ = '\n';
break;
case 'r':
*pos++ = '\r';
break;
case 't':
*pos++ = '\t';
break;
default:
*pos++ = *c;
}
break;
case '%':
++c;
switch (*c) {
case '\0':
--c;
break;
case 'd':
int2dec(va_arg(args, s32), &pos);
break;
case 'p':
pointer(va_arg(args, void*), &pos);
break;
case 's':
s = va_arg(args, const char *);
for (; *s; ++s)
*pos++ = *s;
break;
case 'u':
uint2dec(va_arg(args, u32), &pos);
break;
default:
break;
}
break;
default:
*pos++ = *c;
}
}
va_end(args);
*pos = 0;
con_out->output_string(con_out, buf);
}
/*
* Reads an Unicode character from the input device.
*
* @return: Unicode character
*/
u16 efi_st_get_key(void)
{
struct efi_input_key input_key;
efi_status_t ret;
/* Wait for next key */
do {
ret = con_in->read_key_stroke(con_in, &input_key);
} while (ret == EFI_NOT_READY);
return input_key.unicode_char;
}

View file

@ -0,0 +1,195 @@
/*
* efi_selftest_events
*
* Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This unit test uses timer events to check the implementation
* of the following boottime services:
* CreateEvent, CloseEvent, WaitForEvent, CheckEvent, SetTimer.
*/
#include <efi_selftest.h>
static struct efi_event *event_notify;
static struct efi_event *event_wait;
static unsigned int counter;
static struct efi_boot_services *boottime;
/*
* Notification function, increments a counter.
*
* @event notified event
* @context pointer to the counter
*/
static void EFIAPI notify(struct efi_event *event, void *context)
{
if (!context)
return;
++*(unsigned int *)context;
}
/*
* Setup unit test.
*
* Create two timer events.
* One with EVT_NOTIFY_SIGNAL, the other with EVT_NOTIFY_WAIT.
*
* @handle: handle of the loaded image
* @systable: system table
*/
static int setup(const efi_handle_t handle,
const struct efi_system_table *systable)
{
efi_status_t ret;
boottime = systable->boottime;
ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL,
TPL_CALLBACK, notify, (void *)&counter,
&event_notify);
if (ret != EFI_SUCCESS) {
efi_st_error("could not create event\n");
return 1;
}
ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_WAIT,
TPL_CALLBACK, notify, NULL, &event_wait);
if (ret != EFI_SUCCESS) {
efi_st_error("could not create event\n");
return 1;
}
return 0;
}
/*
* Tear down unit test.
*
* Close the events created in setup.
*/
static int teardown(void)
{
efi_status_t ret;
if (event_notify) {
ret = boottime->close_event(event_notify);
event_notify = NULL;
if (ret != EFI_SUCCESS) {
efi_st_error("could not close event\n");
return 1;
}
}
if (event_wait) {
ret = boottime->close_event(event_wait);
event_wait = NULL;
if (ret != EFI_SUCCESS) {
efi_st_error("could not close event\n");
return 1;
}
}
return 0;
}
/*
* Execute unit test.
*
* Run a 10 ms periodic timer and check that it is called 10 times
* while waiting for 100 ms single shot timer.
*
* Run a 100 ms single shot timer and check that it is called once
* while waiting for 100 ms periodic timer for two periods.
*/
static int execute(void)
{
unsigned long index;
efi_status_t ret;
/* Set 10 ms timer */
counter = 0;
ret = boottime->set_timer(event_notify, EFI_TIMER_PERIODIC, 100000);
if (ret != EFI_SUCCESS) {
efi_st_error("Could not set timer\n");
return 1;
}
/* Set 100 ms timer */
ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 1000000);
if (ret != EFI_SUCCESS) {
efi_st_error("Could not set timer\n");
return 1;
}
index = 5;
ret = boottime->wait_for_event(1, &event_wait, &index);
if (ret != EFI_SUCCESS) {
efi_st_error("Could not wait for event\n");
return 1;
}
ret = boottime->check_event(event_wait);
if (ret != EFI_NOT_READY) {
efi_st_error("Signaled state was not cleared.\n");
efi_st_printf("ret = %u\n", (unsigned int)ret);
return 1;
}
if (index != 0) {
efi_st_error("WaitForEvent returned wrong index\n");
return 1;
}
efi_st_printf("Counter periodic: %u\n", counter);
if (counter < 8 || counter > 12) {
efi_st_error("Incorrect timing of events\n");
return 1;
}
ret = boottime->set_timer(event_notify, EFI_TIMER_STOP, 0);
if (index != 0) {
efi_st_error("Could not cancel timer\n");
return 1;
}
/* Set 10 ms timer */
counter = 0;
ret = boottime->set_timer(event_notify, EFI_TIMER_RELATIVE, 100000);
if (index != 0) {
efi_st_error("Could not set timer\n");
return 1;
}
/* Set 100 ms timer */
ret = boottime->set_timer(event_wait, EFI_TIMER_PERIODIC, 1000000);
if (index != 0) {
efi_st_error("Could not set timer\n");
return 1;
}
ret = boottime->wait_for_event(1, &event_wait, &index);
if (ret != EFI_SUCCESS) {
efi_st_error("Could not wait for event\n");
return 1;
}
efi_st_printf("Counter single shot: %u\n", counter);
if (counter != 1) {
efi_st_error("Single shot timer failed\n");
return 1;
}
ret = boottime->wait_for_event(1, &event_wait, &index);
if (ret != EFI_SUCCESS) {
efi_st_error("Could not wait for event\n");
return 1;
}
efi_st_printf("Stopped counter: %u\n", counter);
if (counter != 1) {
efi_st_error("Stopped timer fired\n");
return 1;
}
ret = boottime->set_timer(event_wait, EFI_TIMER_STOP, 0);
if (index != 0) {
efi_st_error("Could not cancel timer\n");
return 1;
}
return 0;
}
EFI_UNIT_TEST(events) = {
.name = "event services",
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
.setup = setup,
.execute = execute,
.teardown = teardown,
};

View file

@ -0,0 +1,106 @@
/*
* efi_selftest_events
*
* Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This unit test checks that the notification function of an
* EVT_SIGNAL_EXIT_BOOT_SERVICES event is called exactly once.
*/
#include <efi_selftest.h>
static struct efi_boot_services *boottime;
static struct efi_event *event_notify;
static unsigned int counter;
/*
* Notification function, increments a counter.
*
* @event notified event
* @context pointer to the counter
*/
static void EFIAPI notify(struct efi_event *event, void *context)
{
if (!context)
return;
++*(unsigned int *)context;
}
/*
* Setup unit test.
*
* Create an EVT_SIGNAL_EXIT_BOOT_SERVICES event.
*
* @handle: handle of the loaded image
* @systable: system table
*/
static int setup(const efi_handle_t handle,
const struct efi_system_table *systable)
{
efi_status_t ret;
boottime = systable->boottime;
counter = 0;
ret = boottime->create_event(EVT_SIGNAL_EXIT_BOOT_SERVICES,
TPL_CALLBACK, notify, (void *)&counter,
&event_notify);
if (ret != EFI_SUCCESS) {
efi_st_error("could not create event\n");
return 1;
}
return 0;
}
/*
* Tear down unit test.
*
* Close the event created in setup.
*/
static int teardown(void)
{
efi_status_t ret;
if (event_notify) {
ret = boottime->close_event(event_notify);
event_notify = NULL;
if (ret != EFI_SUCCESS) {
efi_st_error("could not close event\n");
return 1;
}
}
return 0;
}
/*
* Execute unit test.
*
* Check that the notification function of the EVT_SIGNAL_EXIT_BOOT_SERVICES
* event has been called.
*
* Call ExitBootServices again and check that the notification function is
* not called again.
*/
static int execute(void)
{
if (counter != 1) {
efi_st_error("ExitBootServices was not notified");
return 1;
}
efi_st_exit_boot_services();
if (counter != 1) {
efi_st_error("ExitBootServices was notified twice");
return 1;
}
return 0;
}
EFI_UNIT_TEST(exitbootservices) = {
.name = "ExitBootServices",
.phase = EFI_SETUP_BEFORE_BOOTTIME_EXIT,
.setup = setup,
.execute = execute,
.teardown = teardown,
};

View file

@ -0,0 +1,214 @@
/*
* efi_selftest_events
*
* Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This unit test uses timer events to check the handling of
* task priority levels.
*/
#include <efi_selftest.h>
static struct efi_event *event_notify;
static struct efi_event *event_wait;
static unsigned int counter;
static struct efi_boot_services *boottime;
/*
* Notification function, increments a counter.
*
* @event notified event
* @context pointer to the counter
*/
static void EFIAPI notify(struct efi_event *event, void *context)
{
if (!context)
return;
++*(unsigned int *)context;
}
/*
* Setup unit test.
*
* Create two timer events.
* One with EVT_NOTIFY_SIGNAL, the other with EVT_NOTIFY_WAIT.
*
* @handle: handle of the loaded image
* @systable: system table
*/
static int setup(const efi_handle_t handle,
const struct efi_system_table *systable)
{
efi_status_t ret;
boottime = systable->boottime;
ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL,
TPL_CALLBACK, notify, (void *)&counter,
&event_notify);
if (ret != EFI_SUCCESS) {
efi_st_error("could not create event\n");
return 1;
}
ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_WAIT,
TPL_HIGH_LEVEL, notify, NULL, &event_wait);
if (ret != EFI_SUCCESS) {
efi_st_error("could not create event\n");
return 1;
}
return 0;
}
/*
* Tear down unit test.
*
* Close the events created in setup.
*/
static int teardown(void)
{
efi_status_t ret;
if (event_notify) {
ret = boottime->close_event(event_notify);
event_notify = NULL;
if (ret != EFI_SUCCESS) {
efi_st_error("could not close event\n");
return 1;
}
}
if (event_wait) {
ret = boottime->close_event(event_wait);
event_wait = NULL;
if (ret != EFI_SUCCESS) {
efi_st_error("could not close event\n");
return 1;
}
}
boottime->restore_tpl(TPL_APPLICATION);
return 0;
}
/*
* Execute unit test.
*
* Run a 10 ms periodic timer and check that it is called 10 times
* while waiting for 100 ms single shot timer.
*
* Raise the TPL level to the level of the 10 ms timer and observe
* that the notification function is not called again.
*
* Lower the TPL level and check that the queued notification
* function is called.
*/
static int execute(void)
{
unsigned long index;
efi_status_t ret;
UINTN old_tpl;
/* Set 10 ms timer */
counter = 0;
ret = boottime->set_timer(event_notify, EFI_TIMER_PERIODIC, 100000);
if (ret != EFI_SUCCESS) {
efi_st_error("Could not set timer\n");
return 1;
}
/* Set 100 ms timer */
ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 1000000);
if (ret != EFI_SUCCESS) {
efi_st_error("Could not set timer\n");
return 1;
}
index = 5;
ret = boottime->wait_for_event(1, &event_wait, &index);
if (ret != EFI_SUCCESS) {
efi_st_error("Could not wait for event\n");
return 1;
}
ret = boottime->check_event(event_wait);
if (ret != EFI_NOT_READY) {
efi_st_error("Signaled state was not cleared.\n");
efi_st_printf("ret = %u\n", (unsigned int)ret);
return 1;
}
if (index != 0) {
efi_st_error("WaitForEvent returned wrong index\n");
return 1;
}
efi_st_printf("Counter with TPL level TPL_APPLICATION: %u\n", counter);
if (counter < 8 || counter > 12) {
efi_st_error("Incorrect timing of events\n");
return 1;
}
ret = boottime->set_timer(event_notify, EFI_TIMER_STOP, 0);
if (index != 0) {
efi_st_error("Could not cancel timer\n");
return 1;
}
/* Raise TPL level */
old_tpl = boottime->raise_tpl(TPL_CALLBACK);
if (old_tpl != TPL_APPLICATION) {
efi_st_error("Initial TPL level was not TPL_APPLICATION");
return 1;
}
/* Set 10 ms timer */
counter = 0;
ret = boottime->set_timer(event_notify, EFI_TIMER_PERIODIC, 100000);
if (index != 0) {
efi_st_error("Could not set timer\n");
return 1;
}
/* Set 100 ms timer */
ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 1000000);
if (ret != EFI_SUCCESS) {
efi_st_error("Could not set timer\n");
return 1;
}
do {
ret = boottime->check_event(event_wait);
} while (ret == EFI_NOT_READY);
if (ret != EFI_SUCCESS) {
efi_st_error("Could not check event\n");
return 1;
}
efi_st_printf("Counter with TPL level TPL_CALLBACK: %u\n", counter);
if (counter != 0) {
efi_st_error("Suppressed timer fired\n");
return 1;
}
/* Set 1 ms timer */
ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 1000);
if (ret != EFI_SUCCESS) {
efi_st_error("Could not set timer\n");
return 1;
}
/* Restore the old TPL level */
boottime->restore_tpl(TPL_APPLICATION);
ret = boottime->wait_for_event(1, &event_wait, &index);
if (ret != EFI_SUCCESS) {
efi_st_error("Could not wait for event\n");
return 1;
}
efi_st_printf("Counter with TPL level TPL_APPLICATION: %u\n", counter);
if (counter < 1) {
efi_st_error("Queued timer event did not fire\n");
return 1;
}
ret = boottime->set_timer(event_wait, EFI_TIMER_STOP, 0);
if (index != 0) {
efi_st_error("Could not cancel timer\n");
return 1;
}
return 0;
}
EFI_UNIT_TEST(tpl) = {
.name = "task priority levels",
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
.setup = setup,
.execute = execute,
.teardown = teardown,
};

View file

@ -359,20 +359,22 @@ cmd_S_ttf= \
$(obj)/%.S: $(src)/%.ttf
$(call cmd,S_ttf)
# EFI Hello World application
# EFI applications
# A Makefile target *.efi is built as EFI application.
# A Makefile target *_efi.S wraps *.efi as built-in EFI application.
# ---------------------------------------------------------------------------
# Generate an assembly file to wrap the EFI app
cmd_S_efi= \
( \
echo '.section .rodata.efi.init,"a"'; \
echo '.balign 16'; \
echo '.global __efi_hello_world_begin'; \
echo '__efi_hello_world_begin:'; \
echo '.incbin "$<" '; \
echo '__efi_hello_world_end:'; \
echo '.global __efi_hello_world_end'; \
echo '.balign 16'; \
cmd_S_efi= \
( \
echo '.section .rodata.$*.init,"a"'; \
echo '.balign 16'; \
echo '.global __efi_$*_begin'; \
echo '__efi_$*_begin:'; \
echo '.incbin "$<" '; \
echo '__efi_$*_end:'; \
echo '.global __efi_$*_end'; \
echo '.balign 16'; \
) > $@
$(obj)/%_efi.S: $(obj)/%.efi
@ -383,7 +385,7 @@ cmd_efi_objcopy = $(OBJCOPY) -j .header -j .text -j .sdata -j .data -j \
.dynamic -j .dynsym -j .rel* -j .rela* -j .reloc \
$(if $(EFI_TARGET),$(EFI_TARGET),-O binary) $^ $@
$(obj)/%.efi: $(obj)/%.so
$(obj)/%.efi: $(obj)/%_efi.so
$(call cmd,efi_objcopy)
quiet_cmd_efi_ld = LD $@
@ -392,9 +394,7 @@ cmd_efi_ld = $(LD) -nostdlib -znocombreloc -T $(EFI_LDS_PATH) -shared \
EFI_LDS_PATH = $(srctree)/arch/$(ARCH)/lib/$(EFI_LDS)
$(obj)/helloworld.so: $(EFI_LDS_PATH)
$(obj)/helloworld.so: $(obj)/helloworld.o arch/$(ARCH)/lib/$(EFI_CRT0) \
$(obj)/%_efi.so: $(obj)/%.o arch/$(ARCH)/lib/$(EFI_CRT0) \
arch/$(ARCH)/lib/$(EFI_RELOC)
$(call cmd,efi_ld)

View file

@ -0,0 +1,25 @@
# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
# Copyright (c) 2017, Heinrich Schuchardt <xypron.glpk@gmx.de>
#
# SPDX-License-Identifier: GPL-2.0
# Test efi API implementation
import pytest
import u_boot_utils
@pytest.mark.buildconfigspec('cmd_bootefi_selftest')
def test_efi_selftest(u_boot_console):
"""
Run bootefi selftest
"""
u_boot_console.run_command(cmd='bootefi selftest', wait_for_prompt=False)
m = u_boot_console.p.expect(['Summary: 0 failures', 'Press any key'])
if m != 0:
raise Exception('Failures occured during the EFI selftest')
u_boot_console.run_command(cmd='', wait_for_echo=False, wait_for_prompt=False);
m = u_boot_console.p.expect(['resetting', 'U-Boot'])
if m != 0:
raise Exception('Reset failed during the EFI selftest')
u_boot_console.restart_uboot();