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:
Matthew Garrett
2024-11-23 11:55:00 -08:00
committed by Simon Glass
parent 6972edf855
commit 9121737455
6 changed files with 84 additions and 15 deletions

View File

@@ -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 = &params->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()) {

View File

@@ -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;

View File

@@ -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
*/

View File

@@ -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;

View File

@@ -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;

View File

@@ -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" },
{ }