mirror of
https://github.com/NixOS/nixpkgs.git
synced 2026-07-06 17:13:24 -05:00
nixos/boot: allow passing extra initrd archives to the bootloader
This is for instance useful on Asahi where an additional initrd archive containing firmware blobs and per-device calibration files is placed on the ESP and updated by the Asahi Linux Installer. These need to be loaded alongside the NixOS initrd. Implemented for systemd-boot and Limine. Grub is left out since its install script does not use bootspec yet. Co-authored-by: Florian Klink <flokli@flokli.de>
This commit is contained in:
parent
5640e1f935
commit
5f267f4dda
6 changed files with 157 additions and 13 deletions
|
|
@ -1911,6 +1911,7 @@
|
|||
./system/boot/clevis-luks-askpass.nix
|
||||
./system/boot/clevis.nix
|
||||
./system/boot/emergency-mode.nix
|
||||
./system/boot/extra-initrd.nix
|
||||
./system/boot/grow-partition.nix
|
||||
./system/boot/initrd-network.nix
|
||||
./system/boot/initrd-openvpn.nix
|
||||
|
|
|
|||
21
nixos/modules/system/boot/extra-initrd.nix
Normal file
21
nixos/modules/system/boot/extra-initrd.nix
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{ config, lib, ... }:
|
||||
{
|
||||
options.system.boot.extraInitrd = {
|
||||
paths = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
description = ''
|
||||
List of paths relative to the ESP that are combined with the NixOS
|
||||
main initrd before being passed to the kernel.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
boot.bootspec.extensions = lib.mkIf (config.system.boot.extraInitrd.paths != [ ]) {
|
||||
"org.nixos.extra-initrd.v1" = {
|
||||
inherit (config.system.boot.extraInitrd) paths;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -36,6 +36,7 @@ class BootSpec:
|
|||
label: str
|
||||
toplevel: str
|
||||
specialisations: Dict[str, "BootSpec"]
|
||||
extraInitrdPaths: list[str]
|
||||
xen: XenBootSpec | None
|
||||
initrd: str | None = None
|
||||
initrdSecrets: str | None = None
|
||||
|
|
@ -173,10 +174,15 @@ def bootjson_to_bootspec(bootjson: dict) -> BootSpec:
|
|||
xen = None
|
||||
if "org.xenproject.bootspec.v2" in bootjson:
|
||||
xen = bootjson["org.xenproject.bootspec.v2"]
|
||||
|
||||
extraInitrdExtension = bootjson.get("org.nixos.extra-initrd.v1", {})
|
||||
extraInitrdPaths = extraInitrdExtension.get("paths", [])
|
||||
|
||||
return BootSpec(
|
||||
**bootjson["org.nixos.bootspec.v1"],
|
||||
specialisations=specialisations,
|
||||
xen=xen,
|
||||
extraInitrdPaths=extraInitrdPaths,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -299,6 +305,10 @@ def xen_config_entry(
|
|||
if bootspec.initrd:
|
||||
# the final module is the initrd
|
||||
entry += "module_path: " + get_kernel_uri(bootspec.initrd) + "\n"
|
||||
|
||||
for p in bootspec.extraInitrdPaths:
|
||||
entry += f"module_path: boot():/{p}\n"
|
||||
|
||||
return entry
|
||||
|
||||
|
||||
|
|
@ -321,6 +331,9 @@ def config_entry(levels: int, bootspec: BootSpec, label: str, time: str) -> str:
|
|||
if bootspec.initrd:
|
||||
entry += f"module_path: " + get_kernel_uri(bootspec.initrd) + "\n"
|
||||
|
||||
for p in bootspec.extraInitrdPaths:
|
||||
entry += f"module_path: boot():/{p}\n"
|
||||
|
||||
if bootspec.initrdSecrets:
|
||||
base_path = str(limine_install_dir) + "/kernels/"
|
||||
initrd_secrets_path = (
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ BOOT_COUNTING = "@bootCounting@" == "True"
|
|||
class BootSpec:
|
||||
init: Path
|
||||
initrd: Path
|
||||
extraInitrdPaths: list[Path]
|
||||
kernel: Path
|
||||
kernelParams: list[str] # noqa: N815
|
||||
label: str
|
||||
|
|
@ -306,9 +307,7 @@ def get_bootspec(profile: str | None, generation: int) -> BootSpec | None:
|
|||
try:
|
||||
bootspec_json = json.load(f)
|
||||
except ValueError as e:
|
||||
print(
|
||||
f"error: Malformed Json: {e}, in {boot_json_path}", file=sys.stderr
|
||||
)
|
||||
print(f"error: Malformed Json: {e}, in {boot_json_path}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
return bootspec_from_json(bootspec_json)
|
||||
|
||||
|
|
@ -320,6 +319,11 @@ def bootspec_from_json(bootspec_json: dict[str, Any]) -> BootSpec:
|
|||
sortKey = systemdBootExtension.get("sortKey", "nixos")
|
||||
devicetree = systemdBootExtension.get("devicetree")
|
||||
|
||||
extraInitrdExtension = bootspec_json.get("org.nixos.extra-initrd.v1", {})
|
||||
extraInitrdPaths = list(
|
||||
map(lambda path: Path(path), extraInitrdExtension.get("paths", []))
|
||||
)
|
||||
|
||||
if devicetree:
|
||||
devicetree = Path(devicetree)
|
||||
|
||||
|
|
@ -332,6 +336,7 @@ def bootspec_from_json(bootspec_json: dict[str, Any]) -> BootSpec:
|
|||
specialisations=specialisations,
|
||||
sortKey=sortKey,
|
||||
devicetree=devicetree,
|
||||
extraInitrdPaths=extraInitrdPaths,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -374,16 +379,21 @@ def boot_file(
|
|||
specialisation=" (%s)" % specialisation if specialisation else "",
|
||||
)
|
||||
description = f"Generation {generation} {bootspec.label}, built on {build_date}"
|
||||
boot_entry = [
|
||||
f"title {title}",
|
||||
f"version {description}",
|
||||
f"linux /{str(kernel.path)}",
|
||||
f"initrd /{str(initrd.path)}",
|
||||
f"options {kernel_params}",
|
||||
f"machine-id {machine_id}" if machine_id is not None else None,
|
||||
f"devicetree /{str(devicetree.path)}" if devicetree is not None else None,
|
||||
f"sort-key {bootspec.sortKey}",
|
||||
]
|
||||
boot_entry = (
|
||||
[
|
||||
f"title {title}",
|
||||
f"version {description}",
|
||||
f"linux /{str(kernel.path)}",
|
||||
f"initrd /{str(initrd.path)}",
|
||||
]
|
||||
+ list(map(lambda initrd: f"initrd /{initrd}", bootspec.extraInitrdPaths))
|
||||
+ [
|
||||
f"options {kernel_params}",
|
||||
f"machine-id {machine_id}" if machine_id is not None else None,
|
||||
f"devicetree /{str(devicetree.path)}" if devicetree is not None else None,
|
||||
f"sort-key {bootspec.sortKey}",
|
||||
]
|
||||
)
|
||||
contents = "\n".join(filter(None, boot_entry))
|
||||
entry, bootctl_id = BootFile.from_entry(contents.encode("utf-8"))
|
||||
return (list(filter(None, [kernel, initrd, devicetree, entry])), bootctl_id)
|
||||
|
|
|
|||
|
|
@ -554,6 +554,9 @@ in
|
|||
etebase-server = runTest ./etebase-server.nix;
|
||||
etesync-dav = runTest ./etesync-dav.nix;
|
||||
evcc = runTest ./evcc.nix;
|
||||
extra-initrd = import ./extra-initrd.nix {
|
||||
inherit runTest pkgs;
|
||||
};
|
||||
facter = runTest ./facter;
|
||||
fail2ban = runTest ./fail2ban.nix;
|
||||
fakeroute = runTest ./fakeroute.nix;
|
||||
|
|
|
|||
96
nixos/tests/extra-initrd.nix
Normal file
96
nixos/tests/extra-initrd.nix
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
{
|
||||
runTest,
|
||||
...
|
||||
}:
|
||||
let
|
||||
common =
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
virtualisation.useBootLoader = true;
|
||||
virtualisation.useEFIBoot = true;
|
||||
system.boot.extraInitrd.paths = [
|
||||
extraInitrdPath
|
||||
];
|
||||
|
||||
boot.loader.timeout = 2;
|
||||
|
||||
boot.initrd.systemd.mounts = [
|
||||
{
|
||||
what = "/canary.txt";
|
||||
where = "/sysroot/run/canary.txt";
|
||||
type = "none";
|
||||
options = "bind";
|
||||
unitConfig = {
|
||||
DefaultDependencies = false;
|
||||
};
|
||||
requiredBy = [ "initrd-fs.target" ];
|
||||
before = [ "initrd-fs.target" ];
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
extraInitrdPath = "custom.cpio";
|
||||
canaryCpio =
|
||||
pkgs:
|
||||
pkgs.runCommand "canary.cpio"
|
||||
{
|
||||
nativeBuildInputs = [ pkgs.cpio ];
|
||||
}
|
||||
''
|
||||
echo canary > canary.txt
|
||||
find . -print0 | cpio --null -o --format=newc > $out
|
||||
'';
|
||||
|
||||
testScript =
|
||||
# python
|
||||
''
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
|
||||
# Check that the extra cpio archive is on the ESP
|
||||
machine.succeed("test -e /boot/custom.cpio")
|
||||
|
||||
# Check that the initrd that we booted with contained the file from
|
||||
# the extra initrd and our initrd mount unit bound it into sysroot
|
||||
assert machine.succeed("cat /run/canary.txt").strip() == "canary"
|
||||
'';
|
||||
in
|
||||
{
|
||||
systemd-boot = runTest (
|
||||
{ pkgs, ... }: {
|
||||
name = "systemd-boot-extra-initrd";
|
||||
|
||||
nodes.machine = { config, ... }: {
|
||||
imports = [ common ];
|
||||
|
||||
boot.loader.systemd-boot = {
|
||||
enable = true;
|
||||
extraInstallCommands = ''
|
||||
cp ${canaryCpio pkgs} ${config.boot.loader.efi.efiSysMountPoint}/${extraInitrdPath}
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
inherit testScript;
|
||||
}
|
||||
);
|
||||
|
||||
limine = runTest {
|
||||
name = "limine-extra-initrd";
|
||||
|
||||
nodes.machine = { pkgs, config, ... }: {
|
||||
imports = [ common ];
|
||||
|
||||
boot.loader.limine = {
|
||||
enable = true;
|
||||
efiSupport = true;
|
||||
enableEditor = true;
|
||||
extraInstallCommands = ''
|
||||
cp ${canaryCpio pkgs} ${config.boot.loader.efi.efiSysMountPoint}/${extraInitrdPath}
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
inherit testScript;
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue