[Concept,03/12] efi: Cache gd->relocaddr for EFI runtime services

Message ID 20260211143309.1183113-4-sjg@u-boot.org
State New
Headers
Series x86: Add single 64-bit U-Boot without SPL for QEMU |

Commit Message

Simon Glass Feb. 11, 2026, 2:32 p.m. UTC
  From: Simon Glass <simon.glass@canonical.com>

The EFI runtime service SetVirtualAddressMap() and the functions it
calls access gd->relocaddr. On architectures where 'gd' is accessed
through a CPU register (ARM via r9/x18, RISC-V via tp, x86_64 via
MSR_FS_BASE), the register is repurposed by the OS after
ExitBootServices(). When the OS later calls SetVirtualAddressMap(),
the gd pointer is invalid, causing a page fault.

On x86_64, the recent switch to MSR_FS_BASE for the gd pointer means
that %fs:0 now reads from the Linux kernel's per-CPU data area instead
of U-Boot's global_data, resulting in a bogus relocaddr value and a
crash during the EFI virtual-address relocation.

Cache gd->relocaddr in an __efi_runtime_data variable during the
initial relocation call from board_r.c (when gd is still valid) and
use this cached value in all three runtime-time references. This is
correct for all architectures since EFI runtime code should not depend
on U-Boot's gd after ExitBootServices().

Fix a checkpatch warning while here.

Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
---

 lib/efi_loader/efi_runtime.c | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)
  

Patch

diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
index 0a6004f404d..bd09d78d047 100644
--- a/lib/efi_loader/efi_runtime.c
+++ b/lib/efi_loader/efi_runtime.c
@@ -97,6 +97,7 @@  struct elf_rela {
 static __efi_runtime_data struct efi_mem_desc *efi_virtmap;
 static __efi_runtime_data efi_uintn_t efi_descriptor_count;
 static __efi_runtime_data efi_uintn_t efi_descriptor_size;
+static __efi_runtime_data ulong efi_relocaddr;
 
 /*
  * EFI runtime code lives in two stages. In the first stage, U-Boot and an EFI
@@ -684,7 +685,7 @@  static __efi_runtime void efi_relocate_runtime_table(ulong offset)
 	void **pos;
 
 	/* Relocate the runtime services pointers */
-	patchoff = offset - gd->relocaddr;
+	patchoff = offset - efi_relocaddr;
 	for (pos = (void **)&efi_runtime_services.get_time;
 	     pos <= (void **)&efi_runtime_services.query_variable_info; ++pos) {
 		if (*pos)
@@ -721,6 +722,18 @@  void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map)
 	if (gd_ulib())
 		return;
 
+	/*
+	 * Cache gd->relocaddr for use by the EFI runtime services after
+	 * the OS has taken over. On architectures where 'gd' is accessed
+	 * through a register (ARM, RISC-V, x86_64), it becomes invalid
+	 * once the OS overwrites that register.
+	 *
+	 * The first call (map == NULL) comes from board_r.c during
+	 * U-Boot init, when gd is still valid.
+	 */
+	if (!map)
+		efi_relocaddr = gd->relocaddr;
+
 #ifdef IS_RELA
 	struct elf_rela *rel = (void *)__efi_runtime_rel_start;
 #else
@@ -734,7 +747,7 @@  void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map)
 		ulong *p;
 		ulong newaddr;
 
-		p = (void*)((ulong)rel->offset - base) + gd->relocaddr;
+		p = (void *)((ulong)rel->offset - base) + efi_relocaddr;
 
 		/*
 		 * The runtime services table is updated in
@@ -910,7 +923,7 @@  static efi_status_t EFIAPI efi_set_virtual_address_map(
 		map = (void*)virtmap + (descriptor_size * i);
 		if (map->type == EFI_RUNTIME_SERVICES_CODE) {
 			ulong new_offset = map->virtual_start -
-					   map->physical_start + gd->relocaddr;
+					   map->physical_start + efi_relocaddr;
 
 			efi_relocate_runtime_table(new_offset);
 			efi_runtime_relocate(new_offset, map);