1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
4 * x86 specific code to for EFI handover boot protocol
5 * Linux kernels version 5.8 and newer support providing the initrd by
6 * LINUX_INITRD_MEDIA_GUID DevicePath. In order to support older kernels too,
7 * this x86 specific linux_exec function passes the initrd by setting the
8 * corresponding fields in the setup_header struct.
10 * see https://docs.kernel.org/x86/boot.html
15 #include "macro-fundamental.h"
18 #define KERNEL_SECTOR_SIZE 512u
19 #define BOOT_FLAG_MAGIC 0xAA55u
20 #define SETUP_MAGIC 0x53726448u /* "HdrS" */
21 #define SETUP_VERSION_2_11 0x20bu
22 #define SETUP_VERSION_2_12 0x20cu
23 #define SETUP_VERSION_2_15 0x20fu
24 #define CMDLINE_PTR_MAX 0xA0000u
27 XLF_KERNEL_64
= 1 << 0,
28 XLF_CAN_BE_LOADED_ABOVE_4G
= 1 << 1,
29 XLF_EFI_HANDOVER_32
= 1 << 2,
30 XLF_EFI_HANDOVER_64
= 1 << 3,
32 XLF_EFI_HANDOVER
= XLF_EFI_HANDOVER_64
,
34 XLF_EFI_HANDOVER
= XLF_EFI_HANDOVER_32
,
46 uint8_t jump
; /* We split the 2-byte jump field from the spec in two for convenience. */
50 uint32_t realmode_swtch
;
51 uint16_t start_sys_seg
;
52 uint16_t kernel_version
;
53 uint8_t type_of_loader
;
55 uint16_t setup_move_size
;
56 uint32_t code32_start
;
57 uint32_t ramdisk_image
;
58 uint32_t ramdisk_size
;
59 uint32_t bootsect_kludge
;
60 uint16_t heap_end_ptr
;
61 uint8_t ext_loader_ver
;
62 uint8_t ext_loader_type
;
63 uint32_t cmd_line_ptr
;
64 uint32_t initrd_addr_max
;
65 uint32_t kernel_alignment
;
66 uint8_t relocatable_kernel
;
67 uint8_t min_alignment
;
69 uint32_t cmdline_size
;
70 uint32_t hardware_subarch
;
71 uint64_t hardware_subarch_data
;
72 uint32_t payload_offset
;
73 uint32_t payload_length
;
75 uint64_t pref_address
;
77 uint32_t handover_offset
;
78 } _packed_ SetupHeader
;
80 /* We really only care about a few fields, but we still have to provide a full page otherwise. */
83 uint32_t ext_ramdisk_image
;
84 uint32_t ext_ramdisk_size
;
85 uint32_t ext_cmd_line_ptr
;
89 } _packed_ BootParams
;
90 assert_cc(offsetof(BootParams
, ext_ramdisk_image
) == 0x0C0);
91 assert_cc(sizeof(BootParams
) == 4096);
94 # define __regparm0__ __attribute__((regparm(0)))
99 typedef void (*handover_f
)(void *parent
, EFI_SYSTEM_TABLE
*table
, BootParams
*params
) __regparm0__
100 __attribute__((sysv_abi
));
102 static void linux_efi_handover(EFI_HANDLE parent
, uintptr_t kernel
, BootParams
*params
) {
105 kernel
+= (params
->hdr
.setup_sects
+ 1) * KERNEL_SECTOR_SIZE
; /* 32-bit entry address. */
107 /* Old kernels needs this set, while newer ones seem to ignore this. */
108 params
->hdr
.code32_start
= kernel
;
111 kernel
+= KERNEL_SECTOR_SIZE
; /* 64-bit entry address. */
114 kernel
+= params
->hdr
.handover_offset
; /* 32/64-bit EFI handover address. */
116 /* Note in EFI mixed mode this now points to the correct 32-bit handover entry point, allowing a 64-bit
117 * kernel to be booted from a 32-bit sd-stub. */
119 handover_f handover
= (handover_f
) kernel
;
120 handover(parent
, ST
, params
);
123 EFI_STATUS
linux_exec_efi_handover(
125 const char16_t
*cmdline
,
126 const void *linux_buffer
,
128 const void *initrd_buffer
,
129 size_t initrd_length
) {
132 assert(linux_buffer
);
133 assert(initrd_buffer
|| initrd_length
== 0);
135 if (linux_length
< sizeof(BootParams
))
136 return EFI_LOAD_ERROR
;
138 const BootParams
*image_params
= (const BootParams
*) linux_buffer
;
139 if (image_params
->hdr
.header
!= SETUP_MAGIC
|| image_params
->hdr
.boot_flag
!= BOOT_FLAG_MAGIC
)
140 return log_error_status(EFI_UNSUPPORTED
, "Unsupported kernel image.");
141 if (image_params
->hdr
.version
< SETUP_VERSION_2_11
)
142 return log_error_status(EFI_UNSUPPORTED
, "Kernel too old.");
143 if (!image_params
->hdr
.relocatable_kernel
)
144 return log_error_status(EFI_UNSUPPORTED
, "Kernel is not relocatable.");
146 /* The xloadflags were added in version 2.12+ of the boot protocol but the handover support predates
147 * that, so we cannot safety-check this for 2.11. */
148 if (image_params
->hdr
.version
>= SETUP_VERSION_2_12
&&
149 !FLAGS_SET(image_params
->hdr
.xloadflags
, XLF_EFI_HANDOVER
))
150 return log_error_status(EFI_UNSUPPORTED
, "Kernel does not support EFI handover protocol.");
152 bool can_4g
= image_params
->hdr
.version
>= SETUP_VERSION_2_12
&&
153 FLAGS_SET(image_params
->hdr
.xloadflags
, XLF_CAN_BE_LOADED_ABOVE_4G
);
155 /* There is no way to pass the high bits of code32_start. Newer kernels seems to handle this
156 * just fine, but older kernels will fail even if they otherwise have above 4G boot support. */
157 _cleanup_pages_ Pages linux_relocated
= {};
158 if (POINTER_TO_PHYSICAL_ADDRESS(linux_buffer
) + linux_length
> UINT32_MAX
) {
159 linux_relocated
= xmalloc_pages(
160 AllocateMaxAddress
, EfiLoaderCode
, EFI_SIZE_TO_PAGES(linux_length
), UINT32_MAX
);
161 linux_buffer
= memcpy(
162 PHYSICAL_ADDRESS_TO_POINTER(linux_relocated
.addr
), linux_buffer
, linux_length
);
165 _cleanup_pages_ Pages initrd_relocated
= {};
166 if (!can_4g
&& POINTER_TO_PHYSICAL_ADDRESS(initrd_buffer
) + initrd_length
> UINT32_MAX
) {
167 initrd_relocated
= xmalloc_pages(
168 AllocateMaxAddress
, EfiLoaderData
, EFI_SIZE_TO_PAGES(initrd_length
), UINT32_MAX
);
169 initrd_buffer
= memcpy(
170 PHYSICAL_ADDRESS_TO_POINTER(initrd_relocated
.addr
),
175 _cleanup_pages_ Pages boot_params_page
= xmalloc_pages(
176 can_4g
? AllocateAnyPages
: AllocateMaxAddress
,
178 EFI_SIZE_TO_PAGES(sizeof(BootParams
)),
179 UINT32_MAX
/* Below the 4G boundary */);
180 BootParams
*boot_params
= PHYSICAL_ADDRESS_TO_POINTER(boot_params_page
.addr
);
181 *boot_params
= (BootParams
){};
183 /* Setup size is determined by offset 0x0202 + byte value at offset 0x0201, which is the same as
184 * offset of the header field and the target from the jump field (which we split for this reason). */
185 memcpy(&boot_params
->hdr
,
187 offsetof(SetupHeader
, header
) + image_params
->hdr
.setup_size
);
189 boot_params
->hdr
.type_of_loader
= 0xff;
191 /* Spec says: For backwards compatibility, if the setup_sects field contains 0, the real value is 4. */
192 if (boot_params
->hdr
.setup_sects
== 0)
193 boot_params
->hdr
.setup_sects
= 4;
195 _cleanup_pages_ Pages cmdline_pages
= {};
197 size_t len
= MIN(strlen16(cmdline
), image_params
->hdr
.cmdline_size
);
199 cmdline_pages
= xmalloc_pages(
200 can_4g
? AllocateAnyPages
: AllocateMaxAddress
,
202 EFI_SIZE_TO_PAGES(len
+ 1),
205 /* Convert cmdline to ASCII. */
206 char *cmdline8
= PHYSICAL_ADDRESS_TO_POINTER(cmdline_pages
.addr
);
207 for (size_t i
= 0; i
< len
; i
++)
208 cmdline8
[i
] = cmdline
[i
] <= 0x7E ? cmdline
[i
] : ' ';
209 cmdline8
[len
] = '\0';
211 boot_params
->hdr
.cmd_line_ptr
= (uint32_t) cmdline_pages
.addr
;
212 boot_params
->ext_cmd_line_ptr
= cmdline_pages
.addr
>> 32;
213 assert(can_4g
|| cmdline_pages
.addr
<= CMDLINE_PTR_MAX
);
216 boot_params
->hdr
.ramdisk_image
= (uintptr_t) initrd_buffer
;
217 boot_params
->ext_ramdisk_image
= POINTER_TO_PHYSICAL_ADDRESS(initrd_buffer
) >> 32;
218 boot_params
->hdr
.ramdisk_size
= initrd_length
;
219 boot_params
->ext_ramdisk_size
= ((uint64_t) initrd_length
) >> 32;
222 linux_efi_handover(parent
, (uintptr_t) linux_buffer
, boot_params
);
223 return EFI_LOAD_ERROR
;