From: Luca Boccassi Date: Mon, 17 Apr 2023 23:40:43 +0000 (+0100) Subject: ukify: allow building PE addon X-Git-Tag: v254-rc1~624 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=00e5933f57c6e336ebed18601299acc6855bb3c2;p=thirdparty%2Fsystemd.git ukify: allow building PE addon 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. --- diff --git a/man/ukify.xml b/man/ukify.xml index c3c0d3f2dfb..97c3f899c77 100644 --- a/man/ukify.xml +++ b/man/ukify.xml @@ -17,14 +17,14 @@ ukify - Combine kernel and initrd into a signed Unified Kernel Image + Combine components into a signed Unified Kernel Image for UEFI systems /usr/lib/systemd/ukify - LINUX - INITRD + LINUX + INITRD OPTIONS @@ -35,8 +35,8 @@ 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. - ukify is a tool that combines a kernel and an initrd with - a UEFI boot stub to create a + ukify is a tool that combines components (e.g.: a kernel and an initrd with + a UEFI boot stub) to create a Unified Kernel Image (UKI) — a PE binary that can be executed by the firmware to start the embedded linux kernel. See systemd-stub7 @@ -53,6 +53,9 @@ and below. + ukify can also be used to assemble a PE binary that is not executable but + contains auxiliary data, for example additional kernel command line entries. + If PCR signing keys are provided via the and 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. @@ -78,10 +81,9 @@ Options - Note that the LINUX positional argument is mandatory. The - INITRD 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. + The LINUX and INITRD positional arguments are + optional. If more than one INITRD are specified, they will all be combined into + a single PE section. This is useful to for example prepend microcode before the actual initrd. The following options are understood: @@ -296,6 +298,19 @@ key pcr-private-system-key.pem. The Linux binary and the resulting combined image will be signed with the SecureBoot key sb.key. + + + Kernel command line auxiliary PE + + ukify \ + --secureboot-private-key=sb.key \ + --secureboot-certificate=sb.cert \ + --cmdline='debug' \ + --output=debug.cmdline.efi + + + This creates a signed PE binary that contains an additional kernel command line parameter. + diff --git a/src/ukify/test/test_ukify.py b/src/ukify/test/test_ukify.py index 7e025b5ef4f..1b58f05d4ca 100755 --- a/src/ukify/test/test_ukify.py +++ b/src/ukify/test/test_ukify.py @@ -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() diff --git a/src/ukify/ukify.py b/src/ukify/ukify.py index b319f2dc913..3fbeb2b2157 100755 --- a/src/ukify/ukify.py +++ b/src/ukify/ukify.py @@ -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