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