nixos: iso-image: use syslinux bootloader for USB booting support

This changes the bootloader for iso generation from Grub to
syslinux. In addition this adds USB booting support, so that
"dd" can be used to burn the generated ISO to USB thumbdrives
instead of needing applications like UnetBootin.

Changed files
+112 -76
nixos
+10 -3
nixos/lib/make-iso9660-image.nix
···
-
{ stdenv, perl, cdrkit, pathsFromGraph
, # The file name of the resulting ISO image.
isoName ? "cd.iso"
···
, # Whether this should be an efi-bootable El-Torito CD.
efiBootable ? false
, # The path (in the ISO file system) of the boot image.
bootImage ? ""
, # The path (in the ISO file system) of the efi boot image.
efiBootImage ? ""
, # Whether to compress the resulting ISO image with bzip2.
compressImage ? false
···
assert bootable -> bootImage != "";
assert efiBootable -> efiBootImage != "";
stdenv.mkDerivation {
name = "iso9660-image";
builder = ./make-iso9660-image.sh;
-
buildInputs = [perl cdrkit];
-
inherit isoName bootable bootImage compressImage volumeID pathsFromGraph efiBootImage efiBootable;
# !!! should use XML.
sources = map (x: x.source) contents;
···
+
{ stdenv, perl, pathsFromGraph, xorriso, syslinux
, # The file name of the resulting ISO image.
isoName ? "cd.iso"
···
, # Whether this should be an efi-bootable El-Torito CD.
efiBootable ? false
+
, # Wheter this should be an hybrid CD (bootable from USB as well as CD).
+
usbBootable ? false
+
, # The path (in the ISO file system) of the boot image.
bootImage ? ""
, # The path (in the ISO file system) of the efi boot image.
efiBootImage ? ""
+
, # The path (outside the ISO file system) of the isohybrid-mbr image.
+
isohybridMbrImage ? ""
+
, # Whether to compress the resulting ISO image with bzip2.
compressImage ? false
···
assert bootable -> bootImage != "";
assert efiBootable -> efiBootImage != "";
+
assert usbBootable -> isohybridMbrImage != "";
stdenv.mkDerivation {
name = "iso9660-image";
builder = ./make-iso9660-image.sh;
+
buildInputs = [perl xorriso syslinux];
+
inherit isoName bootable bootImage compressImage volumeID pathsFromGraph efiBootImage efiBootable isohybridMbrImage usbBootable;
# !!! should use XML.
sources = map (x: x.source) contents;
+40 -8
nixos/lib/make-iso9660-image.sh
···
fi
done
-
bootFlags="-b $bootImage -c .boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table"
fi
if test -n "$efiBootable"; then
-
bootFlags="$bootFlags -eltorito-alt-boot -e $efiBootImage -no-emul-boot"
fi
touch pathlist
···
fi
done
-
# !!! what does this do?
cat pathlist | sed -e 's/=\(.*\)=\(.*\)=/\\=\1=\2\\=/' | tee pathlist.safer
mkdir -p $out/iso
-
genCommand="genisoimage -iso-level 4 -r -J $bootFlags -hide-rr-moved -graft-points -path-list pathlist.safer ${volumeID:+-V $volumeID}"
-
if test -z "$compressImage"; then
-
$genCommand -o $out/iso/$isoName
-
else
-
$genCommand | bzip2 > $out/iso/$isoName.bz2
fi
mkdir -p $out/nix-support
echo $system > $out/nix-support/system
···
fi
done
+
isoBootFlags="-eltorito-boot ${bootImage}
+
-eltorito-catalog .boot.cat
+
-no-emul-boot -boot-load-size 4 -boot-info-table"
+
fi
+
+
if test -n "$usbBootable"; then
+
usbBootFlags="-isohybrid-mbr ${isohybridMbrImage}"
fi
if test -n "$efiBootable"; then
+
efiBootFlags="-eltorito-alt-boot
+
-e $efiBootImage
+
-no-emul-boot
+
-isohybrid-gpt-basdat"
fi
touch pathlist
···
fi
done
+
# Escape filenames that contain '='.
+
# TODO: Handle this properly. This fails for filenames
+
# that contain multiple '=' symbols.
cat pathlist | sed -e 's/=\(.*\)=\(.*\)=/\\=\1=\2\\=/' | tee pathlist.safer
mkdir -p $out/iso
+
+
xorriso="xorriso
+
-as mkisofs
+
-iso-level 3
+
-volid ${volumeID}
+
-appid nixos
+
-publisher nixos
+
-graft-points
+
-full-iso9660-filenames
+
${isoBootFlags}
+
${usbBootFlags}
+
${efiBootFlags}
+
-r
+
-path-list pathlist.safer
+
--sort-weight 0 /
+
--sort-weight 1 /isolinux" # Make sure isolinux is near the beginning of the ISO
+
+
$xorriso -output $out/iso/$isoName
+
+
if test -n "$usbBootable"; then
+
echo "Making image hybrid..."
+
isohybrid --uefi $out/iso/$isoName
fi
+
if test -n "$compressImage"; then
+
echo "Compressing image..."
+
bzip2 $out/iso/$isoName
+
fi
mkdir -p $out/nix-support
echo $system > $out/nix-support/system
+3
nixos/modules/installer/cd-dvd/installation-cd-base.nix
···
# EFI booting
isoImage.makeEfiBootable = true;
# Add Memtest86+ to the CD.
boot.loader.grub.memtest86.enable = true;
···
# EFI booting
isoImage.makeEfiBootable = true;
+
# USB booting
+
isoImage.makeUsbBootable = true;
+
# Add Memtest86+ to the CD.
boot.loader.grub.memtest86.enable = true;
+59 -65
nixos/modules/installer/cd-dvd/iso-image.nix
···
let
-
# The Grub image.
-
grubImage = pkgs.runCommand "grub_eltorito" {}
''
-
${pkgs.grub2}/bin/grub-mkimage -p /boot/grub -O i386-pc -o tmp biosdisk iso9660 help linux linux16 chain png jpeg echo gfxmenu reboot
-
cat ${pkgs.grub2}/lib/grub/*/cdboot.img tmp > $out
-
''; # */
-
-
-
# The configuration file for Grub.
-
grubCfg =
-
''
-
set default=${builtins.toString config.boot.loader.grub.default}
-
set timeout=${builtins.toString config.boot.loader.grub.timeout}
-
if loadfont /boot/grub/unicode.pf2; then
-
set gfxmode=640x480
-
insmod gfxterm
-
insmod vbe
-
terminal_output gfxterm
-
insmod png
-
if background_image /boot/grub/splash.png; then
-
set color_normal=white/black
-
set color_highlight=black/white
-
else
-
set menu_color_normal=cyan/blue
-
set menu_color_highlight=white/blue
-
fi
-
fi
-
${config.boot.loader.grub.extraEntries}
'';
# The efi boot image
efiDir = pkgs.runCommand "efi-directory" {} ''
-
mkdir -p $out/efi/boot
-
cp -v ${pkgs.gummiboot}/lib/gummiboot/gummiboot${targetArch}.efi $out/efi/boot/boot${targetArch}.efi
mkdir -p $out/loader/entries
echo "title NixOS LiveCD" > $out/loader/entries/nixos-livecd.conf
echo "linux /boot/bzImage" >> $out/loader/entries/nixos-livecd.conf
···
'';
};
};
···
# !!! Hack - attributes expected by other modules.
system.boot.loader.kernelFile = "bzImage";
-
environment.systemPackages = [ pkgs.grub2 ];
# 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
···
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" ];
boot.initrd.kernelModules = [ "loop" ];
···
# Individual files to be included on the CD, outside of the Nix
# store on the CD.
isoImage.contents =
-
[ { source = grubImage;
-
target = "/boot/grub/grub_eltorito";
-
}
-
{ source = pkgs.substituteAll {
-
name = "grub.cfg";
-
src = pkgs.writeText "grub.cfg-in" grubCfg;
bootRoot = "/boot";
};
-
target = "/boot/grub/grub.cfg";
}
{ source = config.boot.kernelPackages.kernel + "/bzImage";
target = "/boot/bzImage";
···
{ source = config.system.build.initialRamdisk + "/initrd";
target = "/boot/initrd";
}
-
{ source = "${pkgs.grub2}/share/grub/unicode.pf2";
-
target = "/boot/grub/unicode.pf2";
-
}
-
{ source = config.boot.loader.grub.splashImage;
-
target = "/boot/grub/splash.png";
-
}
{ source = config.system.build.squashfsStore;
target = "/nix-store.squashfs";
}
] ++ optionals config.isoImage.makeEfiBootable [
{ source = efiImg;
target = "/boot/efi.img";
}
-
{ source = "${efiDir}/efi";
-
target = "/efi";
}
{ source = "${efiDir}/loader";
target = "/loader";
}
-
] ++ mapAttrsToList (n: v: { source = v; target = "/boot/${n}"; }) config.boot.loader.grub.extraFiles;
-
-
# The Grub menu.
-
boot.loader.grub.extraEntries =
-
''
-
menuentry "NixOS ${config.system.nixosVersion} Installer" {
-
linux /boot/bzImage init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}
-
initrd /boot/initrd
-
}
-
-
menuentry "Boot from hard disk" {
-
set root=(hd0)
-
chainloader +1
-
}
-
'';
-
-
boot.loader.grub.timeout = 10;
# Create the ISO image.
system.build.isoImage = import ../../../lib/make-iso9660-image.nix ({
-
inherit (pkgs) stdenv perl cdrkit pathsFromGraph;
inherit (config.isoImage) isoName compressImage volumeID contents;
bootable = true;
-
bootImage = "/boot/grub/grub_eltorito";
} // optionalAttrs config.isoImage.makeEfiBootable {
efiBootable = true;
efiBootImage = "boot/efi.img";
···
let
+
# The configuration file for syslinux.
+
baseIsolinuxCfg =
''
+
SERIAL 0 38400
+
UI vesamenu.c32
+
MENU TITLE NixOS
+
MENU BACKGROUND /isolinux/background.png
+
LABEL boot
+
MENU LABEL Boot NixOS
+
LINUX /boot/bzImage init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}
+
INITRD /boot/initrd
+
LABEL chain
+
MENU LABEL Boot existing OS
+
COM32 chain.c32
+
APPEND hd0 0
+
LABEL reboot
+
MENU LABEL Reboot
+
COM32 reboot.c32
+
LABEL poweroff
+
MENU LABEL Power Off
+
COM32 poweroff.c32
'';
+
isolinuxCfg = baseIsolinuxCfg + (optionalString config.boot.loader.grub.memtest86.enable isolinuxMemtest86Entry);
# The efi boot image
efiDir = pkgs.runCommand "efi-directory" {} ''
+
mkdir -p $out/EFI/boot
+
cp -v ${pkgs.gummiboot}/lib/gummiboot/gummiboot${targetArch}.efi $out/EFI/boot/boot${targetArch}.efi
mkdir -p $out/loader/entries
echo "title NixOS LiveCD" > $out/loader/entries/nixos-livecd.conf
echo "linux /boot/bzImage" >> $out/loader/entries/nixos-livecd.conf
···
'';
};
+
isoImage.makeUsbBootable = mkOption {
+
default = false;
+
description = ''
+
Whether the ISO image should be bootable from CD as well as USB.
+
'';
+
};
+
+
isoImage.splashImage = mkOption {
+
default = pkgs.fetchurl {
+
url = https://raw.githubusercontent.com/NixOS/nixos-artwork/5729ab16c6a5793c10a2913b5a1b3f59b91c36ee/ideas/grub-splash/grub-nixos-1.png;
+
sha256 = "43fd8ad5decf6c23c87e9026170a13588c2eba249d9013cb9f888da5e2002217";
+
};
+
description = ''
+
The splash image to use in the bootloader.
+
'';
+
};
};
···
# !!! Hack - attributes expected by other modules.
system.boot.loader.kernelFile = "bzImage";
+
environment.systemPackages = [ pkgs.grub2 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
···
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" "usb-storage" ];
boot.initrd.kernelModules = [ "loop" ];
···
# Individual files to be included on the CD, outside of the Nix
# store on the CD.
isoImage.contents =
+
[ { source = pkgs.substituteAll {
+
name = "isolinux.cfg";
+
src = pkgs.writeText "isolinux.cfg-in" isolinuxCfg;
bootRoot = "/boot";
};
+
target = "/isolinux/isolinux.cfg";
}
{ source = config.boot.kernelPackages.kernel + "/bzImage";
target = "/boot/bzImage";
···
{ source = config.system.build.initialRamdisk + "/initrd";
target = "/boot/initrd";
}
{ source = config.system.build.squashfsStore;
target = "/nix-store.squashfs";
}
+
{ source = "${pkgs.syslinux}/share/syslinux";
+
target = "/isolinux";
+
}
+
{ source = config.isoImage.splashImage;
+
target = "/isolinux/background.png";
+
}
] ++ optionals config.isoImage.makeEfiBootable [
{ source = efiImg;
target = "/boot/efi.img";
}
+
{ source = "${efiDir}/EFI";
+
target = "/EFI";
}
{ source = "${efiDir}/loader";
target = "/loader";
}
+
];
# Create the ISO image.
system.build.isoImage = import ../../../lib/make-iso9660-image.nix ({
+
inherit (pkgs) stdenv perl pathsFromGraph xorriso syslinux;
inherit (config.isoImage) isoName compressImage volumeID contents;
bootable = true;
+
bootImage = "/isolinux/isolinux.bin";
+
} // optionalAttrs config.isoImage.makeUsbBootable {
+
usbBootable = true;
+
isohybridMbrImage = "${pkgs.syslinux}/share/syslinux/isohdpfx.bin";
} // optionalAttrs config.isoImage.makeEfiBootable {
efiBootable = true;
efiBootImage = "boot/efi.img";