]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/boot/efi/linux_x86.c
boot: Use char
[thirdparty/systemd.git] / src / boot / efi / linux_x86.c
CommitLineData
dc467928
MR
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
23struct setup_header {
db4122d1
JJ
24 uint8_t setup_sects;
25 uint16_t root_flags;
26 uint32_t syssize;
27 uint16_t ram_size;
28 uint16_t vid_mode;
29 uint16_t root_dev;
30 uint16_t boot_flag;
31 uint16_t jump;
32 uint32_t header;
33 uint16_t version;
34 uint32_t realmode_swtch;
35 uint16_t start_sys_seg;
36 uint16_t kernel_version;
37 uint8_t type_of_loader;
38 uint8_t loadflags;
39 uint16_t setup_move_size;
40 uint32_t code32_start;
41 uint32_t ramdisk_image;
42 uint32_t ramdisk_size;
43 uint32_t bootsect_kludge;
44 uint16_t heap_end_ptr;
45 uint8_t ext_loader_ver;
46 uint8_t ext_loader_type;
47 uint32_t cmd_line_ptr;
48 uint32_t initrd_addr_max;
49 uint32_t kernel_alignment;
50 uint8_t relocatable_kernel;
51 uint8_t min_alignment;
52 uint16_t xloadflags;
53 uint32_t cmdline_size;
54 uint32_t hardware_subarch;
55 uint64_t hardware_subarch_data;
56 uint32_t payload_offset;
57 uint32_t payload_length;
58 uint64_t setup_data;
59 uint64_t pref_address;
60 uint32_t init_size;
61 uint32_t handover_offset;
dc467928
MR
62} _packed_;
63
64/* adapted from linux' bootparam.h */
65struct boot_params {
db4122d1
JJ
66 uint8_t screen_info[64]; // was: struct screen_info
67 uint8_t apm_bios_info[20]; // was: struct apm_bios_info
68 uint8_t _pad2[4];
69 uint64_t tboot_addr;
70 uint8_t ist_info[16]; // was: struct ist_info
71 uint8_t _pad3[16];
72 uint8_t hd0_info[16];
73 uint8_t hd1_info[16];
74 uint8_t sys_desc_table[16]; // was: struct sys_desc_table
75 uint8_t olpc_ofw_header[16]; // was: struct olpc_ofw_header
76 uint32_t ext_ramdisk_image;
77 uint32_t ext_ramdisk_size;
78 uint32_t ext_cmd_line_ptr;
79 uint8_t _pad4[116];
80 uint8_t edid_info[128]; // was: struct edid_info
81 uint8_t efi_info[32]; // was: struct efi_info
82 uint32_t alt_mem_k;
83 uint32_t scratch;
84 uint8_t e820_entries;
85 uint8_t eddbuf_entries;
86 uint8_t edd_mbr_sig_buf_entries;
87 uint8_t kbd_status;
88 uint8_t secure_boot;
89 uint8_t _pad5[2];
90 uint8_t sentinel;
91 uint8_t _pad6[1];
dc467928 92 struct setup_header hdr;
db4122d1
JJ
93 uint8_t _pad7[0x290-0x1f1-sizeof(struct setup_header)];
94 uint32_t edd_mbr_sig_buffer[16]; // was: edd_mbr_sig_buffer[EDD_MBR_SIG_MAX]
95 uint8_t e820_table[20*128]; // was: struct boot_e820_entry e820_table[E820_MAX_ENTRIES_ZEROPAGE]
96 uint8_t _pad8[48];
97 uint8_t eddbuf[6*82]; // was: struct edd_info eddbuf[EDDMAXNR]
98 uint8_t _pad9[276];
dc467928
MR
99} _packed_;
100
101#ifdef __i386__
102#define __regparm0__ __attribute__((regparm(0)))
103#else
104#define __regparm0__
105#endif
106
70cd15e9 107typedef void(*handover_f)(void *image, EFI_SYSTEM_TABLE *table, struct boot_params *params) __regparm0__;
dc467928 108
70cd15e9 109static void linux_efi_handover(EFI_HANDLE image, struct boot_params *params) {
dc467928
MR
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
123EFI_STATUS linux_exec(
124 EFI_HANDLE image,
07d0fde4 125 const char *cmdline, UINTN cmdline_len,
70cd15e9
JJ
126 const void *linux_buffer, UINTN linux_length,
127 const void *initrd_buffer, UINTN initrd_length) {
dc467928
MR
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;
db4122d1 133 uint8_t setup_sectors;
dc467928
MR
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
2fffe2ed
ZJS
141 if (linux_length < sizeof(struct boot_params))
142 return EFI_LOAD_ERROR;
143
dc467928
MR
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 */
12f32748 153 err = BS->AllocatePages(
dc467928
MR
154 AllocateMaxAddress,
155 EfiLoaderData,
156 EFI_SIZE_TO_PAGES(0x4000),
157 &addr);
2a5e4fe4 158 if (err != EFI_SUCCESS)
dc467928
MR
159 return err;
160
161 boot_params = (struct boot_params *) PHYSICAL_ADDRESS_TO_POINTER(addr);
bbc1f2ea 162 memset(boot_params, 0, 0x4000);
dc467928
MR
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;
db4122d1 166 boot_params->hdr.code32_start = (uint32_t) POINTER_TO_PHYSICAL_ADDRESS(linux_buffer) + (setup_sectors + 1) * 512;
dc467928
MR
167
168 if (cmdline) {
169 addr = 0xA0000;
170
12f32748 171 err = BS->AllocatePages(
dc467928
MR
172 AllocateMaxAddress,
173 EfiLoaderData,
174 EFI_SIZE_TO_PAGES(cmdline_len + 1),
175 &addr);
2a5e4fe4 176 if (err != EFI_SUCCESS)
dc467928
MR
177 return err;
178
bbc1f2ea 179 memcpy(PHYSICAL_ADDRESS_TO_POINTER(addr), cmdline, cmdline_len);
07d0fde4 180 ((char *) PHYSICAL_ADDRESS_TO_POINTER(addr))[cmdline_len] = 0;
db4122d1 181 boot_params->hdr.cmd_line_ptr = (uint32_t) addr;
dc467928
MR
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 */
db4122d1
JJ
190 boot_params->hdr.ramdisk_image = (uint32_t) POINTER_TO_PHYSICAL_ADDRESS(initrd_buffer);
191 boot_params->hdr.ramdisk_size = (uint32_t) initrd_length;
dc467928
MR
192
193 /* register LINUX_INITRD_MEDIA_GUID */
194 err = initrd_register(initrd_buffer, initrd_length, &initrd_handle);
2a5e4fe4 195 if (err != EFI_SUCCESS)
dc467928
MR
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}