From 4d148268258732a216dc83327fe16073082d1063 Mon Sep 17 00:00:00 2001 From: Daniel Fullmer Date: Sat, 13 Jun 2020 23:22:20 -0400 Subject: [PATCH 1/8] qemu-vm: allow bootloader to set EFI vars Without this, systemd-boot does not add an EFI boot entry for itself. The reason it worked before this fix is because it would fall back to the default installed \EFI\BOOT\BOOTX64.EFI --- nixos/modules/virtualisation/qemu-vm.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix index 5a502c36180..c9061ea0608 100644 --- a/nixos/modules/virtualisation/qemu-vm.nix +++ b/nixos/modules/virtualisation/qemu-vm.nix @@ -210,6 +210,10 @@ let mkdir /boot mount /dev/vda2 /boot + ${optionalString config.boot.loader.efi.canTouchEfiVariables '' + mount -t efivarfs efivarfs /sys/firmware/efi/efivars + ''} + # 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 From b278a7d75ae96d1416791da99b778eb46018a9b2 Mon Sep 17 00:00:00 2001 From: Daniel Fullmer Date: Sat, 13 Jun 2020 23:25:34 -0400 Subject: [PATCH 2/8] nixos/systemd-boot: test for EFI boot entry --- nixos/tests/systemd-boot.nix | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/nixos/tests/systemd-boot.nix b/nixos/tests/systemd-boot.nix index eba4729d6de..7a663dd9b42 100644 --- a/nixos/tests/systemd-boot.nix +++ b/nixos/tests/systemd-boot.nix @@ -11,6 +11,8 @@ let virtualisation.useBootLoader = true; virtualisation.useEFIBoot = true; boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + environment.systemPackages = [ pkgs.efibootmgr ]; }; in { @@ -31,6 +33,36 @@ in machine.succeed( "test -e /sys/firmware/efi/efivars/LoaderEntrySelected-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f" ) + + # "bootctl install" should have created an EFI entry + machine.succeed('efibootmgr | grep "Linux Boot Manager"') + ''; + }; + + # Boot without having created an EFI entry--instead using default "/EFI/BOOT/BOOTX64.EFI" + fallback = makeTest { + name = "systemd-boot-fallback"; + meta.maintainers = with pkgs.stdenv.lib.maintainers; [ danielfullmer ]; + + machine = { pkgs, lib, ... }: { + imports = [ common ]; + boot.loader.efi.canTouchEfiVariables = mkForce false; + }; + + testScript = '' + machine.start() + machine.wait_for_unit("multi-user.target") + + machine.succeed("test -e /boot/loader/entries/nixos-generation-1.conf") + + # Ensure we actually booted using systemd-boot + # Magic number is the vendor UUID used by systemd-boot. + machine.succeed( + "test -e /sys/firmware/efi/efivars/LoaderEntrySelected-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f" + ) + + # "bootctl install" should _not_ have created an EFI entry + machine.fail('efibootmgr | grep "Linux Boot Manager"') ''; }; From 90bf7332e119b1049c7c1c388fab89c5d3154c69 Mon Sep 17 00:00:00 2001 From: Daniel Fullmer Date: Sat, 13 Jun 2020 23:35:36 -0400 Subject: [PATCH 3/8] OVMF: use Debian dir layout for aarch64 --- pkgs/applications/virtualization/OVMF/default.nix | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pkgs/applications/virtualization/OVMF/default.nix b/pkgs/applications/virtualization/OVMF/default.nix index 19ba8ced497..9ce9a22381b 100644 --- a/pkgs/applications/virtualization/OVMF/default.nix +++ b/pkgs/applications/virtualization/OVMF/default.nix @@ -38,14 +38,13 @@ edk2.mkDerivation projectDscPath { postFixup = if stdenv.isAarch64 then '' mkdir -vp $fd/FV - mkdir -vp $fd/AAVMF mv -v $out/FV/QEMU_{EFI,VARS}.fd $fd/FV - # Uses Fedora dir layout: https://src.fedoraproject.org/cgit/rpms/edk2.git/tree/edk2.spec - # FIXME: why is it different from Debian dir layout? https://salsa.debian.org/qemu-team/edk2/blob/debian/debian/rules - dd of=$fd/AAVMF/QEMU_EFI-pflash.raw if=/dev/zero bs=1M count=64 - dd of=$fd/AAVMF/QEMU_EFI-pflash.raw if=$fd/FV/QEMU_EFI.fd conv=notrunc - dd of=$fd/AAVMF/vars-template-pflash.raw if=/dev/zero bs=1M count=64 + # Use Debian dir layout: https://salsa.debian.org/qemu-team/edk2/blob/debian/debian/rules + # Note that Fedora dir layout is different: https://src.fedoraproject.org/cgit/rpms/edk2.git/tree/edk2.spec + dd of=$fd/FV/AAVMF_CODE.fd if=/dev/zero bs=1M count=64 + dd of=$fd/FV/AAVMF_CODE.fd if=$fd/FV/QEMU_EFI.fd conv=notrunc + dd of=$fd/FV/AAVMF_VARS.fd if=/dev/zero bs=1M count=64 '' else '' mkdir -vp $fd/FV mv -v $out/FV/OVMF{,_CODE,_VARS}.fd $fd/FV From d7e3312ab12e54757bc939c210f6be31634829f7 Mon Sep 17 00:00:00 2001 From: Daniel Fullmer Date: Sat, 13 Jun 2020 23:47:26 -0400 Subject: [PATCH 4/8] qemu-vm: split EFI NVRAM into CODE and VARS --- nixos/modules/virtualisation/qemu-vm.nix | 26 +++++++++++++++--------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix index c9061ea0608..c896dfb590f 100644 --- a/nixos/modules/virtualisation/qemu-vm.nix +++ b/nixos/modules/virtualisation/qemu-vm.nix @@ -99,6 +99,10 @@ let addDeviceNames = imap1 (idx: drive: drive // { device = driveDeviceName idx; }); + efiPrefix = "${pkgs.OVMF.fd}/FV/OVMF"; + efiFirmware = "${efiPrefix}_CODE.fd"; + efiVars = "${efiPrefix}_VARS.fd"; + # Shell script to start the VM. startVM = '' @@ -125,9 +129,9 @@ let ${qemu}/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 + # VM needs writable EFI vars + cp ${bootDisk}/EFI_VARS.fd $TMPDIR || exit 1 + chmod 0644 $TMPDIR/EFI_VARS.fd || exit 1 '' else '' ''} '' else '' @@ -172,18 +176,19 @@ let '' mkdir $out diskImage=$out/disk.img - bootFlash=$out/bios.bin ${qemu}/bin/qemu-img create -f qcow2 $diskImage "40M" ${if cfg.useEFIBoot then '' - cp ${pkgs.OVMF-CSM.fd}/FV/OVMF.fd $bootFlash - chmod 0644 $bootFlash + efiVars=$out/EFI_VARS.fd + cp ${efiVars} $efiVars + chmod 0644 $efiVars '' else '' ''} ''; buildInputs = [ pkgs.utillinux ]; - QEMU_OPTS = if cfg.useEFIBoot - then "-pflash $out/bios.bin -nographic -serial pty" - else "-nographic -serial pty"; + QEMU_OPTS = "-nographic -serial stdio -monitor none" + + lib.optionalString cfg.useEFIBoot ( + " -drive if=pflash,format=raw,unit=0,readonly=on,file=${efiFirmware}" + + " -drive if=pflash,format=raw,unit=1,file=$efiVars"); } '' # Create a /boot EFI partition with 40M and arbitrary but fixed GUIDs for reproducibility @@ -560,7 +565,8 @@ in ''-append "$(cat ${config.system.build.toplevel}/kernel-params) init=${config.system.build.toplevel}/init regInfo=${regInfo}/registration ${consoles} $QEMU_KERNEL_PARAMS"'' ]) (mkIf cfg.useEFIBoot [ - "-pflash $TMPDIR/bios.bin" + "-drive if=pflash,format=raw,unit=0,readonly,file=${efiFirmware}" + "-drive if=pflash,format=raw,unit=1,file=$TMPDIR/EFI_VARS.fd" ]) (mkIf (cfg.bios != null) [ "-bios ${cfg.bios}/bios.bin" From fec163d21ca7b8ff5369428037406f23d9a19bdb Mon Sep 17 00:00:00 2001 From: Daniel Fullmer Date: Sat, 13 Jun 2020 23:54:20 -0400 Subject: [PATCH 5/8] qemu-vm: add EFI support for aarch64 --- nixos/modules/virtualisation/qemu-vm.nix | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix index c896dfb590f..757952f2029 100644 --- a/nixos/modules/virtualisation/qemu-vm.nix +++ b/nixos/modules/virtualisation/qemu-vm.nix @@ -81,6 +81,7 @@ let drivesCmdLine = drives: concatStringsSep " " (imap1 driveCmdline drives); + # Creates a device name from a 1-based a numerical index, e.g. # * `driveDeviceName 1` -> `/dev/vda` # * `driveDeviceName 2` -> `/dev/vdb` @@ -99,7 +100,10 @@ let addDeviceNames = imap1 (idx: drive: drive // { device = driveDeviceName idx; }); - efiPrefix = "${pkgs.OVMF.fd}/FV/OVMF"; + efiPrefix = + if (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) then "${pkgs.OVMF.fd}/FV/OVMF" + else if pkgs.stdenv.isAarch64 then "${pkgs.OVMF.fd}/FV/AAVMF" + else throw "No EFI firmware available for platform"; efiFirmware = "${efiPrefix}_CODE.fd"; efiVars = "${efiPrefix}_VARS.fd"; @@ -176,7 +180,7 @@ let '' mkdir $out diskImage=$out/disk.img - ${qemu}/bin/qemu-img create -f qcow2 $diskImage "40M" + ${qemu}/bin/qemu-img create -f qcow2 $diskImage "60M" ${if cfg.useEFIBoot then '' efiVars=$out/EFI_VARS.fd cp ${efiVars} $efiVars @@ -191,7 +195,7 @@ let + " -drive if=pflash,format=raw,unit=1,file=$efiVars"); } '' - # Create a /boot EFI partition with 40M and arbitrary but fixed GUIDs for reproducibility + # Create a /boot EFI partition with 60M and arbitrary but fixed GUIDs for reproducibility ${pkgs.gptfdisk}/bin/sgdisk \ --set-alignment=1 --new=1:34:2047 --change-name=1:BIOSBootPartition --typecode=1:ef02 \ --set-alignment=512 --largest-new=2 --change-name=2:EFISystem --typecode=2:ef00 \ From ee43e2f1eacfaf9bfc179b4f0f9445fee480928c Mon Sep 17 00:00:00 2001 From: Daniel Fullmer Date: Sat, 13 Jun 2020 23:57:52 -0400 Subject: [PATCH 6/8] nixos/systemd-boot: run test on aarch64 --- nixos/tests/all-tests.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index e13a5ee57f6..4eb6849cfc6 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -323,7 +323,7 @@ in systemd = handleTest ./systemd.nix {}; systemd-analyze = handleTest ./systemd-analyze.nix {}; systemd-binfmt = handleTestOn ["x86_64-linux"] ./systemd-binfmt.nix {}; - systemd-boot = handleTestOn ["x86_64-linux"] ./systemd-boot.nix {}; + systemd-boot = handleTest ./systemd-boot.nix {}; systemd-confinement = handleTest ./systemd-confinement.nix {}; systemd-timesyncd = handleTest ./systemd-timesyncd.nix {}; systemd-networkd-vrf = handleTest ./systemd-networkd-vrf.nix {}; From 0b4e2167752110ddc03f6ca91872c7e612931f3d Mon Sep 17 00:00:00 2001 From: Daniel Fullmer Date: Sun, 14 Jun 2020 10:20:21 -0400 Subject: [PATCH 7/8] qemu-vm: treat EFI vars as state, similarly to diskImage --- nixos/modules/virtualisation/qemu-vm.nix | 26 ++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix index 757952f2029..a927766eabb 100644 --- a/nixos/modules/virtualisation/qemu-vm.nix +++ b/nixos/modules/virtualisation/qemu-vm.nix @@ -105,7 +105,7 @@ let else if pkgs.stdenv.isAarch64 then "${pkgs.OVMF.fd}/FV/AAVMF" else throw "No EFI firmware available for platform"; efiFirmware = "${efiPrefix}_CODE.fd"; - efiVars = "${efiPrefix}_VARS.fd"; + efiVarsDefault = "${efiPrefix}_VARS.fd"; # Shell script to start the VM. startVM = @@ -132,10 +132,14 @@ let # A writable boot disk can be booted from automatically. ${qemu}/bin/qemu-img create -f qcow2 -b ${bootDisk}/disk.img $TMPDIR/disk.img || exit 1 + NIX_EFI_VARS=$(readlink -f ''${NIX_EFI_VARS:-${cfg.efiVars}}) + ${if cfg.useEFIBoot then '' # VM needs writable EFI vars - cp ${bootDisk}/EFI_VARS.fd $TMPDIR || exit 1 - chmod 0644 $TMPDIR/EFI_VARS.fd || exit 1 + if ! test -e "$NIX_EFI_VARS"; then + cp ${bootDisk}/efi-vars.fd "$NIX_EFI_VARS" || exit 1 + chmod 0644 "$NIX_EFI_VARS" || exit 1 + fi '' else '' ''} '' else '' @@ -182,8 +186,8 @@ let diskImage=$out/disk.img ${qemu}/bin/qemu-img create -f qcow2 $diskImage "60M" ${if cfg.useEFIBoot then '' - efiVars=$out/EFI_VARS.fd - cp ${efiVars} $efiVars + efiVars=$out/efi-vars.fd + cp ${efiVarsDefault} $efiVars chmod 0644 $efiVars '' else '' ''} @@ -480,6 +484,16 @@ in ''; }; + virtualisation.efiVars = + mkOption { + default = "./${vmName}-efi-vars.fd"; + description = + '' + Path to nvram image containing UEFI variables. The will be created + on startup if it does not exist. + ''; + }; + virtualisation.bios = mkOption { default = null; @@ -570,7 +584,7 @@ in ]) (mkIf cfg.useEFIBoot [ "-drive if=pflash,format=raw,unit=0,readonly,file=${efiFirmware}" - "-drive if=pflash,format=raw,unit=1,file=$TMPDIR/EFI_VARS.fd" + "-drive if=pflash,format=raw,unit=1,file=$NIX_EFI_VARS" ]) (mkIf (cfg.bios != null) [ "-bios ${cfg.bios}/bios.bin" From 37bb17cdd498e85ac76bb9e946a351de1204d9bb Mon Sep 17 00:00:00 2001 From: Daniel Fullmer Date: Sun, 14 Jun 2020 17:25:42 -0400 Subject: [PATCH 8/8] OVMF: add symlinks for Fedora dir layout --- pkgs/applications/virtualization/OVMF/default.nix | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkgs/applications/virtualization/OVMF/default.nix b/pkgs/applications/virtualization/OVMF/default.nix index 9ce9a22381b..94d0ae94dbd 100644 --- a/pkgs/applications/virtualization/OVMF/default.nix +++ b/pkgs/applications/virtualization/OVMF/default.nix @@ -38,13 +38,17 @@ edk2.mkDerivation projectDscPath { postFixup = if stdenv.isAarch64 then '' mkdir -vp $fd/FV + mkdir -vp $fd/AAVMF mv -v $out/FV/QEMU_{EFI,VARS}.fd $fd/FV # Use Debian dir layout: https://salsa.debian.org/qemu-team/edk2/blob/debian/debian/rules - # Note that Fedora dir layout is different: https://src.fedoraproject.org/cgit/rpms/edk2.git/tree/edk2.spec dd of=$fd/FV/AAVMF_CODE.fd if=/dev/zero bs=1M count=64 dd of=$fd/FV/AAVMF_CODE.fd if=$fd/FV/QEMU_EFI.fd conv=notrunc dd of=$fd/FV/AAVMF_VARS.fd if=/dev/zero bs=1M count=64 + + # Also add symlinks for Fedora dir layout: https://src.fedoraproject.org/cgit/rpms/edk2.git/tree/edk2.spec + ln -s $fd/FV/AAVMF_CODE.fd $fd/AAVMF/QEMU_EFI-pflash.raw + ln -s $fd/FV/AAVMF_VARS.fd $fd/AAVMF/vars-template-pflash.raw '' else '' mkdir -vp $fd/FV mv -v $out/FV/OVMF{,_CODE,_VARS}.fd $fd/FV