]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/boot/efi/stub.c
boot: Use typedef for PE structs
[thirdparty/systemd.git] / src / boot / efi / stub.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
0fa2cac4
KS
2
3#include <efi.h>
4#include <efilib.h>
5
845707aa 6#include "cpio.h"
33bc9b75 7#include "devicetree.h"
8110e144 8#include "disk.h"
37fa3690 9#include "graphics.h"
0fa2cac4 10#include "linux.h"
d4cbada2
MG
11#include "measure.h"
12#include "pe.h"
ce0f078f 13#include "secure-boot.h"
cf0fbc49
TA
14#include "splash.h"
15#include "util.h"
0fa2cac4
KS
16
17/* magic string to find in the binary image */
2ccd5986 18_used_ _section_(".sdmagic") static const char magic[] = "#### LoaderInfo: systemd-stub " GIT_VERSION " ####";
0fa2cac4 19
845707aa
LP
20static EFI_STATUS combine_initrd(
21 EFI_PHYSICAL_ADDRESS initrd_base, UINTN initrd_size,
70cd15e9 22 const void *credential_initrd, UINTN credential_initrd_size,
f3b6f333 23 const void *global_credential_initrd, UINTN global_credential_initrd_size,
70cd15e9 24 const void *sysext_initrd, UINTN sysext_initrd_size,
845707aa
LP
25 EFI_PHYSICAL_ADDRESS *ret_initrd_base, UINTN *ret_initrd_size) {
26
27 EFI_PHYSICAL_ADDRESS base = UINT32_MAX; /* allocate an area below the 32bit boundary for this */
28 EFI_STATUS err;
db4122d1 29 uint8_t *p;
845707aa
LP
30 UINTN n;
31
32 assert(ret_initrd_base);
33 assert(ret_initrd_size);
34
f3b6f333 35 /* Combines four initrds into one, by simple concatenation in memory */
845707aa 36
b4e7df4a 37 n = ALIGN4(initrd_size); /* main initrd might not be padded yet */
845707aa
LP
38 if (credential_initrd) {
39 if (n > UINTN_MAX - credential_initrd_size)
40 return EFI_OUT_OF_RESOURCES;
41
42 n += credential_initrd_size;
43 }
f3b6f333
AV
44 if (global_credential_initrd) {
45 if (n > UINTN_MAX - global_credential_initrd_size)
46 return EFI_OUT_OF_RESOURCES;
47
48 n += global_credential_initrd_size;
49 }
845707aa
LP
50 if (sysext_initrd) {
51 if (n > UINTN_MAX - sysext_initrd_size)
52 return EFI_OUT_OF_RESOURCES;
53
54 n += sysext_initrd_size;
55 }
56
12f32748 57 err = BS->AllocatePages(
845707aa
LP
58 AllocateMaxAddress,
59 EfiLoaderData,
60 EFI_SIZE_TO_PAGES(n),
61 &base);
2a5e4fe4 62 if (err != EFI_SUCCESS)
845707aa
LP
63 return log_error_status_stall(err, L"Failed to allocate space for combined initrd: %r", err);
64
a0a644be 65 p = PHYSICAL_ADDRESS_TO_POINTER(base);
845707aa
LP
66 if (initrd_base != 0) {
67 UINTN pad;
68
69 /* Order matters, the real initrd must come first, since it might include microcode updates
70 * which the kernel only looks for in the first cpio archive */
bbc1f2ea 71 memcpy(p, PHYSICAL_ADDRESS_TO_POINTER(initrd_base), initrd_size);
845707aa
LP
72 p += initrd_size;
73
b4e7df4a 74 pad = ALIGN4(initrd_size) - initrd_size;
845707aa 75 if (pad > 0) {
bbc1f2ea 76 memset(p, 0, pad);
845707aa
LP
77 p += pad;
78 }
79 }
80
81 if (credential_initrd) {
bbc1f2ea 82 memcpy(p, credential_initrd, credential_initrd_size);
845707aa
LP
83 p += credential_initrd_size;
84 }
85
f3b6f333 86 if (global_credential_initrd) {
bbc1f2ea 87 memcpy(p, global_credential_initrd, global_credential_initrd_size);
f3b6f333
AV
88 p += global_credential_initrd_size;
89 }
90
845707aa 91 if (sysext_initrd) {
bbc1f2ea 92 memcpy(p, sysext_initrd, sysext_initrd_size);
845707aa
LP
93 p += sysext_initrd_size;
94 }
95
db4122d1 96 assert((uint8_t*) PHYSICAL_ADDRESS_TO_POINTER(base) + n == p);
845707aa
LP
97
98 *ret_initrd_base = base;
99 *ret_initrd_size = n;
100
101 return EFI_SUCCESS;
102}
103
70cd15e9 104static void export_variables(EFI_LOADED_IMAGE *loaded_image) {
3639d1b0 105 char16_t uuid[37];
5a186322
LP
106
107 assert(loaded_image);
108
109 /* Export the device path this image is started from, if it's not set yet */
110 if (efivar_get_raw(LOADER_GUID, L"LoaderDevicePartUUID", NULL, NULL) != EFI_SUCCESS)
111 if (disk_get_part_uuid(loaded_image->DeviceHandle, uuid) == EFI_SUCCESS)
112 efivar_set(LOADER_GUID, L"LoaderDevicePartUUID", uuid, 0);
113
114 /* If LoaderImageIdentifier is not set, assume the image with this stub was loaded directly from the
115 * UEFI firmware without any boot loader, and hence set the LoaderImageIdentifier ourselves. Note
116 * that some boot chain loaders neither set LoaderImageIdentifier nor make FilePath available to us,
117 * in which case there's simple nothing to set for us. (The UEFI spec doesn't really say who's wrong
118 * here, i.e. whether FilePath may be NULL or not, hence handle this gracefully and check if FilePath
119 * is non-NULL explicitly.) */
120 if (efivar_get_raw(LOADER_GUID, L"LoaderImageIdentifier", NULL, NULL) != EFI_SUCCESS &&
121 loaded_image->FilePath) {
3639d1b0 122 _cleanup_free_ char16_t *s = NULL;
5a186322
LP
123
124 s = DevicePathToStr(loaded_image->FilePath);
9f048123
JJ
125 if (s)
126 efivar_set(LOADER_GUID, L"LoaderImageIdentifier", s, 0);
127 else
128 log_oom();
5a186322
LP
129 }
130
131 /* if LoaderFirmwareInfo is not set, let's set it */
132 if (efivar_get_raw(LOADER_GUID, L"LoaderFirmwareInfo", NULL, NULL) != EFI_SUCCESS) {
3639d1b0 133 _cleanup_free_ char16_t *s = NULL;
ec4106af 134 s = xpool_print(L"%s %u.%02u", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
0a15a824 135 efivar_set(LOADER_GUID, L"LoaderFirmwareInfo", s, 0);
5a186322
LP
136 }
137
138 /* ditto for LoaderFirmwareType */
139 if (efivar_get_raw(LOADER_GUID, L"LoaderFirmwareType", NULL, NULL) != EFI_SUCCESS) {
3639d1b0 140 _cleanup_free_ char16_t *s = NULL;
ec4106af 141 s = xpool_print(L"UEFI %u.%02u", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
0a15a824 142 efivar_set(LOADER_GUID, L"LoaderFirmwareType", s, 0);
5a186322
LP
143 }
144
145 /* add StubInfo */
146 if (efivar_get_raw(LOADER_GUID, L"StubInfo", NULL, NULL) != EFI_SUCCESS)
147 efivar_set(LOADER_GUID, L"StubInfo", L"systemd-stub " GIT_VERSION, 0);
148}
149
0fa2cac4 150EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
e41d3d89
LP
151
152 enum {
153 SECTION_CMDLINE,
154 SECTION_LINUX,
155 SECTION_INITRD,
156 SECTION_SPLASH,
33bc9b75 157 SECTION_DTB,
e41d3d89
LP
158 _SECTION_MAX,
159 };
160
07d0fde4
JJ
161 static const char * const sections[_SECTION_MAX + 1] = {
162 [SECTION_CMDLINE] = ".cmdline",
163 [SECTION_LINUX] = ".linux",
164 [SECTION_INITRD] = ".initrd",
165 [SECTION_SPLASH] = ".splash",
166 [SECTION_DTB] = ".dtb",
e41d3d89 167 NULL,
0fa2cac4 168 };
5b5d365d 169
33bc9b75 170 UINTN cmdline_len = 0, linux_size, initrd_size, dt_size;
f3b6f333
AV
171 UINTN credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0;
172 _cleanup_freepool_ void *credential_initrd = NULL, *global_credential_initrd = NULL;
173 _cleanup_freepool_ void *sysext_initrd = NULL;
33bc9b75
MR
174 EFI_PHYSICAL_ADDRESS linux_base, initrd_base, dt_base;
175 _cleanup_(devicetree_cleanup) struct devicetree_state dt_state = {};
845707aa 176 EFI_LOADED_IMAGE *loaded_image;
e41d3d89
LP
177 UINTN addrs[_SECTION_MAX] = {};
178 UINTN szs[_SECTION_MAX] = {};
07d0fde4
JJ
179 char *cmdline = NULL;
180 _cleanup_free_ char *cmdline_owned = NULL;
0fa2cac4
KS
181 EFI_STATUS err;
182
183 InitializeLib(image, sys_table);
948d085e
JJ
184 debug_hook(L"systemd-stub");
185 /* Uncomment the next line if you need to wait for debugger. */
186 // debug_break();
0fa2cac4 187
12f32748
JJ
188 err = BS->OpenProtocol(
189 image,
190 &LoadedImageProtocol,
191 (void **)&loaded_image,
192 image,
193 NULL,
194 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
2a5e4fe4 195 if (err != EFI_SUCCESS)
8aba0eec 196 return log_error_status_stall(err, L"Error getting a LoadedImageProtocol handle: %r", err);
0fa2cac4 197
6028dd6d 198 err = pe_memory_locate_sections(loaded_image->ImageBase, sections, addrs, szs);
2a5e4fe4
JJ
199 if (err != EFI_SUCCESS || szs[SECTION_LINUX] == 0) {
200 if (err == EFI_SUCCESS)
65ff3d26 201 err = EFI_NOT_FOUND;
8aba0eec 202 return log_error_status_stall(err, L"Unable to locate embedded .linux section: %r", err);
65ff3d26 203 }
0fa2cac4 204
94b81afb 205 /* Show splash screen as early as possible */
db4122d1 206 graphics_splash((const uint8_t*) loaded_image->ImageBase + addrs[SECTION_SPLASH], szs[SECTION_SPLASH], NULL);
94b81afb 207
e41d3d89 208 if (szs[SECTION_CMDLINE] > 0) {
07d0fde4 209 cmdline = (char *) loaded_image->ImageBase + addrs[SECTION_CMDLINE];
e41d3d89
LP
210 cmdline_len = szs[SECTION_CMDLINE];
211 }
0fa2cac4 212
53a20455 213 /* if we are not in secure boot mode, or none was provided, accept a custom command line and replace the built-in one */
ce0f078f 214 if ((!secure_boot_enabled() || cmdline_len == 0) && loaded_image->LoadOptionsSize > 0 &&
3639d1b0 215 *(char16_t *) loaded_image->LoadOptions > 0x1F) {
07d0fde4 216 cmdline_len = (loaded_image->LoadOptionsSize / sizeof(char16_t)) * sizeof(char);
0af26643 217 cmdline = cmdline_owned = xmalloc(cmdline_len);
9f048123 218
258f0970 219 for (UINTN i = 0; i < cmdline_len; i++)
3639d1b0 220 cmdline[i] = ((char16_t *) loaded_image->LoadOptions)[i];
92ed3bb4 221
e6e24af5
LP
222 /* Let's measure the passed kernel command line into the TPM. Note that this possibly
223 * duplicates what we already did in the boot menu, if that was already used. However, since
224 * we want the boot menu to support an EFI binary, and want to this stub to be usable from
225 * any boot menu, let's measure things anyway. */
70cd15e9 226 (void) tpm_log_load_options(loaded_image->LoadOptions);
0fa2cac4
KS
227 }
228
5a186322 229 export_variables(loaded_image);
1aa15def 230
70cd15e9 231 (void) pack_cpio(loaded_image,
f3b6f333 232 NULL,
845707aa 233 L".cred",
07d0fde4 234 ".extra/credentials",
845707aa
LP
235 /* dir_mode= */ 0500,
236 /* access_mode= */ 0400,
db4122d1 237 /* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_KERNEL_PARAMETERS, TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT },
4d32507f 238 /* n_tpm_pcr= */ 2,
845707aa
LP
239 L"Credentials initrd",
240 &credential_initrd,
241 &credential_initrd_size);
242
70cd15e9 243 (void) pack_cpio(loaded_image,
f3b6f333
AV
244 L"\\loader\\credentials",
245 L".cred",
07d0fde4 246 ".extra/global_credentials",
f3b6f333
AV
247 /* dir_mode= */ 0500,
248 /* access_mode= */ 0400,
db4122d1 249 /* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_KERNEL_PARAMETERS, TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT },
4d32507f 250 /* n_tpm_pcr= */ 2,
f3b6f333
AV
251 L"Global credentials initrd",
252 &global_credential_initrd,
253 &global_credential_initrd_size);
254
255 (void) pack_cpio(loaded_image,
256 NULL,
845707aa 257 L".raw",
07d0fde4 258 ".extra/sysext",
845707aa
LP
259 /* dir_mode= */ 0555,
260 /* access_mode= */ 0444,
db4122d1 261 /* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_INITRD },
4d32507f 262 /* n_tpm_pcr= */ 1,
845707aa
LP
263 L"System extension initrd",
264 &sysext_initrd,
265 &sysext_initrd_size);
266
dc467928 267 linux_size = szs[SECTION_LINUX];
a0a644be 268 linux_base = POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[SECTION_LINUX];
5b5d365d
LP
269
270 initrd_size = szs[SECTION_INITRD];
a0a644be 271 initrd_base = initrd_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[SECTION_INITRD] : 0;
37fa3690 272
33bc9b75
MR
273 dt_size = szs[SECTION_DTB];
274 dt_base = dt_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[SECTION_DTB] : 0;
275
f3b6f333 276 if (credential_initrd || global_credential_initrd || sysext_initrd) {
845707aa
LP
277 /* If we have generated initrds dynamically, let's combine them with the built-in initrd. */
278 err = combine_initrd(
279 initrd_base, initrd_size,
280 credential_initrd, credential_initrd_size,
f3b6f333 281 global_credential_initrd, global_credential_initrd_size,
845707aa
LP
282 sysext_initrd, sysext_initrd_size,
283 &initrd_base, &initrd_size);
2a5e4fe4 284 if (err != EFI_SUCCESS)
845707aa
LP
285 return err;
286
287 /* Given these might be large let's free them explicitly, quickly. */
f3b6f333
AV
288 credential_initrd = mfree(credential_initrd);
289 global_credential_initrd = mfree(global_credential_initrd);
290 sysext_initrd = mfree(sysext_initrd);
845707aa 291 }
0fa2cac4 292
33bc9b75
MR
293 if (dt_size > 0) {
294 err = devicetree_install_from_memory(
295 &dt_state, PHYSICAL_ADDRESS_TO_POINTER(dt_base), dt_size);
2a5e4fe4 296 if (err != EFI_SUCCESS)
33bc9b75
MR
297 log_error_stall(L"Error loading embedded devicetree: %r", err);
298 }
299
a6089431 300 err = linux_exec(image, cmdline, cmdline_len,
dc467928 301 PHYSICAL_ADDRESS_TO_POINTER(linux_base), linux_size,
a6089431 302 PHYSICAL_ADDRESS_TO_POINTER(initrd_base), initrd_size);
e5a1b8f9 303 graphics_mode(false);
8aba0eec 304 return log_error_status_stall(err, L"Execution of embedded linux image failed: %r", err);
0fa2cac4 305}