From 80afabd5b5d034d41792b7cf99d3ca87ae34596a Mon Sep 17 00:00:00 2001 From: Thomas Strobel Date: Mon, 26 Jan 2015 21:13:31 +0100 Subject: [PATCH] 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. --- nixos/modules/virtualisation/qemu-vm.nix | 87 +++++++++++++++---- .../virtualization/OVMF/default.nix | 30 ++++++- .../virtualization/seabios/default.nix | 44 ++++++++++ pkgs/top-level/all-packages.nix | 6 +- 4 files changed, 144 insertions(+), 23 deletions(-) create mode 100644 pkgs/applications/virtualization/seabios/default.nix diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix index 33c64cc890e0..a5a133dfa5dc 100644 --- a/nixos/modules/virtualisation/qemu-vm.nix +++ b/nixos/modules/virtualisation/qemu-vm.nix @@ -36,13 +36,28 @@ let ${toString config.virtualisation.diskSize}M || exit 1 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 TMPDIR=$(mktemp -d nix-vm.XXXXXXXXXX --tmpdir) fi - cd $TMPDIR + # Create a directory for exchanging data with the VM. 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 extraDisks="" ${flip concatMapStrings cfg.emptyDiskImages (size: '' @@ -52,7 +67,6 @@ let '')} # 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 \ -name ${vmName} \ -m ${toString config.virtualisation.memorySize} \ @@ -63,8 +77,11 @@ let -virtfs local,path=''${SHARED_DIR:-$TMPDIR/xchg},security_model=none,mount_tag=shared \ ${if cfg.useBootLoader then '' -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 \ - -boot menu=on \ + -drive index=1,id=drive2,file=$TMPDIR/disk.img,media=disk \ + ${if cfg.useEFIBoot then '' + -pflash $TMPDIR/bios.bin \ + '' else '' + ''} '' else '' -drive file=$NIX_DISK_IMAGE,if=virtio,cache=writeback,werror=report \ -kernel ${config.system.build.toplevel}/kernel \ @@ -74,7 +91,8 @@ let $extraDisks \ ${qemuGraphics} \ ${toString config.virtualisation.qemu.options} \ - $QEMU_OPTS + $QEMU_OPTS \ + $@ ''; @@ -98,23 +116,44 @@ let '' mkdir $out 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 ]; + QEMU_OPTS = if cfg.useEFIBoot + then "-pflash $out/bios.bin -nographic -serial pty" + else "-nographic -serial pty"; } '' - # 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 + # Create a /boot EFI partition with 40M + ${pkgs.gptfdisk}/sbin/sgdisk -G /dev/vda + ${pkgs.gptfdisk}/sbin/sgdisk -a 1 -n 1:34:2047 -c 1:"BIOS Boot Partition" -t 1:ef02 /dev/vda + ${pkgs.gptfdisk}/sbin/sgdisk -a 512 -N 2 -c 2:"EFI System" -t 2:ef00 /dev/vda + ${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 - ${pkgs.e2fsprogs}/sbin/mkfs.ext4 -L boot /dev/vda1 - ${pkgs.e2fsprogs}/sbin/tune2fs -c 0 -i 0 /dev/vda1 + ${pkgs.dosfstools}/bin/mkfs.fat -F16 /dev/vda2 + 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 - mount /dev/vda1 /boot + mount /dev/vda2 /boot # This is needed for GRUB 0.97, which doesn't know about virtio devices. 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 = { @@ -379,8 +429,8 @@ in }; } // optionalAttrs cfg.useBootLoader { "/boot" = - { device = "/dev/disk/by-label/boot"; - fsType = "ext4"; + { device = "/dev/vdb2"; + fsType = "vfat"; options = "ro"; noCheck = true; # fsck fails on a r/o filesystem }; @@ -413,6 +463,7 @@ in # Wireless won't work in the VM. networking.wireless.enable = mkVMOverride false; + networking.connman.enable = mkVMOverride false; # Speed up booting by not waiting for ARP. networking.dhcpcd.extraConfig = "noarp"; diff --git a/pkgs/applications/virtualization/OVMF/default.nix b/pkgs/applications/virtualization/OVMF/default.nix index 388248c5aa86..e29ab11947fd 100644 --- a/pkgs/applications/virtualization/OVMF/default.nix +++ b/pkgs/applications/virtualization/OVMF/default.nix @@ -1,4 +1,4 @@ -{ stdenv, edk2, nasm, iasl }: +{ stdenv, edk2, nasm, iasl, seabios, openssl, secureBoot ? false }: let @@ -14,14 +14,36 @@ in stdenv.mkDerivation (edk2.setup "OvmfPkg/OvmfPkg${targetArch}.dsc" { name = "OVMF-2014-12-10"; - buildInputs = [nasm iasl]; + # TODO: properly include openssl for secureBoot + buildInputs = [nasm iasl] ++ stdenv.lib.optionals (secureBoot == true) [ openssl ]; + unpackPhase = '' 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 ln -sv "$file" . 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 = { description = "Sample UEFI firmware for QEMU and KVM"; diff --git a/pkgs/applications/virtualization/seabios/default.nix b/pkgs/applications/virtualization/seabios/default.nix new file mode 100644 index 000000000000..8e6a7fcb0d26 --- /dev/null +++ b/pkgs/applications/virtualization/seabios/default.nix @@ -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; + }; +} + diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 04a592a7e566..a7a58bcc9293 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -8058,7 +8058,11 @@ let 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 { postgresql = postgresql92;