lol
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at master 1069 lines 34 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, 6 lib, 7 utils, 8 pkgs, 9 ... 10}: 11let 12 # Builds a single menu entry 13 menuBuilderGrub2 = 14 { 15 name, 16 class, 17 image, 18 params, 19 initrd, 20 }: 21 '' 22 menuentry '${name}' --class ${class} { 23 # Fallback to UEFI console for boot, efifb sometimes has difficulties. 24 terminal_output console 25 26 linux ${image} \''${isoboot} ${params} 27 initrd ${initrd} 28 } 29 ''; 30 31 # Builds all menu entries 32 buildMenuGrub2 = 33 { 34 cfg ? config, 35 params ? [ ], 36 }: 37 let 38 menuConfig = { 39 name = lib.concatStrings [ 40 cfg.isoImage.prependToMenuLabel 41 cfg.system.nixos.distroName 42 " " 43 cfg.system.nixos.label 44 cfg.isoImage.appendToMenuLabel 45 (lib.optionalString (cfg.isoImage.configurationName != null) (" " + cfg.isoImage.configurationName)) 46 ]; 47 params = "init=${cfg.system.build.toplevel}/init ${toString cfg.boot.kernelParams} ${toString params}"; 48 image = "/boot/${cfg.boot.kernelPackages.kernel + "/" + cfg.system.boot.loader.kernelFile}"; 49 initrd = "/boot/${cfg.system.build.initialRamdisk + "/" + cfg.system.boot.loader.initrdFile}"; 50 class = "installer"; 51 }; 52 in 53 '' 54 ${lib.optionalString cfg.isoImage.showConfiguration (menuBuilderGrub2 menuConfig)} 55 ${lib.concatStringsSep "\n" ( 56 lib.mapAttrsToList ( 57 specName: 58 { configuration, ... }: 59 buildMenuGrub2 { 60 cfg = configuration; 61 inherit params; 62 } 63 ) cfg.specialisation 64 )} 65 ''; 66 67 targetArch = if config.boot.loader.grub.forcei686 then "ia32" else pkgs.stdenv.hostPlatform.efiArch; 68 69 # Timeout in syslinux is in units of 1/10 of a second. 70 # null means max timeout (35996, just under 1h in 1/10 seconds) 71 # 0 means disable timeout 72 syslinuxTimeout = 73 if config.boot.loader.timeout == null then 35996 else config.boot.loader.timeout * 10; 74 75 # Timeout in grub is in seconds. 76 # null means max timeout (infinity) 77 # 0 means disable timeout 78 grubEfiTimeout = if config.boot.loader.timeout == null then -1 else config.boot.loader.timeout; 79 80 optionsSubMenus = [ 81 { 82 title = "Copy ISO Files to RAM"; 83 class = "copytoram"; 84 params = [ "copytoram" ]; 85 } 86 { 87 title = "No modesetting"; 88 class = "nomodeset"; 89 params = [ "nomodeset" ]; 90 } 91 { 92 title = "Debug Console Output"; 93 class = "debug"; 94 params = [ "debug" ]; 95 } 96 # If we boot into a graphical environment where X is autoran 97 # and always crashes, it makes the media unusable. Allow the user 98 # to disable this. 99 { 100 title = "Disable display-manager"; 101 class = "quirk-disable-displaymanager"; 102 params = [ 103 "systemd.mask=display-manager.service" 104 "plymouth.enable=0" 105 ]; 106 } 107 # Some laptop and convertibles have the panel installed in an 108 # inconvenient way, rotated away from the keyboard. 109 # Those entries makes it easier to use the installer. 110 { 111 title = "Rotate framebuffer Clockwise"; 112 class = "rotate-90cw"; 113 params = [ "fbcon=rotate:1" ]; 114 } 115 { 116 title = "Rotate framebuffer Upside-Down"; 117 class = "rotate-180"; 118 params = [ "fbcon=rotate:2" ]; 119 } 120 { 121 title = "Rotate framebuffer Counter-Clockwise"; 122 class = "rotate-90ccw"; 123 params = [ "fbcon=rotate:3" ]; 124 } 125 # Serial access is a must! 126 { 127 title = "Serial console=ttyS0,115200n8"; 128 class = "serial"; 129 params = [ "console=ttyS0,115200n8" ]; 130 } 131 ]; 132 133 # The configuration file for syslinux. 134 135 # Notes on syslinux configuration and UNetbootin compatibility: 136 # * Do not use '/syslinux/syslinux.cfg' as the path for this 137 # configuration. UNetbootin will not parse the file and use it as-is. 138 # This results in a broken configuration if the partition label does 139 # not match the specified config.isoImage.volumeID. For this reason 140 # we're using '/isolinux/isolinux.cfg'. 141 # * Use APPEND instead of adding command-line arguments directly after 142 # the LINUX entries. 143 # * COM32 entries (chainload, reboot, poweroff) are not recognized. They 144 # result in incorrect boot entries. 145 146 menuBuilderIsolinux = 147 { 148 cfg ? config, 149 label, 150 params ? [ ], 151 }: 152 '' 153 ${lib.optionalString cfg.isoImage.showConfiguration '' 154 LABEL ${label} 155 MENU LABEL ${cfg.isoImage.prependToMenuLabel}${cfg.system.nixos.distroName} ${cfg.system.nixos.label}${cfg.isoImage.appendToMenuLabel}${ 156 lib.optionalString (cfg.isoImage.configurationName != null) (" " + cfg.isoImage.configurationName) 157 } 158 LINUX /boot/${cfg.boot.kernelPackages.kernel + "/" + cfg.system.boot.loader.kernelFile} 159 APPEND init=${cfg.system.build.toplevel}/init ${toString cfg.boot.kernelParams} ${toString params} 160 INITRD /boot/${cfg.system.build.initialRamdisk + "/" + cfg.system.boot.loader.initrdFile} 161 ''} 162 163 ${lib.concatStringsSep "\n\n" ( 164 lib.mapAttrsToList ( 165 name: specCfg: 166 menuBuilderIsolinux { 167 cfg = specCfg.configuration; 168 label = "${label}-${name}"; 169 inherit params; 170 } 171 ) cfg.specialisation 172 )} 173 ''; 174 175 baseIsolinuxCfg = '' 176 SERIAL 0 115200 177 TIMEOUT ${builtins.toString syslinuxTimeout} 178 UI vesamenu.c32 179 MENU BACKGROUND /isolinux/background.png 180 181 ${config.isoImage.syslinuxTheme} 182 183 DEFAULT boot 184 185 ${menuBuilderIsolinux { label = "boot"; }} 186 187 MENU BEGIN Options 188 189 ${lib.concatMapStringsSep "\n" ( 190 { 191 title, 192 class, 193 params, 194 }: 195 '' 196 MENU BEGIN ${title} 197 ${menuBuilderIsolinux { 198 label = "boot-${class}"; 199 inherit params; 200 }} 201 MENU END 202 '' 203 ) optionsSubMenus} 204 205 MENU END 206 ''; 207 208 isolinuxMemtest86Entry = '' 209 LABEL memtest 210 MENU LABEL Memtest86+ 211 LINUX /boot/memtest.bin 212 APPEND ${toString config.boot.loader.grub.memtest86.params} 213 ''; 214 215 isolinuxCfg = lib.concatStringsSep "\n" ( 216 [ baseIsolinuxCfg ] ++ lib.optional config.boot.loader.grub.memtest86.enable isolinuxMemtest86Entry 217 ); 218 219 refindBinary = 220 if targetArch == "x64" || targetArch == "aa64" then "refind_${targetArch}.efi" else null; 221 222 # Setup instructions for rEFInd. 223 refind = 224 if refindBinary != null then 225 '' 226 # Adds rEFInd to the ISO. 227 cp -v ${pkgs.refind}/share/refind/${refindBinary} $out/EFI/BOOT/ 228 '' 229 else 230 "# No refind for ${targetArch}"; 231 232 grubPkgs = if config.boot.loader.grub.forcei686 then pkgs.pkgsi686Linux else pkgs; 233 234 grubMenuCfg = '' 235 set textmode=${lib.boolToString (config.isoImage.forceTextMode)} 236 237 # 238 # Menu configuration 239 # 240 241 # Search using a "marker file" 242 search --set=root --file /EFI/nixos-installer-image 243 244 insmod gfxterm 245 insmod png 246 set gfxpayload=keep 247 set gfxmode=${ 248 lib.concatStringsSep "," [ 249 # GRUB will use the first valid mode listed here. 250 # `auto` will sometimes choose the smallest valid mode it detects. 251 # So instead we'll list a lot of possibly valid modes :/ 252 #"3840x2160" 253 #"2560x1440" 254 "1920x1200" 255 "1920x1080" 256 "1366x768" 257 "1280x800" 258 "1280x720" 259 "1200x1920" 260 "1024x768" 261 "800x1280" 262 "800x600" 263 "auto" 264 ] 265 } 266 267 if [ "\$textmode" == "false" ]; then 268 terminal_output gfxterm 269 terminal_input console 270 else 271 terminal_output console 272 terminal_input console 273 # Sets colors for console term. 274 set menu_color_normal=cyan/blue 275 set menu_color_highlight=white/blue 276 fi 277 278 ${ 279 # When there is a theme configured, use it, otherwise use the background image. 280 if config.isoImage.grubTheme != null then 281 '' 282 # Sets theme. 283 set theme=(\$root)/EFI/BOOT/grub-theme/theme.txt 284 # Load theme fonts 285 $(find ${config.isoImage.grubTheme} -iname '*.pf2' -printf "loadfont (\$root)/EFI/BOOT/grub-theme/%P\n") 286 '' 287 else 288 '' 289 if background_image (\$root)/EFI/BOOT/efi-background.png; then 290 # Black background means transparent background when there 291 # is a background image set... This seems undocumented :( 292 set color_normal=black/black 293 set color_highlight=white/blue 294 else 295 # Falls back again to proper colors. 296 set menu_color_normal=cyan/blue 297 set menu_color_highlight=white/blue 298 fi 299 '' 300 } 301 302 hiddenentry 'Text mode' --hotkey 't' { 303 loadfont (\$root)/EFI/BOOT/unicode.pf2 304 set textmode=true 305 terminal_output console 306 } 307 308 ${lib.optionalString (config.isoImage.grubTheme != null) '' 309 hiddenentry 'GUI mode' --hotkey 'g' { 310 $(find ${config.isoImage.grubTheme} -iname '*.pf2' -printf "loadfont (\$root)/EFI/BOOT/grub-theme/%P\n") 311 set textmode=false 312 terminal_output gfxterm 313 } 314 ''} 315 ''; 316 317 # The EFI boot image. 318 # Notes about grub: 319 # * Yes, the grubMenuCfg has to be repeated in all submenus. Otherwise you 320 # will get white-on-black console-like text on sub-menus. *sigh* 321 efiDir = 322 pkgs.runCommand "efi-directory" 323 { 324 nativeBuildInputs = [ pkgs.buildPackages.grub2_efi ]; 325 strictDeps = true; 326 } 327 '' 328 mkdir -p $out/EFI/BOOT 329 330 # Add a marker so GRUB can find the filesystem. 331 touch $out/EFI/nixos-installer-image 332 333 # ALWAYS required modules. 334 MODULES=( 335 # Basic modules for filesystems and partition schemes 336 "fat" 337 "iso9660" 338 "part_gpt" 339 "part_msdos" 340 341 # Basic stuff 342 "normal" 343 "boot" 344 "linux" 345 "configfile" 346 "loopback" 347 "chain" 348 "halt" 349 350 # Allows rebooting into firmware setup interface 351 "efifwsetup" 352 353 # EFI Graphics Output Protocol 354 "efi_gop" 355 356 # User commands 357 "ls" 358 359 # System commands 360 "search" 361 "search_label" 362 "search_fs_uuid" 363 "search_fs_file" 364 "echo" 365 366 # We're not using it anymore, but we'll leave it in so it can be used 367 # by user, with the console using "C" 368 "serial" 369 370 # Graphical mode stuff 371 "gfxmenu" 372 "gfxterm" 373 "gfxterm_background" 374 "gfxterm_menu" 375 "test" 376 "loadenv" 377 "all_video" 378 "videoinfo" 379 380 # File types for graphical mode 381 "png" 382 ) 383 384 echo "Building GRUB with modules:" 385 for mod in ''${MODULES[@]}; do 386 echo " - $mod" 387 done 388 389 # Modules that may or may not be available per-platform. 390 echo "Adding additional modules:" 391 for mod in efi_uga; do 392 if [ -f ${grubPkgs.grub2_efi}/lib/grub/${grubPkgs.grub2_efi.grubTarget}/$mod.mod ]; then 393 echo " - $mod" 394 MODULES+=("$mod") 395 fi 396 done 397 398 # Make our own efi program, we can't rely on "grub-install" since it seems to 399 # probe for devices, even with --skip-fs-probe. 400 grub-mkimage \ 401 --directory=${grubPkgs.grub2_efi}/lib/grub/${grubPkgs.grub2_efi.grubTarget} \ 402 -o $out/EFI/BOOT/BOOT${lib.toUpper targetArch}.EFI \ 403 -p /EFI/BOOT \ 404 -O ${grubPkgs.grub2_efi.grubTarget} \ 405 ''${MODULES[@]} 406 cp ${grubPkgs.grub2_efi}/share/grub/unicode.pf2 $out/EFI/BOOT/ 407 408 cat <<EOF > $out/EFI/BOOT/grub.cfg 409 410 set timeout=${toString grubEfiTimeout} 411 412 clear 413 # This message will only be viewable on the default (UEFI) console. 414 echo "" 415 echo "Loading graphical boot menu..." 416 echo "" 417 echo "Press 't' to use the text boot menu on this console..." 418 echo "" 419 420 ${grubMenuCfg} 421 422 # If the parameter iso_path is set, append the findiso parameter to the kernel 423 # line. We need this to allow the nixos iso to be booted from grub directly. 424 if [ \''${iso_path} ] ; then 425 set isoboot="findiso=\''${iso_path}" 426 fi 427 428 # 429 # Menu entries 430 # 431 432 ${buildMenuGrub2 { }} 433 submenu "Options" --class submenu --class hidpi { 434 ${grubMenuCfg} 435 436 ${lib.concatMapStringsSep "\n" ( 437 { 438 title, 439 class, 440 params, 441 }: 442 '' 443 submenu "${title}" --class ${class} { 444 ${grubMenuCfg} 445 ${buildMenuGrub2 { inherit params; }} 446 } 447 '' 448 ) optionsSubMenus} 449 } 450 451 ${lib.optionalString (refindBinary != null) '' 452 # GRUB apparently cannot do "chainloader" operations on "CD". 453 if [ "\$root" != "cd0" ]; then 454 menuentry 'rEFInd' --class refind { 455 # Force root to be the FAT partition 456 # Otherwise it breaks rEFInd's boot 457 search --set=root --no-floppy --fs-uuid 1234-5678 458 chainloader (\$root)/EFI/BOOT/${refindBinary} 459 } 460 fi 461 ''} 462 menuentry 'Firmware Setup' --class settings { 463 fwsetup 464 clear 465 echo "" 466 echo "If you see this message, your EFI system doesn't support this feature." 467 echo "" 468 } 469 menuentry 'Shutdown' --class shutdown { 470 halt 471 } 472 EOF 473 474 grub-script-check $out/EFI/BOOT/grub.cfg 475 476 ${refind} 477 ''; 478 479 efiImg = 480 pkgs.runCommand "efi-image_eltorito" 481 { 482 nativeBuildInputs = [ 483 pkgs.buildPackages.mtools 484 pkgs.buildPackages.libfaketime 485 pkgs.buildPackages.dosfstools 486 ]; 487 strictDeps = true; 488 } 489 # Be careful about determinism: du --apparent-size, 490 # dates (cp -p, touch, mcopy -m, faketime for label), IDs (mkfs.vfat -i) 491 '' 492 mkdir ./contents && cd ./contents 493 mkdir -p ./EFI/BOOT 494 cp -rp "${efiDir}"/EFI/BOOT/{grub.cfg,*.EFI,*.efi} ./EFI/BOOT 495 496 # Rewrite dates for everything in the FS 497 find . -exec touch --date=2000-01-01 {} + 498 499 # Round up to the nearest multiple of 1MB, for more deterministic du output 500 usage_size=$(( $(du -s --block-size=1M --apparent-size . | tr -cd '[:digit:]') * 1024 * 1024 )) 501 # Make the image 110% as big as the files need to make up for FAT overhead 502 image_size=$(( ($usage_size * 110) / 100 )) 503 # Make the image fit blocks of 1M 504 block_size=$((1024*1024)) 505 image_size=$(( ($image_size / $block_size + 1) * $block_size )) 506 echo "Usage size: $usage_size" 507 echo "Image size: $image_size" 508 truncate --size=$image_size "$out" 509 mkfs.vfat --invariant -i 12345678 -n EFIBOOT "$out" 510 511 # Force a fixed order in mcopy for better determinism, and avoid file globbing 512 for d in $(find EFI -type d | sort); do 513 faketime "2000-01-01 00:00:00" mmd -i "$out" "::/$d" 514 done 515 516 for f in $(find EFI -type f | sort); do 517 mcopy -pvm -i "$out" "$f" "::/$f" 518 done 519 520 # Verify the FAT partition. 521 fsck.vfat -vn "$out" 522 ''; # */ 523 524in 525 526{ 527 imports = [ 528 (lib.mkRenamedOptionModuleWith { 529 sinceRelease = 2505; 530 from = [ 531 "isoImage" 532 "isoBaseName" 533 ]; 534 to = [ 535 "image" 536 "baseName" 537 ]; 538 }) 539 (lib.mkRenamedOptionModuleWith { 540 sinceRelease = 2505; 541 from = [ 542 "isoImage" 543 "isoName" 544 ]; 545 to = [ 546 "image" 547 "fileName" 548 ]; 549 }) 550 ../../image/file-options.nix 551 ]; 552 553 options = { 554 555 isoImage.compressImage = lib.mkOption { 556 default = false; 557 type = lib.types.bool; 558 description = '' 559 Whether the ISO image should be compressed using 560 {command}`zstd`. 561 ''; 562 }; 563 564 isoImage.squashfsCompression = lib.mkOption { 565 default = "zstd -Xcompression-level 19"; 566 type = lib.types.nullOr lib.types.str; 567 description = '' 568 Compression settings to use for the squashfs nix store. 569 `null` disables compression. 570 ''; 571 example = "zstd -Xcompression-level 6"; 572 }; 573 574 isoImage.edition = lib.mkOption { 575 default = ""; 576 type = lib.types.str; 577 description = '' 578 Specifies which edition string to use in the volume ID of the generated 579 ISO image. 580 ''; 581 }; 582 583 isoImage.volumeID = lib.mkOption { 584 # nixos-$EDITION-$RELEASE-$ARCH 585 default = "nixos${ 586 lib.optionalString (config.isoImage.edition != "") "-${config.isoImage.edition}" 587 }-${config.system.nixos.release}-${pkgs.stdenv.hostPlatform.uname.processor}"; 588 type = lib.types.str; 589 description = '' 590 Specifies the label or volume ID of the generated ISO image. 591 Note that the label is used by stage 1 of the boot process to 592 mount the CD, so it should be reasonably distinctive. 593 ''; 594 }; 595 596 isoImage.contents = lib.mkOption { 597 example = lib.literalExpression '' 598 [ { source = pkgs.memtest86 + "/memtest.bin"; 599 target = "boot/memtest.bin"; 600 } 601 ] 602 ''; 603 description = '' 604 This option lists files to be copied to fixed locations in the 605 generated ISO image. 606 ''; 607 }; 608 609 isoImage.storeContents = lib.mkOption { 610 example = lib.literalExpression "[ pkgs.stdenv ]"; 611 description = '' 612 This option lists additional derivations to be included in the 613 Nix store in the generated ISO image. 614 ''; 615 }; 616 617 isoImage.includeSystemBuildDependencies = lib.mkOption { 618 default = false; 619 type = lib.types.bool; 620 description = '' 621 Set this option to include all the needed sources etc in the 622 image. It significantly increases image size. Use that when 623 you want to be able to keep all the sources needed to build your 624 system or when you are going to install the system on a computer 625 with slow or non-existent network connection. 626 ''; 627 }; 628 629 isoImage.makeBiosBootable = lib.mkOption { 630 # Before this option was introduced, images were BIOS-bootable if the 631 # hostPlatform was x86-based. This option is enabled by default for 632 # backwards compatibility. 633 # 634 # Also note that syslinux package currently cannot be cross-compiled from 635 # non-x86 platforms, so the default is false on non-x86 build platforms. 636 default = pkgs.stdenv.buildPlatform.isx86 && pkgs.stdenv.hostPlatform.isx86; 637 defaultText = lib.literalMD '' 638 `true` if both build and host platforms are x86-based architectures, 639 e.g. i686 and x86_64. 640 ''; 641 type = lib.types.bool; 642 description = '' 643 Whether the ISO image should be a BIOS-bootable disk. 644 ''; 645 }; 646 647 isoImage.makeEfiBootable = lib.mkOption { 648 default = false; 649 type = lib.types.bool; 650 description = '' 651 Whether the ISO image should be an EFI-bootable volume. 652 ''; 653 }; 654 655 isoImage.makeUsbBootable = lib.mkOption { 656 default = false; 657 type = lib.types.bool; 658 description = '' 659 Whether the ISO image should be bootable from CD as well as USB. 660 ''; 661 }; 662 663 isoImage.efiSplashImage = lib.mkOption { 664 default = pkgs.fetchurl { 665 url = "https://raw.githubusercontent.com/NixOS/nixos-artwork/a9e05d7deb38a8e005a2b52575a3f59a63a4dba0/bootloader/efi-background.png"; 666 sha256 = "18lfwmp8yq923322nlb9gxrh5qikj1wsk6g5qvdh31c4h5b1538x"; 667 }; 668 description = '' 669 The splash image to use in the EFI bootloader. 670 ''; 671 }; 672 673 isoImage.splashImage = lib.mkOption { 674 default = pkgs.fetchurl { 675 url = "https://raw.githubusercontent.com/NixOS/nixos-artwork/a9e05d7deb38a8e005a2b52575a3f59a63a4dba0/bootloader/isolinux/bios-boot.png"; 676 sha256 = "1wp822zrhbg4fgfbwkr7cbkr4labx477209agzc0hr6k62fr6rxd"; 677 }; 678 description = '' 679 The splash image to use in the legacy-boot bootloader. 680 ''; 681 }; 682 683 isoImage.grubTheme = lib.mkOption { 684 default = pkgs.nixos-grub2-theme; 685 type = lib.types.nullOr (lib.types.either lib.types.path lib.types.package); 686 description = '' 687 The grub2 theme used for UEFI boot. 688 ''; 689 }; 690 691 isoImage.syslinuxTheme = lib.mkOption { 692 default = '' 693 MENU TITLE ${config.system.nixos.distroName} 694 MENU RESOLUTION 800 600 695 MENU CLEAR 696 MENU ROWS 6 697 MENU CMDLINEROW -4 698 MENU TIMEOUTROW -3 699 MENU TABMSGROW -2 700 MENU HELPMSGROW -1 701 MENU HELPMSGENDROW -1 702 MENU MARGIN 0 703 704 # FG:AARRGGBB BG:AARRGGBB shadow 705 MENU COLOR BORDER 30;44 #00000000 #00000000 none 706 MENU COLOR SCREEN 37;40 #FF000000 #00E2E8FF none 707 MENU COLOR TABMSG 31;40 #80000000 #00000000 none 708 MENU COLOR TIMEOUT 1;37;40 #FF000000 #00000000 none 709 MENU COLOR TIMEOUT_MSG 37;40 #FF000000 #00000000 none 710 MENU COLOR CMDMARK 1;36;40 #FF000000 #00000000 none 711 MENU COLOR CMDLINE 37;40 #FF000000 #00000000 none 712 MENU COLOR TITLE 1;36;44 #00000000 #00000000 none 713 MENU COLOR UNSEL 37;44 #FF000000 #00000000 none 714 MENU COLOR SEL 7;37;40 #FFFFFFFF #FF5277C3 std 715 ''; 716 type = lib.types.str; 717 description = '' 718 The syslinux theme used for BIOS boot. 719 ''; 720 }; 721 722 isoImage.prependToMenuLabel = lib.mkOption { 723 default = ""; 724 type = lib.types.str; 725 example = "Install "; 726 description = '' 727 The string to prepend before the menu label for the NixOS system. 728 This will be directly prepended (without whitespace) to the NixOS version 729 string, like for example if it is set to `XXX`: 730 731 `XXXNixOS 99.99-pre666` 732 ''; 733 }; 734 735 isoImage.appendToMenuLabel = lib.mkOption { 736 default = " Installer"; 737 type = lib.types.str; 738 example = " Live System"; 739 description = '' 740 The string to append after the menu label for the NixOS system. 741 This will be directly appended (without whitespace) to the NixOS version 742 string, like for example if it is set to `XXX`: 743 744 `NixOS 99.99-pre666XXX` 745 ''; 746 }; 747 748 isoImage.configurationName = lib.mkOption { 749 default = null; 750 type = lib.types.nullOr lib.types.str; 751 example = "GNOME"; 752 description = '' 753 The name of the configuration in the title of the boot entry. 754 ''; 755 }; 756 757 isoImage.showConfiguration = lib.mkEnableOption "show this configuration in the menu" // { 758 default = true; 759 }; 760 761 isoImage.forceTextMode = lib.mkOption { 762 default = false; 763 type = lib.types.bool; 764 example = true; 765 description = '' 766 Whether to use text mode instead of graphical grub. 767 A value of `true` means graphical mode is not tried to be used. 768 769 This is useful for validating that graphics mode usage is not at the root cause of a problem with the iso image. 770 771 If text mode is required off-handedly (e.g. for serial use) you can use the `T` key, after being prompted, to use text mode for the current boot. 772 ''; 773 }; 774 775 }; 776 777 # store them in lib so we can mkImageMediaOverride the 778 # entire file system layout in installation media (only) 779 config.lib.isoFileSystems = { 780 "/" = lib.mkImageMediaOverride { 781 fsType = "tmpfs"; 782 options = [ "mode=0755" ]; 783 }; 784 785 # Note that /dev/root is a symlink to the actual root device 786 # specified on the kernel command line, created in the stage 1 787 # init script. 788 "/iso" = lib.mkImageMediaOverride { 789 device = 790 if config.boot.initrd.systemd.enable then 791 "/dev/disk/by-label/${config.isoImage.volumeID}" 792 else 793 "/dev/root"; 794 neededForBoot = true; 795 noCheck = true; 796 }; 797 798 # In stage 1, mount a tmpfs on top of /nix/store (the squashfs 799 # image) to make this a live CD. 800 "/nix/.ro-store" = lib.mkImageMediaOverride { 801 fsType = "squashfs"; 802 device = "${lib.optionalString config.boot.initrd.systemd.enable "/sysroot"}/iso/nix-store.squashfs"; 803 options = [ 804 "loop" 805 ] 806 ++ lib.optional (config.boot.kernelPackages.kernel.kernelAtLeast "6.2") "threads=multi"; 807 neededForBoot = true; 808 }; 809 810 "/nix/.rw-store" = lib.mkImageMediaOverride { 811 fsType = "tmpfs"; 812 options = [ "mode=0755" ]; 813 neededForBoot = true; 814 }; 815 816 "/nix/store" = lib.mkImageMediaOverride { 817 overlay = { 818 lowerdir = [ "/nix/.ro-store" ]; 819 upperdir = "/nix/.rw-store/store"; 820 workdir = "/nix/.rw-store/work"; 821 }; 822 }; 823 }; 824 825 config = { 826 assertions = [ 827 { 828 # Syslinux (and isolinux) only supports x86-based architectures. 829 assertion = config.isoImage.makeBiosBootable -> pkgs.stdenv.hostPlatform.isx86; 830 message = "BIOS boot is only supported on x86-based architectures."; 831 } 832 { 833 assertion = !(lib.stringLength config.isoImage.volumeID > 32); 834 # https://wiki.osdev.org/ISO_9660#The_Primary_Volume_Descriptor 835 # Volume Identifier can only be 32 bytes 836 message = 837 let 838 length = lib.stringLength config.isoImage.volumeID; 839 howmany = toString length; 840 toomany = toString (length - 32); 841 in 842 "isoImage.volumeID ${config.isoImage.volumeID} is ${howmany} characters. That is ${toomany} characters longer than the limit of 32."; 843 } 844 ( 845 let 846 badSpecs = lib.filterAttrs ( 847 specName: specCfg: specCfg.configuration.isoImage.volumeID != config.isoImage.volumeID 848 ) config.specialisation; 849 in 850 { 851 assertion = badSpecs == { }; 852 message = '' 853 All specialisations must use the same 'isoImage.volumeID'. 854 855 Specialisations with different volumeIDs: 856 857 ${lib.concatMapStringsSep "\n" (specName: '' 858 - ${specName} 859 '') (builtins.attrNames badSpecs)} 860 ''; 861 } 862 ) 863 ]; 864 865 # Don't build the GRUB menu builder script, since we don't need it 866 # here and it causes a cyclic dependency. 867 boot.loader.grub.enable = lib.mkImageMediaOverride false; 868 869 environment.systemPackages = [ 870 grubPkgs.grub2 871 ] 872 ++ lib.optional (config.isoImage.makeBiosBootable) pkgs.syslinux; 873 system.extraDependencies = [ grubPkgs.grub2_efi ]; 874 875 # In stage 1 of the boot, mount the CD as the root FS by label so 876 # that we don't need to know its device. We pass the label of the 877 # root filesystem on the kernel command line, rather than in 878 # `fileSystems' below. This allows CD-to-USB converters such as 879 # UNetbootin to rewrite the kernel command line to pass the label or 880 # UUID of the USB stick. It would be nicer to write 881 # `root=/dev/disk/by-label/...' here, but UNetbootin doesn't 882 # recognise that. 883 boot.kernelParams = lib.optionals (!config.boot.initrd.systemd.enable) [ 884 "boot.shell_on_fail" 885 "root=LABEL=${config.isoImage.volumeID}" 886 ]; 887 888 fileSystems = config.lib.isoFileSystems; 889 890 boot.initrd.availableKernelModules = [ 891 "squashfs" 892 "iso9660" 893 "uas" 894 "overlay" 895 ]; 896 897 boot.initrd.kernelModules = [ 898 "loop" 899 "overlay" 900 ]; 901 902 boot.initrd.systemd = lib.mkIf config.boot.initrd.systemd.enable { 903 emergencyAccess = true; 904 905 # Most of util-linux is not included by default. 906 initrdBin = [ config.boot.initrd.systemd.package.util-linux ]; 907 services.copytoram = { 908 description = "Copy ISO contents to RAM"; 909 requiredBy = [ "initrd.target" ]; 910 before = [ 911 "${utils.escapeSystemdPath "/sysroot/nix/.ro-store"}.mount" 912 "initrd-switch-root.target" 913 ]; 914 unitConfig = { 915 RequiresMountsFor = "/sysroot/iso"; 916 ConditionKernelCommandLine = "copytoram"; 917 }; 918 serviceConfig = { 919 Type = "oneshot"; 920 RemainAfterExit = true; 921 }; 922 path = [ 923 pkgs.coreutils 924 config.boot.initrd.systemd.package.util-linux 925 ]; 926 script = '' 927 device=$(findmnt -n -o SOURCE --target /sysroot/iso) 928 fsSize=$(blockdev --getsize64 "$device" || stat -Lc '%s' "$device") 929 mkdir -p /tmp-iso 930 mount --bind --make-private /sysroot/iso /tmp-iso 931 umount /sysroot/iso 932 mount -t tmpfs -o size="$fsSize" tmpfs /sysroot/iso 933 cp -r /tmp-iso/* /sysroot/iso/ 934 umount /tmp-iso 935 rm -r /tmp-iso 936 ''; 937 }; 938 }; 939 940 # Closures to be copied to the Nix store on the CD, namely the init 941 # script and the top-level system configuration directory. 942 isoImage.storeContents = [ 943 config.system.build.toplevel 944 ] 945 ++ lib.optional config.isoImage.includeSystemBuildDependencies config.system.build.toplevel.drvPath; 946 947 # Individual files to be included on the CD, outside of the Nix 948 # store on the CD. 949 isoImage.contents = 950 let 951 cfgFiles = 952 cfg: 953 lib.optionals cfg.isoImage.showConfiguration ([ 954 { 955 source = cfg.boot.kernelPackages.kernel + "/" + cfg.system.boot.loader.kernelFile; 956 target = "/boot/" + cfg.boot.kernelPackages.kernel + "/" + cfg.system.boot.loader.kernelFile; 957 } 958 { 959 source = cfg.system.build.initialRamdisk + "/" + cfg.system.boot.loader.initrdFile; 960 target = "/boot/" + cfg.system.build.initialRamdisk + "/" + cfg.system.boot.loader.initrdFile; 961 } 962 ]) 963 ++ lib.concatLists ( 964 lib.mapAttrsToList (_: { configuration, ... }: cfgFiles configuration) cfg.specialisation 965 ); 966 in 967 [ 968 { 969 source = pkgs.writeText "version" config.system.nixos.label; 970 target = "/version.txt"; 971 } 972 ] 973 ++ lib.unique (cfgFiles config) 974 ++ lib.optionals (config.isoImage.makeBiosBootable) [ 975 { 976 source = config.isoImage.splashImage; 977 target = "/isolinux/background.png"; 978 } 979 { 980 source = pkgs.writeText "isolinux.cfg" isolinuxCfg; 981 target = "/isolinux/isolinux.cfg"; 982 } 983 { 984 source = "${pkgs.syslinux}/share/syslinux"; 985 target = "/isolinux"; 986 } 987 ] 988 ++ lib.optionals config.isoImage.makeEfiBootable [ 989 { 990 source = efiImg; 991 target = "/boot/efi.img"; 992 } 993 { 994 source = "${efiDir}/EFI"; 995 target = "/EFI"; 996 } 997 { 998 source = config.isoImage.efiSplashImage; 999 target = "/EFI/BOOT/efi-background.png"; 1000 } 1001 ] 1002 ++ lib.optionals (config.isoImage.makeEfiBootable && !config.boot.initrd.systemd.enable) [ 1003 # http://www.supergrubdisk.org/wiki/Loopback.cfg 1004 # This feature will be removed, and thus is not supported by systemd initrd 1005 { 1006 source = (pkgs.writeTextDir "grub/loopback.cfg" "source /EFI/BOOT/grub.cfg") + "/grub"; 1007 target = "/boot/grub"; 1008 } 1009 ] 1010 ++ lib.optionals (config.boot.loader.grub.memtest86.enable && config.isoImage.makeBiosBootable) [ 1011 { 1012 source = "${pkgs.memtest86plus}/memtest.bin"; 1013 target = "/boot/memtest.bin"; 1014 } 1015 ] 1016 ++ lib.optionals (config.isoImage.grubTheme != null) [ 1017 { 1018 source = config.isoImage.grubTheme; 1019 target = "/EFI/BOOT/grub-theme"; 1020 } 1021 ]; 1022 1023 boot.loader.timeout = 10; 1024 1025 # Create the ISO image. 1026 image.extension = if config.isoImage.compressImage then "iso.zst" else "iso"; 1027 image.filePath = "iso/${config.image.fileName}"; 1028 image.baseName = "nixos${ 1029 lib.optionalString (config.isoImage.edition != "") "-${config.isoImage.edition}" 1030 }-${config.system.nixos.label}-${pkgs.stdenv.hostPlatform.system}"; 1031 system.build.image = config.system.build.isoImage; 1032 system.build.isoImage = pkgs.callPackage ../../../lib/make-iso9660-image.nix ( 1033 { 1034 inherit (config.isoImage) compressImage volumeID contents; 1035 isoName = "${config.image.baseName}.iso"; 1036 bootable = config.isoImage.makeBiosBootable; 1037 bootImage = "/isolinux/isolinux.bin"; 1038 syslinux = if config.isoImage.makeBiosBootable then pkgs.syslinux else null; 1039 squashfsContents = config.isoImage.storeContents; 1040 squashfsCompression = config.isoImage.squashfsCompression; 1041 } 1042 // lib.optionalAttrs (config.isoImage.makeUsbBootable && config.isoImage.makeBiosBootable) { 1043 usbBootable = true; 1044 isohybridMbrImage = "${pkgs.syslinux}/share/syslinux/isohdpfx.bin"; 1045 } 1046 // lib.optionalAttrs config.isoImage.makeEfiBootable { 1047 efiBootable = true; 1048 efiBootImage = "boot/efi.img"; 1049 } 1050 ); 1051 1052 boot.postBootCommands = '' 1053 # After booting, register the contents of the Nix store on the 1054 # CD in the Nix database in the tmpfs. 1055 ${config.nix.package.out}/bin/nix-store --load-db < /nix/store/nix-path-registration 1056 1057 # nixos-rebuild also requires a "system" profile and an 1058 # /etc/NIXOS tag. 1059 touch /etc/NIXOS 1060 ${config.nix.package.out}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system 1061 ''; 1062 1063 # Add vfat support to the initrd to enable people to copy the 1064 # contents of the CD to a bootable USB stick. 1065 boot.initrd.supportedFilesystems = [ "vfat" ]; 1066 1067 }; 1068 1069}