Skip to content

Commit 914d110

Browse files
author
Tobias Fleig
committed
stub: Add support for .ucode EFI addons
1 parent 82e00a3 commit 914d110

File tree

2 files changed

+215
-22
lines changed

2 files changed

+215
-22
lines changed

man/systemd-stub.xml

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -182,10 +182,10 @@
182182

183183
<listitem><para>Similarly, files
184184
<filename><replaceable>foo</replaceable>.efi.extra.d/*.addon.efi</filename> are loaded and verified as
185-
PE binaries, and a <literal>.cmdline</literal> section is parsed from them. Addons are supposed to be
186-
used to pass additional kernel command line parameters or Devicetree blobs, regardless of the kernel
187-
image being booted, for example to allow platform vendors to ship platform-specific
188-
configuration.</para>
185+
PE binaries, and a <literal>.cmdline</literal> or <literal>.ucode</literal> section is parsed from them.
186+
Addons are supposed to be used to pass additional kernel command line parameters, Devicetree blobs,
187+
and microcode updates, regardless of the kernel image being booted, for example to allow platform vendors
188+
to ship platform-specific configuration.</para>
189189

190190
<para>In case Secure Boot is enabled, these files will be validated using keys in UEFI DB, Shim's DB or
191191
Shim's MOK, and will be rejected otherwise. Additionally, if both the addon and the UKI contain a
@@ -199,7 +199,8 @@
199199
<para>Addon files are sorted, loaded, and measured into TPM PCR 12 (if a TPM is present) and appended
200200
to the kernel command line. UKI command line options are listed first, then options from addons in
201201
<filename>/loader/addons/*.addon.efi</filename>, and finally UKI-specific addons. Device tree blobs are
202-
loaded and measured following the same algorithm. Addons are always loaded in the same order based on
202+
loaded and measured following the same algorithm. Microcode addons are functionally additional initrds,
203+
and are passed to the kernel in the same order. Addons are always loaded in the same order based on
203204
the filename, so that, given the same set of addons, the same set of measurements can be expected in
204205
PCR12. However, note that the filename is not protected by the PE signature, and as such an attacker
205206
with write access to the ESP could potentially rename these files to change the order in which they are
@@ -215,9 +216,10 @@
215216
measured into TPM PCR 12 (if a TPM is present).</para></listitem>
216217

217218
<listitem><para>Additionally, files <filename>/loader/addons/*.addon.efi</filename> are loaded and
218-
verified as PE binaries, and <literal>.cmdline</literal> and/or <literal>.dtb</literal> sections are
219-
parsed from them. This is supposed to be used to pass additional command line parameters or Devicetree
220-
blobs to the kernel, regardless of the kernel being booted.</para></listitem>
219+
verified as PE binaries, and <literal>.cmdline</literal>, <literal>.dtb</literal>, and/or
220+
<literal>.ucode</literal> sections are parsed from them. This is supposed to be used to pass additional
221+
command line parameters, Devicetree blobs, and microcode updates to the kernel, regardless of the
222+
kernel being booted.</para></listitem>
221223
</itemizedlist>
222224

223225
<para>These mechanisms may be used to parameterize and extend trusted (i.e. signed), immutable initrd

src/boot/efi/stub.c

Lines changed: 205 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,146 @@ static void dt_filenames_free(char16_t **dt_filenames, size_t n_dt) {
341341
free(dt_filenames);
342342
}
343343

344+
static EFI_STATUS ucode_merge_and_measure_addons(
345+
void **ucode_bases_addon_global,
346+
size_t *ucode_sizes_addon_global,
347+
size_t n_ucode_addons_global,
348+
void **ucode_bases_addon_uki,
349+
size_t *ucode_sizes_addon_uki,
350+
size_t n_ucode_addons_uki,
351+
void *ucode_section_base,
352+
size_t ucode_section_size,
353+
void **ret_ucode_base,
354+
size_t *ret_ucode_size,
355+
bool *ret_sections_measured) {
356+
357+
int sections_measured = -1;
358+
EFI_STATUS err;
359+
360+
assert(ret_ucode_base);
361+
assert(ret_ucode_size);
362+
assert(ret_sections_measured);
363+
364+
_cleanup_pages_ Pages global_addons_combined_pages = {}, uki_addons_combined_pages = {}, all_ucode_combined_pages = {};
365+
void *global_addons_combined_base = NULL, *uki_addons_combined_base = NULL;
366+
size_t global_addons_combined_size = 0, uki_addons_combined_size = 0, all_ucode_combined_size = 0;
367+
368+
if (n_ucode_addons_global == 0 && n_ucode_addons_uki == 0 && ucode_section_size == 0) {
369+
return EFI_SUCCESS;
370+
}
371+
372+
/* Order of ucode merging (and measurements):
373+
* 1. UKI embedded .ucode section
374+
* 2. Global addons
375+
* 3. UKI addons
376+
*/
377+
378+
/* .ucode section from UKI is already measured as part of the full UKI measurements */
379+
/* Measure global addons */
380+
for (size_t i = 0; i < n_ucode_addons_global; i++) {
381+
bool m = false;
382+
/* First measure the name of the section */
383+
(void) tpm_log_event_ascii(
384+
TPM2_PCR_KERNEL_BOOT,
385+
POINTER_TO_PHYSICAL_ADDRESS(unified_sections[UNIFIED_SECTION_UCODE]),
386+
strsize8(unified_sections[UNIFIED_SECTION_UCODE]), /* including NUL byte */
387+
unified_sections[UNIFIED_SECTION_UCODE],
388+
&m);
389+
390+
sections_measured = sections_measured < 0 ? m : (sections_measured && m);
391+
392+
/* Then measure the data of the section */
393+
(void) tpm_log_event_ascii(
394+
TPM2_PCR_KERNEL_BOOT,
395+
POINTER_TO_PHYSICAL_ADDRESS(ucode_bases_addon_global[i]),
396+
ucode_sizes_addon_global[i],
397+
unified_sections[UNIFIED_SECTION_UCODE],
398+
&m);
399+
400+
sections_measured = sections_measured < 0 ? m : (sections_measured && m);
401+
}
402+
/* Merge global addons */
403+
if (n_ucode_addons_global == 1) {
404+
global_addons_combined_base = ucode_bases_addon_global[0];
405+
global_addons_combined_size = ucode_sizes_addon_global[0];
406+
} else if (n_ucode_addons_global > 1) {
407+
err = combine_initrds(*ucode_bases_addon_global, ucode_sizes_addon_global, n_ucode_addons_global, &global_addons_combined_pages, &global_addons_combined_size);
408+
if (err != EFI_SUCCESS)
409+
return err;
410+
global_addons_combined_base = PHYSICAL_ADDRESS_TO_POINTER(global_addons_combined_pages.addr);
411+
}
412+
413+
414+
/* Measure UKI addons */
415+
for (size_t i = 0; i < n_ucode_addons_uki; i++) {
416+
bool m = false;
417+
/* First measure the name of the section */
418+
(void) tpm_log_event_ascii(
419+
TPM2_PCR_KERNEL_BOOT,
420+
POINTER_TO_PHYSICAL_ADDRESS(unified_sections[UNIFIED_SECTION_UCODE]),
421+
strsize8(unified_sections[UNIFIED_SECTION_UCODE]), /* including NUL byte */
422+
unified_sections[UNIFIED_SECTION_UCODE],
423+
&m);
424+
425+
sections_measured = sections_measured < 0 ? m : (sections_measured && m);
426+
427+
/* Then measure the data of the section */
428+
(void) tpm_log_event_ascii(
429+
TPM2_PCR_KERNEL_BOOT,
430+
POINTER_TO_PHYSICAL_ADDRESS(ucode_bases_addon_uki[i]),
431+
ucode_sizes_addon_uki[i],
432+
unified_sections[UNIFIED_SECTION_UCODE],
433+
&m);
434+
435+
sections_measured = sections_measured < 0 ? m : (sections_measured && m);
436+
}
437+
/* Merge UKI addons */
438+
if (n_ucode_addons_uki == 1) {
439+
uki_addons_combined_base = ucode_bases_addon_uki[0];
440+
uki_addons_combined_size = ucode_sizes_addon_uki[0];
441+
} else if (n_ucode_addons_uki > 1) {
442+
err = combine_initrds(*ucode_bases_addon_uki, ucode_sizes_addon_uki, n_ucode_addons_uki, &uki_addons_combined_pages, &uki_addons_combined_size);
443+
if (err != EFI_SUCCESS)
444+
return err;
445+
uki_addons_combined_base = PHYSICAL_ADDRESS_TO_POINTER(uki_addons_combined_pages.addr);
446+
}
447+
448+
/* Merge the combined global addons, combined UKI addons, and ucode section from UKI */
449+
err = combine_initrds(
450+
(const void*const[]) {
451+
ucode_section_base,
452+
global_addons_combined_base,
453+
uki_addons_combined_base,
454+
},
455+
(const size_t[]) {
456+
ucode_section_size,
457+
global_addons_combined_size,
458+
uki_addons_combined_size,
459+
},
460+
3,
461+
&all_ucode_combined_pages,
462+
&all_ucode_combined_size
463+
);
464+
if (err != EFI_SUCCESS)
465+
return err;
466+
467+
*ret_ucode_base = PHYSICAL_ADDRESS_TO_POINTER(all_ucode_combined_pages.addr);
468+
*ret_ucode_size = all_ucode_combined_size;
469+
all_ucode_combined_pages.n_pages = 0;
470+
*ret_sections_measured = sections_measured;
471+
472+
return EFI_SUCCESS;
473+
}
474+
475+
static void ucode_bases_free(void **ucode_bases, size_t n_ucode) {
476+
assert(ucode_bases || n_ucode == 0);
477+
478+
for (size_t i = 0; i < n_ucode; ++i)
479+
free(ucode_bases[i]);
480+
481+
free(ucode_bases);
482+
}
483+
344484
static EFI_STATUS load_addons(
345485
EFI_HANDLE stub_image,
346486
EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
@@ -350,15 +490,18 @@ static EFI_STATUS load_addons(
350490
void ***ret_dt_bases,
351491
size_t **ret_dt_sizes,
352492
char16_t ***ret_dt_filenames,
353-
size_t *ret_n_dt) {
493+
size_t *ret_n_dt,
494+
void ***ret_ucode_bases,
495+
size_t **ret_ucode_sizes,
496+
size_t *ret_n_ucode) {
354497

355-
_cleanup_free_ size_t *dt_sizes = NULL;
498+
_cleanup_free_ size_t *dt_sizes = NULL, *ucode_sizes = NULL;
356499
_cleanup_(strv_freep) char16_t **items = NULL;
357500
_cleanup_(file_closep) EFI_FILE *root = NULL;
358501
_cleanup_free_ char16_t *cmdline = NULL;
359-
size_t n_items = 0, n_allocated = 0, n_dt = 0;
502+
size_t n_items = 0, n_allocated = 0, n_dt = 0, n_ucode = 0;
360503
char16_t **dt_filenames = NULL;
361-
void **dt_bases = NULL;
504+
void **dt_bases = NULL, **ucode_bases = NULL;
362505
EFI_STATUS err;
363506

364507
assert(stub_image);
@@ -367,12 +510,15 @@ static EFI_STATUS load_addons(
367510
assert(!!ret_dt_bases == !!ret_dt_sizes);
368511
assert(!!ret_dt_bases == !!ret_n_dt);
369512
assert(!!ret_dt_filenames == !!ret_n_dt);
513+
assert(!!ret_ucode_bases == !!ret_ucode_sizes);
514+
assert(!!ret_ucode_bases == !!ret_n_ucode);
370515

371516
if (!loaded_image->DeviceHandle)
372517
return EFI_SUCCESS;
373518

374519
CLEANUP_ARRAY(dt_bases, n_dt, dt_bases_free);
375520
CLEANUP_ARRAY(dt_filenames, n_dt, dt_filenames_free);
521+
CLEANUP_ARRAY(ucode_bases, n_ucode, ucode_bases_free);
376522

377523
err = open_volume(loaded_image->DeviceHandle, &root);
378524
if (err == EFI_UNSUPPORTED)
@@ -424,11 +570,11 @@ static EFI_STATUS load_addons(
424570

425571
err = pe_memory_locate_sections(loaded_addon->ImageBase, unified_sections, addrs, szs);
426572
if (err != EFI_SUCCESS ||
427-
(szs[UNIFIED_SECTION_CMDLINE] == 0 && szs[UNIFIED_SECTION_DTB] == 0)) {
573+
(szs[UNIFIED_SECTION_CMDLINE] == 0 && szs[UNIFIED_SECTION_DTB] == 0 && szs[UNIFIED_SECTION_UCODE] == 0)) {
428574
if (err == EFI_SUCCESS)
429575
err = EFI_NOT_FOUND;
430576
log_error_status(err,
431-
"Unable to locate embedded .cmdline/.dtb sections in %ls, ignoring: %m",
577+
"Unable to locate embedded .cmdline/.dtb/.ucode sections in %ls, ignoring: %m",
432578
items[i]);
433579
continue;
434580
}
@@ -475,6 +621,21 @@ static EFI_STATUS load_addons(
475621

476622
++n_dt;
477623
}
624+
625+
if (ret_ucode_bases && szs[UNIFIED_SECTION_UCODE] > 0) {
626+
ucode_sizes = xrealloc(ucode_sizes,
627+
n_ucode * sizeof(size_t),
628+
(n_ucode + 1) * sizeof(size_t));
629+
ucode_sizes[n_ucode] = szs[UNIFIED_SECTION_UCODE];
630+
631+
ucode_bases = xrealloc(ucode_bases,
632+
n_ucode * sizeof(void *),
633+
(n_ucode + 1) * sizeof(void *));
634+
ucode_bases[n_ucode] = xmemdup((uint8_t*)loaded_addon->ImageBase + addrs[UNIFIED_SECTION_UCODE],
635+
ucode_sizes[n_ucode]);
636+
637+
++n_ucode;
638+
}
478639
}
479640

480641
if (ret_cmdline && !isempty(cmdline))
@@ -486,6 +647,11 @@ static EFI_STATUS load_addons(
486647
*ret_dt_sizes = TAKE_PTR(dt_sizes);
487648
*ret_n_dt = n_dt;
488649
}
650+
if (ret_ucode_bases && n_ucode > 0) {
651+
*ret_ucode_bases = TAKE_PTR(ucode_bases);
652+
*ret_ucode_sizes = TAKE_PTR(ucode_sizes);
653+
*ret_n_ucode = n_ucode;
654+
}
489655

490656
return EFI_SUCCESS;
491657
}
@@ -496,8 +662,11 @@ static EFI_STATUS run(EFI_HANDLE image) {
496662
void **dt_bases_addons_global = NULL, **dt_bases_addons_uki = NULL;
497663
char16_t **dt_filenames_addons_global = NULL, **dt_filenames_addons_uki = NULL;
498664
_cleanup_free_ size_t *dt_sizes_addons_global = NULL, *dt_sizes_addons_uki = NULL;
499-
size_t linux_size, initrd_size, ucode_size, dt_size, n_dts_addons_global = 0, n_dts_addons_uki = 0;
500-
EFI_PHYSICAL_ADDRESS linux_base, initrd_base, ucode_base, dt_base;
665+
void **ucode_bases_addons_global = NULL, **ucode_bases_addons_uki = NULL;
666+
_cleanup_free_ size_t *ucode_sizes_addons_global = NULL, *ucode_sizes_addons_uki = NULL;
667+
size_t linux_size, initrd_size, dt_size, n_dts_addons_global = 0, n_dts_addons_uki = 0, ucode_size = 0, n_ucode_addons_global = 0, n_ucode_addons_uki = 0;
668+
EFI_PHYSICAL_ADDRESS linux_base, initrd_base, dt_base;
669+
void *ucode_base = NULL;
501670
_cleanup_(devicetree_cleanup) struct devicetree_state dt_state = {};
502671
EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
503672
size_t addrs[_UNIFIED_SECTION_MAX] = {}, szs[_UNIFIED_SECTION_MAX] = {};
@@ -533,6 +702,8 @@ static EFI_STATUS run(EFI_HANDLE image) {
533702
CLEANUP_ARRAY(dt_bases_addons_uki, n_dts_addons_uki, dt_bases_free);
534703
CLEANUP_ARRAY(dt_filenames_addons_global, n_dts_addons_global, dt_filenames_free);
535704
CLEANUP_ARRAY(dt_filenames_addons_uki, n_dts_addons_uki, dt_filenames_free);
705+
CLEANUP_ARRAY(ucode_bases_addons_global, n_ucode_addons_global, ucode_bases_free);
706+
CLEANUP_ARRAY(ucode_bases_addons_uki, n_ucode_addons_uki, ucode_bases_free);
536707

537708
if (szs[UNIFIED_SECTION_UNAME] > 0)
538709
uname = xstrndup8((char *)loaded_image->ImageBase + addrs[UNIFIED_SECTION_UNAME],
@@ -549,7 +720,10 @@ static EFI_STATUS run(EFI_HANDLE image) {
549720
&dt_bases_addons_global,
550721
&dt_sizes_addons_global,
551722
&dt_filenames_addons_global,
552-
&n_dts_addons_global);
723+
&n_dts_addons_global,
724+
&ucode_bases_addons_global,
725+
&ucode_sizes_addons_global,
726+
&n_ucode_addons_global);
553727
if (err != EFI_SUCCESS)
554728
log_error_status(err, "Error loading global addons, ignoring: %m");
555729

@@ -565,7 +739,10 @@ static EFI_STATUS run(EFI_HANDLE image) {
565739
&dt_bases_addons_uki,
566740
&dt_sizes_addons_uki,
567741
&dt_filenames_addons_uki,
568-
&n_dts_addons_uki);
742+
&n_dts_addons_uki,
743+
&ucode_bases_addons_uki,
744+
&ucode_sizes_addons_uki,
745+
&n_ucode_addons_uki);
569746
if (err != EFI_SUCCESS)
570747
log_error_status(err, "Error loading UKI-specific addons, ignoring: %m");
571748
}
@@ -735,6 +912,23 @@ static EFI_STATUS run(EFI_HANDLE image) {
735912
&m);
736913
parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
737914

915+
/* Handle ucode */
916+
err = ucode_merge_and_measure_addons(
917+
ucode_bases_addons_global,
918+
ucode_sizes_addons_global,
919+
n_ucode_addons_global,
920+
ucode_bases_addons_uki,
921+
ucode_sizes_addons_uki,
922+
n_ucode_addons_uki,
923+
szs[UNIFIED_SECTION_UCODE] != 0 ? PHYSICAL_ADDRESS_TO_POINTER(POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_UCODE]) : NULL,
924+
szs[UNIFIED_SECTION_UCODE],
925+
&ucode_base,
926+
&ucode_size,
927+
&m);
928+
if (err != EFI_SUCCESS)
929+
return err;
930+
parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
931+
738932
if (parameters_measured > 0)
739933
(void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrKernelParameters", TPM2_PCR_KERNEL_CONFIG, 0);
740934
if (sysext_measured)
@@ -785,17 +979,14 @@ static EFI_STATUS run(EFI_HANDLE image) {
785979
initrd_size = szs[UNIFIED_SECTION_INITRD];
786980
initrd_base = initrd_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_INITRD] : 0;
787981

788-
ucode_size = szs[UNIFIED_SECTION_UCODE];
789-
ucode_base = ucode_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_UCODE] : 0;
790-
791982
_cleanup_pages_ Pages initrd_pages = {};
792983
if (ucode_base || credential_initrd || global_credential_initrd || sysext_initrd || confext_initrd || pcrsig_initrd || pcrpkey_initrd) {
793984
/* If we have generated initrds dynamically or there is a microcode initrd, combine them with the built-in initrd. */
794985
err = combine_initrds(
795986
(const void*const[]) {
796987
/* Microcode must always be first as kernel only scans uncompressed cpios
797988
* and later initrds might be compressed. */
798-
PHYSICAL_ADDRESS_TO_POINTER(ucode_base),
989+
ucode_base,
799990
PHYSICAL_ADDRESS_TO_POINTER(initrd_base),
800991
credential_initrd,
801992
global_credential_initrd,

0 commit comments

Comments
 (0)