diff --git a/default.nix b/default.nix
index e63a76cd03f..6b929537ee4 100644
--- a/default.nix
+++ b/default.nix
@@ -11,11 +11,22 @@ let
inherit (eval) config pkgs;
+ # This is for `nixos-rebuild build-vm'.
vmConfig = (import ./lib/eval-config.nix {
inherit system;
modules = [ configuration ./modules/virtualisation/qemu-vm.nix ];
}).config;
-
+
+ # This is for `nixos-rebuild build-vm-with-bootloader'.
+ vmWithBootLoaderConfig = (import ./lib/eval-config.nix {
+ inherit system;
+ modules =
+ [ configuration
+ ./modules/virtualisation/qemu-vm.nix
+ { virtualisation.useBootLoader = true; }
+ ];
+ }).config;
+
in
{
@@ -25,6 +36,8 @@ in
vm = vmConfig.system.build.vm;
+ vmWithBootLoader = vmWithBootLoaderConfig.system.build.vm;
+
# The following are used by nixos-rebuild.
nixFallback = pkgs.nixUnstable;
manifests = config.installer.manifests;
diff --git a/doc/manual/man-nixos-rebuild.xml b/doc/manual/man-nixos-rebuild.xml
index 0bdc925efd9..6f888172d48 100644
--- a/doc/manual/man-nixos-rebuild.xml
+++ b/doc/manual/man-nixos-rebuild.xml
@@ -24,6 +24,8 @@
+
+
@@ -151,6 +153,35 @@ $ ./result/bin/run-*-vm
+
+
+
+ Like , but boots using the
+ regular boot loader of your configuration (e.g., GRUB 1 or 2),
+ rather than booting directly into the kernel and initial ramdisk
+ of the system. This allows you to test whether the boot loader
+ works correctly. However, it does not guarantee that your NixOS
+ configuration will boot successfully on the host hardware (i.e.,
+ after running nixos-rebuild switch), because
+ the hardware and boot loader configuration in the VM are
+ different. The boot loader is installed on an automatically
+ generated virtual disk containing a /boot
+ partition, which is mounted read-only in the VM.
+
+
+
+
+
+
+ This operation merely fetches the latest manifest in the
+ Nixpkgs channel to speed up subsequent
+ nix-env operations. This is useful if you
+ are not using nix-channel but still want to
+ use pre-built binary packages. It doesn’t reconfigure the
+ system at all.
+
+
+
diff --git a/modules/installer/tools/nixos-rebuild.sh b/modules/installer/tools/nixos-rebuild.sh
index 4d01ff53f89..41b74494337 100644
--- a/modules/installer/tools/nixos-rebuild.sh
+++ b/modules/installer/tools/nixos-rebuild.sh
@@ -22,6 +22,8 @@ The operation is one of the following:
activate it
build-vm: build a virtual machine containing the configuration
(useful for testing)
+ build-vm-with-bootloader:
+ like build-vm, but include a boot loader in the VM
dry-run: just show what store paths would be built/downloaded
pull: just pull the Nixpkgs channel manifest and exit
@@ -64,7 +66,7 @@ while test "$#" -gt 0; do
--help)
showSyntax
;;
- switch|boot|test|build|dry-run|build-vm|pull)
+ switch|boot|test|build|dry-run|build-vm|build-vm-with-bootloader|pull)
action="$i"
;;
--install-grub)
@@ -171,9 +173,12 @@ if test -z "$rollback"; then
elif test "$action" = test -o "$action" = build -o "$action" = dry-run; then
nix-build $NIXOS -A system -K -k $extraBuildFlags > /dev/null
pathToConfig=./result
- elif test "$action" = build-vm; then
+ elif [ "$action" = build-vm ]; then
nix-build $NIXOS -A vm -K -k $extraBuildFlags > /dev/null
pathToConfig=./result
+ elif [ "$action" = build-vm-with-bootloader ]; then
+ nix-build $NIXOS -A vmWithBootLoader -K -k $extraBuildFlags > /dev/null
+ pathToConfig=./result
else
showSyntax
fi
diff --git a/modules/system/activation/switch-to-configuration.sh b/modules/system/activation/switch-to-configuration.sh
index 6aec8225fdf..ffc9845622c 100644
--- a/modules/system/activation/switch-to-configuration.sh
+++ b/modules/system/activation/switch-to-configuration.sh
@@ -38,7 +38,7 @@ if [ "$action" = "switch" -o "$action" = "boot" ]; then
if [ "$NIXOS_INSTALL_GRUB" = 1 -o "$oldGrubVersion" != "$newGrubVersion" ]; then
echo "installing the GRUB bootloader..."
- @grub@/sbin/grub-install "@grubDevice@" --no-floppy --recheck
+ @grub@/sbin/grub-install "@grubDevice@" --no-floppy
echo "$newGrubVersion" > /boot/grub/version
fi
fi
diff --git a/modules/virtualisation/qemu-vm.nix b/modules/virtualisation/qemu-vm.nix
index fb4779c63a1..bc36b32a778 100644
--- a/modules/virtualisation/qemu-vm.nix
+++ b/modules/virtualisation/qemu-vm.nix
@@ -110,7 +110,24 @@ let
example = "-vga std";
description = "Options passed to QEMU.";
};
-
+
+ virtualisation.useBootLoader =
+ mkOption {
+ default = true;
+ description =
+ ''
+ If enabled, the virtual machine will be booted using the
+ regular boot loader (i.e., GRUB 1 or 2). This allows
+ testing of the boot loader. However, it does not
+ guarantee that your NixOS configuration will boot
+ successfully on the host hardware, because the hardware
+ and boot loader configuration in the VM are different. If
+ disabled (the default), the VM directly boots the NixOS
+ kernel and initial ramdisk, bypassing the boot loader
+ altogether.
+ '';
+ };
+
};
cfg = config.virtualisation;
@@ -146,12 +163,17 @@ let
-net nic,vlan=0,model=virtio \
-chardev socket,id=samba,path=./samba \
-net user,vlan=0,guestfwd=tcp:10.0.2.4:139-chardev:samba''${QEMU_NET_OPTS:+,$QEMU_NET_OPTS} \
- -drive file=$NIX_DISK_IMAGE,if=virtio,boot=on,cache=writeback,werror=report \
- -kernel ${config.system.build.toplevel}/kernel \
- -initrd ${config.system.build.toplevel}/initrd \
+ ${if cfg.useBootLoader then ''
+ -drive index=0,file=$NIX_DISK_IMAGE,if=virtio,cache=writeback,werror=report \
+ -drive index=1,file=${bootDisk}/disk.img,if=virtio,boot=on \
+ '' else ''
+ -drive file=$NIX_DISK_IMAGE,if=virtio,boot=on,cache=writeback,werror=report \
+ -kernel ${config.system.build.toplevel}/kernel \
+ -initrd ${config.system.build.toplevel}/initrd \
+ -append "$(cat ${config.system.build.toplevel}/kernel-params) init=${config.system.build.bootStage2} systemConfig=${config.system.build.toplevel} regInfo=${regInfo} ${kernelConsole} $QEMU_KERNEL_PARAMS" \
+ ''}
${qemuGraphics} \
$QEMU_OPTS \
- -append "$(cat ${config.system.build.toplevel}/kernel-params) init=${config.system.build.bootStage2} systemConfig=${config.system.build.toplevel} regInfo=${regInfo} ${kernelConsole} $QEMU_KERNEL_PARAMS" \
${config.virtualisation.qemu.options}
'';
@@ -165,11 +187,54 @@ let
printRegistration=1 perl ${pkgs.pathsFromGraph} closure-* > $out
'';
+
+ # Generate a hard disk image containing a /boot partition and GRUB
+ # in the MBR. Used when the `useBootLoader' option is set.
+ bootDisk =
+ pkgs.vmTools.runInLinuxVM (
+ pkgs.runCommand "nixos-boot-disk"
+ { preVM =
+ ''
+ mkdir $out
+ diskImage=$out/disk.img
+ ${pkgs.vmTools.kvm}/bin/qemu-img create -f qcow2 $diskImage "32M"
+ '';
+ buildInputs = [ pkgs.utillinux ];
+ }
+ ''
+ # Create a single /boot partition.
+ ${pkgs.parted}/sbin/parted /dev/vda mklabel msdos
+ ${pkgs.parted}/sbin/parted /dev/vda -- mkpart primary ext2 1M -1s
+ . /sys/class/block/vda1/uevent
+ mknod /dev/vda1 b $MAJOR $MINOR
+ . /sys/class/block/vda/uevent
+ ${pkgs.e2fsprogs}/sbin/mkfs.ext3 -L boot /dev/vda1
+ ${pkgs.e2fsprogs}/sbin/tune2fs -c 0 -i 0 /dev/vda1
+
+ # Mount /boot.
+ mkdir /boot
+ mount /dev/vda1 /boot
+
+ # This is needed for GRUB 0.97, which doesn't know about virtio devices.
+ mkdir /boot/grub
+ echo '(hd0) /dev/vda' > /boot/grub/device.map
+
+ # Install GRUB and generate the GRUB boot menu.
+ touch /etc/NIXOS
+ mkdir -p /nix/var/nix/profiles
+ ${config.system.build.toplevel}/bin/switch-to-configuration boot
+
+ umount /boot
+ ''
+ );
+
in
{
require = options;
+ boot.loader.grub.device = mkOverride 50 "/dev/vda";
+
# All the modules the initrd needs to mount the host filesystem via
# CIFS. Also use paravirtualised network and block devices for
# performance.
@@ -207,6 +272,7 @@ in
boot.initrd.postMountCommands =
''
+ mkdir -p $targetRoot/boot
mount -o remount,ro $targetRoot/nix/store
${optionalString cfg.writableStore ''
mkdir /mnt-store-tmpfs
@@ -225,7 +291,9 @@ in
boot.postBootCommands =
''
( source /proc/cmdline
- ${config.environment.nix}/bin/nix-store --load-db < $regInfo
+ if [ -n "$regInfo" ]; then
+ ${config.environment.nix}/bin/nix-store --load-db < $regInfo
+ fi
)
'';
@@ -237,7 +305,7 @@ in
# where the regular value for the `fileSystems' attribute should be
# disregarded for the purpose of building a VM test image (since
# those filesystems don't exist in the VM).
- fileSystems = mkOverride 50
+ fileSystems = mkOverride 50 (
[ { mountPoint = "/";
device = "/dev/vda";
}
@@ -253,7 +321,15 @@ in
options = "bind";
neededForBoot = true;
}
- ];
+ ] ++ optional cfg.useBootLoader
+ { mountPoint = "/boot";
+ device = "/dev/disk/by-label/boot";
+ fsType = "ext3";
+ options = "ro";
+ noCheck = true; # fsck fails on a r/o filesystem
+ });
+
+ swapDevices = mkOverride 50 [ ];
# Starting DHCP brings down eth0, which kills the connection to the
# host filesystem and thus deadlocks the system.