<refnamediv>
<refname>ukify</refname>
- <refpurpose>Combine kernel and initrd into a signed Unified Kernel Image</refpurpose>
+ <refpurpose>Combine components into a signed Unified Kernel Image for UEFI systems</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>/usr/lib/systemd/ukify</command>
- <arg choice="plain"><replaceable>LINUX</replaceable></arg>
- <arg choice="plain" rep="repeat"><replaceable>INITRD</replaceable></arg>
+ <arg choice="opt"><replaceable>LINUX</replaceable></arg>
+ <arg choice="opt" rep="repeat"><replaceable>INITRD</replaceable></arg>
<arg choice="opt" rep="repeat">OPTIONS</arg>
</cmdsynopsis>
</refsynopsisdiv>
<para>Note: this command is experimental for now. While it is intended to become a regular component of
systemd, it might still change in behaviour and interface.</para>
- <para><command>ukify</command> is a tool that combines a kernel and an initrd with
- a UEFI boot stub to create a
+ <para><command>ukify</command> is a tool that combines components (e.g.: a kernel and an initrd with
+ a UEFI boot stub) to create a
<ulink url="https://uapi-group.org/specifications/specs/unified_kernel_image/">Unified Kernel Image (UKI)</ulink>
— a PE binary that can be executed by the firmware to start the embedded linux kernel.
See <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>
and <option>--section=</option>
below.</para>
+ <para><command>ukify</command> can also be used to assemble a PE binary that is not executable but
+ contains auxiliary data, for example additional kernel command line entries.</para>
+
<para>If PCR signing keys are provided via the <option>--pcr-public-key=</option> and
<option>--pcr-private-key=</option> options, PCR values that will be seen after booting with the given
kernel, initrd, and other sections, will be calculated, signed, and embedded in the UKI.
<refsect1>
<title>Options</title>
- <para>Note that the <replaceable>LINUX</replaceable> positional argument is mandatory. The
- <replaceable>INITRD</replaceable> positional arguments are optional. If more than one is specified, they
- will all be combined into a single PE section. This is useful to for example prepend microcode before the
- actual initrd.</para>
+ <para>The <replaceable>LINUX</replaceable> and <replaceable>INITRD</replaceable> positional arguments are
+ optional. If more than one <replaceable>INITRD</replaceable> are specified, they will all be combined into
+ a single PE section. This is useful to for example prepend microcode before the actual initrd.</para>
<para>The following options are understood:</para>
key <filename index='false'>pcr-private-system-key.pem</filename>. The Linux binary and the resulting
combined image will be signed with the SecureBoot key <filename index='false'>sb.key</filename>.</para>
</example>
+
+ <example>
+ <title>Kernel command line auxiliary PE</title>
+
+ <programlisting>ukify \
+ --secureboot-private-key=sb.key \
+ --secureboot-certificate=sb.cert \
+ --cmdline='debug' \
+ --output=debug.cmdline.efi
+ </programlisting>
+
+ <para>This creates a signed PE binary that contains an additional kernel command line parameter.</para>
+ </example>
</refsect1>
<refsect1>
for sect in 'text osrel cmdline linux initrd uname test'.split():
assert re.search(fr'^\s*\d+\s+.{sect}\s+0', dump, re.MULTILINE)
+def test_addon(kernel_initrd, tmpdir):
+ output = f'{tmpdir}/addon.efi'
+ opts = ukify.parse_args([
+ f'--output={output}',
+ '--cmdline=ARG1 ARG2 ARG3',
+ '--section=.test:CONTENTZ',
+ ])
+
+ try:
+ ukify.check_inputs(opts)
+ except OSError as e:
+ pytest.skip(str(e))
+
+ ukify.make_uki(opts)
+
+ # let's check that objdump likes the resulting file
+ dump = subprocess.check_output(['objdump', '-h', output], text=True)
+
+ for sect in 'text cmdline test'.split():
+ assert re.search(fr'^\s*\d+\s+.{sect}\s+0', dump, re.MULTILINE)
+
def unbase64(filename):
tmp = tempfile.NamedTemporaryFile()
sbsign_invocation += ['--engine', opts.signing_engine]
sign_kernel = opts.sign_kernel
- if sign_kernel is None and opts.sb_key:
+ if sign_kernel is None and opts.linux is not None and opts.sb_key:
# figure out if we should sign the kernel
sbverify_tool = find_tool('sbverify', opts=opts)
else:
linux = opts.linux
- if opts.uname is None:
+ if opts.uname is None and opts.linux is not None:
print('Kernel version not specified, starting autodetection 😖.')
opts.uname = Uname.scrape(opts.linux, opts=opts)
# UKI creation
- uki.add_section(Section.create('.linux', linux, measure=True))
+ if linux is not None:
+ uki.add_section(Section.create('.linux', linux, measure=True))
if opts.sb_key:
unsigned = tempfile.NamedTemporaryFile(prefix='uki')
description='Build and sign Unified Kernel Images',
allow_abbrev=False,
usage='''\
-usage: ukify [options…] linux initrd…
+usage: ukify [options…] [linux [initrd…]]
ukify -h | --help
''')
p.add_argument('linux',
type=pathlib.Path,
+ nargs="?",
help='vmlinuz file [.linux section]')
p.add_argument('initrd',
type=pathlib.Path,
opts = p.parse_args(args)
- path_is_readable(opts.linux)
+ if opts.linux is not None:
+ path_is_readable(opts.linux)
for initrd in opts.initrd or ():
path_is_readable(initrd)
path_is_readable(opts.devicetree)
if opts.os_release is not None and opts.os_release.startswith('@'):
opts.os_release = path_is_readable(opts.os_release[1:])
- elif opts.os_release is None:
+ elif opts.os_release is None and opts.linux is not None:
p = pathlib.Path('/etc/os-release')
if not p.exists():
p = path_is_readable('/usr/lib/os-release')
raise ValueError('--phases= specifications must match --pcr-private-key=')
if opts.output is None:
+ if opts.linux is None:
+ raise ValueError('--output= must be specified when building a PE addon')
suffix = '.efi' if opts.sb_key else '.unsigned.efi'
opts.output = opts.linux.name + suffix