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(-) 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); -- 2.43.0