1
0
mirror of https://github.com/systemd/systemd synced 2026-03-28 17:54:51 +01:00

Compare commits

...

30 Commits

Author SHA1 Message Date
Lennart Poettering
37eb7108fb
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
2021-09-23 21:17:05 +02:00
Lennart Poettering
f7c9ade22f smack-util: tiny simplification 2021-09-23 21:15:46 +02:00
Lennart Poettering
8e8415e0d5 boot: prefer IMAGE_VERSION from os-release as version string
If the field exists it's probably the best version we have for sorting,
since it will change on every single OS image update.
2021-09-23 17:48:26 +02:00
Lennart Poettering
99d51ed939 man: document the new systemd-boot drop-in driver dir logic 2021-09-23 17:24:28 +02:00
Lennart Poettering
b8cdb66279 man: the sd-boot menu is probably more "textual" then "graphical", hence don't lie
(also, most communication happens between boot loader and OS, only
seldom stuff goes the other way, hence mention that the boot loader
first)
2021-09-23 17:24:28 +02:00
Lennart Poettering
3f9a615dcf man: add man page for the systemd UEFI stub
Fixes: #17215
2021-09-23 17:24:28 +02:00
Lennart Poettering
3a6249127e boot: add get_os_indications_supported() helper
We inquire the EFI var for this at two places, let's add a helper that
queries it and gracefully handles it if we can't get it, by returning a
zero mask, i.e. no features supported.
2021-09-23 17:24:28 +02:00
Lennart Poettering
0d43ce5266 stub: various modernizations to linux.c
Let's make some stuff const. Most importanly call AllocatePages() with
a pointer to an EFI_PHYSICAL_ADDRESS instead of a pointer to a
pointer. On 64bit this makes no difference, but on i386 this is simply
not correct, since EFI_PHYSICAL_ADDRESS is 64bit there, even though
pointers are 32bit.
2021-09-23 17:24:28 +02:00
Lennart Poettering
a0a644be70 boot: add helper for converting EFI_PHYSICAL_ADDRESS to a pointer
This isn't trivial when trying to be compatible with 32bit archs, hence
add a set of helper macro-like functions that make the conversion safe.
2021-09-23 17:24:28 +02:00
Lennart Poettering
61b6249552 boot: move TPM conditionalization into measure.h header
Let's move conditionalization of tpm_log_load_options() into the
measure.h to encapsulate the ifdeffery a bit more.
2021-09-23 17:24:10 +02:00
Lennart Poettering
ef6ff81a53 boot: port more code over to get_file_info_harder() 2021-09-23 17:24:10 +02:00
Lennart Poettering
19c896e99c boot: port more code to readdir_harder() 2021-09-23 17:24:10 +02:00
Lennart Poettering
4cbecde937 boot: use _cleanup_freepool_ at more places 2021-09-23 17:24:10 +02:00
Lennart Poettering
77c015820c boot: port more code to use open_directory() helper 2021-09-23 17:24:10 +02:00
Lennart Poettering
2553a5482c boot: automatically load drop-in EFI drivers off the ESP
Fixes: #15617
2021-09-23 17:24:10 +02:00
Lennart Poettering
d72f81be74 boot: split out code that loads the various menu entries into helper call
Just some refactoring, no real code changes.
2021-09-23 17:24:10 +02:00
Lennart Poettering
0be2a06ac7 boot: split out code that sets various EFI vars from main()
Just some refactoring, no actual code changes.
2021-09-23 17:24:10 +02:00
Lennart Poettering
6e161dc860 stub: make splash image payload const 2021-09-23 17:24:10 +02:00
Lennart Poettering
94b81afb08 stub: show splash screen earlier
let's move showing of the splash screen to the earliest place we know
the splash bmp address. After all a splash screen is all about showing
as early as we can. This matters as doing TPM stuff or packing up a
large cpio might take time.

While we are at it, move the conditionalization of the splash screen
into the function instead of doing it ahead of calling it. This should
encapsulate things more nicely.
2021-09-23 17:24:10 +02:00
Lennart Poettering
5a186322a1 stub: split out code that sets the various efi vars into function of its own
Just some refactoring, no code changes beyond the splitting out.
2021-09-23 17:24:09 +02:00
Lennart Poettering
e6e24af507 boot: unify code that measures image options/kernel command line 2021-09-23 17:24:09 +02:00
Lennart Poettering
faacf1807e boot: stop making TPM PCR to measure kernel command line into configurable
Everyone appears to use PCR 8 for this, hence I think it's safe to
hardcode that in systemd too.

It's also documented, like here:

https://www.gnu.org/software/grub/manual/grub/html_node/Measured-Boot.html

or here:

https://github.com/rhboot/shim/blob/main/README.tpm

(And the previous name was a bit confusing, since we don't actually just
measure one thing anymore, but mutliple things into multiple PCRs...)
2021-09-23 17:24:09 +02:00
Lennart Poettering
845707aae2 stub: when booting a kernel foo.efi then pack foo.efi.extra.d/*.{cred,raw} up as synthetic initrd
This adds support for the EFI stub to look for credential files and
sysext files next to the EFI kernel image being loaded, and pack them up
in an initrd cpio image, and pass them to the kernel.

Specifically, for a kernel image foo.efi it looks for
foo.efi.extra.d/*.cred and packs these files up in an initrd, placing it
inside a directory /.extra/credentials/. It then looks for
foo.efi.extra.d/*.raw and pack these files up in an initrd, placing them
inside a directory /.extra/sysexts/. It then concatenates any other
initrd with these two initrds, so they are combined.

Or in other words auxiliary files placed next to the kernel image are
picked up automatically by the EFI stub and be made available in the
initrd in the /.extra/ directory.

What's the usecase for this? This is supposed to be useful in context of
implementing fully trusted initrds, i.e. initrds that are not built
locally on the system and unsigned/unmeasured – as we do things
currently —, but instead are built by the vendor, and measured to TPM.
The idea is that a basic initrd is always linked into the kernel EFI
image anyway. This will already be sufficient for many cases. However,
in some cases it is necessary to parameterize initrds, or to extend the
basic initrds with additional subsystems (e.g. think complex storage, or
passing server info/certificates/… to initrds). The idea is that the
parameterization is done using the "credentials" logic we already have
in systemd, with these credential files (which can optionally be
encrypted+authenticated by TPM2) being placed in the ESP next to the
kernel image. And the initrd extension via the "sysext" logic we already
have in systemd too.

Note that the files read by this code are not verified immediately, they
are copied *as-is* and placed into /.extra/ in the initrd. In a trusted
environment they need to be validated later, but before first use. For
the credentials logic this should be done via the TPM2
encryption/authentication logic. For the sysext stuff the idea is that
this is done via signed images, as implemented by #20691.
2021-09-23 17:24:09 +02:00
Lennart Poettering
80b2f4d92c boot: generalize sorting code
Let's make this generic, so that we can reuse it elsewhere later.
2021-09-23 17:23:45 +02:00
Lennart Poettering
c6dfe36044 boot: add a bunch of new helper calls 2021-09-23 17:23:45 +02:00
Lennart Poettering
88657f7575 boot: add a way to indicate overflow in ALIGN_TO() 2021-09-23 17:10:21 +02:00
Lennart Poettering
3fc0688d42 update TODO 2021-09-23 17:07:25 +02:00
Lennart Poettering
a02c1239cc mkosi: turn off qemu headless mode
We ship the mkosi files to make sure we can test our own code. A good
chunk of our code (and the main reason to use qemu rather than nspawn)
is the EFI code, i.e. in sd-boot and the EFI stub. Hence it's bad idea
to use qemu headless mode, since that means we bypass all that.

Let's hence toggle the defaults here, but keep the line in place, to
make it easy to switch back if someone wants the speed, rather than the
testing.
2021-09-23 17:07:13 +02:00
Anssi Hannula
26d54e1263 bootctl: Fix update not adding EFI entry if Boot IDs are non-consecutive
"bootctl update" tries to add sd-boot to the EFI boot loader list if it
is not already there. To do so, it uses find_slot() which finds the
proper BootXXXX slot ID to use and also returns 1 if an existing sd-boot
entry was found at this ID or 0 if it is a new unused ID. In "update"
case install_variables() only writes the entry in case 0 (no existing
entry).

However, find_slot() erroneously returns 1 if it finds a gap in the Boot
IDs (i.e. when not resorting to max(ids) + 1). This causes
"bootctl update" to not add a missing systemd-boot boot entry if the
existing BootXXXX entry IDs are not consecutive.

Fix that by returning 0 in find_slot() when an empty gap ID is selected
to make it match the behavior when selecting an empty non-gap ID.
2021-09-23 17:05:20 +02:00
Alberto Mardegan
4db9e01f7f docs: clarify order of events in cgroup scope creation 2021-09-23 16:02:03 +01:00
27 changed files with 1593 additions and 279 deletions

8
TODO
View File

@ -83,6 +83,14 @@ Janitorial Clean-ups:
Features:
* we probably should extend the root verity hash of the root fs into some PCR
on boot. (i.e. maybe add a crypttab option tpm2-measure=8 or so to measure it
into PCR 8)
* add a "policy" to the dissection logic. i.e. a bit mask what is OK to mount,
what must be read-only, what requires encryption, and what requires
authentication.
* in uefi stub: query firmware regarding which PCRs are being used, store that
in EFI var. then use this when enrolling TPM2 in cryptsetup to verify that
the selected PCRs actually are used by firmware.

View File

@ -309,10 +309,12 @@ You basically have three options:
3. 🙁 The *i-like-continents* option. In this option you'd leave your manager
daemon where it is, and would not turn on delegation on its unit. However,
as first thing you register a new scope unit with systemd, and that scope
unit would have `Delegate=` turned on, and then you place all your
containers underneath it. From systemd's PoV there'd be two units: your
manager service and the big scope that contains all your containers in one.
as you start your first managed process (a container, for example) you would
register a new scope unit with systemd, and that scope unit would have
`Delegate=` turned on, and it would contain the PID of this process; all
your managed processes subsequently created should also be moved into this
scope. From systemd's PoV there'd be two units: your manager service and the
big scope that contains all your managed processes in one.
BTW: if for whatever reason you say "I hate D-Bus, I'll never call any D-Bus
API, kthxbye", then options #1 and #3 are not available, as they generally

View File

@ -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',

View File

@ -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>

204
man/systemd-stub.xml Normal file
View File

@ -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>

View File

@ -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

View File

@ -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,

View File

@ -14,7 +14,7 @@ InstallDirectory=mkosi.installdir
SourceFileTransferFinal=copy-git-others
[Host]
QemuHeadless=yes
QemuHeadless=no
NetworkVeth=yes
[Validation]

View File

@ -722,7 +722,7 @@ static int find_slot(sd_id128_t uuid, const char *path, uint16_t *id) {
for (i = 0; i < n; i++)
if (i != options[i]) {
*id = i;
return 1;
return 0;
}
/* use the next one */
@ -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;

View File

@ -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,14 +2424,82 @@ 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);
}
static VOID config_load_all_entries(
Config *config,
EFI_LOADED_IMAGE *loaded_image,
const CHAR16 *loaded_image_path,
EFI_FILE *root_dir) {
assert(config);
assert(loaded_image);
assert(loaded_image_path);
assert(root_dir);
config_load_defaults(config, root_dir);
/* scan /EFI/Linux/ directory */
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);
/* Similar, but on any XBOOTLDR partition */
config_load_xbootldr(config, loaded_image->DeviceHandle);
/* sort entries after version number */
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,
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,
L"auto-efi-default", '\0', L"EFI Default Loader", NULL);
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)
@ -2490,39 +2511,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
return log_error_status_stall(err, L"Error installing security policy: %r", err);
}
/* 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);
(VOID) load_drivers(image, loaded_image, root_dir);
config_load_defaults(&config, root_dir);
/* scan /EFI/Linux/ directory */
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);
/* Similar, but on any XBOOTLDR partition */
config_load_xbootldr(&config, loaded_image->DeviceHandle);
/* sort entries after version number */
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,
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,
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);
}
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.");
@ -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;
}

466
src/boot/efi/cpio.c Normal file
View File

@ -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;
}

15
src/boot/efi/cpio.h Normal file
View File

@ -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);

View File

@ -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) {

150
src/boot/efi/drivers.c Normal file
View File

@ -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;
}

9
src/boot/efi/drivers.h Normal file
View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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(',')

View File

@ -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)

View File

@ -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) {

View File

@ -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);

View File

@ -3,6 +3,7 @@
#include <efi.h>
#include <efilib.h>
#include "cpio.h"
#include "disk.h"
#include "graphics.h"
#include "linux.h"
@ -15,72 +16,84 @@
/* magic string to find in the binary image */
static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-stub " GIT_VERSION " ####";
EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
EFI_LOADED_IMAGE *loaded_image;
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) {
enum {
SECTION_CMDLINE,
SECTION_LINUX,
SECTION_INITRD,
SECTION_SPLASH,
_SECTION_MAX,
};
const CHAR8* const sections[] = {
[SECTION_CMDLINE] = (const CHAR8*) ".cmdline",
[SECTION_LINUX] = (const CHAR8*) ".linux",
[SECTION_INITRD] = (const CHAR8*) ".initrd",
[SECTION_SPLASH] = (const CHAR8*) ".splash",
NULL,
};
EFI_PHYSICAL_ADDRESS linux_base, initrd_base;
UINTN cmdline_len = 0, initrd_size;
UINTN addrs[_SECTION_MAX] = {};
UINTN szs[_SECTION_MAX] = {};
CHAR8 *cmdline = NULL;
CHAR16 uuid[37];
EFI_PHYSICAL_ADDRESS base = UINT32_MAX; /* allocate an area below the 32bit boundary for this */
EFI_STATUS err;
UINT8 *p;
UINTN n;
InitializeLib(image, sys_table);
assert(ret_initrd_base);
assert(ret_initrd_size);
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);
/* Combines three initrds into one, by simple concatenation in memory */
err = pe_memory_locate_sections(loaded_image->ImageBase, (const CHAR8**) sections, addrs, szs);
if (EFI_ERROR(err))
return log_error_status_stall(err, L"Unable to locate embedded .linux section: %r", err);
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;
if (szs[SECTION_CMDLINE] > 0) {
cmdline = (CHAR8*) loaded_image->ImageBase + addrs[SECTION_CMDLINE];
cmdline_len = szs[SECTION_CMDLINE];
n += credential_initrd_size;
}
if (sysext_initrd) {
if (n > UINTN_MAX - sysext_initrd_size)
return EFI_OUT_OF_RESOURCES;
n += sysext_initrd_size;
}
/* if we are not in secure boot mode, or none was provided, accept a custom command line and replace the built-in one */
if ((!secure_boot_enabled() || cmdline_len == 0) && loaded_image->LoadOptionsSize > 0 &&
*(CHAR16 *) loaded_image->LoadOptions > 0x1F) {
CHAR16 *options;
CHAR8 *line;
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);
options = (CHAR16 *)loaded_image->LoadOptions;
cmdline_len = (loaded_image->LoadOptionsSize / sizeof(CHAR16)) * sizeof(CHAR8);
line = AllocatePool(cmdline_len);
for (UINTN i = 0; i < cmdline_len; i++)
line[i] = options[i];
cmdline = line;
p = PHYSICAL_ADDRESS_TO_POINTER(base);
if (initrd_base != 0) {
UINTN pad;
#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
/* 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)
@ -119,17 +132,124 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
/* 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);
EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
linux_base = (EFI_PHYSICAL_ADDRESS) (UINTN) loaded_image->ImageBase + addrs[SECTION_LINUX];
enum {
SECTION_CMDLINE,
SECTION_LINUX,
SECTION_INITRD,
SECTION_SPLASH,
_SECTION_MAX,
};
const CHAR8* const sections[] = {
[SECTION_CMDLINE] = (const CHAR8*) ".cmdline",
[SECTION_LINUX] = (const CHAR8*) ".linux",
[SECTION_INITRD] = (const CHAR8*) ".initrd",
[SECTION_SPLASH] = (const CHAR8*) ".splash",
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;
EFI_LOADED_IMAGE *loaded_image;
UINTN addrs[_SECTION_MAX] = {};
UINTN szs[_SECTION_MAX] = {};
CHAR8 *cmdline = NULL;
EFI_STATUS err;
InitializeLib(image, sys_table);
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);
err = pe_memory_locate_sections(loaded_image->ImageBase, (const CHAR8**) sections, addrs, szs);
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];
}
/* if we are not in secure boot mode, or none was provided, accept a custom command line and replace the built-in one */
if ((!secure_boot_enabled() || cmdline_len == 0) && loaded_image->LoadOptionsSize > 0 &&
*(CHAR16 *) loaded_image->LoadOptions > 0x1F) {
CHAR16 *options;
CHAR8 *line;
options = (CHAR16 *)loaded_image->LoadOptions;
cmdline_len = (loaded_image->LoadOptionsSize / sizeof(CHAR16)) * sizeof(CHAR8);
line = AllocatePool(cmdline_len);
for (UINTN i = 0; i < cmdline_len; i++)
line[i] = options[i];
cmdline = line;
/* 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);
}
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);
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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)

View File

@ -286,14 +286,13 @@ int mac_smack_copy(const char *dest, const char *src) {
#endif
int rename_and_apply_smack_floor_label(const char *from, const char *to) {
int r = 0;
if (rename(from, to) < 0)
return -errno;
#if HAVE_SMACK_RUN_LABEL
r = mac_smack_apply(to, SMACK_ATTR_ACCESS, SMACK_FLOOR_LABEL);
if (r < 0)
return r;
return mac_smack_apply(to, SMACK_ATTR_ACCESS, SMACK_FLOOR_LABEL);
#else
return 0;
#endif
return r;
}