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.
This commit is contained in:
Bob van der Linden 2014-10-26 21:29:04 +01:00 committed by Vladimír Čunát
parent 9a75c1d7c8
commit 8d092270d2
4 changed files with 112 additions and 76 deletions

View File

@ -1,4 +1,4 @@
{ stdenv, perl, cdrkit, pathsFromGraph { stdenv, perl, pathsFromGraph, xorriso, syslinux
, # The file name of the resulting ISO image. , # The file name of the resulting ISO image.
isoName ? "cd.iso" isoName ? "cd.iso"
@ -22,12 +22,18 @@
, # Whether this should be an efi-bootable El-Torito CD. , # Whether this should be an efi-bootable El-Torito CD.
efiBootable ? false 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. , # The path (in the ISO file system) of the boot image.
bootImage ? "" bootImage ? ""
, # The path (in the ISO file system) of the efi boot image. , # The path (in the ISO file system) of the efi boot image.
efiBootImage ? "" efiBootImage ? ""
, # The path (outside the ISO file system) of the isohybrid-mbr image.
isohybridMbrImage ? ""
, # Whether to compress the resulting ISO image with bzip2. , # Whether to compress the resulting ISO image with bzip2.
compressImage ? false compressImage ? false
@ -38,13 +44,14 @@
assert bootable -> bootImage != ""; assert bootable -> bootImage != "";
assert efiBootable -> efiBootImage != ""; assert efiBootable -> efiBootImage != "";
assert usbBootable -> isohybridMbrImage != "";
stdenv.mkDerivation { stdenv.mkDerivation {
name = "iso9660-image"; name = "iso9660-image";
builder = ./make-iso9660-image.sh; builder = ./make-iso9660-image.sh;
buildInputs = [perl cdrkit]; buildInputs = [perl xorriso syslinux];
inherit isoName bootable bootImage compressImage volumeID pathsFromGraph efiBootImage efiBootable; inherit isoName bootable bootImage compressImage volumeID pathsFromGraph efiBootImage efiBootable isohybridMbrImage usbBootable;
# !!! should use XML. # !!! should use XML.
sources = map (x: x.source) contents; sources = map (x: x.source) contents;

View File

@ -31,11 +31,20 @@ if test -n "$bootable"; then
fi fi
done done
bootFlags="-b $bootImage -c .boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table" 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 fi
if test -n "$efiBootable"; then if test -n "$efiBootable"; then
bootFlags="$bootFlags -eltorito-alt-boot -e $efiBootImage -no-emul-boot" efiBootFlags="-eltorito-alt-boot
-e $efiBootImage
-no-emul-boot
-isohybrid-gpt-basdat"
fi fi
touch pathlist touch pathlist
@ -74,18 +83,41 @@ for ((n = 0; n < ${#objects[*]}; n++)); do
fi fi
done done
# !!! what does this do? # 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 cat pathlist | sed -e 's/=\(.*\)=\(.*\)=/\\=\1=\2\\=/' | tee pathlist.safer
mkdir -p $out/iso 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 xorriso="xorriso
$genCommand -o $out/iso/$isoName -as mkisofs
else -iso-level 3
$genCommand | bzip2 > $out/iso/$isoName.bz2 -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 fi
if test -n "$compressImage"; then
echo "Compressing image..."
bzip2 $out/iso/$isoName
fi
mkdir -p $out/nix-support mkdir -p $out/nix-support
echo $system > $out/nix-support/system echo $system > $out/nix-support/system

View File

@ -36,6 +36,9 @@ with lib;
# EFI booting # EFI booting
isoImage.makeEfiBootable = true; isoImage.makeEfiBootable = true;
# USB booting
isoImage.makeUsbBootable = true;
# Add Memtest86+ to the CD. # Add Memtest86+ to the CD.
boot.loader.grub.memtest86.enable = true; boot.loader.grub.memtest86.enable = true;

View File

@ -8,45 +8,39 @@ with lib;
let let
# The Grub image. # The configuration file for syslinux.
grubImage = pkgs.runCommand "grub_eltorito" {} baseIsolinuxCfg =
'' ''
${pkgs.grub2}/bin/grub-mkimage -p /boot/grub -O i386-pc -o tmp biosdisk iso9660 help linux linux16 chain png jpeg echo gfxmenu reboot SERIAL 0 38400
cat ${pkgs.grub2}/lib/grub/*/cdboot.img tmp > $out 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
# The configuration file for Grub. LABEL chain
grubCfg = MENU LABEL Boot existing OS
'' COM32 chain.c32
set default=${builtins.toString config.boot.loader.grub.default} APPEND hd0 0
set timeout=${builtins.toString config.boot.loader.grub.timeout}
if loadfont /boot/grub/unicode.pf2; then LABEL reboot
set gfxmode=640x480 MENU LABEL Reboot
insmod gfxterm COM32 reboot.c32
insmod vbe
terminal_output gfxterm
insmod png LABEL poweroff
if background_image /boot/grub/splash.png; then MENU LABEL Power Off
set color_normal=white/black COM32 poweroff.c32
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}
''; '';
isolinuxCfg = baseIsolinuxCfg + (optionalString config.boot.loader.grub.memtest86.enable isolinuxMemtest86Entry);
# The efi boot image # The efi boot image
efiDir = pkgs.runCommand "efi-directory" {} '' efiDir = pkgs.runCommand "efi-directory" {} ''
mkdir -p $out/efi/boot mkdir -p $out/EFI/boot
cp -v ${pkgs.gummiboot}/lib/gummiboot/gummiboot${targetArch}.efi $out/efi/boot/boot${targetArch}.efi cp -v ${pkgs.gummiboot}/lib/gummiboot/gummiboot${targetArch}.efi $out/EFI/boot/boot${targetArch}.efi
mkdir -p $out/loader/entries mkdir -p $out/loader/entries
echo "title NixOS LiveCD" > $out/loader/entries/nixos-livecd.conf echo "title NixOS LiveCD" > $out/loader/entries/nixos-livecd.conf
echo "linux /boot/bzImage" >> $out/loader/entries/nixos-livecd.conf echo "linux /boot/bzImage" >> $out/loader/entries/nixos-livecd.conf
@ -152,6 +146,22 @@ in
''; '';
}; };
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.
'';
};
}; };
@ -166,7 +176,7 @@ in
# !!! Hack - attributes expected by other modules. # !!! Hack - attributes expected by other modules.
system.boot.loader.kernelFile = "bzImage"; system.boot.loader.kernelFile = "bzImage";
environment.systemPackages = [ pkgs.grub2 ]; environment.systemPackages = [ pkgs.grub2 pkgs.syslinux ];
# In stage 1 of the boot, mount the CD as the root FS by label so # 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 # that we don't need to know its device. We pass the label of the
@ -216,7 +226,7 @@ in
options = "allow_other,cow,nonempty,chroot=/mnt-root,max_files=32768,hide_meta_files,dirs=/nix/.rw-store=rw:/nix/.ro-store=ro"; 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.availableKernelModules = [ "squashfs" "iso9660" "usb-storage" ];
boot.initrd.kernelModules = [ "loop" ]; boot.initrd.kernelModules = [ "loop" ];
@ -236,15 +246,12 @@ in
# Individual files to be included on the CD, outside of the Nix # Individual files to be included on the CD, outside of the Nix
# store on the CD. # store on the CD.
isoImage.contents = isoImage.contents =
[ { source = grubImage; [ { source = pkgs.substituteAll {
target = "/boot/grub/grub_eltorito"; name = "isolinux.cfg";
} src = pkgs.writeText "isolinux.cfg-in" isolinuxCfg;
{ source = pkgs.substituteAll {
name = "grub.cfg";
src = pkgs.writeText "grub.cfg-in" grubCfg;
bootRoot = "/boot"; bootRoot = "/boot";
}; };
target = "/boot/grub/grub.cfg"; target = "/isolinux/isolinux.cfg";
} }
{ source = config.boot.kernelPackages.kernel + "/bzImage"; { source = config.boot.kernelPackages.kernel + "/bzImage";
target = "/boot/bzImage"; target = "/boot/bzImage";
@ -252,51 +259,38 @@ in
{ source = config.system.build.initialRamdisk + "/initrd"; { source = config.system.build.initialRamdisk + "/initrd";
target = "/boot/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; { source = config.system.build.squashfsStore;
target = "/nix-store.squashfs"; target = "/nix-store.squashfs";
} }
{ source = "${pkgs.syslinux}/share/syslinux";
target = "/isolinux";
}
{ source = config.isoImage.splashImage;
target = "/isolinux/background.png";
}
] ++ optionals config.isoImage.makeEfiBootable [ ] ++ optionals config.isoImage.makeEfiBootable [
{ source = efiImg; { source = efiImg;
target = "/boot/efi.img"; target = "/boot/efi.img";
} }
{ source = "${efiDir}/efi"; { source = "${efiDir}/EFI";
target = "/efi"; target = "/EFI";
} }
{ source = "${efiDir}/loader"; { source = "${efiDir}/loader";
target = "/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. # Create the ISO image.
system.build.isoImage = import ../../../lib/make-iso9660-image.nix ({ system.build.isoImage = import ../../../lib/make-iso9660-image.nix ({
inherit (pkgs) stdenv perl cdrkit pathsFromGraph; inherit (pkgs) stdenv perl pathsFromGraph xorriso syslinux;
inherit (config.isoImage) isoName compressImage volumeID contents; inherit (config.isoImage) isoName compressImage volumeID contents;
bootable = true; bootable = true;
bootImage = "/boot/grub/grub_eltorito"; bootImage = "/isolinux/isolinux.bin";
} // optionalAttrs config.isoImage.makeUsbBootable {
usbBootable = true;
isohybridMbrImage = "${pkgs.syslinux}/share/syslinux/isohdpfx.bin";
} // optionalAttrs config.isoImage.makeEfiBootable { } // optionalAttrs config.isoImage.makeEfiBootable {
efiBootable = true; efiBootable = true;
efiBootImage = "boot/efi.img"; efiBootImage = "boot/efi.img";