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