664 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			664 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
# This module creates a bootable ISO image containing the given NixOS
 | 
						||
# configuration.  The derivation for the ISO image will be placed in
 | 
						||
# config.system.build.isoImage.
 | 
						||
 | 
						||
{ config, lib, pkgs, ... }:
 | 
						||
 | 
						||
with lib;
 | 
						||
 | 
						||
let
 | 
						||
  /**
 | 
						||
   * Given a list of `options`, concats the result of mapping each options
 | 
						||
   * to a menuentry for use in grub.
 | 
						||
   *
 | 
						||
   *  * defaults: {name, image, params, initrd}
 | 
						||
   *  * options: [ option... ]
 | 
						||
   *  * option: {name, params, class}
 | 
						||
   */
 | 
						||
  menuBuilderGrub2 =
 | 
						||
  defaults: options: lib.concatStrings
 | 
						||
    (
 | 
						||
      map
 | 
						||
      (option: ''
 | 
						||
        menuentry '${defaults.name} ${
 | 
						||
        # Name appended to menuentry defaults to params if no specific name given.
 | 
						||
        option.name or (if option ? params then "(${option.params})" else "")
 | 
						||
        }' ${if option ? class then " --class ${option.class}" else ""} {
 | 
						||
          linux ${defaults.image} ${defaults.params} ${
 | 
						||
            option.params or ""
 | 
						||
          }
 | 
						||
          initrd ${defaults.initrd}
 | 
						||
        }
 | 
						||
      '')
 | 
						||
      options
 | 
						||
    )
 | 
						||
  ;
 | 
						||
 | 
						||
  /**
 | 
						||
   * Given a `config`, builds the default options.
 | 
						||
   */
 | 
						||
  buildMenuGrub2 = config:
 | 
						||
    buildMenuAdditionalParamsGrub2 config ""
 | 
						||
  ;
 | 
						||
 | 
						||
  /**
 | 
						||
   * Given a `config` and params to add to `params`, build a set of default options.
 | 
						||
   * Use this one when creating a variant (e.g. hidpi)
 | 
						||
   */
 | 
						||
  buildMenuAdditionalParamsGrub2 = config: additional:
 | 
						||
  let
 | 
						||
    finalCfg = {
 | 
						||
      name = "NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel}";
 | 
						||
      params = "init=${config.system.build.toplevel}/init ${additional} ${toString config.boot.kernelParams}";
 | 
						||
      image = "/boot/${config.system.boot.loader.kernelFile}";
 | 
						||
      initrd = "/boot/initrd";
 | 
						||
    };
 | 
						||
  in
 | 
						||
    menuBuilderGrub2
 | 
						||
    finalCfg
 | 
						||
    [
 | 
						||
      { class = "installer"; }
 | 
						||
      { class = "nomodeset"; params = "nomodeset"; }
 | 
						||
      { class = "copytoram"; params = "copytoram"; }
 | 
						||
      { class = "debug";     params = "debug"; }
 | 
						||
    ]
 | 
						||
  ;
 | 
						||
 | 
						||
  # Timeout in syslinux is in units of 1/10 of a second.
 | 
						||
  # 0 is used to disable timeouts.
 | 
						||
  syslinuxTimeout = if config.boot.loader.timeout == null then
 | 
						||
      0
 | 
						||
    else
 | 
						||
      max (config.boot.loader.timeout * 10) 1;
 | 
						||
 | 
						||
 | 
						||
  max = x: y: if x > y then x else y;
 | 
						||
 | 
						||
  # The configuration file for syslinux.
 | 
						||
 | 
						||
  # Notes on syslinux configuration and UNetbootin compatiblity:
 | 
						||
  #   * Do not use '/syslinux/syslinux.cfg' as the path for this
 | 
						||
  #     configuration. UNetbootin will not parse the file and use it as-is.
 | 
						||
  #     This results in a broken configuration if the partition label does
 | 
						||
  #     not match the specified config.isoImage.volumeID. For this reason
 | 
						||
  #     we're using '/isolinux/isolinux.cfg'.
 | 
						||
  #   * Use APPEND instead of adding command-line arguments directly after
 | 
						||
  #     the LINUX entries.
 | 
						||
  #   * COM32 entries (chainload, reboot, poweroff) are not recognized. They
 | 
						||
  #     result in incorrect boot entries.
 | 
						||
 | 
						||
  baseIsolinuxCfg = ''
 | 
						||
    SERIAL 0 38400
 | 
						||
    TIMEOUT ${builtins.toString syslinuxTimeout}
 | 
						||
    UI vesamenu.c32
 | 
						||
    MENU TITLE NixOS
 | 
						||
    MENU BACKGROUND /isolinux/background.png
 | 
						||
    MENU RESOLUTION 800 600
 | 
						||
    MENU CLEAR
 | 
						||
    MENU ROWS 6
 | 
						||
    MENU CMDLINEROW -4
 | 
						||
    MENU TIMEOUTROW -3
 | 
						||
    MENU TABMSGROW  -2
 | 
						||
    MENU HELPMSGROW -1
 | 
						||
    MENU HELPMSGENDROW -1
 | 
						||
    MENU MARGIN 0
 | 
						||
 | 
						||
    #                                FG:AARRGGBB  BG:AARRGGBB   shadow
 | 
						||
    MENU COLOR BORDER       30;44      #00000000    #00000000   none
 | 
						||
    MENU COLOR SCREEN       37;40      #FF000000    #00E2E8FF   none
 | 
						||
    MENU COLOR TABMSG       31;40      #80000000    #00000000   none
 | 
						||
    MENU COLOR TIMEOUT      1;37;40    #FF000000    #00000000   none
 | 
						||
    MENU COLOR TIMEOUT_MSG  37;40      #FF000000    #00000000   none
 | 
						||
    MENU COLOR CMDMARK      1;36;40    #FF000000    #00000000   none
 | 
						||
    MENU COLOR CMDLINE      37;40      #FF000000    #00000000   none
 | 
						||
    MENU COLOR TITLE        1;36;44    #00000000    #00000000   none
 | 
						||
    MENU COLOR UNSEL        37;44      #FF000000    #00000000   none
 | 
						||
    MENU COLOR SEL          7;37;40    #FFFFFFFF    #FF5277C3   std
 | 
						||
 | 
						||
    DEFAULT boot
 | 
						||
 | 
						||
    LABEL boot
 | 
						||
    MENU LABEL NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel}
 | 
						||
    LINUX /boot/${config.system.boot.loader.kernelFile}
 | 
						||
    APPEND init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}
 | 
						||
    INITRD /boot/${config.system.boot.loader.initrdFile}
 | 
						||
 | 
						||
    # A variant to boot with 'nomodeset'
 | 
						||
    LABEL boot-nomodeset
 | 
						||
    MENU LABEL NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel} (nomodeset)
 | 
						||
    LINUX /boot/${config.system.boot.loader.kernelFile}
 | 
						||
    APPEND init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} nomodeset
 | 
						||
    INITRD /boot/${config.system.boot.loader.initrdFile}
 | 
						||
 | 
						||
    # A variant to boot with 'copytoram'
 | 
						||
    LABEL boot-copytoram
 | 
						||
    MENU LABEL NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel} (copytoram)
 | 
						||
    LINUX /boot/${config.system.boot.loader.kernelFile}
 | 
						||
    APPEND init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} copytoram
 | 
						||
    INITRD /boot/${config.system.boot.loader.initrdFile}
 | 
						||
 | 
						||
    # A variant to boot with verbose logging to the console
 | 
						||
    LABEL boot-debug
 | 
						||
    MENU LABEL NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel} (debug)
 | 
						||
    LINUX /boot/${config.system.boot.loader.kernelFile}
 | 
						||
    APPEND init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} loglevel=7
 | 
						||
    INITRD /boot/${config.system.boot.loader.initrdFile}
 | 
						||
  '';
 | 
						||
 | 
						||
  isolinuxMemtest86Entry = ''
 | 
						||
    LABEL memtest
 | 
						||
    MENU LABEL Memtest86+
 | 
						||
    LINUX /boot/memtest.bin
 | 
						||
    APPEND ${toString config.boot.loader.grub.memtest86.params}
 | 
						||
  '';
 | 
						||
 | 
						||
  isolinuxCfg = concatStringsSep "\n"
 | 
						||
    ([ baseIsolinuxCfg ] ++ optional config.boot.loader.grub.memtest86.enable isolinuxMemtest86Entry);
 | 
						||
 | 
						||
  # Setup instructions for rEFInd.
 | 
						||
  refind =
 | 
						||
    if targetArch == "x64" then
 | 
						||
      ''
 | 
						||
      # Adds rEFInd to the ISO.
 | 
						||
      cp -v ${pkgs.refind}/share/refind/refind_x64.efi $out/EFI/boot/
 | 
						||
      ''
 | 
						||
    else
 | 
						||
      "# No refind for ${targetArch}"
 | 
						||
  ;
 | 
						||
 | 
						||
  grubMenuCfg = ''
 | 
						||
    #
 | 
						||
    # Menu configuration
 | 
						||
    #
 | 
						||
 | 
						||
    insmod gfxterm
 | 
						||
    insmod png
 | 
						||
    set gfxpayload=keep
 | 
						||
 | 
						||
    # Fonts can be loaded?
 | 
						||
    # (This font is assumed to always be provided as a fallback by NixOS)
 | 
						||
    if loadfont (hd0)/EFI/boot/unicode.pf2; then
 | 
						||
      # Use graphical term, it can be either with background image or a theme.
 | 
						||
      # input is "console", while output is "gfxterm".
 | 
						||
      # This enables "serial" input and output only when possible.
 | 
						||
      # Otherwise the failure mode is to not even enable gfxterm.
 | 
						||
      if test "\$with_serial" == "yes"; then
 | 
						||
        terminal_output gfxterm serial
 | 
						||
        terminal_input  console serial
 | 
						||
      else
 | 
						||
        terminal_output gfxterm
 | 
						||
        terminal_input  console
 | 
						||
      fi
 | 
						||
    else
 | 
						||
      # Sets colors for the non-graphical term.
 | 
						||
      set menu_color_normal=cyan/blue
 | 
						||
      set menu_color_highlight=white/blue
 | 
						||
    fi
 | 
						||
 | 
						||
    ${ # When there is a theme configured, use it, otherwise use the background image.
 | 
						||
    if (!isNull config.isoImage.grubTheme) then ''
 | 
						||
      # Sets theme.
 | 
						||
      set theme=(hd0)/EFI/boot/grub-theme/theme.txt
 | 
						||
      # Load theme fonts
 | 
						||
      $(find ${config.isoImage.grubTheme} -iname '*.pf2' -printf "loadfont (hd0)/EFI/boot/grub-theme/%P\n")
 | 
						||
    '' else ''
 | 
						||
      if background_image (hd0)/EFI/boot/efi-background.png; then
 | 
						||
        # Black background means transparent background when there
 | 
						||
        # is a background image set... This seems undocumented :(
 | 
						||
        set color_normal=black/black
 | 
						||
        set color_highlight=white/blue
 | 
						||
      else
 | 
						||
        # Falls back again to proper colors.
 | 
						||
        set menu_color_normal=cyan/blue
 | 
						||
        set menu_color_highlight=white/blue
 | 
						||
      fi
 | 
						||
    ''}
 | 
						||
  '';
 | 
						||
 | 
						||
  # The EFI boot image.
 | 
						||
  # Notes about grub:
 | 
						||
  #  * Yes, the grubMenuCfg has to be repeated in all submenus. Otherwise you
 | 
						||
  #    will get white-on-black console-like text on sub-menus. *sigh*
 | 
						||
  efiDir = pkgs.runCommand "efi-directory" {} ''
 | 
						||
    mkdir -p $out/EFI/boot/
 | 
						||
 | 
						||
    # ALWAYS required modules.
 | 
						||
    MODULES="fat iso9660 part_gpt part_msdos \
 | 
						||
             normal boot linux configfile loopback chain halt \
 | 
						||
             efifwsetup efi_gop \
 | 
						||
             ls search search_label search_fs_uuid search_fs_file \
 | 
						||
             gfxmenu gfxterm gfxterm_background gfxterm_menu test all_video loadenv \
 | 
						||
             exfat ext2 ntfs btrfs hfsplus udf \
 | 
						||
             videoinfo png \
 | 
						||
             echo serial \
 | 
						||
            "
 | 
						||
 | 
						||
    echo "Building GRUB with modules:"
 | 
						||
    for mod in $MODULES; do
 | 
						||
      echo " - $mod"
 | 
						||
    done
 | 
						||
 | 
						||
    # Modules that may or may not be available per-platform.
 | 
						||
    echo "Adding additional modules:"
 | 
						||
    for mod in efi_uga; do
 | 
						||
      if [ -f ${pkgs.grub2_efi}/lib/grub/${pkgs.grub2_efi.grubTarget}/$mod.mod ]; then
 | 
						||
        echo " - $mod"
 | 
						||
        MODULES+=" $mod"
 | 
						||
      fi
 | 
						||
    done
 | 
						||
 | 
						||
    # Make our own efi program, we can't rely on "grub-install" since it seems to
 | 
						||
    # probe for devices, even with --skip-fs-probe.
 | 
						||
    ${pkgs.grub2_efi}/bin/grub-mkimage -o $out/EFI/boot/boot${targetArch}.efi -p /EFI/boot -O ${pkgs.grub2_efi.grubTarget} \
 | 
						||
      $MODULES
 | 
						||
    cp ${pkgs.grub2_efi}/share/grub/unicode.pf2 $out/EFI/boot/
 | 
						||
 | 
						||
    cat <<EOF > $out/EFI/boot/grub.cfg
 | 
						||
 | 
						||
    # If you want to use serial for "terminal_*" commands, you need to set one up:
 | 
						||
    #   Example manual configuration:
 | 
						||
    #    → serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1
 | 
						||
    # This uses the defaults, and makes the serial terminal available.
 | 
						||
    set with_serial=no
 | 
						||
    if serial; then set with_serial=yes ;fi
 | 
						||
    export with_serial
 | 
						||
    clear
 | 
						||
    set timeout=10
 | 
						||
    ${grubMenuCfg}
 | 
						||
 | 
						||
    #
 | 
						||
    # Menu entries
 | 
						||
    #
 | 
						||
 | 
						||
    ${buildMenuGrub2 config}
 | 
						||
    submenu "HiDPI, Quirks and Accessibility" --class hidpi --class submenu {
 | 
						||
      ${grubMenuCfg}
 | 
						||
      submenu "Suggests resolution @720p" --class hidpi-720p {
 | 
						||
        ${grubMenuCfg}
 | 
						||
        ${buildMenuAdditionalParamsGrub2 config "video=1280x720@60"}
 | 
						||
      }
 | 
						||
      submenu "Suggests resolution @1080p" --class hidpi-1080p {
 | 
						||
        ${grubMenuCfg}
 | 
						||
        ${buildMenuAdditionalParamsGrub2 config "video=1920x1080@60"}
 | 
						||
      }
 | 
						||
 | 
						||
      # Some laptop and convertibles have the panel installed in an
 | 
						||
      # inconvenient way, rotated away from the keyboard.
 | 
						||
      # Those entries makes it easier to use the installer.
 | 
						||
      submenu "" {return}
 | 
						||
      submenu "Rotate framebuffer Clockwise" --class rotate-90cw {
 | 
						||
        ${grubMenuCfg}
 | 
						||
        ${buildMenuAdditionalParamsGrub2 config "fbcon=rotate:1"}
 | 
						||
      }
 | 
						||
      submenu "Rotate framebuffer Upside-Down" --class rotate-180 {
 | 
						||
        ${grubMenuCfg}
 | 
						||
        ${buildMenuAdditionalParamsGrub2 config "fbcon=rotate:2"}
 | 
						||
      }
 | 
						||
      submenu "Rotate framebuffer Counter-Clockwise" --class rotate-90ccw {
 | 
						||
        ${grubMenuCfg}
 | 
						||
        ${buildMenuAdditionalParamsGrub2 config "fbcon=rotate:3"}
 | 
						||
      }
 | 
						||
 | 
						||
      # As a proof of concept, mainly. (Not sure it has accessibility merits.)
 | 
						||
      submenu "" {return}
 | 
						||
      submenu "Use black on white" --class accessibility-blakconwhite {
 | 
						||
        ${grubMenuCfg}
 | 
						||
        ${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"}
 | 
						||
      }
 | 
						||
 | 
						||
      # Serial access is a must!
 | 
						||
      submenu "" {return}
 | 
						||
      submenu "Serial console=ttyS0,115200n8" --class serial {
 | 
						||
        ${grubMenuCfg}
 | 
						||
        ${buildMenuAdditionalParamsGrub2 config "console=ttyS0,115200n8"}
 | 
						||
      }
 | 
						||
    }
 | 
						||
 | 
						||
    menuentry 'rEFInd' --class refind {
 | 
						||
      # UUID is hard-coded in the derivation.
 | 
						||
      search --set=root --no-floppy --fs-uuid 1234-5678
 | 
						||
      chainloader (\$root)/EFI/boot/refind_x64.efi
 | 
						||
    }
 | 
						||
    menuentry 'Firmware Setup' --class settings {
 | 
						||
      fwsetup
 | 
						||
      clear
 | 
						||
      echo ""
 | 
						||
      echo "If you see this message, your EFI system doesn't support this feature."
 | 
						||
      echo ""
 | 
						||
    }
 | 
						||
    menuentry 'Shutdown' --class shutdown {
 | 
						||
      halt
 | 
						||
    }
 | 
						||
    EOF
 | 
						||
 | 
						||
    ${refind}
 | 
						||
  '';
 | 
						||
 | 
						||
  efiImg = pkgs.runCommand "efi-image_eltorito" { buildInputs = [ pkgs.mtools pkgs.libfaketime ]; }
 | 
						||
    # Be careful about determinism: du --apparent-size,
 | 
						||
    #   dates (cp -p, touch, mcopy -m, faketime for label), IDs (mkfs.vfat -i)
 | 
						||
    ''
 | 
						||
      mkdir ./contents && cd ./contents
 | 
						||
      cp -rp "${efiDir}"/* .
 | 
						||
      mkdir ./boot
 | 
						||
      cp -p "${config.boot.kernelPackages.kernel}/${config.system.boot.loader.kernelFile}" \
 | 
						||
        "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}" ./boot/
 | 
						||
      touch --date=@0 ./*
 | 
						||
 | 
						||
      usage_size=$(du -sb --apparent-size . | tr -cd '[:digit:]')
 | 
						||
      # Make the image 110% as big as the files need to make up for FAT overhead
 | 
						||
      image_size=$(( ($usage_size * 110) / 100 ))
 | 
						||
      # Make the image fit blocks of 1M
 | 
						||
      block_size=$((1024*1024))
 | 
						||
      image_size=$(( ($image_size / $block_size + 1) * $block_size ))
 | 
						||
      echo "Usage size: $usage_size"
 | 
						||
      echo "Image size: $image_size"
 | 
						||
      truncate --size=$image_size "$out"
 | 
						||
      ${pkgs.libfaketime}/bin/faketime "2000-01-01 00:00:00" ${pkgs.dosfstools}/sbin/mkfs.vfat -i 12345678 -n EFIBOOT "$out"
 | 
						||
      mcopy -psvm -i "$out" ./* ::
 | 
						||
      # Verify the FAT partition.
 | 
						||
      ${pkgs.dosfstools}/sbin/fsck.vfat -vn "$out"
 | 
						||
    ''; # */
 | 
						||
 | 
						||
  # Name used by UEFI for architectures.
 | 
						||
  targetArch =
 | 
						||
    if pkgs.stdenv.isi686 then
 | 
						||
      "ia32"
 | 
						||
    else if pkgs.stdenv.isx86_64 then
 | 
						||
      "x64"
 | 
						||
    else if pkgs.stdenv.isAarch64 then
 | 
						||
      "aa64"
 | 
						||
    else
 | 
						||
      throw "Unsupported architecture";
 | 
						||
 | 
						||
  # Syslinux (and isolinux) only supports x86-based architectures.
 | 
						||
  canx86BiosBoot = pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64;
 | 
						||
 | 
						||
in
 | 
						||
 | 
						||
{
 | 
						||
  options = {
 | 
						||
 | 
						||
    isoImage.isoName = mkOption {
 | 
						||
      default = "${config.isoImage.isoBaseName}.iso";
 | 
						||
      description = ''
 | 
						||
        Name of the generated ISO image file.
 | 
						||
      '';
 | 
						||
    };
 | 
						||
 | 
						||
    isoImage.isoBaseName = mkOption {
 | 
						||
      default = "nixos";
 | 
						||
      description = ''
 | 
						||
        Prefix of the name of the generated ISO image file.
 | 
						||
      '';
 | 
						||
    };
 | 
						||
 | 
						||
    isoImage.compressImage = mkOption {
 | 
						||
      default = false;
 | 
						||
      description = ''
 | 
						||
        Whether the ISO image should be compressed using
 | 
						||
        <command>bzip2</command>.
 | 
						||
      '';
 | 
						||
    };
 | 
						||
 | 
						||
    isoImage.volumeID = mkOption {
 | 
						||
      default = "NIXOS_BOOT_CD";
 | 
						||
      description = ''
 | 
						||
        Specifies the label or volume ID of the generated ISO image.
 | 
						||
        Note that the label is used by stage 1 of the boot process to
 | 
						||
        mount the CD, so it should be reasonably distinctive.
 | 
						||
      '';
 | 
						||
    };
 | 
						||
 | 
						||
    isoImage.contents = mkOption {
 | 
						||
      example = literalExample ''
 | 
						||
        [ { source = pkgs.memtest86 + "/memtest.bin";
 | 
						||
            target = "boot/memtest.bin";
 | 
						||
          }
 | 
						||
        ]
 | 
						||
      '';
 | 
						||
      description = ''
 | 
						||
        This option lists files to be copied to fixed locations in the
 | 
						||
        generated ISO image.
 | 
						||
      '';
 | 
						||
    };
 | 
						||
 | 
						||
    isoImage.storeContents = mkOption {
 | 
						||
      example = literalExample "[ pkgs.stdenv ]";
 | 
						||
      description = ''
 | 
						||
        This option lists additional derivations to be included in the
 | 
						||
        Nix store in the generated ISO image.
 | 
						||
      '';
 | 
						||
    };
 | 
						||
 | 
						||
    isoImage.includeSystemBuildDependencies = mkOption {
 | 
						||
      default = false;
 | 
						||
      description = ''
 | 
						||
        Set this option to include all the needed sources etc in the
 | 
						||
        image. It significantly increases image size. Use that when
 | 
						||
        you want to be able to keep all the sources needed to build your
 | 
						||
        system or when you are going to install the system on a computer
 | 
						||
        with slow or non-existent network connection.
 | 
						||
      '';
 | 
						||
    };
 | 
						||
 | 
						||
    isoImage.makeEfiBootable = mkOption {
 | 
						||
      default = false;
 | 
						||
      description = ''
 | 
						||
        Whether the ISO image should be an efi-bootable volume.
 | 
						||
      '';
 | 
						||
    };
 | 
						||
 | 
						||
    isoImage.makeUsbBootable = mkOption {
 | 
						||
      default = false;
 | 
						||
      description = ''
 | 
						||
        Whether the ISO image should be bootable from CD as well as USB.
 | 
						||
      '';
 | 
						||
    };
 | 
						||
 | 
						||
    isoImage.efiSplashImage = mkOption {
 | 
						||
      default = pkgs.fetchurl {
 | 
						||
          url = https://raw.githubusercontent.com/NixOS/nixos-artwork/a9e05d7deb38a8e005a2b52575a3f59a63a4dba0/bootloader/efi-background.png;
 | 
						||
          sha256 = "18lfwmp8yq923322nlb9gxrh5qikj1wsk6g5qvdh31c4h5b1538x";
 | 
						||
        };
 | 
						||
      description = ''
 | 
						||
        The splash image to use in the EFI bootloader.
 | 
						||
      '';
 | 
						||
    };
 | 
						||
 | 
						||
    isoImage.splashImage = mkOption {
 | 
						||
      default = pkgs.fetchurl {
 | 
						||
          url = https://raw.githubusercontent.com/NixOS/nixos-artwork/a9e05d7deb38a8e005a2b52575a3f59a63a4dba0/bootloader/isolinux/bios-boot.png;
 | 
						||
          sha256 = "1wp822zrhbg4fgfbwkr7cbkr4labx477209agzc0hr6k62fr6rxd";
 | 
						||
        };
 | 
						||
      description = ''
 | 
						||
        The splash image to use in the legacy-boot bootloader.
 | 
						||
      '';
 | 
						||
    };
 | 
						||
 | 
						||
    isoImage.grubTheme = mkOption {
 | 
						||
      default = pkgs.nixos-grub2-theme;
 | 
						||
      type = types.nullOr (types.either types.path types.package);
 | 
						||
      description = ''
 | 
						||
        The grub2 theme used for UEFI boot.
 | 
						||
      '';
 | 
						||
    };
 | 
						||
 | 
						||
    isoImage.appendToMenuLabel = mkOption {
 | 
						||
      default = " Installer";
 | 
						||
      example = " Live System";
 | 
						||
      description = ''
 | 
						||
        The string to append after the menu label for the NixOS system.
 | 
						||
        This will be directly appended (without whitespace) to the NixOS version
 | 
						||
        string, like for example if it is set to <literal>XXX</literal>:
 | 
						||
 | 
						||
        <para><literal>NixOS 99.99-pre666XXX</literal></para>
 | 
						||
      '';
 | 
						||
    };
 | 
						||
 | 
						||
  };
 | 
						||
 | 
						||
  config = {
 | 
						||
 | 
						||
    boot.loader.grub.version = 2;
 | 
						||
 | 
						||
    # Don't build the GRUB menu builder script, since we don't need it
 | 
						||
    # here and it causes a cyclic dependency.
 | 
						||
    boot.loader.grub.enable = false;
 | 
						||
 | 
						||
    environment.systemPackages = [ pkgs.grub2 pkgs.grub2_efi ]
 | 
						||
      ++ optional canx86BiosBoot pkgs.syslinux
 | 
						||
    ;
 | 
						||
 | 
						||
    # In stage 1 of the boot, mount the CD as the root FS by label so
 | 
						||
    # that we don't need to know its device.  We pass the label of the
 | 
						||
    # root filesystem on the kernel command line, rather than in
 | 
						||
    # `fileSystems' below.  This allows CD-to-USB converters such as
 | 
						||
    # UNetbootin to rewrite the kernel command line to pass the label or
 | 
						||
    # UUID of the USB stick.  It would be nicer to write
 | 
						||
    # `root=/dev/disk/by-label/...' here, but UNetbootin doesn't
 | 
						||
    # recognise that.
 | 
						||
    boot.kernelParams =
 | 
						||
      [ "root=LABEL=${config.isoImage.volumeID}"
 | 
						||
        "boot.shell_on_fail"
 | 
						||
      ];
 | 
						||
 | 
						||
    fileSystems."/" =
 | 
						||
      { fsType = "tmpfs";
 | 
						||
        options = [ "mode=0755" ];
 | 
						||
      };
 | 
						||
 | 
						||
    # Note that /dev/root is a symlink to the actual root device
 | 
						||
    # specified on the kernel command line, created in the stage 1
 | 
						||
    # init script.
 | 
						||
    fileSystems."/iso" =
 | 
						||
      { device = "/dev/root";
 | 
						||
        neededForBoot = true;
 | 
						||
        noCheck = true;
 | 
						||
      };
 | 
						||
 | 
						||
    # In stage 1, mount a tmpfs on top of /nix/store (the squashfs
 | 
						||
    # image) to make this a live CD.
 | 
						||
    fileSystems."/nix/.ro-store" =
 | 
						||
      { fsType = "squashfs";
 | 
						||
        device = "/iso/nix-store.squashfs";
 | 
						||
        options = [ "loop" ];
 | 
						||
        neededForBoot = true;
 | 
						||
      };
 | 
						||
 | 
						||
    fileSystems."/nix/.rw-store" =
 | 
						||
      { fsType = "tmpfs";
 | 
						||
        options = [ "mode=0755" ];
 | 
						||
        neededForBoot = true;
 | 
						||
      };
 | 
						||
 | 
						||
    fileSystems."/nix/store" =
 | 
						||
      { fsType = "unionfs-fuse";
 | 
						||
        device = "unionfs";
 | 
						||
        options = [ "allow_other" "cow" "nonempty" "chroot=/mnt-root" "max_files=32768" "hide_meta_files" "dirs=/nix/.rw-store=rw:/nix/.ro-store=ro" ];
 | 
						||
      };
 | 
						||
 | 
						||
    boot.initrd.availableKernelModules = [ "squashfs" "iso9660" "uas" ];
 | 
						||
 | 
						||
    boot.blacklistedKernelModules = [ "nouveau" ];
 | 
						||
 | 
						||
    boot.initrd.kernelModules = [ "loop" ];
 | 
						||
 | 
						||
    # Closures to be copied to the Nix store on the CD, namely the init
 | 
						||
    # script and the top-level system configuration directory.
 | 
						||
    isoImage.storeContents =
 | 
						||
      [ config.system.build.toplevel ] ++
 | 
						||
      optional config.isoImage.includeSystemBuildDependencies
 | 
						||
        config.system.build.toplevel.drvPath;
 | 
						||
 | 
						||
    # Create the squashfs image that contains the Nix store.
 | 
						||
    system.build.squashfsStore = pkgs.callPackage ../../../lib/make-squashfs.nix {
 | 
						||
      storeContents = config.isoImage.storeContents;
 | 
						||
    };
 | 
						||
 | 
						||
    # Individual files to be included on the CD, outside of the Nix
 | 
						||
    # store on the CD.
 | 
						||
    isoImage.contents =
 | 
						||
      [
 | 
						||
        { source = config.boot.kernelPackages.kernel + "/" + config.system.boot.loader.kernelFile;
 | 
						||
          target = "/boot/" + config.system.boot.loader.kernelFile;
 | 
						||
        }
 | 
						||
        { source = config.system.build.initialRamdisk + "/" + config.system.boot.loader.initrdFile;
 | 
						||
          target = "/boot/" + config.system.boot.loader.initrdFile;
 | 
						||
        }
 | 
						||
        { source = config.system.build.squashfsStore;
 | 
						||
          target = "/nix-store.squashfs";
 | 
						||
        }
 | 
						||
        { source = config.isoImage.efiSplashImage;
 | 
						||
          target = "/EFI/boot/efi-background.png";
 | 
						||
        }
 | 
						||
        { source = config.isoImage.splashImage;
 | 
						||
          target = "/isolinux/background.png";
 | 
						||
        }
 | 
						||
        { source = pkgs.writeText "version" config.system.nixos.label;
 | 
						||
          target = "/version.txt";
 | 
						||
        }
 | 
						||
      ] ++ optionals canx86BiosBoot [
 | 
						||
        { source = pkgs.substituteAll  {
 | 
						||
            name = "isolinux.cfg";
 | 
						||
            src = pkgs.writeText "isolinux.cfg-in" isolinuxCfg;
 | 
						||
            bootRoot = "/boot";
 | 
						||
          };
 | 
						||
          target = "/isolinux/isolinux.cfg";
 | 
						||
        }
 | 
						||
        { source = "${pkgs.syslinux}/share/syslinux";
 | 
						||
          target = "/isolinux";
 | 
						||
        }
 | 
						||
      ] ++ optionals config.isoImage.makeEfiBootable [
 | 
						||
        { source = efiImg;
 | 
						||
          target = "/boot/efi.img";
 | 
						||
        }
 | 
						||
        { source = "${efiDir}/EFI";
 | 
						||
          target = "/EFI";
 | 
						||
        }
 | 
						||
      ] ++ optionals (config.boot.loader.grub.memtest86.enable && canx86BiosBoot) [
 | 
						||
        { source = "${pkgs.memtest86plus}/memtest.bin";
 | 
						||
          target = "/boot/memtest.bin";
 | 
						||
        }
 | 
						||
      ] ++ optionals (!isNull config.isoImage.grubTheme) [
 | 
						||
        { source = config.isoImage.grubTheme;
 | 
						||
          target = "/EFI/boot/grub-theme";
 | 
						||
        }
 | 
						||
      ];
 | 
						||
 | 
						||
    boot.loader.timeout = 10;
 | 
						||
 | 
						||
    # Create the ISO image.
 | 
						||
    system.build.isoImage = pkgs.callPackage ../../../lib/make-iso9660-image.nix ({
 | 
						||
      inherit (config.isoImage) isoName compressImage volumeID contents;
 | 
						||
      bootable = canx86BiosBoot;
 | 
						||
      bootImage = "/isolinux/isolinux.bin";
 | 
						||
      syslinux = if canx86BiosBoot then pkgs.syslinux else null;
 | 
						||
    } // optionalAttrs (config.isoImage.makeUsbBootable && canx86BiosBoot) {
 | 
						||
      usbBootable = true;
 | 
						||
      isohybridMbrImage = "${pkgs.syslinux}/share/syslinux/isohdpfx.bin";
 | 
						||
    } // optionalAttrs config.isoImage.makeEfiBootable {
 | 
						||
      efiBootable = true;
 | 
						||
      efiBootImage = "boot/efi.img";
 | 
						||
    });
 | 
						||
 | 
						||
    boot.postBootCommands =
 | 
						||
      ''
 | 
						||
        # After booting, register the contents of the Nix store on the
 | 
						||
        # CD in the Nix database in the tmpfs.
 | 
						||
        ${config.nix.package.out}/bin/nix-store --load-db < /nix/store/nix-path-registration
 | 
						||
 | 
						||
        # nixos-rebuild also requires a "system" profile and an
 | 
						||
        # /etc/NIXOS tag.
 | 
						||
        touch /etc/NIXOS
 | 
						||
        ${config.nix.package.out}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
 | 
						||
      '';
 | 
						||
 | 
						||
    # Add vfat support to the initrd to enable people to copy the
 | 
						||
    # contents of the CD to a bootable USB stick.
 | 
						||
    boot.initrd.supportedFilesystems = [ "vfat" ];
 | 
						||
 | 
						||
  };
 | 
						||
 | 
						||
}
 |