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