at 21.11-pre 27 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 /** 11 * Given a list of `options`, concats the result of mapping each options 12 * to a menuentry for use in grub. 13 * 14 * * defaults: {name, image, params, initrd} 15 * * options: [ option... ] 16 * * option: {name, params, class} 17 */ 18 menuBuilderGrub2 = 19 defaults: options: lib.concatStrings 20 ( 21 map 22 (option: '' 23 menuentry '${defaults.name} ${ 24 # Name appended to menuentry defaults to params if no specific name given. 25 option.name or (if option ? params then "(${option.params})" else "") 26 }' ${if option ? class then " --class ${option.class}" else ""} { 27 linux ${defaults.image} \''${isoboot} ${defaults.params} ${ 28 option.params or "" 29 } 30 initrd ${defaults.initrd} 31 } 32 '') 33 options 34 ) 35 ; 36 37 /** 38 * Given a `config`, builds the default options. 39 */ 40 buildMenuGrub2 = config: 41 buildMenuAdditionalParamsGrub2 config "" 42 ; 43 44 /** 45 * Given a `config` and params to add to `params`, build a set of default options. 46 * Use this one when creating a variant (e.g. hidpi) 47 */ 48 buildMenuAdditionalParamsGrub2 = config: additional: 49 let 50 finalCfg = { 51 name = "NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel}"; 52 params = "init=${config.system.build.toplevel}/init ${additional} ${toString config.boot.kernelParams}"; 53 image = "/boot/${config.system.boot.loader.kernelFile}"; 54 initrd = "/boot/initrd"; 55 }; 56 in 57 menuBuilderGrub2 58 finalCfg 59 [ 60 { class = "installer"; } 61 { class = "nomodeset"; params = "nomodeset"; } 62 { class = "copytoram"; params = "copytoram"; } 63 { class = "debug"; params = "debug"; } 64 ] 65 ; 66 67 # Timeout in syslinux is in units of 1/10 of a second. 68 # 0 is used to disable timeouts. 69 syslinuxTimeout = if config.boot.loader.timeout == null then 70 0 71 else 72 max (config.boot.loader.timeout * 10) 1; 73 74 75 max = x: y: if x > y then x else y; 76 77 # The configuration file for syslinux. 78 79 # Notes on syslinux configuration and UNetbootin compatiblity: 80 # * Do not use '/syslinux/syslinux.cfg' as the path for this 81 # configuration. UNetbootin will not parse the file and use it as-is. 82 # This results in a broken configuration if the partition label does 83 # not match the specified config.isoImage.volumeID. For this reason 84 # we're using '/isolinux/isolinux.cfg'. 85 # * Use APPEND instead of adding command-line arguments directly after 86 # the LINUX entries. 87 # * COM32 entries (chainload, reboot, poweroff) are not recognized. They 88 # result in incorrect boot entries. 89 90 baseIsolinuxCfg = '' 91 SERIAL 0 115200 92 TIMEOUT ${builtins.toString syslinuxTimeout} 93 UI vesamenu.c32 94 MENU TITLE NixOS 95 MENU BACKGROUND /isolinux/background.png 96 MENU RESOLUTION 800 600 97 MENU CLEAR 98 MENU ROWS 6 99 MENU CMDLINEROW -4 100 MENU TIMEOUTROW -3 101 MENU TABMSGROW -2 102 MENU HELPMSGROW -1 103 MENU HELPMSGENDROW -1 104 MENU MARGIN 0 105 106 # FG:AARRGGBB BG:AARRGGBB shadow 107 MENU COLOR BORDER 30;44 #00000000 #00000000 none 108 MENU COLOR SCREEN 37;40 #FF000000 #00E2E8FF none 109 MENU COLOR TABMSG 31;40 #80000000 #00000000 none 110 MENU COLOR TIMEOUT 1;37;40 #FF000000 #00000000 none 111 MENU COLOR TIMEOUT_MSG 37;40 #FF000000 #00000000 none 112 MENU COLOR CMDMARK 1;36;40 #FF000000 #00000000 none 113 MENU COLOR CMDLINE 37;40 #FF000000 #00000000 none 114 MENU COLOR TITLE 1;36;44 #00000000 #00000000 none 115 MENU COLOR UNSEL 37;44 #FF000000 #00000000 none 116 MENU COLOR SEL 7;37;40 #FFFFFFFF #FF5277C3 std 117 118 DEFAULT boot 119 120 LABEL boot 121 MENU LABEL NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel} 122 LINUX /boot/${config.system.boot.loader.kernelFile} 123 APPEND init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} 124 INITRD /boot/${config.system.boot.loader.initrdFile} 125 126 # A variant to boot with 'nomodeset' 127 LABEL boot-nomodeset 128 MENU LABEL NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel} (nomodeset) 129 LINUX /boot/${config.system.boot.loader.kernelFile} 130 APPEND init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} nomodeset 131 INITRD /boot/${config.system.boot.loader.initrdFile} 132 133 # A variant to boot with 'copytoram' 134 LABEL boot-copytoram 135 MENU LABEL NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel} (copytoram) 136 LINUX /boot/${config.system.boot.loader.kernelFile} 137 APPEND init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} copytoram 138 INITRD /boot/${config.system.boot.loader.initrdFile} 139 140 # A variant to boot with verbose logging to the console 141 LABEL boot-debug 142 MENU LABEL NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel} (debug) 143 LINUX /boot/${config.system.boot.loader.kernelFile} 144 APPEND init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} loglevel=7 145 INITRD /boot/${config.system.boot.loader.initrdFile} 146 147 # A variant to boot with a serial console enabled 148 LABEL boot-serial 149 MENU LABEL NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel} (serial console=ttyS0,115200n8) 150 LINUX /boot/${config.system.boot.loader.kernelFile} 151 APPEND init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} console=ttyS0,115200n8 152 INITRD /boot/${config.system.boot.loader.initrdFile} 153 ''; 154 155 isolinuxMemtest86Entry = '' 156 LABEL memtest 157 MENU LABEL Memtest86+ 158 LINUX /boot/memtest.bin 159 APPEND ${toString config.boot.loader.grub.memtest86.params} 160 ''; 161 162 isolinuxCfg = concatStringsSep "\n" 163 ([ baseIsolinuxCfg ] ++ optional config.boot.loader.grub.memtest86.enable isolinuxMemtest86Entry); 164 165 refindBinary = if targetArch == "x64" || targetArch == "aa64" then "refind_${targetArch}.efi" else null; 166 167 # Setup instructions for rEFInd. 168 refind = 169 if refindBinary != null then 170 '' 171 # Adds rEFInd to the ISO. 172 cp -v ${pkgs.refind}/share/refind/${refindBinary} $out/EFI/boot/ 173 '' 174 else 175 "# No refind for ${targetArch}" 176 ; 177 178 grubPkgs = if config.boot.loader.grub.forcei686 then pkgs.pkgsi686Linux else pkgs; 179 180 grubMenuCfg = '' 181 # 182 # Menu configuration 183 # 184 185 insmod gfxterm 186 insmod png 187 set gfxpayload=keep 188 189 # Fonts can be loaded? 190 # (This font is assumed to always be provided as a fallback by NixOS) 191 if loadfont /EFI/boot/unicode.pf2; then 192 set with_fonts=true 193 fi 194 if [ "\$textmode" != "true" -a "\$with_fonts" == "true" ]; then 195 # Use graphical term, it can be either with background image or a theme. 196 # input is "console", while output is "gfxterm". 197 # This enables "serial" input and output only when possible. 198 # Otherwise the failure mode is to not even enable gfxterm. 199 if test "\$with_serial" == "yes"; then 200 terminal_output gfxterm serial 201 terminal_input console serial 202 else 203 terminal_output gfxterm 204 terminal_input console 205 fi 206 else 207 # Sets colors for the non-graphical term. 208 set menu_color_normal=cyan/blue 209 set menu_color_highlight=white/blue 210 fi 211 212 ${ # When there is a theme configured, use it, otherwise use the background image. 213 if config.isoImage.grubTheme != null then '' 214 # Sets theme. 215 set theme=/EFI/boot/grub-theme/theme.txt 216 # Load theme fonts 217 $(find ${config.isoImage.grubTheme} -iname '*.pf2' -printf "loadfont /EFI/boot/grub-theme/%P\n") 218 '' else '' 219 if background_image /EFI/boot/efi-background.png; then 220 # Black background means transparent background when there 221 # is a background image set... This seems undocumented :( 222 set color_normal=black/black 223 set color_highlight=white/blue 224 else 225 # Falls back again to proper colors. 226 set menu_color_normal=cyan/blue 227 set menu_color_highlight=white/blue 228 fi 229 ''} 230 ''; 231 232 # The EFI boot image. 233 # Notes about grub: 234 # * Yes, the grubMenuCfg has to be repeated in all submenus. Otherwise you 235 # will get white-on-black console-like text on sub-menus. *sigh* 236 efiDir = pkgs.runCommand "efi-directory" { 237 nativeBuildInputs = [ pkgs.buildPackages.grub2_efi ]; 238 strictDeps = true; 239 } '' 240 mkdir -p $out/EFI/boot/ 241 242 # ALWAYS required modules. 243 MODULES="fat iso9660 part_gpt part_msdos \ 244 normal boot linux configfile loopback chain halt \ 245 efifwsetup efi_gop \ 246 ls search search_label search_fs_uuid search_fs_file \ 247 gfxmenu gfxterm gfxterm_background gfxterm_menu test all_video loadenv \ 248 exfat ext2 ntfs btrfs hfsplus udf \ 249 videoinfo png \ 250 echo serial \ 251 " 252 253 echo "Building GRUB with modules:" 254 for mod in $MODULES; do 255 echo " - $mod" 256 done 257 258 # Modules that may or may not be available per-platform. 259 echo "Adding additional modules:" 260 for mod in efi_uga; do 261 if [ -f ${grubPkgs.grub2_efi}/lib/grub/${grubPkgs.grub2_efi.grubTarget}/$mod.mod ]; then 262 echo " - $mod" 263 MODULES+=" $mod" 264 fi 265 done 266 267 # Make our own efi program, we can't rely on "grub-install" since it seems to 268 # probe for devices, even with --skip-fs-probe. 269 grub-mkimage --directory=${grubPkgs.grub2_efi}/lib/grub/${grubPkgs.grub2_efi.grubTarget} -o $out/EFI/boot/boot${targetArch}.efi -p /EFI/boot -O ${grubPkgs.grub2_efi.grubTarget} \ 270 $MODULES 271 cp ${grubPkgs.grub2_efi}/share/grub/unicode.pf2 $out/EFI/boot/ 272 273 cat <<EOF > $out/EFI/boot/grub.cfg 274 275 set with_fonts=false 276 set textmode=false 277 # If you want to use serial for "terminal_*" commands, you need to set one up: 278 # Example manual configuration: 279 # serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1 280 # This uses the defaults, and makes the serial terminal available. 281 set with_serial=no 282 if serial; then set with_serial=yes ;fi 283 export with_serial 284 clear 285 set timeout=10 286 287 # This message will only be viewable when "gfxterm" is not used. 288 echo "" 289 echo "Loading graphical boot menu..." 290 echo "" 291 echo "Press 't' to use the text boot menu on this console..." 292 echo "" 293 294 ${grubMenuCfg} 295 296 hiddenentry 'Text mode' --hotkey 't' { 297 loadfont /EFI/boot/unicode.pf2 298 set textmode=true 299 terminal_output gfxterm console 300 } 301 hiddenentry 'GUI mode' --hotkey 'g' { 302 $(find ${config.isoImage.grubTheme} -iname '*.pf2' -printf "loadfont /EFI/boot/grub-theme/%P\n") 303 set textmode=false 304 terminal_output gfxterm 305 } 306 307 308 # If the parameter iso_path is set, append the findiso parameter to the kernel 309 # line. We need this to allow the nixos iso to be booted from grub directly. 310 if [ \''${iso_path} ] ; then 311 set isoboot="findiso=\''${iso_path}" 312 fi 313 314 # 315 # Menu entries 316 # 317 318 ${buildMenuGrub2 config} 319 submenu "HiDPI, Quirks and Accessibility" --class hidpi --class submenu { 320 ${grubMenuCfg} 321 submenu "Suggests resolution @720p" --class hidpi-720p { 322 ${grubMenuCfg} 323 ${buildMenuAdditionalParamsGrub2 config "video=1280x720@60"} 324 } 325 submenu "Suggests resolution @1080p" --class hidpi-1080p { 326 ${grubMenuCfg} 327 ${buildMenuAdditionalParamsGrub2 config "video=1920x1080@60"} 328 } 329 330 # If we boot into a graphical environment where X is autoran 331 # and always crashes, it makes the media unusable. Allow the user 332 # to disable this. 333 submenu "Disable display-manager" --class quirk-disable-displaymanager { 334 ${grubMenuCfg} 335 ${buildMenuAdditionalParamsGrub2 config "systemd.mask=display-manager.service"} 336 } 337 338 # Some laptop and convertibles have the panel installed in an 339 # inconvenient way, rotated away from the keyboard. 340 # Those entries makes it easier to use the installer. 341 submenu "" {return} 342 submenu "Rotate framebuffer Clockwise" --class rotate-90cw { 343 ${grubMenuCfg} 344 ${buildMenuAdditionalParamsGrub2 config "fbcon=rotate:1"} 345 } 346 submenu "Rotate framebuffer Upside-Down" --class rotate-180 { 347 ${grubMenuCfg} 348 ${buildMenuAdditionalParamsGrub2 config "fbcon=rotate:2"} 349 } 350 submenu "Rotate framebuffer Counter-Clockwise" --class rotate-90ccw { 351 ${grubMenuCfg} 352 ${buildMenuAdditionalParamsGrub2 config "fbcon=rotate:3"} 353 } 354 355 # As a proof of concept, mainly. (Not sure it has accessibility merits.) 356 submenu "" {return} 357 submenu "Use black on white" --class accessibility-blakconwhite { 358 ${grubMenuCfg} 359 ${buildMenuAdditionalParamsGrub2 config "vt.default_red=0xFF,0xBC,0x4F,0xB4,0x56,0xBC,0x4F,0x00,0xA1,0xCF,0x84,0xCA,0x8D,0xB4,0x84,0x68 vt.default_grn=0xFF,0x55,0xBA,0xBA,0x4D,0x4D,0xB3,0x00,0xA0,0x8F,0xB3,0xCA,0x88,0x93,0xA4,0x68 vt.default_blu=0xFF,0x58,0x5F,0x58,0xC5,0xBD,0xC5,0x00,0xA8,0xBB,0xAB,0x97,0xBD,0xC7,0xC5,0x68"} 360 } 361 362 # Serial access is a must! 363 submenu "" {return} 364 submenu "Serial console=ttyS0,115200n8" --class serial { 365 ${grubMenuCfg} 366 ${buildMenuAdditionalParamsGrub2 config "console=ttyS0,115200n8"} 367 } 368 } 369 370 ${lib.optionalString (refindBinary != null) '' 371 # GRUB apparently cannot do "chainloader" operations on "CD". 372 if [ "\$root" != "cd0" ]; then 373 menuentry 'rEFInd' --class refind { 374 # \$root defaults to the drive the EFI is found on. 375 chainloader (\$root)/EFI/boot/${refindBinary} 376 } 377 fi 378 ''} 379 menuentry 'Firmware Setup' --class settings { 380 fwsetup 381 clear 382 echo "" 383 echo "If you see this message, your EFI system doesn't support this feature." 384 echo "" 385 } 386 menuentry 'Shutdown' --class shutdown { 387 halt 388 } 389 EOF 390 391 ${refind} 392 ''; 393 394 efiImg = pkgs.runCommand "efi-image_eltorito" { 395 nativeBuildInputs = [ pkgs.buildPackages.mtools pkgs.buildPackages.libfaketime pkgs.buildPackages.dosfstools ]; 396 strictDeps = true; 397 } 398 # Be careful about determinism: du --apparent-size, 399 # dates (cp -p, touch, mcopy -m, faketime for label), IDs (mkfs.vfat -i) 400 '' 401 mkdir ./contents && cd ./contents 402 cp -rp "${efiDir}"/EFI . 403 mkdir ./boot 404 cp -p "${config.boot.kernelPackages.kernel}/${config.system.boot.loader.kernelFile}" \ 405 "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}" ./boot/ 406 touch --date=@0 ./EFI ./boot 407 408 usage_size=$(du -sb --apparent-size . | tr -cd '[:digit:]') 409 # Make the image 110% as big as the files need to make up for FAT overhead 410 image_size=$(( ($usage_size * 110) / 100 )) 411 # Make the image fit blocks of 1M 412 block_size=$((1024*1024)) 413 image_size=$(( ($image_size / $block_size + 1) * $block_size )) 414 echo "Usage size: $usage_size" 415 echo "Image size: $image_size" 416 truncate --size=$image_size "$out" 417 faketime "2000-01-01 00:00:00" mkfs.vfat -i 12345678 -n EFIBOOT "$out" 418 mcopy -psvm -i "$out" ./EFI ./boot :: 419 # Verify the FAT partition. 420 fsck.vfat -vn "$out" 421 ''; # */ 422 423 # Name used by UEFI for architectures. 424 targetArch = 425 if pkgs.stdenv.isi686 || config.boot.loader.grub.forcei686 then 426 "ia32" 427 else if pkgs.stdenv.isx86_64 then 428 "x64" 429 else if pkgs.stdenv.isAarch32 then 430 "arm" 431 else if pkgs.stdenv.isAarch64 then 432 "aa64" 433 else 434 throw "Unsupported architecture"; 435 436 # Syslinux (and isolinux) only supports x86-based architectures. 437 canx86BiosBoot = pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64; 438 439in 440 441{ 442 options = { 443 444 isoImage.isoName = mkOption { 445 default = "${config.isoImage.isoBaseName}.iso"; 446 description = '' 447 Name of the generated ISO image file. 448 ''; 449 }; 450 451 isoImage.isoBaseName = mkOption { 452 default = "nixos"; 453 description = '' 454 Prefix of the name of the generated ISO image file. 455 ''; 456 }; 457 458 isoImage.compressImage = mkOption { 459 default = false; 460 description = '' 461 Whether the ISO image should be compressed using 462 <command>zstd</command>. 463 ''; 464 }; 465 466 isoImage.squashfsCompression = mkOption { 467 default = with pkgs.stdenv.targetPlatform; "xz -Xdict-size 100% " 468 + lib.optionalString (isx86_32 || isx86_64) "-Xbcj x86" 469 # Untested but should also reduce size for these platforms 470 + lib.optionalString (isAarch32 || isAarch64) "-Xbcj arm" 471 + lib.optionalString (isPowerPC) "-Xbcj powerpc" 472 + lib.optionalString (isSparc) "-Xbcj sparc"; 473 description = '' 474 Compression settings to use for the squashfs nix store. 475 ''; 476 example = "zstd -Xcompression-level 6"; 477 }; 478 479 isoImage.edition = mkOption { 480 default = ""; 481 description = '' 482 Specifies which edition string to use in the volume ID of the generated 483 ISO image. 484 ''; 485 }; 486 487 isoImage.volumeID = mkOption { 488 # nixos-$EDITION-$RELEASE-$ARCH 489 default = "nixos${optionalString (config.isoImage.edition != "") "-${config.isoImage.edition}"}-${config.system.nixos.release}-${pkgs.stdenv.hostPlatform.uname.processor}"; 490 description = '' 491 Specifies the label or volume ID of the generated ISO image. 492 Note that the label is used by stage 1 of the boot process to 493 mount the CD, so it should be reasonably distinctive. 494 ''; 495 }; 496 497 isoImage.contents = mkOption { 498 example = literalExample '' 499 [ { source = pkgs.memtest86 + "/memtest.bin"; 500 target = "boot/memtest.bin"; 501 } 502 ] 503 ''; 504 description = '' 505 This option lists files to be copied to fixed locations in the 506 generated ISO image. 507 ''; 508 }; 509 510 isoImage.storeContents = mkOption { 511 example = literalExample "[ pkgs.stdenv ]"; 512 description = '' 513 This option lists additional derivations to be included in the 514 Nix store in the generated ISO image. 515 ''; 516 }; 517 518 isoImage.includeSystemBuildDependencies = mkOption { 519 default = false; 520 description = '' 521 Set this option to include all the needed sources etc in the 522 image. It significantly increases image size. Use that when 523 you want to be able to keep all the sources needed to build your 524 system or when you are going to install the system on a computer 525 with slow or non-existent network connection. 526 ''; 527 }; 528 529 isoImage.makeEfiBootable = mkOption { 530 default = false; 531 description = '' 532 Whether the ISO image should be an efi-bootable volume. 533 ''; 534 }; 535 536 isoImage.makeUsbBootable = mkOption { 537 default = false; 538 description = '' 539 Whether the ISO image should be bootable from CD as well as USB. 540 ''; 541 }; 542 543 isoImage.efiSplashImage = mkOption { 544 default = pkgs.fetchurl { 545 url = "https://raw.githubusercontent.com/NixOS/nixos-artwork/a9e05d7deb38a8e005a2b52575a3f59a63a4dba0/bootloader/efi-background.png"; 546 sha256 = "18lfwmp8yq923322nlb9gxrh5qikj1wsk6g5qvdh31c4h5b1538x"; 547 }; 548 description = '' 549 The splash image to use in the EFI bootloader. 550 ''; 551 }; 552 553 isoImage.splashImage = mkOption { 554 default = pkgs.fetchurl { 555 url = "https://raw.githubusercontent.com/NixOS/nixos-artwork/a9e05d7deb38a8e005a2b52575a3f59a63a4dba0/bootloader/isolinux/bios-boot.png"; 556 sha256 = "1wp822zrhbg4fgfbwkr7cbkr4labx477209agzc0hr6k62fr6rxd"; 557 }; 558 description = '' 559 The splash image to use in the legacy-boot bootloader. 560 ''; 561 }; 562 563 isoImage.grubTheme = mkOption { 564 default = pkgs.nixos-grub2-theme; 565 type = types.nullOr (types.either types.path types.package); 566 description = '' 567 The grub2 theme used for UEFI boot. 568 ''; 569 }; 570 571 isoImage.appendToMenuLabel = mkOption { 572 default = " Installer"; 573 example = " Live System"; 574 description = '' 575 The string to append after the menu label for the NixOS system. 576 This will be directly appended (without whitespace) to the NixOS version 577 string, like for example if it is set to <literal>XXX</literal>: 578 579 <para><literal>NixOS 99.99-pre666XXX</literal></para> 580 ''; 581 }; 582 583 }; 584 585 config = { 586 assertions = [ 587 { 588 assertion = !(stringLength config.isoImage.volumeID > 32); 589 # https://wiki.osdev.org/ISO_9660#The_Primary_Volume_Descriptor 590 # Volume Identifier can only be 32 bytes 591 message = let 592 length = stringLength config.isoImage.volumeID; 593 howmany = toString length; 594 toomany = toString (length - 32); 595 in 596 "isoImage.volumeID ${config.isoImage.volumeID} is ${howmany} characters. That is ${toomany} characters longer than the limit of 32."; 597 } 598 ]; 599 600 boot.loader.grub.version = 2; 601 602 # Don't build the GRUB menu builder script, since we don't need it 603 # here and it causes a cyclic dependency. 604 boot.loader.grub.enable = false; 605 606 environment.systemPackages = [ grubPkgs.grub2 grubPkgs.grub2_efi ] 607 ++ optional canx86BiosBoot pkgs.syslinux 608 ; 609 610 # In stage 1 of the boot, mount the CD as the root FS by label so 611 # that we don't need to know its device. We pass the label of the 612 # root filesystem on the kernel command line, rather than in 613 # `fileSystems' below. This allows CD-to-USB converters such as 614 # UNetbootin to rewrite the kernel command line to pass the label or 615 # UUID of the USB stick. It would be nicer to write 616 # `root=/dev/disk/by-label/...' here, but UNetbootin doesn't 617 # recognise that. 618 boot.kernelParams = 619 [ "root=LABEL=${config.isoImage.volumeID}" 620 "boot.shell_on_fail" 621 ]; 622 623 fileSystems."/" = 624 { fsType = "tmpfs"; 625 options = [ "mode=0755" ]; 626 }; 627 628 # Note that /dev/root is a symlink to the actual root device 629 # specified on the kernel command line, created in the stage 1 630 # init script. 631 fileSystems."/iso" = 632 { device = "/dev/root"; 633 neededForBoot = true; 634 noCheck = true; 635 }; 636 637 # In stage 1, mount a tmpfs on top of /nix/store (the squashfs 638 # image) to make this a live CD. 639 fileSystems."/nix/.ro-store" = 640 { fsType = "squashfs"; 641 device = "/iso/nix-store.squashfs"; 642 options = [ "loop" ]; 643 neededForBoot = true; 644 }; 645 646 fileSystems."/nix/.rw-store" = 647 { fsType = "tmpfs"; 648 options = [ "mode=0755" ]; 649 neededForBoot = true; 650 }; 651 652 fileSystems."/nix/store" = 653 { fsType = "overlay"; 654 device = "overlay"; 655 options = [ 656 "lowerdir=/nix/.ro-store" 657 "upperdir=/nix/.rw-store/store" 658 "workdir=/nix/.rw-store/work" 659 ]; 660 }; 661 662 boot.initrd.availableKernelModules = [ "squashfs" "iso9660" "uas" "overlay" ]; 663 664 boot.initrd.kernelModules = [ "loop" "overlay" ]; 665 666 # Closures to be copied to the Nix store on the CD, namely the init 667 # script and the top-level system configuration directory. 668 isoImage.storeContents = 669 [ config.system.build.toplevel ] ++ 670 optional config.isoImage.includeSystemBuildDependencies 671 config.system.build.toplevel.drvPath; 672 673 # Create the squashfs image that contains the Nix store. 674 system.build.squashfsStore = pkgs.callPackage ../../../lib/make-squashfs.nix { 675 storeContents = config.isoImage.storeContents; 676 comp = config.isoImage.squashfsCompression; 677 }; 678 679 # Individual files to be included on the CD, outside of the Nix 680 # store on the CD. 681 isoImage.contents = 682 [ 683 { source = config.boot.kernelPackages.kernel + "/" + config.system.boot.loader.kernelFile; 684 target = "/boot/" + config.system.boot.loader.kernelFile; 685 } 686 { source = config.system.build.initialRamdisk + "/" + config.system.boot.loader.initrdFile; 687 target = "/boot/" + config.system.boot.loader.initrdFile; 688 } 689 { source = config.system.build.squashfsStore; 690 target = "/nix-store.squashfs"; 691 } 692 { source = config.isoImage.splashImage; 693 target = "/isolinux/background.png"; 694 } 695 { source = pkgs.writeText "version" config.system.nixos.label; 696 target = "/version.txt"; 697 } 698 ] ++ optionals canx86BiosBoot [ 699 { source = pkgs.substituteAll { 700 name = "isolinux.cfg"; 701 src = pkgs.writeText "isolinux.cfg-in" isolinuxCfg; 702 bootRoot = "/boot"; 703 }; 704 target = "/isolinux/isolinux.cfg"; 705 } 706 { source = "${pkgs.syslinux}/share/syslinux"; 707 target = "/isolinux"; 708 } 709 ] ++ optionals config.isoImage.makeEfiBootable [ 710 { source = efiImg; 711 target = "/boot/efi.img"; 712 } 713 { source = "${efiDir}/EFI"; 714 target = "/EFI"; 715 } 716 { source = (pkgs.writeTextDir "grub/loopback.cfg" "source /EFI/boot/grub.cfg") + "/grub"; 717 target = "/boot/grub"; 718 } 719 ] ++ optionals (config.boot.loader.grub.memtest86.enable && canx86BiosBoot) [ 720 { source = "${pkgs.memtest86plus}/memtest.bin"; 721 target = "/boot/memtest.bin"; 722 } 723 ] ++ optionals (config.isoImage.grubTheme != null) [ 724 { source = config.isoImage.grubTheme; 725 target = "/EFI/boot/grub-theme"; 726 } 727 ] ++ [ 728 { source = config.isoImage.efiSplashImage; 729 target = "/EFI/boot/efi-background.png"; 730 } 731 ]; 732 733 boot.loader.timeout = 10; 734 735 # Create the ISO image. 736 system.build.isoImage = pkgs.callPackage ../../../lib/make-iso9660-image.nix ({ 737 inherit (config.isoImage) isoName compressImage volumeID contents; 738 bootable = canx86BiosBoot; 739 bootImage = "/isolinux/isolinux.bin"; 740 syslinux = if canx86BiosBoot then pkgs.syslinux else null; 741 } // optionalAttrs (config.isoImage.makeUsbBootable && canx86BiosBoot) { 742 usbBootable = true; 743 isohybridMbrImage = "${pkgs.syslinux}/share/syslinux/isohdpfx.bin"; 744 } // optionalAttrs config.isoImage.makeEfiBootable { 745 efiBootable = true; 746 efiBootImage = "boot/efi.img"; 747 }); 748 749 boot.postBootCommands = 750 '' 751 # After booting, register the contents of the Nix store on the 752 # CD in the Nix database in the tmpfs. 753 ${config.nix.package.out}/bin/nix-store --load-db < /nix/store/nix-path-registration 754 755 # nixos-rebuild also requires a "system" profile and an 756 # /etc/NIXOS tag. 757 touch /etc/NIXOS 758 ${config.nix.package.out}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system 759 ''; 760 761 # Add vfat support to the initrd to enable people to copy the 762 # contents of the CD to a bootable USB stick. 763 boot.initrd.supportedFilesystems = [ "vfat" ]; 764 765 }; 766 767}