1# This module creates netboot media containing the given NixOS
2# configuration.
3
4{ config, lib, pkgs, ... }:
5
6with lib;
7
8{
9 options = {
10
11 netboot.squashfsCompression = mkOption {
12 default = with pkgs.stdenv.hostPlatform; "xz -Xdict-size 100% "
13 + lib.optionalString isx86 "-Xbcj x86"
14 # Untested but should also reduce size for these platforms
15 + lib.optionalString isAarch "-Xbcj arm"
16 + lib.optionalString (isPower && is32bit && isBigEndian) "-Xbcj powerpc"
17 + lib.optionalString (isSparc) "-Xbcj sparc";
18 description = lib.mdDoc ''
19 Compression settings to use for the squashfs nix store.
20 '';
21 example = "zstd -Xcompression-level 6";
22 type = types.str;
23 };
24
25 netboot.storeContents = mkOption {
26 example = literalExpression "[ pkgs.stdenv ]";
27 description = lib.mdDoc ''
28 This option lists additional derivations to be included in the
29 Nix store in the generated netboot image.
30 '';
31 };
32
33 };
34
35 config = {
36 # Don't build the GRUB menu builder script, since we don't need it
37 # here and it causes a cyclic dependency.
38 boot.loader.grub.enable = false;
39
40 # !!! Hack - attributes expected by other modules.
41 environment.systemPackages = [ pkgs.grub2_efi ]
42 ++ (if pkgs.stdenv.hostPlatform.system == "aarch64-linux"
43 then []
44 else [ pkgs.grub2 pkgs.syslinux ]);
45
46 fileSystems."/" = mkImageMediaOverride
47 { fsType = "tmpfs";
48 options = [ "mode=0755" ];
49 };
50
51 # In stage 1, mount a tmpfs on top of /nix/store (the squashfs
52 # image) to make this a live CD.
53 fileSystems."/nix/.ro-store" = mkImageMediaOverride
54 { fsType = "squashfs";
55 device = "../nix-store.squashfs";
56 options = [ "loop" ];
57 neededForBoot = true;
58 };
59
60 fileSystems."/nix/.rw-store" = mkImageMediaOverride
61 { fsType = "tmpfs";
62 options = [ "mode=0755" ];
63 neededForBoot = true;
64 };
65
66 fileSystems."/nix/store" = mkImageMediaOverride
67 { fsType = "overlay";
68 device = "overlay";
69 options = [
70 "lowerdir=/nix/.ro-store"
71 "upperdir=/nix/.rw-store/store"
72 "workdir=/nix/.rw-store/work"
73 ];
74
75 depends = [
76 "/nix/.ro-store"
77 "/nix/.rw-store/store"
78 "/nix/.rw-store/work"
79 ];
80 };
81
82 boot.initrd.availableKernelModules = [ "squashfs" "overlay" ];
83
84 boot.initrd.kernelModules = [ "loop" "overlay" ];
85
86 # Closures to be copied to the Nix store, namely the init
87 # script and the top-level system configuration directory.
88 netboot.storeContents =
89 [ config.system.build.toplevel ];
90
91 # Create the squashfs image that contains the Nix store.
92 system.build.squashfsStore = pkgs.callPackage ../../../lib/make-squashfs.nix {
93 storeContents = config.netboot.storeContents;
94 comp = config.netboot.squashfsCompression;
95 };
96
97
98 # Create the initrd
99 system.build.netbootRamdisk = pkgs.makeInitrdNG {
100 inherit (config.boot.initrd) compressor;
101 prepend = [ "${config.system.build.initialRamdisk}/initrd" ];
102
103 contents =
104 [ { object = config.system.build.squashfsStore;
105 symlink = "/nix-store.squashfs";
106 }
107 ];
108 };
109
110 system.build.netbootIpxeScript = pkgs.writeTextDir "netboot.ipxe" ''
111 #!ipxe
112 # Use the cmdline variable to allow the user to specify custom kernel params
113 # when chainloading this script from other iPXE scripts like netboot.xyz
114 kernel ${pkgs.stdenv.hostPlatform.linux-kernel.target} init=${config.system.build.toplevel}/init initrd=initrd ${toString config.boot.kernelParams} ''${cmdline}
115 initrd initrd
116 boot
117 '';
118
119 # A script invoking kexec on ./bzImage and ./initrd.gz.
120 # Usually used through system.build.kexecTree, but exposed here for composability.
121 system.build.kexecScript = pkgs.writeScript "kexec-boot" ''
122 #!/usr/bin/env bash
123 if ! kexec -v >/dev/null 2>&1; then
124 echo "kexec not found: please install kexec-tools" 2>&1
125 exit 1
126 fi
127 SCRIPT_DIR=$( cd -- "$( dirname -- "''${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
128 kexec --load ''${SCRIPT_DIR}/bzImage \
129 --initrd=''${SCRIPT_DIR}/initrd.gz \
130 --command-line "init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}"
131 kexec -e
132 '';
133
134 # A tree containing initrd.gz, bzImage and a kexec-boot script.
135 system.build.kexecTree = pkgs.linkFarm "kexec-tree" [
136 {
137 name = "initrd.gz";
138 path = "${config.system.build.netbootRamdisk}/initrd";
139 }
140 {
141 name = "bzImage";
142 path = "${config.system.build.kernel}/${config.system.boot.loader.kernelFile}";
143 }
144 {
145 name = "kexec-boot";
146 path = config.system.build.kexecScript;
147 }
148 ];
149
150 boot.loader.timeout = 10;
151
152 boot.postBootCommands =
153 ''
154 # After booting, register the contents of the Nix store
155 # in the Nix database in the tmpfs.
156 ${config.nix.package}/bin/nix-store --load-db < /nix/store/nix-path-registration
157
158 # nixos-rebuild also requires a "system" profile and an
159 # /etc/NIXOS tag.
160 touch /etc/NIXOS
161 ${config.nix.package}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
162 '';
163
164 };
165
166}