1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
4 #include "device-path-util.h"
5 #include "devicetree.h"
9 #include "part-discovery.h"
11 #include "proto/shell-parameters.h"
12 #include "random-seed.h"
13 #include "secure-boot.h"
19 /* magic string to find in the binary image */
20 _used_
_section_(".sdmagic") static const char magic
[] = "#### LoaderInfo: systemd-stub " GIT_VERSION
" ####";
22 static EFI_STATUS
combine_initrd(
23 EFI_PHYSICAL_ADDRESS initrd_base
, size_t initrd_size
,
24 const void * const extra_initrds
[], const size_t extra_initrd_sizes
[], size_t n_extra_initrds
,
25 Pages
*ret_initr_pages
, size_t *ret_initrd_size
) {
29 assert(ret_initr_pages
);
30 assert(ret_initrd_size
);
32 /* Combines four initrds into one, by simple concatenation in memory */
34 n
= ALIGN4(initrd_size
); /* main initrd might not be padded yet */
36 for (size_t i
= 0; i
< n_extra_initrds
; i
++) {
37 if (!extra_initrds
[i
])
40 if (n
> SIZE_MAX
- extra_initrd_sizes
[i
])
41 return EFI_OUT_OF_RESOURCES
;
43 n
+= extra_initrd_sizes
[i
];
46 _cleanup_pages_ Pages pages
= xmalloc_pages(
50 UINT32_MAX
/* Below 4G boundary. */);
51 uint8_t *p
= PHYSICAL_ADDRESS_TO_POINTER(pages
.addr
);
52 if (initrd_base
!= 0) {
55 /* Order matters, the real initrd must come first, since it might include microcode updates
56 * which the kernel only looks for in the first cpio archive */
57 p
= mempcpy(p
, PHYSICAL_ADDRESS_TO_POINTER(initrd_base
), initrd_size
);
59 pad
= ALIGN4(initrd_size
) - initrd_size
;
66 for (size_t i
= 0; i
< n_extra_initrds
; i
++) {
67 if (!extra_initrds
[i
])
70 p
= mempcpy(p
, extra_initrds
[i
], extra_initrd_sizes
[i
]);
73 assert(PHYSICAL_ADDRESS_TO_POINTER(pages
.addr
+ n
) == p
);
75 *ret_initr_pages
= pages
;
82 static void export_variables(EFI_LOADED_IMAGE_PROTOCOL
*loaded_image
) {
83 static const uint64_t stub_features
=
84 EFI_STUB_FEATURE_REPORT_BOOT_PARTITION
| /* We set LoaderDevicePartUUID */
85 EFI_STUB_FEATURE_PICK_UP_CREDENTIALS
| /* We pick up credentials from the boot partition */
86 EFI_STUB_FEATURE_PICK_UP_SYSEXTS
| /* We pick up system extensions from the boot partition */
87 EFI_STUB_FEATURE_THREE_PCRS
| /* We can measure kernel image, parameters and sysext */
88 EFI_STUB_FEATURE_RANDOM_SEED
| /* We pass a random seed to the kernel */
93 /* Export the device path this image is started from, if it's not set yet */
94 if (efivar_get_raw(MAKE_GUID_PTR(LOADER
), u
"LoaderDevicePartUUID", NULL
, NULL
) != EFI_SUCCESS
) {
95 _cleanup_free_ char16_t
*uuid
= disk_get_part_uuid(loaded_image
->DeviceHandle
);
97 efivar_set(MAKE_GUID_PTR(LOADER
), u
"LoaderDevicePartUUID", uuid
, 0);
100 /* If LoaderImageIdentifier is not set, assume the image with this stub was loaded directly from the
101 * UEFI firmware without any boot loader, and hence set the LoaderImageIdentifier ourselves. Note
102 * that some boot chain loaders neither set LoaderImageIdentifier nor make FilePath available to us,
103 * in which case there's simple nothing to set for us. (The UEFI spec doesn't really say who's wrong
104 * here, i.e. whether FilePath may be NULL or not, hence handle this gracefully and check if FilePath
105 * is non-NULL explicitly.) */
106 if (efivar_get_raw(MAKE_GUID_PTR(LOADER
), u
"LoaderImageIdentifier", NULL
, NULL
) != EFI_SUCCESS
&&
107 loaded_image
->FilePath
) {
108 _cleanup_free_ char16_t
*s
= NULL
;
109 if (device_path_to_str(loaded_image
->FilePath
, &s
) == EFI_SUCCESS
)
110 efivar_set(MAKE_GUID_PTR(LOADER
), u
"LoaderImageIdentifier", s
, 0);
113 /* if LoaderFirmwareInfo is not set, let's set it */
114 if (efivar_get_raw(MAKE_GUID_PTR(LOADER
), u
"LoaderFirmwareInfo", NULL
, NULL
) != EFI_SUCCESS
) {
115 _cleanup_free_ char16_t
*s
= NULL
;
116 s
= xasprintf("%ls %u.%02u", ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& 0xffff);
117 efivar_set(MAKE_GUID_PTR(LOADER
), u
"LoaderFirmwareInfo", s
, 0);
120 /* ditto for LoaderFirmwareType */
121 if (efivar_get_raw(MAKE_GUID_PTR(LOADER
), u
"LoaderFirmwareType", NULL
, NULL
) != EFI_SUCCESS
) {
122 _cleanup_free_ char16_t
*s
= NULL
;
123 s
= xasprintf("UEFI %u.%02u", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& 0xffff);
124 efivar_set(MAKE_GUID_PTR(LOADER
), u
"LoaderFirmwareType", s
, 0);
128 /* add StubInfo (this is one is owned by the stub, hence we unconditionally override this with our
130 (void) efivar_set(MAKE_GUID_PTR(LOADER
), u
"StubInfo", u
"systemd-stub " GIT_VERSION
, 0);
132 (void) efivar_set_uint64_le(MAKE_GUID_PTR(LOADER
), u
"StubFeatures", stub_features
, 0);
135 static bool use_load_options(
136 EFI_HANDLE stub_image
,
137 EFI_LOADED_IMAGE_PROTOCOL
*loaded_image
,
142 assert(loaded_image
);
145 /* We only allow custom command lines if we aren't in secure boot or if no cmdline was baked into
147 if (secure_boot_enabled() && have_cmdline
)
150 /* We also do a superficial check whether first character of passed command line
151 * is printable character (for compat with some Dell systems which fill in garbage?). */
152 if (loaded_image
->LoadOptionsSize
< sizeof(char16_t
) || ((char16_t
*) loaded_image
->LoadOptions
)[0] <= 0x1F)
155 /* The UEFI shell registers EFI_SHELL_PARAMETERS_PROTOCOL onto images it runs. This lets us know that
156 * LoadOptions starts with the stub binary path which we want to strip off. */
157 EFI_SHELL_PARAMETERS_PROTOCOL
*shell
;
158 if (BS
->HandleProtocol(stub_image
, MAKE_GUID_PTR(EFI_SHELL_PARAMETERS_PROTOCOL
), (void **) &shell
)
160 /* Not running from EFI shell, use entire LoadOptions. Note that LoadOptions is a void*, so
161 * it could be anything! */
162 *ret
= xstrndup16(loaded_image
->LoadOptions
, loaded_image
->LoadOptionsSize
/ sizeof(char16_t
));
163 mangle_stub_cmdline(*ret
);
168 /* No arguments were provided? Then we fall back to built-in cmdline. */
171 /* Assemble the command line ourselves without our stub path. */
172 *ret
= xstrdup16(shell
->Argv
[1]);
173 for (size_t i
= 2; i
< shell
->Argc
; i
++) {
174 _cleanup_free_ char16_t
*old
= *ret
;
175 *ret
= xasprintf("%ls %ls", old
, shell
->Argv
[i
]);
178 mangle_stub_cmdline(*ret
);
182 static EFI_STATUS
run(EFI_HANDLE image
) {
183 _cleanup_free_
void *credential_initrd
= NULL
, *global_credential_initrd
= NULL
, *sysext_initrd
= NULL
, *pcrsig_initrd
= NULL
, *pcrpkey_initrd
= NULL
;
184 size_t credential_initrd_size
= 0, global_credential_initrd_size
= 0, sysext_initrd_size
= 0, pcrsig_initrd_size
= 0, pcrpkey_initrd_size
= 0;
185 size_t linux_size
, initrd_size
, dt_size
;
186 EFI_PHYSICAL_ADDRESS linux_base
, initrd_base
, dt_base
;
187 _cleanup_(devicetree_cleanup
) struct devicetree_state dt_state
= {};
188 EFI_LOADED_IMAGE_PROTOCOL
*loaded_image
;
189 size_t addrs
[_UNIFIED_SECTION_MAX
] = {}, szs
[_UNIFIED_SECTION_MAX
] = {};
190 _cleanup_free_ char16_t
*cmdline
= NULL
;
191 int sections_measured
= -1, parameters_measured
= -1;
192 bool sysext_measured
= false, m
;
193 uint64_t loader_features
= 0;
196 err
= BS
->OpenProtocol(
198 MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL
),
199 (void **) &loaded_image
,
202 EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
203 if (err
!= EFI_SUCCESS
)
204 return log_error_status(err
, "Error getting a LoadedImageProtocol handle: %m");
206 if (efivar_get_uint64_le(MAKE_GUID_PTR(LOADER
), u
"LoaderFeatures", &loader_features
) != EFI_SUCCESS
||
207 !FLAGS_SET(loader_features
, EFI_LOADER_FEATURE_RANDOM_SEED
)) {
208 _cleanup_(file_closep
) EFI_FILE
*esp_dir
= NULL
;
210 err
= partition_open(MAKE_GUID_PTR(ESP
), loaded_image
->DeviceHandle
, NULL
, &esp_dir
);
211 if (err
== EFI_SUCCESS
) /* Non-fatal on failure, so that we still boot without it. */
212 (void) process_random_seed(esp_dir
);
215 err
= pe_memory_locate_sections(loaded_image
->ImageBase
, unified_sections
, addrs
, szs
);
216 if (err
!= EFI_SUCCESS
|| szs
[UNIFIED_SECTION_LINUX
] == 0) {
217 if (err
== EFI_SUCCESS
)
219 return log_error_status(err
, "Unable to locate embedded .linux section: %m");
222 /* Measure all "payload" of this PE image into a separate PCR (i.e. where nothing else is written
223 * into so far), so that we have one PCR that we can nicely write policies against because it
224 * contains all static data of this image, and thus can be easily be pre-calculated. */
225 for (UnifiedSection section
= 0; section
< _UNIFIED_SECTION_MAX
; section
++) {
227 if (!unified_section_measure(section
)) /* shall not measure? */
230 if (szs
[section
] == 0) /* not found */
235 /* First measure the name of the section */
236 (void) tpm_log_event_ascii(
237 TPM_PCR_INDEX_KERNEL_IMAGE
,
238 POINTER_TO_PHYSICAL_ADDRESS(unified_sections
[section
]),
239 strsize8(unified_sections
[section
]), /* including NUL byte */
240 unified_sections
[section
],
243 sections_measured
= sections_measured
< 0 ? m
: (sections_measured
&& m
);
245 /* Then measure the data of the section */
246 (void) tpm_log_event_ascii(
247 TPM_PCR_INDEX_KERNEL_IMAGE
,
248 POINTER_TO_PHYSICAL_ADDRESS(loaded_image
->ImageBase
) + addrs
[section
],
250 unified_sections
[section
],
253 sections_measured
= sections_measured
< 0 ? m
: (sections_measured
&& m
);
256 /* After we are done, set an EFI variable that tells userspace this was done successfully, and encode
257 * in it which PCR was used. */
258 if (sections_measured
> 0)
259 (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER
), u
"StubPcrKernelImage", TPM_PCR_INDEX_KERNEL_IMAGE
, 0);
261 /* Show splash screen as early as possible */
262 graphics_splash((const uint8_t*) loaded_image
->ImageBase
+ addrs
[UNIFIED_SECTION_SPLASH
], szs
[UNIFIED_SECTION_SPLASH
]);
264 if (use_load_options(image
, loaded_image
, szs
[UNIFIED_SECTION_CMDLINE
] > 0, &cmdline
)) {
265 /* Let's measure the passed kernel command line into the TPM. Note that this possibly
266 * duplicates what we already did in the boot menu, if that was already used. However, since
267 * we want the boot menu to support an EFI binary, and want to this stub to be usable from
268 * any boot menu, let's measure things anyway. */
270 (void) tpm_log_load_options(cmdline
, &m
);
271 parameters_measured
= m
;
272 } else if (szs
[UNIFIED_SECTION_CMDLINE
] > 0) {
273 cmdline
= xstrn8_to_16(
274 (char *) loaded_image
->ImageBase
+ addrs
[UNIFIED_SECTION_CMDLINE
],
275 szs
[UNIFIED_SECTION_CMDLINE
]);
276 mangle_stub_cmdline(cmdline
);
279 const char *extra
= smbios_find_oem_string("io.systemd.stub.kernel-cmdline-extra");
281 _cleanup_free_ char16_t
*tmp
= TAKE_PTR(cmdline
), *extra16
= xstr8_to_16(extra
);
282 cmdline
= xasprintf("%ls %ls", tmp
, extra16
);
285 export_variables(loaded_image
);
287 if (pack_cpio(loaded_image
,
290 ".extra/credentials",
291 /* dir_mode= */ 0500,
292 /* access_mode= */ 0400,
293 /* tpm_pcr= */ TPM_PCR_INDEX_KERNEL_PARAMETERS
,
294 u
"Credentials initrd",
296 &credential_initrd_size
,
298 parameters_measured
= parameters_measured
< 0 ? m
: (parameters_measured
&& m
);
300 if (pack_cpio(loaded_image
,
301 u
"\\loader\\credentials",
303 ".extra/global_credentials",
304 /* dir_mode= */ 0500,
305 /* access_mode= */ 0400,
306 /* tpm_pcr= */ TPM_PCR_INDEX_KERNEL_PARAMETERS
,
307 u
"Global credentials initrd",
308 &global_credential_initrd
,
309 &global_credential_initrd_size
,
311 parameters_measured
= parameters_measured
< 0 ? m
: (parameters_measured
&& m
);
313 if (pack_cpio(loaded_image
,
317 /* dir_mode= */ 0555,
318 /* access_mode= */ 0444,
319 /* tpm_pcr= */ TPM_PCR_INDEX_INITRD_SYSEXTS
,
320 u
"System extension initrd",
326 if (parameters_measured
> 0)
327 (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER
), u
"StubPcrKernelParameters", TPM_PCR_INDEX_KERNEL_PARAMETERS
, 0);
329 (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER
), u
"StubPcrInitRDSysExts", TPM_PCR_INDEX_INITRD_SYSEXTS
, 0);
331 /* If the PCR signature was embedded in the PE image, then let's wrap it in a cpio and also pass it
332 * to the kernel, so that it can be read from /.extra/tpm2-pcr-signature.json. Note that this section
333 * is not measured, neither as raw section (see above), nor as cpio (here), because it is the
334 * signature of expected PCR values, i.e. its input are PCR measurements, and hence it shouldn't
335 * itself be input for PCR measurements. */
336 if (szs
[UNIFIED_SECTION_PCRSIG
] > 0)
337 (void) pack_cpio_literal(
338 (uint8_t*) loaded_image
->ImageBase
+ addrs
[UNIFIED_SECTION_PCRSIG
],
339 szs
[UNIFIED_SECTION_PCRSIG
],
341 u
"tpm2-pcr-signature.json",
342 /* dir_mode= */ 0555,
343 /* access_mode= */ 0444,
344 /* tpm_pcr= */ UINT32_MAX
,
345 /* tpm_description= */ NULL
,
348 /* ret_measured= */ NULL
);
350 /* If the public key used for the PCR signatures was embedded in the PE image, then let's wrap it in
351 * a cpio and also pass it to the kernel, so that it can be read from
352 * /.extra/tpm2-pcr-public-key.pem. This section is already measure above, hence we won't measure the
354 if (szs
[UNIFIED_SECTION_PCRPKEY
] > 0)
355 (void) pack_cpio_literal(
356 (uint8_t*) loaded_image
->ImageBase
+ addrs
[UNIFIED_SECTION_PCRPKEY
],
357 szs
[UNIFIED_SECTION_PCRPKEY
],
359 u
"tpm2-pcr-public-key.pem",
360 /* dir_mode= */ 0555,
361 /* access_mode= */ 0444,
362 /* tpm_pcr= */ UINT32_MAX
,
363 /* tpm_description= */ NULL
,
365 &pcrpkey_initrd_size
,
366 /* ret_measured= */ NULL
);
368 linux_size
= szs
[UNIFIED_SECTION_LINUX
];
369 linux_base
= POINTER_TO_PHYSICAL_ADDRESS(loaded_image
->ImageBase
) + addrs
[UNIFIED_SECTION_LINUX
];
371 initrd_size
= szs
[UNIFIED_SECTION_INITRD
];
372 initrd_base
= initrd_size
!= 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image
->ImageBase
) + addrs
[UNIFIED_SECTION_INITRD
] : 0;
374 dt_size
= szs
[UNIFIED_SECTION_DTB
];
375 dt_base
= dt_size
!= 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image
->ImageBase
) + addrs
[UNIFIED_SECTION_DTB
] : 0;
377 _cleanup_pages_ Pages initrd_pages
= {};
378 if (credential_initrd
|| global_credential_initrd
|| sysext_initrd
|| pcrsig_initrd
|| pcrpkey_initrd
) {
379 /* If we have generated initrds dynamically, let's combine them with the built-in initrd. */
380 err
= combine_initrd(
381 initrd_base
, initrd_size
,
382 (const void*const[]) {
384 global_credential_initrd
,
390 credential_initrd_size
,
391 global_credential_initrd_size
,
397 &initrd_pages
, &initrd_size
);
398 if (err
!= EFI_SUCCESS
)
401 initrd_base
= initrd_pages
.addr
;
403 /* Given these might be large let's free them explicitly, quickly. */
404 credential_initrd
= mfree(credential_initrd
);
405 global_credential_initrd
= mfree(global_credential_initrd
);
406 sysext_initrd
= mfree(sysext_initrd
);
407 pcrsig_initrd
= mfree(pcrsig_initrd
);
408 pcrpkey_initrd
= mfree(pcrpkey_initrd
);
412 err
= devicetree_install_from_memory(
413 &dt_state
, PHYSICAL_ADDRESS_TO_POINTER(dt_base
), dt_size
);
414 if (err
!= EFI_SUCCESS
)
415 log_error_status(err
, "Error loading embedded devicetree: %m");
418 err
= linux_exec(image
, cmdline
,
419 PHYSICAL_ADDRESS_TO_POINTER(linux_base
), linux_size
,
420 PHYSICAL_ADDRESS_TO_POINTER(initrd_base
), initrd_size
);
421 graphics_mode(false);
425 DEFINE_EFI_MAIN_FUNCTION(run
, "systemd-stub", /*wait_for_debugger=*/false);
427 /* See comment in boot.c. */
428 EFI_STATUS
_entry(EFI_HANDLE image
, EFI_SYSTEM_TABLE
*system_table
) {
429 return efi_main(image
, system_table
);