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