From 6944c611634ddca985fe54966469d8844fc34fb3 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Sat, 17 Sep 2022 22:24:51 +0200 Subject: [PATCH] stub: Refuse operating above 4G if not supported We could try to relocate these but that is probably not needed. So far we have not been bitten by this in the real world and kernels we boot likely already support this anyway. --- src/boot/efi/linux_x86.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/boot/efi/linux_x86.c b/src/boot/efi/linux_x86.c index e8231de2c5..eeb3c7abc6 100644 --- a/src/boot/efi/linux_x86.c +++ b/src/boot/efi/linux_x86.c @@ -22,6 +22,13 @@ #define BOOT_FLAG_MAGIC 0xAA55u #define SETUP_MAGIC 0x53726448u /* "HdrS" */ #define SETUP_VERSION_2_11 0x20bu +#define SETUP_VERSION_2_12 0x20cu +#define CMDLINE_PTR_MAX 0xA0000u + +enum { + XLF_KERNEL_64 = 1 << 0, + XLF_CAN_BE_LOADED_ABOVE_4G = 1 << 1, +}; typedef struct { uint8_t setup_sects; @@ -131,9 +138,20 @@ EFI_STATUS linux_exec( if (!image_params->hdr.relocatable_kernel) return log_error_status_stall(EFI_UNSUPPORTED, u"Kernel is not relocatable."); + 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_stall( + EFI_UNSUPPORTED, + u"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_stall( + EFI_UNSUPPORTED, u"Initrd is above 4G, but kernel lacks support."); + addr = UINT32_MAX; /* Below the 32bit boundary */ err = BS->AllocatePages( - AllocateMaxAddress, + can_4g ? AllocateAnyPages : AllocateMaxAddress, EfiLoaderData, EFI_SIZE_TO_PAGES(sizeof(BootParams)), &addr); @@ -156,10 +174,10 @@ EFI_STATUS linux_exec( boot_params->hdr.setup_sects = 4; if (cmdline) { - addr = 0xA0000; + addr = CMDLINE_PTR_MAX; err = BS->AllocatePages( - AllocateMaxAddress, + can_4g ? AllocateAnyPages : AllocateMaxAddress, EfiLoaderData, EFI_SIZE_TO_PAGES(cmdline_len + 1), &addr); @@ -169,6 +187,8 @@ EFI_STATUS linux_exec( memcpy(PHYSICAL_ADDRESS_TO_POINTER(addr), cmdline, cmdline_len); ((char *) PHYSICAL_ADDRESS_TO_POINTER(addr))[cmdline_len] = 0; boot_params->hdr.cmd_line_ptr = (uint32_t) addr; + boot_params->ext_cmd_line_ptr = addr >> 32; + assert(can_4g || addr <= CMDLINE_PTR_MAX); } /* Providing the initrd via LINUX_INITRD_MEDIA_GUID is only supported by Linux 5.8+ (5.7+ on ARM64). @@ -177,8 +197,10 @@ EFI_STATUS linux_exec( If you need to know which protocol was used by the kernel, pass "efi=debug" to the kernel, this will print a line when InitrdMediaGuid was successfully used to load the initrd. */ - boot_params->hdr.ramdisk_image = (uint32_t) POINTER_TO_PHYSICAL_ADDRESS(initrd_buffer); - boot_params->hdr.ramdisk_size = (uint32_t) initrd_length; + boot_params->hdr.ramdisk_image = (uintptr_t) initrd_buffer; + boot_params->ext_ramdisk_image = POINTER_TO_PHYSICAL_ADDRESS(initrd_buffer) >> 32; + boot_params->hdr.ramdisk_size = initrd_length; + boot_params->ext_ramdisk_size = ((uint64_t) initrd_length) >> 32; /* register LINUX_INITRD_MEDIA_GUID */ err = initrd_register(initrd_buffer, initrd_length, &initrd_handle);