diff options
Diffstat (limited to 'lib/efi_loader/efi_bootmgr.c')
-rw-r--r-- | lib/efi_loader/efi_bootmgr.c | 257 |
1 files changed, 181 insertions, 76 deletions
diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c index 4ac519228a6..c0df5cb9acd 100644 --- a/lib/efi_loader/efi_bootmgr.c +++ b/lib/efi_loader/efi_bootmgr.c @@ -11,13 +11,15 @@ #include <blkmap.h> #include <charset.h> #include <dm.h> +#include <efi.h> #include <log.h> #include <malloc.h> #include <net.h> -#include <efi_default_filename.h> #include <efi_loader.h> #include <efi_variable.h> #include <asm/unaligned.h> +#include <linux/kernel.h> +#include <linux/sizes.h> static const struct efi_boot_services *bs; static const struct efi_runtime_services *rs; @@ -82,8 +84,12 @@ struct efi_device_path *expand_media_path(struct efi_device_path *device_path) &efi_simple_file_system_protocol_guid, &rem); if (handle) { if (rem->type == DEVICE_PATH_TYPE_END) { - full_path = efi_dp_from_file(device_path, - "/EFI/BOOT/" BOOTEFI_NAME); + char fname[30]; + + snprintf(fname, sizeof(fname), "/EFI/BOOT/%s", + efi_get_basename()); + full_path = efi_dp_from_file(device_path, fname); + } else { full_path = efi_dp_dup(device_path); } @@ -130,7 +136,7 @@ static efi_status_t try_load_from_file_path(efi_handle_t *fs_handles, if (!dp) continue; - dp = efi_dp_concat(dp, fp, false); + dp = efi_dp_concat(dp, fp, 0); if (!dp) continue; @@ -344,6 +350,7 @@ static efi_status_t prepare_loaded_image(u16 *label, ulong addr, ulong size, struct efi_device_path **dp, struct udevice **blk) { + u64 pages; efi_status_t ret; struct udevice *ramdisk_blk; @@ -358,13 +365,18 @@ static efi_status_t prepare_loaded_image(u16 *label, ulong addr, ulong size, } /* - * TODO: expose the ramdisk to OS. - * Need to pass the ramdisk information by the architecture-specific - * methods such as 'pmem' device-tree node. + * Linux supports 'pmem' which allows OS installers to find, reclaim + * the mounted images and continue the installation since the contents + * of the pmem region are treated as local media. + * + * The memory regions used for it needs to be carved out of the EFI + * memory map. */ - ret = efi_add_memory_map(addr, size, EFI_RESERVED_MEMORY_TYPE); + pages = efi_size_in_pages(size + (addr & EFI_PAGE_MASK)); + ret = efi_update_memory_map(addr, pages, EFI_CONVENTIONAL_MEMORY, + false, true); if (ret != EFI_SUCCESS) { - log_err("Memory reservation failed\n"); + log_err("Failed to reserve memory\n"); goto err; } @@ -380,14 +392,15 @@ err: } /** - * efi_bootmgr_release_uridp_resource() - cleanup uri device path resource + * efi_bootmgr_release_uridp() - cleanup uri device path resource * * @ctx: event context * Return: status code */ -efi_status_t efi_bootmgr_release_uridp_resource(struct uridp_context *ctx) +static efi_status_t efi_bootmgr_release_uridp(struct uridp_context *ctx) { efi_status_t ret = EFI_SUCCESS; + efi_status_t ret2 = EFI_SUCCESS; if (!ctx) return ret; @@ -407,32 +420,33 @@ efi_status_t efi_bootmgr_release_uridp_resource(struct uridp_context *ctx) /* cleanup for PE-COFF image */ if (ctx->mem_handle) { - ret = efi_uninstall_multiple_protocol_interfaces( - ctx->mem_handle, &efi_guid_device_path, ctx->loaded_dp, - NULL); - if (ret != EFI_SUCCESS) + ret2 = efi_uninstall_multiple_protocol_interfaces(ctx->mem_handle, + &efi_guid_device_path, + ctx->loaded_dp, + NULL); + if (ret2 != EFI_SUCCESS) log_err("Uninstall device_path protocol failed\n"); } efi_free_pool(ctx->loaded_dp); free(ctx); - return ret; + return ret == EFI_SUCCESS ? ret2 : ret; } /** - * efi_bootmgr_image_return_notify() - return to efibootmgr callback + * efi_bootmgr_http_return() - return to efibootmgr callback * * @event: the event for which this notification function is registered * @context: event context */ -static void EFIAPI efi_bootmgr_image_return_notify(struct efi_event *event, - void *context) +static void EFIAPI efi_bootmgr_http_return(struct efi_event *event, + void *context) { efi_status_t ret; EFI_ENTRY("%p, %p", event, context); - ret = efi_bootmgr_release_uridp_resource(context); + ret = efi_bootmgr_release_uridp(context); EFI_EXIT(ret); } @@ -473,7 +487,7 @@ static efi_status_t try_load_from_uri_path(struct efi_device_path_uri *uridp, } image_addr = hextoul(s, NULL); - err = wget_with_dns(image_addr, uridp->uri); + err = wget_do_request(image_addr, uridp->uri); if (err < 0) { ret = EFI_INVALID_PARAMETER; goto err; @@ -484,6 +498,13 @@ static efi_status_t try_load_from_uri_path(struct efi_device_path_uri *uridp, ret = EFI_INVALID_PARAMETER; goto err; } + /* + * Depending on the kernel configuration, pmem memory areas must be + * page aligned or 2MiB aligned. PowerPC is an exception here and + * requires 16MiB alignment, but since we don't have EFI support for + * it, limit the alignment to 2MiB. + */ + image_size = ALIGN(image_size, SZ_2M); /* * If the file extension is ".iso" or ".img", mount it and try to load @@ -533,7 +554,7 @@ static efi_status_t try_load_from_uri_path(struct efi_device_path_uri *uridp, /* create event for cleanup when the image returns or error occurs */ ret = efi_create_event(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, - efi_bootmgr_image_return_notify, ctx, + efi_bootmgr_http_return, ctx, &efi_guid_event_group_return_to_efibootmgr, &event); if (ret != EFI_SUCCESS) { @@ -544,7 +565,7 @@ static efi_status_t try_load_from_uri_path(struct efi_device_path_uri *uridp, return ret; err: - efi_bootmgr_release_uridp_resource(ctx); + efi_bootmgr_release_uridp(ctx); return ret; } @@ -613,9 +634,12 @@ static efi_status_t try_load_entry(u16 n, efi_handle_t *handle, void *load_option; efi_uintn_t size; efi_status_t ret; + u32 attributes; - efi_create_indexed_name(varname, sizeof(varname), "Boot", n); + *handle = NULL; + *load_options = NULL; + efi_create_indexed_name(varname, sizeof(varname), "Boot", n); load_option = efi_get_var(varname, &efi_global_variable_guid, &size); if (!load_option) return EFI_LOAD_ERROR; @@ -626,55 +650,54 @@ static efi_status_t try_load_entry(u16 n, efi_handle_t *handle, goto error; } - if (lo.attributes & LOAD_OPTION_ACTIVE) { - u32 attributes; - - log_debug("trying to load \"%ls\" from %pD\n", lo.label, - lo.file_path); - - if (EFI_DP_TYPE(lo.file_path, MEDIA_DEVICE, FILE_PATH)) { - /* file_path doesn't contain a device path */ - ret = try_load_from_short_path(lo.file_path, handle); - } else if (EFI_DP_TYPE(lo.file_path, MESSAGING_DEVICE, MSG_URI)) { - if (IS_ENABLED(CONFIG_EFI_HTTP_BOOT)) - ret = try_load_from_uri_path( - (struct efi_device_path_uri *)lo.file_path, - lo.label, handle); - else - ret = EFI_LOAD_ERROR; - } else { - ret = try_load_from_media(lo.file_path, handle); - } - if (ret != EFI_SUCCESS) { - log_warning("Loading %ls '%ls' failed\n", - varname, lo.label); - goto error; - } + if (!(lo.attributes & LOAD_OPTION_ACTIVE)) { + ret = EFI_LOAD_ERROR; + goto error; + } - attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS | - EFI_VARIABLE_RUNTIME_ACCESS; - ret = efi_set_variable_int(u"BootCurrent", - &efi_global_variable_guid, - attributes, sizeof(n), &n, false); - if (ret != EFI_SUCCESS) - goto unload; - /* try to register load file2 for initrd's */ - if (IS_ENABLED(CONFIG_EFI_LOAD_FILE2_INITRD)) { - ret = efi_initrd_register(); - if (ret != EFI_SUCCESS) - goto unload; - } + log_debug("trying to load \"%ls\" from %pD\n", lo.label, lo.file_path); - log_info("Booting: %ls\n", lo.label); + if (EFI_DP_TYPE(lo.file_path, MEDIA_DEVICE, FILE_PATH)) { + /* file_path doesn't contain a device path */ + ret = try_load_from_short_path(lo.file_path, handle); + } else if (EFI_DP_TYPE(lo.file_path, MESSAGING_DEVICE, MSG_URI)) { + if (IS_ENABLED(CONFIG_EFI_HTTP_BOOT)) + ret = try_load_from_uri_path( + (struct efi_device_path_uri *)lo.file_path, + lo.label, handle); + else + ret = EFI_LOAD_ERROR; } else { - ret = EFI_LOAD_ERROR; + ret = try_load_from_media(lo.file_path, handle); } + if (ret != EFI_SUCCESS) { + log_warning("Loading %ls '%ls' failed\n", + varname, lo.label); + goto error; + } + + attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS; + ret = efi_set_variable_int(u"BootCurrent", &efi_global_variable_guid, + attributes, sizeof(n), &n, false); + if (ret != EFI_SUCCESS) + goto error; - /* Set load options */ + /* try to register load file2 for initrd's */ + if (IS_ENABLED(CONFIG_EFI_LOAD_FILE2_INITRD)) { + ret = efi_initrd_register(NULL); + if (ret != EFI_SUCCESS) + goto error; + } + + log_info("Booting: Label: %ls Device path: %pD\n", lo.label, lo.file_path); + + /* Ignore the optional data in auto-generated boot options */ if (size >= sizeof(efi_guid_t) && !guidcmp(lo.optional_data, &efi_guid_bootmenu_auto_generated)) size = 0; + /* Set optional data in loaded file protocol */ if (size) { *load_options = malloc(size); if (!*load_options) { @@ -683,18 +706,15 @@ static efi_status_t try_load_entry(u16 n, efi_handle_t *handle, } memcpy(*load_options, lo.optional_data, size); ret = efi_set_load_options(*handle, size, *load_options); - } else { - *load_options = NULL; + if (ret != EFI_SUCCESS) + free(load_options); } error: - free(load_option); - - return ret; - -unload: - if (EFI_CALL(efi_unload_image(*handle)) != EFI_SUCCESS) + if (ret != EFI_SUCCESS && *handle && + EFI_CALL(efi_unload_image(*handle)) != EFI_SUCCESS) log_err("Unloading image failed\n"); + free(load_option); return ret; @@ -1187,6 +1207,59 @@ out: } /** + * load_fdt_from_load_option - load device-tree from load option + * + * @fdt: pointer to loaded device-tree or NULL + * Return: status code + */ +static efi_status_t load_fdt_from_load_option(void **fdt) +{ + struct efi_device_path *dp = NULL; + struct efi_file_handle *f = NULL; + efi_uintn_t filesize; + efi_status_t ret; + + *fdt = NULL; + + dp = efi_get_dp_from_boot(&efi_guid_fdt); + if (!dp) + return EFI_SUCCESS; + + /* Open file */ + f = efi_file_from_path(dp); + if (!f) { + log_err("Can't find %pD specified in Boot####\n", dp); + ret = EFI_NOT_FOUND; + goto out; + } + + /* Get file size */ + ret = efi_file_size(f, &filesize); + if (ret != EFI_SUCCESS) + goto out; + + *fdt = calloc(1, filesize); + if (!*fdt) { + log_err("Out of memory\n"); + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + ret = EFI_CALL(f->read(f, &filesize, *fdt)); + if (ret != EFI_SUCCESS) { + log_err("Can't read fdt\n"); + free(*fdt); + *fdt = NULL; + } + +out: + efi_free_pool(dp); + if (f) + EFI_CALL(f->close(f)); + + return ret; +} + +/** * efi_bootmgr_run() - execute EFI boot manager * @fdt: Flat device tree * @@ -1201,6 +1274,8 @@ efi_status_t efi_bootmgr_run(void *fdt) efi_handle_t handle; void *load_options; efi_status_t ret; + void *fdt_lo, *fdt_distro = NULL; + efi_uintn_t fdt_size; /* Initialize EFI drivers */ ret = efi_init_obj_list(); @@ -1210,15 +1285,45 @@ efi_status_t efi_bootmgr_run(void *fdt) return CMD_RET_FAILURE; } - ret = efi_install_fdt(fdt); - if (ret != EFI_SUCCESS) - return ret; - ret = efi_bootmgr_load(&handle, &load_options); if (ret != EFI_SUCCESS) { log_notice("EFI boot manager: Cannot load any image\n"); return ret; } + if (!IS_ENABLED(CONFIG_GENERATE_ACPI_TABLE)) { + ret = load_fdt_from_load_option(&fdt_lo); + if (ret != EFI_SUCCESS) + return ret; + if (fdt_lo) + fdt = fdt_lo; + if (!fdt) { + efi_load_distro_fdt(handle, &fdt_distro, &fdt_size); + fdt = fdt_distro; + } + } + + /* + * Needed in ACPI case to create reservations based on + * control device-tree. + */ + ret = efi_install_fdt(fdt); + + if (!IS_ENABLED(CONFIG_GENERATE_ACPI_TABLE)) { + free(fdt_lo); + if (fdt_distro) + efi_free_pages((uintptr_t)fdt_distro, + efi_size_in_pages(fdt_size)); + } + + if (ret != EFI_SUCCESS) { + if (EFI_CALL(efi_unload_image(handle)) == EFI_SUCCESS) + free(load_options); + else + log_err("Unloading image failed\n"); + + return ret; + } + return do_bootefi_exec(handle, load_options); } |