]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
ukify: allow building PE addon
authorLuca Boccassi <bluca@debian.org>
Mon, 17 Apr 2023 23:40:43 +0000 (00:40 +0100)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Wed, 26 Apr 2023 14:55:26 +0000 (16:55 +0200)
Make the kernel optional too, so that we can easily build and sign a PE addon,
that can be used to carry extra command line options.

man/ukify.xml
src/ukify/test/test_ukify.py
src/ukify/ukify.py

index c3c0d3f2dfb2c1d29b6a4761b05f6a14856d1e42..97c3f899c772c3d8db860d08c102b62632be110b 100644 (file)
 
   <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>
@@ -35,8 +35,8 @@
     <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>
@@ -53,6 +53,9 @@
     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>
index 7e025b5ef4f5125a50c0d798cd6e62b840b23e76..1b58f05d4ca01fa89bbf4faabe1a59da99730e83 100755 (executable)
@@ -206,6 +206,27 @@ def test_sections(kernel_initrd, tmpdir):
     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()
index b319f2dc913509b5150cea70d0d58af4fd38062c..3fbeb2b215703b16e0258f2b6059c9bb4232229c 100755 (executable)
@@ -551,7 +551,7 @@ def make_uki(opts):
         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)
 
@@ -583,7 +583,7 @@ def make_uki(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)
 
@@ -624,7 +624,8 @@ def make_uki(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')
@@ -657,7 +658,7 @@ def parse_args(args=None):
         description='Build and sign Unified Kernel Images',
         allow_abbrev=False,
         usage='''\
-usage: ukify [options…] linux initrd…
+usage: ukify [options…] [linux [initrd…]]
        ukify -h | --help
 ''')
 
@@ -666,6 +667,7 @@ usage: ukify [options…] linux initrd…
 
     p.add_argument('linux',
                    type=pathlib.Path,
+                   nargs="?",
                    help='vmlinuz file [.linux section]')
     p.add_argument('initrd',
                    type=pathlib.Path,
@@ -769,7 +771,8 @@ usage: ukify [options…] linux initrd…
 
     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)
@@ -784,7 +787,7 @@ usage: ukify [options…] linux initrd…
 
     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')
@@ -815,6 +818,8 @@ usage: ukify [options…] linux initrd…
         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