Add EFI handover support to bootm
We want to jump into the EFI stub in the kernel so it can perform appropriate init and call ExitBootServices. Add support for doing that, including ensuring that we copy the kernel to somewhere that's not currently being used by the firmware. Signed-off-by: Matthew Garrett <mgarrett@aurora.tech> Reviewed-by: Simon Glass <sjg@chromium.org> Drop use of image_info_t: Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
committed by
Simon Glass
parent
6972edf855
commit
9121737455
@@ -11,6 +11,7 @@
|
||||
#include <bootstage.h>
|
||||
#include <command.h>
|
||||
#include <efi.h>
|
||||
#include <efi_api.h>
|
||||
#include <hang.h>
|
||||
#include <log.h>
|
||||
#include <asm/global_data.h>
|
||||
@@ -150,6 +151,38 @@ error:
|
||||
return 1;
|
||||
}
|
||||
|
||||
typedef void(*handover_func)(void *, struct efi_system_table *sys_table, struct
|
||||
boot_params *params);
|
||||
|
||||
int efi_boot(ulong setup_base, ulong entry, bool image_64bit)
|
||||
{
|
||||
struct boot_params *params = (struct boot_params *)setup_base;
|
||||
struct setup_header *hdr = ¶ms->hdr;
|
||||
struct efi_priv *priv = efi_get_priv();
|
||||
handover_func hf;
|
||||
int offset = 0;
|
||||
|
||||
if (IS_ENABLED(CONFIG_EFI_APP_64BIT)) {
|
||||
if (!image_64bit) {
|
||||
printf("## Can only boot 64-bit kernels\n");
|
||||
return 1;
|
||||
}
|
||||
offset = 512;
|
||||
} else if (image_64bit) {
|
||||
printf("# Can only boot 32-bit kernels\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
hdr->code32_start = (int)entry;
|
||||
hdr->type_of_loader = 0x80; /* U-Boot, from Linux Documentation/x86/boot.rst */
|
||||
|
||||
hf = (handover_func)(entry + hdr->handover_offset + offset);
|
||||
asm volatile ("cli");
|
||||
priv->loaded_image->image_base = (char *)entry;
|
||||
hf(priv->parent_image, priv->sys_table, params);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
int boot_linux_kernel(ulong setup_base, ulong entry, bool image_64bit)
|
||||
{
|
||||
bootm_announce_and_cleanup();
|
||||
@@ -158,21 +191,8 @@ int boot_linux_kernel(ulong setup_base, ulong entry, bool image_64bit)
|
||||
timestamp_add_now(TS_U_BOOT_START_KERNEL);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Exit EFI boot services just before jumping, after all console
|
||||
* output, since the console won't be available afterwards.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_EFI_APP)) {
|
||||
int ret;
|
||||
|
||||
ret = efi_store_memory_map(efi_get_priv());
|
||||
if (ret)
|
||||
return ret;
|
||||
printf("Exiting EFI boot services\n");
|
||||
ret = efi_call_exit_boot_services();
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
if (IS_ENABLED(CONFIG_EFI_APP))
|
||||
return efi_boot(setup_base, entry, image_64bit);
|
||||
|
||||
if (image_64bit) {
|
||||
if (!cpu_has_64bit()) {
|
||||
|
||||
@@ -49,6 +49,10 @@ __weak void board_quiesce_devices(void)
|
||||
{
|
||||
}
|
||||
|
||||
__weak void board_fixup_os(struct image_info *os)
|
||||
{
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT)
|
||||
/**
|
||||
* image_get_kernel - verify legacy format kernel image
|
||||
@@ -999,6 +1003,7 @@ int bootm_run_states(struct bootm_info *bmi, int states)
|
||||
/* Load the OS */
|
||||
if (!ret && (states & BOOTM_STATE_LOADOS)) {
|
||||
iflag = bootm_disable_interrupts();
|
||||
board_fixup_os(&images->os);
|
||||
ret = bootm_load_os(images, 0);
|
||||
if (ret && ret != BOOTM_ERR_OVERLAP)
|
||||
goto err;
|
||||
|
||||
@@ -217,6 +217,12 @@ void arch_preboot_os(void);
|
||||
*/
|
||||
void board_quiesce_devices(void);
|
||||
|
||||
/*
|
||||
* boards should define this if they need to fix up the kernel before boot
|
||||
* (eg, by modifying the desired load address).
|
||||
*/
|
||||
void board_fixup_os(struct image_info *os);
|
||||
|
||||
/**
|
||||
* switch_to_non_secure_mode() - switch to non-secure mode
|
||||
*/
|
||||
|
||||
@@ -459,6 +459,7 @@ static inline struct efi_mem_desc *efi_get_next_mem_desc(
|
||||
*/
|
||||
struct efi_priv {
|
||||
efi_handle_t parent_image;
|
||||
struct efi_loaded_image *loaded_image;
|
||||
struct efi_system_table *sys_table;
|
||||
struct efi_boot_services *boot;
|
||||
struct efi_runtime_services *run;
|
||||
|
||||
@@ -113,6 +113,7 @@ int efi_init(struct efi_priv *priv, const char *banner, efi_handle_t image,
|
||||
efi_puts(priv, "Failed to get loaded image protocol\n");
|
||||
return ret;
|
||||
}
|
||||
priv->loaded_image = loaded_image;
|
||||
priv->image_data_type = loaded_image->image_data_type;
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <efi.h>
|
||||
#include <efi_api.h>
|
||||
#include <errno.h>
|
||||
#include <image.h>
|
||||
#include <init.h>
|
||||
#include <malloc.h>
|
||||
#include <sysreset.h>
|
||||
@@ -218,6 +219,41 @@ static int efi_sysreset_request(struct udevice *dev, enum sysreset_t type)
|
||||
return -EINPROGRESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to relocate the kernel to somewhere the firmware isn't using
|
||||
*/
|
||||
#define PAGE_SIZE_BITS 12
|
||||
void board_fixup_os(struct image_info *os)
|
||||
{
|
||||
int pages;
|
||||
ulong load_addr;
|
||||
u64 addr;
|
||||
efi_status_t status;
|
||||
struct efi_priv *priv = efi_get_priv();
|
||||
struct efi_boot_services *boot = priv->boot;
|
||||
|
||||
pages = (os->image_len + ((1 << PAGE_SIZE_BITS) - 1)) >> PAGE_SIZE_BITS;
|
||||
|
||||
addr = os->load;
|
||||
|
||||
/* Try to allocate at the preferred address */
|
||||
status = boot->allocate_pages(EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
|
||||
pages, &addr);
|
||||
if (status == EFI_SUCCESS)
|
||||
return;
|
||||
|
||||
/* That failed, so try allocating anywhere there's enough room */
|
||||
status = boot->allocate_pages(EFI_ALLOCATE_ANY_PAGES, EFI_LOADER_DATA, pages, &addr);
|
||||
if (status == EFI_SUCCESS) {
|
||||
/* Make sure bootm knows where we loaded the image */
|
||||
os->load = addr;
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Failed to alloc %lx bytes at %lx: %lx\n", os->image_len, load_addr,
|
||||
status);
|
||||
}
|
||||
|
||||
static const struct udevice_id efi_sysreset_ids[] = {
|
||||
{ .compatible = "efi,reset" },
|
||||
{ }
|
||||
|
||||
Reference in New Issue
Block a user