diff options
author | Lennart Poettering <lennart@poettering.net> | 2021-09-23 21:17:05 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-23 21:17:05 +0200 |
commit | 37eb7108fbe67550e92ed97e092fc0507bd4b4b1 (patch) | |
tree | 2818a8b4fb04b73cb3ff2bd76bb0213e588e47a3 | |
parent | f7c9ade22fcd0156fb76a3e1dbdac76883f43472 (diff) | |
parent | 8e8415e0d5d6714491180abe4e7d058f93bdf1c9 (diff) | |
download | systemd-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.build | 4 | ||||
-rw-r--r-- | man/systemd-boot.xml | 43 | ||||
-rw-r--r-- | man/systemd-stub.xml | 204 | ||||
-rw-r--r-- | meson.build | 2 | ||||
-rw-r--r-- | meson_options.txt | 2 | ||||
-rw-r--r-- | src/boot/bootctl.c | 1 | ||||
-rw-r--r-- | src/boot/efi/boot.c | 255 | ||||
-rw-r--r-- | src/boot/efi/cpio.c | 466 | ||||
-rw-r--r-- | src/boot/efi/cpio.h | 15 | ||||
-rw-r--r-- | src/boot/efi/devicetree.c | 24 | ||||
-rw-r--r-- | src/boot/efi/drivers.c | 150 | ||||
-rw-r--r-- | src/boot/efi/drivers.h | 9 | ||||
-rw-r--r-- | src/boot/efi/linux.c | 41 | ||||
-rw-r--r-- | src/boot/efi/measure.c | 14 | ||||
-rw-r--r-- | src/boot/efi/measure.h | 14 | ||||
-rw-r--r-- | src/boot/efi/meson.build | 5 | ||||
-rw-r--r-- | src/boot/efi/random-seed.c | 6 | ||||
-rw-r--r-- | src/boot/efi/splash.c | 27 | ||||
-rw-r--r-- | src/boot/efi/splash.h | 2 | ||||
-rw-r--r-- | src/boot/efi/stub.c | 232 | ||||
-rw-r--r-- | src/boot/efi/util.c | 238 | ||||
-rw-r--r-- | src/boot/efi/util.h | 72 | ||||
-rw-r--r-- | src/fundamental/efi-loader-features.h | 1 |
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) |