at 18.03-beta 15 kB view raw
1# This module creates a bootable ISO image containing the given NixOS 2# configuration. The derivation for the ISO image will be placed in 3# config.system.build.isoImage. 4 5{ config, lib, pkgs, ... }: 6 7with lib; 8 9let 10 # Timeout in syslinux is in units of 1/10 of a second. 11 # 0 is used to disable timeouts. 12 syslinuxTimeout = if config.boot.loader.timeout == null then 13 0 14 else 15 max (config.boot.loader.timeout * 10) 1; 16 17 18 max = x: y: if x > y then x else y; 19 20 # The configuration file for syslinux. 21 22 # Notes on syslinux configuration and UNetbootin compatiblity: 23 # * Do not use '/syslinux/syslinux.cfg' as the path for this 24 # configuration. UNetbootin will not parse the file and use it as-is. 25 # This results in a broken configuration if the partition label does 26 # not match the specified config.isoImage.volumeID. For this reason 27 # we're using '/isolinux/isolinux.cfg'. 28 # * Use APPEND instead of adding command-line arguments directly after 29 # the LINUX entries. 30 # * COM32 entries (chainload, reboot, poweroff) are not recognized. They 31 # result in incorrect boot entries. 32 33 baseIsolinuxCfg = '' 34 SERIAL 0 38400 35 TIMEOUT ${builtins.toString syslinuxTimeout} 36 UI vesamenu.c32 37 MENU TITLE NixOS 38 MENU BACKGROUND /isolinux/background.png 39 DEFAULT boot 40 41 LABEL boot 42 MENU LABEL NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel} 43 LINUX /boot/${config.system.boot.loader.kernelFile} 44 APPEND init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} 45 INITRD /boot/${config.system.boot.loader.initrdFile} 46 47 # A variant to boot with 'nomodeset' 48 LABEL boot-nomodeset 49 MENU LABEL NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel} (nomodeset) 50 LINUX /boot/${config.system.boot.loader.kernelFile} 51 APPEND init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} nomodeset 52 INITRD /boot/${config.system.boot.loader.initrdFile} 53 54 # A variant to boot with 'copytoram' 55 LABEL boot-copytoram 56 MENU LABEL NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel} (copytoram) 57 LINUX /boot/${config.system.boot.loader.kernelFile} 58 APPEND init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} copytoram 59 INITRD /boot/${config.system.boot.loader.initrdFile} 60 61 # A variant to boot with verbose logging to the console 62 LABEL boot-nomodeset 63 MENU LABEL NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel} (debug) 64 LINUX /boot/${config.system.boot.loader.kernelFile} 65 APPEND init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} loglevel=7 66 INITRD /boot/${config.system.boot.loader.initrdFile} 67 ''; 68 69 isolinuxMemtest86Entry = '' 70 LABEL memtest 71 MENU LABEL Memtest86+ 72 LINUX /boot/memtest.bin 73 APPEND ${toString config.boot.loader.grub.memtest86.params} 74 ''; 75 76 isolinuxCfg = baseIsolinuxCfg + (optionalString config.boot.loader.grub.memtest86.enable isolinuxMemtest86Entry); 77 78 # The EFI boot image. 79 efiDir = pkgs.runCommand "efi-directory" {} '' 80 mkdir -p $out/EFI/boot 81 cp -v ${pkgs.systemd}/lib/systemd/boot/efi/systemd-boot${targetArch}.efi $out/EFI/boot/boot${targetArch}.efi 82 mkdir -p $out/loader/entries 83 84 cat << EOF > $out/loader/entries/nixos-iso.conf 85 title NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel} 86 linux /boot/${config.system.boot.loader.kernelFile} 87 initrd /boot/${config.system.boot.loader.initrdFile} 88 options init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} 89 EOF 90 91 # A variant to boot with 'nomodeset' 92 cat << EOF > $out/loader/entries/nixos-iso-nomodeset.conf 93 title NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel} 94 version nomodeset 95 linux /boot/${config.system.boot.loader.kernelFile} 96 initrd /boot/${config.system.boot.loader.initrdFile} 97 options init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} nomodeset 98 EOF 99 100 # A variant to boot with 'copytoram' 101 cat << EOF > $out/loader/entries/nixos-iso-copytoram.conf 102 title NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel} 103 version copytoram 104 linux /boot/${config.system.boot.loader.kernelFile} 105 initrd /boot/${config.system.boot.loader.initrdFile} 106 options init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} copytoram 107 EOF 108 109 # A variant to boot with verbose logging to the console 110 cat << EOF > $out/loader/entries/nixos-iso-debug.conf 111 title NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel} (debug) 112 linux /boot/${config.system.boot.loader.kernelFile} 113 initrd /boot/${config.system.boot.loader.initrdFile} 114 options init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} loglevel=7 115 EOF 116 117 cat << EOF > $out/loader/loader.conf 118 default nixos-iso 119 timeout ${builtins.toString config.boot.loader.timeout} 120 EOF 121 ''; 122 123 efiImg = pkgs.runCommand "efi-image_eltorito" { buildInputs = [ pkgs.mtools pkgs.libfaketime ]; } 124 # Be careful about determinism: du --apparent-size, 125 # dates (cp -p, touch, mcopy -m, faketime for label), IDs (mkfs.vfat -i) 126 '' 127 mkdir ./contents && cd ./contents 128 cp -rp "${efiDir}"/* . 129 mkdir ./boot 130 cp -p "${config.boot.kernelPackages.kernel}/${config.system.boot.loader.kernelFile}" \ 131 "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}" ./boot/ 132 touch --date=@0 ./* 133 134 usage_size=$(du -sb --apparent-size . | tr -cd '[:digit:]') 135 # Make the image 110% as big as the files need to make up for FAT overhead 136 image_size=$(( ($usage_size * 110) / 100 )) 137 # Make the image fit blocks of 1M 138 block_size=$((1024*1024)) 139 image_size=$(( ($image_size / $block_size + 1) * $block_size )) 140 echo "Usage size: $usage_size" 141 echo "Image size: $image_size" 142 truncate --size=$image_size "$out" 143 ${pkgs.libfaketime}/bin/faketime "2000-01-01 00:00:00" ${pkgs.dosfstools}/sbin/mkfs.vfat -i 12345678 -n EFIBOOT "$out" 144 mcopy -bpsvm -i "$out" ./* :: 145 ''; # */ 146 147 targetArch = if pkgs.stdenv.isi686 then 148 "ia32" 149 else if pkgs.stdenv.isx86_64 then 150 "x64" 151 else 152 throw "Unsupported architecture"; 153 154in 155 156{ 157 options = { 158 159 isoImage.isoName = mkOption { 160 default = "${config.isoImage.isoBaseName}.iso"; 161 description = '' 162 Name of the generated ISO image file. 163 ''; 164 }; 165 166 isoImage.isoBaseName = mkOption { 167 default = "nixos"; 168 description = '' 169 Prefix of the name of the generated ISO image file. 170 ''; 171 }; 172 173 isoImage.compressImage = mkOption { 174 default = false; 175 description = '' 176 Whether the ISO image should be compressed using 177 <command>bzip2</command>. 178 ''; 179 }; 180 181 isoImage.volumeID = mkOption { 182 default = "NIXOS_BOOT_CD"; 183 description = '' 184 Specifies the label or volume ID of the generated ISO image. 185 Note that the label is used by stage 1 of the boot process to 186 mount the CD, so it should be reasonably distinctive. 187 ''; 188 }; 189 190 isoImage.contents = mkOption { 191 example = literalExample '' 192 [ { source = pkgs.memtest86 + "/memtest.bin"; 193 target = "boot/memtest.bin"; 194 } 195 ] 196 ''; 197 description = '' 198 This option lists files to be copied to fixed locations in the 199 generated ISO image. 200 ''; 201 }; 202 203 isoImage.storeContents = mkOption { 204 example = literalExample "[ pkgs.stdenv ]"; 205 description = '' 206 This option lists additional derivations to be included in the 207 Nix store in the generated ISO image. 208 ''; 209 }; 210 211 isoImage.includeSystemBuildDependencies = mkOption { 212 default = false; 213 description = '' 214 Set this option to include all the needed sources etc in the 215 image. It significantly increases image size. Use that when 216 you want to be able to keep all the sources needed to build your 217 system or when you are going to install the system on a computer 218 with slow or non-existent network connection. 219 ''; 220 }; 221 222 isoImage.makeEfiBootable = mkOption { 223 default = false; 224 description = '' 225 Whether the ISO image should be an efi-bootable volume. 226 ''; 227 }; 228 229 isoImage.makeUsbBootable = mkOption { 230 default = false; 231 description = '' 232 Whether the ISO image should be bootable from CD as well as USB. 233 ''; 234 }; 235 236 isoImage.splashImage = mkOption { 237 default = pkgs.fetchurl { 238 url = https://raw.githubusercontent.com/NixOS/nixos-artwork/5729ab16c6a5793c10a2913b5a1b3f59b91c36ee/ideas/grub-splash/grub-nixos-1.png; 239 sha256 = "43fd8ad5decf6c23c87e9026170a13588c2eba249d9013cb9f888da5e2002217"; 240 }; 241 description = '' 242 The splash image to use in the bootloader. 243 ''; 244 }; 245 246 isoImage.appendToMenuLabel = mkOption { 247 default = " Installer"; 248 example = " Live System"; 249 description = '' 250 The string to append after the menu label for the NixOS system. 251 This will be directly appended (without whitespace) to the NixOS version 252 string, like for example if it is set to <literal>XXX</literal>: 253 254 <para><literal>NixOS 99.99-pre666XXX</literal></para> 255 ''; 256 }; 257 258 }; 259 260 config = { 261 262 boot.loader.grub.version = 2; 263 264 # Don't build the GRUB menu builder script, since we don't need it 265 # here and it causes a cyclic dependency. 266 boot.loader.grub.enable = false; 267 268 # !!! Hack - attributes expected by other modules. 269 system.boot.loader.kernelFile = "bzImage"; 270 environment.systemPackages = [ pkgs.grub2 pkgs.grub2_efi pkgs.syslinux ]; 271 272 # In stage 1 of the boot, mount the CD as the root FS by label so 273 # that we don't need to know its device. We pass the label of the 274 # root filesystem on the kernel command line, rather than in 275 # `fileSystems' below. This allows CD-to-USB converters such as 276 # UNetbootin to rewrite the kernel command line to pass the label or 277 # UUID of the USB stick. It would be nicer to write 278 # `root=/dev/disk/by-label/...' here, but UNetbootin doesn't 279 # recognise that. 280 boot.kernelParams = 281 [ "root=LABEL=${config.isoImage.volumeID}" 282 "boot.shell_on_fail" 283 ]; 284 285 fileSystems."/" = 286 { fsType = "tmpfs"; 287 options = [ "mode=0755" ]; 288 }; 289 290 # Note that /dev/root is a symlink to the actual root device 291 # specified on the kernel command line, created in the stage 1 292 # init script. 293 fileSystems."/iso" = 294 { device = "/dev/root"; 295 neededForBoot = true; 296 noCheck = true; 297 }; 298 299 # In stage 1, mount a tmpfs on top of /nix/store (the squashfs 300 # image) to make this a live CD. 301 fileSystems."/nix/.ro-store" = 302 { fsType = "squashfs"; 303 device = "/iso/nix-store.squashfs"; 304 options = [ "loop" ]; 305 neededForBoot = true; 306 }; 307 308 fileSystems."/nix/.rw-store" = 309 { fsType = "tmpfs"; 310 options = [ "mode=0755" ]; 311 neededForBoot = true; 312 }; 313 314 fileSystems."/nix/store" = 315 { fsType = "unionfs-fuse"; 316 device = "unionfs"; 317 options = [ "allow_other" "cow" "nonempty" "chroot=/mnt-root" "max_files=32768" "hide_meta_files" "dirs=/nix/.rw-store=rw:/nix/.ro-store=ro" ]; 318 }; 319 320 boot.initrd.availableKernelModules = [ "squashfs" "iso9660" "usb-storage" "uas" ]; 321 322 boot.blacklistedKernelModules = [ "nouveau" ]; 323 324 boot.initrd.kernelModules = [ "loop" ]; 325 326 # Closures to be copied to the Nix store on the CD, namely the init 327 # script and the top-level system configuration directory. 328 isoImage.storeContents = 329 [ config.system.build.toplevel ] ++ 330 optional config.isoImage.includeSystemBuildDependencies 331 config.system.build.toplevel.drvPath; 332 333 # Create the squashfs image that contains the Nix store. 334 system.build.squashfsStore = pkgs.callPackage ../../../lib/make-squashfs.nix { 335 storeContents = config.isoImage.storeContents; 336 }; 337 338 # Individual files to be included on the CD, outside of the Nix 339 # store on the CD. 340 isoImage.contents = 341 [ { source = pkgs.substituteAll { 342 name = "isolinux.cfg"; 343 src = pkgs.writeText "isolinux.cfg-in" isolinuxCfg; 344 bootRoot = "/boot"; 345 }; 346 target = "/isolinux/isolinux.cfg"; 347 } 348 { source = config.boot.kernelPackages.kernel + "/" + config.system.boot.loader.kernelFile; 349 target = "/boot/" + config.system.boot.loader.kernelFile; 350 } 351 { source = config.system.build.initialRamdisk + "/" + config.system.boot.loader.initrdFile; 352 target = "/boot/" + config.system.boot.loader.initrdFile; 353 } 354 { source = config.system.build.squashfsStore; 355 target = "/nix-store.squashfs"; 356 } 357 { source = "${pkgs.syslinux}/share/syslinux"; 358 target = "/isolinux"; 359 } 360 { source = config.isoImage.splashImage; 361 target = "/isolinux/background.png"; 362 } 363 { source = pkgs.writeText "version" config.system.nixos.label; 364 target = "/version.txt"; 365 } 366 ] ++ optionals config.isoImage.makeEfiBootable [ 367 { source = efiImg; 368 target = "/boot/efi.img"; 369 } 370 { source = "${efiDir}/EFI"; 371 target = "/EFI"; 372 } 373 { source = "${efiDir}/loader"; 374 target = "/loader"; 375 } 376 ] ++ optionals config.boot.loader.grub.memtest86.enable [ 377 { source = "${pkgs.memtest86plus}/memtest.bin"; 378 target = "/boot/memtest.bin"; 379 } 380 ]; 381 382 boot.loader.timeout = 10; 383 384 # Create the ISO image. 385 system.build.isoImage = pkgs.callPackage ../../../lib/make-iso9660-image.nix ({ 386 inherit (config.isoImage) isoName compressImage volumeID contents; 387 bootable = true; 388 bootImage = "/isolinux/isolinux.bin"; 389 } // optionalAttrs config.isoImage.makeUsbBootable { 390 usbBootable = true; 391 isohybridMbrImage = "${pkgs.syslinux}/share/syslinux/isohdpfx.bin"; 392 } // optionalAttrs config.isoImage.makeEfiBootable { 393 efiBootable = true; 394 efiBootImage = "boot/efi.img"; 395 }); 396 397 boot.postBootCommands = 398 '' 399 # After booting, register the contents of the Nix store on the 400 # CD in the Nix database in the tmpfs. 401 ${config.nix.package.out}/bin/nix-store --load-db < /nix/store/nix-path-registration 402 403 # nixos-rebuild also requires a "system" profile and an 404 # /etc/NIXOS tag. 405 touch /etc/NIXOS 406 ${config.nix.package.out}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system 407 ''; 408 409 # Add vfat support to the initrd to enable people to copy the 410 # contents of the CD to a bootable USB stick. 411 boot.initrd.supportedFilesystems = [ "vfat" ]; 412 413 }; 414 415}