Move all of NixOS to nixos/ in preparation of the repository merge
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
#! @bash@/bin/sh -e
|
||||
|
||||
shopt -s nullglob
|
||||
|
||||
export PATH=/empty
|
||||
for i in @path@; do PATH=$PATH:$i/bin:$i/sbin; done
|
||||
|
||||
default=$1
|
||||
if test -z "$1"; then
|
||||
echo "Syntax: efi-boot-stub-builder.sh <DEFAULT-CONFIG>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "updating the efi system partition..."
|
||||
|
||||
# Convert a path to a file in the Nix store such as
|
||||
# /nix/store/<hash>-<name>/file to <hash>-<name>-<file>.
|
||||
# Also, efi executables need the .efi extension
|
||||
cleanName() {
|
||||
local path="$1"
|
||||
echo "$path" | sed 's|^/nix/store/||' | sed 's|/|-|g' | sed 's|@kernelFile@$|@kernelFile@.efi|'
|
||||
}
|
||||
|
||||
# Copy a file from the Nix store to the EFI system partition
|
||||
declare -A filesCopied
|
||||
|
||||
copyToKernelsDir() {
|
||||
local src="$1"
|
||||
local dst="@efiSysMountPoint@/efi/nixos/$(cleanName $src)"
|
||||
# Don't copy the file if $dst already exists. This means that we
|
||||
# have to create $dst atomically to prevent partially copied
|
||||
# kernels or initrd if this script is ever interrupted.
|
||||
if ! test -e $dst; then
|
||||
local dstTmp=$dst.tmp.$$
|
||||
cp $src $dstTmp
|
||||
mv $dstTmp $dst
|
||||
fi
|
||||
filesCopied[$dst]=1
|
||||
result=$dst
|
||||
}
|
||||
|
||||
# Copy its kernel, initrd, and startup script to the efi system partition
|
||||
# Add the efibootmgr entry if requested
|
||||
addEntry() {
|
||||
local path="$1"
|
||||
local generation="$2"
|
||||
|
||||
if ! test -e $path/kernel -a -e $path/initrd; then
|
||||
return
|
||||
fi
|
||||
|
||||
local kernel=$(readlink -f $path/kernel)
|
||||
local initrd=$(readlink -f $path/initrd)
|
||||
copyToKernelsDir $kernel; kernel=$result
|
||||
copyToKernelsDir $initrd; initrd=$result
|
||||
|
||||
local startup="@efiSysMountPoint@/efi/nixos/generation-$generation-startup.nsh"
|
||||
if ! test -e $startup; then
|
||||
local dstTmp=$startup.tmp.$$
|
||||
echo "$(echo $kernel | sed 's|@efiSysMountPoint@||' | sed 's|/|\\|g') systemConfig=$(readlink -f $path) init=$(readlink -f $path/init) initrd=$(echo $initrd | sed 's|@efiSysMountPoint@||' | sed 's|/|\\|g') $(cat $path/kernel-params)" > $dstTmp
|
||||
mv $dstTmp $startup
|
||||
fi
|
||||
filesCopied[$startup]=1
|
||||
|
||||
if test -n "@runEfibootmgr@"; then
|
||||
set +e
|
||||
efibootmgr -c -d "@efiDisk@" -g -l $(echo $kernel | sed 's|@efiSysMountPoint@||' | sed 's|/|\\|g') -L "NixOS $generation Generation" -p "@efiPartition@" \
|
||||
-u systemConfig=$(readlink -f $path) init=$(readlink -f $path/init) initrd=$(echo $initrd | sed 's|@efiSysMountPoint@||' | sed 's|/|\\|g') $(cat $path/kernel-params) > /dev/null 2>&1
|
||||
set -e
|
||||
fi
|
||||
|
||||
if test $(readlink -f "$path") = "$default"; then
|
||||
if test -n "@runEfibootmgr@"; then
|
||||
set +e
|
||||
defaultbootnum=$(efibootmgr | grep "NixOS $generation Generation" | sed 's/Boot//' | sed 's/\*.*//')
|
||||
set -e
|
||||
fi
|
||||
|
||||
if test -n "@installStartupNsh@"; then
|
||||
sed 's|.*@kernelFile@.efi|@kernelFile@.efi|' < $startup > "@efiSysMountPoint@/startup.nsh"
|
||||
cp $kernel "@efiSysMountPoint@/@kernelFile@.efi"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
mkdir -p "@efiSysMountPoint@/efi/nixos/"
|
||||
|
||||
# Remove all old boot manager entries
|
||||
if test -n "@runEfibootmgr@"; then
|
||||
set +e
|
||||
modprobe efivars > /dev/null 2>&1
|
||||
for bootnum in $(efibootmgr | grep "NixOS" | grep "Generation" | sed 's/Boot//' | sed 's/\*.*//'); do
|
||||
efibootmgr -B -b "$bootnum" > /dev/null 2>&1
|
||||
done
|
||||
set -e
|
||||
fi
|
||||
|
||||
# Add all generations of the system profile to the system partition, in reverse
|
||||
# (most recent to least recent) order.
|
||||
for generation in $(
|
||||
(cd /nix/var/nix/profiles && ls -d system-*-link) \
|
||||
| sed 's/system-\([0-9]\+\)-link/\1/' \
|
||||
| sort -n -r); do
|
||||
link=/nix/var/nix/profiles/system-$generation-link
|
||||
addEntry $link $generation
|
||||
done
|
||||
|
||||
if test -n "@runEfibootmgr@"; then
|
||||
set +e
|
||||
efibootmgr -o $defaultbootnum > /dev/null 2>&1
|
||||
set -e
|
||||
fi
|
||||
|
||||
if test -n "@efiShell@"; then
|
||||
mkdir -pv "@efiSysMountPoint@"/efi/boot
|
||||
cp "@efiShell@" "@efiSysMountPoint@"/efi/boot/boot"@targetArch@".efi
|
||||
fi
|
||||
|
||||
# Remove obsolete files from the EFI system partition
|
||||
for fn in "@efiSysMountPoint@/efi/nixos/"*; do
|
||||
if ! test "${filesCopied[$fn]}" = 1; then
|
||||
rm -vf -- "$fn"
|
||||
fi
|
||||
done
|
||||
|
||||
# Run any extra commands users may need
|
||||
if test -n "@runEfibootmgr@"; then
|
||||
set +e
|
||||
@postEfiBootMgrCommands@
|
||||
set -e
|
||||
fi
|
||||
@@ -0,0 +1,98 @@
|
||||
{pkgs, config, ...}:
|
||||
|
||||
with pkgs.lib;
|
||||
|
||||
let
|
||||
efiBootStubBuilder = pkgs.substituteAll {
|
||||
src = ./efi-boot-stub-builder.sh;
|
||||
isExecutable = true;
|
||||
inherit (pkgs) bash;
|
||||
path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep pkgs.glibc] ++ (pkgs.stdenv.lib.optionals config.boot.loader.efi.canTouchEfiVariables [pkgs.efibootmgr pkgs.module_init_tools]);
|
||||
inherit (config.boot.loader.efiBootStub) installStartupNsh;
|
||||
|
||||
inherit (config.boot.loader.efi) efiSysMountPoint;
|
||||
|
||||
inherit (config.boot.loader.efi.efibootmgr) efiDisk efiPartition postEfiBootMgrCommands;
|
||||
|
||||
runEfibootmgr = config.boot.loader.efi.canTouchEfiVariables;
|
||||
|
||||
efiShell = if config.boot.loader.efiBootStub.installShell then
|
||||
if pkgs.stdenv.isi686 then
|
||||
pkgs.fetchurl {
|
||||
url = "https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2/EdkShellBinPkg/FullShell/Ia32/Shell_Full.efi";
|
||||
sha256 = "1gv6kyaspczdp7x8qnx5x76ilriaygkfs99ay7ihhdi6riclkhfl";
|
||||
}
|
||||
else
|
||||
pkgs.fetchurl {
|
||||
url = "https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2/EdkShellBinPkg/FullShell/X64/Shell_Full.efi";
|
||||
sha256 = "1g18z84rlavxr5gsrh2g942rfr6znv9fs3fqww5m7dhmnysgyv8p";
|
||||
}
|
||||
else
|
||||
null;
|
||||
|
||||
kernelFile = platform.kernelTarget;
|
||||
targetArch = if pkgs.stdenv.isi686 then
|
||||
"IA32"
|
||||
else if pkgs.stdenv.isx86_64 then
|
||||
"X64"
|
||||
else
|
||||
throw "Unsupported architecture";
|
||||
};
|
||||
|
||||
# Temporary check, for nixos to cope both with nixpkgs stdenv-updates and trunk
|
||||
platform = pkgs.stdenv.platform;
|
||||
in
|
||||
{
|
||||
options = {
|
||||
boot = {
|
||||
loader = {
|
||||
efiBootStub = {
|
||||
|
||||
enable = mkOption {
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to use the linux kernel as an EFI bootloader.
|
||||
When enabled, the kernel, initrd, and an EFI shell script
|
||||
to boot the system are copied to the EFI system partition.
|
||||
'';
|
||||
};
|
||||
|
||||
installStartupNsh = mkOption {
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to install a startup.nsh in the root of the EFI system partition.
|
||||
For now, it will just boot the latest version when run, the eventual goal
|
||||
is to have a basic menu-type interface.
|
||||
'';
|
||||
};
|
||||
|
||||
installShell = mkOption {
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to install an EFI shell in \EFI\BOOT.
|
||||
This _should_ only be needed for removable devices
|
||||
(CDs, usb sticks, etc.), but it may be an option for broken
|
||||
systems where efibootmgr doesn't work. Particularly useful in
|
||||
conjunction with installStartupNsh
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf config.boot.loader.efiBootStub.enable {
|
||||
assertions = [ { assertion = ! config.boot.kernelPackages.kernel ? features || config.boot.kernelPackages.kernel.features ? efiBootStub; message = "This kernel does not support the EFI boot stub"; } ];
|
||||
|
||||
system = {
|
||||
build.installBootLoader = efiBootStubBuilder;
|
||||
boot.loader.id = "efiBootStub";
|
||||
boot.loader.kernelFile = platform.kernelTarget;
|
||||
requiredKernelConfig = with config.lib.kernelConfig; [
|
||||
(isYes "EFI_STUB")
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
{ pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
|
||||
{
|
||||
options.boot.loader.efi = {
|
||||
canTouchEfiVariables = mkOption {
|
||||
default = false;
|
||||
|
||||
type = types.bool;
|
||||
|
||||
description = "Whether or not the installation process should modify efi boot variables.";
|
||||
};
|
||||
|
||||
efibootmgr = {
|
||||
efiDisk = mkOption {
|
||||
default = "/dev/sda";
|
||||
|
||||
type = types.string;
|
||||
|
||||
description = "The disk that contains the EFI system partition.";
|
||||
};
|
||||
|
||||
efiPartition = mkOption {
|
||||
default = "1";
|
||||
description = "The partition number of the EFI system partition.";
|
||||
};
|
||||
|
||||
postEfiBootMgrCommands = mkOption {
|
||||
default = "";
|
||||
type = types.string;
|
||||
description = ''
|
||||
Shell commands to be executed immediately after efibootmgr has setup the system EFI.
|
||||
Some systems do not follow the EFI specifications properly and insert extra entries.
|
||||
Others will brick (fix by removing battery) on boot when it finds more than X entries.
|
||||
This hook allows for running a few extra efibootmgr commands to combat these issues.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
efiSysMountPoint = mkOption {
|
||||
default = "/boot";
|
||||
|
||||
type = types.string;
|
||||
|
||||
description = "Where the EFI System Partition is mounted.";
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
#! @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
|
||||
@@ -0,0 +1,63 @@
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
with pkgs.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;
|
||||
};
|
||||
|
||||
# Temporary check, for nixos to cope both with nixpkgs stdenv-updates and trunk
|
||||
platform = pkgs.stdenv.platform;
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
options = {
|
||||
|
||||
boot.loader.generationsDir = {
|
||||
|
||||
enable = mkOption {
|
||||
default = false;
|
||||
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;
|
||||
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 = platform.kernelTarget;
|
||||
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
|
||||
let
|
||||
|
||||
cfg = config.boot.loader.grub;
|
||||
|
||||
realGrub = if cfg.version == 1 then pkgs.grub else pkgs.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;
|
||||
|
||||
f = x: if x == null then "" else "" + x;
|
||||
|
||||
grubConfig = pkgs.writeText "grub-config.xml" (builtins.toXML
|
||||
{ splashImage = f config.boot.loader.grub.splashImage;
|
||||
grub = f grub;
|
||||
shell = "${pkgs.stdenv.shell}";
|
||||
fullVersion = (builtins.parseDrvName realGrub.name).version;
|
||||
inherit (cfg)
|
||||
version extraConfig extraPerEntryConfig extraEntries
|
||||
extraEntriesBeforeNixOS extraPrepareConfig configurationLimit copyKernels timeout
|
||||
default devices;
|
||||
path = (makeSearchPath "bin" [
|
||||
pkgs.coreutils pkgs.gnused pkgs.gnugrep pkgs.findutils pkgs.diffutils
|
||||
]) + ":" + (makeSearchPath "sbin" [
|
||||
pkgs.mdadm
|
||||
]);
|
||||
});
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
|
||||
###### interface
|
||||
|
||||
options = {
|
||||
|
||||
boot.loader.grub = {
|
||||
|
||||
enable = mkOption {
|
||||
default = true;
|
||||
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/hda";
|
||||
type = types.uniq types.string;
|
||||
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/hda" ];
|
||||
type = types.listOf types.string;
|
||||
description = ''
|
||||
The devices on which the boot loader, GRUB, will be
|
||||
installed. Can be used instead of <literal>device</literal> to
|
||||
install grub into multiple devices (e.g., if as softraid arrays holding /boot).
|
||||
'';
|
||||
};
|
||||
|
||||
# !!! How can we mark options as obsolete?
|
||||
bootDevice = mkOption {
|
||||
default = "";
|
||||
description = "Obsolete.";
|
||||
};
|
||||
|
||||
configurationName = mkOption {
|
||||
default = "";
|
||||
example = "Stable 2.6.21";
|
||||
type = types.uniq types.string;
|
||||
description = ''
|
||||
GRUB entry name instead of default.
|
||||
'';
|
||||
};
|
||||
|
||||
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; terminal_output.serial";
|
||||
type = types.lines;
|
||||
description = ''
|
||||
Additional GRUB commands inserted in the configuration file
|
||||
just before the 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 "Windows7" {
|
||||
title Windows7
|
||||
insmod ntfs
|
||||
set root='(hd1,1)'
|
||||
chainloader +1
|
||||
}
|
||||
'';
|
||||
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 {
|
||||
default = {};
|
||||
example = literalExample ''
|
||||
{ "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.
|
||||
'';
|
||||
};
|
||||
|
||||
splashImage = mkOption {
|
||||
default =
|
||||
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 ./winkler-gnu-blue-640x480.png;
|
||||
example = null;
|
||||
description = ''
|
||||
Background image used for GRUB. It must be a 640x480,
|
||||
14-colour image in XPM format, optionally compressed with
|
||||
<command>gzip</command> or <command>bzip2</command>. Set to
|
||||
<literal>null</literal> to run GRUB in text mode.
|
||||
'';
|
||||
};
|
||||
|
||||
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 /.
|
||||
'';
|
||||
};
|
||||
|
||||
timeout = mkOption {
|
||||
default = 5;
|
||||
type = types.int;
|
||||
description = ''
|
||||
Timeout (in seconds) until GRUB boots the default menu item.
|
||||
'';
|
||||
};
|
||||
|
||||
default = mkOption {
|
||||
default = 0;
|
||||
type = types.int;
|
||||
description = ''
|
||||
Index of the default menu item to be booted.
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
###### implementation
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
boot.loader.grub.devices = optional (cfg.device != "") cfg.device;
|
||||
|
||||
system.build = mkAssert (cfg.devices != [])
|
||||
"You must set the ‘boot.loader.grub.device’ option to make the system bootable."
|
||||
{ installBootLoader =
|
||||
"PERL5LIB=${makePerlPath [ pkgs.perlPackages.XMLLibXML pkgs.perlPackages.XMLSAX ]} " +
|
||||
"${pkgs.perl}/bin/perl ${./install-grub.pl} ${grubConfig}";
|
||||
inherit grub;
|
||||
};
|
||||
|
||||
# Common attribute for boot loaders so only one of them can be
|
||||
# set at once.
|
||||
system.boot.loader.id = "grub";
|
||||
|
||||
environment.systemPackages = [ grub ];
|
||||
|
||||
boot.loader.grub.extraPrepareConfig =
|
||||
concatStrings (mapAttrsToList (n: v: ''
|
||||
${pkgs.coreutils}/bin/cp -pf "${v}" "/boot/${n}"
|
||||
'') config.boot.loader.grub.extraFiles);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
use strict;
|
||||
use warnings;
|
||||
use XML::LibXML;
|
||||
use File::Basename;
|
||||
use File::Path;
|
||||
use File::stat;
|
||||
use File::Copy;
|
||||
use POSIX;
|
||||
use Cwd;
|
||||
|
||||
my $defaultConfig = $ARGV[1] or die;
|
||||
|
||||
my $dom = XML::LibXML->load_xml(location => $ARGV[0]);
|
||||
|
||||
sub get { my ($name) = @_; return $dom->findvalue("/expr/attrs/attr[\@name = '$name']/*/\@value"); }
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
my $grub = get("grub");
|
||||
my $grubVersion = int(get("version"));
|
||||
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 $configurationLimit = int(get("configurationLimit"));
|
||||
my $copyKernels = get("copyKernels") eq "true";
|
||||
my $timeout = int(get("timeout"));
|
||||
my $defaultEntry = int(get("default"));
|
||||
$ENV{'PATH'} = get("path");
|
||||
|
||||
die "unsupported GRUB version\n" if $grubVersion != 1 && $grubVersion != 2;
|
||||
|
||||
print STDERR "updating GRUB $grubVersion menu...\n";
|
||||
|
||||
mkpath("/boot/grub", 0, 0700);
|
||||
|
||||
|
||||
# Discover whether /boot is on the same filesystem as / and
|
||||
# /nix/store. If not, then all kernels and initrds must be copied to
|
||||
# /boot, and all paths in the GRUB config file must be relative to the
|
||||
# root of the /boot filesystem. `$bootRoot' is the path to be
|
||||
# prepended to paths under /boot.
|
||||
my $bootRoot = "/boot";
|
||||
if (stat("/")->dev != stat("/boot")->dev) {
|
||||
$bootRoot = "";
|
||||
$copyKernels = 1;
|
||||
} elsif (stat("/boot")->dev != stat("/nix/store")->dev) {
|
||||
$copyKernels = 1;
|
||||
}
|
||||
|
||||
|
||||
# Generate the header.
|
||||
my $conf .= "# Automatically generated. DO NOT EDIT THIS FILE!\n";
|
||||
|
||||
if ($grubVersion == 1) {
|
||||
$conf .= "
|
||||
default $defaultEntry
|
||||
timeout $timeout
|
||||
";
|
||||
if ($splashImage) {
|
||||
copy $splashImage, "/boot/background.xpm.gz" or die "cannot copy $splashImage to /boot\n";
|
||||
$conf .= "splashimage $bootRoot/background.xpm.gz\n";
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
$conf .= "
|
||||
if [ -s \$prefix/grubenv ]; then
|
||||
load_env
|
||||
fi
|
||||
|
||||
# ‘grub-reboot’ sets a one-time saved entry, which we process here and
|
||||
# then delete.
|
||||
if [ \"\${saved_entry}\" ]; then
|
||||
# The next line *has* to look exactly like this, otherwise KDM's
|
||||
# reboot feature won't work properly with GRUB 2.
|
||||
set default=\"\${saved_entry}\"
|
||||
set saved_entry=
|
||||
set prev_saved_entry=
|
||||
save_env saved_entry
|
||||
save_env prev_saved_entry
|
||||
set timeout=1
|
||||
else
|
||||
set default=$defaultEntry
|
||||
set timeout=$timeout
|
||||
fi
|
||||
|
||||
if loadfont $bootRoot/grub/fonts/unicode.pf2; then
|
||||
set gfxmode=640x480
|
||||
insmod gfxterm
|
||||
insmod vbe
|
||||
terminal_output gfxterm
|
||||
fi
|
||||
";
|
||||
|
||||
if ($splashImage) {
|
||||
# FIXME: GRUB 1.97 doesn't resize the background image if it
|
||||
# doesn't match the video resolution.
|
||||
copy $splashImage, "/boot/background.png" or die "cannot copy $splashImage to /boot\n";
|
||||
$conf .= "
|
||||
insmod png
|
||||
if background_image $bootRoot/background.png; then
|
||||
set color_normal=white/black
|
||||
set color_highlight=black/white
|
||||
else
|
||||
set menu_color_normal=cyan/blue
|
||||
set menu_color_highlight=white/blue
|
||||
fi
|
||||
";
|
||||
}
|
||||
}
|
||||
|
||||
$conf .= "$extraConfig\n";
|
||||
|
||||
|
||||
# Generate the menu entries.
|
||||
$conf .= "\n";
|
||||
|
||||
my %copied;
|
||||
mkpath("/boot/kernels", 0, 0755) if $copyKernels;
|
||||
|
||||
sub copyToKernelsDir {
|
||||
my ($path) = @_;
|
||||
return $path unless $copyKernels;
|
||||
$path =~ /\/nix\/store\/(.*)/ or die;
|
||||
my $name = $1; $name =~ s/\//-/g;
|
||||
my $dst = "/boot/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 "$bootRoot/kernels/$name";
|
||||
}
|
||||
|
||||
sub addEntry {
|
||||
my ($name, $path) = @_;
|
||||
return unless -e "$path/kernel" && -e "$path/initrd";
|
||||
|
||||
my $kernel = copyToKernelsDir(Cwd::abs_path("$path/kernel"));
|
||||
my $initrd = copyToKernelsDir(Cwd::abs_path("$path/initrd"));
|
||||
my $xen = -e "$path/xen.gz" ? copyToKernelsDir(Cwd::abs_path("$path/xen.gz")) : undef;
|
||||
|
||||
# FIXME: $confName
|
||||
|
||||
my $kernelParams =
|
||||
"systemConfig=" . Cwd::abs_path($path) . " " .
|
||||
"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\n";
|
||||
} else {
|
||||
$conf .= "menuentry \"$name\" {\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);
|
||||
|
||||
$conf .= "$extraEntries\n" unless $extraEntriesBeforeNixOS;
|
||||
|
||||
# extraEntries could refer to @bootRoot@, which we have to substitute
|
||||
$conf =~ s/\@bootRoot\@/$bootRoot/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;
|
||||
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'";
|
||||
}
|
||||
}
|
||||
|
||||
# Run extraPrepareConfig in sh
|
||||
if ($extraPrepareConfig ne "") {
|
||||
system((get("shell"), "-c", $extraPrepareConfig));
|
||||
}
|
||||
|
||||
# Atomically update the GRUB config.
|
||||
my $confFile = $grubVersion == 1 ? "/boot/grub/menu.lst" : "/boot/grub/grub.cfg";
|
||||
my $tmpFile = $confFile . ".tmp";
|
||||
writeFile($tmpFile, $conf);
|
||||
rename $tmpFile, $confFile or die "cannot rename $tmpFile to $confFile\n";
|
||||
|
||||
|
||||
# Remove obsolete files from /boot/kernels.
|
||||
foreach my $fn (glob "/boot/kernels/*") {
|
||||
next if defined $copied{$fn};
|
||||
print STDERR "removing obsolete file $fn\n";
|
||||
unlink $fn;
|
||||
}
|
||||
|
||||
|
||||
# Install GRUB if the version changed from the last time we installed
|
||||
# it. FIXME: shouldn't we reinstall if ‘devices’ changed?
|
||||
my $prevVersion = readFile("/boot/grub/version") // "";
|
||||
if (($ENV{'NIXOS_INSTALL_GRUB'} // "") eq "1" || get("fullVersion") ne $prevVersion) {
|
||||
foreach my $dev ($dom->findnodes('/expr/attrs/attr[@name = "devices"]/list/string/@value')) {
|
||||
$dev = $dev->findvalue(".") or die;
|
||||
next if $dev eq "nodev";
|
||||
print STDERR "installing the GRUB $grubVersion boot loader on $dev...\n";
|
||||
system("$grub/sbin/grub-install", "--recheck", Cwd::abs_path($dev)) == 0
|
||||
or die "$0: installation of GRUB on $dev failed\n";
|
||||
}
|
||||
writeFile("/boot/grub/version", get("fullVersion"));
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
# This module adds Memtest86+ to the GRUB boot menu.
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
|
||||
let
|
||||
memtest86 = pkgs.memtest86plus;
|
||||
in
|
||||
|
||||
{
|
||||
options = {
|
||||
|
||||
boot.loader.grub.memtest86 = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = ''
|
||||
Make Memtest86+, a memory testing program, available from the
|
||||
GRUB boot menu.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf config.boot.loader.grub.memtest86 {
|
||||
|
||||
boot.loader.grub.extraEntries = mkFixStrictness (
|
||||
if config.boot.loader.grub.version == 2 then
|
||||
''
|
||||
menuentry "Memtest86+" {
|
||||
linux16 @bootRoot@/memtest.bin
|
||||
}
|
||||
''
|
||||
else
|
||||
throw "Memtest86+ is not supported with GRUB 1.");
|
||||
|
||||
boot.loader.grub.extraFiles."memtest.bin" = "${memtest86}/memtest.bin";
|
||||
|
||||
};
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 73 KiB |
@@ -0,0 +1,6 @@
|
||||
This is a resized version of
|
||||
|
||||
http://www.gnu.org/graphics/winkler-gnu-blue.png
|
||||
|
||||
by Kyle Winkler and released under the Free Art License
|
||||
(http://artlibre.org/licence.php/lalgb.html).
|
||||
@@ -0,0 +1,114 @@
|
||||
#! @python@/bin/python
|
||||
import argparse
|
||||
import shutil
|
||||
import os
|
||||
import errno
|
||||
import subprocess
|
||||
import glob
|
||||
import tempfile
|
||||
import errno
|
||||
|
||||
def copy_if_not_exists(source, dest):
|
||||
known_paths.append(dest)
|
||||
if not os.path.exists(dest):
|
||||
shutil.copyfile(source, dest)
|
||||
|
||||
system_dir = lambda generation: "/nix/var/nix/profiles/system-%d-link" % (generation)
|
||||
|
||||
def write_entry(generation, kernel, initrd):
|
||||
entry_file = "@efiSysMountPoint@/loader/entries/nixos-generation-%d.conf" % (generation)
|
||||
generation_dir = os.readlink(system_dir(generation))
|
||||
tmp_path = "%s.tmp" % (entry_file)
|
||||
kernel_params = "systemConfig=%s init=%s/init " % (generation_dir, 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:
|
||||
print >> f, "title NixOS"
|
||||
print >> f, "version Generation %d" % (generation)
|
||||
if machine_id is not None: print >> f, "machine-id %s" % (machine_id)
|
||||
print >> f, "linux %s" % (kernel)
|
||||
print >> f, "initrd %s" % (initrd)
|
||||
print >> f, "options %s" % (kernel_params)
|
||||
os.rename(tmp_path, entry_file)
|
||||
|
||||
def write_loader_conf(generation):
|
||||
with open("@efiSysMountPoint@/loader/loader.conf.tmp", 'w') as f:
|
||||
if "@timeout@" != "":
|
||||
print >> f, "timeout @timeout@"
|
||||
print >> f, "default nixos-generation-%d" % (generation)
|
||||
os.rename("@efiSysMountPoint@/loader/loader.conf.tmp", "@efiSysMountPoint@/loader/loader.conf")
|
||||
|
||||
def copy_from_profile(generation, name):
|
||||
store_file_path = os.readlink("%s/%s" % (system_dir(generation), 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)
|
||||
copy_if_not_exists(store_file_path, "@efiSysMountPoint@%s" % (efi_file_path))
|
||||
return efi_file_path
|
||||
|
||||
def add_entry(generation):
|
||||
efi_kernel_path = copy_from_profile(generation, "kernel")
|
||||
efi_initrd_path = copy_from_profile(generation, "initrd")
|
||||
write_entry(generation, efi_kernel_path, efi_initrd_path)
|
||||
|
||||
def mkdir_p(path):
|
||||
try:
|
||||
os.makedirs(path)
|
||||
except OSError as e:
|
||||
if e.errno != errno.EEXIST or not os.path.isdir(path):
|
||||
raise
|
||||
|
||||
def get_generations(profile):
|
||||
gen_list = subprocess.check_output([
|
||||
"@nix@/bin/nix-env",
|
||||
"--list-generations",
|
||||
"-p",
|
||||
"/nix/var/nix/profiles/%s" % (profile)
|
||||
])
|
||||
gen_lines = gen_list.split('\n')
|
||||
gen_lines.pop()
|
||||
return [ int(line.split()[0]) for line in gen_lines ]
|
||||
|
||||
def remove_old_entries(gens):
|
||||
slice_start = len("@efiSysMountPoint@/loader/entries/nixos-generation-")
|
||||
slice_end = -1 * len(".conf")
|
||||
for path in glob.iglob("@efiSysMountPoint@/loader/entries/nixos-generation-[1-9]*.conf"):
|
||||
try:
|
||||
gen = int(path[slice_start:slice_end])
|
||||
if not gen in gens:
|
||||
os.unlink(path)
|
||||
except ValueError:
|
||||
pass
|
||||
for path in glob.iglob("@efiSysMountPoint@/efi/nixos/*"):
|
||||
if not path in known_paths:
|
||||
os.unlink(path)
|
||||
|
||||
parser = argparse.ArgumentParser(description='Update NixOS-related gummiboot files')
|
||||
parser.add_argument('default_config', metavar='DEFAULT-CONFIG', help='The default NixOS config to boot')
|
||||
args = parser.parse_args()
|
||||
|
||||
# We deserve our own env var!
|
||||
if os.getenv("NIXOS_INSTALL_GRUB") == "1":
|
||||
if "@canTouchEfiVariables@" == "1":
|
||||
subprocess.check_call(["@gummiboot@/bin/gummiboot", "--path=@efiSysMountPoint@", "install"])
|
||||
else:
|
||||
subprocess.check_call(["@gummiboot@/bin/gummiboot", "--path=@efiSysMountPoint@", "--no-variables", "install"])
|
||||
|
||||
known_paths = []
|
||||
mkdir_p("@efiSysMountPoint@/efi/nixos")
|
||||
mkdir_p("@efiSysMountPoint@/loader/entries")
|
||||
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
|
||||
machine_id = None
|
||||
|
||||
gens = get_generations("system")
|
||||
for gen in gens:
|
||||
add_entry(gen)
|
||||
if os.readlink(system_dir(gen)) == args.default_config:
|
||||
write_loader_conf(gen)
|
||||
|
||||
remove_old_entries(gens)
|
||||
@@ -0,0 +1,67 @@
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
|
||||
let
|
||||
cfg = config.boot.loader.gummiboot;
|
||||
|
||||
efi = config.boot.loader.efi;
|
||||
|
||||
gummibootBuilder = pkgs.substituteAll {
|
||||
src = ./gummiboot-builder.py;
|
||||
|
||||
isExecutable = true;
|
||||
|
||||
inherit (pkgs) python gummiboot;
|
||||
|
||||
inherit (config.environment) nix;
|
||||
|
||||
inherit (cfg) timeout;
|
||||
|
||||
inherit (efi) efiSysMountPoint canTouchEfiVariables;
|
||||
};
|
||||
in {
|
||||
options.boot.loader.gummiboot = {
|
||||
enable = mkOption {
|
||||
default = false;
|
||||
|
||||
type = types.bool;
|
||||
|
||||
description = "Whether to enable the gummiboot UEFI boot manager";
|
||||
};
|
||||
|
||||
timeout = mkOption {
|
||||
default = null;
|
||||
|
||||
example = 4;
|
||||
|
||||
type = types.nullOr types.int;
|
||||
|
||||
description = ''
|
||||
Timeout (in seconds) for how long to show the menu (null if none).
|
||||
Note that even with no timeout the menu can be forced if the space
|
||||
key is pressed during bootup
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
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";
|
||||
}
|
||||
];
|
||||
|
||||
system = {
|
||||
build.installBootLoader = gummibootBuilder;
|
||||
|
||||
boot.loader.id = "gummiboot";
|
||||
|
||||
requiredKernelConfig = with config.lib.kernelConfig; [
|
||||
(isYes "EFI_STUB")
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
#! @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 "export systemConfig=$(readlink -f $path)"
|
||||
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/fine-tune/* ) | 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/\..*//')
|
||||
kernelVersion=$(cd $(dirname $(readlink -f $link/kernel))/lib/modules && echo *)
|
||||
addEntry "NixOS - Configuration $generation ($date - $kernelVersion)" $link "$generation ($date)"
|
||||
done
|
||||
|
||||
mv $tmpOther $targetOther
|
||||
mv $tmp $target
|
||||
@@ -0,0 +1,50 @@
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
with pkgs.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;
|
||||
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;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
#! @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: builder.sh <DEFAULT-CONFIG>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "updating the boot generations directory..."
|
||||
|
||||
mkdir -p /boot/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 /boot/kernels.
|
||||
declare -A filesCopied
|
||||
|
||||
copyToKernelsDir() {
|
||||
local src="$1"
|
||||
local dst="/boot/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=/boot/old
|
||||
mkdir -p $outdir || true
|
||||
|
||||
# Copy its kernel and initrd to /boot/kernels.
|
||||
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)
|
||||
|
||||
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 $(readlink -f "$path") = "$default"; then
|
||||
copyForced $kernel /boot/kernel.img
|
||||
# copyForced $initrd /boot/initrd
|
||||
cp "$(readlink -f "$path/init")" /boot/nixos-init
|
||||
echo "`cat $path/kernel-params` init=$path/init" >/boot/cmdline.txt
|
||||
|
||||
echo "$2" > /boot/defaultgeneration
|
||||
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
|
||||
|
||||
# Add the firmware files
|
||||
fwdir=@firmware@/share/raspberrypi/boot/
|
||||
copyForced $fwdir/bootcode.bin /boot/bootcode.bin
|
||||
copyForced $fwdir/fixup.dat /boot/fixup.dat
|
||||
copyForced $fwdir/fixup_cd.dat /boot/fixup_cd.dat
|
||||
copyForced $fwdir/start.elf /boot/start.elf
|
||||
copyForced $fwdir/start_cd.elf /boot/start_cd.elf
|
||||
|
||||
# Remove obsolete files from /boot/old.
|
||||
for fn in /boot/old/*linux* /boot/old/*initrd*; do
|
||||
if ! test "${filesCopied[$fn]}" = 1; then
|
||||
rm -vf -- "$fn"
|
||||
fi
|
||||
done
|
||||
@@ -0,0 +1,38 @@
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
|
||||
let
|
||||
|
||||
builder = pkgs.substituteAll {
|
||||
src = ./builder.sh;
|
||||
isExecutable = true;
|
||||
inherit (pkgs) bash;
|
||||
path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep];
|
||||
firmware = pkgs.raspberrypifw;
|
||||
};
|
||||
|
||||
platform = pkgs.stdenv.platform;
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
options = {
|
||||
|
||||
boot.loader.raspberryPi.enable = mkOption {
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to create files with the system generations in
|
||||
<literal>/boot</literal>.
|
||||
<literal>/boot/old</literal> will hold files from old generations.
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = mkIf config.boot.loader.raspberryPi.enable {
|
||||
system.build.installBootLoader = builder;
|
||||
system.boot.loader.id = "raspberrypi";
|
||||
system.boot.loader.kernelFile = platform.kernelTarget;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user