[PATCH v9 0/9] efi: Improvements to U-Boot running on top of UEFI

At present U-Boot can be built as an EFI app, but it is really just for testing, with very few features. Instead, the payload build is used for booting on top of UEFI, where U-Boot takes over the machine immediately and supplies its own drivers.
But the app could be made more useful.
This series provides access to EFI block devices and the video console within U-Boot. It also restructures the app to make it possible to boot a kernel, although further work is needed to implement this.
This can be thought of as making U-Boot perform a little like 'grub', in that it runs purely based on UEFI-provided services.
Of course a lot more work is required, but this is a start.
Note: It would be very useful to include qemu tests of the app and stub in CI.
This is available at u-boot-dm/efi-working
Changes in v9: - Add a comment as to why printf() cannot be used - Comment out storing the EFI memory map in the app and update commit message - Drop changes previously applied
Changes in v8: - Add new patch to tidy up header comments
Changes in v7: - Rebase on -master instead of -next
Changes in v6: - Fix typo in function comment
Changes in v5: - Add new patch to avoid using the 64-bit link script for the EFI app - Add new patch to build the 64-bit app properly - Add new patch to round out the link script for 64-bit EFI - Add new patch to set the correct link flags for the 64-bit EFI app - Add new patch to tweak the code used for the 64-bit EFI app - Add various patches to fix up the 64-bit app so that it boots
Changes in v2: - Add a sentence about what the patch does - Add new patch to support the efi command in the app
Simon Glass (9): efi: Move exit_boot_services into a function x86: efi: Update efi_get_next_mem_desc() to avoid needing a map efi: Support the efi command in the app x86: efi: Tweak the code used for the 64-bit EFI app x86: efi: Round out the link script for 64-bit EFI x86: efi: Don't use the 64-bit link script for the EFI app x86: efi: Set the correct link flags for the 64-bit EFI app efi: Build the 64-bit app properly efi: Tidy up some comments in efi header
Makefile | 4 +- arch/x86/config.mk | 15 +++++-- arch/x86/cpu/config.mk | 2 +- arch/x86/cpu/efi/payload.c | 8 ++-- arch/x86/cpu/x86_64/cpu.c | 5 +++ arch/x86/lib/Makefile | 5 +-- arch/x86/lib/elf_x86_64_efi.lds | 5 ++- arch/x86/lib/relocate.c | 2 + cmd/Makefile | 2 +- cmd/efi.c | 78 +++++++++++++++++++++------------ include/efi.h | 64 +++++++++++++++++++++++---- lib/efi/efi.c | 72 ++++++++++++++++++++++++++++++ lib/efi/efi_app.c | 42 ++++++++++++++++++ lib/efi/efi_stub.c | 66 +++++----------------------- 14 files changed, 263 insertions(+), 107 deletions(-)

At present this code is inline in the app and stub. But they do the same thing. The difference is that the stub does it immediately and the app doesn't want to do it until the end (when it boots a kernel) or not at all, if returning to UEFI.
Move it into a function so it can be called as needed.
Add a comment showing how to store the memory map so that it can be accessed within the app if needed, for debugging purposes only. The map can change without notice.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v9: - Add a comment as to why printf() cannot be used - Comment out storing the EFI memory map in the app and update commit message
Changes in v6: - Fix typo in function comment
Changes in v2: - Add a sentence about what the patch does
include/efi.h | 32 +++++++++++++++++++++ lib/efi/efi.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++ lib/efi/efi_app.c | 9 ++++++ lib/efi/efi_stub.c | 66 +++++++----------------------------------- 4 files changed, 124 insertions(+), 55 deletions(-)
diff --git a/include/efi.h b/include/efi.h index 877a2e5a8d3..dc9907fa16e 100644 --- a/include/efi.h +++ b/include/efi.h @@ -407,6 +407,12 @@ static inline struct efi_mem_desc *efi_get_next_mem_desc( * @sys_table: Pointer to system table * @boot: Pointer to boot-services table * @run: Pointer to runtime-services table + * @memmap_key: Key returned from get_memory_map() + * @memmap_desc: List of memory-map records + * @memmap_alloc: Amount of memory allocated for memory map list + * @memmap_size Size of memory-map list in bytes + * @memmap_desc_size: Size of an individual memory-map record, in bytes + * @memmap_version: Memory-map version * * @use_pool_for_malloc: true if all allocation should go through the EFI 'pool' * methods allocate_pool() and free_pool(); false to use 'pages' methods @@ -424,6 +430,12 @@ struct efi_priv { struct efi_system_table *sys_table; struct efi_boot_services *boot; struct efi_runtime_services *run; + efi_uintn_t memmap_key; + struct efi_mem_desc *memmap_desc; + efi_uintn_t memmap_alloc; + efi_uintn_t memmap_size; + efi_uintn_t memmap_desc_size; + u32 memmap_version;
/* app: */ bool use_pool_for_malloc; @@ -578,4 +590,24 @@ void efi_putc(struct efi_priv *priv, const char ch); */ int efi_info_get(enum efi_entry_t type, void **datap, int *sizep);
+/** + * efi_store_memory_map() - Collect the memory-map info from EFI + * + * Collect the memory info and store it for later use, e.g. in calling + * exit_boot_services() + * + * @priv: Pointer to private EFI structure + * @return 0 if OK, non-zero on error + */ +int efi_store_memory_map(struct efi_priv *priv); + +/** + * efi_call_exit_boot_services() - Handle the exit-boot-service procedure + * + * Tell EFI we don't want their boot services anymore + * + * Return: 0 if OK, non-zero on error + */ +int efi_call_exit_boot_services(void); + #endif /* _LINUX_EFI_H */ diff --git a/lib/efi/efi.c b/lib/efi/efi.c index cd6bf47b180..aa42f1842f3 100644 --- a/lib/efi/efi.c +++ b/lib/efi/efi.c @@ -135,3 +135,75 @@ void efi_free(struct efi_priv *priv, void *ptr)
boot->free_pool(ptr); } + +int efi_store_memory_map(struct efi_priv *priv) +{ + struct efi_boot_services *boot = priv->sys_table->boottime; + efi_uintn_t size, desc_size; + efi_status_t ret; + + /* Get the memory map so we can switch off EFI */ + size = 0; + ret = boot->get_memory_map(&size, NULL, &priv->memmap_key, + &priv->memmap_desc_size, + &priv->memmap_version); + if (ret != EFI_BUFFER_TOO_SMALL) { + /* + * Note this function avoids using printf() since it is not + * available in the stub + */ + printhex2(EFI_BITS_PER_LONG); + putc(' '); + printhex2(ret); + puts(" No memory map\n"); + return ret; + } + /* + * Since doing a malloc() may change the memory map and also we want to + * be able to read the memory map in efi_call_exit_boot_services() + * below, after more changes have happened + */ + priv->memmap_alloc = size + 1024; + priv->memmap_size = priv->memmap_alloc; + priv->memmap_desc = efi_malloc(priv, size, &ret); + if (!priv->memmap_desc) { + printhex2(ret); + puts(" No memory for memory descriptor\n"); + return ret; + } + + ret = boot->get_memory_map(&priv->memmap_size, priv->memmap_desc, + &priv->memmap_key, &desc_size, + &priv->memmap_version); + if (ret) { + printhex2(ret); + puts(" Can't get memory map\n"); + return ret; + } + + return 0; +} + +int efi_call_exit_boot_services(void) +{ + struct efi_priv *priv = efi_get_priv(); + const struct efi_boot_services *boot = priv->boot; + efi_uintn_t size; + u32 version; + efi_status_t ret; + + size = priv->memmap_alloc; + ret = boot->get_memory_map(&size, priv->memmap_desc, + &priv->memmap_key, + &priv->memmap_desc_size, &version); + if (ret) { + printhex2(ret); + puts(" Can't get memory map\n"); + return ret; + } + ret = boot->exit_boot_services(priv->parent_image, priv->memmap_key); + if (ret) + return ret; + + return 0; +} diff --git a/lib/efi/efi_app.c b/lib/efi/efi_app.c index d60f2f6c28f..5c2593bc4d9 100644 --- a/lib/efi/efi_app.c +++ b/lib/efi/efi_app.c @@ -321,6 +321,15 @@ efi_status_t EFIAPI efi_main(efi_handle_t image, return ret; }
+ /* + * We could store the EFI memory map here, but it changes all the time, + * so this is only useful for debugging. + * + * ret = efi_store_memory_map(priv); + * if (ret) + * return ret; + */ + printf("starting\n");
board_init_f(GD_FLG_SKIP_RELOC); diff --git a/lib/efi/efi_stub.c b/lib/efi/efi_stub.c index c89ae7c9072..646cde3214c 100644 --- a/lib/efi/efi_stub.c +++ b/lib/efi/efi_stub.c @@ -304,15 +304,12 @@ efi_status_t EFIAPI efi_main(efi_handle_t image, { struct efi_priv local_priv, *priv = &local_priv; struct efi_boot_services *boot = sys_table->boottime; - struct efi_mem_desc *desc; struct efi_entry_memmap map; struct efi_gop *gop; struct efi_entry_gopmode mode; struct efi_entry_systable table; efi_guid_t efi_gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; - efi_uintn_t key, desc_size, size; efi_status_t ret; - u32 version; int cs32;
ret = efi_init(priv, "Payload", image, sys_table); @@ -327,24 +324,11 @@ efi_status_t EFIAPI efi_main(efi_handle_t image, if (cs32 < 0) return EFI_UNSUPPORTED;
- /* Get the memory map so we can switch off EFI */ - size = 0; - ret = boot->get_memory_map(&size, NULL, &key, &desc_size, &version); - if (ret != EFI_BUFFER_TOO_SMALL) { - printhex2(EFI_BITS_PER_LONG); - putc(' '); - printhex2(ret); - puts(" No memory map\n"); - return ret; - } - size += 1024; /* Since doing a malloc() may change the memory map! */ - desc = efi_malloc(priv, size, &ret); - if (!desc) { - printhex2(ret); - puts(" No memory for memory descriptor\n"); + ret = efi_store_memory_map(priv); + if (ret) return ret; - } - ret = setup_info_table(priv, size + 128); + + ret = setup_info_table(priv, priv->memmap_size + 128); if (ret) return ret;
@@ -360,48 +344,20 @@ efi_status_t EFIAPI efi_main(efi_handle_t image, sizeof(struct efi_gop_mode_info)); }
- ret = boot->get_memory_map(&size, desc, &key, &desc_size, &version); - if (ret) { - printhex2(ret); - puts(" Can't get memory map\n"); - return ret; - } - table.sys_table = (ulong)sys_table; add_entry_addr(priv, EFIET_SYS_TABLE, &table, sizeof(table), NULL, 0);
- ret = boot->exit_boot_services(image, key); - if (ret) { - /* - * Unfortunately it happens that we cannot exit boot services - * the first time. But the second time it work. I don't know - * why but this seems to be a repeatable problem. To get - * around it, just try again. - */ - printhex2(ret); - puts(" Can't exit boot services\n"); - size = sizeof(desc); - ret = boot->get_memory_map(&size, desc, &key, &desc_size, - &version); - if (ret) { - printhex2(ret); - puts(" Can't get memory map\n"); - return ret; - } - ret = boot->exit_boot_services(image, key); - if (ret) { - printhex2(ret); - puts(" Can't exit boot services 2\n"); - return ret; - } - } + ret = efi_call_exit_boot_services(); + if (ret) + return ret;
/* The EFI UART won't work now, switch to a debug one */ use_uart = true;
- map.version = version; - map.desc_size = desc_size; - add_entry_addr(priv, EFIET_MEMORY_MAP, &map, sizeof(map), desc, size); + map.version = priv->memmap_version; + map.desc_size = priv->memmap_desc_size; + add_entry_addr(priv, EFIET_MEMORY_MAP, &map, sizeof(map), + priv->memmap_desc, priv->memmap_size); add_entry_addr(priv, EFIET_END, NULL, 0, 0, 0);
memcpy((void *)CONFIG_SYS_TEXT_BASE, _binary_u_boot_bin_start,

At present this function requires a pointer to struct efi_entry_memmap but the only field used in there is the desc_size. We want to be able to use it from the app, so update it to use desc_size directly.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
arch/x86/cpu/efi/payload.c | 8 ++++---- cmd/efi.c | 34 ++++++++++++++++++---------------- include/efi.h | 4 ++-- 3 files changed, 24 insertions(+), 22 deletions(-)
diff --git a/arch/x86/cpu/efi/payload.c b/arch/x86/cpu/efi/payload.c index 04ce1880b4d..b7778565b19 100644 --- a/arch/x86/cpu/efi/payload.c +++ b/arch/x86/cpu/efi/payload.c @@ -51,7 +51,7 @@ ulong board_get_usable_ram_top(ulong total_size)
end = (struct efi_mem_desc *)((ulong)map + size); desc = map->desc; - for (; desc < end; desc = efi_get_next_mem_desc(map, desc)) { + for (; desc < end; desc = efi_get_next_mem_desc(desc, map->desc_size)) { if (desc->type != EFI_CONVENTIONAL_MEMORY || desc->physical_start >= 1ULL << 32) continue; @@ -89,7 +89,7 @@ int dram_init(void) end = (struct efi_mem_desc *)((ulong)map + size); gd->ram_size = 0; desc = map->desc; - for (; desc < end; desc = efi_get_next_mem_desc(map, desc)) { + for (; desc < end; desc = efi_get_next_mem_desc(desc, map->desc_size)) { if (desc->type < EFI_MMAP_IO) gd->ram_size += desc->num_pages << EFI_PAGE_SHIFT; } @@ -114,7 +114,7 @@ int dram_init_banksize(void) desc = map->desc; for (num_banks = 0; desc < end && num_banks < CONFIG_NR_DRAM_BANKS; - desc = efi_get_next_mem_desc(map, desc)) { + desc = efi_get_next_mem_desc(desc, map->desc_size)) { /* * We only use conventional memory and ignore * anything less than 1MB. @@ -197,7 +197,7 @@ unsigned int install_e820_map(unsigned int max_entries,
end = (struct efi_mem_desc *)((ulong)map + size); for (desc = map->desc; desc < end; - desc = efi_get_next_mem_desc(map, desc)) { + desc = efi_get_next_mem_desc(desc, map->desc_size)) { if (desc->num_pages == 0) continue;
diff --git a/cmd/efi.c b/cmd/efi.c index f2ed26bd4b2..d2400acbbba 100644 --- a/cmd/efi.c +++ b/cmd/efi.c @@ -75,16 +75,17 @@ static int h_cmp_entry(const void *v1, const void *v2) /** * efi_build_mem_table() - make a sorted copy of the memory table * - * @map: Pointer to EFI memory map table + * @desc_base: Pointer to EFI memory map table * @size: Size of table in bytes + * @desc_size: Size of each @desc_base record * @skip_bs: True to skip boot-time memory and merge it with conventional * memory. This will significantly reduce the number of table * entries. * Return: pointer to the new table. It should be freed with free() by the * caller. */ -static void *efi_build_mem_table(struct efi_entry_memmap *map, int size, - bool skip_bs) +static void *efi_build_mem_table(struct efi_mem_desc *desc_base, int size, + int desc_size, bool skip_bs) { struct efi_mem_desc *desc, *end, *base, *dest, *prev; int count; @@ -95,15 +96,16 @@ static void *efi_build_mem_table(struct efi_entry_memmap *map, int size, debug("%s: Cannot allocate %#x bytes\n", __func__, size); return NULL; } - end = (struct efi_mem_desc *)((ulong)map + size); - count = ((ulong)end - (ulong)map->desc) / map->desc_size; - memcpy(base, map->desc, (ulong)end - (ulong)map->desc); - qsort(base, count, map->desc_size, h_cmp_entry); + end = (void *)desc_base + size; + count = ((ulong)end - (ulong)desc_base) / desc_size; + memcpy(base, desc_base, (ulong)end - (ulong)desc_base); + qsort(base, count, desc_size, h_cmp_entry); prev = NULL; addr = 0; dest = base; - end = (struct efi_mem_desc *)((ulong)base + count * map->desc_size); - for (desc = base; desc < end; desc = efi_get_next_mem_desc(map, desc)) { + end = (struct efi_mem_desc *)((ulong)base + count * desc_size); + for (desc = base; desc < end; + desc = efi_get_next_mem_desc(desc, desc_size)) { bool merge = true; u32 type = desc->type;
@@ -116,7 +118,7 @@ static void *efi_build_mem_table(struct efi_entry_memmap *map, int size, if (skip_bs && is_boot_services(desc->type)) type = EFI_CONVENTIONAL_MEMORY;
- memcpy(dest, desc, map->desc_size); + memcpy(dest, desc, desc_size); dest->type = type; if (!skip_bs || !prev) merge = false; @@ -131,7 +133,7 @@ static void *efi_build_mem_table(struct efi_entry_memmap *map, int size, prev->num_pages += desc->num_pages; } else { prev = dest; - dest = efi_get_next_mem_desc(map, dest); + dest = efi_get_next_mem_desc(dest, desc_size); } addr = desc->physical_start + (desc->num_pages << EFI_PAGE_SHIFT); @@ -143,8 +145,8 @@ static void *efi_build_mem_table(struct efi_entry_memmap *map, int size, return base; }
-static void efi_print_mem_table(struct efi_entry_memmap *map, - struct efi_mem_desc *desc, bool skip_bs) +static void efi_print_mem_table(struct efi_mem_desc *desc, int desc_size, + bool skip_bs) { u64 attr_seen[ATTR_SEEN_MAX]; int attr_seen_count; @@ -158,7 +160,7 @@ static void efi_print_mem_table(struct efi_entry_memmap *map, attr_seen_count = 0; addr = 0; for (upto = 0; desc->type != EFI_MAX_MEMORY_TYPE; - upto++, desc = efi_get_next_mem_desc(map, desc)) { + upto++, desc = efi_get_next_mem_desc(desc, desc_size)) { const char *name; u64 size;
@@ -238,13 +240,13 @@ static int do_efi_mem(struct cmd_tbl *cmdtp, int flag, int argc, goto done; }
- desc = efi_build_mem_table(map, size, skip_bs); + desc = efi_build_mem_table(map->desc, size, map->desc_size, skip_bs); if (!desc) { ret = -ENOMEM; goto done; }
- efi_print_mem_table(map, desc, skip_bs); + efi_print_mem_table(desc, map->desc_size, skip_bs); free(desc); done: if (ret) diff --git a/include/efi.h b/include/efi.h index dc9907fa16e..3508ff8f639 100644 --- a/include/efi.h +++ b/include/efi.h @@ -395,9 +395,9 @@ struct efi_entry_systable { };
static inline struct efi_mem_desc *efi_get_next_mem_desc( - struct efi_entry_memmap *map, struct efi_mem_desc *desc) + struct efi_mem_desc *desc, int desc_size) { - return (struct efi_mem_desc *)((ulong)desc + map->desc_size); + return (struct efi_mem_desc *)((ulong)desc + desc_size); }
/**

At present the 'efi' command only works in the EFI payload. Update it to work in the app too, so the memory map can be examined.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
cmd/Makefile | 2 +- cmd/efi.c | 48 ++++++++++++++++++++++++++++++++--------------- include/efi.h | 15 +++++++++++++++ lib/efi/efi_app.c | 33 ++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 16 deletions(-)
diff --git a/cmd/Makefile b/cmd/Makefile index 891819ae0f6..df50625bde7 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -58,7 +58,7 @@ obj-$(CONFIG_CMD_EXTENSION) += extension_board.o obj-$(CONFIG_CMD_ECHO) += echo.o obj-$(CONFIG_ENV_IS_IN_EEPROM) += eeprom.o obj-$(CONFIG_CMD_EEPROM) += eeprom.o -obj-$(CONFIG_EFI_STUB) += efi.o +obj-$(CONFIG_EFI) += efi.o obj-$(CONFIG_CMD_EFIDEBUG) += efidebug.o obj-$(CONFIG_CMD_ELF) += elf.o obj-$(CONFIG_HUSH_PARSER) += exit.o diff --git a/cmd/efi.c b/cmd/efi.c index d2400acbbba..c0384e0db28 100644 --- a/cmd/efi.c +++ b/cmd/efi.c @@ -13,6 +13,8 @@ #include <sort.h> #include <asm/global_data.h>
+DECLARE_GLOBAL_DATA_PTR; + static const char *const type_name[] = { "reserved", "loader_code", @@ -217,37 +219,53 @@ static void efi_print_mem_table(struct efi_mem_desc *desc, int desc_size, static int do_efi_mem(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { - struct efi_mem_desc *desc; - struct efi_entry_memmap *map; + struct efi_mem_desc *orig, *desc; + uint version, key; + int desc_size; int size, ret; bool skip_bs;
skip_bs = !argc || *argv[0] != 'a'; - ret = efi_info_get(EFIET_MEMORY_MAP, (void **)&map, &size); - switch (ret) { - case -ENOENT: - printf("No EFI table available\n"); - goto done; - case -EPROTONOSUPPORT: - printf("Incorrect EFI table version\n"); - goto done; + if (IS_ENABLED(CONFIG_EFI_APP)) { + ret = efi_get_mmap(&orig, &size, &key, &desc_size, &version); + if (ret) { + printf("Cannot read memory map (err=%d)\n", ret); + return CMD_RET_FAILURE; + } + } else { + struct efi_entry_memmap *map; + + ret = efi_info_get(EFIET_MEMORY_MAP, (void **)&map, &size); + switch (ret) { + case -ENOENT: + printf("No EFI table available\n"); + goto done; + case -EPROTONOSUPPORT: + printf("Incorrect EFI table version\n"); + goto done; + } + orig = map->desc; + desc_size = map->desc_size; + version = map->version; } - printf("EFI table at %lx, memory map %p, size %x, version %x, descr. size %#x\n", - gd->arch.table, map, size, map->version, map->desc_size); - if (map->version != EFI_MEM_DESC_VERSION) { + printf("EFI table at %lx, memory map %p, size %x, key %x, version %x, descr. size %#x\n", + gd->arch.table, orig, size, key, version, desc_size); + if (version != EFI_MEM_DESC_VERSION) { printf("Incorrect memory map version\n"); ret = -EPROTONOSUPPORT; goto done; }
- desc = efi_build_mem_table(map->desc, size, map->desc_size, skip_bs); + desc = efi_build_mem_table(orig, size, desc_size, skip_bs); if (!desc) { ret = -ENOMEM; goto done; }
- efi_print_mem_table(desc, map->desc_size, skip_bs); + efi_print_mem_table(desc, desc_size, skip_bs); free(desc); + if (IS_ENABLED(CONFIG_EFI_APP)) + free(orig); done: if (ret) printf("Error: %d\n", ret); diff --git a/include/efi.h b/include/efi.h index 3508ff8f639..9b7ba0d54d3 100644 --- a/include/efi.h +++ b/include/efi.h @@ -610,4 +610,19 @@ int efi_store_memory_map(struct efi_priv *priv); */ int efi_call_exit_boot_services(void);
+/** + * efi_get_mmap() - Get the memory map from EFI + * + * This is used in the app. The caller must free *@descp when done + * + * @descp: Returns allocated pointer to EFI memory map table + * @sizep: Returns size of table in bytes + * @keyp: Returns memory-map key + * @desc_sizep: Returns size of each @desc_base record + * @versionp: Returns version number of memory map + * @return 0 on success, -ve on error + */ +int efi_get_mmap(struct efi_mem_desc **descp, int *sizep, uint *keyp, + int *desc_sizep, uint *versionp); + #endif /* _LINUX_EFI_H */ diff --git a/lib/efi/efi_app.c b/lib/efi/efi_app.c index 5c2593bc4d9..6980933d7ea 100644 --- a/lib/efi/efi_app.c +++ b/lib/efi/efi_app.c @@ -32,6 +32,39 @@ int efi_info_get(enum efi_entry_t type, void **datap, int *sizep) return -ENOSYS; }
+int efi_get_mmap(struct efi_mem_desc **descp, int *sizep, uint *keyp, + int *desc_sizep, uint *versionp) +{ + struct efi_priv *priv = efi_get_priv(); + struct efi_boot_services *boot = priv->sys_table->boottime; + efi_uintn_t size, desc_size, key; + struct efi_mem_desc *desc; + efi_status_t ret; + u32 version; + + /* Get the memory map so we can switch off EFI */ + size = 0; + ret = boot->get_memory_map(&size, NULL, &key, &desc_size, &version); + if (ret != EFI_BUFFER_TOO_SMALL) + return log_msg_ret("get", -ENOMEM); + + desc = malloc(size); + if (!desc) + return log_msg_ret("mem", -ENOMEM); + + ret = boot->get_memory_map(&size, desc, &key, &desc_size, &version); + if (ret) + return log_msg_ret("get", -EINVAL); + + *descp = desc; + *sizep = size; + *desc_sizep = desc_size; + *versionp = version; + *keyp = key; + + return 0; +} + /** * efi_bind_block() - bind a new block device to an EFI device *

Add an empty CPU init function to avoid fiddling with low-level CPU features in the app. Set up the C runtime correctly for 64-bit use and avoid clearing BSS, since this is done by EFI when U-Boot is loaded.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v5)
Changes in v5: - Add new patch to tweak the code used for the 64-bit EFI app
arch/x86/cpu/x86_64/cpu.c | 5 +++++ arch/x86/lib/Makefile | 5 ++--- arch/x86/lib/relocate.c | 2 ++ 3 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/arch/x86/cpu/x86_64/cpu.c b/arch/x86/cpu/x86_64/cpu.c index a3674e8e29a..6a387612916 100644 --- a/arch/x86/cpu/x86_64/cpu.c +++ b/arch/x86/cpu/x86_64/cpu.c @@ -45,3 +45,8 @@ int cpu_phys_address_size(void) { return CONFIG_CPU_ADDR_BITS; } + +int x86_cpu_init_f(void) +{ + return 0; +} diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile index 18757b29aa9..e5235b7c4f4 100644 --- a/arch/x86/lib/Makefile +++ b/arch/x86/lib/Makefile @@ -65,9 +65,8 @@ endif
lib-$(CONFIG_USE_PRIVATE_LIBGCC) += div64.o
-ifeq ($(CONFIG_$(SPL_)X86_64),) -obj-$(CONFIG_EFI_APP) += crt0_ia32_efi.o reloc_ia32_efi.o -endif +obj-$(CONFIG_EFI_APP_32BIT) += crt0_ia32_efi.o reloc_ia32_efi.o +obj-$(CONFIG_EFI_APP_64BIT) += crt0_x86_64_efi.o reloc_x86_64_efi.o
ifneq ($(CONFIG_EFI_STUB),)
diff --git a/arch/x86/lib/relocate.c b/arch/x86/lib/relocate.c index 6fe51516477..9060d19d46a 100644 --- a/arch/x86/lib/relocate.c +++ b/arch/x86/lib/relocate.c @@ -35,6 +35,7 @@ int copy_uboot_to_ram(void) return 0; }
+#ifndef CONFIG_EFI_APP int clear_bss(void) { ulong dst_addr = (ulong)&__bss_start + gd->reloc_off; @@ -46,6 +47,7 @@ int clear_bss(void)
return 0; } +#endif
#if CONFIG_IS_ENABLED(X86_64) static void do_elf_reloc_fixups64(unsigned int text_base, uintptr_t size,

Make sure the linker lists are in the right place and drop the eh_frame section, which is not needed.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v5)
Changes in v5: - Add new patch to round out the link script for 64-bit EFI
arch/x86/lib/elf_x86_64_efi.lds | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/arch/x86/lib/elf_x86_64_efi.lds b/arch/x86/lib/elf_x86_64_efi.lds index b436429b33e..75727400aa4 100644 --- a/arch/x86/lib/elf_x86_64_efi.lds +++ b/arch/x86/lib/elf_x86_64_efi.lds @@ -63,6 +63,7 @@ SECTIONS *(.rela.data*) *(.rela.got) *(.rela.stab) + *(.rela.u_boot_list*) }
. = ALIGN(4096); @@ -70,9 +71,11 @@ SECTIONS . = ALIGN(4096); .dynstr : { *(.dynstr) } . = ALIGN(4096); + + /DISCARD/ : { *(.eh_frame) } + .ignored.reloc : { *(.rela.reloc) - *(.eh_frame) *(.note.GNU-stack) }

That script is not intended for use with EFI, so update the logic to avoid using it.
Signed-off-by: Simon Glass sjg@chromium.org Signed-off-by: Christian Melki christian.melki@t2data.com ---
(no changes since v5)
Changes in v5: - Add new patch to avoid using the 64-bit link script for the EFI app
arch/x86/cpu/config.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/x86/cpu/config.mk b/arch/x86/cpu/config.mk index d3033b41603..87e242a2065 100644 --- a/arch/x86/cpu/config.mk +++ b/arch/x86/cpu/config.mk @@ -9,7 +9,7 @@ LDPPFLAGS += -DRESET_VEC_LOC=$(CONFIG_RESET_VEC_LOC) LDPPFLAGS += -DSTART_16=$(CONFIG_SYS_X86_START16)
ifdef CONFIG_X86_64 -ifndef CONFIG_SPL_BUILD +ifeq ($(CONFIG_SPL_BUILD)$(CONFIG_EFI_APP),) LDSCRIPT = $(srctree)/arch/x86/cpu/u-boot-64.lds endif endif

At present some 32-bit settings are used with the 64-bit app. Fix this by separating out the two cases.
Be careful not to break the 64-bit payload, which needs to build a 64-bit EFI stub with a 32-bit U-Boot.
Signed-off-by: Christian Melki christian.melki@t2data.com Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v5)
Changes in v5: - Add new patch to set the correct link flags for the 64-bit EFI app
arch/x86/config.mk | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/arch/x86/config.mk b/arch/x86/config.mk index 589f2aed2bc..889497b6bd7 100644 --- a/arch/x86/config.mk +++ b/arch/x86/config.mk @@ -20,6 +20,11 @@ IS_32BIT := y endif endif
+EFI_IS_32BIT := $(IS_32BIT) +ifdef CONFIG_EFI_STUB_64BIT +EFI_IS_32BIT := +endif + ifeq ($(IS_32BIT),y) PLATFORM_CPPFLAGS += -march=i386 -m32 else @@ -44,8 +49,14 @@ CFLAGS_EFI := -fpic -fshort-wchar # Compiler flags to be removed when building UEFI applications CFLAGS_NON_EFI := -mregparm=3 -fstack-protector-strong
-ifeq ($(CONFIG_EFI_STUB_64BIT),) +ifeq ($(IS_32BIT),y) +EFIPAYLOAD_BFDARCH = i386 +else CFLAGS_EFI += $(call cc-option, -mno-red-zone) +EFIPAYLOAD_BFDARCH = x86_64 +endif + +ifeq ($(EFI_IS_32BIT),y) EFIARCH = ia32 EFIPAYLOAD_BFDTARGET = elf32-i386 else @@ -53,8 +64,6 @@ EFIARCH = x86_64 EFIPAYLOAD_BFDTARGET = elf64-x86-64 endif
-EFIPAYLOAD_BFDARCH = i386 - LDSCRIPT_EFI := $(srctree)/arch/x86/lib/elf_$(EFIARCH)_efi.lds EFISTUB := crt0_$(EFIARCH)_efi.o reloc_$(EFIARCH)_efi.o OBJCOPYFLAGS_EFI += --target=efi-app-$(EFIARCH)

Now that the linker crash is resolved, build the 64-bit EFI app, including all the required code.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v9: - Drop changes previously applied
Changes in v7: - Rebase on -master instead of -next
Changes in v5: - Add new patch to build the 64-bit app properly - Add various patches to fix up the 64-bit app so that it boots
Changes in v2: - Add new patch to support the efi command in the app
Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Makefile b/Makefile index 074ba7349f2..1ba168aeb2d 100644 --- a/Makefile +++ b/Makefile @@ -1776,9 +1776,9 @@ else quiet_cmd_u-boot__ ?= LD $@ cmd_u-boot__ ?= $(LD) $(KBUILD_LDFLAGS) $(LDFLAGS_u-boot) -o $@ \ -T u-boot.lds $(u-boot-init) \ - $(if $(CONFIG_EFI_APP_64BIT),,--whole-archive) \ + --whole-archive \ $(u-boot-main) \ - $(if $(CONFIG_EFI_APP_64BIT),,--no-whole-archive) \ + --no-whole-archive \ $(PLATFORM_LIBS) -Map u-boot.map; \ $(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true) endif

Document the return value in efi_init(). Fix up @sizep in efi_info_get(). Use Return: instead of @return
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v8)
Changes in v8: - Add new patch to tidy up header comments
include/efi.h | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/include/efi.h b/include/efi.h index 9b7ba0d54d3..6159f34ad2b 100644 --- a/include/efi.h +++ b/include/efi.h @@ -510,14 +510,14 @@ void efi_set_priv(struct efi_priv *priv); /** * efi_get_sys_table() - Get access to the main EFI system table * - * @return pointer to EFI system table + * Returns: pointer to EFI system table */ struct efi_system_table *efi_get_sys_table(void);
/** * efi_get_boot() - Get access to the EFI boot services table * - * @return pointer to EFI boot services table + * Returns: pointer to EFI boot services table */ struct efi_boot_services *efi_get_boot(void);
@@ -526,7 +526,7 @@ struct efi_boot_services *efi_get_boot(void); * * This is used when U-Boot is built as an EFI application. * - * @return the base of RAM as known to U-Boot + * Returns: the base of RAM as known to U-Boot */ unsigned long efi_get_ram_base(void);
@@ -537,6 +537,7 @@ unsigned long efi_get_ram_base(void); * @banner: Banner to display when starting * @image: The image handle passed to efi_main() * @sys_table: The EFI system table pointer passed to efi_main() + * Return: 0 on succcess, EFI error code on failure */ int efi_init(struct efi_priv *priv, const char *banner, efi_handle_t image, struct efi_system_table *sys_table); @@ -547,7 +548,7 @@ int efi_init(struct efi_priv *priv, const char *banner, efi_handle_t image, * @priv: Pointer to private EFI structure * @size: Number of bytes to allocate * @retp: Return EFI status result - * @return pointer to memory allocated, or NULL on error + * Returns: pointer to memory allocated, or NULL on error */ void *efi_malloc(struct efi_priv *priv, int size, efi_status_t *retp);
@@ -584,8 +585,8 @@ void efi_putc(struct efi_priv *priv, const char ch); * * @type: Entry type to search for * @datap: Returns pointer to entry data - * @sizep: Returns pointer to entry size - * @return 0 if OK, -ENODATA if there is no table, -ENOENT if there is no entry + * @sizep: Returns entry size + * Return: 0 if OK, -ENODATA if there is no table, -ENOENT if there is no entry * of the requested type, -EPROTONOSUPPORT if the table has the wrong version */ int efi_info_get(enum efi_entry_t type, void **datap, int *sizep); @@ -597,7 +598,7 @@ int efi_info_get(enum efi_entry_t type, void **datap, int *sizep); * exit_boot_services() * * @priv: Pointer to private EFI structure - * @return 0 if OK, non-zero on error + * Returns: 0 if OK, non-zero on error */ int efi_store_memory_map(struct efi_priv *priv);
@@ -620,7 +621,7 @@ int efi_call_exit_boot_services(void); * @keyp: Returns memory-map key * @desc_sizep: Returns size of each @desc_base record * @versionp: Returns version number of memory map - * @return 0 on success, -ve on error + * Returns: 0 on success, -ve on error */ int efi_get_mmap(struct efi_mem_desc **descp, int *sizep, uint *keyp, int *desc_sizep, uint *versionp);

On 1/4/22 11:51, Simon Glass wrote:
Document the return value in efi_init(). Fix up @sizep in efi_info_get(). Use Return: instead of @return
Signed-off-by: Simon Glasssjg@chromium.org
Reviewed-by: Heinrich Schuchardt xypron.glpk@gmx.de

Hi Heinrich,
On Tue, 4 Jan 2022 at 03:51, Simon Glass sjg@chromium.org wrote:
At present U-Boot can be built as an EFI app, but it is really just for testing, with very few features. Instead, the payload build is used for booting on top of UEFI, where U-Boot takes over the machine immediately and supplies its own drivers.
But the app could be made more useful.
This series provides access to EFI block devices and the video console within U-Boot. It also restructures the app to make it possible to boot a kernel, although further work is needed to implement this.
This can be thought of as making U-Boot perform a little like 'grub', in that it runs purely based on UEFI-provided services.
Of course a lot more work is required, but this is a start.
Note: It would be very useful to include qemu tests of the app and stub in CI.
This is available at u-boot-dm/efi-working
Changes in v9:
- Add a comment as to why printf() cannot be used
- Comment out storing the EFI memory map in the app and update commit message
- Drop changes previously applied
Changes in v8:
- Add new patch to tidy up header comments
Changes in v7:
- Rebase on -master instead of -next
Changes in v6:
- Fix typo in function comment
Changes in v5:
- Add new patch to avoid using the 64-bit link script for the EFI app
- Add new patch to build the 64-bit app properly
- Add new patch to round out the link script for 64-bit EFI
- Add new patch to set the correct link flags for the 64-bit EFI app
- Add new patch to tweak the code used for the 64-bit EFI app
- Add various patches to fix up the 64-bit app so that it boots
Changes in v2:
- Add a sentence about what the patch does
- Add new patch to support the efi command in the app
Simon Glass (9): efi: Move exit_boot_services into a function x86: efi: Update efi_get_next_mem_desc() to avoid needing a map efi: Support the efi command in the app x86: efi: Tweak the code used for the 64-bit EFI app x86: efi: Round out the link script for 64-bit EFI x86: efi: Don't use the 64-bit link script for the EFI app x86: efi: Set the correct link flags for the 64-bit EFI app efi: Build the 64-bit app properly efi: Tidy up some comments in efi header
Makefile | 4 +- arch/x86/config.mk | 15 +++++-- arch/x86/cpu/config.mk | 2 +- arch/x86/cpu/efi/payload.c | 8 ++-- arch/x86/cpu/x86_64/cpu.c | 5 +++ arch/x86/lib/Makefile | 5 +-- arch/x86/lib/elf_x86_64_efi.lds | 5 ++- arch/x86/lib/relocate.c | 2 + cmd/Makefile | 2 +- cmd/efi.c | 78 +++++++++++++++++++++------------ include/efi.h | 64 +++++++++++++++++++++++---- lib/efi/efi.c | 72 ++++++++++++++++++++++++++++++ lib/efi/efi_app.c | 42 ++++++++++++++++++ lib/efi/efi_stub.c | 66 +++++----------------------- 14 files changed, 263 insertions(+), 107 deletions(-)
-- 2.34.1.448.ga2b2bfdf31-goog
Does this series look OK to you now? I'd like to put it to bed.
Regards, Simon

Am 13. Januar 2022 20:39:15 MEZ schrieb Simon Glass sjg@chromium.org:
Hi Heinrich,
On Tue, 4 Jan 2022 at 03:51, Simon Glass sjg@chromium.org wrote:
At present U-Boot can be built as an EFI app, but it is really just for testing, with very few features. Instead, the payload build is used for booting on top of UEFI, where U-Boot takes over the machine immediately and supplies its own drivers.
But the app could be made more useful.
This series provides access to EFI block devices and the video console within U-Boot. It also restructures the app to make it possible to boot a kernel, although further work is needed to implement this.
This can be thought of as making U-Boot perform a little like 'grub', in that it runs purely based on UEFI-provided services.
Of course a lot more work is required, but this is a start.
Note: It would be very useful to include qemu tests of the app and stub in CI.
This is available at u-boot-dm/efi-working
Changes in v9:
- Add a comment as to why printf() cannot be used
- Comment out storing the EFI memory map in the app and update commit message
- Drop changes previously applied
Changes in v8:
- Add new patch to tidy up header comments
Changes in v7:
- Rebase on -master instead of -next
Changes in v6:
- Fix typo in function comment
Changes in v5:
- Add new patch to avoid using the 64-bit link script for the EFI app
- Add new patch to build the 64-bit app properly
- Add new patch to round out the link script for 64-bit EFI
- Add new patch to set the correct link flags for the 64-bit EFI app
- Add new patch to tweak the code used for the 64-bit EFI app
- Add various patches to fix up the 64-bit app so that it boots
Changes in v2:
- Add a sentence about what the patch does
- Add new patch to support the efi command in the app
Simon Glass (9): efi: Move exit_boot_services into a function x86: efi: Update efi_get_next_mem_desc() to avoid needing a map efi: Support the efi command in the app x86: efi: Tweak the code used for the 64-bit EFI app x86: efi: Round out the link script for 64-bit EFI x86: efi: Don't use the 64-bit link script for the EFI app x86: efi: Set the correct link flags for the 64-bit EFI app efi: Build the 64-bit app properly efi: Tidy up some comments in efi header
Makefile | 4 +- arch/x86/config.mk | 15 +++++-- arch/x86/cpu/config.mk | 2 +- arch/x86/cpu/efi/payload.c | 8 ++-- arch/x86/cpu/x86_64/cpu.c | 5 +++ arch/x86/lib/Makefile | 5 +-- arch/x86/lib/elf_x86_64_efi.lds | 5 ++- arch/x86/lib/relocate.c | 2 + cmd/Makefile | 2 +- cmd/efi.c | 78 +++++++++++++++++++++------------ include/efi.h | 64 +++++++++++++++++++++++---- lib/efi/efi.c | 72 ++++++++++++++++++++++++++++++ lib/efi/efi_app.c | 42 ++++++++++++++++++ lib/efi/efi_stub.c | 66 +++++----------------------- 14 files changed, 263 insertions(+), 107 deletions(-)
-- 2.34.1.448.ga2b2bfdf31-goog
Does this series look OK to you now? I'd like to put it to bed.
I just need to find time to create the next pull request.
Best regards
Heinrich
Regards, Simon

Hi Heinrich,
On Thu, 13 Jan 2022 at 14:08, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
Am 13. Januar 2022 20:39:15 MEZ schrieb Simon Glass sjg@chromium.org:
Hi Heinrich,
On Tue, 4 Jan 2022 at 03:51, Simon Glass sjg@chromium.org wrote:
At present U-Boot can be built as an EFI app, but it is really just for testing, with very few features. Instead, the payload build is used for booting on top of UEFI, where U-Boot takes over the machine immediately and supplies its own drivers.
But the app could be made more useful.
This series provides access to EFI block devices and the video console within U-Boot. It also restructures the app to make it possible to boot a kernel, although further work is needed to implement this.
This can be thought of as making U-Boot perform a little like 'grub', in that it runs purely based on UEFI-provided services.
Of course a lot more work is required, but this is a start.
Note: It would be very useful to include qemu tests of the app and stub in CI.
This is available at u-boot-dm/efi-working
Changes in v9:
- Add a comment as to why printf() cannot be used
- Comment out storing the EFI memory map in the app and update commit message
- Drop changes previously applied
Changes in v8:
- Add new patch to tidy up header comments
Changes in v7:
- Rebase on -master instead of -next
Changes in v6:
- Fix typo in function comment
Changes in v5:
- Add new patch to avoid using the 64-bit link script for the EFI app
- Add new patch to build the 64-bit app properly
- Add new patch to round out the link script for 64-bit EFI
- Add new patch to set the correct link flags for the 64-bit EFI app
- Add new patch to tweak the code used for the 64-bit EFI app
- Add various patches to fix up the 64-bit app so that it boots
Changes in v2:
- Add a sentence about what the patch does
- Add new patch to support the efi command in the app
Simon Glass (9): efi: Move exit_boot_services into a function x86: efi: Update efi_get_next_mem_desc() to avoid needing a map efi: Support the efi command in the app x86: efi: Tweak the code used for the 64-bit EFI app x86: efi: Round out the link script for 64-bit EFI x86: efi: Don't use the 64-bit link script for the EFI app x86: efi: Set the correct link flags for the 64-bit EFI app efi: Build the 64-bit app properly efi: Tidy up some comments in efi header
Makefile | 4 +- arch/x86/config.mk | 15 +++++-- arch/x86/cpu/config.mk | 2 +- arch/x86/cpu/efi/payload.c | 8 ++-- arch/x86/cpu/x86_64/cpu.c | 5 +++ arch/x86/lib/Makefile | 5 +-- arch/x86/lib/elf_x86_64_efi.lds | 5 ++- arch/x86/lib/relocate.c | 2 + cmd/Makefile | 2 +- cmd/efi.c | 78 +++++++++++++++++++++------------ include/efi.h | 64 +++++++++++++++++++++++---- lib/efi/efi.c | 72 ++++++++++++++++++++++++++++++ lib/efi/efi_app.c | 42 ++++++++++++++++++ lib/efi/efi_stub.c | 66 +++++----------------------- 14 files changed, 263 insertions(+), 107 deletions(-)
-- 2.34.1.448.ga2b2bfdf31-goog
Does this series look OK to you now? I'd like to put it to bed.
I just need to find time to create the next pull request.
OK thanks.
Regards, SImon
participants (2)
-
Heinrich Schuchardt
-
Simon Glass