mirror of
https://github.com/systemd/systemd.git
synced 2025-02-22 09:57:34 +03:00
stub: Add support for .ucode EFI addons
This extends #31872 to also load microcode from addon files.
This commit is contained in:
parent
cd8eb9605f
commit
fc31d929c7
@ -120,6 +120,16 @@ Devicetree addons are measured individually as a tagged event.
|
||||
|
||||
→ **Measured hash** covers the content of the Devicetree.
|
||||
|
||||
### PCR 12, `EV_EVENT_TAG`, "Ucode addons"
|
||||
|
||||
Ucode addons are measured individually as a tagged event.
|
||||
|
||||
→ **Event Tag** `0xdac08e1a`
|
||||
|
||||
→ **Description** the addon filename.
|
||||
|
||||
→ **Measured hash** covers the contents of the ucode initrd.
|
||||
|
||||
### PCR 12, `EV_IPL`, "Per-UKI Credentials initrd"
|
||||
|
||||
→ **Description** in the event log record is the constant string "Credentials
|
||||
|
@ -182,10 +182,10 @@
|
||||
|
||||
<listitem><para>Similarly, files
|
||||
<filename><replaceable>foo</replaceable>.efi.extra.d/*.addon.efi</filename> are loaded and verified as
|
||||
PE binaries, and a <literal>.cmdline</literal> section is parsed from them. Addons are supposed to be
|
||||
used to pass additional kernel command line parameters or Devicetree blobs, regardless of the kernel
|
||||
image being booted, for example to allow platform vendors to ship platform-specific
|
||||
configuration.</para>
|
||||
PE binaries, and a <literal>.cmdline</literal> or <literal>.ucode</literal> section is parsed from them.
|
||||
Addons are supposed to be used to pass additional kernel command line parameters, or Devicetree blobs,
|
||||
and microcode updates, regardless of the kernel image being booted, for example to allow platform vendors
|
||||
to ship platform-specific configuration.</para>
|
||||
|
||||
<para>In case Secure Boot is enabled, these files will be validated using keys in UEFI DB, Shim's DB or
|
||||
Shim's MOK, and will be rejected otherwise. Additionally, if both the addon and the UKI contain a
|
||||
@ -199,7 +199,9 @@
|
||||
<para>Addon files are sorted, loaded, and measured into TPM PCR 12 (if a TPM is present) and appended
|
||||
to the kernel command line. UKI command line options are listed first, then options from addons in
|
||||
<filename>/loader/addons/*.addon.efi</filename>, and finally UKI-specific addons. Device tree blobs are
|
||||
loaded and measured following the same algorithm. Addons are always loaded in the same order based on
|
||||
loaded and measured following the same algorithm. Microcode addons are passed to the kernel in inverse
|
||||
order (UKI specific addons, global addons, UKI embedded section). This is because the microcode update
|
||||
driver stops on the first matching filename. Addons are always loaded in the same order based on
|
||||
the filename, so that, given the same set of addons, the same set of measurements can be expected in
|
||||
PCR12. However, note that the filename is not protected by the PE signature, and as such an attacker
|
||||
with write access to the ESP could potentially rename these files to change the order in which they are
|
||||
@ -215,9 +217,10 @@
|
||||
measured into TPM PCR 12 (if a TPM is present).</para></listitem>
|
||||
|
||||
<listitem><para>Additionally, files <filename>/loader/addons/*.addon.efi</filename> are loaded and
|
||||
verified as PE binaries, and <literal>.cmdline</literal> and/or <literal>.dtb</literal> sections are
|
||||
parsed from them. This is supposed to be used to pass additional command line parameters or Devicetree
|
||||
blobs to the kernel, regardless of the kernel being booted.</para></listitem>
|
||||
verified as PE binaries, and <literal>.cmdline</literal>, <literal>.dtb</literal> and/or
|
||||
<literal>.ucode</literal>sections are parsed from them. This is supposed to be used to pass additional
|
||||
command line parameters, Devicetree blobs and microcode updates to the kernel, regardless of the
|
||||
kernel being booted.</para></listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>These mechanisms may be used to parameterize and extend trusted (i.e. signed), immutable initrd
|
||||
|
@ -104,7 +104,6 @@ static EFI_STATUS combine_initrds(
|
||||
|
||||
FOREACH_ARRAY(i, initrds, n_initrds) {
|
||||
/* some initrds (the ones from UKI sections) need padding, pad all to be safe */
|
||||
|
||||
size_t initrd_size = ALIGN4(i->iov_len);
|
||||
if (n > SIZE_MAX - initrd_size)
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
@ -298,30 +297,30 @@ static void cmdline_append_and_measure_addons(
|
||||
*cmdline_append = xasprintf("%ls %ls", tmp, copy);
|
||||
}
|
||||
|
||||
typedef struct DevicetreeAddon {
|
||||
typedef struct NamedAddon {
|
||||
char16_t *filename;
|
||||
struct iovec blob;
|
||||
} DevicetreeAddon;
|
||||
} NamedAddon;
|
||||
|
||||
static void devicetree_addon_done(DevicetreeAddon *a) {
|
||||
static void named_addon_done(NamedAddon *a) {
|
||||
assert(a);
|
||||
|
||||
a->filename = mfree(a->filename);
|
||||
iovec_done(&a->blob);
|
||||
}
|
||||
|
||||
static void devicetree_addon_free_many(DevicetreeAddon *a, size_t n) {
|
||||
static void named_addon_free_many(NamedAddon *a, size_t n) {
|
||||
assert(a || n == 0);
|
||||
|
||||
FOREACH_ARRAY(i, a, n)
|
||||
devicetree_addon_done(i);
|
||||
named_addon_done(i);
|
||||
|
||||
free(a);
|
||||
}
|
||||
|
||||
static void install_addon_devicetrees(
|
||||
struct devicetree_state *dt_state,
|
||||
DevicetreeAddon *addons,
|
||||
NamedAddon *addons,
|
||||
size_t n_addons,
|
||||
int *parameters_measured) {
|
||||
|
||||
@ -357,14 +356,78 @@ static void install_addon_devicetrees(
|
||||
}
|
||||
}
|
||||
|
||||
static inline void iovec_array_extend(struct iovec **arr, size_t *n_arr, struct iovec elem) {
|
||||
assert(arr);
|
||||
assert(n_arr);
|
||||
|
||||
if (!iovec_is_set(&elem))
|
||||
return;
|
||||
|
||||
*arr = xrealloc(*arr, *n_arr * sizeof(struct iovec), (*n_arr + 1) * sizeof(struct iovec));
|
||||
(*arr)[(*n_arr)++] = elem;
|
||||
}
|
||||
|
||||
static void measure_and_append_ucode_addons(
|
||||
struct iovec **all_initrds,
|
||||
size_t *n_all_initrds,
|
||||
NamedAddon *ucode_addons,
|
||||
size_t n_ucode_addons,
|
||||
int *sections_measured) {
|
||||
|
||||
EFI_STATUS err;
|
||||
|
||||
assert(all_initrds);
|
||||
assert(n_all_initrds);
|
||||
assert(ucode_addons || n_ucode_addons == 0);
|
||||
assert(sections_measured);
|
||||
|
||||
/* Ucode addons need to be measured and copied into all_initrds in reverse order,
|
||||
* the kernel takes the first one it finds. */
|
||||
for (ssize_t i = n_ucode_addons - 1; i >= 0; i--) {
|
||||
bool m = false;
|
||||
err = tpm_log_tagged_event(
|
||||
TPM2_PCR_KERNEL_CONFIG,
|
||||
POINTER_TO_PHYSICAL_ADDRESS(ucode_addons[i].blob.iov_base),
|
||||
ucode_addons[i].blob.iov_len,
|
||||
UCODE_ADDON_EVENT_TAG_ID,
|
||||
ucode_addons[i].filename,
|
||||
&m);
|
||||
if (err != EFI_SUCCESS)
|
||||
return (void) log_error_status(
|
||||
err,
|
||||
"Unable to extend PCR %i with UCODE addon '%ls': %m",
|
||||
TPM2_PCR_KERNEL_CONFIG,
|
||||
ucode_addons[i].filename);
|
||||
|
||||
combine_measured_flag(sections_measured, m);
|
||||
|
||||
iovec_array_extend(all_initrds, n_all_initrds, ucode_addons[i].blob);
|
||||
}
|
||||
}
|
||||
|
||||
static void extend_initrds(
|
||||
struct iovec initrds[static _INITRD_MAX],
|
||||
struct iovec **all_initrds,
|
||||
size_t *n_all_initrds) {
|
||||
|
||||
assert(initrds);
|
||||
assert(all_initrds);
|
||||
assert(n_all_initrds);
|
||||
|
||||
FOREACH_ARRAY(i, initrds, _INITRD_MAX)
|
||||
iovec_array_extend(all_initrds, n_all_initrds, *i);
|
||||
}
|
||||
|
||||
static EFI_STATUS load_addons(
|
||||
EFI_HANDLE stub_image,
|
||||
EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
|
||||
const char16_t *prefix,
|
||||
const char *uname,
|
||||
char16_t **cmdline, /* Both input+output, extended with new addons we find */
|
||||
DevicetreeAddon **devicetree_addons, /* Ditto */
|
||||
size_t *n_devicetree_addons) {
|
||||
NamedAddon **devicetree_addons, /* Ditto */
|
||||
size_t *n_devicetree_addons,
|
||||
NamedAddon **ucode_addons, /* Ditto */
|
||||
size_t *n_ucode_addons) {
|
||||
|
||||
_cleanup_(strv_freep) char16_t **items = NULL;
|
||||
_cleanup_(file_closep) EFI_FILE *root = NULL;
|
||||
@ -429,11 +492,12 @@ static EFI_STATUS load_addons(
|
||||
err = pe_memory_locate_sections(loaded_addon->ImageBase, unified_sections, sections);
|
||||
if (err != EFI_SUCCESS ||
|
||||
(!PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_CMDLINE) &&
|
||||
!PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_DTB))) {
|
||||
!PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_DTB) &&
|
||||
!PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_UCODE))) {
|
||||
if (err == EFI_SUCCESS)
|
||||
err = EFI_NOT_FOUND;
|
||||
log_error_status(err,
|
||||
"Unable to locate embedded .cmdline/.dtb sections in %ls, ignoring: %m",
|
||||
"Unable to locate embedded .cmdline/.dtb/.ucode sections in %ls, ignoring: %m",
|
||||
items[i]);
|
||||
continue;
|
||||
}
|
||||
@ -463,15 +527,28 @@ static EFI_STATUS load_addons(
|
||||
|
||||
if (devicetree_addons && PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_DTB)) {
|
||||
*devicetree_addons = xrealloc(*devicetree_addons,
|
||||
*n_devicetree_addons * sizeof(DevicetreeAddon),
|
||||
(*n_devicetree_addons + 1) * sizeof(DevicetreeAddon));
|
||||
*n_devicetree_addons * sizeof(NamedAddon),
|
||||
(*n_devicetree_addons + 1) * sizeof(NamedAddon));
|
||||
|
||||
(*devicetree_addons)[(*n_devicetree_addons)++] = (DevicetreeAddon) {
|
||||
(*devicetree_addons)[(*n_devicetree_addons)++] = (NamedAddon) {
|
||||
.blob = {
|
||||
.iov_base = xmemdup((const uint8_t*) loaded_addon->ImageBase + sections[UNIFIED_SECTION_DTB].memory_offset, sections[UNIFIED_SECTION_DTB].size),
|
||||
.iov_len = sections[UNIFIED_SECTION_DTB].size,
|
||||
},
|
||||
.filename = xstrdup16(items[i]),
|
||||
.filename = xstrdup16(items[i]),
|
||||
};
|
||||
}
|
||||
|
||||
if (ucode_addons && PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_UCODE)) {
|
||||
*ucode_addons = xrealloc(*ucode_addons,
|
||||
*n_ucode_addons * sizeof(NamedAddon),
|
||||
(*n_ucode_addons + 1) * sizeof(NamedAddon));
|
||||
(*ucode_addons)[(*n_ucode_addons)++] = (NamedAddon) {
|
||||
.blob = {
|
||||
.iov_base = xmemdup((const uint8_t*) loaded_addon->ImageBase + sections[UNIFIED_SECTION_UCODE].memory_offset, sections[UNIFIED_SECTION_UCODE].size),
|
||||
.iov_len = sections[UNIFIED_SECTION_UCODE].size,
|
||||
},
|
||||
.filename = xstrdup16(items[i]),
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -587,23 +664,6 @@ static void initrds_free(struct iovec (*initrds)[_INITRD_MAX]) {
|
||||
iovec_done((*initrds) + i);
|
||||
}
|
||||
|
||||
static bool initrds_need_combine(struct iovec initrds[static _INITRD_MAX]) {
|
||||
assert(initrds);
|
||||
|
||||
/* Returns true if we have any initrds set that aren't the base initrd. In that case we need to
|
||||
* merge, otherwise we can pass the embedded initrd as is */
|
||||
|
||||
for (size_t i = 0; i < _INITRD_MAX; i++) {
|
||||
if (i == INITRD_BASE)
|
||||
continue;
|
||||
|
||||
if (iovec_is_set(initrds + i))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void generate_sidecar_initrds(
|
||||
EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
|
||||
struct iovec initrds[static _INITRD_MAX],
|
||||
@ -782,8 +842,10 @@ static void load_all_addons(
|
||||
EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
|
||||
const char *uname,
|
||||
char16_t **cmdline_addons,
|
||||
DevicetreeAddon **dt_addons,
|
||||
size_t *n_dt_addons) {
|
||||
NamedAddon **dt_addons,
|
||||
size_t *n_dt_addons,
|
||||
NamedAddon **ucode_addons,
|
||||
size_t *n_ucode_addons) {
|
||||
|
||||
EFI_STATUS err;
|
||||
|
||||
@ -791,6 +853,8 @@ static void load_all_addons(
|
||||
assert(cmdline_addons);
|
||||
assert(dt_addons);
|
||||
assert(n_dt_addons);
|
||||
assert(ucode_addons);
|
||||
assert(n_ucode_addons);
|
||||
|
||||
err = load_addons(
|
||||
image,
|
||||
@ -799,7 +863,9 @@ static void load_all_addons(
|
||||
uname,
|
||||
cmdline_addons,
|
||||
dt_addons,
|
||||
n_dt_addons);
|
||||
n_dt_addons,
|
||||
ucode_addons,
|
||||
n_ucode_addons);
|
||||
if (err != EFI_SUCCESS)
|
||||
log_error_status(err, "Error loading global addons, ignoring: %m");
|
||||
|
||||
@ -815,7 +881,9 @@ static void load_all_addons(
|
||||
uname,
|
||||
cmdline_addons,
|
||||
dt_addons,
|
||||
n_dt_addons);
|
||||
n_dt_addons,
|
||||
ucode_addons,
|
||||
n_ucode_addons);
|
||||
if (err != EFI_SUCCESS)
|
||||
log_error_status(err, "Error loading UKI-specific addons, ignoring: %m");
|
||||
}
|
||||
@ -863,8 +931,10 @@ static EFI_STATUS run(EFI_HANDLE image) {
|
||||
PeSectionVector sections[ELEMENTSOF(unified_sections)] = {};
|
||||
EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
|
||||
_cleanup_free_ char *uname = NULL;
|
||||
DevicetreeAddon *dt_addons = NULL;
|
||||
size_t n_dt_addons = 0;
|
||||
NamedAddon *dt_addons = NULL, *ucode_addons = NULL;
|
||||
size_t n_dt_addons = 0, n_ucode_addons = 0;
|
||||
_cleanup_free_ struct iovec *all_initrds = NULL;
|
||||
size_t n_all_initrds = 0;
|
||||
EFI_STATUS err;
|
||||
|
||||
err = BS->HandleProtocol(image, MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL), (void **) &loaded_image);
|
||||
@ -890,8 +960,9 @@ static EFI_STATUS run(EFI_HANDLE image) {
|
||||
|
||||
/* Now that we have the UKI sections loaded, also load global first and then local (per-UKI)
|
||||
* addons. The data is loaded at once, and then used later. */
|
||||
CLEANUP_ARRAY(dt_addons, n_dt_addons, devicetree_addon_free_many);
|
||||
load_all_addons(image, loaded_image, uname, &cmdline_addons, &dt_addons, &n_dt_addons);
|
||||
CLEANUP_ARRAY(dt_addons, n_dt_addons, named_addon_free_many);
|
||||
CLEANUP_ARRAY(ucode_addons, n_ucode_addons, named_addon_free_many);
|
||||
load_all_addons(image, loaded_image, uname, &cmdline_addons, &dt_addons, &n_dt_addons, &ucode_addons, &n_ucode_addons);
|
||||
|
||||
/* If we have any extra command line to add via PE addons, load them now and append, and measure the
|
||||
* additions together, after the embedded options, but before the smbios ones, so that the order is
|
||||
@ -912,16 +983,20 @@ static EFI_STATUS run(EFI_HANDLE image) {
|
||||
generate_embedded_initrds(loaded_image, sections, initrds);
|
||||
lookup_embedded_initrds(loaded_image, sections, initrds);
|
||||
|
||||
/* Measures ucode addons and puts them into all_initrds */
|
||||
measure_and_append_ucode_addons(&all_initrds, &n_all_initrds, ucode_addons, n_ucode_addons, ¶meters_measured);
|
||||
/* Adds all other initrds to all_initrds */
|
||||
extend_initrds(initrds, &all_initrds, &n_all_initrds);
|
||||
|
||||
/* Export variables indicating what we measured */
|
||||
export_pcr_variables(sections_measured, parameters_measured, sysext_measured, confext_measured);
|
||||
|
||||
/* Combine the initrds into one */
|
||||
_cleanup_pages_ Pages initrd_pages = {};
|
||||
struct iovec final_initrd;
|
||||
if (initrds_need_combine(initrds)) {
|
||||
/* If we have generated initrds dynamically or there is a microcode initrd, combine them with
|
||||
* the built-in initrd. */
|
||||
err = combine_initrds(initrds, _INITRD_MAX, &initrd_pages, &final_initrd.iov_len);
|
||||
if (n_all_initrds > 1) {
|
||||
/* There will always be a base initrd, if this counter is higher, we need to combine them */
|
||||
err = combine_initrds(all_initrds, n_all_initrds, &initrd_pages, &final_initrd.iov_len);
|
||||
if (err != EFI_SUCCESS)
|
||||
return err;
|
||||
|
||||
@ -930,7 +1005,7 @@ static EFI_STATUS run(EFI_HANDLE image) {
|
||||
/* Given these might be large let's free them explicitly before we pass control to Linux */
|
||||
initrds_free(&initrds);
|
||||
} else
|
||||
final_initrd = initrds[INITRD_BASE];
|
||||
final_initrd = all_initrds[0];
|
||||
|
||||
struct iovec kernel = IOVEC_MAKE(
|
||||
(const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_LINUX].memory_offset,
|
||||
|
@ -49,3 +49,6 @@ enum {
|
||||
|
||||
/* The tag used for EV_EVENT_TAG event log records covering Devicetree blobs */
|
||||
#define DEVICETREE_ADDON_EVENT_TAG_ID UINT32_C(0x6c46f751)
|
||||
|
||||
/* The tag used for EV_EVENT_TAG event log records covering ucode addons (effectively initrds) */
|
||||
#define UCODE_ADDON_EVENT_TAG_ID UINT32_C(0xdac08e1a)
|
||||
|
Loading…
x
Reference in New Issue
Block a user