]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
stub: Provide a proper boot params page
authorJan Janssen <medhefgo@web.de>
Sat, 17 Sep 2022 20:02:42 +0000 (22:02 +0200)
committerJan Janssen <medhefgo@web.de>
Tue, 20 Sep 2022 10:54:47 +0000 (12:54 +0200)
The boot params passed to the kernel only has to be one page in size
and the size of the setup header has to be calculated as it depends on
the boot protocol version supported by the kernel.

src/boot/efi/linux_x86.c

index 756494b9c7c668c236bfdc16e34da71aee431816..e8231de2c5c196097d2cf84beff93a4724e6a551 100644 (file)
 #include "util.h"
 
 #define KERNEL_SECTOR_SIZE 512u
-#define SETUP_MAGIC             0x53726448      /* "HdrS" */
+#define BOOT_FLAG_MAGIC    0xAA55u
+#define SETUP_MAGIC        0x53726448u /* "HdrS" */
+#define SETUP_VERSION_2_11 0x20bu
 
-struct setup_header {
+typedef struct {
         uint8_t  setup_sects;
         uint16_t root_flags;
         uint32_t syssize;
@@ -29,7 +31,8 @@ struct setup_header {
         uint16_t vid_mode;
         uint16_t root_dev;
         uint16_t boot_flag;
-        uint16_t jump;
+        uint8_t  jump; /* We split the 2-byte jump field from the spec in two for convenience. */
+        uint8_t  setup_size;
         uint32_t header;
         uint16_t version;
         uint32_t realmode_swtch;
@@ -60,54 +63,30 @@ struct setup_header {
         uint64_t pref_address;
         uint32_t init_size;
         uint32_t handover_offset;
-} _packed_;
-
-/* adapted from linux' bootparam.h */
-struct boot_params {
-        uint8_t  screen_info[64];         // was: struct screen_info
-        uint8_t  apm_bios_info[20];       // was: struct apm_bios_info
-        uint8_t  _pad2[4];
-        uint64_t tboot_addr;
-        uint8_t  ist_info[16];            // was: struct ist_info
-        uint8_t  _pad3[16];
-        uint8_t  hd0_info[16];
-        uint8_t  hd1_info[16];
-        uint8_t  sys_desc_table[16];      // was: struct sys_desc_table
-        uint8_t  olpc_ofw_header[16];     // was: struct olpc_ofw_header
+} _packed_ SetupHeader;
+
+/* We really only care about a few fields, but we still have to provide a full page otherwise. */
+typedef struct {
+        uint8_t pad[192];
         uint32_t ext_ramdisk_image;
         uint32_t ext_ramdisk_size;
         uint32_t ext_cmd_line_ptr;
-        uint8_t  _pad4[116];
-        uint8_t  edid_info[128];          // was: struct edid_info
-        uint8_t  efi_info[32];            // was: struct efi_info
-        uint32_t alt_mem_k;
-        uint32_t scratch;
-        uint8_t  e820_entries;
-        uint8_t  eddbuf_entries;
-        uint8_t  edd_mbr_sig_buf_entries;
-        uint8_t  kbd_status;
-        uint8_t  secure_boot;
-        uint8_t  _pad5[2];
-        uint8_t  sentinel;
-        uint8_t  _pad6[1];
-        struct setup_header hdr;
-        uint8_t  _pad7[0x290-0x1f1-sizeof(struct setup_header)];
-        uint32_t edd_mbr_sig_buffer[16];  // was: edd_mbr_sig_buffer[EDD_MBR_SIG_MAX]
-        uint8_t  e820_table[20*128];      // was: struct boot_e820_entry e820_table[E820_MAX_ENTRIES_ZEROPAGE]
-        uint8_t  _pad8[48];
-        uint8_t  eddbuf[6*82];            // was: struct edd_info eddbuf[EDDMAXNR]
-        uint8_t  _pad9[276];
-} _packed_;
+        uint8_t pad2[293];
+        SetupHeader hdr;
+        uint8_t pad3[3480];
+} _packed_ BootParams;
+assert_cc(offsetof(BootParams, ext_ramdisk_image) == 0x0C0);
+assert_cc(sizeof(BootParams) == 4096);
 
 #ifdef __i386__
-#define __regparm0__ __attribute__((regparm(0)))
+#  define __regparm0__ __attribute__((regparm(0)))
 #else
-#define __regparm0__
+#  define __regparm0__
 #endif
 
-typedef void(*handover_f)(void *image, EFI_SYSTEM_TABLE *table, struct boot_params *params) __regparm0__;
+typedef void (*handover_f)(void *image, EFI_SYSTEM_TABLE *table, BootParams *params) __regparm0__;
 
-static void linux_efi_handover(EFI_HANDLE image, uintptr_t kernel, struct boot_params *params) {
+static void linux_efi_handover(EFI_HANDLE image, uintptr_t kernel, BootParams *params) {
         assert(params);
 
         kernel += (params->hdr.setup_sects + 1) * KERNEL_SECTOR_SIZE; /* 32bit entry address. */
@@ -132,8 +111,6 @@ EFI_STATUS linux_exec(
                 const void *linux_buffer, UINTN linux_length,
                 const void *initrd_buffer, UINTN initrd_length) {
 
-        const struct boot_params *image_params;
-        struct boot_params *boot_params;
         EFI_HANDLE initrd_handle = NULL;
         EFI_PHYSICAL_ADDRESS addr;
         EFI_STATUS err;
@@ -143,29 +120,35 @@ EFI_STATUS linux_exec(
         assert(linux_buffer);
         assert(initrd_buffer || initrd_length == 0);
 
-        if (linux_length < sizeof(struct boot_params))
+        if (linux_length < sizeof(BootParams))
                 return EFI_LOAD_ERROR;
 
-        image_params = (const struct boot_params *) linux_buffer;
-
-        if (image_params->hdr.boot_flag != 0xAA55 ||
-            image_params->hdr.header != SETUP_MAGIC ||
-            image_params->hdr.version < 0x20b ||
-            !image_params->hdr.relocatable_kernel)
-                return EFI_LOAD_ERROR;
+        const BootParams *image_params = (const BootParams *) linux_buffer;
+        if (image_params->hdr.header != SETUP_MAGIC || image_params->hdr.boot_flag != BOOT_FLAG_MAGIC)
+                return log_error_status_stall(EFI_UNSUPPORTED, u"Unsupported kernel image.");
+        if (image_params->hdr.version < SETUP_VERSION_2_11)
+                return log_error_status_stall(EFI_UNSUPPORTED, u"Kernel too old.");
+        if (!image_params->hdr.relocatable_kernel)
+                return log_error_status_stall(EFI_UNSUPPORTED, u"Kernel is not relocatable.");
 
         addr = UINT32_MAX; /* Below the 32bit boundary */
         err = BS->AllocatePages(
                         AllocateMaxAddress,
                         EfiLoaderData,
-                        EFI_SIZE_TO_PAGES(0x4000),
+                        EFI_SIZE_TO_PAGES(sizeof(BootParams)),
                         &addr);
         if (err != EFI_SUCCESS)
                 return err;
 
-        boot_params = (struct boot_params *) PHYSICAL_ADDRESS_TO_POINTER(addr);
-        memset(boot_params, 0, 0x4000);
-        boot_params->hdr = image_params->hdr;
+        BootParams *boot_params = PHYSICAL_ADDRESS_TO_POINTER(addr);
+        *boot_params = (BootParams){};
+
+        /* Setup size is determined by offset 0x0202 + byte value at offset 0x0201, which is the same as
+         * offset of the header field and the target from the jump field (which we split for this reason). */
+        memcpy(&boot_params->hdr,
+               &image_params->hdr,
+               offsetof(SetupHeader, header) + image_params->hdr.setup_size);
+
         boot_params->hdr.type_of_loader = 0xff;
 
         /* Spec says: For backwards compatibility, if the setup_sects field contains 0, the real value is 4. */