mirror of
https://github.com/u-boot/u-boot.git
synced 2024-11-23 20:24:26 +08:00
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:
commit
958046fc78
@ -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
|
||||
|
@ -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
|
||||
|
288
cmd/bootefi.c
288
cmd/bootefi.c
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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[];
|
||||
|
@ -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
|
||||
|
@ -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 " \
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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
|
||||
|
@ -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
91
include/efi_selftest.h
Normal 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 */
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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/
|
||||
|
@ -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
|
||||
|
180
lib/efi_loader/efi_bootmgr.c
Normal file
180
lib/efi_loader/efi_bootmgr.c
Normal 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;
|
||||
}
|
@ -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:
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
563
lib/efi_loader/efi_device_path.c
Normal file
563
lib/efi_loader/efi_device_path.c
Normal 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;
|
||||
}
|
@ -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)
|
||||
{
|
||||
|
@ -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
560
lib/efi_loader/efi_file.c
Normal 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;
|
||||
}
|
@ -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 = §ions[i];
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
};
|
||||
|
335
lib/efi_loader/efi_variable.c
Normal file
335
lib/efi_loader/efi_variable.c
Normal 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
7
lib/efi_selftest/Kconfig
Normal 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
26
lib/efi_selftest/Makefile
Normal 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
|
219
lib/efi_selftest/efi_selftest.c
Normal file
219
lib/efi_selftest/efi_selftest.c
Normal 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;
|
||||
}
|
187
lib/efi_selftest/efi_selftest_console.c
Normal file
187
lib/efi_selftest/efi_selftest_console.c
Normal 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;
|
||||
}
|
195
lib/efi_selftest/efi_selftest_events.c
Normal file
195
lib/efi_selftest/efi_selftest_events.c
Normal 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,
|
||||
};
|
106
lib/efi_selftest/efi_selftest_exitbootservices.c
Normal file
106
lib/efi_selftest/efi_selftest_exitbootservices.c
Normal 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,
|
||||
};
|
214
lib/efi_selftest/efi_selftest_tpl.c
Normal file
214
lib/efi_selftest/efi_selftest_tpl.c
Normal 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,
|
||||
};
|
@ -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)
|
||||
|
||||
|
25
test/py/tests/test_efi_selftest.py
Normal file
25
test/py/tests/test_efi_selftest.py
Normal 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();
|
Loading…
Reference in New Issue
Block a user