stub: Relocate kernels below 4G for EFI handover
Old kernels can fail to boot when they are located above the 4G boundary even if they claim to support it. Fixes: #27472
This commit is contained in:
@@ -104,8 +104,7 @@ static void linux_efi_handover(EFI_HANDLE parent, uintptr_t kernel, BootParams *
|
||||
|
||||
kernel += (params->hdr.setup_sects + 1) * KERNEL_SECTOR_SIZE; /* 32bit entry address. */
|
||||
|
||||
/* Old kernels needs this set, while newer ones seem to ignore this. Note that this gets truncated on
|
||||
* above 4G boots, which is fine as long as we do not use the value to jump to kernel entry. */
|
||||
/* Old kernels needs this set, while newer ones seem to ignore this. */
|
||||
params->hdr.code32_start = kernel;
|
||||
|
||||
#ifdef __x86_64__
|
||||
@@ -153,12 +152,25 @@ EFI_STATUS linux_exec_efi_handover(
|
||||
bool can_4g = image_params->hdr.version >= SETUP_VERSION_2_12 &&
|
||||
FLAGS_SET(image_params->hdr.xloadflags, XLF_CAN_BE_LOADED_ABOVE_4G);
|
||||
|
||||
if (!can_4g && POINTER_TO_PHYSICAL_ADDRESS(linux_buffer) + linux_length > UINT32_MAX)
|
||||
return log_error_status(
|
||||
EFI_UNSUPPORTED,
|
||||
"Unified kernel image was loaded above 4G, but kernel lacks support.");
|
||||
if (!can_4g && POINTER_TO_PHYSICAL_ADDRESS(initrd_buffer) + initrd_length > UINT32_MAX)
|
||||
return log_error_status(EFI_UNSUPPORTED, "Initrd is above 4G, but kernel lacks support.");
|
||||
/* There is no way to pass the high bits of code32_start. Newer kernels seems to handle this
|
||||
* just fine, but older kernels will fail even if they otherwise have above 4G boot support. */
|
||||
_cleanup_pages_ Pages linux_relocated = {};
|
||||
if (POINTER_TO_PHYSICAL_ADDRESS(linux_buffer) + linux_length > UINT32_MAX) {
|
||||
linux_relocated = xmalloc_pages(
|
||||
AllocateMaxAddress, EfiLoaderCode, EFI_SIZE_TO_PAGES(linux_length), UINT32_MAX);
|
||||
linux_buffer = memcpy(
|
||||
PHYSICAL_ADDRESS_TO_POINTER(linux_relocated.addr), linux_buffer, linux_length);
|
||||
}
|
||||
|
||||
_cleanup_pages_ Pages initrd_relocated = {};
|
||||
if (!can_4g && POINTER_TO_PHYSICAL_ADDRESS(initrd_buffer) + initrd_length > UINT32_MAX) {
|
||||
initrd_relocated = xmalloc_pages(
|
||||
AllocateMaxAddress, EfiLoaderData, EFI_SIZE_TO_PAGES(initrd_length), UINT32_MAX);
|
||||
initrd_buffer = memcpy(
|
||||
PHYSICAL_ADDRESS_TO_POINTER(initrd_relocated.addr),
|
||||
initrd_buffer,
|
||||
initrd_length);
|
||||
}
|
||||
|
||||
_cleanup_pages_ Pages boot_params_page = xmalloc_pages(
|
||||
can_4g ? AllocateAnyPages : AllocateMaxAddress,
|
||||
|
||||
Reference in New Issue
Block a user