Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e811e52e4a | ||
|
|
20cb96558a | ||
|
|
37ccf4a751 | ||
|
|
8bcb506b4d | ||
|
|
13d70bcd37 | ||
|
|
95d6a60c75 | ||
|
|
fdead6777e |
@@ -52,40 +52,6 @@ static bool bootmeth_uses_network(struct bootflow *bflow)
|
||||
device_get_uclass_id(media) == UCLASS_ETH;
|
||||
}
|
||||
|
||||
static void set_efi_bootdev(struct blk_desc *desc, struct bootflow *bflow)
|
||||
{
|
||||
const struct udevice *media_dev;
|
||||
int size = bflow->size;
|
||||
const char *dev_name;
|
||||
char devnum_str[9];
|
||||
char dirname[200];
|
||||
char *last_slash;
|
||||
|
||||
/*
|
||||
* This is a horrible hack to tell EFI about this boot device. Once we
|
||||
* unify EFI with the rest of U-Boot we can clean this up. The same hack
|
||||
* exists in multiple places, e.g. in the fs, tftp and load commands.
|
||||
*
|
||||
* Once we can clean up the EFI code to make proper use of driver model,
|
||||
* this can go away.
|
||||
*/
|
||||
media_dev = dev_get_parent(bflow->dev);
|
||||
snprintf(devnum_str, sizeof(devnum_str), "%x:%x",
|
||||
desc ? desc->devnum : dev_seq(media_dev),
|
||||
bflow->part);
|
||||
|
||||
strlcpy(dirname, bflow->fname, sizeof(dirname));
|
||||
last_slash = strrchr(dirname, '/');
|
||||
if (last_slash)
|
||||
*last_slash = '\0';
|
||||
|
||||
dev_name = device_get_uclass_id(media_dev) == UCLASS_MASS_STORAGE ?
|
||||
"usb" : blk_get_uclass_name(device_get_uclass_id(media_dev));
|
||||
log_debug("setting bootdev %s, %s, %s, %p, %x\n",
|
||||
dev_name, devnum_str, bflow->fname, bflow->buf, size);
|
||||
efi_set_bootdev(dev_name, devnum_str, bflow->fname, bflow->buf, size);
|
||||
}
|
||||
|
||||
static int efiload_read_file(struct bootflow *bflow, ulong addr)
|
||||
{
|
||||
struct blk_desc *desc = NULL;
|
||||
@@ -103,8 +69,6 @@ static int efiload_read_file(struct bootflow *bflow, ulong addr)
|
||||
return log_msg_ret("read", ret);
|
||||
bflow->buf = map_sysmem(addr, bflow->size);
|
||||
|
||||
set_efi_bootdev(desc, bflow);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -246,6 +210,7 @@ static int distro_efi_read_bootflow_net(struct bootflow *bflow)
|
||||
if (size <= 0)
|
||||
return log_msg_ret("sz", -EINVAL);
|
||||
bflow->size = size;
|
||||
bflow->buf = map_sysmem(addr, size);
|
||||
|
||||
/* bootfile should be setup by dhcp */
|
||||
bootfile_name = env_get("bootfile");
|
||||
@@ -253,10 +218,6 @@ static int distro_efi_read_bootflow_net(struct bootflow *bflow)
|
||||
return log_msg_ret("bootfile_name", ret);
|
||||
bflow->fname = strdup(bootfile_name);
|
||||
|
||||
/* do the hideous EFI hack */
|
||||
efi_set_bootdev("Net", "", bflow->fname, map_sysmem(addr, 0),
|
||||
bflow->size);
|
||||
|
||||
/* read the DT file also */
|
||||
fdt_addr_str = env_get("fdt_addr_r");
|
||||
if (!fdt_addr_str)
|
||||
@@ -330,29 +291,10 @@ static int distro_efi_boot(struct udevice *dev, struct bootflow *bflow)
|
||||
if (bflow->flags & ~BOOTFLOWF_USE_BUILTIN_FDT)
|
||||
fdt = bflow->fdt_addr;
|
||||
|
||||
} else {
|
||||
/*
|
||||
* This doesn't actually work for network devices:
|
||||
*
|
||||
* do_bootefi_image() No UEFI binary known at 0x02080000
|
||||
*
|
||||
* But this is the same behaviour for distro boot, so it can be
|
||||
* fixed here.
|
||||
*/
|
||||
fdt = env_get_hex("fdt_addr_r", 0);
|
||||
}
|
||||
|
||||
if (bflow->flags & BOOTFLOWF_USE_BUILTIN_FDT) {
|
||||
log_debug("Booting with built-in fdt\n");
|
||||
if (efi_binary_run(map_sysmem(kernel, 0), bflow->size,
|
||||
EFI_FDT_USE_INTERNAL))
|
||||
return log_msg_ret("run", -EINVAL);
|
||||
} else {
|
||||
log_debug("Booting with external fdt\n");
|
||||
if (efi_binary_run(map_sysmem(kernel, 0), bflow->size,
|
||||
map_sysmem(fdt, 0)))
|
||||
return log_msg_ret("run", -EINVAL);
|
||||
}
|
||||
if (efi_bootflow_run(bflow))
|
||||
return log_msg_ret("run", -EINVAL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <linux/oid_registry.h>
|
||||
|
||||
struct blk_desc;
|
||||
struct bootflow;
|
||||
struct jmp_buf_data;
|
||||
|
||||
#if CONFIG_IS_ENABLED(EFI_LOADER)
|
||||
@@ -578,6 +579,15 @@ efi_status_t efi_install_fdt(void *fdt);
|
||||
efi_status_t do_bootefi_exec(efi_handle_t handle, void *load_options);
|
||||
/* Run loaded UEFI image with given fdt */
|
||||
efi_status_t efi_binary_run(void *image, size_t size, void *fdt);
|
||||
|
||||
/**
|
||||
* efi_bootflow_run() - Run a bootflow containing an EFI application
|
||||
*
|
||||
* @bootflow: Bootflow to run
|
||||
* Return: Status code, something went wrong
|
||||
*/
|
||||
efi_status_t efi_bootflow_run(struct bootflow *bootflow);
|
||||
|
||||
/* Initialize variable services */
|
||||
efi_status_t efi_init_variables(void);
|
||||
/* Notify ExitBootServices() is called */
|
||||
|
||||
@@ -6,13 +6,16 @@
|
||||
|
||||
#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;
|
||||
@@ -44,12 +47,64 @@ void efi_clear_bootdev(void)
|
||||
image_size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* calculate_paths() - Calculate the device and image patch given a device
|
||||
*
|
||||
* @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
|
||||
@@ -59,7 +114,6 @@ void efi_clear_bootdev(void)
|
||||
void efi_set_bootdev(const char *dev, const char *devnr, const char *path,
|
||||
void *buffer, size_t buffer_size)
|
||||
{
|
||||
struct efi_device_path *device, *image;
|
||||
efi_status_t ret;
|
||||
|
||||
log_debug("dev=%s, devnr=%s, path=%s, buffer=%p, size=%zx\n", dev,
|
||||
@@ -93,34 +147,12 @@ void efi_set_bootdev(const char *dev, const char *devnr, const char *path,
|
||||
image_addr = buffer;
|
||||
image_size = buffer_size;
|
||||
|
||||
#if IS_ENABLED(CONFIG_NETDEVICES)
|
||||
if (!strcmp(dev, "Net") || !strcmp(dev, "Http")) {
|
||||
ret = efi_net_set_dp(dev, devnr);
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto error;
|
||||
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();
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = efi_dp_from_name(dev, devnr, path, &device, &image);
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto error;
|
||||
|
||||
bootefi_device_path = 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);
|
||||
}
|
||||
bootefi_image_path = image;
|
||||
log_debug("- boot device %pD\n", device);
|
||||
if (image)
|
||||
log_debug("- image %pD\n", image);
|
||||
return;
|
||||
error:
|
||||
log_debug("- efi_dp_from_name() failed, err=%lx\n", ret);
|
||||
efi_clear_bootdev();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,42 +160,21 @@ error:
|
||||
*
|
||||
* @source_buffer: memory address of the UEFI image
|
||||
* @source_size: size of the UEFI image
|
||||
* @device: EFI device-path
|
||||
* @image: EFI image-path
|
||||
* Return: status code
|
||||
*/
|
||||
efi_status_t efi_run_image(void *source_buffer, efi_uintn_t source_size)
|
||||
static efi_status_t efi_run_image(void *source_buffer, efi_uintn_t source_size,
|
||||
struct efi_device_path *device,
|
||||
struct efi_device_path *image)
|
||||
{
|
||||
efi_handle_t mem_handle = NULL, handle;
|
||||
struct efi_device_path *file_path = NULL;
|
||||
struct efi_device_path *msg_path;
|
||||
efi_handle_t handle;
|
||||
struct efi_device_path *msg_path, *file_path;
|
||||
efi_status_t ret;
|
||||
u16 *load_options;
|
||||
|
||||
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)source_buffer,
|
||||
source_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;
|
||||
msg_path = file_path;
|
||||
} else {
|
||||
file_path = efi_dp_concat(bootefi_device_path,
|
||||
bootefi_image_path, 0);
|
||||
msg_path = bootefi_image_path;
|
||||
log_debug("Loaded from disk\n");
|
||||
}
|
||||
file_path = efi_dp_concat(device, image, 0);
|
||||
msg_path = image;
|
||||
|
||||
log_info("Booting %pD\n", msg_path);
|
||||
|
||||
@@ -182,19 +193,45 @@ efi_status_t efi_run_image(void *source_buffer, efi_uintn_t source_size)
|
||||
ret = do_bootefi_exec(handle, load_options);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_binary_run_() - run loaded UEFI image
|
||||
*
|
||||
* @image_ptr: memory address of the UEFI image
|
||||
* @size: size of the UEFI image
|
||||
* @fdt: device-tree
|
||||
* @device: EFI device-path
|
||||
* @image: 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(void *image_ptr, size_t size, void *fdt,
|
||||
struct efi_device_path *device,
|
||||
struct efi_device_path *image)
|
||||
{
|
||||
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_ptr, size, device, image);
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_binary_run() - run loaded UEFI image
|
||||
*
|
||||
@@ -209,19 +246,116 @@ out:
|
||||
*/
|
||||
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;
|
||||
|
||||
/* 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;
|
||||
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_install_fdt(fdt);
|
||||
if (ret != EFI_SUCCESS)
|
||||
return ret;
|
||||
ret = _efi_binary_run(image, size, fdt, bootefi_device_path,
|
||||
bootefi_image_path);
|
||||
out:
|
||||
if (mem_handle) {
|
||||
efi_status_t r;
|
||||
|
||||
return efi_run_image(image, size);
|
||||
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) {
|
||||
if (device_get_uclass_id(media_dev) == UCLASS_ETH)
|
||||
return "Net";
|
||||
|
||||
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 EFI_UNSUPPORTED;
|
||||
|
||||
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(bflow->buf, bflow->size, fdt, device, image);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user