[PATCH 01/10] Add EFI handover support to bootm

Matthew Garrett mjg59 at srcf.ucam.org
Sat Nov 23 20:55:00 CET 2024


From: Matthew Garrett <mgarrett at aurora.tech>

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 at aurora.tech>

---

 arch/x86/lib/bootm.c | 49 ++++++++++++++++++++++++++++++--------------
 boot/bootm.c         |  5 +++++
 include/bootm.h      |  6 ++++++
 include/efi.h        |  1 +
 lib/efi/efi.c        |  1 +
 lib/efi/efi_app.c    | 36 ++++++++++++++++++++++++++++++++
 6 files changed, 83 insertions(+), 15 deletions(-)

diff --git a/arch/x86/lib/bootm.c b/arch/x86/lib/bootm.c
index 55f581836df..c4446b1f9c6 100644
--- a/arch/x86/lib/bootm.c
+++ b/arch/x86/lib/bootm.c
@@ -150,6 +150,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 +190,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()) {
diff --git a/boot/bootm.c b/boot/bootm.c
index 16a43d519a8..d5caa4cdb31 100644
--- a/boot/bootm.c
+++ b/boot/bootm.c
@@ -49,6 +49,10 @@ __weak void board_quiesce_devices(void)
 {
 }
 
+__weak void board_fixup_os(image_info_t *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;
diff --git a/include/bootm.h b/include/bootm.h
index 61160705215..b0d10123b53 100644
--- a/include/bootm.h
+++ b/include/bootm.h
@@ -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(image_info_t *os);
+
 /**
  * switch_to_non_secure_mode() - switch to non-secure mode
  */
diff --git a/include/efi.h b/include/efi.h
index c559fda3004..1d06230439f 100644
--- a/include/efi.h
+++ b/include/efi.h
@@ -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;
diff --git a/lib/efi/efi.c b/lib/efi/efi.c
index bcb34d67465..bb1d9e24f84 100644
--- a/lib/efi/efi.c
+++ b/lib/efi/efi.c
@@ -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;
diff --git a/lib/efi/efi_app.c b/lib/efi/efi_app.c
index 9b94a93ee4f..7c3ef9a7926 100644
--- a/lib/efi/efi_app.c
+++ b/lib/efi/efi_app.c
@@ -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(image_info_t *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" },
 	{ }
-- 
2.47.0



More information about the U-Boot mailing list