{ pkgs
, kernel ? pkgs.linux
, img ? pkgs.stdenv.hostPlatform.platform.kernelTarget
, storeDir ? builtins.storeDir
, rootModules ?
[ "virtio_pci" "virtio_mmio" "virtio_blk" "virtio_balloon" "virtio_rng" "ext4" "unix" "9p" "9pnet_virtio" "crc32c_generic" ]
++ pkgs.lib.optional (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) "rtc_cmos"
}:
with pkgs;
with import ../../../nixos/lib/qemu-flags.nix { inherit pkgs; };
rec {
qemu = pkgs.qemu_kvm;
modulesClosure = makeModulesClosure {
inherit kernel rootModules;
firmware = kernel;
};
hd = "vda"; # either "sda" or "vda"
initrdUtils = runCommand "initrd-utils"
{ buildInputs = [ nukeReferences ];
allowedReferences = [ "out" modulesClosure ]; # prevent accidents like glibc being included in the initrd
}
''
mkdir -p $out/bin
mkdir -p $out/lib
# Copy what we need from Glibc.
cp -p ${pkgs.stdenv.glibc.out}/lib/ld-linux*.so.? $out/lib
cp -p ${pkgs.stdenv.glibc.out}/lib/libc.so.* $out/lib
cp -p ${pkgs.stdenv.glibc.out}/lib/libm.so.* $out/lib
cp -p ${pkgs.stdenv.glibc.out}/lib/libresolv.so.* $out/lib
# Copy BusyBox.
cp -pd ${pkgs.busybox}/bin/* $out/bin
# Run patchelf to make the programs refer to the copied libraries.
for i in $out/bin/* $out/lib/*; do if ! test -L $i; then nuke-refs $i; fi; done
for i in $out/bin/*; do
if [ -f "$i" -a ! -L "$i" ]; then
echo "patching $i..."
patchelf --set-interpreter $out/lib/ld-linux*.so.? --set-rpath $out/lib $i || true
fi
done
''; # */
stage1Init = writeScript "vm-run-stage1" ''
#! ${initrdUtils}/bin/ash -e
export PATH=${initrdUtils}/bin
mkdir /etc
echo -n > /etc/fstab
mount -t proc none /proc
mount -t sysfs none /sys
echo 2 > /proc/sys/vm/panic_on_oom
for o in $(cat /proc/cmdline); do
case $o in
mountDisk=1)
mountDisk=1
;;
command=*)
set -- $(IFS==; echo $o)
command=$2
;;
out=*)
set -- $(IFS==; echo $o)
export out=$2
;;
esac
done
echo "loading kernel modules..."
for i in $(cat ${modulesClosure}/insmod-list); do
insmod $i || echo "warning: unable to load $i"
done
mount -t devtmpfs devtmpfs /dev
ifconfig lo up
mkdir /fs
if test -z "$mountDisk"; then
mount -t tmpfs none /fs
else
mount /dev/${hd} /fs
fi
mkdir -p /fs/dev
mount -o bind /dev /fs/dev
mkdir -p /fs/dev/shm /fs/dev/pts
mount -t tmpfs -o "mode=1777" none /fs/dev/shm
mount -t devpts none /fs/dev/pts
echo "mounting Nix store..."
mkdir -p /fs${storeDir}
mount -t 9p store /fs${storeDir} -o trans=virtio,version=9p2000.L,cache=loose
mkdir -p /fs/tmp /fs/run /fs/var
mount -t tmpfs -o "mode=1777" none /fs/tmp
mount -t tmpfs -o "mode=755" none /fs/run
ln -sfn /run /fs/var/run
echo "mounting host's temporary directory..."
mkdir -p /fs/tmp/xchg
mount -t 9p xchg /fs/tmp/xchg -o trans=virtio,version=9p2000.L,cache=loose
mkdir -p /fs/proc
mount -t proc none /fs/proc
mkdir -p /fs/sys
mount -t sysfs none /fs/sys
mkdir -p /fs/etc
ln -sf /proc/mounts /fs/etc/mtab
echo "127.0.0.1 localhost" > /fs/etc/hosts
echo "starting stage 2 ($command)"
exec switch_root /fs $command $out
'';
initrd = makeInitrd {
contents = [
{ object = stage1Init;
symlink = "/init";
}
];
};
stage2Init = writeScript "vm-run-stage2" ''
#! ${bash}/bin/sh
source /tmp/xchg/saved-env
# Set the system time from the hardware clock. Works around an
# apparent KVM > 1.5.2 bug.
${pkgs.utillinux}/bin/hwclock -s
export NIX_STORE=${storeDir}
export NIX_BUILD_TOP=/tmp
export TMPDIR=/tmp
export PATH=/empty
out="$1"
cd "$NIX_BUILD_TOP"
if ! test -e /bin/sh; then
${coreutils}/bin/mkdir -p /bin
${coreutils}/bin/ln -s ${bash}/bin/sh /bin/sh
fi
# Set up automatic kernel module loading.
export MODULE_DIR=${kernel}/lib/modules/
${coreutils}/bin/cat <<EOF > /run/modprobe
#! /bin/sh
export MODULE_DIR=$MODULE_DIR
exec ${kmod}/bin/modprobe "\$@"
EOF
${coreutils}/bin/chmod 755 /run/modprobe
echo /run/modprobe > /proc/sys/kernel/modprobe
# For debugging: if this is the second time this image is run,
# then don't start the build again, but instead drop the user into
# an interactive shell.
if test -n "$origBuilder" -a ! -e /.debug; then
exec < /dev/null
${coreutils}/bin/touch /.debug
$origBuilder $origArgs
echo $? > /tmp/xchg/in-vm-exit
${busybox}/bin/mount -o remount,ro dummy /
${busybox}/bin/powe