mirror of
https://github.com/systemd/systemd.git
synced 2025-01-09 01:18:19 +03:00
ukify: add 'build' verb
The old syntax with linux + initrds as positional arguments is still accepted, but a warning is emitted. We should remove the support for this after the next release or so. Adding a single verb by itself is not very useful, but opens the door to adding other verbs.
This commit is contained in:
parent
320266b961
commit
a3b227d28a
@ -23,9 +23,8 @@
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>/usr/lib/systemd/ukify</command>
|
||||
<arg choice="opt"><replaceable>LINUX</replaceable></arg>
|
||||
<arg choice="opt" rep="repeat"><replaceable>INITRD</replaceable></arg>
|
||||
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||
<arg choice="plain">build</arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
@ -35,13 +34,18 @@
|
||||
<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 components (e.g.: a kernel and an initrd with
|
||||
a UEFI boot stub) to create a
|
||||
<para><command>ukify</command> is a tool that combines components (usually a kernel, an initrd, and 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>
|
||||
for details about the stub.</para>
|
||||
|
||||
<para>The two primary options that should be specified for the <command>build</command> verb are
|
||||
<varname>Linux=</varname>/<option>--linux=</option>, and
|
||||
<varname>Initrd=</varname>/<option>--initrd=</option>. <varname>Initrd=</varname> accepts multiple
|
||||
whitespace-separated paths and <option>--initrd=</option> can be specified multiple times.</para>
|
||||
|
||||
<para>Additional sections will be inserted into the UKI, either automatically or only if a specific
|
||||
option is provided. See the discussions of
|
||||
<varname>Cmdline=</varname>/<option>--cmdline=</option>,
|
||||
@ -173,14 +177,14 @@
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><varname>Linux=<replaceable>LINUX</replaceable></varname></term>
|
||||
<term>positional argument <replaceable>LINUX</replaceable></term>
|
||||
<term><option>--linux=<replaceable>LINUX</replaceable></option></term>
|
||||
|
||||
<listitem><para>A path to the kernel binary.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>Initrd=<replaceable>INITRD</replaceable>...</varname></term>
|
||||
<term>positional argument <replaceable>INITRD</replaceable></term>
|
||||
<term><option>--initrd=<replaceable>LINUX</replaceable></option></term>
|
||||
|
||||
<listitem><para>Zero or more initrd paths. In the configuration file, items are separated by
|
||||
whitespace. The initrds are combined in the order of specification, with the initrds specified in
|
||||
@ -399,9 +403,9 @@
|
||||
<example>
|
||||
<title>Minimal invocation</title>
|
||||
|
||||
<programlisting>$ ukify \
|
||||
/lib/modules/6.0.9-300.fc37.x86_64/vmlinuz \
|
||||
/some/path/initramfs-6.0.9-300.fc37.x86_64.img \
|
||||
<programlisting>$ ukify build \
|
||||
--linux=/lib/modules/6.0.9-300.fc37.x86_64/vmlinuz \
|
||||
--initrd=/some/path/initramfs-6.0.9-300.fc37.x86_64.img \
|
||||
--cmdline='quiet rw'
|
||||
</programlisting>
|
||||
|
||||
@ -411,10 +415,10 @@
|
||||
<example>
|
||||
<title>All the bells and whistles</title>
|
||||
|
||||
<programlisting># /usr/lib/systemd/ukify \
|
||||
/lib/modules/6.0.9-300.fc37.x86_64/vmlinuz \
|
||||
early_cpio \
|
||||
/some/path/initramfs-6.0.9-300.fc37.x86_64.img \
|
||||
<programlisting># /usr/lib/systemd/ukify build \
|
||||
--linux=/lib/modules/6.0.9-300.fc37.x86_64/vmlinuz \
|
||||
--initrd=early_cpio \
|
||||
--initrd=/some/path/initramfs-6.0.9-300.fc37.x86_64.img \
|
||||
--pcr-private-key=pcr-private-initrd-key.pem \
|
||||
--pcr-public-key=pcr-public-initrd-key.pem \
|
||||
--phases='enter-initrd' \
|
||||
@ -468,9 +472,9 @@ Phases=enter-initrd:leave-initrd
|
||||
enter-initrd:leave-initrd:sysinit
|
||||
enter-initrd:leave-initrd:sysinit:ready
|
||||
|
||||
# /usr/lib/systemd/ukify -c ukify.conf \
|
||||
/lib/modules/6.0.9-300.fc37.x86_64/vmlinuz \
|
||||
/some/path/initramfs-6.0.9-300.fc37.x86_64.img
|
||||
# /usr/lib/systemd/ukify -c ukify.conf build \
|
||||
--linux=/lib/modules/6.0.9-300.fc37.x86_64/vmlinuz \
|
||||
--initrd=/some/path/initramfs-6.0.9-300.fc37.x86_64.img
|
||||
</programlisting>
|
||||
|
||||
<para>One "initrd" (<filename index='false'>early_cpio</filename>) is specified in the config file, and
|
||||
@ -482,7 +486,7 @@ Phases=enter-initrd:leave-initrd
|
||||
<example>
|
||||
<title>Kernel command line auxiliary PE</title>
|
||||
|
||||
<programlisting>ukify \
|
||||
<programlisting>ukify build \
|
||||
--secureboot-private-key=sb.key \
|
||||
--secureboot-certificate=sb.cert \
|
||||
--cmdline='debug' \
|
||||
|
@ -52,7 +52,7 @@ def test_round_up():
|
||||
def test_namespace_creation():
|
||||
ns = ukify.create_parser().parse_args(())
|
||||
assert ns.linux is None
|
||||
assert ns.initrd == []
|
||||
assert ns.initrd is None
|
||||
|
||||
def test_config_example():
|
||||
ex = ukify.config_example()
|
||||
@ -143,7 +143,7 @@ def test_parse_args_minimal():
|
||||
assert opts.os_release in (pathlib.Path('/etc/os-release'),
|
||||
pathlib.Path('/usr/lib/os-release'))
|
||||
|
||||
def test_parse_args_many():
|
||||
def test_parse_args_many_deprecated():
|
||||
opts = ukify.parse_args(
|
||||
['/ARG1', '///ARG2', '/ARG3 WITH SPACE',
|
||||
'--cmdline=a b c',
|
||||
@ -186,9 +186,57 @@ def test_parse_args_many():
|
||||
assert opts.output == pathlib.Path('OUTPUT')
|
||||
assert opts.measure is False
|
||||
|
||||
def test_parse_args_many():
|
||||
opts = ukify.parse_args(
|
||||
['build',
|
||||
'--linux=/ARG1',
|
||||
'--initrd=///ARG2',
|
||||
'--initrd=/ARG3 WITH SPACE',
|
||||
'--cmdline=a b c',
|
||||
'--os-release=K1=V1\nK2=V2',
|
||||
'--devicetree=DDDDTTTT',
|
||||
'--splash=splash',
|
||||
'--pcrpkey=PATH',
|
||||
'--uname=1.2.3',
|
||||
'--stub=STUBPATH',
|
||||
'--pcr-private-key=PKEY1',
|
||||
'--pcr-public-key=PKEY2',
|
||||
'--pcr-banks=SHA1,SHA256',
|
||||
'--signing-engine=ENGINE',
|
||||
'--secureboot-private-key=SBKEY',
|
||||
'--secureboot-certificate=SBCERT',
|
||||
'--sign-kernel',
|
||||
'--no-sign-kernel',
|
||||
'--tools=TOOLZ///',
|
||||
'--output=OUTPUT',
|
||||
'--measure',
|
||||
'--no-measure',
|
||||
])
|
||||
assert opts.linux == pathlib.Path('/ARG1')
|
||||
assert opts.initrd == [pathlib.Path('/ARG2'), pathlib.Path('/ARG3 WITH SPACE')]
|
||||
assert opts.cmdline == 'a b c'
|
||||
assert opts.os_release == 'K1=V1\nK2=V2'
|
||||
assert opts.devicetree == pathlib.Path('DDDDTTTT')
|
||||
assert opts.splash == pathlib.Path('splash')
|
||||
assert opts.pcrpkey == pathlib.Path('PATH')
|
||||
assert opts.uname == '1.2.3'
|
||||
assert opts.stub == pathlib.Path('STUBPATH')
|
||||
assert opts.pcr_private_keys == [pathlib.Path('PKEY1')]
|
||||
assert opts.pcr_public_keys == [pathlib.Path('PKEY2')]
|
||||
assert opts.pcr_banks == ['SHA1', 'SHA256']
|
||||
assert opts.signing_engine == 'ENGINE'
|
||||
assert opts.sb_key == 'SBKEY'
|
||||
assert opts.sb_cert == 'SBCERT'
|
||||
assert opts.sign_kernel is False
|
||||
assert opts.tools == [pathlib.Path('TOOLZ/')]
|
||||
assert opts.output == pathlib.Path('OUTPUT')
|
||||
assert opts.measure is False
|
||||
|
||||
def test_parse_sections():
|
||||
opts = ukify.parse_args(
|
||||
['/ARG1', '/ARG2',
|
||||
['build',
|
||||
'--linux=/ARG1',
|
||||
'--initrd=/ARG2',
|
||||
'--section=test:TESTTESTTEST',
|
||||
'--section=test2:@FILE',
|
||||
])
|
||||
@ -239,7 +287,10 @@ def test_config_priority(tmp_path):
|
||||
'''))
|
||||
|
||||
opts = ukify.parse_args(
|
||||
['/ARG1', '///ARG2', '/ARG3 WITH SPACE',
|
||||
['build',
|
||||
'--linux=/ARG1',
|
||||
'--initrd=///ARG2',
|
||||
'--initrd=/ARG3 WITH SPACE',
|
||||
'--cmdline= a b c ',
|
||||
'--os-release=K1=V1\nK2=V2',
|
||||
'--devicetree=DDDDTTTT',
|
||||
@ -302,7 +353,7 @@ def test_help(capsys):
|
||||
assert '--section' in out.out
|
||||
assert not out.err
|
||||
|
||||
def test_help_error(capsys):
|
||||
def test_help_error_deprecated(capsys):
|
||||
with pytest.raises(SystemExit):
|
||||
ukify.parse_args(['a', 'b', '--no-such-option'])
|
||||
out = capsys.readouterr()
|
||||
@ -310,6 +361,14 @@ def test_help_error(capsys):
|
||||
assert '--no-such-option' in out.err
|
||||
assert len(out.err.splitlines()) == 1
|
||||
|
||||
def test_help_error(capsys):
|
||||
with pytest.raises(SystemExit):
|
||||
ukify.parse_args(['build', '--no-such-option'])
|
||||
out = capsys.readouterr()
|
||||
assert not out.out
|
||||
assert '--no-such-option' in out.err
|
||||
assert len(out.err.splitlines()) == 1
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def kernel_initrd():
|
||||
try:
|
||||
@ -326,7 +385,7 @@ def kernel_initrd():
|
||||
initrd = f"{item['root']}{item['initrd'][0].split(' ')[0]}"
|
||||
except (KeyError, IndexError):
|
||||
continue
|
||||
return [linux, initrd]
|
||||
return ['--linux', linux, '--initrd', initrd]
|
||||
else:
|
||||
return None
|
||||
|
||||
@ -345,7 +404,11 @@ def test_basic_operation(kernel_initrd, tmpdir):
|
||||
pytest.skip('linux+initrd not found')
|
||||
|
||||
output = f'{tmpdir}/basic.efi'
|
||||
opts = ukify.parse_args(kernel_initrd + [f'--output={output}'])
|
||||
opts = ukify.parse_args([
|
||||
'build',
|
||||
*kernel_initrd,
|
||||
f'--output={output}',
|
||||
])
|
||||
try:
|
||||
ukify.check_inputs(opts)
|
||||
except OSError as e:
|
||||
@ -362,6 +425,7 @@ def test_sections(kernel_initrd, tmpdir):
|
||||
|
||||
output = f'{tmpdir}/basic.efi'
|
||||
opts = ukify.parse_args([
|
||||
'build',
|
||||
*kernel_initrd,
|
||||
f'--output={output}',
|
||||
'--uname=1.2.3',
|
||||
@ -386,6 +450,7 @@ def test_sections(kernel_initrd, tmpdir):
|
||||
def test_addon(kernel_initrd, tmpdir):
|
||||
output = f'{tmpdir}/addon.efi'
|
||||
args = [
|
||||
'build',
|
||||
f'--output={output}',
|
||||
'--cmdline=ARG1 ARG2 ARG3',
|
||||
'--section=.test:CONTENTZ',
|
||||
@ -422,7 +487,8 @@ def test_uname_scraping(kernel_initrd):
|
||||
if kernel_initrd is None:
|
||||
pytest.skip('linux+initrd not found')
|
||||
|
||||
uname = ukify.Uname.scrape(kernel_initrd[0])
|
||||
assert kernel_initrd[0] == '--linux'
|
||||
uname = ukify.Uname.scrape(kernel_initrd[1])
|
||||
assert re.match(r'\d+\.\d+\.\d+', uname)
|
||||
|
||||
def test_efi_signing_sbsign(kernel_initrd, tmpdir):
|
||||
@ -437,6 +503,7 @@ def test_efi_signing_sbsign(kernel_initrd, tmpdir):
|
||||
|
||||
output = f'{tmpdir}/signed.efi'
|
||||
opts = ukify.parse_args([
|
||||
'build',
|
||||
*kernel_initrd,
|
||||
f'--output={output}',
|
||||
'--uname=1.2.3',
|
||||
@ -480,6 +547,7 @@ def test_efi_signing_pesign(kernel_initrd, tmpdir):
|
||||
|
||||
output = f'{tmpdir}/signed.efi'
|
||||
opts = ukify.parse_args([
|
||||
'build',
|
||||
*kernel_initrd,
|
||||
f'--output={output}',
|
||||
'--uname=1.2.3',
|
||||
@ -514,6 +582,7 @@ def test_pcr_signing(kernel_initrd, tmpdir):
|
||||
|
||||
output = f'{tmpdir}/signed.efi'
|
||||
opts = ukify.parse_args([
|
||||
'build',
|
||||
*kernel_initrd,
|
||||
f'--output={output}',
|
||||
'--uname=1.2.3',
|
||||
@ -576,8 +645,12 @@ def test_pcr_signing2(kernel_initrd, tmpdir):
|
||||
microcode.write(b'1234567890')
|
||||
|
||||
output = f'{tmpdir}/signed.efi'
|
||||
assert kernel_initrd[0] == '--linux'
|
||||
opts = ukify.parse_args([
|
||||
kernel_initrd[0], microcode.name, kernel_initrd[1],
|
||||
'build',
|
||||
*kernel_initrd[:2],
|
||||
f'--initrd={microcode.name}',
|
||||
*kernel_initrd[2:],
|
||||
f'--output={output}',
|
||||
'--uname=1.2.3',
|
||||
'--cmdline=ARG1 ARG2 ARG3',
|
||||
|
@ -438,7 +438,7 @@ def call_systemd_measure(uki, linux, opts):
|
||||
|
||||
|
||||
def join_initrds(initrds):
|
||||
if len(initrds) == 0:
|
||||
if not initrds:
|
||||
return None
|
||||
if len(initrds) == 1:
|
||||
return initrds[0]
|
||||
@ -820,7 +820,10 @@ class ConfigItem:
|
||||
else:
|
||||
conv = lambda s:s
|
||||
|
||||
if self.nargs == '*':
|
||||
# This is a bit ugly, but --initrd is the only option which is specified
|
||||
# with multiple args on the command line and a space-separated list in the
|
||||
# config file.
|
||||
if self.name == '--initrd':
|
||||
value = [conv(v) for v in value.split()]
|
||||
else:
|
||||
value = conv(value)
|
||||
@ -840,7 +843,16 @@ class ConfigItem:
|
||||
return (section_name, key, value)
|
||||
|
||||
|
||||
VERBS = ('build',)
|
||||
|
||||
CONFIG_ITEMS = [
|
||||
ConfigItem(
|
||||
'positional',
|
||||
metavar = 'VERB',
|
||||
nargs = '*',
|
||||
help = f"operation to perform ({','.join(VERBS)})",
|
||||
),
|
||||
|
||||
ConfigItem(
|
||||
'--version',
|
||||
action = 'version',
|
||||
@ -854,20 +866,18 @@ CONFIG_ITEMS = [
|
||||
),
|
||||
|
||||
ConfigItem(
|
||||
'linux',
|
||||
metavar = 'LINUX',
|
||||
'--linux',
|
||||
type = pathlib.Path,
|
||||
nargs = '?',
|
||||
help = 'vmlinuz file [.linux section]',
|
||||
config_key = 'UKI/Linux',
|
||||
),
|
||||
|
||||
ConfigItem(
|
||||
'initrd',
|
||||
metavar = 'INITRD…',
|
||||
'--initrd',
|
||||
metavar = 'INITRD',
|
||||
type = pathlib.Path,
|
||||
nargs = '*',
|
||||
help = 'initrd files [.initrd section]',
|
||||
action = 'append',
|
||||
help = 'initrd file [part of .initrd section]',
|
||||
config_key = 'UKI/Initrd',
|
||||
config_push = ConfigItem.config_list_prepend,
|
||||
),
|
||||
@ -1199,6 +1209,20 @@ def parse_args(args=None):
|
||||
p = create_parser()
|
||||
opts = p.parse_args(args)
|
||||
|
||||
# Figure out which syntax is being used, one of:
|
||||
# ukify verb --arg --arg --arg
|
||||
# ukify linux initrd…
|
||||
if len(opts.positional) == 1 and opts.positional[0] in VERBS:
|
||||
opts.verb = opts.positional[0]
|
||||
elif opts.linux or opts.initrd:
|
||||
raise ValueError('--linux/--initrd options cannot be used with positional arguments')
|
||||
else:
|
||||
print("Assuming obsolete commandline syntax with no verb. Please use 'build'.")
|
||||
if opts.positional:
|
||||
opts.linux = pathlib.Path(opts.positional[0])
|
||||
opts.initrd = [pathlib.Path(arg) for arg in opts.positional[1:]]
|
||||
opts.verb = 'build'
|
||||
|
||||
# Check that --pcr-public-key=, --pcr-private-key=, and --phases=
|
||||
# have either the same number of arguments are are not specified at all.
|
||||
n_pcr_pub = None if opts.pcr_public_keys is None else len(opts.pcr_public_keys)
|
||||
@ -1219,6 +1243,7 @@ def parse_args(args=None):
|
||||
def main():
|
||||
opts = parse_args()
|
||||
check_inputs(opts)
|
||||
assert opts.verb == 'build'
|
||||
make_uki(opts)
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user