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