]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
stub: Refuse operating above 4G if not supported
authorJan Janssen <medhefgo@web.de>
Sat, 17 Sep 2022 20:24:51 +0000 (22:24 +0200)
committerJan Janssen <medhefgo@web.de>
Tue, 20 Sep 2022 10:54:53 +0000 (12:54 +0200)
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

index e8231de2c5c196097d2cf84beff93a4724e6a551..eeb3c7abc6041d2b7eb9b99c2577baf61099ab2e 100644 (file)
 #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);