summaryrefslogtreecommitdiff
path: root/lib/efi_loader/efi_bootmgr.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/efi_loader/efi_bootmgr.c')
-rw-r--r--lib/efi_loader/efi_bootmgr.c257
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);
}