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