Update QEMU Nixos Virtual Machine

The Nixos Qemu VM that are used for VM tests can now start without
boot menu even when using a bootloader.
The Nixos Qemu VM with bootloader can emulate a EFI boot now.
This commit is contained in:
Thomas Strobel 2015-01-26 21:13:31 +01:00
parent b9cc04329b
commit 80afabd5b5
4 changed files with 144 additions and 23 deletions

View File

@ -36,13 +36,28 @@ let
${toString config.virtualisation.diskSize}M || exit 1 ${toString config.virtualisation.diskSize}M || exit 1
fi fi
# Create a directory for exchanging data with the VM. # Create a directory for storing temporary data of the running VM.
if [ -z "$TMPDIR" -o -z "$USE_TMPDIR" ]; then if [ -z "$TMPDIR" -o -z "$USE_TMPDIR" ]; then
TMPDIR=$(mktemp -d nix-vm.XXXXXXXXXX --tmpdir) TMPDIR=$(mktemp -d nix-vm.XXXXXXXXXX --tmpdir)
fi fi
cd $TMPDIR # Create a directory for exchanging data with the VM.
mkdir -p $TMPDIR/xchg mkdir -p $TMPDIR/xchg
${if cfg.useBootLoader then ''
# Create a writable copy/snapshot of the boot disk
# A writable boot disk can be booted from automatically
${pkgs.qemu_kvm}/bin/qemu-img create -f qcow2 -b ${bootDisk}/disk.img $TMPDIR/disk.img || exit 1
${if cfg.useEFIBoot then ''
# VM needs a writable flash BIOS
cp ${bootDisk}/bios.bin $TMPDIR || exit 1
chmod 0644 $TMPDIR/bios.bin || exit 1
'' else ''
''}
'' else ''
''}
cd $TMPDIR
idx=2 idx=2
extraDisks="" extraDisks=""
${flip concatMapStrings cfg.emptyDiskImages (size: '' ${flip concatMapStrings cfg.emptyDiskImages (size: ''
@ -52,7 +67,6 @@ let
'')} '')}
# Start QEMU. # Start QEMU.
# "-boot menu=on" is there, because I don't know how to make qemu boot from 2nd hd.
exec ${pkgs.qemu_kvm}/bin/qemu-kvm \ exec ${pkgs.qemu_kvm}/bin/qemu-kvm \
-name ${vmName} \ -name ${vmName} \
-m ${toString config.virtualisation.memorySize} \ -m ${toString config.virtualisation.memorySize} \
@ -63,8 +77,11 @@ let
-virtfs local,path=''${SHARED_DIR:-$TMPDIR/xchg},security_model=none,mount_tag=shared \ -virtfs local,path=''${SHARED_DIR:-$TMPDIR/xchg},security_model=none,mount_tag=shared \
${if cfg.useBootLoader then '' ${if cfg.useBootLoader then ''
-drive index=0,id=drive1,file=$NIX_DISK_IMAGE,if=virtio,cache=writeback,werror=report \ -drive index=0,id=drive1,file=$NIX_DISK_IMAGE,if=virtio,cache=writeback,werror=report \
-drive index=1,id=drive2,file=${bootDisk}/disk.img,if=virtio,readonly \ -drive index=1,id=drive2,file=$TMPDIR/disk.img,media=disk \
-boot menu=on \ ${if cfg.useEFIBoot then ''
-pflash $TMPDIR/bios.bin \
'' else ''
''}
'' else '' '' else ''
-drive file=$NIX_DISK_IMAGE,if=virtio,cache=writeback,werror=report \ -drive file=$NIX_DISK_IMAGE,if=virtio,cache=writeback,werror=report \
-kernel ${config.system.build.toplevel}/kernel \ -kernel ${config.system.build.toplevel}/kernel \
@ -74,7 +91,8 @@ let
$extraDisks \ $extraDisks \
${qemuGraphics} \ ${qemuGraphics} \
${toString config.virtualisation.qemu.options} \ ${toString config.virtualisation.qemu.options} \
$QEMU_OPTS $QEMU_OPTS \
$@
''; '';
@ -98,23 +116,44 @@ let
'' ''
mkdir $out mkdir $out
diskImage=$out/disk.img diskImage=$out/disk.img
${pkgs.qemu_kvm}/bin/qemu-img create -f qcow2 $diskImage "32M" bootFlash=$out/bios.bin
${pkgs.qemu_kvm}/bin/qemu-img create -f qcow2 $diskImage "40M"
${if cfg.useEFIBoot then ''
cp ${pkgs.OVMF-CSM}/FV/OVMF.fd $bootFlash
chmod 0644 $bootFlash
'' else ''
''}
''; '';
buildInputs = [ pkgs.utillinux ]; buildInputs = [ pkgs.utillinux ];
QEMU_OPTS = if cfg.useEFIBoot
then "-pflash $out/bios.bin -nographic -serial pty"
else "-nographic -serial pty";
} }
'' ''
# Create a single /boot partition. # Create a /boot EFI partition with 40M
${pkgs.parted}/sbin/parted /dev/vda mklabel msdos ${pkgs.gptfdisk}/sbin/sgdisk -G /dev/vda
${pkgs.parted}/sbin/parted /dev/vda -- mkpart primary ext2 1M -1s ${pkgs.gptfdisk}/sbin/sgdisk -a 1 -n 1:34:2047 -c 1:"BIOS Boot Partition" -t 1:ef02 /dev/vda
. /sys/class/block/vda1/uevent ${pkgs.gptfdisk}/sbin/sgdisk -a 512 -N 2 -c 2:"EFI System" -t 2:ef00 /dev/vda
mknod /dev/vda1 b $MAJOR $MINOR ${pkgs.gptfdisk}/sbin/sgdisk -A 1:set:1 /dev/vda
${pkgs.gptfdisk}/sbin/sgdisk -A 2:set:2 /dev/vda
${pkgs.gptfdisk}/sbin/sgdisk -h 2 /dev/vda
${pkgs.gptfdisk}/sbin/sgdisk -C /dev/vda
${pkgs.utillinux}/bin/sfdisk /dev/vda -A 2
. /sys/class/block/vda2/uevent
mknod /dev/vda2 b $MAJOR $MINOR
. /sys/class/block/vda/uevent . /sys/class/block/vda/uevent
${pkgs.e2fsprogs}/sbin/mkfs.ext4 -L boot /dev/vda1 ${pkgs.dosfstools}/bin/mkfs.fat -F16 /dev/vda2
${pkgs.e2fsprogs}/sbin/tune2fs -c 0 -i 0 /dev/vda1 export MTOOLS_SKIP_CHECK=1
${pkgs.mtools}/bin/mlabel -i /dev/vda2 ::boot
# Mount /boot. # Mount /boot; load necessary modules first.
${pkgs.module_init_tools}/sbin/insmod ${pkgs.linux}/lib/modules/*/kernel/fs/nls/nls_cp437.ko || true
${pkgs.module_init_tools}/sbin/insmod ${pkgs.linux}/lib/modules/*/kernel/fs/nls/nls_iso8859-1.ko || true
${pkgs.module_init_tools}/sbin/insmod ${pkgs.linux}/lib/modules/*/kernel/fs/fat/fat.ko || true
${pkgs.module_init_tools}/sbin/insmod ${pkgs.linux}/lib/modules/*/kernel/fs/fat/vfat.ko || true
${pkgs.module_init_tools}/sbin/insmod ${pkgs.linux}/lib/modules/*/kernel/fs/efivarfs/efivarfs.ko || true
mkdir /boot mkdir /boot
mount /dev/vda1 /boot mount /dev/vda2 /boot
# This is needed for GRUB 0.97, which doesn't know about virtio devices. # This is needed for GRUB 0.97, which doesn't know about virtio devices.
mkdir /boot/grub mkdir /boot/grub
@ -287,6 +326,17 @@ in
''; '';
}; };
virtualisation.useEFIBoot =
mkOption {
default = false;
description =
''
If enabled, the virtual machine will provide a EFI boot
manager.
useEFIBoot is ignored if useBootLoader == false.
'';
};
}; };
config = { config = {
@ -379,8 +429,8 @@ in
}; };
} // optionalAttrs cfg.useBootLoader } // optionalAttrs cfg.useBootLoader
{ "/boot" = { "/boot" =
{ device = "/dev/disk/by-label/boot"; { device = "/dev/vdb2";
fsType = "ext4"; fsType = "vfat";
options = "ro"; options = "ro";
noCheck = true; # fsck fails on a r/o filesystem noCheck = true; # fsck fails on a r/o filesystem
}; };
@ -413,6 +463,7 @@ in
# Wireless won't work in the VM. # Wireless won't work in the VM.
networking.wireless.enable = mkVMOverride false; networking.wireless.enable = mkVMOverride false;
networking.connman.enable = mkVMOverride false;
# Speed up booting by not waiting for ARP. # Speed up booting by not waiting for ARP.
networking.dhcpcd.extraConfig = "noarp"; networking.dhcpcd.extraConfig = "noarp";

View File

@ -1,4 +1,4 @@
{ stdenv, edk2, nasm, iasl }: { stdenv, edk2, nasm, iasl, seabios, openssl, secureBoot ? false }:
let let
@ -14,13 +14,35 @@ in
stdenv.mkDerivation (edk2.setup "OvmfPkg/OvmfPkg${targetArch}.dsc" { stdenv.mkDerivation (edk2.setup "OvmfPkg/OvmfPkg${targetArch}.dsc" {
name = "OVMF-2014-12-10"; name = "OVMF-2014-12-10";
buildInputs = [nasm iasl]; # TODO: properly include openssl for secureBoot
buildInputs = [nasm iasl] ++ stdenv.lib.optionals (secureBoot == true) [ openssl ];
unpackPhase = '' unpackPhase = ''
for file in \ for file in \
"${edk2.src}"/{OvmfPkg,UefiCpuPkg,MdeModulePkg,IntelFrameworkModulePkg,PcAtChipsetPkg,FatBinPkg,EdkShellBinPkg,MdePkg,ShellPkg,OptionRomPkg,IntelFrameworkPkg}; "${edk2.src}"/{UefiCpuPkg,MdeModulePkg,IntelFrameworkModulePkg,PcAtChipsetPkg,FatBinPkg,EdkShellBinPkg,MdePkg,ShellPkg,OptionRomPkg,IntelFrameworkPkg};
do do
ln -sv "$file" . ln -sv "$file" .
done done
${if (seabios == false) then ''
ln -sv ${edk2.src}/OvmfPkg .
'' else ''
cp -r ${edk2.src}/OvmfPkg .
chmod +w OvmfPkg/Csm/Csm16
cp ${seabios}/Csm16.bin OvmfPkg/Csm/Csm16/Csm16.bin
''}
${if (secureBoot == true) then ''
ln -sv ${edk2.src}/SecurityPkg .
ln -sv ${edk2.src}/CryptoPkg .
'' else ''
''}
'';
buildPhase = if (seabios == false) then ''
build ${if secureBoot then "-DSECURE_BOOT_ENABLE=TRUE" else ""}
'' else ''
build -D CSM_ENABLE -D FD_SIZE_2MB ${if secureBoot then "-DSECURE_BOOT_ENABLE=TRUE" else ""}
''; '';
meta = { meta = {

View File

@ -0,0 +1,44 @@
{ stdenv, fetchurl, iasl, python }:
stdenv.mkDerivation rec {
name = "seabios-${version}";
version = "1.7.5.2";
src = fetchurl {
url = "http://code.coreboot.org/p/seabios/downloads/get/${name}.tar.gz";
sha256 = "1syd3gi5gq0gj2pjvmdis64xc3j1xf0jgy49ngymap0pdpm0cmh0";
};
buildInputs = [ iasl python ];
configurePhase = ''
# build SeaBIOS for CSM
cat > .config << EOF
CONFIG_CSM=y
CONFIG_QEMU_HARDWARE=y
CONFIG_PERMIT_UNALIGNED_PCIROM=y
EOF
make olddefconfig
'';
installPhase = ''
mkdir $out
cp out/Csm16.bin $out/Csm16.bin
'';
meta = with stdenv.lib; {
description = "Open source implementation of a 16bit X86 BIOS";
longDescription = ''
SeaBIOS is an open source implementation of a 16bit X86 BIOS.
It can run in an emulator or it can run natively on X86 hardware with the use of coreboot.
SeaBIOS is the default BIOS for QEMU and KVM.
'';
homepage = http://www.seabios.org;
license = licenses.lgpl3;
maintainers = [ maintainers.tstrobel ];
platforms = platforms.linux;
};
}

View File

@ -8058,7 +8058,11 @@ let
oracleXE = callPackage ../servers/sql/oracle-xe { }; oracleXE = callPackage ../servers/sql/oracle-xe { };
OVMF = callPackage ../applications/virtualization/OVMF { }; OVMF = callPackage ../applications/virtualization/OVMF { seabios=false; openssl=null; };
OVMF-CSM = callPackage ../applications/virtualization/OVMF { openssl=null; };
#WIP: OVMF-secureBoot = callPackage ../applications/virtualization/OVMF { seabios=false; secureBoot=true; };
seabios = callPackage ../applications/virtualization/seabios { };
pgpool92 = callPackage ../servers/sql/pgpool/default.nix { pgpool92 = callPackage ../servers/sql/pgpool/default.nix {
postgresql = postgresql92; postgresql = postgresql92;