]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/boot/efi/stub.c
stub: add new special PE sections ".pcrsig" and ".pcrpkey" in unified kernels
[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 14#include "splash.h"
aa323c09 15#include "tpm-pcr.h"
cf0fbc49 16#include "util.h"
0fa2cac4
KS
17
18/* magic string to find in the binary image */
2ccd5986 19_used_ _section_(".sdmagic") static const char magic[] = "#### LoaderInfo: systemd-stub " GIT_VERSION " ####";
0fa2cac4 20
845707aa
LP
21static EFI_STATUS combine_initrd(
22 EFI_PHYSICAL_ADDRESS initrd_base, UINTN initrd_size,
70cd15e9 23 const void *credential_initrd, UINTN credential_initrd_size,
f3b6f333 24 const void *global_credential_initrd, UINTN global_credential_initrd_size,
70cd15e9 25 const void *sysext_initrd, UINTN sysext_initrd_size,
845707aa
LP
26 EFI_PHYSICAL_ADDRESS *ret_initrd_base, UINTN *ret_initrd_size) {
27
28 EFI_PHYSICAL_ADDRESS base = UINT32_MAX; /* allocate an area below the 32bit boundary for this */
29 EFI_STATUS err;
db4122d1 30 uint8_t *p;
845707aa
LP
31 UINTN n;
32
33 assert(ret_initrd_base);
34 assert(ret_initrd_size);
35
f3b6f333 36 /* Combines four initrds into one, by simple concatenation in memory */
845707aa 37
b4e7df4a 38 n = ALIGN4(initrd_size); /* main initrd might not be padded yet */
845707aa
LP
39 if (credential_initrd) {
40 if (n > UINTN_MAX - credential_initrd_size)
41 return EFI_OUT_OF_RESOURCES;
42
43 n += credential_initrd_size;
44 }
f3b6f333
AV
45 if (global_credential_initrd) {
46 if (n > UINTN_MAX - global_credential_initrd_size)
47 return EFI_OUT_OF_RESOURCES;
48
49 n += global_credential_initrd_size;
50 }
845707aa
LP
51 if (sysext_initrd) {
52 if (n > UINTN_MAX - sysext_initrd_size)
53 return EFI_OUT_OF_RESOURCES;
54
55 n += sysext_initrd_size;
56 }
57
12f32748 58 err = BS->AllocatePages(
845707aa
LP
59 AllocateMaxAddress,
60 EfiLoaderData,
61 EFI_SIZE_TO_PAGES(n),
62 &base);
2a5e4fe4 63 if (err != EFI_SUCCESS)
845707aa
LP
64 return log_error_status_stall(err, L"Failed to allocate space for combined initrd: %r", err);
65
a0a644be 66 p = PHYSICAL_ADDRESS_TO_POINTER(base);
845707aa
LP
67 if (initrd_base != 0) {
68 UINTN pad;
69
70 /* Order matters, the real initrd must come first, since it might include microcode updates
71 * which the kernel only looks for in the first cpio archive */
515581d6 72 p = mempcpy(p, PHYSICAL_ADDRESS_TO_POINTER(initrd_base), initrd_size);
845707aa 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
515581d6
JJ
81 if (credential_initrd)
82 p = mempcpy(p, credential_initrd, credential_initrd_size);
83 if (global_credential_initrd)
84 p = mempcpy(p, global_credential_initrd, global_credential_initrd_size);
85 if (sysext_initrd)
86 p = mempcpy(p, sysext_initrd, sysext_initrd_size);
845707aa 87
db4122d1 88 assert((uint8_t*) PHYSICAL_ADDRESS_TO_POINTER(base) + n == p);
845707aa
LP
89
90 *ret_initrd_base = base;
91 *ret_initrd_size = n;
92
93 return EFI_SUCCESS;
94}
95
b30a43df 96static void export_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) {
46d33672
LP
97 static const uint64_t stub_features =
98 EFI_STUB_FEATURE_REPORT_BOOT_PARTITION | /* We set LoaderDevicePartUUID */
99 EFI_STUB_FEATURE_PICK_UP_CREDENTIALS | /* We pick up credentials from the boot partition */
100 EFI_STUB_FEATURE_PICK_UP_SYSEXTS | /* We pick up system extensions from the boot partition */
101 EFI_STUB_FEATURE_THREE_PCRS | /* We can measure kernel image, parameters and sysext */
102 0;
103
3639d1b0 104 char16_t uuid[37];
5a186322
LP
105
106 assert(loaded_image);
107
108 /* Export the device path this image is started from, if it's not set yet */
109 if (efivar_get_raw(LOADER_GUID, L"LoaderDevicePartUUID", NULL, NULL) != EFI_SUCCESS)
110 if (disk_get_part_uuid(loaded_image->DeviceHandle, uuid) == EFI_SUCCESS)
111 efivar_set(LOADER_GUID, L"LoaderDevicePartUUID", uuid, 0);
112
113 /* If LoaderImageIdentifier is not set, assume the image with this stub was loaded directly from the
114 * UEFI firmware without any boot loader, and hence set the LoaderImageIdentifier ourselves. Note
115 * that some boot chain loaders neither set LoaderImageIdentifier nor make FilePath available to us,
116 * in which case there's simple nothing to set for us. (The UEFI spec doesn't really say who's wrong
117 * here, i.e. whether FilePath may be NULL or not, hence handle this gracefully and check if FilePath
118 * is non-NULL explicitly.) */
119 if (efivar_get_raw(LOADER_GUID, L"LoaderImageIdentifier", NULL, NULL) != EFI_SUCCESS &&
120 loaded_image->FilePath) {
3639d1b0 121 _cleanup_free_ char16_t *s = NULL;
5a186322
LP
122
123 s = DevicePathToStr(loaded_image->FilePath);
9f048123
JJ
124 if (s)
125 efivar_set(LOADER_GUID, L"LoaderImageIdentifier", s, 0);
126 else
127 log_oom();
5a186322
LP
128 }
129
130 /* if LoaderFirmwareInfo is not set, let's set it */
131 if (efivar_get_raw(LOADER_GUID, L"LoaderFirmwareInfo", NULL, NULL) != EFI_SUCCESS) {
3639d1b0 132 _cleanup_free_ char16_t *s = NULL;
ec4106af 133 s = xpool_print(L"%s %u.%02u", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
0a15a824 134 efivar_set(LOADER_GUID, L"LoaderFirmwareInfo", s, 0);
5a186322
LP
135 }
136
137 /* ditto for LoaderFirmwareType */
138 if (efivar_get_raw(LOADER_GUID, L"LoaderFirmwareType", NULL, NULL) != EFI_SUCCESS) {
3639d1b0 139 _cleanup_free_ char16_t *s = NULL;
ec4106af 140 s = xpool_print(L"UEFI %u.%02u", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
0a15a824 141 efivar_set(LOADER_GUID, L"LoaderFirmwareType", s, 0);
5a186322
LP
142 }
143
46d33672 144
24120e40
LP
145 /* add StubInfo (this is one is owned by the stub, hence we unconditionally override this with our
146 * own data) */
147 (void) efivar_set(LOADER_GUID, L"StubInfo", L"systemd-stub " GIT_VERSION, 0);
46d33672
LP
148
149 (void) efivar_set_uint64_le(LOADER_GUID, L"StubFeatures", stub_features, 0);
5a186322
LP
150}
151
0fa2cac4 152EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
33bc9b75 153 UINTN cmdline_len = 0, linux_size, initrd_size, dt_size;
f3b6f333 154 UINTN credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0;
93521e55
JJ
155 _cleanup_free_ void *credential_initrd = NULL, *global_credential_initrd = NULL;
156 _cleanup_free_ void *sysext_initrd = NULL;
33bc9b75
MR
157 EFI_PHYSICAL_ADDRESS linux_base, initrd_base, dt_base;
158 _cleanup_(devicetree_cleanup) struct devicetree_state dt_state = {};
b30a43df 159 EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
6017eee9 160 UINTN addrs[_UNIFIED_SECTION_MAX] = {}, szs[_UNIFIED_SECTION_MAX] = {};
07d0fde4
JJ
161 char *cmdline = NULL;
162 _cleanup_free_ char *cmdline_owned = NULL;
72c97c19 163 int sections_measured = -1, parameters_measured = -1;
de7ad6d4 164 bool sysext_measured = false, m;
0fa2cac4
KS
165 EFI_STATUS err;
166
167 InitializeLib(image, sys_table);
948d085e
JJ
168 debug_hook(L"systemd-stub");
169 /* Uncomment the next line if you need to wait for debugger. */
170 // debug_break();
0fa2cac4 171
12f32748
JJ
172 err = BS->OpenProtocol(
173 image,
174 &LoadedImageProtocol,
175 (void **)&loaded_image,
176 image,
177 NULL,
178 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
2a5e4fe4 179 if (err != EFI_SUCCESS)
8aba0eec 180 return log_error_status_stall(err, L"Error getting a LoadedImageProtocol handle: %r", err);
0fa2cac4 181
6017eee9
LP
182 err = pe_memory_locate_sections(loaded_image->ImageBase, unified_sections, addrs, szs);
183 if (err != EFI_SUCCESS || szs[UNIFIED_SECTION_LINUX] == 0) {
2a5e4fe4 184 if (err == EFI_SUCCESS)
65ff3d26 185 err = EFI_NOT_FOUND;
8aba0eec 186 return log_error_status_stall(err, L"Unable to locate embedded .linux section: %r", err);
65ff3d26 187 }
0fa2cac4 188
72c97c19
LP
189 /* Measure all "payload" of this PE image into a separate PCR (i.e. where nothing else is written
190 * into so far), so that we have one PCR that we can nicely write policies against because it
191 * contains all static data of this image, and thus can be easily be pre-calculated. */
6017eee9 192 for (UnifiedSection section = 0; section < _UNIFIED_SECTION_MAX; section++) {
df7ee6f8
LP
193
194 if (!unified_section_measure(section)) /* shall not measure? */
195 continue;
72c97c19
LP
196
197 if (szs[section] == 0) /* not found */
198 continue;
199
df7ee6f8
LP
200 m = false;
201
72c97c19
LP
202 /* First measure the name of the section */
203 (void) tpm_log_event_ascii(
204 TPM_PCR_INDEX_KERNEL_IMAGE,
6017eee9
LP
205 POINTER_TO_PHYSICAL_ADDRESS(unified_sections[section]),
206 strsize8(unified_sections[section]), /* including NUL byte */
207 unified_sections[section],
72c97c19
LP
208 &m);
209
210 sections_measured = sections_measured < 0 ? m : (sections_measured && m);
211
212 /* Then measure the data of the section */
213 (void) tpm_log_event_ascii(
214 TPM_PCR_INDEX_KERNEL_IMAGE,
215 POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[section],
216 szs[section],
6017eee9 217 unified_sections[section],
72c97c19
LP
218 &m);
219
220 sections_measured = sections_measured < 0 ? m : (sections_measured && m);
221 }
222
223 /* After we are done, set an EFI variable that tells userspace this was done successfully, and encode
224 * in it which PCR was used. */
225 if (sections_measured > 0)
226 (void) efivar_set_uint_string(LOADER_GUID, L"StubPcrKernelImage", TPM_PCR_INDEX_KERNEL_IMAGE, 0);
227
94b81afb 228 /* Show splash screen as early as possible */
6017eee9 229 graphics_splash((const uint8_t*) loaded_image->ImageBase + addrs[UNIFIED_SECTION_SPLASH], szs[UNIFIED_SECTION_SPLASH], NULL);
94b81afb 230
6017eee9
LP
231 if (szs[UNIFIED_SECTION_CMDLINE] > 0) {
232 cmdline = (char *) loaded_image->ImageBase + addrs[UNIFIED_SECTION_CMDLINE];
233 cmdline_len = szs[UNIFIED_SECTION_CMDLINE];
e41d3d89 234 }
0fa2cac4 235
03d5449f 236 /* if we are not in secure boot mode, or none was provided, accept a custom command line and replace
5c19169f 237 * the built-in one. We also do a superficial check whether first character of passed command line
03d5449f
LP
238 * is printable character (for compat with some Dell systems which fill in garbage?). */
239 if ((!secure_boot_enabled() || cmdline_len == 0) &&
240 loaded_image->LoadOptionsSize > 0 &&
241 ((char16_t *) loaded_image->LoadOptions)[0] > 0x1F) {
07d0fde4 242 cmdline_len = (loaded_image->LoadOptionsSize / sizeof(char16_t)) * sizeof(char);
03d5449f 243 cmdline = cmdline_owned = xnew(char, cmdline_len);
9f048123 244
03d5449f
LP
245 for (UINTN i = 0; i < cmdline_len; i++) {
246 char16_t c = ((char16_t *) loaded_image->LoadOptions)[i];
247 cmdline[i] = c > 0x1F && c < 0x7F ? c : ' '; /* convert non-printable and non_ASCII characters to spaces. */
248 }
92ed3bb4 249
e6e24af5
LP
250 /* Let's measure the passed kernel command line into the TPM. Note that this possibly
251 * duplicates what we already did in the boot menu, if that was already used. However, since
252 * we want the boot menu to support an EFI binary, and want to this stub to be usable from
253 * any boot menu, let's measure things anyway. */
599fe002
LP
254 m = false;
255 (void) tpm_log_load_options(loaded_image->LoadOptions, &m);
256 parameters_measured = m;
0fa2cac4
KS
257 }
258
5a186322 259 export_variables(loaded_image);
1aa15def 260
599fe002
LP
261 if (pack_cpio(loaded_image,
262 NULL,
263 L".cred",
264 ".extra/credentials",
265 /* dir_mode= */ 0500,
266 /* access_mode= */ 0400,
267 /* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_KERNEL_PARAMETERS, TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT },
268 /* n_tpm_pcr= */ 2,
269 L"Credentials initrd",
270 &credential_initrd,
271 &credential_initrd_size,
272 &m) == EFI_SUCCESS)
273 parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
274
275 if (pack_cpio(loaded_image,
276 L"\\loader\\credentials",
277 L".cred",
278 ".extra/global_credentials",
279 /* dir_mode= */ 0500,
280 /* access_mode= */ 0400,
281 /* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_KERNEL_PARAMETERS, TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT },
282 /* n_tpm_pcr= */ 2,
283 L"Global credentials initrd",
284 &global_credential_initrd,
285 &global_credential_initrd_size,
286 &m) == EFI_SUCCESS)
287 parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
f3b6f333 288
de7ad6d4
LP
289 if (pack_cpio(loaded_image,
290 NULL,
291 L".raw",
292 ".extra/sysext",
293 /* dir_mode= */ 0555,
294 /* access_mode= */ 0444,
295 /* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_INITRD_SYSEXTS },
296 /* n_tpm_pcr= */ 1,
297 L"System extension initrd",
298 &sysext_initrd,
299 &sysext_initrd_size,
300 &m) == EFI_SUCCESS)
301 sysext_measured = m;
599fe002
LP
302
303 if (parameters_measured > 0)
304 (void) efivar_set_uint_string(LOADER_GUID, L"StubPcrKernelParameters", TPM_PCR_INDEX_KERNEL_PARAMETERS, 0);
de7ad6d4
LP
305 if (sysext_measured)
306 (void) efivar_set_uint_string(LOADER_GUID, L"StubPcrInitRDSysExts", TPM_PCR_INDEX_INITRD_SYSEXTS, 0);
845707aa 307
6017eee9
LP
308 linux_size = szs[UNIFIED_SECTION_LINUX];
309 linux_base = POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_LINUX];
5b5d365d 310
6017eee9
LP
311 initrd_size = szs[UNIFIED_SECTION_INITRD];
312 initrd_base = initrd_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_INITRD] : 0;
37fa3690 313
6017eee9
LP
314 dt_size = szs[UNIFIED_SECTION_DTB];
315 dt_base = dt_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_DTB] : 0;
33bc9b75 316
f3b6f333 317 if (credential_initrd || global_credential_initrd || sysext_initrd) {
845707aa
LP
318 /* If we have generated initrds dynamically, let's combine them with the built-in initrd. */
319 err = combine_initrd(
320 initrd_base, initrd_size,
321 credential_initrd, credential_initrd_size,
f3b6f333 322 global_credential_initrd, global_credential_initrd_size,
845707aa
LP
323 sysext_initrd, sysext_initrd_size,
324 &initrd_base, &initrd_size);
2a5e4fe4 325 if (err != EFI_SUCCESS)
845707aa
LP
326 return err;
327
328 /* Given these might be large let's free them explicitly, quickly. */
f3b6f333
AV
329 credential_initrd = mfree(credential_initrd);
330 global_credential_initrd = mfree(global_credential_initrd);
331 sysext_initrd = mfree(sysext_initrd);
845707aa 332 }
0fa2cac4 333
33bc9b75
MR
334 if (dt_size > 0) {
335 err = devicetree_install_from_memory(
336 &dt_state, PHYSICAL_ADDRESS_TO_POINTER(dt_base), dt_size);
2a5e4fe4 337 if (err != EFI_SUCCESS)
33bc9b75
MR
338 log_error_stall(L"Error loading embedded devicetree: %r", err);
339 }
340
a6089431 341 err = linux_exec(image, cmdline, cmdline_len,
dc467928 342 PHYSICAL_ADDRESS_TO_POINTER(linux_base), linux_size,
a6089431 343 PHYSICAL_ADDRESS_TO_POINTER(initrd_base), initrd_size);
e5a1b8f9 344 graphics_mode(false);
8aba0eec 345 return log_error_status_stall(err, L"Execution of embedded linux image failed: %r", err);
0fa2cac4 346}