From 33755e544dbb23f26f47c38a5a33d9b48c429cb1 Mon Sep 17 00:00:00 2001 From: qbisi Date: Mon, 5 May 2025 20:03:47 +0800 Subject: [PATCH 1/4] nixos/grub: insmod efi_uga on i386 and x86_64 only The insmod efi_gop and insmod efi_uga directives load two modules for EFI-based video support. On most systems the efi_gop module is enough. The efi_uga module is only useful and available for legacy system on i386 and x86_64. Therefore, we load the efi_uga module conditionally. --- nixos/modules/system/boot/loader/grub/install-grub.pl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/nixos/modules/system/boot/loader/grub/install-grub.pl b/nixos/modules/system/boot/loader/grub/install-grub.pl index f5ae844da21f9..12a3bcded24ce 100644 --- a/nixos/modules/system/boot/loader/grub/install-grub.pl +++ b/nixos/modules/system/boot/loader/grub/install-grub.pl @@ -337,7 +337,9 @@ sub GrubFs { # Setup the graphics stack for bios and efi systems if [ \"\${grub_platform}\" = \"efi\" ]; then insmod efi_gop - insmod efi_uga + if [ \"\${grub_cpu}\" = \"i386\" -o \"\${grub_cpu}\" = \"x86_64\"]; then + insmod efi_uga + fi else insmod vbe fi From dffee5f0be06d4fffaa67c4223165ceea1cb2cd0 Mon Sep 17 00:00:00 2001 From: qbisi Date: Sat, 27 Sep 2025 14:07:59 +0800 Subject: [PATCH 2/4] nixos/loader: introduce option loadDeviceTree Rename `boot.loader.systemd-boot.installDeviceTree` to `boot.loader.loadDeviceTree`. This option provides generic support for loading device trees. --- .../generic-extlinux-compatible/default.nix | 2 +- nixos/modules/system/boot/loader/loader.nix | 36 +++++++++++++++---- .../boot/loader/systemd-boot/systemd-boot.nix | 31 ++++++++-------- nixos/tests/systemd-boot.nix | 2 +- 4 files changed, 45 insertions(+), 26 deletions(-) diff --git a/nixos/modules/system/boot/loader/generic-extlinux-compatible/default.nix b/nixos/modules/system/boot/loader/generic-extlinux-compatible/default.nix index 41c4ba88996e1..3b4f94b7829a1 100644 --- a/nixos/modules/system/boot/loader/generic-extlinux-compatible/default.nix +++ b/nixos/modules/system/boot/loader/generic-extlinux-compatible/default.nix @@ -106,7 +106,7 @@ in let builderArgs = "-g ${toString cfg.configurationLimit} -t ${timeoutStr}" - + lib.optionalString (dtCfg.name != null) " -n ${dtCfg.name}" + + lib.optionalString config.boot.loader.loadDeviceTree " -n ${dtCfg.name}" + lib.optionalString (!cfg.useGenerationDeviceTree) " -r"; installBootLoader = pkgs.writeScript "install-extlinux-conf.sh" ( '' diff --git a/nixos/modules/system/boot/loader/loader.nix b/nixos/modules/system/boot/loader/loader.nix index bc876ff8f804e..edd1b5dfcb21b 100644 --- a/nixos/modules/system/boot/loader/loader.nix +++ b/nixos/modules/system/boot/loader/loader.nix @@ -1,4 +1,4 @@ -{ lib, ... }: +{ config, lib, ... }: with lib; @@ -9,12 +9,34 @@ with lib; ]; options = { - boot.loader.timeout = mkOption { - default = 5; - type = types.nullOr types.int; - description = '' - Timeout (in seconds) until loader boots the default menu item. Use null if the loader menu should be displayed indefinitely. - ''; + boot.loader = { + timeout = mkOption { + default = 5; + type = types.nullOr types.int; + description = '' + Timeout (in seconds) until loader boots the default menu item. Use null if the loader menu should be displayed indefinitely. + ''; + }; + loadDeviceTree = mkOption { + default = with config.hardware.deviceTree; enable && name != null; + defaultText = ''with config.hardware.deviceTree; enable && name != null''; + description = '' + Load the devicetree blob specified by `config.hardware.deviceTree.name` + and instruct bootloader to pass this DTB to linux. + ''; + }; }; }; + + config = { + assertions = [ + { + assertion = + config.boot.loader.loadDeviceTree + -> config.hardware.deviceTree.enable + -> config.hardware.deviceTree.name != null; + message = "Cannot load devicetree without 'config.hardware.deviceTree.enable' enabled and 'config.hardware.deviceTree.name' set"; + } + ]; + }; } diff --git a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix index 035462d10402b..a00d11aaf2dab 100644 --- a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix +++ b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix @@ -161,6 +161,19 @@ in ] (config: lib.strings.removeSuffix ".conf" config.boot.loader.systemd-boot.netbootxyz.entryFilename) ) + (mkRenamedOptionModule + [ + "boot" + "loader" + "systemd-boot" + "installDeviceTree" + ] + [ + "boot" + "loader" + "loadDeviceTree" + ] + ) ]; options.boot.loader.systemd-boot = { @@ -244,15 +257,6 @@ 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 = '' @@ -535,13 +539,6 @@ 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: [ { @@ -626,7 +623,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}"; + devicetree = lib.mkIf config.boot.loader.loadDeviceTree "${config.hardware.deviceTree.package}/${config.hardware.deviceTree.name}"; }; system = { diff --git a/nixos/tests/systemd-boot.nix b/nixos/tests/systemd-boot.nix index fe0bde3176462..1c164315c2053 100644 --- a/nixos/tests/systemd-boot.nix +++ b/nixos/tests/systemd-boot.nix @@ -212,7 +212,7 @@ in # (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; + boot.loader.loadDeviceTree = pkgs.stdenv.hostPlatform.isAarch64; hardware.deviceTree.name = "dummy.dtb"; hardware.deviceTree.package = lib.mkForce ( pkgs.runCommand "dummy-devicetree-package" { } '' From a7bcf3552dc29f3801e7acec4d56a2b2d054532a Mon Sep 17 00:00:00 2001 From: qbisi Date: Sat, 27 Sep 2025 14:19:27 +0800 Subject: [PATCH 3/4] nixos/grub: add support for loading device-tree --- nixos/modules/system/boot/loader/grub/grub.nix | 4 ++++ nixos/modules/system/boot/loader/grub/install-grub.pl | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/nixos/modules/system/boot/loader/grub/grub.nix b/nixos/modules/system/boot/loader/grub/grub.nix index c18d5431bd8ab..04d94b3084c1b 100644 --- a/nixos/modules/system/boot/loader/grub/grub.nix +++ b/nixos/modules/system/boot/loader/grub/grub.nix @@ -845,6 +845,10 @@ in environment.systemPackages = mkIf (grub != null) [ grub ]; + boot.bootspec.extensions."org.nixos.grub" = { + devicetree = lib.mkIf config.boot.loader.loadDeviceTree "${config.hardware.deviceTree.package}/${config.hardware.deviceTree.name}"; + }; + boot.loader.grub.extraPrepareConfig = concatStrings ( mapAttrsToList ( fileName: sourcePath: diff --git a/nixos/modules/system/boot/loader/grub/install-grub.pl b/nixos/modules/system/boot/loader/grub/install-grub.pl index 12a3bcded24ce..f2c788bc9341a 100644 --- a/nixos/modules/system/boot/loader/grub/install-grub.pl +++ b/nixos/modules/system/boot/loader/grub/install-grub.pl @@ -467,6 +467,13 @@ sub addEntry { my $kernel = copyToKernelsDir(Cwd::abs_path("$path/kernel")); my $initrd = copyToKernelsDir(Cwd::abs_path("$path/initrd")); + my $json_text = read_file("$path/boot.json"); + my $data = decode_json($json_text); + my $devicetree; + if (exists $data->{"org.nixos.grub"} && exists $data->{"org.nixos.grub"}->{devicetree}) { + $devicetree = $data->{"org.nixos.grub"}->{devicetree}; + } + my $fdtfile = defined $devicetree ? copyToKernelsDir(Cwd::abs_path("$devicetree")) : undef; # Include second initrd with secrets if (-e -x "$path/append-initrd-secrets") { @@ -518,6 +525,7 @@ sub addEntry { } $conf .= " $extraPerEntryConfig\n" if $extraPerEntryConfig; $conf .= " multiboot $xen $xenParams\n" if $xen; + $conf .= " devicetree $fdtfile\n" if $fdtfile; $conf .= " " . ($xen ? "module" : "linux") . " $kernel $kernelParams\n"; $conf .= " " . ($xen ? "module" : "initrd") . " $initrd\n"; $conf .= "}\n\n"; From 7a0c81709079bd6b16fecdc52a023218d0abd540 Mon Sep 17 00:00:00 2001 From: qbisi Date: Sat, 27 Sep 2025 15:15:00 +0800 Subject: [PATCH 4/4] nixosTest.device-tree: init with systemd-boot and grub tests --- nixos/tests/all-tests.nix | 1 + nixos/tests/device-tree.nix | 80 +++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 nixos/tests/device-tree.nix diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 98cb57a02c3aa..ad1baedeb0a30 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -440,6 +440,7 @@ in dendrite = runTest ./matrix/dendrite.nix; dep-scan = runTest ./dep-scan.nix; dependency-track = runTest ./dependency-track.nix; + device-tree = import ./device-tree.nix { inherit pkgs runTestOn; }; devpi-server = runTest ./devpi-server.nix; dex-oidc = runTest ./dex-oidc.nix; dhparams = runTest ./dhparams.nix; diff --git a/nixos/tests/device-tree.nix b/nixos/tests/device-tree.nix new file mode 100644 index 0000000000000..4e09ab19353e4 --- /dev/null +++ b/nixos/tests/device-tree.nix @@ -0,0 +1,80 @@ +{ + pkgs, + runTestOn, +}: + +let + supportedSystems = [ + "aarch64-linux" + ]; + common = + { + config, + lib, + pkgs, + ... + }: + let + inherit (pkgs.stdenv.hostPlatform) qemuArch; + in + { + virtualisation.useBootLoader = true; + virtualisation.useEFIBoot = true; + + hardware.deviceTree = { + name = "qemu-${qemuArch}-virt.dtb"; + package = lib.mkForce ( + pkgs.runCommand "qemu-dtb" { } '' + mkdir $out + ${pkgs.qemu}/bin/qemu-system-${qemuArch} \ + -cpu max -machine virt,gic-version=max,accel=kvm:tcg,dumpdtb=$out/${config.hardware.deviceTree.name} + '' + ); + }; + }; + testScript = + { nodes, ... }: + '' + machine.start() + machine.wait_for_unit("multi-user.target") + + machine.succeed("grep 'linux,dummy-virt' /sys/firmware/devicetree/base/model") + ''; +in + +{ + systemd-boot = runTestOn supportedSystems { + name = "device-tree-systemd-boot"; + meta.maintainers = with pkgs.lib.maintainers; [ + qbisi + ]; + + nodes.machine = { + imports = [ common ]; + + boot.loader.systemd-boot.enable = true; + }; + + inherit testScript; + }; + + grub = runTestOn supportedSystems { + name = "device-tree-grub"; + meta.maintainers = with pkgs.lib.maintainers; [ + qbisi + ]; + + nodes.machine = { + imports = [ common ]; + + boot.loader.grub = { + enable = true; + device = "nodev"; + efiSupport = true; + efiInstallAsRemovable = true; + }; + }; + + inherit testScript; + }; +}