From 001fcad421b659befcf0e36e4f3a4d834d2c4dbd Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Tue, 13 Mar 2012 19:27:59 +0000 Subject: [PATCH] First shot at EFI booting. Note: This feature is INCOMPLETE. Moreover, when runEfibootmgr is true it will MODIFY NVRAM and, on Apple systems, possibly brick your firmware. PLEASE be careful while further testing is performed svn path=/nixos/trunk/; revision=33047 --- .../efi-boot-stub/efi-boot-stub-builder.sh | 118 ++++++++++++++++++ .../installer/efi-boot-stub/efi-boot-stub.nix | 98 +++++++++++++++ modules/module-list.nix | 1 + .../activation/switch-to-configuration.sh | 2 + 4 files changed, 219 insertions(+) create mode 100644 modules/installer/efi-boot-stub/efi-boot-stub-builder.sh create mode 100644 modules/installer/efi-boot-stub/efi-boot-stub.nix diff --git a/modules/installer/efi-boot-stub/efi-boot-stub-builder.sh b/modules/installer/efi-boot-stub/efi-boot-stub-builder.sh new file mode 100644 index 000000000000..d37b56c7ffe4 --- /dev/null +++ b/modules/installer/efi-boot-stub/efi-boot-stub-builder.sh @@ -0,0 +1,118 @@ +#! @bash@/bin/sh -e + +shopt -s nullglob + +export PATH=/empty +for i in @path@; do PATH=$PATH:$i/bin:$i/sbin; done + +default=$1 +if test -z "$1"; then + echo "Syntax: efi-boot-stub-builder.sh " + exit 1 +fi + +echo "updating the efi system partition..." + +# Convert a path to a file in the Nix store such as +# /nix/store/-/file to --. +# Also, efi executables need the .efi extension +cleanName() { + local path="$1" + echo "$path" | sed 's|^/nix/store/||' | sed 's|/|-|g' | sed 's|@kernelFile@$|@kernelFile@.efi|' +} + +# Copy a file from the Nix store to the EFI system partition +declare -A filesCopied + +copyToKernelsDir() { + local src="$1" + local dst="@efiSysMountPoint@/efi/nixos/$(cleanName $src)" + # Don't copy the file if $dst already exists. This means that we + # have to create $dst atomically to prevent partially copied + # kernels or initrd if this script is ever interrupted. + if ! test -e $dst; then + local dstTmp=$dst.tmp.$$ + cp $src $dstTmp + mv $dstTmp $dst + fi + filesCopied[$dst]=1 + result=$dst +} + +# Copy its kernel, initrd, and startup script to the efi system partition +# Add the efibootmgr entry if requested +addEntry() { + local path="$1" + local generation="$2" + + if ! test -e $path/kernel -a -e $path/initrd; then + return + fi + + local kernel=$(readlink -f $path/kernel) + local initrd=$(readlink -f $path/initrd) + copyToKernelsDir $kernel; kernel=$result + copyToKernelsDir $initrd; initrd=$result + + local startup="@efiSysMountPoint@/efi/nixos/$(cleanName $(readlink -f $path))-startup.nsh" + if ! test -e $startup; then + local dstTmp=$startup.tmp.$$ + echo "$(echo $kernel | sed 's|@efiSysMountPoint@||' | sed 's|/|\\|g') systemConfig=$(readlink -f $path) init=$(readlink -f $path/init) initrd=$(echo $initrd | sed 's|@efiSysMountPoint@||' | sed 's|/|\\|g') $(cat $path/kernel-params)" > $dstTmp + mv $dstTmp $startup + fi + filesCopied[$startup]=1 + + if test -n "@runEfibootmgr@"; then + set +e + efibootmgr -c -d "@efiDisk@" -g -l $(echo $kernel | sed 's|@efiSysMountPoint@||' | sed 's|/|\\|g') -L "NixOS $generation Generation" -p "@efiPartition@" \ + -u systemConfig=$(readlink -f $path) init=$(readlink -f $path/init) initrd=$(echo $initrd | sed 's|@efiSysMountPoint@||' | sed 's|/|\\|g') $(cat $path/kernel-params) + set -e + fi + + if test $(readlink -f "$path") = "$default"; then + if test -n "@runEfibootmgr@"; then + set +e + defaultbootnum=$(efibootmgr | grep "NixOS $generation Generation" | sed 's/Boot//' | sed 's/\*.*//') + set -e + fi + if test -n "@installStartupNsh@"; then + sed 's|.*@kernelFile@.efi|@kernelFile@.efi|' < $startup > "@efiSysMountPoint@/startup.nsh" + cp $kernel "@efiSysMountPoint@/@kernelFile@.efi" + fi + fi +} + +mkdir -p "@efiSysMountPoint@/efi/nixos/" + +# Remove all old boot manager entries +if test -n "@runEfibootmgr@"; then + set +e + modprobe efivars + for bootnum in $(efibootmgr | grep "NixOS" | grep "Generation" | sed 's/Boot//' | sed 's/\*.*//'); do + efibootmgr -B -b "$bootnum" + set -e + done +fi + +# Add all generations of the system profile to the system partition, in reverse +# (most recent to least recent) order. +for generation in $( + (cd /nix/var/nix/profiles && ls -d system-*-link) \ + | sed 's/system-\([0-9]\+\)-link/\1/' \ + | sort -n -r); do + link=/nix/var/nix/profiles/system-$generation-link + addEntry $link $generation +done + +if test -n "@runEfibootmgr@"; then + set +e + efibootmgr -o $defaultbootnum + set -e +fi + +# Remove obsolete files from the EFI system partition +for fn in "@efiSysMountPoint@/efi/nixos/"*; do + if ! test "${filesCopied[$fn]}" = 1; then + rm -vf -- "$fn" + fi +done diff --git a/modules/installer/efi-boot-stub/efi-boot-stub.nix b/modules/installer/efi-boot-stub/efi-boot-stub.nix new file mode 100644 index 000000000000..eeabd55c7292 --- /dev/null +++ b/modules/installer/efi-boot-stub/efi-boot-stub.nix @@ -0,0 +1,98 @@ +{pkgs, config, ...}: + +###### interface +let + inherit (pkgs.lib) mkOption mkIf; + + options = { + boot = { + loader = { + efiBootStub = { + + enable = mkOption { + default = false; + description = '' + Whether to use the linux kernel as an EFI bootloader. + When enabled, the kernel, initrd, and an EFI shell script + to boot the system are copied to the EFI system partition. + ''; + }; + + efiDisk = mkOption { + default = "/dev/sda"; + description = '' + The disk that contains the EFI system partition. Only used by + efibootmgr + ''; + }; + + efiPartition = mkOption { + default = "1"; + description = '' + The partition number of the EFI system partition. Only used by + efibootmgr + ''; + }; + + efiSysMountPoint = mkOption { + default = "/boot"; + description = '' + Where the EFI System Partition is mounted. + ''; + }; + + runEfibootmgr = mkOption { + default = false; + description = '' + Whether to run efibootmgr to add the configuration to the boot options list. + WARNING! efibootmgr has been rumored to brick Apple firmware! Use 'bless' on + Apple efi systems. + ''; + }; + + installStartupNsh = mkOption { + default = false; + description = '' + Whether to install a startup.nsh in the root of the EFI system partition. + For now, it will just boot the latest version when run, the eventual goal + is to have a basic menu-type interface. + ''; + }; + + }; + }; + }; + }; + +in + +###### implementation +let + efiBootStubBuilder = pkgs.substituteAll { + src = ./efi-boot-stub-builder.sh; + isExecutable = true; + inherit (pkgs) bash; + path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep] ++ (pkgs.stdenv.lib.optionals config.boot.loader.efiBootStub.runEfibootmgr [pkgs.efibootmgr pkgs.module_init_tools]); + inherit (config.boot.loader.efiBootStub) efiSysMountPoint runEfibootmgr installStartupNsh efiDisk efiPartition; + kernelFile = platform.kernelTarget; + }; + + # Temporary check, for nixos to cope both with nixpkgs stdenv-updates and trunk + platform = pkgs.stdenv.platform; +in +{ + require = [ + options + + # config.system.build + # ../system/system-options.nix + ]; + + system = mkIf config.boot.loader.efiBootStub.enable { + build = { + menuBuilder = efiBootStubBuilder; + }; + boot.loader.id = "efiBootStub"; + boot.loader.kernelFile = platform.kernelTarget; + }; +} diff --git a/modules/module-list.nix b/modules/module-list.nix index 668841d3da27..fa234f552902 100644 --- a/modules/module-list.nix +++ b/modules/module-list.nix @@ -22,6 +22,7 @@ ./hardware/network/rtl8192c.nix ./hardware/pcmcia.nix ./hardware/all-firmware.nix + ./installer/efi-boot-stub/efi-boot-stub.nix ./installer/generations-dir/generations-dir.nix ./installer/grub/grub.nix ./installer/init-script/init-script.nix diff --git a/modules/system/activation/switch-to-configuration.sh b/modules/system/activation/switch-to-configuration.sh index d24bf4848246..c09fa89083bd 100644 --- a/modules/system/activation/switch-to-configuration.sh +++ b/modules/system/activation/switch-to-configuration.sh @@ -50,6 +50,8 @@ if [ "$action" = "switch" -o "$action" = "boot" ]; then elif [ "@bootLoader@" = "generationsDir" ]; then @menuBuilder@ @out@ + elif [ "@bootLoader@" = "efiBootStub" ]; then + @menuBuilder@ @out@ else echo "Warning: don't know how to make this configuration bootable; please enable a boot loader." 1>&2 fi