Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nixos/systemd-boot: add support for devicetree entry #319422

Merged
merged 2 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class BootSpec:
toplevel: str
specialisations: dict[str, "BootSpec"]
sortKey: str # noqa: N815
devicetree: str | None = None # noqa: N815
initrdSecrets: str | None = None # noqa: N815

@dataclass
Expand Down Expand Up @@ -84,6 +85,7 @@ class DiskEntry:
kernel_params: str | None
machine_id: str | None
sort_key: str
devicetree: str | None

@classmethod
def from_path(cls: Type["DiskEntry"], path: Path) -> "DiskEntry":
Expand All @@ -108,7 +110,9 @@ def from_path(cls: Type["DiskEntry"], path: Path) -> "DiskEntry":
initrd=entry_map["initrd"],
kernel_params=entry_map.get("options"),
machine_id=entry_map.get("machine-id"),
sort_key=entry_map.get("sort_key", "nixos"))
sort_key=entry_map.get("sort_key", "nixos"),
devicetree=entry_map.get("devicetree"),
)
return disk_entry

def write(self, sorted_first: str) -> None:
Expand All @@ -127,7 +131,8 @@ def write(self, sorted_first: str) -> None:
f"initrd {self.initrd}",
f"options {self.kernel_params}" if self.kernel_params is not None else None,
f"machine-id {self.machine_id}" if self.machine_id is not None else None,
f"sort-key {default_sort_key if self.default else self.sort_key}"
f"sort-key {default_sort_key if self.default else self.sort_key}",
f"devicetree {self.devicetree}" if self.devicetree is not None else None,
]

f.write("\n".join(filter(None, boot_entry)))
Expand Down Expand Up @@ -233,10 +238,12 @@ def bootspec_from_json(bootspec_json: dict[str, Any]) -> BootSpec:
specialisations = {k: bootspec_from_json(v) for k, v in specialisations.items()}
systemdBootExtension = bootspec_json.get('org.nixos.systemd-boot', {})
sortKey = systemdBootExtension.get('sortKey', 'nixos')
devicetree = systemdBootExtension.get('devicetree')
return BootSpec(
**bootspec_json['org.nixos.bootspec.v1'],
specialisations=specialisations,
sortKey=sortKey
sortKey=sortKey,
devicetree=devicetree,
)


Expand All @@ -261,6 +268,7 @@ def write_entry(profile: str | None,
bootspec = bootspec.specialisations[specialisation]
kernel = copy_from_file(bootspec.kernel)
initrd = copy_from_file(bootspec.initrd)
devicetree = copy_from_file(bootspec.devicetree) if bootspec.devicetree is not None else None

title = "{name}{profile}{specialisation}".format(
name=DISTRO_NAME,
Expand Down Expand Up @@ -303,6 +311,7 @@ def write_entry(profile: str | None,
machine_id=machine_id,
description=f"Generation {generation} {bootspec.label}, built on {build_date}",
sort_key=bootspec.sortKey,
devicetree=devicetree,
default=current
).write(sorted_first)

Expand Down
14 changes: 14 additions & 0 deletions nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,15 @@ in {
'';
};

installDeviceTree = mkOption {
default = with config.hardware.deviceTree; enable && name != null;
defaultText = ''with config.hardware.deviceTree; enable && name != null'';
description = ''
Install the devicetree blob specified by `config.hardware.deviceTree.name`
to the ESP and instruct systemd-boot to pass this DTB to linux.
'';
};

extraInstallCommands = mkOption {
default = "";
example = ''
Expand Down Expand Up @@ -353,6 +362,10 @@ in {
assertion = (config.boot.kernelPackages.kernel.features or { efiBootStub = true; }) ? efiBootStub;
message = "This kernel does not support the EFI boot stub";
}
{
assertion = cfg.installDeviceTree -> config.hardware.deviceTree.enable -> config.hardware.deviceTree.name != null;
message = "Cannot install devicetree without 'config.hardware.deviceTree.enable' enabled and 'config.hardware.deviceTree.name' set";
}
] ++ concatMap (filename: [
{
assertion = !(hasInfix "/" filename);
Expand Down Expand Up @@ -410,6 +423,7 @@ in {

boot.bootspec.extensions."org.nixos.systemd-boot" = {
inherit (config.boot.loader.systemd-boot) sortKey;
devicetree = lib.mkIf cfg.installDeviceTree "${config.hardware.deviceTree.package}/${config.hardware.deviceTree.name}";
};

system = {
Expand Down
19 changes: 18 additions & 1 deletion nixos/tests/systemd-boot.nix
Original file line number Diff line number Diff line change
Expand Up @@ -172,10 +172,23 @@ rec {
imports = [ common ];
specialisation.something.configuration = {
boot.loader.systemd-boot.sortKey = "something";

# Since qemu will dynamically create a devicetree blob when starting
# up, it is not straight forward to create an export of that devicetree
# blob without knowing before-hand all the flags we would pass to qemu
# (we would then be able to use `dumpdtb`). Thus, the following config
# will not boot, but it does allow us to assert that the boot entry has
# the correct contents.
boot.loader.systemd-boot.installDeviceTree = pkgs.stdenv.hostPlatform.isAarch64;
hardware.deviceTree.name = "dummy.dtb";
hardware.deviceTree.package = lib.mkForce (pkgs.runCommand "dummy-devicetree-package" { } ''
mkdir -p $out
cp ${pkgs.emptyFile} $out/dummy.dtb
'');
};
};

testScript = ''
testScript = { nodes, ... }: ''
machine.start()
machine.wait_for_unit("multi-user.target")

Expand All @@ -188,6 +201,10 @@ rec {
machine.succeed(
"grep 'sort-key something' /boot/loader/entries/nixos-generation-1-specialisation-something.conf"
)
'' + pkgs.lib.optionalString pkgs.stdenv.hostPlatform.isAarch64 ''
machine.succeed(
"grep 'devicetree .*dummy' /boot/loader/entries/nixos-generation-1-specialisation-something.conf"
)
'';
};

Expand Down