Skip to content

Commit

Permalink
Merge pull request #3074 from NekkoDroid/mkosi-uki-addons
Browse files Browse the repository at this point in the history
Add support for building UKI addons
  • Loading branch information
DaanDeMeyer authored Sep 29, 2024
2 parents 16dc117 + 807f429 commit 5d45c4d
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 40 deletions.
146 changes: 106 additions & 40 deletions mkosi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1471,50 +1471,37 @@ def want_signed_pcrs(config: Config) -> bool:
)


def build_uki(
def run_ukify(
context: Context,
arch: str,
stub: Path,
kver: str,
kimg: Path,
microcodes: list[Path],
initrds: list[Path],
cmdline: Sequence[str],
output: Path,
arguments: list[PathString],
options: list[PathString],
) -> None:
# Older versions of systemd-stub expect the cmdline section to be null terminated. We can't
# embed NUL terminators in argv so let's communicate the cmdline via a file instead.
(context.workspace / "cmdline").write_text(f"{' '.join(cmdline).strip()}\x00")

if not (arch := context.config.architecture.to_efi()):
die(f"Architecture {context.config.architecture} does not support UEFI")

if not (ukify := context.config.find_binary("ukify", "/usr/lib/systemd/ukify")):
ukify = context.config.find_binary("ukify", "/usr/lib/systemd/ukify")
if not ukify:
die("Could not find ukify")

cmd: list[PathString] = [
cmd = [
python_binary(context.config, binary=ukify),
ukify,
*(["--cmdline", f"@{context.workspace / 'cmdline'}"] if cmdline else []),
"--os-release", f"@{context.root / 'usr/lib/os-release'}",
"build",
*arguments,
"--efi-arch", arch,
"--stub", stub,
"--output", workdir(output),
"--efi-arch", arch,
"--uname", kver,
] # fmt: skip

options: list[PathString] = [
"--bind", output.parent, workdir(output.parent),
"--ro-bind", context.workspace / "cmdline", context.workspace / "cmdline",
"--ro-bind", context.root / "usr/lib/os-release", context.root / "usr/lib/os-release",
options += [
"--ro-bind", stub, stub,
"--bind", output.parent, workdir(output.parent),
] # fmt: skip

if context.config.secure_boot:
assert context.config.secure_boot_key
assert context.config.secure_boot_certificate

cmd += ["--sign-kernel"]

if context.config.secure_boot_sign_tool != SecureBootSignTool.pesign:
cmd += [
"--signtool", "sbsign",
Expand All @@ -1540,8 +1527,57 @@ def build_uki(
] # fmt: skip
options += ["--ro-bind", context.workspace / "pesign", context.workspace / "pesign"]

run(
cmd,
sandbox=context.sandbox(
binary=ukify,
options=options,
devices=context.config.secure_boot_key_source.type != KeySourceType.file,
),
)


def build_uki(
context: Context,
stub: Path,
kver: str,
kimg: Path,
microcodes: list[Path],
initrds: list[Path],
cmdline: Sequence[str],
output: Path,
) -> None:
# Older versions of systemd-stub expect the cmdline section to be null terminated. We can't
# embed NUL terminators in argv so let's communicate the cmdline via a file instead.
(context.workspace / "cmdline").write_text(f"{' '.join(cmdline).strip()}\x00")

if not (arch := context.config.architecture.to_efi()):
die(f"Architecture {context.config.architecture} does not support UEFI")

if not (ukify := context.config.find_binary("ukify", "/usr/lib/systemd/ukify")):
die("Could not find ukify")

arguments: list[PathString] = [
*(["--cmdline", f"@{context.workspace / 'cmdline'}"] if cmdline else []),
"--os-release", f"@{context.root / 'usr/lib/os-release'}",
"--uname", kver,
"--linux", kimg,
] # fmt: skip

options: list[PathString] = [
"--ro-bind", context.workspace / "cmdline", context.workspace / "cmdline",
"--ro-bind", context.root / "usr/lib/os-release", context.root / "usr/lib/os-release",
"--ro-bind", kimg, kimg,
] # fmt: skip

if context.config.secure_boot:
assert context.config.secure_boot_key
assert context.config.secure_boot_certificate

arguments += ["--sign-kernel"]

if want_signed_pcrs(context.config):
cmd += [
arguments += [
"--pcr-private-key", context.config.secure_boot_key,
# SHA1 might be disabled in OpenSSL depending on the distro so we opt to not sign
# for SHA1 to avoid having to manage a bunch of configuration to re-enable SHA1.
Expand All @@ -1550,7 +1586,7 @@ def build_uki(
if context.config.secure_boot_key.exists():
options += ["--bind", context.config.secure_boot_key, context.config.secure_boot_key]
if context.config.secure_boot_key_source.type == KeySourceType.engine:
cmd += [
arguments += [
"--signing-engine", context.config.secure_boot_key_source.source,
"--pcr-public-key", context.config.secure_boot_certificate,
] # fmt: skip
Expand All @@ -1559,9 +1595,6 @@ def build_uki(
"--bind-try", "/run/pcscd", "/run/pcscd",
] # fmt: skip

cmd += ["build", "--linux", kimg]
options += ["--ro-bind", kimg, kimg]

if microcodes:
# new .ucode section support?
if (
Expand All @@ -1575,24 +1608,17 @@ def build_uki(
and version >= "256"
):
for microcode in microcodes:
cmd += ["--microcode", microcode]
arguments += ["--microcode", microcode]
options += ["--ro-bind", microcode, microcode]
else:
initrds = microcodes + initrds

for initrd in initrds:
cmd += ["--initrd", initrd]
arguments += ["--initrd", initrd]
options += ["--ro-bind", initrd, initrd]

with complete_step(f"Generating unified kernel image for kernel version {kver}"):
run(
cmd,
sandbox=context.sandbox(
binary=ukify,
options=options,
devices=context.config.secure_boot_key_source.type != KeySourceType.file,
),
)
run_ukify(context, arch, stub, output, arguments, options)


def systemd_stub_binary(context: Context) -> Path:
Expand Down Expand Up @@ -1904,6 +1930,45 @@ def install_uki(
f.write("fi\n")


def build_pe_addon(context: Context, arch: str, stub: Path, config: Path, output: Path) -> None:
arguments: list[PathString] = [
"--config", config,
] # fmt: skip

options: list[PathString] = [
"--ro-bind", config, config,
] # fmt: skip

with complete_step(f"Generating PE addon /{output.relative_to(context.root)}"):
run_ukify(context, arch, stub, output, arguments, options)


def install_pe_addons(context: Context) -> None:
if not context.config.pe_addons:
return

if not (arch := context.config.architecture.to_efi()):
die(f"Architecture {context.config.architecture} does not support UEFI")

stub = systemd_addon_stub_binary(context)
if not stub.exists():
die(f"sd-stub not found at /{stub.relative_to(context.root)} in the image")

addon_dir = context.root / "boot/loader/addons"
with umask(~0o755):
addon_dir.mkdir(parents=True, exist_ok=True)

for addon in context.config.pe_addons:
output = addon_dir / addon.with_suffix(".addon.efi").name
build_pe_addon(context, arch, stub, config=addon, output=output)


def systemd_addon_stub_binary(context: Context) -> Path:
arch = context.config.architecture.to_efi()
stub = context.root / f"usr/lib/systemd/boot/efi/addon{arch}.efi.stub"
return stub


def install_kernel(context: Context, partitions: Sequence[Partition]) -> None:
# Iterates through all kernel versions included in the image and generates a combined
# kernel+initrd+cmdline+osrelease EFI file from it and places it in the /EFI/Linux directory of
Expand Down Expand Up @@ -3372,6 +3437,7 @@ def build_image(context: Context) -> None:
install_systemd_boot(context)
install_grub(context)
install_shim(context)
install_pe_addons(context)
run_sysusers(context)
run_tmpfiles(context)
run_preset(context)
Expand Down
11 changes: 11 additions & 0 deletions mkosi/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -1588,6 +1588,7 @@ class Config:
shim_bootloader: ShimBootloader
unified_kernel_images: ConfigFeature
unified_kernel_image_format: str
pe_addons: list[Path]
initrds: list[Path]
initrd_packages: list[str]
initrd_volatile_packages: list[str]
Expand Down Expand Up @@ -2552,6 +2553,15 @@ def parse_ini(path: Path, only_sections: Collection[str] = ()) -> Iterator[tuple
# default=
help="Specify the format used for the UKI filename",
),
ConfigSetting(
dest="pe_addons",
long="--pe-addon",
metavar="PATH",
section="Content",
parse=config_make_list_parser(delimiter=",", parse=make_path_parser()),
recursive_paths=("mkosi.pe-addons/",),
help="Configuration files to generate PE addons",
),
ConfigSetting(
dest="initrds",
long="--initrd",
Expand Down Expand Up @@ -4479,6 +4489,7 @@ def summary(config: Config) -> str:
Shim Bootloader: {config.shim_bootloader}
Unified Kernel Images: {config.unified_kernel_images}
Unified Kernel Image Format: {config.unified_kernel_image_format}
Unified Kernel Image Addons: {line_join_list(config.pe_addons)}
Initrds: {line_join_list(config.initrds)}
Initrd Packages: {line_join_list(config.initrd_packages)}
Initrd Volatile Packages: {line_join_list(config.initrd_volatile_packages)}
Expand Down
8 changes: 8 additions & 0 deletions mkosi/resources/man/mkosi.md
Original file line number Diff line number Diff line change
Expand Up @@ -984,6 +984,14 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`,
| `&h` | `roothash=` or `usrhash=` value of kernel argument |
| `&c` | Number of tries used for boot attempt counting |

`PeAddons=`, `--pe-addon`
: Build additional PE addons. Takes a comma separated list of paths to
`ukify` config files. This option may be used multiple times in which case
each config gets built into a corresponding addon. Each addon has the name
of the config file, with the extension replaced with `.addon.efi`.
Config files in the `mkosi.pe-addons/` directory are automatically picked
up.

`Initrds=`, `--initrd`
: Use user-provided initrd(s). Takes a comma separated list of paths to initrd
files. This option may be used multiple times in which case the initrd lists
Expand Down
4 changes: 4 additions & 0 deletions tests/test_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,9 @@ def test_config() -> None:
"abc"
],
"Passphrase": null,
"PeAddons": [
"/my-addon.conf"
],
"PostInstallationScripts": [
"/bar/qux"
],
Expand Down Expand Up @@ -442,6 +445,7 @@ def test_config() -> None:
packages=[],
pass_environment=["abc"],
passphrase=None,
pe_addons=[Path("/my-addon.conf")],
postinst_scripts=[Path("/bar/qux")],
postoutput_scripts=[Path("/foo/src")],
prepare_scripts=[Path("/run/foo")],
Expand Down

0 comments on commit 5d45c4d

Please sign in to comment.