diff options
Diffstat (limited to 'nixos/modules/system/boot/loader')
20 files changed, 0 insertions, 3346 deletions
diff --git a/nixos/modules/system/boot/loader/efi.nix b/nixos/modules/system/boot/loader/efi.nix deleted file mode 100644 index 6043c904c45..00000000000 --- a/nixos/modules/system/boot/loader/efi.nix +++ /dev/null @@ -1,20 +0,0 @@ -{ lib, ... }: - -with lib; - -{ - options.boot.loader.efi = { - - canTouchEfiVariables = mkOption { - default = false; - type = types.bool; - description = "Whether the installation process is allowed to modify EFI boot variables."; - }; - - efiSysMountPoint = mkOption { - default = "/boot"; - type = types.str; - description = "Where the EFI System Partition is mounted."; - }; - }; -} diff --git a/nixos/modules/system/boot/loader/generations-dir/generations-dir-builder.sh b/nixos/modules/system/boot/loader/generations-dir/generations-dir-builder.sh deleted file mode 100644 index 8ae23dc988c..00000000000 --- a/nixos/modules/system/boot/loader/generations-dir/generations-dir-builder.sh +++ /dev/null @@ -1,106 +0,0 @@ -#! @bash@/bin/sh -e - -shopt -s nullglob - -export PATH=/empty -for i in @path@; do PATH=$PATH:$i/bin; done - -default=$1 -if test -z "$1"; then - echo "Syntax: generations-dir-builder.sh <DEFAULT-CONFIG>" - exit 1 -fi - -echo "updating the boot generations directory..." - -mkdir -p /boot - -rm -Rf /boot/system* || true - -target=/boot/grub/menu.lst -tmp=$target.tmp - -# Convert a path to a file in the Nix store such as -# /nix/store/<hash>-<name>/file to <hash>-<name>-<file>. -cleanName() { - local path="$1" - echo "$path" | sed 's|^/nix/store/||' | sed 's|/|-|g' -} - -# Copy a file from the Nix store to /boot/kernels. -declare -A filesCopied - -copyToKernelsDir() { - local src="$1" - local dst="/boot/kernels/$(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 and initrd to /boot/kernels. -addEntry() { - local path="$1" - local generation="$2" - local outdir=/boot/system-$generation - - if ! test -e $path/kernel -a -e $path/initrd; then - return - fi - - local kernel=$(readlink -f $path/kernel) - local initrd=$(readlink -f $path/initrd) - - if test -n "@copyKernels@"; then - copyToKernelsDir $kernel; kernel=$result - copyToKernelsDir $initrd; initrd=$result - fi - - mkdir -p $outdir - ln -sf $(readlink -f $path) $outdir/system - ln -sf $(readlink -f $path/init) $outdir/init - ln -sf $initrd $outdir/initrd - ln -sf $kernel $outdir/kernel - - if test $(readlink -f "$path") = "$default"; then - cp "$kernel" /boot/nixos-kernel - cp "$initrd" /boot/nixos-initrd - cp "$(readlink -f "$path/init")" /boot/nixos-init - - mkdir -p /boot/default - # ln -sfT: overrides target even if it exists. - ln -sfT $(readlink -f $path) /boot/default/system - ln -sfT $(readlink -f $path/init) /boot/default/init - ln -sfT $initrd /boot/default/initrd - ln -sfT $kernel /boot/default/kernel - fi -} - -if test -n "@copyKernels@"; then - mkdir -p /boot/kernels -fi - -# Add all generations of the system profile to the menu, 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 - -# Remove obsolete files from /boot/kernels. -for fn in /boot/kernels/*; do - if ! test "${filesCopied[$fn]}" = 1; then - rm -vf -- "$fn" - fi -done diff --git a/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix b/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix deleted file mode 100644 index 1437ab38770..00000000000 --- a/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix +++ /dev/null @@ -1,62 +0,0 @@ -{ config, lib, pkgs, ... }: - -with lib; - -let - - generationsDirBuilder = pkgs.substituteAll { - src = ./generations-dir-builder.sh; - isExecutable = true; - inherit (pkgs) bash; - path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep]; - inherit (config.boot.loader.generationsDir) copyKernels; - }; - -in - -{ - options = { - - boot.loader.generationsDir = { - - enable = mkOption { - default = false; - type = types.bool; - description = '' - Whether to create symlinks to the system generations under - <literal>/boot</literal>. When enabled, - <literal>/boot/default/kernel</literal>, - <literal>/boot/default/initrd</literal>, etc., are updated to - point to the current generation's kernel image, initial RAM - disk, and other bootstrap files. - - This optional is not necessary with boot loaders such as GNU GRUB - for which the menu is updated to point to the latest bootstrap - files. However, it is needed for U-Boot on platforms where the - boot command line is stored in flash memory rather than in a - menu file. - ''; - }; - - copyKernels = mkOption { - default = false; - type = types.bool; - description = '' - Whether copy the necessary boot files into /boot, so - /nix/store is not needed by the boot loader. - ''; - }; - - }; - - }; - - - config = mkIf config.boot.loader.generationsDir.enable { - - system.build.installBootLoader = generationsDirBuilder; - system.boot.loader.id = "generationsDir"; - system.boot.loader.kernelFile = pkgs.stdenv.hostPlatform.linux-kernel.target; - - }; -} diff --git a/nixos/modules/system/boot/loader/generic-extlinux-compatible/default.nix b/nixos/modules/system/boot/loader/generic-extlinux-compatible/default.nix deleted file mode 100644 index 545b594674f..00000000000 --- a/nixos/modules/system/boot/loader/generic-extlinux-compatible/default.nix +++ /dev/null @@ -1,82 +0,0 @@ -{ config, lib, pkgs, ... }: - -with lib; - -let - blCfg = config.boot.loader; - dtCfg = config.hardware.deviceTree; - cfg = blCfg.generic-extlinux-compatible; - - timeoutStr = if blCfg.timeout == null then "-1" else toString blCfg.timeout; - - # The builder used to write during system activation - builder = import ./extlinux-conf-builder.nix { inherit pkgs; }; - # The builder exposed in populateCmd, which runs on the build architecture - populateBuilder = import ./extlinux-conf-builder.nix { pkgs = pkgs.buildPackages; }; -in -{ - options = { - boot.loader.generic-extlinux-compatible = { - enable = mkOption { - default = false; - type = types.bool; - description = '' - Whether to generate an extlinux-compatible configuration file - under <literal>/boot/extlinux.conf</literal>. For instance, - U-Boot's generic distro boot support uses this file format. - - See <link xlink:href="http://git.denx.de/?p=u-boot.git;a=blob;f=doc/README.distro;hb=refs/heads/master">U-boot's documentation</link> - for more information. - ''; - }; - - useGenerationDeviceTree = mkOption { - default = true; - type = types.bool; - description = '' - Whether to generate Device Tree-related directives in the - extlinux configuration. - - When enabled, the bootloader will attempt to load the device - tree binaries from the generation's kernel. - - Note that this affects all generations, regardless of the - setting value used in their configurations. - ''; - }; - - configurationLimit = mkOption { - default = 20; - example = 10; - type = types.int; - description = '' - Maximum number of configurations in the boot menu. - ''; - }; - - populateCmd = mkOption { - type = types.str; - readOnly = true; - description = '' - Contains the builder command used to populate an image, - honoring all options except the <literal>-c <path-to-default-configuration></literal> - argument. - Useful to have for sdImage.populateRootCommands - ''; - }; - - }; - }; - - config = let - builderArgs = "-g ${toString cfg.configurationLimit} -t ${timeoutStr}" - + lib.optionalString (dtCfg.name != null) " -n ${dtCfg.name}" - + lib.optionalString (!cfg.useGenerationDeviceTree) " -r"; - in - mkIf cfg.enable { - system.build.installBootLoader = "${builder} ${builderArgs} -c"; - system.boot.loader.id = "generic-extlinux-compatible"; - - boot.loader.generic-extlinux-compatible.populateCmd = "${populateBuilder} ${builderArgs}"; - }; -} diff --git a/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.nix b/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.nix deleted file mode 100644 index 576a07c1d27..00000000000 --- a/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.nix +++ /dev/null @@ -1,8 +0,0 @@ -{ pkgs }: - -pkgs.substituteAll { - src = ./extlinux-conf-builder.sh; - isExecutable = true; - path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep]; - inherit (pkgs) bash; -} diff --git a/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.sh b/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.sh deleted file mode 100644 index 1a0da005029..00000000000 --- a/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.sh +++ /dev/null @@ -1,157 +0,0 @@ -#! @bash@/bin/sh -e - -shopt -s nullglob - -export PATH=/empty -for i in @path@; do PATH=$PATH:$i/bin; done - -usage() { - echo "usage: $0 -t <timeout> -c <path-to-default-configuration> [-d <boot-dir>] [-g <num-generations>] [-n <dtbName>] [-r]" >&2 - exit 1 -} - -timeout= # Timeout in centiseconds -default= # Default configuration -target=/boot # Target directory -numGenerations=0 # Number of other generations to include in the menu - -while getopts "t:c:d:g:n:r" opt; do - case "$opt" in - t) # U-Boot interprets '0' as infinite and negative as instant boot - if [ "$OPTARG" -lt 0 ]; then - timeout=0 - elif [ "$OPTARG" = 0 ]; then - timeout=-10 - else - timeout=$((OPTARG * 10)) - fi - ;; - c) default="$OPTARG" ;; - d) target="$OPTARG" ;; - g) numGenerations="$OPTARG" ;; - n) dtbName="$OPTARG" ;; - r) noDeviceTree=1 ;; - \?) usage ;; - esac -done - -[ "$timeout" = "" -o "$default" = "" ] && usage - -mkdir -p $target/nixos -mkdir -p $target/extlinux - -# Convert a path to a file in the Nix store such as -# /nix/store/<hash>-<name>/file to <hash>-<name>-<file>. -cleanName() { - local path="$1" - echo "$path" | sed 's|^/nix/store/||' | sed 's|/|-|g' -} - -# Copy a file from the Nix store to $target/nixos. -declare -A filesCopied - -copyToKernelsDir() { - local src=$(readlink -f "$1") - local dst="$target/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 -r $src $dstTmp - mv $dstTmp $dst - fi - filesCopied[$dst]=1 - result=$dst -} - -# Copy its kernel, initrd and dtbs to $target/nixos, and echo out an -# extlinux menu entry -addEntry() { - local path=$(readlink -f "$1") - local tag="$2" # Generation number or 'default' - - if ! test -e $path/kernel -a -e $path/initrd; then - return - fi - - copyToKernelsDir "$path/kernel"; kernel=$result - copyToKernelsDir "$path/initrd"; initrd=$result - dtbDir=$(readlink -m "$path/dtbs") - if [ -e "$dtbDir" ]; then - copyToKernelsDir "$dtbDir"; dtbs=$result - fi - - timestampEpoch=$(stat -L -c '%Z' $path) - - timestamp=$(date "+%Y-%m-%d %H:%M" -d @$timestampEpoch) - nixosLabel="$(cat $path/nixos-version)" - extraParams="$(cat $path/kernel-params)" - - echo - echo "LABEL nixos-$tag" - if [ "$tag" = "default" ]; then - echo " MENU LABEL NixOS - Default" - else - echo " MENU LABEL NixOS - Configuration $tag ($timestamp - $nixosLabel)" - fi - echo " LINUX ../nixos/$(basename $kernel)" - echo " INITRD ../nixos/$(basename $initrd)" - echo " APPEND init=$path/init $extraParams" - - if [ -n "$noDeviceTree" ]; then - return - fi - - if [ -d "$dtbDir" ]; then - # if a dtbName was specified explicitly, use that, else use FDTDIR - if [ -n "$dtbName" ]; then - echo " FDT ../nixos/$(basename $dtbs)/${dtbName}" - else - echo " FDTDIR ../nixos/$(basename $dtbs)" - fi - else - if [ -n "$dtbName" ]; then - echo "Explicitly requested dtbName $dtbName, but there's no FDTDIR - bailing out." >&2 - exit 1 - fi - fi -} - -tmpFile="$target/extlinux/extlinux.conf.tmp.$$" - -cat > $tmpFile <<EOF -# Generated file, all changes will be lost on nixos-rebuild! - -# Change this to e.g. nixos-42 to temporarily boot to an older configuration. -DEFAULT nixos-default - -MENU TITLE ------------------------------------------------------------ -TIMEOUT $timeout -EOF - -addEntry $default default >> $tmpFile - -if [ "$numGenerations" -gt 0 ]; then - # Add up to $numGenerations generations of the system profile to the menu, - # 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 \ - | head -n $numGenerations); do - link=/nix/var/nix/profiles/system-$generation-link - addEntry $link $generation - done >> $tmpFile -fi - -mv -f $tmpFile $target/extlinux/extlinux.conf - -# Remove obsolete files from $target/nixos. -for fn in $target/nixos/*; do - if ! test "${filesCopied[$fn]}" = 1; then - echo "Removing no longer needed boot file: $fn" - chmod +w -- "$fn" - rm -rf -- "$fn" - fi -done diff --git a/nixos/modules/system/boot/loader/grub/grub.nix b/nixos/modules/system/boot/loader/grub/grub.nix deleted file mode 100644 index 8db271f8713..00000000000 --- a/nixos/modules/system/boot/loader/grub/grub.nix +++ /dev/null @@ -1,848 +0,0 @@ -{ config, lib, pkgs, ... }: - -with lib; - -let - - cfg = config.boot.loader.grub; - - efi = config.boot.loader.efi; - - grubPkgs = - # Package set of targeted architecture - if cfg.forcei686 then pkgs.pkgsi686Linux else pkgs; - - realGrub = if cfg.version == 1 then grubPkgs.grub - else if cfg.zfsSupport then grubPkgs.grub2.override { zfsSupport = true; } - else if cfg.trustedBoot.enable - then if cfg.trustedBoot.isHPLaptop - then grubPkgs.trustedGrub-for-HP - else grubPkgs.trustedGrub - else grubPkgs.grub2; - - grub = - # Don't include GRUB if we're only generating a GRUB menu (e.g., - # in EC2 instances). - if cfg.devices == ["nodev"] - then null - else realGrub; - - grubEfi = - # EFI version of Grub v2 - if cfg.efiSupport && (cfg.version == 2) - then realGrub.override { efiSupport = cfg.efiSupport; } - else null; - - f = x: if x == null then "" else "" + x; - - grubConfig = args: - let - efiSysMountPoint = if args.efiSysMountPoint == null then args.path else args.efiSysMountPoint; - efiSysMountPoint' = replaceChars [ "/" ] [ "-" ] efiSysMountPoint; - in - pkgs.writeText "grub-config.xml" (builtins.toXML - { splashImage = f cfg.splashImage; - splashMode = f cfg.splashMode; - backgroundColor = f cfg.backgroundColor; - grub = f grub; - grubTarget = f (grub.grubTarget or ""); - shell = "${pkgs.runtimeShell}"; - fullName = lib.getName realGrub; - fullVersion = lib.getVersion realGrub; - grubEfi = f grubEfi; - grubTargetEfi = if cfg.efiSupport && (cfg.version == 2) then f (grubEfi.grubTarget or "") else ""; - bootPath = args.path; - storePath = config.boot.loader.grub.storePath; - bootloaderId = if args.efiBootloaderId == null then "NixOS${efiSysMountPoint'}" else args.efiBootloaderId; - timeout = if config.boot.loader.timeout == null then -1 else config.boot.loader.timeout; - users = if cfg.users == {} || cfg.version != 1 then cfg.users else throw "GRUB version 1 does not support user accounts."; - theme = f cfg.theme; - inherit efiSysMountPoint; - inherit (args) devices; - inherit (efi) canTouchEfiVariables; - inherit (cfg) - version extraConfig extraPerEntryConfig extraEntries forceInstall useOSProber - extraGrubInstallArgs - extraEntriesBeforeNixOS extraPrepareConfig configurationLimit copyKernels - default fsIdentifier efiSupport efiInstallAsRemovable gfxmodeEfi gfxmodeBios gfxpayloadEfi gfxpayloadBios; - path = with pkgs; makeBinPath ( - [ coreutils gnused gnugrep findutils diffutils btrfs-progs util-linux mdadm ] - ++ optional (cfg.efiSupport && (cfg.version == 2)) efibootmgr - ++ optionals cfg.useOSProber [ busybox os-prober ]); - font = if cfg.font == null then "" - else (if lib.last (lib.splitString "." cfg.font) == "pf2" - then cfg.font - else "${convertedFont}"); - }); - - bootDeviceCounters = foldr (device: attr: attr // { ${device} = (attr.${device} or 0) + 1; }) {} - (concatMap (args: args.devices) cfg.mirroredBoots); - - convertedFont = (pkgs.runCommand "grub-font-converted.pf2" {} - (builtins.concatStringsSep " " - ([ "${realGrub}/bin/grub-mkfont" - cfg.font - "--output" "$out" - ] ++ (optional (cfg.fontSize!=null) "--size ${toString cfg.fontSize}"))) - ); - - defaultSplash = pkgs.nixos-artwork.wallpapers.simple-dark-gray-bootloader.gnomeFilePath; -in - -{ - - ###### interface - - options = { - - boot.loader.grub = { - - enable = mkOption { - default = !config.boot.isContainer; - defaultText = literalExpression "!config.boot.isContainer"; - type = types.bool; - description = '' - Whether to enable the GNU GRUB boot loader. - ''; - }; - - version = mkOption { - default = 2; - example = 1; - type = types.int; - description = '' - The version of GRUB to use: <literal>1</literal> for GRUB - Legacy (versions 0.9x), or <literal>2</literal> (the - default) for GRUB 2. - ''; - }; - - device = mkOption { - default = ""; - example = "/dev/disk/by-id/wwn-0x500001234567890a"; - type = types.str; - description = '' - The device on which the GRUB boot loader will be installed. - The special value <literal>nodev</literal> means that a GRUB - boot menu will be generated, but GRUB itself will not - actually be installed. To install GRUB on multiple devices, - use <literal>boot.loader.grub.devices</literal>. - ''; - }; - - devices = mkOption { - default = []; - example = [ "/dev/disk/by-id/wwn-0x500001234567890a" ]; - type = types.listOf types.str; - description = '' - The devices on which the boot loader, GRUB, will be - installed. Can be used instead of <literal>device</literal> to - install GRUB onto multiple devices. - ''; - }; - - users = mkOption { - default = {}; - example = { - root = { hashedPasswordFile = "/path/to/file"; }; - }; - description = '' - User accounts for GRUB. When specified, the GRUB command line and - all boot options except the default are password-protected. - All passwords and hashes provided will be stored in /boot/grub/grub.cfg, - and will be visible to any local user who can read this file. Additionally, - any passwords and hashes provided directly in a Nix configuration - (as opposed to external files) will be copied into the Nix store, and - will be visible to all local users. - ''; - type = with types; attrsOf (submodule { - options = { - hashedPasswordFile = mkOption { - example = "/path/to/file"; - default = null; - type = with types; uniq (nullOr str); - description = '' - Specifies the path to a file containing the password hash - for the account, generated with grub-mkpasswd-pbkdf2. - This hash will be stored in /boot/grub/grub.cfg, and will - be visible to any local user who can read this file. - ''; - }; - hashedPassword = mkOption { - example = "grub.pbkdf2.sha512.10000.674DFFDEF76E13EA...2CC972B102CF4355"; - default = null; - type = with types; uniq (nullOr str); - description = '' - Specifies the password hash for the account, - generated with grub-mkpasswd-pbkdf2. - This hash will be copied to the Nix store, and will be visible to all local users. - ''; - }; - passwordFile = mkOption { - example = "/path/to/file"; - default = null; - type = with types; uniq (nullOr str); - description = '' - Specifies the path to a file containing the - clear text password for the account. - This password will be stored in /boot/grub/grub.cfg, and will - be visible to any local user who can read this file. - ''; - }; - password = mkOption { - example = "Pa$$w0rd!"; - default = null; - type = with types; uniq (nullOr str); - description = '' - Specifies the clear text password for the account. - This password will be copied to the Nix store, and will be visible to all local users. - ''; - }; - }; - }); - }; - - mirroredBoots = mkOption { - default = [ ]; - example = [ - { path = "/boot1"; devices = [ "/dev/disk/by-id/wwn-0x500001234567890a" ]; } - { path = "/boot2"; devices = [ "/dev/disk/by-id/wwn-0x500009876543210a" ]; } - ]; - description = '' - Mirror the boot configuration to multiple partitions and install grub - to the respective devices corresponding to those partitions. - ''; - - type = with types; listOf (submodule { - options = { - - path = mkOption { - example = "/boot1"; - type = types.str; - description = '' - The path to the boot directory where GRUB will be written. Generally - this boot path should double as an EFI path. - ''; - }; - - efiSysMountPoint = mkOption { - default = null; - example = "/boot1/efi"; - type = types.nullOr types.str; - description = '' - The path to the efi system mount point. Usually this is the same - partition as the above path and can be left as null. - ''; - }; - - efiBootloaderId = mkOption { - default = null; - example = "NixOS-fsid"; - type = types.nullOr types.str; - description = '' - The id of the bootloader to store in efi nvram. - The default is to name it NixOS and append the path or efiSysMountPoint. - This is only used if <literal>boot.loader.efi.canTouchEfiVariables</literal> is true. - ''; - }; - - devices = mkOption { - default = [ ]; - example = [ "/dev/disk/by-id/wwn-0x500001234567890a" "/dev/disk/by-id/wwn-0x500009876543210a" ]; - type = types.listOf types.str; - description = '' - The path to the devices which will have the GRUB MBR written. - Note these are typically device paths and not paths to partitions. - ''; - }; - - }; - }); - }; - - configurationName = mkOption { - default = ""; - example = "Stable 2.6.21"; - type = types.str; - description = '' - GRUB entry name instead of default. - ''; - }; - - storePath = mkOption { - default = "/nix/store"; - type = types.str; - description = '' - Path to the Nix store when looking for kernels at boot. - Only makes sense when copyKernels is false. - ''; - }; - - extraPrepareConfig = mkOption { - default = ""; - type = types.lines; - description = '' - Additional bash commands to be run at the script that - prepares the GRUB menu entries. - ''; - }; - - extraConfig = mkOption { - default = ""; - example = '' - serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1 - terminal_input --append serial - terminal_output --append serial - ''; - type = types.lines; - description = '' - Additional GRUB commands inserted in the configuration file - just before the menu entries. - ''; - }; - - extraGrubInstallArgs = mkOption { - default = [ ]; - example = [ "--modules=nativedisk ahci pata part_gpt part_msdos diskfilter mdraid1x lvm ext2" ]; - type = types.listOf types.str; - description = '' - Additional arguments passed to <literal>grub-install</literal>. - - A use case for this is to build specific GRUB2 modules - directly into the GRUB2 kernel image, so that they are available - and activated even in the <literal>grub rescue</literal> shell. - - They are also necessary when the BIOS/UEFI is bugged and cannot - correctly read large disks (e.g. above 2 TB), so GRUB2's own - <literal>nativedisk</literal> and related modules can be used - to use its own disk drivers. The example shows one such case. - This is also useful for booting from USB. - See the - <link xlink:href="http://git.savannah.gnu.org/cgit/grub.git/tree/grub-core/commands/nativedisk.c?h=grub-2.04#n326"> - GRUB source code - </link> - for which disk modules are available. - - The list elements are passed directly as <literal>argv</literal> - arguments to the <literal>grub-install</literal> program, in order. - ''; - }; - - extraInstallCommands = mkOption { - default = ""; - example = '' - # the example below generates detached signatures that GRUB can verify - # https://www.gnu.org/software/grub/manual/grub/grub.html#Using-digital-signatures - ''${pkgs.findutils}/bin/find /boot -not -path "/boot/efi/*" -type f -name '*.sig' -delete - old_gpg_home=$GNUPGHOME - export GNUPGHOME="$(mktemp -d)" - ''${pkgs.gnupg}/bin/gpg --import ''${priv_key} > /dev/null 2>&1 - ''${pkgs.findutils}/bin/find /boot -not -path "/boot/efi/*" -type f -exec ''${pkgs.gnupg}/bin/gpg --detach-sign "{}" \; > /dev/null 2>&1 - rm -rf $GNUPGHOME - export GNUPGHOME=$old_gpg_home - ''; - type = types.lines; - description = '' - Additional shell commands inserted in the bootloader installer - script after generating menu entries. - ''; - }; - - extraPerEntryConfig = mkOption { - default = ""; - example = "root (hd0)"; - type = types.lines; - description = '' - Additional GRUB commands inserted in the configuration file - at the start of each NixOS menu entry. - ''; - }; - - extraEntries = mkOption { - default = ""; - type = types.lines; - example = '' - # GRUB 1 example (not GRUB 2 compatible) - title Windows - chainloader (hd0,1)+1 - - # GRUB 2 example - menuentry "Windows 7" { - chainloader (hd0,4)+1 - } - - # GRUB 2 with UEFI example, chainloading another distro - menuentry "Fedora" { - set root=(hd1,1) - chainloader /efi/fedora/grubx64.efi - } - ''; - description = '' - Any additional entries you want added to the GRUB boot menu. - ''; - }; - - extraEntriesBeforeNixOS = mkOption { - default = false; - type = types.bool; - description = '' - Whether extraEntries are included before the default option. - ''; - }; - - extraFiles = mkOption { - type = types.attrsOf types.path; - default = {}; - example = literalExpression '' - { "memtest.bin" = "''${pkgs.memtest86plus}/memtest.bin"; } - ''; - description = '' - A set of files to be copied to <filename>/boot</filename>. - Each attribute name denotes the destination file name in - <filename>/boot</filename>, while the corresponding - attribute value specifies the source file. - ''; - }; - - useOSProber = mkOption { - default = false; - type = types.bool; - description = '' - If set to true, append entries for other OSs detected by os-prober. - ''; - }; - - splashImage = mkOption { - type = types.nullOr types.path; - example = literalExpression "./my-background.png"; - description = '' - Background image used for GRUB. - Set to <literal>null</literal> to run GRUB in text mode. - - <note><para> - For grub 1: - It must be a 640x480, - 14-colour image in XPM format, optionally compressed with - <command>gzip</command> or <command>bzip2</command>. - </para></note> - - <note><para> - For grub 2: - File must be one of .png, .tga, .jpg, or .jpeg. JPEG images must - not be progressive. - The image will be scaled if necessary to fit the screen. - </para></note> - ''; - }; - - backgroundColor = mkOption { - type = types.nullOr types.str; - example = "#7EBAE4"; - default = null; - description = '' - Background color to be used for GRUB to fill the areas the image isn't filling. - - <note><para> - This options has no effect for GRUB 1. - </para></note> - ''; - }; - - theme = mkOption { - type = types.nullOr types.path; - example = literalExpression "pkgs.nixos-grub2-theme"; - default = null; - description = '' - Grub theme to be used. - - <note><para> - This options has no effect for GRUB 1. - </para></note> - ''; - }; - - splashMode = mkOption { - type = types.enum [ "normal" "stretch" ]; - default = "stretch"; - description = '' - Whether to stretch the image or show the image in the top-left corner unstretched. - - <note><para> - This options has no effect for GRUB 1. - </para></note> - ''; - }; - - font = mkOption { - type = types.nullOr types.path; - default = "${realGrub}/share/grub/unicode.pf2"; - defaultText = literalExpression ''"''${pkgs.grub2}/share/grub/unicode.pf2"''; - description = '' - Path to a TrueType, OpenType, or pf2 font to be used by Grub. - ''; - }; - - fontSize = mkOption { - type = types.nullOr types.int; - example = 16; - default = null; - description = '' - Font size for the grub menu. Ignored unless <literal>font</literal> - is set to a ttf or otf font. - ''; - }; - - gfxmodeEfi = mkOption { - default = "auto"; - example = "1024x768"; - type = types.str; - description = '' - The gfxmode to pass to GRUB when loading a graphical boot interface under EFI. - ''; - }; - - gfxmodeBios = mkOption { - default = "1024x768"; - example = "auto"; - type = types.str; - description = '' - The gfxmode to pass to GRUB when loading a graphical boot interface under BIOS. - ''; - }; - - gfxpayloadEfi = mkOption { - default = "keep"; - example = "text"; - type = types.str; - description = '' - The gfxpayload to pass to GRUB when loading a graphical boot interface under EFI. - ''; - }; - - gfxpayloadBios = mkOption { - default = "text"; - example = "keep"; - type = types.str; - description = '' - The gfxpayload to pass to GRUB when loading a graphical boot interface under BIOS. - ''; - }; - - configurationLimit = mkOption { - default = 100; - example = 120; - type = types.int; - description = '' - Maximum of configurations in boot menu. GRUB has problems when - there are too many entries. - ''; - }; - - copyKernels = mkOption { - default = false; - type = types.bool; - description = '' - Whether the GRUB menu builder should copy kernels and initial - ramdisks to /boot. This is done automatically if /boot is - on a different partition than /. - ''; - }; - - default = mkOption { - default = "0"; - type = types.either types.int types.str; - apply = toString; - description = '' - Index of the default menu item to be booted. - Can also be set to "saved", which will make GRUB select - the menu item that was used at the last boot. - ''; - }; - - fsIdentifier = mkOption { - default = "uuid"; - type = types.enum [ "uuid" "label" "provided" ]; - description = '' - Determines how GRUB will identify devices when generating the - configuration file. A value of uuid / label signifies that grub - will always resolve the uuid or label of the device before using - it in the configuration. A value of provided means that GRUB will - use the device name as show in <command>df</command> or - <command>mount</command>. Note, zfs zpools / datasets are ignored - and will always be mounted using their labels. - ''; - }; - - zfsSupport = mkOption { - default = false; - type = types.bool; - description = '' - Whether GRUB should be built against libzfs. - ZFS support is only available for GRUB v2. - This option is ignored for GRUB v1. - ''; - }; - - efiSupport = mkOption { - default = false; - type = types.bool; - description = '' - Whether GRUB should be built with EFI support. - EFI support is only available for GRUB v2. - This option is ignored for GRUB v1. - ''; - }; - - efiInstallAsRemovable = mkOption { - default = false; - type = types.bool; - description = '' - Whether to invoke <literal>grub-install</literal> with - <literal>--removable</literal>.</para> - - <para>Unless you turn this on, GRUB will install itself somewhere in - <literal>boot.loader.efi.efiSysMountPoint</literal> (exactly where - depends on other config variables). If you've set - <literal>boot.loader.efi.canTouchEfiVariables</literal> *AND* you - are currently booted in UEFI mode, then GRUB will use - <literal>efibootmgr</literal> to modify the boot order in the - EFI variables of your firmware to include this location. If you are - *not* booted in UEFI mode at the time GRUB is being installed, the - NVRAM will not be modified, and your system will not find GRUB at - boot time. However, GRUB will still return success so you may miss - the warning that gets printed ("<literal>efibootmgr: EFI variables - are not supported on this system.</literal>").</para> - - <para>If you turn this feature on, GRUB will install itself in a - special location within <literal>efiSysMountPoint</literal> (namely - <literal>EFI/boot/boot$arch.efi</literal>) which the firmwares - are hardcoded to try first, regardless of NVRAM EFI variables.</para> - - <para>To summarize, turn this on if: - <itemizedlist> - <listitem><para>You are installing NixOS and want it to boot in UEFI mode, - but you are currently booted in legacy mode</para></listitem> - <listitem><para>You want to make a drive that will boot regardless of - the NVRAM state of the computer (like a USB "removable" drive)</para></listitem> - <listitem><para>You simply dislike the idea of depending on NVRAM - state to make your drive bootable</para></listitem> - </itemizedlist> - ''; - }; - - enableCryptodisk = mkOption { - default = false; - type = types.bool; - description = '' - Enable support for encrypted partitions. GRUB should automatically - unlock the correct encrypted partition and look for filesystems. - ''; - }; - - forceInstall = mkOption { - default = false; - type = types.bool; - description = '' - Whether to try and forcibly install GRUB even if problems are - detected. It is not recommended to enable this unless you know what - you are doing. - ''; - }; - - forcei686 = mkOption { - default = false; - type = types.bool; - description = '' - Whether to force the use of a ia32 boot loader on x64 systems. Required - to install and run NixOS on 64bit x86 systems with 32bit (U)EFI. - ''; - }; - - trustedBoot = { - - enable = mkOption { - default = false; - type = types.bool; - description = '' - Enable trusted boot. GRUB will measure all critical components during - the boot process to offer TCG (TPM) support. - ''; - }; - - systemHasTPM = mkOption { - default = ""; - example = "YES_TPM_is_activated"; - type = types.str; - description = '' - Assertion that the target system has an activated TPM. It is a safety - check before allowing the activation of 'trustedBoot.enable'. TrustedBoot - WILL FAIL TO BOOT YOUR SYSTEM if no TPM is available. - ''; - }; - - isHPLaptop = mkOption { - default = false; - type = types.bool; - description = '' - Use a special version of TrustedGRUB that is needed by some HP laptops - and works only for the HP laptops. - ''; - }; - - }; - - }; - - }; - - - ###### implementation - - config = mkMerge [ - - { boot.loader.grub.splashImage = mkDefault ( - if cfg.version == 1 then pkgs.fetchurl { - url = "http://www.gnome-look.org/CONTENT/content-files/36909-soft-tux.xpm.gz"; - sha256 = "14kqdx2lfqvh40h6fjjzqgff1mwk74dmbjvmqphi6azzra7z8d59"; - } - # GRUB 1.97 doesn't support gzipped XPMs. - else defaultSplash); - } - - (mkIf (cfg.splashImage == defaultSplash) { - boot.loader.grub.backgroundColor = mkDefault "#2F302F"; - boot.loader.grub.splashMode = mkDefault "normal"; - }) - - (mkIf cfg.enable { - - boot.loader.grub.devices = optional (cfg.device != "") cfg.device; - - boot.loader.grub.mirroredBoots = optionals (cfg.devices != [ ]) [ - { path = "/boot"; inherit (cfg) devices; inherit (efi) efiSysMountPoint; } - ]; - - boot.loader.supportsInitrdSecrets = true; - - system.build.installBootLoader = - let - install-grub-pl = pkgs.substituteAll { - src = ./install-grub.pl; - utillinux = pkgs.util-linux; - btrfsprogs = pkgs.btrfs-progs; - }; - perl = pkgs.perl.withPackages (p: with p; [ - FileSlurp FileCopyRecursive - XMLLibXML XMLSAX XMLSAXBase - ListCompare JSON - ]); - in pkgs.writeScript "install-grub.sh" ('' - #!${pkgs.runtimeShell} - set -e - ${optionalString cfg.enableCryptodisk "export GRUB_ENABLE_CRYPTODISK=y"} - '' + flip concatMapStrings cfg.mirroredBoots (args: '' - ${perl}/bin/perl ${install-grub-pl} ${grubConfig args} $@ - '') + cfg.extraInstallCommands); - - system.build.grub = grub; - - # Common attribute for boot loaders so only one of them can be - # set at once. - system.boot.loader.id = "grub"; - - environment.systemPackages = optional (grub != null) grub; - - boot.loader.grub.extraPrepareConfig = - concatStrings (mapAttrsToList (n: v: '' - ${pkgs.coreutils}/bin/cp -pf "${v}" "@bootPath@/${n}" - '') config.boot.loader.grub.extraFiles); - - assertions = [ - { - assertion = !cfg.zfsSupport || cfg.version == 2; - message = "Only GRUB version 2 provides ZFS support"; - } - { - assertion = cfg.mirroredBoots != [ ]; - message = "You must set the option ‘boot.loader.grub.devices’ or " - + "'boot.loader.grub.mirroredBoots' to make the system bootable."; - } - { - assertion = cfg.efiSupport || all (c: c < 2) (mapAttrsToList (n: c: if n == "nodev" then 0 else c) bootDeviceCounters); - message = "You cannot have duplicated devices in mirroredBoots"; - } - { - assertion = !cfg.trustedBoot.enable || cfg.version == 2; - message = "Trusted GRUB is only available for GRUB 2"; - } - { - assertion = !cfg.efiSupport || !cfg.trustedBoot.enable; - message = "Trusted GRUB does not have EFI support"; - } - { - assertion = !cfg.zfsSupport || !cfg.trustedBoot.enable; - message = "Trusted GRUB does not have ZFS support"; - } - { - assertion = !cfg.trustedBoot.enable || cfg.trustedBoot.systemHasTPM == "YES_TPM_is_activated"; - message = "Trusted GRUB can break the system! Confirm that the system has an activated TPM by setting 'systemHasTPM'."; - } - { - assertion = cfg.efiInstallAsRemovable -> cfg.efiSupport; - message = "If you wish to to use boot.loader.grub.efiInstallAsRemovable, then turn on boot.loader.grub.efiSupport"; - } - { - assertion = cfg.efiInstallAsRemovable -> !config.boot.loader.efi.canTouchEfiVariables; - message = "If you wish to to use boot.loader.grub.efiInstallAsRemovable, then turn off boot.loader.efi.canTouchEfiVariables"; - } - ] ++ flip concatMap cfg.mirroredBoots (args: [ - { - assertion = args.devices != [ ]; - message = "A boot path cannot have an empty devices string in ${args.path}"; - } - { - assertion = hasPrefix "/" args.path; - message = "Boot paths must be absolute, not ${args.path}"; - } - { - assertion = if args.efiSysMountPoint == null then true else hasPrefix "/" args.efiSysMountPoint; - message = "EFI paths must be absolute, not ${args.efiSysMountPoint}"; - } - ] ++ forEach args.devices (device: { - assertion = device == "nodev" || hasPrefix "/" device; - message = "GRUB devices must be absolute paths, not ${device} in ${args.path}"; - })); - }) - - ]; - - - imports = - [ (mkRemovedOptionModule [ "boot" "loader" "grub" "bootDevice" ] "") - (mkRenamedOptionModule [ "boot" "copyKernels" ] [ "boot" "loader" "grub" "copyKernels" ]) - (mkRenamedOptionModule [ "boot" "extraGrubEntries" ] [ "boot" "loader" "grub" "extraEntries" ]) - (mkRenamedOptionModule [ "boot" "extraGrubEntriesBeforeNixos" ] [ "boot" "loader" "grub" "extraEntriesBeforeNixOS" ]) - (mkRenamedOptionModule [ "boot" "grubDevice" ] [ "boot" "loader" "grub" "device" ]) - (mkRenamedOptionModule [ "boot" "bootMount" ] [ "boot" "loader" "grub" "bootDevice" ]) - (mkRenamedOptionModule [ "boot" "grubSplashImage" ] [ "boot" "loader" "grub" "splashImage" ]) - (mkRemovedOptionModule [ "boot" "loader" "grub" "extraInitrd" ] '' - This option has been replaced with the bootloader agnostic - boot.initrd.secrets option. To migrate to the initrd secrets system, - extract the extraInitrd archive into your main filesystem: - - # zcat /boot/extra_initramfs.gz | cpio -idvmD /etc/secrets/initrd - /path/to/secret1 - /path/to/secret2 - - then replace boot.loader.grub.extraInitrd with boot.initrd.secrets: - - boot.initrd.secrets = { - "/path/to/secret1" = "/etc/secrets/initrd/path/to/secret1"; - "/path/to/secret2" = "/etc/secrets/initrd/path/to/secret2"; - }; - - See the boot.initrd.secrets option documentation for more information. - '') - ]; - -} diff --git a/nixos/modules/system/boot/loader/grub/install-grub.pl b/nixos/modules/system/boot/loader/grub/install-grub.pl deleted file mode 100644 index 0c93b288fc6..00000000000 --- a/nixos/modules/system/boot/loader/grub/install-grub.pl +++ /dev/null @@ -1,780 +0,0 @@ -use strict; -use warnings; -use Class::Struct; -use XML::LibXML; -use File::Basename; -use File::Path; -use File::stat; -use File::Copy; -use File::Copy::Recursive qw(rcopy pathrm); -use File::Slurp; -use File::Temp; -use JSON; -use File::Find; -require List::Compare; -use POSIX; -use Cwd; - -# system.build.toplevel path -my $defaultConfig = $ARGV[1] or die; - -# Grub config XML generated by grubConfig function in grub.nix -my $dom = XML::LibXML->load_xml(location => $ARGV[0]); - -sub get { my ($name) = @_; return $dom->findvalue("/expr/attrs/attr[\@name = '$name']/*/\@value"); } - -sub getList { - my ($name) = @_; - my @list = (); - foreach my $entry ($dom->findnodes("/expr/attrs/attr[\@name = '$name']/list/string/\@value")) { - $entry = $entry->findvalue(".") or die; - push(@list, $entry); - } - return @list; -} - -sub readFile { - my ($fn) = @_; local $/ = undef; - open FILE, "<$fn" or return undef; my $s = <FILE>; close FILE; - local $/ = "\n"; chomp $s; return $s; -} - -sub writeFile { - my ($fn, $s) = @_; - open FILE, ">$fn" or die "cannot create $fn: $!\n"; - print FILE $s or die; - close FILE or die; -} - -sub runCommand { - my ($cmd) = @_; - open FILE, "$cmd 2>/dev/null |" or die "Failed to execute: $cmd\n"; - my @ret = <FILE>; - close FILE; - return ($?, @ret); -} - -my $grub = get("grub"); -my $grubVersion = int(get("version")); -my $grubTarget = get("grubTarget"); -my $extraConfig = get("extraConfig"); -my $extraPrepareConfig = get("extraPrepareConfig"); -my $extraPerEntryConfig = get("extraPerEntryConfig"); -my $extraEntries = get("extraEntries"); -my $extraEntriesBeforeNixOS = get("extraEntriesBeforeNixOS") eq "true"; -my $splashImage = get("splashImage"); -my $splashMode = get("splashMode"); -my $backgroundColor = get("backgroundColor"); -my $configurationLimit = int(get("configurationLimit")); -my $copyKernels = get("copyKernels") eq "true"; -my $timeout = int(get("timeout")); -my $defaultEntry = get("default"); -my $fsIdentifier = get("fsIdentifier"); -my $grubEfi = get("grubEfi"); -my $grubTargetEfi = get("grubTargetEfi"); -my $bootPath = get("bootPath"); -my $storePath = get("storePath"); -my $canTouchEfiVariables = get("canTouchEfiVariables"); -my $efiInstallAsRemovable = get("efiInstallAsRemovable"); -my $efiSysMountPoint = get("efiSysMountPoint"); -my $gfxmodeEfi = get("gfxmodeEfi"); -my $gfxmodeBios = get("gfxmodeBios"); -my $gfxpayloadEfi = get("gfxpayloadEfi"); -my $gfxpayloadBios = get("gfxpayloadBios"); -my $bootloaderId = get("bootloaderId"); -my $forceInstall = get("forceInstall"); -my $font = get("font"); -my $theme = get("theme"); -my $saveDefault = $defaultEntry eq "saved"; -$ENV{'PATH'} = get("path"); - -die "unsupported GRUB version\n" if $grubVersion != 1 && $grubVersion != 2; - -print STDERR "updating GRUB $grubVersion menu...\n"; - -mkpath("$bootPath/grub", 0, 0700); - -# Discover whether the bootPath is on the same filesystem as / and -# /nix/store. If not, then all kernels and initrds must be copied to -# the bootPath. -if (stat($bootPath)->dev != stat("/nix/store")->dev) { - $copyKernels = 1; -} - -# Discover information about the location of the bootPath -struct(Fs => { - device => '$', - type => '$', - mount => '$', -}); -sub PathInMount { - my ($path, $mount) = @_; - my @splitMount = split /\//, $mount; - my @splitPath = split /\//, $path; - if ($#splitPath < $#splitMount) { - return 0; - } - for (my $i = 0; $i <= $#splitMount; $i++) { - if ($splitMount[$i] ne $splitPath[$i]) { - return 0; - } - } - return 1; -} - -# Figure out what filesystem is used for the directory with init/initrd/kernel files -sub GetFs { - my ($dir) = @_; - my $bestFs = Fs->new(device => "", type => "", mount => ""); - foreach my $fs (read_file("/proc/self/mountinfo")) { - chomp $fs; - my @fields = split / /, $fs; - my $mountPoint = $fields[4]; - next unless -d $mountPoint; - my @mountOptions = split /,/, $fields[5]; - - # Skip the optional fields. - my $n = 6; $n++ while $fields[$n] ne "-"; $n++; - my $fsType = $fields[$n]; - my $device = $fields[$n + 1]; - my @superOptions = split /,/, $fields[$n + 2]; - - # Skip the bind-mount on /nix/store. - next if $mountPoint eq "/nix/store" && (grep { $_ eq "rw" } @superOptions); - # Skip mount point generated by systemd-efi-boot-generator? - next if $fsType eq "autofs"; - - # Ensure this matches the intended directory - next unless PathInMount($dir, $mountPoint); - - # Is it better than our current match? - if (length($mountPoint) > length($bestFs->mount)) { - $bestFs = Fs->new(device => $device, type => $fsType, mount => $mountPoint); - } - } - return $bestFs; -} -struct (Grub => { - path => '$', - search => '$', -}); -my $driveid = 1; -sub GrubFs { - my ($dir) = @_; - my $fs = GetFs($dir); - my $path = substr($dir, length($fs->mount)); - if (substr($path, 0, 1) ne "/") { - $path = "/$path"; - } - my $search = ""; - - if ($grubVersion > 1) { - # ZFS is completely separate logic as zpools are always identified by a label - # or custom UUID - if ($fs->type eq 'zfs') { - my $sid = index($fs->device, '/'); - - if ($sid < 0) { - $search = '--label ' . $fs->device; - $path = '/@' . $path; - } else { - $search = '--label ' . substr($fs->device, 0, $sid); - $path = '/' . substr($fs->device, $sid) . '/@' . $path; - } - } else { - my %types = ('uuid' => '--fs-uuid', 'label' => '--label'); - - if ($fsIdentifier eq 'provided') { - # If the provided dev is identifying the partition using a label or uuid, - # we should get the label / uuid and do a proper search - my @matches = $fs->device =~ m/\/dev\/disk\/by-(label|uuid)\/(.*)/; - if ($#matches > 1) { - die "Too many matched devices" - } elsif ($#matches == 1) { - $search = "$types{$matches[0]} $matches[1]" - } - } else { - # Determine the identifying type - $search = $types{$fsIdentifier} . ' '; - - # Based on the type pull in the identifier from the system - my ($status, @devInfo) = runCommand("@utillinux@/bin/blkid -o export @{[$fs->device]}"); - if ($status != 0) { - die "Failed to get blkid info (returned $status) for @{[$fs->mount]} on @{[$fs->device]}"; - } - my @matches = join("", @devInfo) =~ m/@{[uc $fsIdentifier]}=([^\n]*)/; - if ($#matches != 0) { - die "Couldn't find a $types{$fsIdentifier} for @{[$fs->device]}\n" - } - $search .= $matches[0]; - } - - # BTRFS is a special case in that we need to fix the referrenced path based on subvolumes - if ($fs->type eq 'btrfs') { - my ($status, @id_info) = runCommand("@btrfsprogs@/bin/btrfs subvol show @{[$fs->mount]}"); - if ($status != 0) { - die "Failed to retrieve subvolume info for @{[$fs->mount]}\n"; - } - my @ids = join("\n", @id_info) =~ m/^(?!\/\n).*Subvolume ID:[ \t\n]*([0-9]+)/s; - if ($#ids > 0) { - die "Btrfs subvol name for @{[$fs->device]} listed multiple times in mount\n" - } elsif ($#ids == 0) { - my ($status, @path_info) = runCommand("@btrfsprogs@/bin/btrfs subvol list @{[$fs->mount]}"); - if ($status != 0) { - die "Failed to find @{[$fs->mount]} subvolume id from btrfs\n"; - } - my @paths = join("", @path_info) =~ m/ID $ids[0] [^\n]* path ([^\n]*)/; - if ($#paths > 0) { - die "Btrfs returned multiple paths for a single subvolume id, mountpoint @{[$fs->mount]}\n"; - } elsif ($#paths != 0) { - die "Btrfs did not return a path for the subvolume at @{[$fs->mount]}\n"; - } - $path = "/$paths[0]$path"; - } - } - } - if (not $search eq "") { - $search = "search --set=drive$driveid " . $search; - $path = "(\$drive$driveid)$path"; - $driveid += 1; - } - } - return Grub->new(path => $path, search => $search); -} -my $grubBoot = GrubFs($bootPath); -my $grubStore; -if ($copyKernels == 0) { - $grubStore = GrubFs($storePath); -} - -# Generate the header. -my $conf .= "# Automatically generated. DO NOT EDIT THIS FILE!\n"; - -if ($grubVersion == 1) { - # $defaultEntry might be "saved", indicating that we want to use the last selected configuration as default. - # Incidentally this is already the correct value for the grub 1 config to achieve this behaviour. - $conf .= " - default $defaultEntry - timeout $timeout - "; - if ($splashImage) { - copy $splashImage, "$bootPath/background.xpm.gz" or die "cannot copy $splashImage to $bootPath: $!\n"; - $conf .= "splashimage " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/background.xpm.gz\n"; - } -} - -else { - my @users = (); - foreach my $user ($dom->findnodes('/expr/attrs/attr[@name = "users"]/attrs/attr')) { - my $name = $user->findvalue('@name') or die; - my $hashedPassword = $user->findvalue('./attrs/attr[@name = "hashedPassword"]/string/@value'); - my $hashedPasswordFile = $user->findvalue('./attrs/attr[@name = "hashedPasswordFile"]/string/@value'); - my $password = $user->findvalue('./attrs/attr[@name = "password"]/string/@value'); - my $passwordFile = $user->findvalue('./attrs/attr[@name = "passwordFile"]/string/@value'); - - if ($hashedPasswordFile) { - open(my $f, '<', $hashedPasswordFile) or die "Can't read file '$hashedPasswordFile'!"; - $hashedPassword = <$f>; - chomp $hashedPassword; - } - if ($passwordFile) { - open(my $f, '<', $passwordFile) or die "Can't read file '$passwordFile'!"; - $password = <$f>; - chomp $password; - } - - if ($hashedPassword) { - if (index($hashedPassword, "grub.pbkdf2.") == 0) { - $conf .= "\npassword_pbkdf2 $name $hashedPassword"; - } - else { - die "Password hash for GRUB user '$name' is not valid!"; - } - } - elsif ($password) { - $conf .= "\npassword $name $password"; - } - else { - die "GRUB user '$name' has no password!"; - } - push(@users, $name); - } - if (@users) { - $conf .= "\nset superusers=\"" . join(' ',@users) . "\"\n"; - } - - if ($copyKernels == 0) { - $conf .= " - " . $grubStore->search; - } - # FIXME: should use grub-mkconfig. - my $defaultEntryText = $defaultEntry; - if ($saveDefault) { - $defaultEntryText = "\"\${saved_entry}\""; - } - $conf .= " - " . $grubBoot->search . " - if [ -s \$prefix/grubenv ]; then - load_env - fi - - # ‘grub-reboot’ sets a one-time saved entry, which we process here and - # then delete. - if [ \"\${next_entry}\" ]; then - set default=\"\${next_entry}\" - set next_entry= - save_env next_entry - set timeout=1 - set boot_once=true - else - set default=$defaultEntryText - set timeout=$timeout - fi - - function savedefault { - if [ -z \"\${boot_once}\"]; then - saved_entry=\"\${chosen}\" - save_env saved_entry - fi - } - - # Setup the graphics stack for bios and efi systems - if [ \"\${grub_platform}\" = \"efi\" ]; then - insmod efi_gop - insmod efi_uga - else - insmod vbe - fi - "; - - if ($font) { - copy $font, "$bootPath/converted-font.pf2" or die "cannot copy $font to $bootPath: $!\n"; - $conf .= " - insmod font - if loadfont " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/converted-font.pf2; then - insmod gfxterm - if [ \"\${grub_platform}\" = \"efi\" ]; then - set gfxmode=$gfxmodeEfi - set gfxpayload=$gfxpayloadEfi - else - set gfxmode=$gfxmodeBios - set gfxpayload=$gfxpayloadBios - fi - terminal_output gfxterm - fi - "; - } - if ($splashImage) { - # Keeps the image's extension. - my ($filename, $dirs, $suffix) = fileparse($splashImage, qr"\..[^.]*$"); - # The module for jpg is jpeg. - if ($suffix eq ".jpg") { - $suffix = ".jpeg"; - } - if ($backgroundColor) { - $conf .= " - background_color '$backgroundColor' - "; - } - copy $splashImage, "$bootPath/background$suffix" or die "cannot copy $splashImage to $bootPath: $!\n"; - $conf .= " - insmod " . substr($suffix, 1) . " - if background_image --mode '$splashMode' " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/background$suffix; 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 - "; - } - - rmtree("$bootPath/theme") or die "cannot clean up theme folder in $bootPath\n" if -e "$bootPath/theme"; - - if ($theme) { - # Copy theme - rcopy($theme, "$bootPath/theme") or die "cannot copy $theme to $bootPath\n"; - $conf .= " - # Sets theme. - set theme=" . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/theme/theme.txt - export theme - # Load theme fonts, if any - "; - - find( { wanted => sub { - if ($_ =~ /\.pf2$/i) { - $font = File::Spec->abs2rel($File::Find::name, $theme); - $conf .= " - loadfont " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/theme/$font - "; - } - }, no_chdir => 1 }, $theme ); - } -} - -$conf .= "$extraConfig\n"; - - -# Generate the menu entries. -$conf .= "\n"; - -my %copied; -mkpath("$bootPath/kernels", 0, 0755) if $copyKernels; - -sub copyToKernelsDir { - my ($path) = @_; - return $grubStore->path . substr($path, length("/nix/store")) unless $copyKernels; - $path =~ /\/nix\/store\/(.*)/ or die; - my $name = $1; $name =~ s/\//-/g; - my $dst = "$bootPath/kernels/$name"; - # 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 (! -e $dst) { - my $tmp = "$dst.tmp"; - copy $path, $tmp or die "cannot copy $path to $tmp: $!\n"; - rename $tmp, $dst or die "cannot rename $tmp to $dst: $!\n"; - } - $copied{$dst} = 1; - return ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/kernels/$name"; -} - -sub addEntry { - my ($name, $path, $options) = @_; - return unless -e "$path/kernel" && -e "$path/initrd"; - - my $kernel = copyToKernelsDir(Cwd::abs_path("$path/kernel")); - my $initrd = copyToKernelsDir(Cwd::abs_path("$path/initrd")); - - # Include second initrd with secrets - if (-e -x "$path/append-initrd-secrets") { - my $initrdName = basename($initrd); - my $initrdSecretsPath = "$bootPath/kernels/$initrdName-secrets"; - - mkpath(dirname($initrdSecretsPath), 0, 0755); - my $oldUmask = umask; - # Make sure initrd is not world readable (won't work if /boot is FAT) - umask 0137; - my $initrdSecretsPathTemp = File::Temp::mktemp("$initrdSecretsPath.XXXXXXXX"); - system("$path/append-initrd-secrets", $initrdSecretsPathTemp) == 0 or die "failed to create initrd secrets: $!\n"; - # Check whether any secrets were actually added - if (-e $initrdSecretsPathTemp && ! -z _) { - rename $initrdSecretsPathTemp, $initrdSecretsPath or die "failed to move initrd secrets into place: $!\n"; - $copied{$initrdSecretsPath} = 1; - $initrd .= " " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/kernels/$initrdName-secrets"; - } else { - unlink $initrdSecretsPathTemp; - rmdir dirname($initrdSecretsPathTemp); - } - umask $oldUmask; - } - - my $xen = -e "$path/xen.gz" ? copyToKernelsDir(Cwd::abs_path("$path/xen.gz")) : undef; - - # FIXME: $confName - - my $kernelParams = - "init=" . Cwd::abs_path("$path/init") . " " . - readFile("$path/kernel-params"); - my $xenParams = $xen && -e "$path/xen-params" ? readFile("$path/xen-params") : ""; - - if ($grubVersion == 1) { - $conf .= "title $name\n"; - $conf .= " $extraPerEntryConfig\n" if $extraPerEntryConfig; - $conf .= " kernel $xen $xenParams\n" if $xen; - $conf .= " " . ($xen ? "module" : "kernel") . " $kernel $kernelParams\n"; - $conf .= " " . ($xen ? "module" : "initrd") . " $initrd\n"; - if ($saveDefault) { - $conf .= " savedefault\n"; - } - $conf .= "\n"; - } else { - $conf .= "menuentry \"$name\" " . ($options||"") . " {\n"; - if ($saveDefault) { - $conf .= " savedefault\n"; - } - $conf .= $grubBoot->search . "\n"; - if ($copyKernels == 0) { - $conf .= $grubStore->search . "\n"; - } - $conf .= " $extraPerEntryConfig\n" if $extraPerEntryConfig; - $conf .= " multiboot $xen $xenParams\n" if $xen; - $conf .= " " . ($xen ? "module" : "linux") . " $kernel $kernelParams\n"; - $conf .= " " . ($xen ? "module" : "initrd") . " $initrd\n"; - $conf .= "}\n\n"; - } -} - - -# Add default entries. -$conf .= "$extraEntries\n" if $extraEntriesBeforeNixOS; - -addEntry("NixOS - Default", $defaultConfig, "--unrestricted"); - -$conf .= "$extraEntries\n" unless $extraEntriesBeforeNixOS; - -# Find all the children of the current default configuration -# Do not search for grand children -my @links = sort (glob "$defaultConfig/specialisation/*"); -foreach my $link (@links) { - - my $entryName = ""; - - my $cfgName = readFile("$link/configuration-name"); - - my $date = strftime("%F", localtime(lstat($link)->mtime)); - my $version = - -e "$link/nixos-version" - ? readFile("$link/nixos-version") - : basename((glob(dirname(Cwd::abs_path("$link/kernel")) . "/lib/modules/*"))[0]); - - if ($cfgName) { - $entryName = $cfgName; - } else { - my $linkname = basename($link); - $entryName = "($linkname - $date - $version)"; - } - addEntry("NixOS - $entryName", $link); -} - -my $grubBootPath = $grubBoot->path; -# extraEntries could refer to @bootRoot@, which we have to substitute -$conf =~ s/\@bootRoot\@/$grubBootPath/g; - -# Emit submenus for all system profiles. -sub addProfile { - my ($profile, $description) = @_; - - # Add entries for all generations of this profile. - $conf .= "submenu \"$description\" {\n" if $grubVersion == 2; - - sub nrFromGen { my ($x) = @_; $x =~ /\/\w+-(\d+)-link/; return $1; } - - my @links = sort - { nrFromGen($b) <=> nrFromGen($a) } - (glob "$profile-*-link"); - - my $curEntry = 0; - foreach my $link (@links) { - last if $curEntry++ >= $configurationLimit; - if (! -e "$link/nixos-version") { - warn "skipping corrupt system profile entry ‘$link’\n"; - next; - } - my $date = strftime("%F", localtime(lstat($link)->mtime)); - my $version = - -e "$link/nixos-version" - ? readFile("$link/nixos-version") - : basename((glob(dirname(Cwd::abs_path("$link/kernel")) . "/lib/modules/*"))[0]); - addEntry("NixOS - Configuration " . nrFromGen($link) . " ($date - $version)", $link); - } - - $conf .= "}\n" if $grubVersion == 2; -} - -addProfile "/nix/var/nix/profiles/system", "NixOS - All configurations"; - -if ($grubVersion == 2) { - for my $profile (glob "/nix/var/nix/profiles/system-profiles/*") { - my $name = basename($profile); - next unless $name =~ /^\w+$/; - addProfile $profile, "NixOS - Profile '$name'"; - } -} - -# extraPrepareConfig could refer to @bootPath@, which we have to substitute -$extraPrepareConfig =~ s/\@bootPath\@/$bootPath/g; - -# Run extraPrepareConfig in sh -if ($extraPrepareConfig ne "") { - system((get("shell"), "-c", $extraPrepareConfig)); -} - -# write the GRUB config. -my $confFile = $grubVersion == 1 ? "$bootPath/grub/menu.lst" : "$bootPath/grub/grub.cfg"; -my $tmpFile = $confFile . ".tmp"; -writeFile($tmpFile, $conf); - - -# check whether to install GRUB EFI or not -sub getEfiTarget { - if ($grubVersion == 1) { - return "no" - } elsif (($grub ne "") && ($grubEfi ne "")) { - # EFI can only be installed when target is set; - # A target is also required then for non-EFI grub - if (($grubTarget eq "") || ($grubTargetEfi eq "")) { die } - else { return "both" } - } elsif (($grub ne "") && ($grubEfi eq "")) { - # TODO: It would be safer to disallow non-EFI grub installation if no taget is given. - # If no target is given, then grub auto-detects the target which can lead to errors. - # E.g. it seems as if grub would auto-detect a EFI target based on the availability - # of a EFI partition. - # However, it seems as auto-detection is currently relied on for non-x86_64 and non-i386 - # architectures in NixOS. That would have to be fixed in the nixos modules first. - return "no" - } elsif (($grub eq "") && ($grubEfi ne "")) { - # EFI can only be installed when target is set; - if ($grubTargetEfi eq "") { die } - else {return "only" } - } else { - # prevent an installation if neither grub nor grubEfi is given - return "neither" - } -} - -my $efiTarget = getEfiTarget(); - -# Append entries detected by os-prober -if (get("useOSProber") eq "true") { - if ($saveDefault) { - # os-prober will read this to determine if "savedefault" should be added to generated entries - $ENV{'GRUB_SAVEDEFAULT'} = "true"; - } - - my $targetpackage = ($efiTarget eq "no") ? $grub : $grubEfi; - system(get("shell"), "-c", "pkgdatadir=$targetpackage/share/grub $targetpackage/etc/grub.d/30_os-prober >> $tmpFile"); -} - -# Atomically switch to the new config -rename $tmpFile, $confFile or die "cannot rename $tmpFile to $confFile: $!\n"; - - -# Remove obsolete files from $bootPath/kernels. -foreach my $fn (glob "$bootPath/kernels/*") { - next if defined $copied{$fn}; - print STDERR "removing obsolete file $fn\n"; - unlink $fn; -} - - -# -# Install GRUB if the parameters changed from the last time we installed it. -# - -struct(GrubState => { - name => '$', - version => '$', - efi => '$', - devices => '$', - efiMountPoint => '$', - extraGrubInstallArgs => '@', -}); -# If you add something to the state file, only add it to the end -# because it is read line-by-line. -sub readGrubState { - my $defaultGrubState = GrubState->new(name => "", version => "", efi => "", devices => "", efiMountPoint => "", extraGrubInstallArgs => () ); - open FILE, "<$bootPath/grub/state" or return $defaultGrubState; - local $/ = "\n"; - my $name = <FILE>; - chomp($name); - my $version = <FILE>; - chomp($version); - my $efi = <FILE>; - chomp($efi); - my $devices = <FILE>; - chomp($devices); - my $efiMountPoint = <FILE>; - chomp($efiMountPoint); - # Historically, arguments in the state file were one per each line, but that - # gets really messy when newlines are involved, structured arguments - # like lists are needed (they have to have a separator encoding), or even worse, - # when we need to remove a setting in the future. Thus, the 6th line is a JSON - # object that can store structured data, with named keys, and all new state - # should go in there. - my $jsonStateLine = <FILE>; - # For historical reasons we do not check the values above for un-definedness - # (that is, when the state file has too few lines and EOF is reached), - # because the above come from the first version of this logic and are thus - # guaranteed to be present. - $jsonStateLine = defined $jsonStateLine ? $jsonStateLine : '{}'; # empty JSON object - chomp($jsonStateLine); - if ($jsonStateLine eq "") { - $jsonStateLine = '{}'; # empty JSON object - } - my %jsonState = %{decode_json($jsonStateLine)}; - my @extraGrubInstallArgs = exists($jsonState{'extraGrubInstallArgs'}) ? @{$jsonState{'extraGrubInstallArgs'}} : (); - close FILE; - my $grubState = GrubState->new(name => $name, version => $version, efi => $efi, devices => $devices, efiMountPoint => $efiMountPoint, extraGrubInstallArgs => \@extraGrubInstallArgs ); - return $grubState -} - -my @deviceTargets = getList('devices'); -my $prevGrubState = readGrubState(); -my @prevDeviceTargets = split/,/, $prevGrubState->devices; -my @extraGrubInstallArgs = getList('extraGrubInstallArgs'); -my @prevExtraGrubInstallArgs = @{$prevGrubState->extraGrubInstallArgs}; - -my $devicesDiffer = scalar (List::Compare->new( '-u', '-a', \@deviceTargets, \@prevDeviceTargets)->get_symmetric_difference()); -my $extraGrubInstallArgsDiffer = scalar (List::Compare->new( '-u', '-a', \@extraGrubInstallArgs, \@prevExtraGrubInstallArgs)->get_symmetric_difference()); -my $nameDiffer = get("fullName") ne $prevGrubState->name; -my $versionDiffer = get("fullVersion") ne $prevGrubState->version; -my $efiDiffer = $efiTarget ne $prevGrubState->efi; -my $efiMountPointDiffer = $efiSysMountPoint ne $prevGrubState->efiMountPoint; -if (($ENV{'NIXOS_INSTALL_GRUB'} // "") eq "1") { - warn "NIXOS_INSTALL_GRUB env var deprecated, use NIXOS_INSTALL_BOOTLOADER"; - $ENV{'NIXOS_INSTALL_BOOTLOADER'} = "1"; -} -my $requireNewInstall = $devicesDiffer || $extraGrubInstallArgsDiffer || $nameDiffer || $versionDiffer || $efiDiffer || $efiMountPointDiffer || (($ENV{'NIXOS_INSTALL_BOOTLOADER'} // "") eq "1"); - -# install a symlink so that grub can detect the boot drive -my $tmpDir = File::Temp::tempdir(CLEANUP => 1) or die "Failed to create temporary space: $!"; -symlink "$bootPath", "$tmpDir/boot" or die "Failed to symlink $tmpDir/boot: $!"; - -# install non-EFI GRUB -if (($requireNewInstall != 0) && ($efiTarget eq "no" || $efiTarget eq "both")) { - foreach my $dev (@deviceTargets) { - next if $dev eq "nodev"; - print STDERR "installing the GRUB $grubVersion boot loader on $dev...\n"; - my @command = ("$grub/sbin/grub-install", "--recheck", "--root-directory=$tmpDir", Cwd::abs_path($dev), @extraGrubInstallArgs); - if ($forceInstall eq "true") { - push @command, "--force"; - } - if ($grubTarget ne "") { - push @command, "--target=$grubTarget"; - } - (system @command) == 0 or die "$0: installation of GRUB on $dev failed: $!\n"; - } -} - - -# install EFI GRUB -if (($requireNewInstall != 0) && ($efiTarget eq "only" || $efiTarget eq "both")) { - print STDERR "installing the GRUB $grubVersion EFI boot loader into $efiSysMountPoint...\n"; - my @command = ("$grubEfi/sbin/grub-install", "--recheck", "--target=$grubTargetEfi", "--boot-directory=$bootPath", "--efi-directory=$efiSysMountPoint", @extraGrubInstallArgs); - if ($forceInstall eq "true") { - push @command, "--force"; - } - if ($canTouchEfiVariables eq "true") { - push @command, "--bootloader-id=$bootloaderId"; - } else { - push @command, "--no-nvram"; - push @command, "--removable" if $efiInstallAsRemovable eq "true"; - } - - (system @command) == 0 or die "$0: installation of GRUB EFI into $efiSysMountPoint failed: $!\n"; -} - - -# update GRUB state file -if ($requireNewInstall != 0) { - # Temp file for atomic rename. - my $stateFile = "$bootPath/grub/state"; - my $stateFileTmp = $stateFile . ".tmp"; - - open FILE, ">$stateFileTmp" or die "cannot create $stateFileTmp: $!\n"; - print FILE get("fullName"), "\n" or die; - print FILE get("fullVersion"), "\n" or die; - print FILE $efiTarget, "\n" or die; - print FILE join( ",", @deviceTargets ), "\n" or die; - print FILE $efiSysMountPoint, "\n" or die; - my %jsonState = ( - extraGrubInstallArgs => \@extraGrubInstallArgs - ); - my $jsonStateLine = encode_json(\%jsonState); - print FILE $jsonStateLine, "\n" or die; - close FILE or die; - - # Atomically switch to the new state file - rename $stateFileTmp, $stateFile or die "cannot rename $stateFileTmp to $stateFile: $!\n"; -} diff --git a/nixos/modules/system/boot/loader/grub/ipxe.nix b/nixos/modules/system/boot/loader/grub/ipxe.nix deleted file mode 100644 index ef8595592f4..00000000000 --- a/nixos/modules/system/boot/loader/grub/ipxe.nix +++ /dev/null @@ -1,64 +0,0 @@ -# This module adds a scripted iPXE entry to the GRUB boot menu. - -{ config, lib, pkgs, ... }: - -with lib; - -let - scripts = builtins.attrNames config.boot.loader.grub.ipxe; - - grubEntry = name: - '' - menuentry "iPXE - ${name}" { - linux16 @bootRoot@/ipxe.lkrn - initrd16 @bootRoot@/${name}.ipxe - } - - ''; - - scriptFile = name: - let - value = builtins.getAttr name config.boot.loader.grub.ipxe; - in - if builtins.typeOf value == "path" then value - else builtins.toFile "${name}.ipxe" value; -in -{ - options = - { boot.loader.grub.ipxe = mkOption { - type = types.attrsOf (types.either types.path types.str); - description = - '' - Set of iPXE scripts available for - booting from the GRUB boot menu. - ''; - default = { }; - example = literalExpression '' - { demo = ''' - #!ipxe - dhcp - chain http://boot.ipxe.org/demo/boot.php - '''; - } - ''; - }; - }; - - config = mkIf (builtins.length scripts != 0) { - - boot.loader.grub.extraEntries = - if config.boot.loader.grub.version == 2 then - toString (map grubEntry scripts) - else - throw "iPXE is not supported with GRUB 1."; - - boot.loader.grub.extraFiles = - { "ipxe.lkrn" = "${pkgs.ipxe}/ipxe.lkrn"; } - // - builtins.listToAttrs ( map - (name: { name = name+".ipxe"; value = scriptFile name; }) - scripts - ); - }; - -} diff --git a/nixos/modules/system/boot/loader/grub/memtest.nix b/nixos/modules/system/boot/loader/grub/memtest.nix deleted file mode 100644 index 71e50dd0577..00000000000 --- a/nixos/modules/system/boot/loader/grub/memtest.nix +++ /dev/null @@ -1,116 +0,0 @@ -# This module adds Memtest86+/Memtest86 to the GRUB boot menu. - -{ config, lib, pkgs, ... }: - -with lib; - -let - memtest86 = pkgs.memtest86plus; - efiSupport = config.boot.loader.grub.efiSupport; - cfg = config.boot.loader.grub.memtest86; -in - -{ - options = { - - boot.loader.grub.memtest86 = { - - enable = mkOption { - default = false; - type = types.bool; - description = '' - Make Memtest86+ (or MemTest86 if EFI support is enabled), - a memory testing program, available from the - GRUB boot menu. MemTest86 is an unfree program, so - this requires <literal>allowUnfree</literal> to be set to - <literal>true</literal>. - ''; - }; - - params = mkOption { - default = []; - example = [ "console=ttyS0,115200" ]; - type = types.listOf types.str; - description = '' - Parameters added to the Memtest86+ command line. As of memtest86+ 5.01 - the following list of (apparently undocumented) parameters are - accepted: - - <itemizedlist> - - <listitem> - <para><literal>console=...</literal>, set up a serial console. - Examples: - <literal>console=ttyS0</literal>, - <literal>console=ttyS0,9600</literal> or - <literal>console=ttyS0,115200n8</literal>.</para> - </listitem> - - <listitem> - <para><literal>btrace</literal>, enable boot trace.</para> - </listitem> - - <listitem> - <para><literal>maxcpus=N</literal>, limit number of CPUs.</para> - </listitem> - - <listitem> - <para><literal>onepass</literal>, run one pass and exit if there - are no errors.</para> - </listitem> - - <listitem> - <para><literal>tstlist=...</literal>, list of tests to run. - Example: <literal>0,1,2</literal>.</para> - </listitem> - - <listitem> - <para><literal>cpumask=...</literal>, set a CPU mask, to select CPUs - to use for testing.</para> - </listitem> - - </itemizedlist> - - This list of command line options was obtained by reading the - Memtest86+ source code. - ''; - }; - - }; - }; - - config = mkMerge [ - (mkIf (cfg.enable && efiSupport) { - assertions = [ - { - assertion = cfg.params == []; - message = "Parameters are not available for MemTest86"; - } - ]; - - boot.loader.grub.extraFiles = { - "memtest86.efi" = "${pkgs.memtest86-efi}/BOOTX64.efi"; - }; - - boot.loader.grub.extraEntries = '' - menuentry "Memtest86" { - chainloader /memtest86.efi - } - ''; - }) - - (mkIf (cfg.enable && !efiSupport) { - boot.loader.grub.extraEntries = - if config.boot.loader.grub.version == 2 then - '' - menuentry "Memtest86+" { - linux16 @bootRoot@/memtest.bin ${toString cfg.params} - } - '' - else - throw "Memtest86+ is not supported with GRUB 1."; - - boot.loader.grub.extraFiles."memtest.bin" = "${memtest86}/memtest.bin"; - }) - ]; -} diff --git a/nixos/modules/system/boot/loader/init-script/init-script-builder.sh b/nixos/modules/system/boot/loader/init-script/init-script-builder.sh deleted file mode 100644 index bd3fc64999d..00000000000 --- a/nixos/modules/system/boot/loader/init-script/init-script-builder.sh +++ /dev/null @@ -1,92 +0,0 @@ -#! @bash@/bin/sh -e - -shopt -s nullglob - -export PATH=/empty -for i in @path@; do PATH=$PATH:$i/bin; done - -if test $# -ne 1; then - echo "Usage: init-script-builder.sh DEFAULT-CONFIG" - exit 1 -fi - -defaultConfig="$1" - - -[ "$(stat -f -c '%i' /)" = "$(stat -f -c '%i' /boot)" ] || { - # see grub-menu-builder.sh - echo "WARNING: /boot being on a different filesystem not supported by init-script-builder.sh" -} - - - -target="/sbin/init" -targetOther="/boot/init-other-configurations-contents.txt" - -tmp="$target.tmp" -tmpOther="$targetOther.tmp" - - -configurationCounter=0 -numAlienEntries=`cat <<EOF | egrep '^[[:space:]]*title' | wc -l -@extraEntries@ -EOF` - - - - -# Add an entry to $targetOther -addEntry() { - local name="$1" - local path="$2" - local shortSuffix="$3" - - configurationCounter=$((configurationCounter + 1)) - - local stage2=$path/init - - content="$( - echo "#!/bin/sh" - echo "# $name" - echo "# created by init-script-builder.sh" - echo "exec $stage2" - )" - - [ "$path" != "$defaultConfig" ] || { - echo "$content" > $tmp - echo "# older configurations: $targetOther" >> $tmp - chmod +x $tmp - } - - echo -e "$content\n\n" >> $tmpOther -} - - -mkdir -p /boot /sbin - -addEntry "NixOS - Default" $defaultConfig "" - -# Add all generations of the system profile to the menu, in reverse -# (most recent to least recent) order. -for link in $((ls -d $defaultConfig/specialisation/* ) | sort -n); do - date=$(stat --printf="%y\n" $link | sed 's/\..*//') - addEntry "NixOS - variation" $link "" -done - -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 - date=$(stat --printf="%y\n" $link | sed 's/\..*//') - if [ -d $link/kernel ]; then - kernelVersion=$(cd $(dirname $(readlink -f $link/kernel))/lib/modules && echo *) - suffix="($date - $kernelVersion)" - else - suffix="($date)" - fi - addEntry "NixOS - Configuration $generation $suffix" $link "$generation ($date)" -done - -mv $tmpOther $targetOther -mv $tmp $target diff --git a/nixos/modules/system/boot/loader/init-script/init-script.nix b/nixos/modules/system/boot/loader/init-script/init-script.nix deleted file mode 100644 index 374d9524ff1..00000000000 --- a/nixos/modules/system/boot/loader/init-script/init-script.nix +++ /dev/null @@ -1,51 +0,0 @@ -{ config, lib, pkgs, ... }: - -with lib; - -let - - initScriptBuilder = pkgs.substituteAll { - src = ./init-script-builder.sh; - isExecutable = true; - inherit (pkgs) bash; - path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep]; - }; - -in - -{ - - ###### interface - - options = { - - boot.loader.initScript = { - - enable = mkOption { - default = false; - type = types.bool; - description = '' - Some systems require a /sbin/init script which is started. - Or having it makes starting NixOS easier. - This applies to some kind of hosting services and user mode linux. - - Additionally this script will create - /boot/init-other-configurations-contents.txt containing - contents of remaining configurations. You can copy paste them into - /sbin/init manually running a rescue system or such. - ''; - }; - }; - - }; - - - ###### implementation - - config = mkIf config.boot.loader.initScript.enable { - - system.build.installBootLoader = initScriptBuilder; - - }; - -} diff --git a/nixos/modules/system/boot/loader/loader.nix b/nixos/modules/system/boot/loader/loader.nix deleted file mode 100644 index 01475f79b9c..00000000000 --- a/nixos/modules/system/boot/loader/loader.nix +++ /dev/null @@ -1,20 +0,0 @@ -{ lib, ... }: - -with lib; - -{ - imports = [ - (mkRenamedOptionModule [ "boot" "loader" "grub" "timeout" ] [ "boot" "loader" "timeout" ]) - (mkRenamedOptionModule [ "boot" "loader" "gummiboot" "timeout" ] [ "boot" "loader" "timeout" ]) - ]; - - options = { - boot.loader.timeout = mkOption { - default = 5; - type = types.nullOr types.int; - description = '' - Timeout (in seconds) until loader boots the default menu item. Use null if the loader menu should be displayed indefinitely. - ''; - }; - }; -} diff --git a/nixos/modules/system/boot/loader/raspberrypi/raspberrypi-builder.nix b/nixos/modules/system/boot/loader/raspberrypi/raspberrypi-builder.nix deleted file mode 100644 index 64e106036ab..00000000000 --- a/nixos/modules/system/boot/loader/raspberrypi/raspberrypi-builder.nix +++ /dev/null @@ -1,9 +0,0 @@ -{ pkgs, configTxt, firmware ? pkgs.raspberrypifw }: - -pkgs.substituteAll { - src = ./raspberrypi-builder.sh; - isExecutable = true; - inherit (pkgs) bash; - path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep]; - inherit firmware configTxt; -} diff --git a/nixos/modules/system/boot/loader/raspberrypi/raspberrypi-builder.sh b/nixos/modules/system/boot/loader/raspberrypi/raspberrypi-builder.sh deleted file mode 100644 index 0541ca1ba62..00000000000 --- a/nixos/modules/system/boot/loader/raspberrypi/raspberrypi-builder.sh +++ /dev/null @@ -1,143 +0,0 @@ -#! @bash@/bin/sh - -# This can end up being called disregarding the shebang. -set -e - -shopt -s nullglob - -export PATH=/empty -for i in @path@; do PATH=$PATH:$i/bin; done - -usage() { - echo "usage: $0 -c <path-to-default-configuration> [-d <boot-dir>]" >&2 - exit 1 -} - -default= # Default configuration -target=/boot # Target directory - -while getopts "c:d:" opt; do - case "$opt" in - c) default="$OPTARG" ;; - d) target="$OPTARG" ;; - \?) usage ;; - esac -done - -echo "updating the boot generations directory..." - -mkdir -p $target/old - -# Convert a path to a file in the Nix store such as -# /nix/store/<hash>-<name>/file to <hash>-<name>-<file>. -cleanName() { - local path="$1" - echo "$path" | sed 's|^/nix/store/||' | sed 's|/|-|g' -} - -# Copy a file from the Nix store to $target/kernels. -declare -A filesCopied - -copyToKernelsDir() { - local src="$1" - local dst="$target/old/$(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 -} - -copyForced() { - local src="$1" - local dst="$2" - cp $src $dst.tmp - mv $dst.tmp $dst -} - -outdir=$target/old -mkdir -p $outdir || true - -# Copy its kernel and initrd to $target/old. -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) - local dtb_path=$(readlink -f $path/dtbs) - - if test -n "@copyKernels@"; then - copyToKernelsDir $kernel; kernel=$result - copyToKernelsDir $initrd; initrd=$result - fi - - echo $(readlink -f $path) > $outdir/$generation-system - echo $(readlink -f $path/init) > $outdir/$generation-init - cp $path/kernel-params $outdir/$generation-cmdline.txt - echo $initrd > $outdir/$generation-initrd - echo $kernel > $outdir/$generation-kernel - - if test "$generation" = "default"; then - copyForced $kernel $target/kernel.img - copyForced $initrd $target/initrd - for dtb in $dtb_path/{broadcom,}/bcm*.dtb; do - dst="$target/$(basename $dtb)" - copyForced $dtb "$dst" - filesCopied[$dst]=1 - done - cp "$(readlink -f "$path/init")" $target/nixos-init - echo "`cat $path/kernel-params` init=$path/init" >$target/cmdline.txt - fi -} - -addEntry $default default - -# Add all generations of the system profile to the menu, 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 - -# Add the firmware files -fwdir=@firmware@/share/raspberrypi/boot/ -copyForced $fwdir/bootcode.bin $target/bootcode.bin -copyForced $fwdir/fixup.dat $target/fixup.dat -copyForced $fwdir/fixup4.dat $target/fixup4.dat -copyForced $fwdir/fixup4cd.dat $target/fixup4cd.dat -copyForced $fwdir/fixup4db.dat $target/fixup4db.dat -copyForced $fwdir/fixup4x.dat $target/fixup4x.dat -copyForced $fwdir/fixup_cd.dat $target/fixup_cd.dat -copyForced $fwdir/fixup_db.dat $target/fixup_db.dat -copyForced $fwdir/fixup_x.dat $target/fixup_x.dat -copyForced $fwdir/start.elf $target/start.elf -copyForced $fwdir/start4.elf $target/start4.elf -copyForced $fwdir/start4cd.elf $target/start4cd.elf -copyForced $fwdir/start4db.elf $target/start4db.elf -copyForced $fwdir/start4x.elf $target/start4x.elf -copyForced $fwdir/start_cd.elf $target/start_cd.elf -copyForced $fwdir/start_db.elf $target/start_db.elf -copyForced $fwdir/start_x.elf $target/start_x.elf - -# Add the config.txt -copyForced @configTxt@ $target/config.txt - -# Remove obsolete files from $target and $target/old. -for fn in $target/old/*linux* $target/old/*initrd-initrd* $target/bcm*.dtb; do - if ! test "${filesCopied[$fn]}" = 1; then - rm -vf -- "$fn" - fi -done diff --git a/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix b/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix deleted file mode 100644 index 1023361f0b1..00000000000 --- a/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix +++ /dev/null @@ -1,105 +0,0 @@ -{ config, lib, pkgs, ... }: - -with lib; - -let - cfg = config.boot.loader.raspberryPi; - - builderUboot = import ./uboot-builder.nix { inherit pkgs configTxt; inherit (cfg) version; }; - builderGeneric = import ./raspberrypi-builder.nix { inherit pkgs configTxt; }; - - builder = - if cfg.uboot.enable then - "${builderUboot} -g ${toString cfg.uboot.configurationLimit} -t ${timeoutStr} -c" - else - "${builderGeneric} -c"; - - blCfg = config.boot.loader; - timeoutStr = if blCfg.timeout == null then "-1" else toString blCfg.timeout; - - isAarch64 = pkgs.stdenv.hostPlatform.isAarch64; - optional = pkgs.lib.optionalString; - - configTxt = - pkgs.writeText "config.txt" ('' - # U-Boot used to need this to work, regardless of whether UART is actually used or not. - # TODO: check when/if this can be removed. - enable_uart=1 - - # Prevent the firmware from smashing the framebuffer setup done by the mainline kernel - # when attempting to show low-voltage or overtemperature warnings. - avoid_warnings=1 - '' + optional isAarch64 '' - # Boot in 64-bit mode. - arm_64bit=1 - '' + (if cfg.uboot.enable then '' - kernel=u-boot-rpi.bin - '' else '' - kernel=kernel.img - initramfs initrd followkernel - '') + optional (cfg.firmwareConfig != null) cfg.firmwareConfig); - -in - -{ - options = { - - boot.loader.raspberryPi = { - enable = mkOption { - default = false; - type = types.bool; - description = '' - Whether to create files with the system generations in - <literal>/boot</literal>. - <literal>/boot/old</literal> will hold files from old generations. - ''; - }; - - version = mkOption { - default = 2; - type = types.enum [ 0 1 2 3 4 ]; - description = ""; - }; - - uboot = { - enable = mkOption { - default = false; - type = types.bool; - description = '' - Enable using uboot as bootmanager for the raspberry pi. - ''; - }; - - configurationLimit = mkOption { - default = 20; - example = 10; - type = types.int; - description = '' - Maximum number of configurations in the boot menu. - ''; - }; - - }; - - firmwareConfig = mkOption { - default = null; - type = types.nullOr types.lines; - description = '' - Extra options that will be appended to <literal>/boot/config.txt</literal> file. - For possible values, see: https://www.raspberrypi.org/documentation/configuration/config-txt/ - ''; - }; - }; - }; - - config = mkIf cfg.enable { - assertions = singleton { - assertion = !pkgs.stdenv.hostPlatform.isAarch64 || cfg.version >= 3; - message = "Only Raspberry Pi >= 3 supports aarch64."; - }; - - system.build.installBootLoader = builder; - system.boot.loader.id = "raspberrypi"; - system.boot.loader.kernelFile = pkgs.stdenv.hostPlatform.linux-kernel.target; - }; -} diff --git a/nixos/modules/system/boot/loader/raspberrypi/uboot-builder.nix b/nixos/modules/system/boot/loader/raspberrypi/uboot-builder.nix deleted file mode 100644 index a4352ab9a24..00000000000 --- a/nixos/modules/system/boot/loader/raspberrypi/uboot-builder.nix +++ /dev/null @@ -1,37 +0,0 @@ -{ pkgs, version, configTxt }: - -let - isAarch64 = pkgs.stdenv.hostPlatform.isAarch64; - - uboot = - if version == 0 then - pkgs.ubootRaspberryPiZero - else if version == 1 then - pkgs.ubootRaspberryPi - else if version == 2 then - pkgs.ubootRaspberryPi2 - else if version == 3 then - if isAarch64 then - pkgs.ubootRaspberryPi3_64bit - else - pkgs.ubootRaspberryPi3_32bit - else - throw "U-Boot is not yet supported on the raspberry pi 4."; - - extlinuxConfBuilder = - import ../generic-extlinux-compatible/extlinux-conf-builder.nix { - pkgs = pkgs.buildPackages; - }; -in -pkgs.substituteAll { - src = ./uboot-builder.sh; - isExecutable = true; - inherit (pkgs) bash; - path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep]; - firmware = pkgs.raspberrypifw; - inherit uboot; - inherit configTxt; - inherit extlinuxConfBuilder; - inherit version; -} - diff --git a/nixos/modules/system/boot/loader/raspberrypi/uboot-builder.sh b/nixos/modules/system/boot/loader/raspberrypi/uboot-builder.sh deleted file mode 100644 index ea591427179..00000000000 --- a/nixos/modules/system/boot/loader/raspberrypi/uboot-builder.sh +++ /dev/null @@ -1,38 +0,0 @@ -#! @bash@/bin/sh -e - -target=/boot # Target directory - -while getopts "t:c:d:g:" opt; do - case "$opt" in - d) target="$OPTARG" ;; - *) ;; - esac -done - -copyForced() { - local src="$1" - local dst="$2" - cp $src $dst.tmp - mv $dst.tmp $dst -} - -# Call the extlinux builder -"@extlinuxConfBuilder@" "$@" - -# Add the firmware files -fwdir=@firmware@/share/raspberrypi/boot/ -copyForced $fwdir/bootcode.bin $target/bootcode.bin -copyForced $fwdir/fixup.dat $target/fixup.dat -copyForced $fwdir/fixup_cd.dat $target/fixup_cd.dat -copyForced $fwdir/fixup_db.dat $target/fixup_db.dat -copyForced $fwdir/fixup_x.dat $target/fixup_x.dat -copyForced $fwdir/start.elf $target/start.elf -copyForced $fwdir/start_cd.elf $target/start_cd.elf -copyForced $fwdir/start_db.elf $target/start_db.elf -copyForced $fwdir/start_x.elf $target/start_x.elf - -# Add the uboot file -copyForced @uboot@/u-boot.bin $target/u-boot-rpi.bin - -# Add the config.txt -copyForced @configTxt@ $target/config.txt diff --git a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py deleted file mode 100644 index adc89306309..00000000000 --- a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py +++ /dev/null @@ -1,305 +0,0 @@ -#! @python3@/bin/python3 -B -import argparse -import shutil -import os -import sys -import errno -import subprocess -import glob -import tempfile -import errno -import warnings -import ctypes -libc = ctypes.CDLL("libc.so.6") -import re -import datetime -import glob -import os.path -from typing import Tuple, List, Optional - -SystemIdentifier = Tuple[Optional[str], int, Optional[str]] - - -def copy_if_not_exists(source: str, dest: str) -> None: - if not os.path.exists(dest): - shutil.copyfile(source, dest) - - -def generation_dir(profile: Optional[str], generation: int) -> str: - if profile: - return "/nix/var/nix/profiles/system-profiles/%s-%d-link" % (profile, generation) - else: - return "/nix/var/nix/profiles/system-%d-link" % (generation) - -def system_dir(profile: Optional[str], generation: int, specialisation: Optional[str]) -> str: - d = generation_dir(profile, generation) - if specialisation: - return os.path.join(d, "specialisation", specialisation) - else: - return d - -BOOT_ENTRY = """title NixOS{profile}{specialisation} -version Generation {generation} {description} -linux {kernel} -initrd {initrd} -options {kernel_params} -""" - -def generation_conf_filename(profile: Optional[str], generation: int, specialisation: Optional[str]) -> str: - pieces = [ - "nixos", - profile or None, - "generation", - str(generation), - f"specialisation-{specialisation}" if specialisation else None, - ] - return "-".join(p for p in pieces if p) + ".conf" - - -def write_loader_conf(profile: Optional[str], generation: int, specialisation: Optional[str]) -> None: - with open("@efiSysMountPoint@/loader/loader.conf.tmp", 'w') as f: - if "@timeout@" != "": - f.write("timeout @timeout@\n") - f.write("default %s\n" % generation_conf_filename(profile, generation, specialisation)) - if not @editor@: - f.write("editor 0\n"); - f.write("console-mode @consoleMode@\n"); - os.rename("@efiSysMountPoint@/loader/loader.conf.tmp", "@efiSysMountPoint@/loader/loader.conf") - - -def profile_path(profile: Optional[str], generation: int, specialisation: Optional[str], name: str) -> str: - return os.path.realpath("%s/%s" % (system_dir(profile, generation, specialisation), name)) - - -def copy_from_profile(profile: Optional[str], generation: int, specialisation: Optional[str], name: str, dry_run: bool = False) -> str: - store_file_path = profile_path(profile, generation, specialisation, name) - suffix = os.path.basename(store_file_path) - store_dir = os.path.basename(os.path.dirname(store_file_path)) - efi_file_path = "/efi/nixos/%s-%s.efi" % (store_dir, suffix) - if not dry_run: - copy_if_not_exists(store_file_path, "@efiSysMountPoint@%s" % (efi_file_path)) - return efi_file_path - - -def describe_generation(generation_dir: str) -> str: - try: - with open("%s/nixos-version" % generation_dir) as f: - nixos_version = f.read() - except IOError: - nixos_version = "Unknown" - - kernel_dir = os.path.dirname(os.path.realpath("%s/kernel" % generation_dir)) - module_dir = glob.glob("%s/lib/modules/*" % kernel_dir)[0] - kernel_version = os.path.basename(module_dir) - - build_time = int(os.path.getctime(generation_dir)) - build_date = datetime.datetime.fromtimestamp(build_time).strftime('%F') - - description = "NixOS {}, Linux Kernel {}, Built on {}".format( - nixos_version, kernel_version, build_date - ) - - return description - - -def write_entry(profile: Optional[str], generation: int, specialisation: Optional[str], machine_id: str) -> None: - kernel = copy_from_profile(profile, generation, specialisation, "kernel") - initrd = copy_from_profile(profile, generation, specialisation, "initrd") - try: - append_initrd_secrets = profile_path(profile, generation, specialisation, "append-initrd-secrets") - subprocess.check_call([append_initrd_secrets, "@efiSysMountPoint@%s" % (initrd)]) - except FileNotFoundError: - pass - entry_file = "@efiSysMountPoint@/loader/entries/%s" % ( - generation_conf_filename(profile, generation, specialisation)) - generation_dir = os.readlink(system_dir(profile, generation, specialisation)) - tmp_path = "%s.tmp" % (entry_file) - kernel_params = "init=%s/init " % generation_dir - - with open("%s/kernel-params" % (generation_dir)) as params_file: - kernel_params = kernel_params + params_file.read() - with open(tmp_path, 'w') as f: - f.write(BOOT_ENTRY.format(profile=" [" + profile + "]" if profile else "", - specialisation=" (%s)" % specialisation if specialisation else "", - generation=generation, - kernel=kernel, - initrd=initrd, - kernel_params=kernel_params, - description=describe_generation(generation_dir))) - if machine_id is not None: - f.write("machine-id %s\n" % machine_id) - os.rename(tmp_path, entry_file) - - -def mkdir_p(path: str) -> None: - try: - os.makedirs(path) - except OSError as e: - if e.errno != errno.EEXIST or not os.path.isdir(path): - raise - - -def get_generations(profile: Optional[str] = None) -> List[SystemIdentifier]: - gen_list = subprocess.check_output([ - "@nix@/bin/nix-env", - "--list-generations", - "-p", - "/nix/var/nix/profiles/%s" % ("system-profiles/" + profile if profile else "system"), - "--option", "build-users-group", ""], - universal_newlines=True) - gen_lines = gen_list.split('\n') - gen_lines.pop() - - configurationLimit = @configurationLimit@ - configurations: List[SystemIdentifier] = [ (profile, int(line.split()[0]), None) for line in gen_lines ] - return configurations[-configurationLimit:] - - -def get_specialisations(profile: Optional[str], generation: int, _: Optional[str]) -> List[SystemIdentifier]: - specialisations_dir = os.path.join( - system_dir(profile, generation, None), "specialisation") - if not os.path.exists(specialisations_dir): - return [] - return [(profile, generation, spec) for spec in os.listdir(specialisations_dir)] - - -def remove_old_entries(gens: List[SystemIdentifier]) -> None: - rex_profile = re.compile("^@efiSysMountPoint@/loader/entries/nixos-(.*)-generation-.*\.conf$") - rex_generation = re.compile("^@efiSysMountPoint@/loader/entries/nixos.*-generation-(.*)\.conf$") - known_paths = [] - for gen in gens: - known_paths.append(copy_from_profile(*gen, "kernel", True)) - known_paths.append(copy_from_profile(*gen, "initrd", True)) - for path in glob.iglob("@efiSysMountPoint@/loader/entries/nixos*-generation-[1-9]*.conf"): - try: - if rex_profile.match(path): - prof = rex_profile.sub(r"\1", path) - else: - prof = "system" - gen_number = int(rex_generation.sub(r"\1", path)) - if not (prof, gen_number) in gens: - os.unlink(path) - except ValueError: - pass - for path in glob.iglob("@efiSysMountPoint@/efi/nixos/*"): - if not path in known_paths and not os.path.isdir(path): - os.unlink(path) - - -def get_profiles() -> List[str]: - if os.path.isdir("/nix/var/nix/profiles/system-profiles/"): - return [x - for x in os.listdir("/nix/var/nix/profiles/system-profiles/") - if not x.endswith("-link")] - else: - return [] - - -def main() -> None: - parser = argparse.ArgumentParser(description='Update NixOS-related systemd-boot files') - parser.add_argument('default_config', metavar='DEFAULT-CONFIG', help='The default NixOS config to boot') - args = parser.parse_args() - - try: - with open("/etc/machine-id") as machine_file: - machine_id = machine_file.readlines()[0] - except IOError as e: - if e.errno != errno.ENOENT: - raise - # Since systemd version 232 a machine ID is required and it might not - # be there on newly installed systems, so let's generate one so that - # bootctl can find it and we can also pass it to write_entry() later. - cmd = ["@systemd@/bin/systemd-machine-id-setup", "--print"] - machine_id = subprocess.run( - cmd, text=True, check=True, stdout=subprocess.PIPE - ).stdout.rstrip() - - if os.getenv("NIXOS_INSTALL_GRUB") == "1": - warnings.warn("NIXOS_INSTALL_GRUB env var deprecated, use NIXOS_INSTALL_BOOTLOADER", DeprecationWarning) - os.environ["NIXOS_INSTALL_BOOTLOADER"] = "1" - - if os.getenv("NIXOS_INSTALL_BOOTLOADER") == "1": - # bootctl uses fopen() with modes "wxe" and fails if the file exists. - if os.path.exists("@efiSysMountPoint@/loader/loader.conf"): - os.unlink("@efiSysMountPoint@/loader/loader.conf") - - flags = [] - - if "@canTouchEfiVariables@" != "1": - flags.append("--no-variables") - - if "@graceful@" == "1": - flags.append("--graceful") - - subprocess.check_call(["@systemd@/bin/bootctl", "--path=@efiSysMountPoint@"] + flags + ["install"]) - else: - # Update bootloader to latest if needed - systemd_version = subprocess.check_output(["@systemd@/bin/bootctl", "--version"], universal_newlines=True).split()[2] - sdboot_status = subprocess.check_output(["@systemd@/bin/bootctl", "--path=@efiSysMountPoint@", "status"], universal_newlines=True) - - # See status_binaries() in systemd bootctl.c for code which generates this - m = re.search("^\W+File:.*/EFI/(BOOT|systemd)/.*\.efi \(systemd-boot ([\d.]+[^)]*)\)$", - sdboot_status, re.IGNORECASE | re.MULTILINE) - - needs_install = False - - if m is None: - print("could not find any previously installed systemd-boot, installing.") - # Let systemd-boot attempt an installation if a previous one wasn't found - needs_install = True - else: - sdboot_version = f'({m.group(2)})' - if systemd_version != sdboot_version: - print("updating systemd-boot from %s to %s" % (sdboot_version, systemd_version)) - needs_install = True - - if needs_install: - subprocess.check_call(["@systemd@/bin/bootctl", "--path=@efiSysMountPoint@", "update"]) - - mkdir_p("@efiSysMountPoint@/efi/nixos") - mkdir_p("@efiSysMountPoint@/loader/entries") - - gens = get_generations() - for profile in get_profiles(): - gens += get_generations(profile) - remove_old_entries(gens) - for gen in gens: - try: - write_entry(*gen, machine_id) - for specialisation in get_specialisations(*gen): - write_entry(*specialisation, machine_id) - if os.readlink(system_dir(*gen)) == args.default_config: - write_loader_conf(*gen) - except OSError as e: - print("ignoring generation '{}' in the list of boot entries because of the following error:\n{}".format(*gen, e), file=sys.stderr) - - for root, _, files in os.walk('@efiSysMountPoint@/efi/nixos/.extra-files', topdown=False): - relative_root = root.removeprefix("@efiSysMountPoint@/efi/nixos/.extra-files").removeprefix("/") - actual_root = os.path.join("@efiSysMountPoint@", relative_root) - - for file in files: - actual_file = os.path.join(actual_root, file) - - if os.path.exists(actual_file): - os.unlink(actual_file) - os.unlink(os.path.join(root, file)) - - if not len(os.listdir(actual_root)): - os.rmdir(actual_root) - os.rmdir(root) - - mkdir_p("@efiSysMountPoint@/efi/nixos/.extra-files") - - subprocess.check_call("@copyExtraFiles@") - - # Since fat32 provides little recovery facilities after a crash, - # it can leave the system in an unbootable state, when a crash/outage - # happens shortly after an update. To decrease the likelihood of this - # event sync the efi filesystem after each update. - rc = libc.syncfs(os.open("@efiSysMountPoint@", os.O_RDONLY)) - if rc != 0: - print("could not sync @efiSysMountPoint@: {}".format(os.strerror(rc)), file=sys.stderr) - - -if __name__ == '__main__': - main() diff --git a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix deleted file mode 100644 index c07567ec82e..00000000000 --- a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix +++ /dev/null @@ -1,303 +0,0 @@ -{ config, lib, pkgs, ... }: - -with lib; - -let - cfg = config.boot.loader.systemd-boot; - - efi = config.boot.loader.efi; - - systemdBootBuilder = pkgs.substituteAll { - src = ./systemd-boot-builder.py; - - isExecutable = true; - - inherit (pkgs) python3; - - systemd = config.systemd.package; - - nix = config.nix.package.out; - - timeout = if config.boot.loader.timeout != null then config.boot.loader.timeout else ""; - - editor = if cfg.editor then "True" else "False"; - - configurationLimit = if cfg.configurationLimit == null then 0 else cfg.configurationLimit; - - inherit (cfg) consoleMode graceful; - - inherit (efi) efiSysMountPoint canTouchEfiVariables; - - memtest86 = if cfg.memtest86.enable then pkgs.memtest86-efi else ""; - - netbootxyz = if cfg.netbootxyz.enable then pkgs.netbootxyz-efi else ""; - - copyExtraFiles = pkgs.writeShellScript "copy-extra-files" '' - empty_file=$(mktemp) - - ${concatStrings (mapAttrsToList (n: v: '' - ${pkgs.coreutils}/bin/install -Dp "${v}" "${efi.efiSysMountPoint}/"${escapeShellArg n} - ${pkgs.coreutils}/bin/install -D $empty_file "${efi.efiSysMountPoint}/efi/nixos/.extra-files/"${escapeShellArg n} - '') cfg.extraFiles)} - - ${concatStrings (mapAttrsToList (n: v: '' - ${pkgs.coreutils}/bin/install -Dp "${pkgs.writeText n v}" "${efi.efiSysMountPoint}/loader/entries/"${escapeShellArg n} - ${pkgs.coreutils}/bin/install -D $empty_file "${efi.efiSysMountPoint}/efi/nixos/.extra-files/loader/entries/"${escapeShellArg n} - '') cfg.extraEntries)} - ''; - }; - - checkedSystemdBootBuilder = pkgs.runCommand "systemd-boot" { - nativeBuildInputs = [ pkgs.mypy ]; - } '' - install -m755 ${systemdBootBuilder} $out - mypy \ - --no-implicit-optional \ - --disallow-untyped-calls \ - --disallow-untyped-defs \ - $out - ''; -in { - - imports = - [ (mkRenamedOptionModule [ "boot" "loader" "gummiboot" "enable" ] [ "boot" "loader" "systemd-boot" "enable" ]) - ]; - - options.boot.loader.systemd-boot = { - enable = mkOption { - default = false; - - type = types.bool; - - description = "Whether to enable the systemd-boot (formerly gummiboot) EFI boot manager"; - }; - - editor = mkOption { - default = true; - - type = types.bool; - - description = '' - Whether to allow editing the kernel command-line before - boot. It is recommended to set this to false, as it allows - gaining root access by passing init=/bin/sh as a kernel - parameter. However, it is enabled by default for backwards - compatibility. - ''; - }; - - configurationLimit = mkOption { - default = null; - example = 120; - type = types.nullOr types.int; - description = '' - Maximum number of latest generations in the boot menu. - Useful to prevent boot partition running out of disk space. - - <literal>null</literal> means no limit i.e. all generations - that were not garbage collected yet. - ''; - }; - - consoleMode = mkOption { - default = "keep"; - - type = types.enum [ "0" "1" "2" "auto" "max" "keep" ]; - - description = '' - The resolution of the console. The following values are valid: - - <itemizedlist> - <listitem><para> - <literal>"0"</literal>: Standard UEFI 80x25 mode - </para></listitem> - <listitem><para> - <literal>"1"</literal>: 80x50 mode, not supported by all devices - </para></listitem> - <listitem><para> - <literal>"2"</literal>: The first non-standard mode provided by the device firmware, if any - </para></listitem> - <listitem><para> - <literal>"auto"</literal>: Pick a suitable mode automatically using heuristics - </para></listitem> - <listitem><para> - <literal>"max"</literal>: Pick the highest-numbered available mode - </para></listitem> - <listitem><para> - <literal>"keep"</literal>: Keep the mode selected by firmware (the default) - </para></listitem> - </itemizedlist> - ''; - }; - - memtest86 = { - enable = mkOption { - default = false; - type = types.bool; - description = '' - Make MemTest86 available from the systemd-boot menu. MemTest86 is a - program for testing memory. MemTest86 is an unfree program, so - this requires <literal>allowUnfree</literal> to be set to - <literal>true</literal>. - ''; - }; - - entryFilename = mkOption { - default = "memtest86.conf"; - type = types.str; - description = '' - <literal>systemd-boot</literal> orders the menu entries by the config file names, - so if you want something to appear after all the NixOS entries, - it should start with <filename>o</filename> or onwards. - ''; - }; - }; - - netbootxyz = { - enable = mkOption { - default = false; - type = types.bool; - description = '' - Make <literal>netboot.xyz</literal> available from the - <literal>systemd-boot</literal> menu. <literal>netboot.xyz</literal> - is a menu system that allows you to boot OS installers and - utilities over the network. - ''; - }; - - entryFilename = mkOption { - default = "o_netbootxyz.conf"; - type = types.str; - description = '' - <literal>systemd-boot</literal> orders the menu entries by the config file names, - so if you want something to appear after all the NixOS entries, - it should start with <filename>o</filename> or onwards. - ''; - }; - }; - - extraEntries = mkOption { - type = types.attrsOf types.lines; - default = {}; - example = literalExpression '' - { "memtest86.conf" = ''' - title MemTest86 - efi /efi/memtest86/memtest86.efi - '''; } - ''; - description = '' - Any additional entries you want added to the <literal>systemd-boot</literal> menu. - These entries will be copied to <filename>/boot/loader/entries</filename>. - Each attribute name denotes the destination file name, - and the corresponding attribute value is the contents of the entry. - - <literal>systemd-boot</literal> orders the menu entries by the config file names, - so if you want something to appear after all the NixOS entries, - it should start with <filename>o</filename> or onwards. - ''; - }; - - extraFiles = mkOption { - type = types.attrsOf types.path; - default = {}; - example = literalExpression '' - { "efi/memtest86/memtest86.efi" = "''${pkgs.memtest86-efi}/BOOTX64.efi"; } - ''; - description = '' - A set of files to be copied to <filename>/boot</filename>. - Each attribute name denotes the destination file name in - <filename>/boot</filename>, while the corresponding - attribute value specifies the source file. - ''; - }; - - graceful = mkOption { - default = false; - - type = types.bool; - - description = '' - Invoke <literal>bootctl install</literal> with the <literal>--graceful</literal> option, - which ignores errors when EFI variables cannot be written or when the EFI System Partition - cannot be found. Currently only applies to random seed operations. - - Only enable this option if <literal>systemd-boot</literal> otherwise fails to install, as the - scope or implication of the <literal>--graceful</literal> option may change in the future. - ''; - }; - - }; - - config = mkIf cfg.enable { - assertions = [ - { - assertion = (config.boot.kernelPackages.kernel.features or { efiBootStub = true; }) ? efiBootStub; - message = "This kernel does not support the EFI boot stub"; - } - ] ++ concatMap (filename: [ - { - assertion = !(hasInfix "/" filename); - message = "boot.loader.systemd-boot.extraEntries.${lib.strings.escapeNixIdentifier filename} is invalid: entries within folders are not supported"; - } - { - assertion = hasSuffix ".conf" filename; - message = "boot.loader.systemd-boot.extraEntries.${lib.strings.escapeNixIdentifier filename} is invalid: entries must have a .conf file extension"; - } - ]) (builtins.attrNames cfg.extraEntries) - ++ concatMap (filename: [ - { - assertion = !(hasPrefix "/" filename); - message = "boot.loader.systemd-boot.extraFiles.${lib.strings.escapeNixIdentifier filename} is invalid: paths must not begin with a slash"; - } - { - assertion = !(hasInfix ".." filename); - message = "boot.loader.systemd-boot.extraFiles.${lib.strings.escapeNixIdentifier filename} is invalid: paths must not reference the parent directory"; - } - { - assertion = !(hasInfix "nixos/.extra-files" (toLower filename)); - message = "boot.loader.systemd-boot.extraFiles.${lib.strings.escapeNixIdentifier filename} is invalid: files cannot be placed in the nixos/.extra-files directory"; - } - ]) (builtins.attrNames cfg.extraFiles); - - boot.loader.grub.enable = mkDefault false; - - boot.loader.supportsInitrdSecrets = true; - - boot.loader.systemd-boot.extraFiles = mkMerge [ - # TODO: This is hard-coded to use the 64-bit EFI app, but it could probably - # be updated to use the 32-bit EFI app on 32-bit systems. The 32-bit EFI - # app filename is BOOTIA32.efi. - (mkIf cfg.memtest86.enable { - "efi/memtest86/BOOTX64.efi" = "${pkgs.memtest86-efi}/BOOTX64.efi"; - }) - (mkIf cfg.netbootxyz.enable { - "efi/netbootxyz/netboot.xyz.efi" = "${pkgs.netbootxyz-efi}"; - }) - ]; - - boot.loader.systemd-boot.extraEntries = mkMerge [ - (mkIf cfg.memtest86.enable { - "${cfg.memtest86.entryFilename}" = '' - title MemTest86 - efi /efi/memtest86/BOOTX64.efi - ''; - }) - (mkIf cfg.netbootxyz.enable { - "${cfg.netbootxyz.entryFilename}" = '' - title netboot.xyz - efi /efi/netbootxyz/netboot.xyz.efi - ''; - }) - ]; - - system = { - build.installBootLoader = checkedSystemdBootBuilder; - - boot.loader.id = "systemd-boot"; - - requiredKernelConfig = with config.lib.kernelConfig; [ - (isYes "EFI_STUB") - ]; - }; - }; -} |