u-boot/lib/efi_loader/efi_bootbin.c
Simon Glass a2338955fc efi_loader: Pass in the required parameters from EFI bootmeth
Rather than setting up the global variables and then making the call,
pass them into function directly. This cleans up the code and makes it
all a bit easier to understand.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-01-26 11:06:57 +01:00

357 lines
8.9 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* For the code moved from cmd/bootefi.c
* Copyright (c) 2016 Alexander Graf
*/
#define LOG_CATEGORY LOGC_EFI
#include <bootflow.h>
#include <charset.h>
#include <dm.h>
#include <efi.h>
#include <efi_loader.h>
#include <env.h>
#include <image.h>
#include <log.h>
#include <malloc.h>
#include <mapmem.h>
static struct efi_device_path *bootefi_image_path;
static struct efi_device_path *bootefi_device_path;
static void *image_addr;
static size_t image_size;
/**
* efi_get_image_parameters() - return image parameters
*
* @img_addr: address of loaded image in memory
* @img_size: size of loaded image
*/
void efi_get_image_parameters(void **img_addr, size_t *img_size)
{
*img_addr = image_addr;
*img_size = image_size;
}
/**
* efi_clear_bootdev() - clear boot device
*/
void efi_clear_bootdev(void)
{
efi_free_pool(bootefi_device_path);
efi_free_pool(bootefi_image_path);
bootefi_device_path = NULL;
bootefi_image_path = NULL;
image_addr = NULL;
image_size = 0;
}
/**
* calculate_paths() - Calculate the device and image patch from strings
*
* @dev: device, e.g. "MMC"
* @devnr: number of the device, e.g. "1:2"
* @path: path to file loaded
* @device_pathp: returns EFI device path
* @image_pathp: returns EFI image path
* Return: EFI_SUCCESS on success, else error code
*/
static efi_status_t calculate_paths(const char *dev, const char *devnr,
const char *path,
struct efi_device_path **device_pathp,
struct efi_device_path **image_pathp)
{
struct efi_device_path *image, *device;
efi_status_t ret;
#if IS_ENABLED(CONFIG_NETDEVICES)
if (!strcmp(dev, "Net") || !strcmp(dev, "Http")) {
ret = efi_net_set_dp(dev, devnr);
if (ret != EFI_SUCCESS)
return ret;
}
#endif
ret = efi_dp_from_name(dev, devnr, path, &device, &image);
if (ret != EFI_SUCCESS)
return ret;
*device_pathp = device;
if (image) {
/* FIXME: image should not contain device */
struct efi_device_path *image_tmp = image;
efi_dp_split_file_path(image, &device, &image);
efi_free_pool(image_tmp);
}
*image_pathp = image;
log_debug("- boot device %pD\n", device);
if (image)
log_debug("- image %pD\n", image);
return EFI_SUCCESS;
}
/**
* efi_set_bootdev() - set boot device
*
* This function is called when a file is loaded, e.g. via the 'load' command.
* We use the path to this file to inform the UEFI binary about the boot device.
*
* For a valid image, it sets:
* - image_addr to the provided buffer
* - image_size to the provided buffer_size
* - bootefi_device_path to the EFI device-path
* - bootefi_image_path to the EFI image-path
*
* @dev: device, e.g. "MMC"
* @devnr: number of the device, e.g. "1:2"
* @path: path to file loaded
* @buffer: buffer with file loaded
* @buffer_size: size of file loaded
*/
void efi_set_bootdev(const char *dev, const char *devnr, const char *path,
void *buffer, size_t buffer_size)
{
efi_status_t ret;
log_debug("dev=%s, devnr=%s, path=%s, buffer=%p, size=%zx\n", dev,
devnr, path, buffer, buffer_size);
/* Forget overwritten image */
if (buffer + buffer_size >= image_addr &&
image_addr + image_size >= buffer)
efi_clear_bootdev();
/* Remember only PE-COFF and FIT images */
if (efi_check_pe(buffer, buffer_size, NULL) != EFI_SUCCESS) {
if (IS_ENABLED(CONFIG_FIT) &&
!fit_check_format(buffer, IMAGE_SIZE_INVAL)) {
/*
* FIT images of type EFI_OS are started via command
* bootm. We should not use their boot device with the
* bootefi command.
*/
buffer = 0;
buffer_size = 0;
} else {
log_debug("- not remembering image\n");
return;
}
}
/* efi_set_bootdev() is typically called repeatedly, recover memory */
efi_clear_bootdev();
image_addr = buffer;
image_size = buffer_size;
ret = calculate_paths(dev, devnr, path, &bootefi_device_path,
&bootefi_image_path);
if (ret) {
log_debug("- efi_dp_from_name() failed, err=%lx\n", ret);
efi_clear_bootdev();
}
}
/**
* efi_run_image() - run loaded UEFI image
*
* @source_buffer: memory address of the UEFI image
* @source_size: size of the UEFI image
* @dp_dev: EFI device-path
* @dp_img: EFI image-path
* Return: status code
*/
static efi_status_t efi_run_image(void *source_buffer, efi_uintn_t source_size,
struct efi_device_path *dp_dev,
struct efi_device_path *dp_img)
{
efi_handle_t handle;
struct efi_device_path *msg_path, *file_path;
efi_status_t ret;
u16 *load_options;
file_path = efi_dp_concat(dp_dev, dp_img, 0);
msg_path = dp_img;
log_info("Booting %pD\n", msg_path);
ret = EFI_CALL(efi_load_image(false, efi_root, file_path, source_buffer,
source_size, &handle));
if (ret != EFI_SUCCESS) {
log_err("Loading image failed\n");
goto out;
}
/* Transfer environment variable as load options */
ret = efi_env_set_load_options(handle, "bootargs", &load_options);
if (ret != EFI_SUCCESS)
goto out;
ret = do_bootefi_exec(handle, load_options);
out:
return ret;
}
/**
* efi_binary_run_dp() - run loaded UEFI image
*
* @image: memory address of the UEFI image
* @size: size of the UEFI image
* @fdt: device-tree
* @dp_dev: EFI device-path
* @dp_img: EFI image-path
*
* Execute an EFI binary image loaded at @image.
* @size may be zero if the binary is loaded with U-Boot load command.
*
* Return: status code
*/
static efi_status_t efi_binary_run_dp(void *image, size_t size, void *fdt,
struct efi_device_path *dp_dev,
struct efi_device_path *dp_img)
{
efi_status_t ret;
/* Initialize EFI drivers */
ret = efi_init_obj_list();
if (ret != EFI_SUCCESS) {
log_err("Error: Cannot initialize UEFI sub-system, r = %lu\n",
ret & ~EFI_ERROR_MASK);
return -1;
}
ret = efi_install_fdt(fdt);
if (ret != EFI_SUCCESS)
return ret;
return efi_run_image(image, size, dp_dev, dp_img);
}
/**
* efi_binary_run() - run loaded UEFI image
*
* @image: memory address of the UEFI image
* @size: size of the UEFI image
* @fdt: device-tree
*
* Execute an EFI binary image loaded at @image.
* @size may be zero if the binary is loaded with U-Boot load command.
*
* Return: status code
*/
efi_status_t efi_binary_run(void *image, size_t size, void *fdt)
{
efi_handle_t mem_handle = NULL;
struct efi_device_path *file_path = NULL;
efi_status_t ret;
if (!bootefi_device_path || !bootefi_image_path) {
log_debug("Not loaded from disk\n");
/*
* Special case for efi payload not loaded from disk,
* such as 'bootefi hello' or for example payload
* loaded directly into memory via JTAG, etc:
*/
file_path = efi_dp_from_mem(EFI_RESERVED_MEMORY_TYPE,
(uintptr_t)image, size);
/*
* Make sure that device for device_path exist
* in load_image(). Otherwise, shell and grub will fail.
*/
ret = efi_install_multiple_protocol_interfaces(&mem_handle,
&efi_guid_device_path,
file_path, NULL);
if (ret != EFI_SUCCESS)
goto out;
} else {
log_debug("Loaded from disk\n");
}
ret = efi_binary_run_dp(image, size, fdt, bootefi_device_path,
bootefi_image_path);
out:
if (mem_handle) {
efi_status_t r;
r = efi_uninstall_multiple_protocol_interfaces(mem_handle,
&efi_guid_device_path, file_path, NULL);
if (r != EFI_SUCCESS)
log_err("Uninstalling protocol interfaces failed\n");
}
efi_free_pool(file_path);
return ret;
}
/**
* calc_dev_name() - Calculate the device name to give to EFI
*
* If not supported, this shows an error.
*
* Return name, or NULL if not supported
*/
static const char *calc_dev_name(struct bootflow *bflow)
{
const struct udevice *media_dev;
media_dev = dev_get_parent(bflow->dev);
if (!bflow->blk) {
log_err("Cannot boot EFI app on media '%s'\n",
dev_get_uclass_name(media_dev));
return NULL;
}
if (device_get_uclass_id(media_dev) == UCLASS_MASS_STORAGE)
return "usb";
return blk_get_uclass_name(device_get_uclass_id(media_dev));
}
efi_status_t efi_bootflow_run(struct bootflow *bflow)
{
struct efi_device_path *device, *image;
const struct udevice *media_dev;
struct blk_desc *desc = NULL;
const char *dev_name;
char devnum_str[9];
efi_status_t ret;
void *fdt;
media_dev = dev_get_parent(bflow->dev);
if (bflow->blk) {
desc = dev_get_uclass_plat(bflow->blk);
snprintf(devnum_str, sizeof(devnum_str), "%x:%x",
desc ? desc->devnum : dev_seq(media_dev), bflow->part);
} else {
*devnum_str = '\0';
}
dev_name = calc_dev_name(bflow);
log_debug("dev_name '%s' devnum_str '%s' fname '%s' media_dev '%s'\n",
dev_name, devnum_str, bflow->fname, media_dev->name);
if (!dev_name)
return EFI_UNSUPPORTED;
ret = calculate_paths(dev_name, devnum_str, bflow->fname, &device,
&image);
if (ret)
return ret;
if (bflow->flags & BOOTFLOWF_USE_BUILTIN_FDT) {
log_debug("Booting with built-in fdt\n");
fdt = EFI_FDT_USE_INTERNAL;
} else {
log_debug("Booting with external fdt\n");
fdt = map_sysmem(bflow->fdt_addr, 0);
}
ret = efi_binary_run_dp(bflow->buf, bflow->size, fdt, device, image);
return ret;
}