summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2021-09-23 21:17:05 +0200
committerGitHub <noreply@github.com>2021-09-23 21:17:05 +0200
commit37eb7108fbe67550e92ed97e092fc0507bd4b4b1 (patch)
tree2818a8b4fb04b73cb3ff2bd76bb0213e588e47a3
parentf7c9ade22fcd0156fb76a3e1dbdac76883f43472 (diff)
parent8e8415e0d5d6714491180abe4e7d058f93bdf1c9 (diff)
downloadsystemd-37eb7108fbe67550e92ed97e092fc0507bd4b4b1.tar.gz
Merge pull request #20789 from poettering/initrd-cpio
efi-stub: when booting a kernel foo.efi then pack foo.efi.extra.d/*.{cred,raw} as an initrd
-rw-r--r--man/rules/meson.build4
-rw-r--r--man/systemd-boot.xml43
-rw-r--r--man/systemd-stub.xml204
-rw-r--r--meson.build2
-rw-r--r--meson_options.txt2
-rw-r--r--src/boot/bootctl.c1
-rw-r--r--src/boot/efi/boot.c255
-rw-r--r--src/boot/efi/cpio.c466
-rw-r--r--src/boot/efi/cpio.h15
-rw-r--r--src/boot/efi/devicetree.c24
-rw-r--r--src/boot/efi/drivers.c150
-rw-r--r--src/boot/efi/drivers.h9
-rw-r--r--src/boot/efi/linux.c41
-rw-r--r--src/boot/efi/measure.c14
-rw-r--r--src/boot/efi/measure.h14
-rw-r--r--src/boot/efi/meson.build5
-rw-r--r--src/boot/efi/random-seed.c6
-rw-r--r--src/boot/efi/splash.c27
-rw-r--r--src/boot/efi/splash.h2
-rw-r--r--src/boot/efi/stub.c232
-rw-r--r--src/boot/efi/util.c238
-rw-r--r--src/boot/efi/util.h72
-rw-r--r--src/fundamental/efi-loader-features.h1
23 files changed, 1566 insertions, 261 deletions
diff --git a/man/rules/meson.build b/man/rules/meson.build
index c6f343d8e5..f9c69f1846 100644
--- a/man/rules/meson.build
+++ b/man/rules/meson.build
@@ -954,6 +954,10 @@ manpages = [
['systemd-sleep.conf', '5', ['sleep.conf.d'], ''],
['systemd-socket-activate', '1', [], ''],
['systemd-socket-proxyd', '8', [], ''],
+ ['systemd-stub',
+ '7',
+ ['linuxaa64.efi.stub', 'linuxia32.efi.stub', 'linuxx64.efi.stub'],
+ 'ENABLE_EFI'],
['systemd-suspend.service',
'8',
['systemd-hibernate.service',
diff --git a/man/systemd-boot.xml b/man/systemd-boot.xml
index 8685ed50ff..b7b06bd5eb 100644
--- a/man/systemd-boot.xml
+++ b/man/systemd-boot.xml
@@ -25,7 +25,7 @@
<title>Description</title>
<para><command>systemd-boot</command> (short: <command>sd-boot</command>) is a simple UEFI boot
- manager. It provides a graphical menu to select the entry to boot and an editor for the kernel command
+ manager. It provides a textual menu to select the entry to boot and an editor for the kernel command
line. <command>systemd-boot</command> supports systems with UEFI firmware only.</para>
<para><command>systemd-boot</command> loads boot entry information from the EFI system partition (ESP),
@@ -102,6 +102,12 @@
may be used to copy kernel images onto the ESP or the Extended Boot Loader Partition and to generate
description files compliant with the Boot Loader
Specification.</para>
+
+ <para><citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+ may be used as UEFI boot stub for executed kernels, which is useful to show graphical boot splashes
+ before transitioning into the Linux world. It is also capable of automatically picking up auxiliary
+ credential files (for boot parameterization) and system extension images, as companion files to the
+ booted kernel images.</para>
</refsect1>
<refsect1>
@@ -274,25 +280,37 @@
usually mounted to <filename>/efi/</filename>, <filename>/boot/</filename> or
<filename>/boot/efi/</filename> during OS runtime. It also processes files on the Extended Boot Loader
partition which is typically mounted to <filename>/boot/</filename>, if it
- exists. <command>systemd-boot</command> reads runtime configuration such as the boot timeout and default
+ exists.</para>
+
+ <para><command>systemd-boot</command> reads runtime configuration such as the boot timeout and default
entry from <filename>/loader/loader.conf</filename> on the ESP (in combination with data read from EFI
variables). See
- <citerefentry><refentrytitle>loader.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>. Boot
- entry description files following the <ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot
- Loader Specification</ulink> are read from <filename>/loader/entries/</filename> on the ESP and the
- Extended Boot Loader partition. Unified kernel boot entries following the <ulink
+ <citerefentry><refentrytitle>loader.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
+
+ <para>Boot entry description files following the <ulink
url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink> are read from
- <filename>/EFI/Linux/</filename> on the ESP and the Extended Boot Loader partition. Optionally, a random
- seed for early boot entropy pool provisioning is stored in <filename>/loader/random-seed</filename> in
- the ESP.</para>
+ <filename>/loader/entries/</filename> on the ESP and the Extended Boot Loader partition.</para>
+
+ <para>Unified kernel boot entries following the <ulink
+ url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink> are read from
+ <filename>/EFI/Linux/</filename> on the ESP and the Extended Boot Loader partition.</para>
+
+ <para>Optionally, a random seed for early boot entropy pool provisioning is stored in
+ <filename>/loader/random-seed</filename> in the ESP.</para>
+
+ <para>During initialization, <command>sd-boot</command> automatically loads all driver files placed in
+ the <filename>/EFI/systemd/drivers/</filename> directory of the ESP. The files placed there must have an
+ extension of the EFI architecture ID followed by <filename>.efi</filename> (e.g. for x86-64 this means a
+ suffix of <filename>x64.efi</filename>). This may be used to automatically load file system drivers and
+ similar, to extend the native firmware support.</para>
</refsect1>
<refsect1>
<title>EFI Variables</title>
- <para>The following EFI variables are defined, set and read by <command>systemd-boot</command>, under the vendor
- UUID <literal>4a67b082-0a4c-41cf-b6c7-440b29bb8c4f</literal>, for communication between the OS and the boot
- loader:</para>
+ <para>The following EFI variables are defined, set and read by <command>systemd-boot</command>, under the
+ vendor UUID <literal>4a67b082-0a4c-41cf-b6c7-440b29bb8c4f</literal>, for communication between the boot
+ loader and the OS:</para>
<variablelist class='efi-variables'>
<varlistentry>
@@ -493,6 +511,7 @@
<citerefentry><refentrytitle>systemd-bless-boot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-boot-system-token.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>kernel-install</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
<ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink>,
<ulink url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader Interface</ulink>
</para>
diff --git a/man/systemd-stub.xml b/man/systemd-stub.xml
new file mode 100644
index 0000000000..2edc64ce17
--- /dev/null
+++ b/man/systemd-stub.xml
@@ -0,0 +1,204 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+
+<refentry id="systemd-stub" conditional='ENABLE_EFI'
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+ <refentryinfo>
+ <title>systemd-stub</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>systemd-stub</refentrytitle>
+ <manvolnum>7</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>systemd-stub</refname>
+ <refname>linuxx64.efi.stub</refname>
+ <refname>linuxia32.efi.stub</refname>
+ <refname>linuxaa64.efi.stub</refname>
+ <refpurpose>A simple UEFI kernel boot stub</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <para><filename>/usr/lib/systemd/boot/efi/linuxx64.efi.stub</filename></para>
+ <para><filename>/usr/lib/systemd/boot/efi/linuxia32.efi.stub</filename></para>
+ <para><filename>/usr/lib/systemd/boot/efi/linuxaa64.efi.stub</filename></para>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><command>systemd-stub</command> (stored in per-architecture files
+ <filename>linuxx64.efi.stub</filename>, <filename>linuxia32.efi.stub</filename>,
+ <filename>linuxaa64.efi.stub</filename> on disk) is a simple UEFI boot stub. An UEFI boot stub is
+ attached to a Linux kernel binary image, and is a piece of code that runs in the UEFI firmware
+ environment before transitioning into the Linux kernel environment. The UEFI boot stub ensures a Linux
+ kernel is executable as regular UEFI binary, and is able to do various preparations before switching the
+ system into the Linux world.</para>
+
+ <para>The UEFI boot stub looks for various resources for the kernel invocation inside the UEFI PE binary
+ itself. This allows combining various resources inside a single PE binary image, which may then be signed
+ via UEFI SecureBoot as a whole, covering all individual resources at once. Specifically it may
+ include:</para>
+
+ <itemizedlist>
+ <listitem><para>The ELF Linux kernel images will be looked for in the <literal>.linux</literal> PE
+ section of the executed image.</para></listitem>
+
+ <listitem><para>The initial RAM disk (initrd) will be looked for in the <literal>.initrd</literal> PE
+ section.</para></listitem>
+
+ <listitem><para>The kernel command line to pass to the invoked kernel will be looked for in the
+ <literal>.cmdline</literal> PE section.</para></listitem>
+
+ <listitem><para>A boot splash (in Windows <filename>.BMP</filename> format) to show on screen before
+ invoking the kernel will be looked for in the <literal>.splash</literal> PE section.</para></listitem>
+ </itemizedlist>
+
+ <para>If UEFI SecureBoot is enabled and the <literal>.cmdline</literal> section is present in the executed
+ image, any attempts to override the kernel command line by passing one as invocation parameters to the
+ EFI binary are ignored. Thus, in order to allow overriding the kernel command line, either disable UEFI
+ SecureBoot, or don't include a kernel command line PE section in the kernel image file. If a command line
+ is accepted via EFI invocation parameters to the EFI binary it is measured into TPM PCR 8 (if a TPM is
+ present).</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Companion Files</title>
+
+ <para>The <command>systemd-stub</command> UEFI boot stub automatically collects two types of auxiliary
+ companion files optionally placed in a drop-in directory next to the EFI binary and dynamically generates
+ <command>cpio</command> initrd archives from them, and passes them to the kernel. Specifically:</para>
+
+ <itemizedlist>
+ <listitem><para>For a kernel binary called <filename><replaceable>foo</replaceable>.efi</filename> it
+ will look for files with the <filename>.cred</filename> suffix in a directory named
+ <filename><replaceable>foo</replaceable>.efi.extra.d/</filename>, next to it. A <command>cpio</command>
+ archive is generated from all files found that way, placing them in the
+ <filename>/.extra/credentials/</filename> directory of the initrd file hierarchy. The main initrd may
+ then access them in this directory. This is supposed to be used to store auxiliary, encrypted,
+ authenticated credentials for use with <varname>LoadCredentialEncrypted=</varname> in the UEFI System
+ Partition. See
+ <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+ details on encrypted credentials. The generated <command>cpio</command> archive is measured into TPM
+ PCR 4 (if a TPM is present)</para></listitem>
+
+ <listitem><para>Similar, files <filename><replaceable>foo</replaceable>.efi.extra.d/*.raw</filename>
+ are packed up as <command>cpio</command> archive and placed in the <filename>/.extra/sysext/</filename>
+ directory in the initrd file hierarchy. This is supposed to be used to pass additional system extension
+ images to the initrd. See
+ <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry> for
+ details on system extension images. The generated <command>cpio</command> archive containing these
+ system extension images is measured into TPM PCR 8 (if a TPM is present).</para></listitem>
+ </itemizedlist>
+
+ <para>Both mechanisms may be used to parameterize and extend trusted (i.e. signed), immutable initrd
+ images in a reasonably safe way: all data they contain is measured into TPM PCRs. On access they should be
+ further validated: in case of the credentials case by encrypting/authenticating them via TPM, as exposed
+ by <command>systemd-creds encrypt -T</command> (see
+ <citerefentry><refentrytitle>systemd-creds</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+ details); in case of the system extension images by using signed Verity images.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>EFI Variables</title>
+
+ <para>The following EFI variables are defined, set and read by <command>systemd-stub</command>, under the
+ vendor UUID <literal>4a67b082-0a4c-41cf-b6c7-440b29bb8c4f</literal>, for communication between the boot
+ stub and the OS:</para>
+
+ <variablelist class='efi-variables'>
+ <varlistentry>
+ <term><varname>LoaderDevicePartUUID</varname></term>
+
+ <listitem><para>Contains the partition UUID of the EFI System Partition the EFI image was run
+ from. <citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ uses this information to automatically find the disk booted from, in order to discover various other
+ partitions on the same disk automatically.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>LoaderFirmwareInfo</varname></term>
+ <term><varname>LoaderFirmwareType</varname></term>
+
+ <listitem><para>Brief firmware information. Use
+ <citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> to view this
+ data.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>LoaderImageIdentifier</varname></term>
+
+ <listitem><para>The path of EFI executable, relative to the EFI System Partition's root
+ directory. Use
+ <citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> to view
+ this data.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>StubInfo</varname></term>
+
+ <listitem><para>Brief stub information. Use
+ <citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> to view
+ this data.</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>Note that some of the variables above may also be set by the boot loader. The stub will only set
+ them if they aren't set already. Some of these variables are defined by the <ulink
+ url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader Interface</ulink>.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Assembling Kernel Images</title>
+
+ <para>In order to assemble an UEFI PE kernel image from various components as described above, use an
+ <citerefentry><refentrytitle>objcopy</refentrytitle><manvolnum>1</manvolnum></citerefentry> command line
+ like this:</para>
+
+ <programlisting>objcopy \
+ --add-section .osrel=os-release --change-section-vma .osrel=0x20000 \
+ --add-section .cmdline=cmdline.txt --change-section-vma .cmdline=0x30000 \
+ --add-section .splash=splash.bmp --change-section-vma .splash=0x40000 \
+ --add-section .linux=vmlinux --change-section-vma .linux=0x2000000 \
+ --add-section .initrd=initrd.cpio --change-section-vma .initrd=0x3000000 \
+ /usr/lib/systemd/boot/efi/linuxx64.efi.stub \
+ foo-unsigned.efi</programlisting>
+
+ <para>This generates one PE executable file <filename>foo-unsigned.efi</filename> from the six individual
+ files for OS release information, kernel command line, boot splash image, kernel image, main initrd and
+ UEFI boot stub.</para>
+
+ <para>To then sign the resulting image for UEFI SecureBoot use an
+ <citerefentry><refentrytitle>sbsign</refentrytitle><manvolnum>1</manvolnum></citerefentry> command like
+ the following:</para>
+
+ <programlisting>sbsign \
+ --key mykey.pem \
+ --cert mykey.crt \
+ --output foo.efi \
+ foo-unsigned.efi</programlisting>
+
+ <para>This expects a pair of X.509 private key and certificate as parameters and then signs the UEFI PE
+ executable we generated above for UEFI SecureBoot and generates a signed UEFI PE executable as
+ result.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-creds</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink>,
+ <ulink url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader Interface</ulink>,
+ <citerefentry><refentrytitle>objcopy</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sbsign</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+</refentry>
diff --git a/meson.build b/meson.build
index 30609f8515..1447f55521 100644
--- a/meson.build
+++ b/meson.build
@@ -1636,8 +1636,6 @@ if get_option('efi')
have = true
conf.set_quoted('EFI_MACHINE_TYPE_NAME', EFI_MACHINE_TYPE_NAME)
-
- conf.set('SD_TPM_PCR', get_option('tpm-pcrindex'))
else
have = false
endif
diff --git a/meson_options.txt b/meson_options.txt
index b122f275da..e094d48c6e 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -416,8 +416,6 @@ option('efi-libdir', type : 'string',
description : 'path to the EFI lib directory')
option('efi-includedir', type : 'string', value : '/usr/include/efi',
description : 'path to the EFI header directory')
-option('tpm-pcrindex', type : 'integer', value : 8,
- description : 'TPM PCR register number to use')
option('sbat-distro', type : 'string',
description : 'SBAT distribution ID, e.g. fedora, or auto for autodetection')
option('sbat-distro-generation', type : 'integer', value : 1,
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index 65e5262a09..27b0086406 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -1298,6 +1298,7 @@ static int verb_status(int argc, char *argv[], void *userdata) {
{ EFI_LOADER_FEATURE_ENTRY_ONESHOT, "One-shot entry control" },
{ EFI_LOADER_FEATURE_XBOOTLDR, "Support for XBOOTLDR partition" },
{ EFI_LOADER_FEATURE_RANDOM_SEED, "Support for passing random seed to OS" },
+ { EFI_LOADER_FEATURE_LOAD_DRIVER, "Load drop-in drivers" },
};
_cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL;
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
index 074a856598..d82d679a47 100644
--- a/src/boot/efi/boot.c
+++ b/src/boot/efi/boot.c
@@ -7,6 +7,7 @@
#include "console.h"
#include "devicetree.h"
#include "disk.h"
+#include "drivers.h"
#include "efi-loader-features.h"
#include "graphics.h"
#include "linux.h"
@@ -366,7 +367,7 @@ static UINTN entry_lookup_key(Config *config, UINTN start, CHAR16 key) {
}
static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
- UINT64 key, indvar;
+ UINT64 key;
UINTN timeout;
BOOLEAN modevar;
_cleanup_freepool_ CHAR16 *partstr = NULL, *defaultstr = NULL;
@@ -394,8 +395,7 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
if (shim_loaded())
Print(L"Shim: present\n");
- if (efivar_get_uint64_le(EFI_GLOBAL_GUID, L"OsIndicationsSupported", &indvar) == EFI_SUCCESS)
- Print(L"OsIndicationsSupported: %d\n", indvar);
+ Print(L"OsIndicationsSupported: %d\n", get_os_indications_supported());
Print(L"\n--- press key ---\n\n");
console_key_read(&key, 0);
@@ -517,7 +517,8 @@ static BOOLEAN menu_run(
BOOLEAN refresh = TRUE, highlight = FALSE;
UINTN x_start = 0, y_start = 0, y_status = 0;
UINTN x_max, y_max;
- CHAR16 **lines = NULL, *status = NULL, *clearline = NULL;
+ CHAR16 **lines = NULL;
+ _cleanup_freepool_ CHAR16 *clearline = NULL, *status = NULL;
UINTN timeout_remain = config->timeout_sec;
INT16 idx;
BOOLEAN exit = FALSE, run = TRUE;
@@ -901,7 +902,6 @@ static BOOLEAN menu_run(
for (UINTN i = 0; i < config->entry_count; i++)
FreePool(lines[i]);
FreePool(lines);
- FreePool(clearline);
clear_screen(COLOR_NORMAL);
return run;
@@ -1225,7 +1225,7 @@ static VOID config_entry_bump_counters(
_cleanup_(FileHandleClosep) EFI_FILE_HANDLE handle = NULL;
static const EFI_GUID EfiFileInfoGuid = EFI_FILE_INFO_ID;
_cleanup_freepool_ EFI_FILE_INFO *file_info = NULL;
- UINTN file_info_size, a, b;
+ UINTN file_info_size;
EFI_STATUS r;
assert(entry);
@@ -1243,26 +1243,9 @@ static VOID config_entry_bump_counters(
if (EFI_ERROR(r))
return;
- a = StrLen(entry->current_name);
- b = StrLen(entry->next_name);
-
- file_info_size = OFFSETOF(EFI_FILE_INFO, FileName) + (a > b ? a : b) + 1;
-
- for (;;) {
- file_info = AllocatePool(file_info_size);
-
- r = uefi_call_wrapper(handle->GetInfo, 4, handle, (EFI_GUID*) &EfiFileInfoGuid, &file_info_size, file_info);
- if (!EFI_ERROR(r))
- break;
-
- if (r != EFI_BUFFER_TOO_SMALL || file_info_size * 2 < file_info_size) {
- log_error_stall(L"Failed to get file info for '%s': %r", old_path, r);
- return;
- }
-
- file_info_size *= 2;
- FreePool(file_info);
- }
+ r = get_file_info_harder(handle, &file_info, &file_info_size);
+ if (EFI_ERROR(r))
+ return;
/* And rename the file */
StrCpy(file_info->FileName, entry->next_name);
@@ -1487,9 +1470,11 @@ static VOID config_load_entries(
Config *config,
EFI_HANDLE *device,
EFI_FILE *root_dir,
- CHAR16 *loaded_image_path) {
+ const CHAR16 *loaded_image_path) {
_cleanup_(FileHandleClosep) EFI_FILE_HANDLE entries_dir = NULL;
+ _cleanup_freepool_ EFI_FILE_INFO *f = NULL;
+ UINTN f_size = 0;
EFI_STATUS err;
assert(config);
@@ -1497,22 +1482,17 @@ static VOID config_load_entries(
assert(root_dir);
assert(loaded_image_path);
- err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &entries_dir, (CHAR16*) L"\\loader\\entries", EFI_FILE_MODE_READ, 0ULL);
+ err = open_directory(root_dir, L"\\loader\\entries", &entries_dir);
if (EFI_ERROR(err))
return;
for (;;) {
- CHAR16 buf[256];
- UINTN bufsize;
- EFI_FILE_INFO *f;
_cleanup_freepool_ CHAR8 *content = NULL;
- bufsize = sizeof(buf);
- err = uefi_call_wrapper(entries_dir->Read, 3, entries_dir, &bufsize, buf);
- if (bufsize == 0 || EFI_ERROR(err))
+ err = readdir_harder(entries_dir, &f, &f_size);
+ if (f_size == 0 || EFI_ERROR(err))
break;
- f = (EFI_FILE_INFO *) buf;
if (f->FileName[0] == '.')
continue;
if (f->Attribute & EFI_FILE_DIRECTORY)
@@ -1529,7 +1509,7 @@ static VOID config_load_entries(
}
}
-static INTN config_entry_compare(ConfigEntry *a, ConfigEntry *b) {
+static INTN config_entry_compare(const ConfigEntry *a, const ConfigEntry *b) {
INTN r;
assert(a);
@@ -1567,24 +1547,7 @@ static INTN config_entry_compare(ConfigEntry *a, ConfigEntry *b) {
static VOID config_sort_entries(Config *config) {
assert(config);
- for (UINTN i = 1; i < config->entry_count; i++) {
- BOOLEAN more;
-
- more = FALSE;
- for (UINTN k = 0; k < config->entry_count - i; k++) {
- ConfigEntry *entry;
-
- if (config_entry_compare(config->entries[k], config->entries[k+1]) <= 0)
- continue;
-
- entry = config->entries[k];
- config->entries[k] = config->entries[k+1];
- config->entries[k+1] = entry;
- more = TRUE;
- }
- if (!more)
- break;
- }
+ sort_pointer_array((void**) config->entries, config->entry_count, (compare_pointer_func_t) config_entry_compare);
}
static INTN config_entry_find(Config *config, CHAR16 *id) {
@@ -1978,21 +1941,23 @@ static VOID config_entry_add_linux(
EFI_FILE *root_dir) {
_cleanup_(FileHandleClosep) EFI_FILE_HANDLE linux_dir = NULL;
- EFI_STATUS err;
+ _cleanup_freepool_ EFI_FILE_INFO *f = NULL;
ConfigEntry *entry;
+ UINTN f_size = 0;
+ EFI_STATUS err;
assert(config);
assert(device);
assert(root_dir);
- err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &linux_dir, (CHAR16*) L"\\EFI\\Linux", EFI_FILE_MODE_READ, 0ULL);
+ err = open_directory(root_dir, L"\\EFI\\Linux", &linux_dir);
if (EFI_ERROR(err))
return;
for (;;) {
- CHAR16 buf[256];
- UINTN bufsize = sizeof buf;
- EFI_FILE_INFO *f;
+ _cleanup_freepool_ CHAR16 *os_name_pretty = NULL, *os_name = NULL, *os_id = NULL,
+ *os_version = NULL, *os_version_id = NULL, *os_build_id = NULL, *os_image_version = NULL;
+ _cleanup_freepool_ CHAR8 *content = NULL;
const CHAR8 *sections[] = {
(CHAR8 *)".osrel",
(CHAR8 *)".cmdline",
@@ -2000,22 +1965,14 @@ static VOID config_entry_add_linux(
};
UINTN offs[ELEMENTSOF(sections)-1] = {};
UINTN szs[ELEMENTSOF(sections)-1] = {};
- CHAR8 *content = NULL;
CHAR8 *line;
UINTN pos = 0;
CHAR8 *key, *value;
- CHAR16 *os_name_pretty = NULL;
- CHAR16 *os_name = NULL;
- CHAR16 *os_id = NULL;
- CHAR16 *os_version = NULL;
- CHAR16 *os_version_id = NULL;
- CHAR16 *os_build_id = NULL;
-
- err = uefi_call_wrapper(linux_dir->Read, 3, linux_dir, &bufsize, buf);
- if (bufsize == 0 || EFI_ERROR(err))
+
+ err = readdir_harder(linux_dir, &f, &f_size);
+ if (f_size == 0 || EFI_ERROR(err))
break;
- f = (EFI_FILE_INFO *) buf;
if (f->FileName[0] == '.')
continue;
if (f->Attribute & EFI_FILE_DIRECTORY)
@@ -2071,16 +2028,28 @@ static VOID config_entry_add_linux(
os_build_id = stra_to_str(value);
continue;
}
+
+ if (strcmpa((const CHAR8*) "IMAGE_VERSION", key) == 0) {
+ FreePool(os_image_version);
+ os_image_version = stra_to_str(value);
+ continue;
+ }
}
- if ((os_name_pretty || os_name) && os_id && (os_version || os_version_id || os_build_id)) {
+ if ((os_name_pretty || os_name) && os_id && (os_image_version || os_version || os_version_id || os_build_id)) {
_cleanup_freepool_ CHAR16 *path = NULL;
path = PoolPrint(L"\\EFI\\Linux\\%s", f->FileName);
- entry = config_entry_add_loader(config, device, LOADER_LINUX, f->FileName, 'l',
- os_name_pretty ?: os_name, path,
- os_version ?: (os_version_id ? : os_build_id));
+ entry = config_entry_add_loader(
+ config,
+ device,
+ LOADER_LINUX,
+ f->FileName,
+ /* key= */ 'l',
+ os_name_pretty ?: os_name,
+ path,
+ os_image_version ?: (os_version ?: (os_version_id ? : os_build_id)));
FreePool(content);
content = NULL;
@@ -2098,14 +2067,6 @@ static VOID config_entry_add_linux(
config_entry_parse_tries(entry, L"\\EFI\\Linux", f->FileName, L".efi");
}
-
- FreePool(os_name_pretty);
- FreePool(os_name);
- FreePool(os_id);
- FreePool(os_version);
- FreePool(os_version_id);
- FreePool(os_build_id);
- FreePool(content);
}
}
@@ -2364,14 +2325,8 @@ static EFI_STATUS image_start(
loaded_image->LoadOptions = options;
loaded_image->LoadOptionsSize = StrSize(loaded_image->LoadOptions);
-#if ENABLE_TPM
/* Try to log any options to the TPM, especially to catch manually edited options */
- err = tpm_log_event(SD_TPM_PCR,
- (EFI_PHYSICAL_ADDRESS) (UINTN) loaded_image->LoadOptions,
- loaded_image->LoadOptionsSize, loaded_image->LoadOptions);
- if (EFI_ERROR(err))
- log_error_stall(L"Unable to add image options measurement: %r", err);
-#endif
+ (VOID) tpm_log_load_options(options);
}
efivar_set_time_usec(LOADER_GUID, L"LoaderTimeExecUSec", 0);
@@ -2436,7 +2391,11 @@ static VOID config_write_entries_to_variable(Config *config) {
(void) efivar_set_raw(LOADER_GUID, L"LoaderEntries", buffer, sz, 0);
}
-EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
+static VOID export_variables(
+ EFI_LOADED_IMAGE *loaded_image,
+ const CHAR16 *loaded_image_path,
+ UINT64 init_usec) {
+
static const UINT64 loader_features =
EFI_LOADER_FEATURE_CONFIG_TIMEOUT |
EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT |
@@ -2445,21 +2404,15 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
EFI_LOADER_FEATURE_BOOT_COUNTING |
EFI_LOADER_FEATURE_XBOOTLDR |
EFI_LOADER_FEATURE_RANDOM_SEED |
+ EFI_LOADER_FEATURE_LOAD_DRIVER |
0;
_cleanup_freepool_ CHAR16 *infostr = NULL, *typestr = NULL;
- UINT64 osind = 0;
- EFI_LOADED_IMAGE *loaded_image;
- EFI_FILE *root_dir;
- CHAR16 *loaded_image_path;
- EFI_STATUS err;
- Config config;
- UINT64 init_usec;
- BOOLEAN menu = FALSE;
CHAR16 uuid[37];
- InitializeLib(image, sys_table);
- init_usec = time_usec();
+ assert(loaded_image);
+ assert(loaded_image_path);
+
efivar_set_time_usec(LOADER_GUID, L"LoaderTimeInitUSec", init_usec);
efivar_set(LOADER_GUID, L"LoaderInfo", L"systemd-boot " GIT_VERSION, 0);
@@ -2471,59 +2424,97 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
(void) efivar_set_uint64_le(LOADER_GUID, L"LoaderFeatures", loader_features, 0);
- err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image,
- image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
- if (EFI_ERROR(err))
- return log_error_status_stall(err, L"Error getting a LoadedImageProtocol handle: %r", err);
+ /* the filesystem path to this image, to prevent adding ourselves to the menu */
+ efivar_set(LOADER_GUID, L"LoaderImageIdentifier", loaded_image_path, 0);
/* export the device path this image is started from */
if (disk_get_part_uuid(loaded_image->DeviceHandle, uuid) == EFI_SUCCESS)
efivar_set(LOADER_GUID, L"LoaderDevicePartUUID", uuid, 0);
+}
- root_dir = LibOpenRoot(loaded_image->DeviceHandle);
- if (!root_dir)
- return log_error_status_stall(EFI_LOAD_ERROR, L"Unable to open root directory.", EFI_LOAD_ERROR);
-
- if (secure_boot_enabled() && shim_loaded()) {
- err = security_policy_install();
- if (EFI_ERROR(err))
- return log_error_status_stall(err, L"Error installing security policy: %r", err);
- }
+static VOID config_load_all_entries(
+ Config *config,
+ EFI_LOADED_IMAGE *loaded_image,
+ const CHAR16 *loaded_image_path,
+ EFI_FILE *root_dir) {
- /* the filesystem path to this image, to prevent adding ourselves to the menu */
- loaded_image_path = DevicePathToStr(loaded_image->FilePath);
- efivar_set(LOADER_GUID, L"LoaderImageIdentifier", loaded_image_path, 0);
+ assert(config);
+ assert(loaded_image);
+ assert(loaded_image_path);
+ assert(root_dir);
- config_load_defaults(&config, root_dir);
+ config_load_defaults(config, root_dir);
/* scan /EFI/Linux/ directory */
- config_entry_add_linux(&config, loaded_image->DeviceHandle, root_dir);
+ config_entry_add_linux(config, loaded_image->DeviceHandle, root_dir);
/* scan /loader/entries/\*.conf files */
- config_load_entries(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path);
+ config_load_entries(config, loaded_image->DeviceHandle, root_dir, loaded_image_path);
/* Similar, but on any XBOOTLDR partition */
- config_load_xbootldr(&config, loaded_image->DeviceHandle);
+ config_load_xbootldr(config, loaded_image->DeviceHandle);
/* sort entries after version number */
- config_sort_entries(&config);
+ config_sort_entries(config);
/* if we find some well-known loaders, add them to the end of the list */
- config_entry_add_osx(&config);
- config_entry_add_windows(&config, loaded_image->DeviceHandle, root_dir);
- config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, NULL,
+ config_entry_add_osx(config);
+ config_entry_add_windows(config, loaded_image->DeviceHandle, root_dir);
+ config_entry_add_loader_auto(config, loaded_image->DeviceHandle, root_dir, NULL,
L"auto-efi-shell", 's', L"EFI Shell", L"\\shell" EFI_MACHINE_TYPE_NAME ".efi");
- config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
+ config_entry_add_loader_auto(config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
L"auto-efi-default", '\0', L"EFI Default Loader", NULL);
- if (config.auto_firmware && efivar_get_uint64_le(EFI_GLOBAL_GUID, L"OsIndicationsSupported", &osind) == EFI_SUCCESS) {
- if (osind & EFI_OS_INDICATIONS_BOOT_TO_FW_UI)
- config_entry_add_call(&config,
- L"auto-reboot-to-firmware-setup",
- L"Reboot Into Firmware Interface",
- reboot_into_firmware);
+ if (config->auto_firmware && (get_os_indications_supported() & EFI_OS_INDICATIONS_BOOT_TO_FW_UI))
+ config_entry_add_call(config,
+ L"auto-reboot-to-firmware-setup",
+ L"Reboot Into Firmware Interface",
+ reboot_into_firmware);
+}
+
+EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
+ _cleanup_freepool_ EFI_LOADED_IMAGE *loaded_image = NULL;
+ _cleanup_(FileHandleClosep) EFI_FILE *root_dir = NULL;
+ CHAR16 *loaded_image_path;
+ EFI_STATUS err;
+ Config config;
+ UINT64 init_usec;
+ BOOLEAN menu = FALSE;
+
+ InitializeLib(image, sys_table);
+ init_usec = time_usec();
+
+ err = uefi_call_wrapper(
+ BS->OpenProtocol, 6,
+ image,
+ &LoadedImageProtocol,
+ (VOID **)&loaded_image,
+ image,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Error getting a LoadedImageProtocol handle: %r", err);
+
+ loaded_image_path = DevicePathToStr(loaded_image->FilePath);
+ if (!loaded_image_path)
+ return log_oom();
+
+ export_variables(loaded_image, loaded_image_path, init_usec);
+
+ root_dir = LibOpenRoot(loaded_image->DeviceHandle);
+ if (!root_dir)
+ return log_error_status_stall(EFI_LOAD_ERROR, L"Unable to open root directory.", EFI_LOAD_ERROR);
+
+ if (secure_boot_enabled() && shim_loaded()) {
+ err = security_policy_install();
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Error installing security policy: %r", err);
}
+ (VOID) load_drivers(image, loaded_image, root_dir);
+
+ config_load_all_entries(&config, loaded_image, loaded_image_path, root_dir);
+
if (config.entry_count == 0) {
log_error_stall(L"No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
goto out;
@@ -2601,9 +2592,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
}
err = EFI_SUCCESS;
out:
- FreePool(loaded_image_path);
config_free(&config);
- uefi_call_wrapper(root_dir->Close, 1, root_dir);
uefi_call_wrapper(BS->CloseProtocol, 4, image, &LoadedImageProtocol, image, NULL);
return err;
}
diff --git a/src/boot/efi/cpio.c b/src/boot/efi/cpio.c
new file mode 100644
index 0000000000..10a044cc92
--- /dev/null
+++ b/src/boot/efi/cpio.c
@@ -0,0 +1,466 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "cpio.h"
+#include "measure.h"
+#include "util.h"
+
+#define EXTRA_DIR_SUFFIX L".extra.d"
+
+static CHAR8* write_cpio_word(CHAR8 *p, UINT32 v) {
+ static const char hex[] = "0123456789abcdef";
+
+ assert(p);
+
+ /* Writes a CPIO header 8 character hex value */
+
+ for (UINTN i = 0; i < 8; i++)
+ p[7-i] = hex[(v >> (4 * i)) & 0xF];
+
+ return p + 8;
+}
+
+static CHAR8* mangle_filename(CHAR8 *p, const CHAR16 *f) {
+ CHAR8* w;
+
+ assert(p);
+ assert(f);
+
+ /* Basically converts UTF-16 to plain ASCII (note that we filtered non-ASCII filenames beforehand, so
+ * this operation is always safe) */
+
+ for (w = p; *f != 0; f++) {
+ assert(*f <= 0x7fu);
+
+ *(w++) = *f;
+ }
+
+ *w = 0;
+ return w;
+}
+
+static CHAR8* pad4(CHAR8 *p, const CHAR8* start) {
+ assert(p);
+ assert(start);
+ assert(p >= start);
+
+ /* Appends NUL bytes to 'p', until the address is divisable by 4, when taken relative to 'start' */
+
+ while ((p - start) % 4 != 0)
+ *(p++) = 0;
+
+ return p;
+}
+
+static EFI_STATUS pack_cpio_one(
+ const CHAR16 *fname,
+ const VOID *contents,
+ UINTN contents_size,
+ const CHAR8 *target_dir_prefix,
+ UINT32 access_mode,
+ UINT32 *inode_counter,
+ VOID **cpio_buffer,
+ UINTN *cpio_buffer_size) {
+
+ UINTN l, target_dir_prefix_size, fname_size, q;
+ CHAR8 *a;
+
+ assert(fname);
+ assert(contents_size || contents_size == 0);
+ assert(target_dir_prefix);
+ assert(inode_counter);
+ assert(cpio_buffer);
+ assert(cpio_buffer_size);
+
+ /* Serializes one file in the cpio format understood by the kernel initrd logic.
+ *
+ * See: https://www.kernel.org/doc/Documentation/early-userspace/buffer-format.txt */
+
+ if (contents_size > UINT32_MAX) /* cpio cannot deal with > 32bit file sizes */
+ return EFI_LOAD_ERROR;
+
+ if (*inode_counter == UINT32_MAX) /* more than 2^32-1 inodes? yikes. cpio doesn't support that either */
+ return EFI_OUT_OF_RESOURCES;
+
+ l = 6 + 13*8 + 1 + 1; /* Fixed CPIO header size, slash separator, and NUL byte after the file name*/
+
+ target_dir_prefix_size = strlena(target_dir_prefix);
+ if (l > UINTN_MAX - target_dir_prefix_size)
+ return EFI_OUT_OF_RESOURCES;
+ l += target_dir_prefix_size;
+
+ fname_size = StrLen(fname);
+ if (l > UINTN_MAX - fname_size)
+ return EFI_OUT_OF_RESOURCES;
+ l += fname_size; /* append space for file name */
+
+ /* CPIO can't deal with fnames longer than 2^32-1 */
+ if (target_dir_prefix_size + fname_size >= UINT32_MAX)
+ return EFI_OUT_OF_RESOURCES;
+
+ /* Align the whole header to 4 byte size */
+ l = ALIGN_TO(l, 4);
+ if (l == UINTN_MAX) /* overflow check */
+ return EFI_OUT_OF_RESOURCES;
+
+ /* Align the contents to 4 byte size */
+ q = ALIGN_TO(contents_size, 4);
+ if (q == UINTN_MAX) /* overflow check */
+ return EFI_OUT_OF_RESOURCES;
+
+ if (l > UINTN_MAX - q) /* overflow check */
+ return EFI_OUT_OF_RESOURCES;
+ l += q; /* Add contents to header */
+
+ if (*cpio_buffer_size > UINTN_MAX - l) /* overflow check */
+ return EFI_OUT_OF_RESOURCES;
+ a = ReallocatePool(*cpio_buffer, *cpio_buffer_size, *cpio_buffer_size + l);
+ if (!a)
+ return EFI_OUT_OF_RESOURCES;
+
+ *cpio_buffer = a;
+ a = (CHAR8*) *cpio_buffer + *cpio_buffer_size;
+
+ CopyMem(a, "070701", 6); /* magic ID */
+ a += 6;
+
+ a = write_cpio_word(a, (*inode_counter)++); /* inode */
+ a = write_cpio_word(a, access_mode | 0100000 /* = S_IFREG */); /* mode */
+ a = write_cpio_word(a, 0); /* uid */
+ a = write_cpio_word(a, 0); /* gid */
+ a = write_cpio_word(a, 1); /* nlink */
+
+ /* Note: we don't make any attempt to propagate the mtime here, for two reasons: it's a mess given
+ * that FAT usually is assumed to operate with timezoned timestamps, while UNIX does not. More
+ * importantly though: the modifications times would hamper our goals of providing stable
+ * measurements for the same boots. After all we extend the initrds we generate here into TPM2
+ * PCRs. */
+ a = write_cpio_word(a, 0); /* mtime */
+ a = write_cpio_word(a, contents_size); /* size */
+ a = write_cpio_word(a, 0); /* major(dev) */
+ a = write_cpio_word(a, 0); /* minor(dev) */
+ a = write_cpio_word(a, 0); /* major(rdev) */
+ a = write_cpio_word(a, 0); /* minor(rdev) */
+ a = write_cpio_word(a, target_dir_prefix_size + fname_size + 1); /* fname size */
+ a = write_cpio_word(a, 0); /* "crc" */
+
+ CopyMem(a, target_dir_prefix, target_dir_prefix_size);
+ a += target_dir_prefix_size;
+ *(a++) = '/';
+ a = mangle_filename(a, fname);
+
+ /* Pad to next multiple of 4 */
+ a = pad4(a, *cpio_buffer);
+
+ CopyMem(a, contents, contents_size);
+ a += contents_size;
+
+ /* Pad to next multiple of 4 */
+ a = pad4(a, *cpio_buffer);
+
+ assert(a == (CHAR8*) *cpio_buffer + *cpio_buffer_size + l);
+ *cpio_buffer_size += l;
+
+ return EFI_SUCCESS;
+}
+
+static EFI_STATUS pack_cpio_dir(
+ const CHAR8 *path,
+ UINT32 access_mode,
+ UINT32 *inode_counter,
+ VOID **cpio_buffer,
+ UINTN *cpio_buffer_size) {
+
+ UINTN l, path_size;
+ CHAR8 *a;
+
+ assert(path);
+ assert(inode_counter);
+ assert(cpio_buffer);
+ assert(cpio_buffer_size);
+
+ /* Serializes one directory inode in cpio format. Note that cpio archives must first create the dirs
+ * they want to place files in. */
+
+ if (*inode_counter == UINT32_MAX)
+ return EFI_OUT_OF_RESOURCES;
+
+ l = 6 + 13*8 + 1; /* Fixed CPIO header size, and NUL byte after the file name*/
+
+ path_size = strlena(path);
+ if (l > UINTN_MAX - path_size)
+ return EFI_OUT_OF_RESOURCES;
+ l += path_size;
+
+ /* Align the whole header to 4 byte size */
+ l = ALIGN_TO(l, 4);
+ if (l == UINTN_MAX) /* overflow check */
+ return EFI_OUT_OF_RESOURCES;
+
+ if (*cpio_buffer_size > UINTN_MAX - l) /* overflow check */
+ return EFI_OUT_OF_RESOURCES;
+ a = ReallocatePool(*cpio_buffer, *cpio_buffer_size, *cpio_buffer_size + l);
+ if (!a)
+ return EFI_OUT_OF_RESOURCES;
+
+ *cpio_buffer = a;
+ a = (CHAR8*) *cpio_buffer + *cpio_buffer_size;
+
+ CopyMem(a, "070701", 6); /* magic ID */
+ a += 6;
+
+ a = write_cpio_word(a, (*inode_counter)++); /* inode */
+ a = write_cpio_word(a, access_mode | 0040000 /* = S_IFDIR */); /* mode */
+ a = write_cpio_word(a, 0); /* uid */
+ a = write_cpio_word(a, 0); /* gid */
+ a = write_cpio_word(a, 1); /* nlink */
+ a = write_cpio_word(a, 0); /* mtime */
+ a = write_cpio_word(a, 0); /* size */
+ a = write_cpio_word(a, 0); /* major(dev) */
+ a = write_cpio_word(a, 0); /* minor(dev) */
+ a = write_cpio_word(a, 0); /* major(rdev) */
+ a = write_cpio_word(a, 0); /* minor(rdev) */
+ a = write_cpio_word(a, path_size + 1); /* fname size */
+ a = write_cpio_word(a, 0); /* "crc" */
+
+ CopyMem(a, path, path_size + 1);
+ a += path_size + 1;
+
+ /* Pad to next multiple of 4 */
+ a = pad4(a, *cpio_buffer);
+
+ assert(a == (CHAR8*) *cpio_buffer + *cpio_buffer_size + l);
+
+ *cpio_buffer_size += l;
+ return EFI_SUCCESS;
+}
+
+static EFI_STATUS pack_cpio_prefix(
+ const CHAR8 *path,
+ UINT32 dir_mode,
+ UINT32 *inode_counter,
+ VOID **cpio_buffer,
+ UINTN *cpio_buffer_size) {
+
+ EFI_STATUS err;
+
+ assert(path);
+ assert(inode_counter);
+ assert(cpio_buffer);
+ assert(cpio_buffer_size);
+
+ /* Serializes directory inodes of all prefix paths of the specified path in cpio format. Note that
+ * (similar to mkdir -p behaviour) all leading paths are created with 0555 access mode, only the
+ * final dir is created with the specified directory access mode. */
+
+ for (const CHAR8 *p = path;;) {
+ const CHAR8 *e;
+
+ e = strchra(p, '/');
+ if (!e)
+ break;
+
+ if (e > p) {
+ _cleanup_freepool_ CHAR8 *t = NULL;
+
+ t = strndup8(path, e - path);
+ if (!t)
+ return EFI_OUT_OF_RESOURCES;
+
+ err = pack_cpio_dir(t, 0555, inode_counter, cpio_buffer, cpio_buffer_size);
+ if (EFI_ERROR(err))
+ return err;
+ }
+
+ p = e + 1;
+ }
+
+ return pack_cpio_dir(path, dir_mode, inode_counter, cpio_buffer, cpio_buffer_size);
+}
+
+static EFI_STATUS pack_cpio_trailer(
+ VOID **cpio_buffer,
+ UINTN *cpio_buffer_size) {
+
+ static const char trailer[] =
+ "070701"
+ "00000000"
+ "00000000"
+ "00000000"
+ "00000000"
+ "00000001"
+ "00000000"
+ "00000000"
+ "00000000"
+ "00000000"
+ "00000000"
+ "00000000"
+ "0000000B"
+ "00000000"
+ "TRAILER!!!\0\0\0"; /* There's a fourth NUL byte appended here, because this is a string */
+
+ VOID *a;
+
+ /* Generates the cpio trailer record that indicates the end of our initrd cpio archive */
+
+ assert(cpio_buffer);
+ assert(cpio_buffer_size);
+ assert_cc(sizeof(trailer) % 4 == 0);
+
+ a = ReallocatePool(*cpio_buffer, *cpio_buffer_size, *cpio_buffer_size + sizeof(trailer));
+ if (!a)
+ return EFI_OUT_OF_RESOURCES;
+
+ *cpio_buffer = a;
+ CopyMem((UINT8*) *cpio_buffer + *cpio_buffer_size, trailer, sizeof(trailer));
+ *cpio_buffer_size += sizeof(trailer);
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS pack_cpio(
+ EFI_LOADED_IMAGE *loaded_image,
+ const CHAR16 *match_suffix,
+ const CHAR8 *target_dir_prefix,
+ UINT32 dir_mode,
+ UINT32 access_mode,
+ UINTN tpm_pcr,
+ const CHAR16 *tpm_description,
+ VOID **ret_buffer,
+ UINTN *ret_buffer_size) {
+
+ _cleanup_(FileHandleClosep) EFI_FILE_HANDLE root = NULL, extra_dir = NULL;
+ UINTN dirent_size = 0, buffer_size = 0, n_items = 0, n_allocated = 0;
+ _cleanup_freepool_ CHAR16 *loaded_image_path = NULL, *j = NULL;
+ _cleanup_freepool_ EFI_FILE_INFO *dirent = NULL;
+ _cleanup_(strv_freep) CHAR16 **items = NULL;
+ _cleanup_freepool_ VOID *buffer = NULL;
+ UINT32 inode = 1; /* inode counter, so that each item gets a new inode */
+ EFI_STATUS err;
+
+ assert(loaded_image);
+ assert(target_dir_prefix);
+ assert(ret_buffer);
+ assert(ret_buffer_size);
+
+ root = LibOpenRoot(loaded_image->DeviceHandle);
+ if (!root)
+ return log_error_status_stall(EFI_LOAD_ERROR, L"Unable to open root directory.");
+
+ loaded_image_path = DevicePathToStr(loaded_image->FilePath);
+ if (!loaded_image_path)
+ return log_oom();
+
+ j = PoolPrint(L"%s" EXTRA_DIR_SUFFIX, loaded_image_path);
+ if (!j)
+ return log_oom();
+
+ err = open_directory(root, j, &extra_dir);
+ if (err == EFI_NOT_FOUND) {
+ /* No extra subdir, that's totally OK */
+ *ret_buffer = NULL;
+ *ret_buffer_size = 0;
+ return EFI_SUCCESS;
+ }
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Failed to open extra directory of loaded image: %r", err);
+
+ for (;;) {
+ _cleanup_freepool_ CHAR16 *d = NULL;
+
+ err = readdir_harder(extra_dir, &dirent, &dirent_size);
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Failed to read extra directory of loaded image: %r", err);
+ if (!dirent) /* End of directory */
+ break;
+
+ if (dirent->FileName[0] == '.')
+ continue;
+ if (dirent->Attribute & EFI_FILE_DIRECTORY)
+ continue;
+ if (match_suffix && !endswith_no_case(dirent->FileName, match_suffix))
+ continue;
+ if (!is_ascii(dirent->FileName))
+ continue;
+ if (StrLen(dirent->FileName) > 255) /* Max filename size on Linux */
+ continue;
+
+ d = StrDuplicate(dirent->FileName);
+ if (!d)
+ return log_oom();
+
+ if (n_items+2 > n_allocated) {
+ UINTN m;
+
+ /* We allocate 16 entries at a time, as a matter of optimization */
+ if (n_items > (UINTN_MAX / sizeof(UINT16)) - 16) /* Overflow check, just in case */
+ return log_oom();
+
+ m = n_items + 16;
+ items = ReallocatePool(items, n_allocated * sizeof(UINT16*), m * sizeof(UINT16*));
+ if (!items)
+ return log_oom();
+
+ n_allocated = m;
+ }
+
+ items[n_items++] = TAKE_PTR(d);
+ items[n_items] = NULL; /* Let's always NUL terminate, to make freeing via strv_free() easy */
+ }
+
+ if (n_items == 0) {
+ /* Empty directory */
+ *ret_buffer = NULL;
+ *ret_buffer_size = 0;
+ return EFI_SUCCESS;
+ }
+
+ /* Now, sort the files we found, to make this uniform and stable (and to ensure the TPM measurements
+ * are not dependent on read order) */
+ sort_pointer_array((VOID**) items, n_items, (compare_pointer_func_t) StrCmp);
+
+ /* Generate the leading directory inodes right before adding the first files, to the
+ * archive. Otherwise the cpio archive cannot be unpacked, since the leading dirs won't exist. */
+ err = pack_cpio_prefix(target_dir_prefix, dir_mode, &inode, &buffer, &buffer_size);
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Failed to pack cpio prefix: %r", err);
+
+ for (UINTN i = 0; i < n_items; i++) {
+ _cleanup_freepool_ CHAR8 *content = NULL;
+ UINTN contentsize;
+
+ err = file_read(extra_dir, items[i], 0, 0, &content, &contentsize);
+ if (EFI_ERROR(err)) {
+ log_error_status_stall(err, L"Failed to read %s, ignoring: %r", items[i], err);
+ continue;
+ }
+
+ err = pack_cpio_one(
+ items[i],
+ content, contentsize,
+ target_dir_prefix,
+ access_mode,
+ &inode,
+ &buffer, &buffer_size);
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Failed to pack cpio file %s: %r", dirent->FileName, err);
+ }
+
+ err = pack_cpio_trailer(&buffer, &buffer_size);
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Failed to pack cpio trailer: %r");
+
+#if ENABLE_TPM
+ err = tpm_log_event(
+ tpm_pcr,
+ POINTER_TO_PHYSICAL_ADDRESS(buffer),
+ buffer_size,
+ tpm_description);
+ if (EFI_ERROR(err))
+ log_error_stall(L"Unable to add initrd TPM measurement for PCR %u (%s), ignoring: %r", tpm_pcr, tpm_description, err);
+#endif
+
+ *ret_buffer = TAKE_PTR(buffer);
+ *ret_buffer_size = buffer_size;
+
+ return EFI_SUCCESS;
+}
diff --git a/src/boot/efi/cpio.h b/src/boot/efi/cpio.h
new file mode 100644
index 0000000000..8a6d093122
--- /dev/null
+++ b/src/boot/efi/cpio.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <efi.h>
+
+EFI_STATUS pack_cpio(
+ EFI_LOADED_IMAGE *loaded_image,
+ const CHAR16 *match_suffix,
+ const CHAR8 *target_dir_prefix,
+ UINT32 dir_mode,
+ UINT32 access_mode,
+ UINTN pcr,
+ const CHAR16 *tpm_description,
+ VOID **ret_buffer,
+ UINTN *ret_buffer_size);
diff --git a/src/boot/efi/devicetree.c b/src/boot/efi/devicetree.c
index c65936bdb4..fd4b9c406c 100644
--- a/src/boot/efi/devicetree.c
+++ b/src/boot/efi/devicetree.c
@@ -28,12 +28,6 @@ static UINTN devicetree_allocated(const struct devicetree_state *state) {
return state->pages * EFI_PAGE_SIZE;
}
-static VOID *devicetree_ptr(const struct devicetree_state *state) {
- assert(state);
- assert(state->addr <= UINTN_MAX);
- return (VOID *)(UINTN)state->addr;
-}
-
static EFI_STATUS devicetree_fixup(struct devicetree_state *state, UINTN len) {
EFI_DT_FIXUP_PROTOCOL *fixup;
UINTN size;
@@ -47,24 +41,24 @@ static EFI_STATUS devicetree_fixup(struct devicetree_state *state, UINTN len) {
L"Could not locate device tree fixup protocol, skipping.");
size = devicetree_allocated(state);
- err = uefi_call_wrapper(fixup->Fixup, 4, fixup, devicetree_ptr(state), &size,
+ err = uefi_call_wrapper(fixup->Fixup, 4, fixup, PHYSICAL_ADDRESS_TO_POINTER(state->addr), &size,
EFI_DT_APPLY_FIXUPS | EFI_DT_RESERVE_MEMORY);
if (err == EFI_BUFFER_TOO_SMALL) {
EFI_PHYSICAL_ADDRESS oldaddr = state->addr;
UINTN oldpages = state->pages;
- VOID *oldptr = devicetree_ptr(state);
+ VOID *oldptr = PHYSICAL_ADDRESS_TO_POINTER(state->addr);
err = devicetree_allocate(state, size);
if (EFI_ERROR(err))
return err;
- CopyMem(devicetree_ptr(state), oldptr, len);
+ CopyMem(PHYSICAL_ADDRESS_TO_POINTER(state->addr), oldptr, len);
err = uefi_call_wrapper(BS->FreePages, 2, oldaddr, oldpages);
if (EFI_ERROR(err))
return err;
size = devicetree_allocated(state);
- err = uefi_call_wrapper(fixup->Fixup, 4, fixup, devicetree_ptr(state), &size,
+ err = uefi_call_wrapper(fixup->Fixup, 4, fixup, PHYSICAL_ADDRESS_TO_POINTER(state->addr), &size,
EFI_DT_APPLY_FIXUPS | EFI_DT_RESERVE_MEMORY);
}
@@ -91,9 +85,9 @@ EFI_STATUS devicetree_install(struct devicetree_state *state,
if (EFI_ERROR(err))
return err;
- info = LibFileInfo(handle);
- if (!info)
- return EFI_OUT_OF_RESOURCES;
+ err = get_file_info_harder(handle, &info, NULL);
+ if (EFI_ERROR(err))
+ return err;
if (info->FileSize < FDT_V1_SIZE || info->FileSize > 32 * 1024 * 1024)
/* 32MB device tree blob doesn't seem right */
return EFI_INVALID_PARAMETER;
@@ -104,7 +98,7 @@ EFI_STATUS devicetree_install(struct devicetree_state *state,
if (EFI_ERROR(err))
return err;
- err = uefi_call_wrapper(handle->Read, 3, handle, &len, devicetree_ptr(state));
+ err = uefi_call_wrapper(handle->Read, 3, handle, &len, PHYSICAL_ADDRESS_TO_POINTER(state->addr));
if (EFI_ERROR(err))
return err;
@@ -112,7 +106,7 @@ EFI_STATUS devicetree_install(struct devicetree_state *state,
if (EFI_ERROR(err))
return err;
- return uefi_call_wrapper(BS->InstallConfigurationTable, 2, &EfiDtbTableGuid, devicetree_ptr(state));
+ return uefi_call_wrapper(BS->InstallConfigurationTable, 2, &EfiDtbTableGuid, PHYSICAL_ADDRESS_TO_POINTER(state->addr));
}
void devicetree_cleanup(struct devicetree_state *state) {
diff --git a/src/boot/efi/drivers.c b/src/boot/efi/drivers.c
new file mode 100644
index 0000000000..a876c3df7f
--- /dev/null
+++ b/src/boot/efi/drivers.c
@@ -0,0 +1,150 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "drivers.h"
+#include "util.h"
+
+static VOID efi_unload_image(EFI_HANDLE *h) {
+ if (*h)
+ (VOID) uefi_call_wrapper(BS->UnloadImage, 1, *h);
+}
+
+static EFI_STATUS load_one_driver(
+ EFI_HANDLE parent_image,
+ EFI_LOADED_IMAGE *loaded_image,
+ const CHAR16 *fname) {
+
+ _cleanup_(efi_unload_image) EFI_HANDLE image = NULL;
+ _cleanup_freepool_ EFI_DEVICE_PATH *path = NULL;
+ _cleanup_freepool_ CHAR16 *spath = NULL;
+ EFI_STATUS err;
+
+ assert(parent_image);
+ assert(loaded_image);
+ assert(fname);
+
+ spath = PoolPrint(L"\\EFI\\systemd\\drivers\\%s", fname);
+ if (!spath)
+ return log_oom();
+
+ path = FileDevicePath(loaded_image->DeviceHandle, spath);
+ if (!path)
+ return log_oom();
+
+ err = uefi_call_wrapper(
+ BS->LoadImage, 6,
+ FALSE,
+ parent_image,
+ path,
+ NULL, 0,
+ &image);
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Failed to load image %s: %r", fname, err);
+
+ err = uefi_call_wrapper(
+ BS->HandleProtocol, 3,
+ image,
+ &LoadedImageProtocol,
+ (VOID **)&loaded_image);
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Failed to find protocol in driver image s: %r", fname, err);
+
+ if (loaded_image->ImageCodeType != EfiBootServicesCode &&
+ loaded_image->ImageCodeType != EfiRuntimeServicesCode)
+ return log_error_status_stall(EFI_INVALID_PARAMETER, L"Image %s is not a driver, refusing: %r", fname);
+
+ err = uefi_call_wrapper(
+ BS->StartImage, 3,
+ image,
+ NULL,
+ NULL);
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Failed to start image %s: %r", fname, err);
+
+ TAKE_PTR(image);
+ return EFI_SUCCESS;
+}
+
+static EFI_STATUS reconnect(VOID) {
+ _cleanup_freepool_ EFI_HANDLE *handles = NULL;
+ UINTN n_handles = 0;
+ EFI_STATUS err;
+
+ /* Reconnects all handles, so that any loaded drivers can take effect. */
+
+ err = uefi_call_wrapper(
+ BS->LocateHandleBuffer, 5,
+ AllHandles,
+ NULL,
+ NULL,
+ &n_handles,
+ &handles);
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Failed to get list of handles: %r", err);
+
+ for (UINTN i = 0; i < n_handles; i++) {
+ err = uefi_call_wrapper(
+ BS->ConnectController, 4,
+ handles[i],
+ NULL,
+ NULL,
+ TRUE);
+ if (err == EFI_NOT_FOUND) /* No drivers for this handle */
+ continue;
+ if (EFI_ERROR(err))
+ log_error_status_stall(err, L"Failed to reconnect handle %u, ignoring: %r", i, err);
+ }
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS load_drivers(
+ EFI_HANDLE parent_image,
+ EFI_LOADED_IMAGE *loaded_image,
+ EFI_FILE_HANDLE root_dir) {
+
+ _cleanup_(FileHandleClosep) EFI_FILE_HANDLE drivers_dir = NULL;
+ _cleanup_freepool_ EFI_FILE_INFO *dirent = NULL;
+ _cleanup_freepool_ EFI_DEVICE_PATH *path = NULL;
+ UINTN dirent_size = 0, n_succeeded = 0;
+ EFI_STATUS err;
+
+ err = open_directory(
+ root_dir,
+ L"\\EFI\\systemd\\drivers",
+ &drivers_dir);
+ if (err == EFI_NOT_FOUND)
+ return EFI_SUCCESS;
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Failed to open \\EFI\\systemd\\drivers: %r", err);
+
+ for (;;) {
+ _cleanup_freepool_ CHAR16 *d = NULL;
+
+ err = readdir_harder(drivers_dir, &dirent, &dirent_size);
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Failed to read extra directory of loaded image: %r", err);
+ if (!dirent) /* End of directory */
+ break;
+
+ if (dirent->FileName[0] == '.')
+ continue;
+ if (dirent->Attribute & EFI_FILE_DIRECTORY)
+ continue;
+ if (!endswith_no_case(dirent->FileName, EFI_MACHINE_TYPE_NAME L".efi"))
+ continue;
+
+ err = load_one_driver(parent_image, loaded_image, dirent->FileName);
+ if (EFI_ERROR(err))
+ continue;
+
+ n_succeeded++;
+ }
+
+ if (n_succeeded > 0)
+ (VOID) reconnect();
+
+ return EFI_SUCCESS;
+}
diff --git a/src/boot/efi/drivers.h b/src/boot/efi/drivers.h
new file mode 100644
index 0000000000..c192c6d44c
--- /dev/null
+++ b/src/boot/efi/drivers.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <efi.h>
+
+EFI_STATUS load_drivers(
+ EFI_HANDLE parent_image,
+ EFI_LOADED_IMAGE *loaded_image,
+ EFI_FILE_HANDLE root_dir);
diff --git a/src/boot/efi/linux.c b/src/boot/efi/linux.c
index 529325fef9..5232a3ba40 100644
--- a/src/boot/efi/linux.c
+++ b/src/boot/efi/linux.c
@@ -13,6 +13,7 @@
#endif
typedef VOID(*handover_f)(VOID *image, EFI_SYSTEM_TABLE *table, struct boot_params *params) __regparm0__;
+
static VOID linux_efi_handover(EFI_HANDLE image, struct boot_params *params) {
handover_f handover;
UINTN start = (UINTN)params->hdr.code32_start;
@@ -31,16 +32,17 @@ EFI_STATUS linux_exec(EFI_HANDLE image,
CHAR8 *cmdline, UINTN cmdline_len,
UINTN linux_addr,
UINTN initrd_addr, UINTN initrd_size) {
- struct boot_params *image_params;
+
+ const struct boot_params *image_params;
struct boot_params *boot_params;
- UINT8 setup_sectors;
EFI_PHYSICAL_ADDRESS addr;
+ UINT8 setup_sectors;
EFI_STATUS err;
assert(image);
assert(cmdline);
- image_params = (struct boot_params *) linux_addr;
+ image_params = (const struct boot_params *) linux_addr;
if (image_params->hdr.boot_flag != 0xAA55 ||
image_params->hdr.header != SETUP_MAGIC ||
@@ -48,31 +50,42 @@ EFI_STATUS linux_exec(EFI_HANDLE image,
!image_params->hdr.relocatable_kernel)
return EFI_LOAD_ERROR;
- boot_params = (struct boot_params *) 0xFFFFFFFF;
- err = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, EfiLoaderData,
- EFI_SIZE_TO_PAGES(0x4000), (EFI_PHYSICAL_ADDRESS*) &boot_params);
+ addr = UINT32_MAX; /* Below the 32bit boundary */
+ err = uefi_call_wrapper(
+ BS->AllocatePages, 4,
+ AllocateMaxAddress,
+ EfiLoaderData,
+ EFI_SIZE_TO_PAGES(0x4000),
+ &addr);
if (EFI_ERROR(err))
return err;
+ boot_params = (struct boot_params *) PHYSICAL_ADDRESS_TO_POINTER(addr);
ZeroMem(boot_params, 0x4000);
- CopyMem(&boot_params->hdr, &image_params->hdr, sizeof(struct setup_header));
+ boot_params->hdr = image_params->hdr;
boot_params->hdr.type_of_loader = 0xff;
setup_sectors = image_params->hdr.setup_sects > 0 ? image_params->hdr.setup_sects : 4;
boot_params->hdr.code32_start = (UINT32)linux_addr + (setup_sectors + 1) * 512;
if (cmdline) {
addr = 0xA0000;
- err = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, EfiLoaderData,
- EFI_SIZE_TO_PAGES(cmdline_len + 1), &addr);
+
+ err = uefi_call_wrapper(
+ BS->AllocatePages, 4,
+ AllocateMaxAddress,
+ EfiLoaderData,
+ EFI_SIZE_TO_PAGES(cmdline_len + 1),
+ &addr);
if (EFI_ERROR(err))
return err;
- CopyMem((VOID *)(UINTN)addr, cmdline, cmdline_len);
- ((CHAR8 *)(UINTN)addr)[cmdline_len] = 0;
- boot_params->hdr.cmd_line_ptr = (UINT32)addr;
+
+ CopyMem(PHYSICAL_ADDRESS_TO_POINTER(addr), cmdline, cmdline_len);
+ ((CHAR8 *) PHYSICAL_ADDRESS_TO_POINTER(addr))[cmdline_len] = 0;
+ boot_params->hdr.cmd_line_ptr = (UINT32) addr;
}
- boot_params->hdr.ramdisk_image = (UINT32)initrd_addr;
- boot_params->hdr.ramdisk_size = (UINT32)initrd_size;
+ boot_params->hdr.ramdisk_image = (UINT32) initrd_addr;
+ boot_params->hdr.ramdisk_size = (UINT32) initrd_size;
linux_efi_handover(image, boot_params);
return EFI_LOAD_ERROR;
diff --git a/src/boot/efi/measure.c b/src/boot/efi/measure.c
index fbca67bbf1..de7856bacf 100644
--- a/src/boot/efi/measure.c
+++ b/src/boot/efi/measure.c
@@ -162,4 +162,18 @@ EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UIN
return EFI_SUCCESS;
}
+EFI_STATUS tpm_log_load_options(const CHAR16 *load_options) {
+ EFI_STATUS err;
+
+ /* Measures a load options string into the TPM2, i.e. the kernel command line */
+
+ err = tpm_log_event(TPM_PCR_INDEX_KERNEL_PARAMETERS,
+ POINTER_TO_PHYSICAL_ADDRESS(load_options),
+ StrSize(load_options), load_options);
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Unable to add load options (i.e. kernel command) line measurement: %r", err);
+
+ return EFI_SUCCESS;
+}
+
#endif
diff --git a/src/boot/efi/measure.h b/src/boot/efi/measure.h
index e2873adae3..b92d0574c9 100644
--- a/src/boot/efi/measure.h
+++ b/src/boot/efi/measure.h
@@ -3,4 +3,18 @@
#include <efi.h>
+#if ENABLE_TPM
+
EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description);
+EFI_STATUS tpm_log_load_options(const CHAR16 *cmdline);
+
+#else
+
+static inline EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description) {
+ return EFI_SUCCESS;
+}
+static inline EFI_STATUS tpm_log_load_options(const CHAR16 *cmdline) {
+ return EFI_SUCCESS;
+}
+
+#endif
diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build
index a90f04e766..fef8572c6a 100644
--- a/src/boot/efi/meson.build
+++ b/src/boot/efi/meson.build
@@ -2,8 +2,10 @@
efi_headers = files('''
console.h
+ cpio.h
devicetree.h
disk.h
+ drivers.h
graphics.h
linux.h
measure.h
@@ -30,6 +32,7 @@ systemd_boot_sources = '''
boot.c
console.c
devicetree.c
+ drivers.c
random-seed.c
sha256.c
shim.c
@@ -39,6 +42,7 @@ stub_sources = '''
linux.c
splash.c
stub.c
+ cpio.c
'''.split()
if conf.get('ENABLE_EFI') == 1 and get_option('gnu-efi') != 'false'
@@ -101,7 +105,6 @@ if have_gnu_efi
efi_conf = configuration_data()
efi_conf.set_quoted('EFI_MACHINE_TYPE_NAME', EFI_MACHINE_TYPE_NAME)
efi_conf.set10('ENABLE_TPM', get_option('tpm'))
- efi_conf.set('SD_TPM_PCR', get_option('tpm-pcrindex'))
foreach ctype : ['color-normal', 'color-entry', 'color-highlight', 'color-edit']
c = get_option('efi-' + ctype).split(',')
diff --git a/src/boot/efi/random-seed.c b/src/boot/efi/random-seed.c
index d20923eacc..1505b9d93b 100644
--- a/src/boot/efi/random-seed.c
+++ b/src/boot/efi/random-seed.c
@@ -263,9 +263,9 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir, RandomSeedMode mode) {
return err;
}
- info = LibFileInfo(handle);
- if (!info)
- return log_oom();
+ err = get_file_info_harder(handle, &info, NULL);
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Failed to get file info for random seed: %r");
size = info->FileSize;
if (size < RANDOM_MAX_SIZE_MIN)
diff --git a/src/boot/efi/splash.c b/src/boot/efi/splash.c
index 0286a5a906..0071a9317b 100644
--- a/src/boot/efi/splash.c
+++ b/src/boot/efi/splash.c
@@ -37,8 +37,13 @@ struct bmp_map {
UINT8 reserved;
} _packed_;
-static EFI_STATUS bmp_parse_header(UINT8 *bmp, UINTN size, struct bmp_dib **ret_dib,
- struct bmp_map **ret_map, UINT8 **pixmap) {
+static EFI_STATUS bmp_parse_header(
+ const UINT8 *bmp,
+ UINTN size,
+ struct bmp_dib **ret_dib,
+ struct bmp_map **ret_map,
+ const UINT8 **pixmap) {
+
struct bmp_file *file;
struct bmp_dib *dib;
struct bmp_map *map;
@@ -154,10 +159,13 @@ static VOID pixel_blend(UINT32 *dst, const UINT32 source) {
*dst = (rb | g);
}
-static EFI_STATUS bmp_to_blt(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf,
- struct bmp_dib *dib, struct bmp_map *map,
- UINT8 *pixmap) {
- UINT8 *in;
+static EFI_STATUS bmp_to_blt(
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf,
+ struct bmp_dib *dib,
+ struct bmp_map *map,
+ const UINT8 *pixmap) {
+
+ const UINT8 *in;
assert(buf);
assert(dib);
@@ -246,19 +254,22 @@ static EFI_STATUS bmp_to_blt(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf,
return EFI_SUCCESS;
}
-EFI_STATUS graphics_splash(UINT8 *content, UINTN len, const EFI_GRAPHICS_OUTPUT_BLT_PIXEL *background) {
+EFI_STATUS graphics_splash(const UINT8 *content, UINTN len, const EFI_GRAPHICS_OUTPUT_BLT_PIXEL *background) {
EFI_GRAPHICS_OUTPUT_BLT_PIXEL pixel = {};
static const EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL;
struct bmp_dib *dib;
struct bmp_map *map;
- UINT8 *pixmap;
+ const UINT8 *pixmap;
UINT64 blt_size;
_cleanup_freepool_ VOID *blt = NULL;
UINTN x_pos = 0;
UINTN y_pos = 0;
EFI_STATUS err;
+ if (len == 0)
+ return EFI_SUCCESS;
+
assert(content);
if (!background) {
diff --git a/src/boot/efi/splash.h b/src/boot/efi/splash.h
index b9f74ffbf2..37ccc6b6a4 100644
--- a/src/boot/efi/splash.h
+++ b/src/boot/efi/splash.h
@@ -3,4 +3,4 @@
#include <efi.h>
-EFI_STATUS graphics_splash(UINT8 *content, UINTN len, const EFI_GRAPHICS_OUTPUT_BLT_PIXEL *background);
+EFI_STATUS graphics_splash(const UINT8 *content, UINTN len, const EFI_GRAPHICS_OUTPUT_BLT_PIXEL *background);
diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c
index 9a3c9982d9..dad0f61335 100644
--- a/src/boot/efi/stub.c
+++ b/src/boot/efi/stub.c
@@ -3,6 +3,7 @@
#include <efi.h>
#include <efilib.h>
+#include "cpio.h"
#include "disk.h"
#include "graphics.h"
#include "linux.h"
@@ -15,8 +16,125 @@
/* magic string to find in the binary image */
static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-stub " GIT_VERSION " ####";
+static EFI_STATUS combine_initrd(
+ EFI_PHYSICAL_ADDRESS initrd_base, UINTN initrd_size,
+ const VOID *credential_initrd, UINTN credential_initrd_size,
+ const VOID *sysext_initrd, UINTN sysext_initrd_size,
+ EFI_PHYSICAL_ADDRESS *ret_initrd_base, UINTN *ret_initrd_size) {
+
+ EFI_PHYSICAL_ADDRESS base = UINT32_MAX; /* allocate an area below the 32bit boundary for this */
+ EFI_STATUS err;
+ UINT8 *p;
+ UINTN n;
+
+ assert(ret_initrd_base);
+ assert(ret_initrd_size);
+
+ /* Combines three initrds into one, by simple concatenation in memory */
+
+ n = ALIGN_TO(initrd_size, 4); /* main initrd might not be padded yet */
+ if (credential_initrd) {
+ if (n > UINTN_MAX - credential_initrd_size)
+ return EFI_OUT_OF_RESOURCES;
+
+ n += credential_initrd_size;
+ }
+ if (sysext_initrd) {
+ if (n > UINTN_MAX - sysext_initrd_size)
+ return EFI_OUT_OF_RESOURCES;
+
+ n += sysext_initrd_size;
+ }
+
+ err = uefi_call_wrapper(
+ BS->AllocatePages, 4,
+ AllocateMaxAddress,
+ EfiLoaderData,
+ EFI_SIZE_TO_PAGES(n),
+ &base);
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Failed to allocate space for combined initrd: %r", err);
+
+ p = PHYSICAL_ADDRESS_TO_POINTER(base);
+ if (initrd_base != 0) {
+ UINTN pad;
+
+ /* Order matters, the real initrd must come first, since it might include microcode updates
+ * which the kernel only looks for in the first cpio archive */
+ CopyMem(p, PHYSICAL_ADDRESS_TO_POINTER(initrd_base), initrd_size);
+ p += initrd_size;
+
+ pad = ALIGN_TO(initrd_size, 4) - initrd_size;
+ if (pad > 0) {
+ ZeroMem(p, pad);
+ p += pad;
+ }
+ }
+
+ if (credential_initrd) {
+ CopyMem(p, credential_initrd, credential_initrd_size);
+ p += credential_initrd_size;
+ }
+
+ if (sysext_initrd) {
+ CopyMem(p, sysext_initrd, sysext_initrd_size);
+ p += sysext_initrd_size;
+ }
+
+ assert((UINT8*) PHYSICAL_ADDRESS_TO_POINTER(base) + n == p);
+
+ *ret_initrd_base = base;
+ *ret_initrd_size = n;
+
+ return EFI_SUCCESS;
+}
+
+static VOID export_variables(EFI_LOADED_IMAGE *loaded_image) {
+ CHAR16 uuid[37];
+
+ assert(loaded_image);
+
+ /* Export the device path this image is started from, if it's not set yet */
+ if (efivar_get_raw(LOADER_GUID, L"LoaderDevicePartUUID", NULL, NULL) != EFI_SUCCESS)
+ if (disk_get_part_uuid(loaded_image->DeviceHandle, uuid) == EFI_SUCCESS)
+ efivar_set(LOADER_GUID, L"LoaderDevicePartUUID", uuid, 0);
+
+ /* If LoaderImageIdentifier is not set, assume the image with this stub was loaded directly from the
+ * UEFI firmware without any boot loader, and hence set the LoaderImageIdentifier ourselves. Note
+ * that some boot chain loaders neither set LoaderImageIdentifier nor make FilePath available to us,
+ * in which case there's simple nothing to set for us. (The UEFI spec doesn't really say who's wrong
+ * here, i.e. whether FilePath may be NULL or not, hence handle this gracefully and check if FilePath
+ * is non-NULL explicitly.) */
+ if (efivar_get_raw(LOADER_GUID, L"LoaderImageIdentifier", NULL, NULL) != EFI_SUCCESS &&
+ loaded_image->FilePath) {
+ _cleanup_freepool_ CHAR16 *s = NULL;
+
+ s = DevicePathToStr(loaded_image->FilePath);
+ efivar_set(LOADER_GUID, L"LoaderImageIdentifier", s, 0);
+ }
+
+ /* if LoaderFirmwareInfo is not set, let's set it */
+ if (efivar_get_raw(LOADER_GUID, L"LoaderFirmwareInfo", NULL, NULL) != EFI_SUCCESS) {
+ _cleanup_freepool_ CHAR16 *s = NULL;
+
+ s = PoolPrint(L"%s %d.%02d", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
+ efivar_set(LOADER_GUID, L"LoaderFirmwareInfo", s, 0);
+ }
+
+ /* ditto for LoaderFirmwareType */
+ if (efivar_get_raw(LOADER_GUID, L"LoaderFirmwareType", NULL, NULL) != EFI_SUCCESS) {
+ _cleanup_freepool_ CHAR16 *s = NULL;
+
+ s = PoolPrint(L"UEFI %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
+ efivar_set(LOADER_GUID, L"LoaderFirmwareType", s, 0);
+ }
+
+ /* add StubInfo */
+ if (efivar_get_raw(LOADER_GUID, L"StubInfo", NULL, NULL) != EFI_SUCCESS)
+ efivar_set(LOADER_GUID, L"StubInfo", L"systemd-stub " GIT_VERSION, 0);
+}
+
EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
- EFI_LOADED_IMAGE *loaded_image;
enum {
SECTION_CMDLINE,
@@ -34,12 +152,13 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
NULL,
};
+ UINTN cmdline_len = 0, initrd_size, credential_initrd_size = 0, sysext_initrd_size = 0;
+ _cleanup_freepool_ VOID *credential_initrd = NULL, *sysext_initrd = NULL;
EFI_PHYSICAL_ADDRESS linux_base, initrd_base;
- UINTN cmdline_len = 0, initrd_size;
+ EFI_LOADED_IMAGE *loaded_image;
UINTN addrs[_SECTION_MAX] = {};
UINTN szs[_SECTION_MAX] = {};
CHAR8 *cmdline = NULL;
- CHAR16 uuid[37];
EFI_STATUS err;
InitializeLib(image, sys_table);
@@ -53,6 +172,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
if (EFI_ERROR(err))
return log_error_status_stall(err, L"Unable to locate embedded .linux section: %r", err);
+ /* Show splash screen as early as possible */
+ graphics_splash((const UINT8*) loaded_image->ImageBase + addrs[SECTION_SPLASH], szs[SECTION_SPLASH], NULL);
+
if (szs[SECTION_CMDLINE] > 0) {
cmdline = (CHAR8*) loaded_image->ImageBase + addrs[SECTION_CMDLINE];
cmdline_len = szs[SECTION_CMDLINE];
@@ -71,65 +193,63 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
line[i] = options[i];
cmdline = line;
-#if ENABLE_TPM
- /* Try to log any options to the TPM, especially manually edited options */
- err = tpm_log_event(SD_TPM_PCR,
- (EFI_PHYSICAL_ADDRESS) (UINTN) loaded_image->LoadOptions,
- loaded_image->LoadOptionsSize, loaded_image->LoadOptions);
- if (EFI_ERROR(err))
- log_error_stall(L"Unable to add image options measurement: %r", err);
-#endif
- }
-
- /* Export the device path this image is started from, if it's not set yet */
- if (efivar_get_raw(LOADER_GUID, L"LoaderDevicePartUUID", NULL, NULL) != EFI_SUCCESS)
- if (disk_get_part_uuid(loaded_image->DeviceHandle, uuid) == EFI_SUCCESS)
- efivar_set(LOADER_GUID, L"LoaderDevicePartUUID", uuid, 0);
-
- /* If LoaderImageIdentifier is not set, assume the image with this stub was loaded directly from the
- * UEFI firmware without any boot loader, and hence set the LoaderImageIdentifier ourselves. Note
- * that some boot chain loaders neither set LoaderImageIdentifier nor make FilePath available to us,
- * in which case there's simple nothing to set for us. (The UEFI spec doesn't really say who's wrong
- * here, i.e. whether FilePath may be NULL or not, hence handle this gracefully and check if FilePath
- * is non-NULL explicitly.) */
- if (efivar_get_raw(LOADER_GUID, L"LoaderImageIdentifier", NULL, NULL) != EFI_SUCCESS &&
- loaded_image->FilePath) {
- _cleanup_freepool_ CHAR16 *s = NULL;
-
- s = DevicePathToStr(loaded_image->FilePath);
- efivar_set(LOADER_GUID, L"LoaderImageIdentifier", s, 0);
- }
-
- /* if LoaderFirmwareInfo is not set, let's set it */
- if (efivar_get_raw(LOADER_GUID, L"LoaderFirmwareInfo", NULL, NULL) != EFI_SUCCESS) {
- _cleanup_freepool_ CHAR16 *s = NULL;
-
- s = PoolPrint(L"%s %d.%02d", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
- efivar_set(LOADER_GUID, L"LoaderFirmwareInfo", s, 0);
- }
-
- /* ditto for LoaderFirmwareType */
- if (efivar_get_raw(LOADER_GUID, L"LoaderFirmwareType", NULL, NULL) != EFI_SUCCESS) {
- _cleanup_freepool_ CHAR16 *s = NULL;
-
- s = PoolPrint(L"UEFI %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
- efivar_set(LOADER_GUID, L"LoaderFirmwareType", s, 0);
+ /* Let's measure the passed kernel command line into the TPM. Note that this possibly
+ * duplicates what we already did in the boot menu, if that was already used. However, since
+ * we want the boot menu to support an EFI binary, and want to this stub to be usable from
+ * any boot menu, let's measure things anyway. */
+ (VOID) tpm_log_load_options(loaded_image->LoadOptions);
}
- /* add StubInfo */
- if (efivar_get_raw(LOADER_GUID, L"StubInfo", NULL, NULL) != EFI_SUCCESS)
- efivar_set(LOADER_GUID, L"StubInfo", L"systemd-stub " GIT_VERSION, 0);
-
- if (szs[SECTION_SPLASH] > 0)
- graphics_splash((UINT8*) (UINTN) loaded_image->ImageBase + addrs[SECTION_SPLASH], szs[SECTION_SPLASH], NULL);
-
- linux_base = (EFI_PHYSICAL_ADDRESS) (UINTN) loaded_image->ImageBase + addrs[SECTION_LINUX];
+ export_variables(loaded_image);
+
+ (VOID) pack_cpio(loaded_image,
+ L".cred",
+ (const CHAR8*) ".extra/credentials",
+ /* dir_mode= */ 0500,
+ /* access_mode= */ 0400,
+ /* tpm_pcr= */ TPM_PCR_INDEX_KERNEL_PARAMETERS,
+ L"Credentials initrd",
+ &credential_initrd,
+ &credential_initrd_size);
+
+ (VOID) pack_cpio(loaded_image,
+ L".raw",
+ (const CHAR8*) ".extra/sysext",
+ /* dir_mode= */ 0555,
+ /* access_mode= */ 0444,
+ /* tpm_pcr= */ TPM_PCR_INDEX_INITRD,
+ L"System extension initrd",
+ &sysext_initrd,
+ &sysext_initrd_size);
+
+ linux_base = POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[SECTION_LINUX];
initrd_size = szs[SECTION_INITRD];
- initrd_base = initrd_size != 0 ? (EFI_PHYSICAL_ADDRESS) (UINTN) loaded_image->ImageBase + addrs[SECTION_INITRD] : 0;
+ initrd_base = initrd_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[SECTION_INITRD] : 0;
+
+ if (credential_initrd || sysext_initrd) {
+ /* If we have generated initrds dynamically, let's combine them with the built-in initrd. */
+ err = combine_initrd(
+ initrd_base, initrd_size,
+ credential_initrd, credential_initrd_size,
+ sysext_initrd, sysext_initrd_size,
+ &initrd_base, &initrd_size);
+ if (EFI_ERROR(err))
+ return err;
+
+ /* Given these might be large let's free them explicitly, quickly. */
+ if (credential_initrd) {
+ FreePool(credential_initrd);
+ credential_initrd = NULL;
+ }
+
+ if (sysext_initrd) {
+ FreePool(sysext_initrd);
+ sysext_initrd = NULL;
+ }
+ }
err = linux_exec(image, cmdline, cmdline_len, linux_base, initrd_base, initrd_size);
-
graphics_mode(FALSE);
return log_error_status_stall(err, L"Execution of embedded linux image failed: %r", err);
}
diff --git a/src/boot/efi/util.c b/src/boot/efi/util.c
index 065e1ea396..13697c9433 100644
--- a/src/boot/efi/util.c
+++ b/src/boot/efi/util.c
@@ -428,11 +428,14 @@ CHAR16 *stra_to_path(const CHAR8 *stra) {
}
CHAR8 *strchra(const CHAR8 *s, CHAR8 c) {
- assert(s);
+ if (!s)
+ return NULL;
+
do {
if (*s == c)
return (CHAR8*) s;
} while (*s++);
+
return NULL;
}
@@ -451,9 +454,9 @@ EFI_STATUS file_read(EFI_FILE_HANDLE dir, const CHAR16 *name, UINTN off, UINTN s
if (size == 0) {
_cleanup_freepool_ EFI_FILE_INFO *info = NULL;
- info = LibFileInfo(handle);
- if (!info)
- return EFI_OUT_OF_RESOURCES;
+ err = get_file_info_harder(handle, &info, NULL);
+ if (EFI_ERROR(err))
+ return err;
size = info->FileSize+1;
}
@@ -527,3 +530,230 @@ VOID clear_screen(UINTN attr) {
uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, attr);
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
}
+
+void sort_pointer_array(
+ VOID **array,
+ UINTN n_members,
+ compare_pointer_func_t compare) {
+
+ assert(array || n_members == 0);
+ assert(compare);
+
+ if (n_members <= 1)
+ return;
+
+ for (UINTN i = 1; i < n_members; i++) {
+ BOOLEAN more = FALSE;
+
+ for (UINTN k = 0; k < n_members - i; k++) {
+ void *entry;
+
+ if (compare(array[k], array[k+1]) <= 0)
+ continue;
+
+ entry = array[k];
+ array[k] = array[k+1];
+ array[k+1] = entry;
+ more = TRUE;
+ }
+ if (!more)
+ break;
+ }
+}
+
+EFI_STATUS get_file_info_harder(
+ EFI_FILE_HANDLE handle,
+ EFI_FILE_INFO **ret,
+ UINTN *ret_size) {
+
+ static const EFI_GUID EfiFileInfoGuid = EFI_FILE_INFO_ID;
+ UINTN size = OFFSETOF(EFI_FILE_INFO, FileName) + 256;
+ _cleanup_freepool_ EFI_FILE_INFO *fi = NULL;
+ EFI_STATUS err;
+
+ assert(handle);
+ assert(ret);
+
+ /* A lot like LibFileInfo() but with useful error propagation */
+
+ fi = AllocatePool(size);
+ if (!fi)
+ return EFI_OUT_OF_RESOURCES;
+
+ err = uefi_call_wrapper(handle->GetInfo, 4, handle, (EFI_GUID*) &EfiFileInfoGuid, &size, fi);
+ if (err == EFI_BUFFER_TOO_SMALL) {
+ FreePool(fi);
+ fi = AllocatePool(size); /* GetInfo tells us the required size, let's use that now */
+ if (!fi)
+ return EFI_OUT_OF_RESOURCES;
+
+ err = uefi_call_wrapper(handle->GetInfo, 4, handle, (EFI_GUID*) &EfiFileInfoGuid, &size, fi);
+ }
+
+ if (EFI_ERROR(err))
+ return err;
+
+ *ret = TAKE_PTR(fi);
+
+ if (ret_size)
+ *ret_size = size;
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS readdir_harder(
+ EFI_FILE_HANDLE handle,
+ EFI_FILE_INFO **buffer,
+ UINTN *buffer_size) {
+
+ EFI_STATUS err;
+ UINTN sz;
+
+ assert(handle);
+ assert(buffer);
+ assert(buffer_size);
+
+ /* buffer/buffer_size are both in and output parameters. Should be zero-initialized initially, and
+ * the specified buffer needs to be freed by caller, after final use. */
+
+ if (!*buffer) {
+ sz = OFFSETOF(EFI_FILE_INFO, FileName) /* + 256 */;
+
+ *buffer = AllocatePool(sz);
+ if (!*buffer)
+ return EFI_OUT_OF_RESOURCES;
+
+ *buffer_size = sz;
+ } else
+ sz = *buffer_size;
+
+ err = uefi_call_wrapper(handle->Read, 3, handle, &sz, *buffer);
+ if (err == EFI_BUFFER_TOO_SMALL) {
+ FreePool(*buffer);
+
+ *buffer = AllocatePool(sz);
+ if (!*buffer) {
+ *buffer_size = 0;
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ *buffer_size = sz;
+
+ err = uefi_call_wrapper(handle->Read, 3, handle, &sz, *buffer);
+ }
+ if (EFI_ERROR(err))
+ return err;
+
+ if (sz == 0) {
+ /* End of directory */
+ FreePool(*buffer);
+ *buffer = NULL;
+ *buffer_size = 0;
+ }
+
+ return EFI_SUCCESS;
+}
+
+UINTN strnlena(const CHAR8 *p, UINTN maxlen) {
+ UINTN c;
+
+ if (!p)
+ return 0;
+
+ for (c = 0; c < maxlen; c++)
+ if (p[c] == 0)
+ break;
+
+ return c;
+}
+
+CHAR8 *strndup8(const CHAR8 *p, UINTN sz) {
+ CHAR8 *n;
+
+ /* Following efilib's naming scheme this function would be called strndupa(), but we already have a
+ * function named like this in userspace, and it does something different there, hence to minimize
+ * confusion, let's pick a different name here */
+
+ assert(p || sz == 0);
+
+ sz = strnlena(p, sz);
+
+ n = AllocatePool(sz + 1);
+ if (!n)
+ return NULL;
+
+ if (sz > 0)
+ CopyMem(n, p, sz);
+ n[sz] = 0;
+
+ return n;
+}
+
+BOOLEAN is_ascii(const CHAR16 *f) {
+ if (!f)
+ return FALSE;
+
+ for (; *f != 0; f++)
+ if (*f > 127)
+ return FALSE;
+
+ return TRUE;
+}
+
+CHAR16 **strv_free(CHAR16 **v) {
+ if (!v)
+ return NULL;
+
+ for (CHAR16 **i = v; *i; i++)
+ FreePool(*i);
+
+ FreePool(v);
+ return NULL;
+}
+
+EFI_STATUS open_directory(
+ EFI_FILE_HANDLE root,
+ const CHAR16 *path,
+ EFI_FILE_HANDLE *ret) {
+
+ _cleanup_(FileHandleClosep) EFI_FILE_HANDLE dir = NULL;
+ _cleanup_freepool_ EFI_FILE_INFO *file_info = NULL;
+ EFI_STATUS err;
+
+ assert(root);
+
+ /* Opens a file, and then verifies it is actually a directory */
+
+ err = uefi_call_wrapper(
+ root->Open, 5,
+ root,
+ &dir,
+ (CHAR16*) path,
+ EFI_FILE_MODE_READ,
+ 0ULL);
+ if (EFI_ERROR(err))
+ return err;
+
+ err = get_file_info_harder(dir, &file_info, NULL);
+ if (EFI_ERROR(err))
+ return err;
+ if (!(file_info->Attribute & EFI_FILE_DIRECTORY))
+ return EFI_LOAD_ERROR;
+
+ *ret = TAKE_PTR(dir);
+ return EFI_SUCCESS;
+}
+
+UINT64 get_os_indications_supported(VOID) {
+ UINT64 osind;
+ EFI_STATUS err;
+
+ /* Returns the supported OS indications. If we can't acquire it, returns a zeroed out mask, i.e. no
+ * supported features. */
+
+ err = efivar_get_uint64_le(EFI_GLOBAL_GUID, L"OsIndicationsSupported", &osind);
+ if (EFI_ERROR(err))
+ return 0;
+
+ return osind;
+}
diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h
index a5b6435f1b..fa17b18f42 100644
--- a/src/boot/efi/util.h
+++ b/src/boot/efi/util.h
@@ -6,10 +6,29 @@
#include "string-util-fundamental.h"
+/* This TPM PCR is where most Linux infrastructure extends the kernel command line into, and so do we. We also extend
+ * any passed credentials here. */
+#define TPM_PCR_INDEX_KERNEL_PARAMETERS 8
+
+/* This TPM PCR is where most Linux infrastructure extends the initrd binary images into, and so do we. */
+#define TPM_PCR_INDEX_INITRD 4
+
#define OFFSETOF(x,y) __builtin_offsetof(x,y)
+#define UINTN_MAX (~(UINTN)0)
+#define INTN_MAX ((INTN)(UINTN_MAX>>1))
+#ifndef UINT32_MAX
+#define UINT32_MAX ((UINT32) -1)
+#endif
+#ifndef UINT64_MAX
+#define UINT64_MAX ((UINT64) -1)
+#endif
+
static inline UINTN ALIGN_TO(UINTN l, UINTN ali) {
- return ((l + ali - 1) & ~(ali - 1));
+ if (l > UINTN_MAX - (ali - 1)) /* Overflow? */
+ return UINTN_MAX;
+
+ return ((l + (ali - 1)) & ~(ali - 1));
}
EFI_STATUS parse_boolean(const CHAR8 *v, BOOLEAN *b);
@@ -65,15 +84,6 @@ static inline void FileHandleClosep(EFI_FILE_HANDLE *handle) {
&(const EFI_GUID) { 0x4a67b082, 0x0a4c, 0x41cf, { 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f } }
#define EFI_GLOBAL_GUID &(const EFI_GUID) EFI_GLOBAL_VARIABLE
-#define UINTN_MAX (~(UINTN)0)
-#define INTN_MAX ((INTN)(UINTN_MAX>>1))
-#ifndef UINT32_MAX
-#define UINT32_MAX ((UINT32) -1)
-#endif
-#ifndef UINT64_MAX
-#define UINT64_MAX ((UINT64) -1)
-#endif
-
VOID log_error_stall(const CHAR16 *fmt, ...);
EFI_STATUS log_oom(void);
@@ -94,3 +104,45 @@ static inline VOID *mempmem_safe(const VOID *haystack, UINTN haystack_len, const
VOID print_at(UINTN x, UINTN y, UINTN attr, const CHAR16 *str);
VOID clear_screen(UINTN attr);
+
+typedef INTN (*compare_pointer_func_t)(const VOID *a, const VOID *b);
+void sort_pointer_array(VOID **array, UINTN n_members, compare_pointer_func_t compare);
+
+EFI_STATUS get_file_info_harder(EFI_FILE_HANDLE handle, EFI_FILE_INFO **ret, UINTN *ret_size);
+
+EFI_STATUS readdir_harder(EFI_FILE_HANDLE handle, EFI_FILE_INFO **buffer, UINTN *buffer_size);
+
+UINTN strnlena(const CHAR8 *p, UINTN maxlen);
+CHAR8 *strndup8(const CHAR8 *p, UINTN sz);
+
+BOOLEAN is_ascii(const CHAR16 *f);
+
+CHAR16 **strv_free(CHAR16 **l);
+
+static inline void strv_freep(CHAR16 ***p) {
+ strv_free(*p);
+}
+
+EFI_STATUS open_directory(EFI_FILE_HANDLE root_dir, const CHAR16 *path, EFI_FILE_HANDLE *ret);
+
+/* Conversion between EFI_PHYSICAL_ADDRESS and pointers is not obvious. The former is always 64bit, even on
+ * 32bit archs. And gcc complains if we cast a pointer to an integer of a different size. Hence let's do the
+ * conversion indirectly: first into UINTN (which is defined by UEFI to have the same size as a pointer), and
+ * then extended to EFI_PHYSICAL_ADDRESS. */
+static inline EFI_PHYSICAL_ADDRESS POINTER_TO_PHYSICAL_ADDRESS(const void *p) {
+ return (EFI_PHYSICAL_ADDRESS) (UINTN) p;
+}
+
+static inline void *PHYSICAL_ADDRESS_TO_POINTER(EFI_PHYSICAL_ADDRESS addr) {
+#if __SIZEOF_POINTER__ == 4
+ /* On 32bit systems the address might not be convertible (as pointers are 32bit but
+ * EFI_PHYSICAL_ADDRESS 64bit) */
+ assert(addr <= UINT32_MAX);
+#elif __SIZEOF_POINTER__ != 8
+ #error "Unexpected pointer size"
+#endif
+
+ return (void*) (UINTN) addr;
+}
+
+UINT64 get_os_indications_supported(VOID);
diff --git a/src/fundamental/efi-loader-features.h b/src/fundamental/efi-loader-features.h
index f07dacb859..287ae3c892 100644
--- a/src/fundamental/efi-loader-features.h
+++ b/src/fundamental/efi-loader-features.h
@@ -12,3 +12,4 @@
#define EFI_LOADER_FEATURE_BOOT_COUNTING (UINT64_C(1) << 4)
#define EFI_LOADER_FEATURE_XBOOTLDR (UINT64_C(1) << 5)
#define EFI_LOADER_FEATURE_RANDOM_SEED (UINT64_C(1) << 6)
+#define EFI_LOADER_FEATURE_LOAD_DRIVER (UINT64_C(1) << 7)