1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 /*
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.
9 *
10 * see https://www.kernel.org/doc/html/latest/x86/boot.html
11 */
12
13 #include <efi.h>
14 #include <efilib.h>
15
16 #include "initrd.h"
17 #include "linux.h"
18 #include "macro-fundamental.h"
19 #include "util.h"
20
21 #define SETUP_MAGIC 0x53726448 /* "HdrS" */
22
23 struct setup_header {
24 UINT8 setup_sects;
25 UINT16 root_flags;
26 UINT32 syssize;
27 UINT16 ram_size;
28 UINT16 vid_mode;
29 UINT16 root_dev;
30 UINT16 boot_flag;
31 UINT16 jump;
32 UINT32 header;
33 UINT16 version;
34 UINT32 realmode_swtch;
35 UINT16 start_sys_seg;
36 UINT16 kernel_version;
37 UINT8 type_of_loader;
38 UINT8 loadflags;
39 UINT16 setup_move_size;
40 UINT32 code32_start;
41 UINT32 ramdisk_image;
42 UINT32 ramdisk_size;
43 UINT32 bootsect_kludge;
44 UINT16 heap_end_ptr;
45 UINT8 ext_loader_ver;
46 UINT8 ext_loader_type;
47 UINT32 cmd_line_ptr;
48 UINT32 initrd_addr_max;
49 UINT32 kernel_alignment;
50 UINT8 relocatable_kernel;
51 UINT8 min_alignment;
52 UINT16 xloadflags;
53 UINT32 cmdline_size;
54 UINT32 hardware_subarch;
55 UINT64 hardware_subarch_data;
56 UINT32 payload_offset;
57 UINT32 payload_length;
58 UINT64 setup_data;
59 UINT64 pref_address;
60 UINT32 init_size;
61 UINT32 handover_offset;
62 } _packed_;
63
64 /* adapted from linux' bootparam.h */
65 struct boot_params {
66 UINT8 screen_info[64]; // was: struct screen_info
67 UINT8 apm_bios_info[20]; // was: struct apm_bios_info
68 UINT8 _pad2[4];
69 UINT64 tboot_addr;
70 UINT8 ist_info[16]; // was: struct ist_info
71 UINT8 _pad3[16];
72 UINT8 hd0_info[16];
73 UINT8 hd1_info[16];
74 UINT8 sys_desc_table[16]; // was: struct sys_desc_table
75 UINT8 olpc_ofw_header[16]; // was: struct olpc_ofw_header
76 UINT32 ext_ramdisk_image;
77 UINT32 ext_ramdisk_size;
78 UINT32 ext_cmd_line_ptr;
79 UINT8 _pad4[116];
80 UINT8 edid_info[128]; // was: struct edid_info
81 UINT8 efi_info[32]; // was: struct efi_info
82 UINT32 alt_mem_k;
83 UINT32 scratch;
84 UINT8 e820_entries;
85 UINT8 eddbuf_entries;
86 UINT8 edd_mbr_sig_buf_entries;
87 UINT8 kbd_status;
88 UINT8 secure_boot;
89 UINT8 _pad5[2];
90 UINT8 sentinel;
91 UINT8 _pad6[1];
92 struct setup_header hdr;
93 UINT8 _pad7[0x290-0x1f1-sizeof(struct setup_header)];
94 UINT32 edd_mbr_sig_buffer[16]; // was: edd_mbr_sig_buffer[EDD_MBR_SIG_MAX]
95 UINT8 e820_table[20*128]; // was: struct boot_e820_entry e820_table[E820_MAX_ENTRIES_ZEROPAGE]
96 UINT8 _pad8[48];
97 UINT8 eddbuf[6*82]; // was: struct edd_info eddbuf[EDDMAXNR]
98 UINT8 _pad9[276];
99 } _packed_;
100
101 #ifdef __i386__
102 #define __regparm0__ __attribute__((regparm(0)))
103 #else
104 #define __regparm0__
105 #endif
106
107 typedef void(*handover_f)(void *image, EFI_SYSTEM_TABLE *table, struct boot_params *params) __regparm0__;
108
linux_efi_handover(EFI_HANDLE image,struct boot_params * params)109 static void linux_efi_handover(EFI_HANDLE image, struct boot_params *params) {
110 handover_f handover;
111 UINTN start = (UINTN)params->hdr.code32_start;
112
113 assert(params);
114
115 #ifdef __x86_64__
116 asm volatile ("cli");
117 start += 512;
118 #endif
119 handover = (handover_f)(start + params->hdr.handover_offset);
120 handover(image, ST, params);
121 }
122
linux_exec(EFI_HANDLE image,const CHAR8 * cmdline,UINTN cmdline_len,const void * linux_buffer,UINTN linux_length,const void * initrd_buffer,UINTN initrd_length)123 EFI_STATUS linux_exec(
124 EFI_HANDLE image,
125 const CHAR8 *cmdline, UINTN cmdline_len,
126 const void *linux_buffer, UINTN linux_length,
127 const void *initrd_buffer, UINTN initrd_length) {
128
129 const struct boot_params *image_params;
130 struct boot_params *boot_params;
131 EFI_HANDLE initrd_handle = NULL;
132 EFI_PHYSICAL_ADDRESS addr;
133 UINT8 setup_sectors;
134 EFI_STATUS err;
135
136 assert(image);
137 assert(cmdline || cmdline_len == 0);
138 assert(linux_buffer);
139 assert(initrd_buffer || initrd_length == 0);
140
141 if (linux_length < sizeof(struct boot_params))
142 return EFI_LOAD_ERROR;
143
144 image_params = (const struct boot_params *) linux_buffer;
145
146 if (image_params->hdr.boot_flag != 0xAA55 ||
147 image_params->hdr.header != SETUP_MAGIC ||
148 image_params->hdr.version < 0x20b ||
149 !image_params->hdr.relocatable_kernel)
150 return EFI_LOAD_ERROR;
151
152 addr = UINT32_MAX; /* Below the 32bit boundary */
153 err = BS->AllocatePages(
154 AllocateMaxAddress,
155 EfiLoaderData,
156 EFI_SIZE_TO_PAGES(0x4000),
157 &addr);
158 if (EFI_ERROR(err))
159 return err;
160
161 boot_params = (struct boot_params *) PHYSICAL_ADDRESS_TO_POINTER(addr);
162 ZeroMem(boot_params, 0x4000);
163 boot_params->hdr = image_params->hdr;
164 boot_params->hdr.type_of_loader = 0xff;
165 setup_sectors = image_params->hdr.setup_sects > 0 ? image_params->hdr.setup_sects : 4;
166 boot_params->hdr.code32_start = (UINT32) POINTER_TO_PHYSICAL_ADDRESS(linux_buffer) + (setup_sectors + 1) * 512;
167
168 if (cmdline) {
169 addr = 0xA0000;
170
171 err = BS->AllocatePages(
172 AllocateMaxAddress,
173 EfiLoaderData,
174 EFI_SIZE_TO_PAGES(cmdline_len + 1),
175 &addr);
176 if (EFI_ERROR(err))
177 return err;
178
179 CopyMem(PHYSICAL_ADDRESS_TO_POINTER(addr), cmdline, cmdline_len);
180 ((CHAR8 *) PHYSICAL_ADDRESS_TO_POINTER(addr))[cmdline_len] = 0;
181 boot_params->hdr.cmd_line_ptr = (UINT32) addr;
182 }
183
184 /* Providing the initrd via LINUX_INITRD_MEDIA_GUID is only supported by Linux 5.8+ (5.7+ on ARM64).
185 Until supported kernels become more established, we continue to set ramdisk in the handover struct.
186 This value is overridden by kernels that support LINUX_INITRD_MEDIA_GUID.
187 If you need to know which protocol was used by the kernel, pass "efi=debug" to the kernel,
188 this will print a line when InitrdMediaGuid was successfully used to load the initrd.
189 */
190 boot_params->hdr.ramdisk_image = (UINT32) POINTER_TO_PHYSICAL_ADDRESS(initrd_buffer);
191 boot_params->hdr.ramdisk_size = (UINT32) initrd_length;
192
193 /* register LINUX_INITRD_MEDIA_GUID */
194 err = initrd_register(initrd_buffer, initrd_length, &initrd_handle);
195 if (EFI_ERROR(err))
196 return err;
197 linux_efi_handover(image, boot_params);
198 (void) initrd_unregister(initrd_handle);
199 initrd_handle = NULL;
200 return EFI_LOAD_ERROR;
201 }
202