about summary refs log tree commit diff
path: root/nixpkgs/pkgs/applications/virtualization/OVMF/default.nix
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/pkgs/applications/virtualization/OVMF/default.nix')
-rw-r--r--nixpkgs/pkgs/applications/virtualization/OVMF/default.nix155
1 files changed, 113 insertions, 42 deletions
diff --git a/nixpkgs/pkgs/applications/virtualization/OVMF/default.nix b/nixpkgs/pkgs/applications/virtualization/OVMF/default.nix
index 63c137c220c2..cff31a759a2b 100644
--- a/nixpkgs/pkgs/applications/virtualization/OVMF/default.nix
+++ b/nixpkgs/pkgs/applications/virtualization/OVMF/default.nix
@@ -1,8 +1,23 @@
 { stdenv, nixosTests, lib, edk2, util-linux, nasm, acpica-tools, llvmPackages
+, fetchurl, python3, pexpect, xorriso, qemu, dosfstools, mtools
 , csmSupport ? false, seabios
 , fdSize2MB ? csmSupport
-, fdSize4MB ? false
+, fdSize4MB ? secureBoot
 , secureBoot ? false
+, systemManagementModeRequired ? secureBoot && stdenv.hostPlatform.isx86
+# Whether to create an nvram variables template
+# which includes the MSFT secure boot keys
+, msVarsTemplate ? false
+# When creating the nvram variables template with
+# the MSFT keys, we also must provide a certificate
+# to use as the PK and first KEK for the keystore.
+#
+# By default, we use Debian's cert. This default
+# should chnage to a NixOS cert once we have our
+# own secure boot signing infrastructure.
+#
+# Ignored if msVarsTemplate is false.
+, vendorPkKek ? "$NIX_BUILD_TOP/debian/PkKek-1-Debian.pem"
 , httpSupport ? false
 , tpmSupport ? false
 , tlsSupport ? false
@@ -14,28 +29,56 @@
 
 let
 
-  projectDscPath = if stdenv.isi686 then
-    "OvmfPkg/OvmfPkgIa32.dsc"
-  else if stdenv.isx86_64 then
-    "OvmfPkg/OvmfPkgX64.dsc"
-  else if stdenv.hostPlatform.isAarch then
-    "ArmVirtPkg/ArmVirtQemu.dsc"
-  else if stdenv.hostPlatform.isRiscV then
-    "OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc"
-  else
-    throw "Unsupported architecture";
+  platformSpecific = {
+    i686 = {
+      projectDscPath = "OvmfPkg/OvmfPkgIa32.dsc";
+      fwPrefix = "OVMF";
+    };
+    x86_64 = {
+      projectDscPath = "OvmfPkg/OvmfPkgX64.dsc";
+      fwPrefix = "OVMF";
+      msVarsArgs = {
+        flavor = "OVMF_4M";
+        archDir = "X64";
+      };
+    };
+    aarch64 = {
+      projectDscPath = "ArmVirtPkg/ArmVirtQemu.dsc";
+      fwPrefix = "AAVMF";
+      msVarsArgs = {
+        flavor = "AAVMF";
+        archDir = "AARCH64";
+      };
+    };
+    riscv64 = {
+      projectDscPath = "OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc";
+      fwPrefix = "RISCV_VIRT";
+    };
+  };
+
+  cpuName = stdenv.hostPlatform.parsed.cpu.name;
+
+  inherit (platformSpecific.${cpuName})
+    projectDscPath fwPrefix msVarsArgs;
 
   version = lib.getVersion edk2;
 
-  suffixes = {
-    i686 = "FV/OVMF";
-    x86_64 = "FV/OVMF";
-    aarch64 = "FV/AAVMF";
-    riscv64 = "FV/RISCV_VIRT";
+  OvmfPkKek1AppPrefix = "4e32566d-8e9e-4f52-81d3-5bb9715f9727";
+
+  debian-edk-src = fetchurl {
+    url = "http://deb.debian.org/debian/pool/main/e/edk2/edk2_2023.11-5.debian.tar.xz";
+    sha256 = "1yxlab4md30pxvjadr6b4xn6cyfw0c292q63pyfv4vylvhsb24g4";
   };
 
+  buildPrefix = "Build/*/*";
+
 in
 
+assert platformSpecific ? ${cpuName};
+assert systemManagementModeRequired -> stdenv.hostPlatform.isx86;
+assert msVarsTemplate -> fdSize4MB;
+assert msVarsTemplate -> platformSpecific.${cpuName} ? msVarsArgs;
+
 edk2.mkDerivation projectDscPath (finalAttrs: {
   pname = "OVMF";
   inherit version;
@@ -43,7 +86,8 @@ edk2.mkDerivation projectDscPath (finalAttrs: {
   outputs = [ "out" "fd" ];
 
   nativeBuildInputs = [ util-linux nasm acpica-tools ]
-    ++ lib.optionals stdenv.cc.isClang [ llvmPackages.bintools llvmPackages.llvm ];
+    ++ lib.optionals stdenv.cc.isClang [ llvmPackages.bintools llvmPackages.llvm ]
+    ++ lib.optionals msVarsTemplate [ python3 pexpect xorriso qemu dosfstools mtools ];
   strictDeps = true;
 
   hardeningDisable = [ "format" "stackprotector" "pic" "fortify" ];
@@ -54,6 +98,7 @@ edk2.mkDerivation projectDscPath (finalAttrs: {
     ++ lib.optionals debug [ "-D DEBUG_ON_SERIAL_PORT=TRUE" ]
     ++ lib.optionals sourceDebug [ "-D SOURCE_DEBUG_ENABLE=TRUE" ]
     ++ lib.optionals secureBoot [ "-D SECURE_BOOT_ENABLE=TRUE" ]
+    ++ lib.optionals systemManagementModeRequired [ "-D SMM_REQUIRE=TRUE" ]
     ++ lib.optionals csmSupport [ "-D CSM_ENABLE" ]
     ++ lib.optionals fdSize2MB ["-D FD_SIZE_2MB"]
     ++ lib.optionals fdSize4MB ["-D FD_SIZE_4MB"]
@@ -66,49 +111,75 @@ edk2.mkDerivation projectDscPath (finalAttrs: {
 
   env.PYTHON_COMMAND = "python3";
 
+  postUnpack = lib.optionalDrvAttr msVarsTemplate ''
+    unpackFile ${debian-edk-src}
+  '';
+
   postPatch = lib.optionalString csmSupport ''
     cp ${seabios}/share/seabios/Csm16.bin OvmfPkg/Csm/Csm16/Csm16.bin
   '';
 
-  postFixup = (
-    if stdenv.hostPlatform.isAarch then ''
-    mkdir -vp $fd/FV
-    mkdir -vp $fd/AAVMF
-    mv -v $out/FV/QEMU_{EFI,VARS}.fd $fd/FV
+  postConfigure = lib.optionalDrvAttr msVarsTemplate ''
+    tr -d '\n' < ${vendorPkKek} | sed \
+      -e 's/.*-----BEGIN CERTIFICATE-----/${OvmfPkKek1AppPrefix}:/' \
+      -e 's/-----END CERTIFICATE-----//' > vendor-cert-string
+    export PYTHONPATH=$NIX_BUILD_TOP/debian/python:$PYTHONPATH
+  '';
 
-    # Use Debian dir layout: https://salsa.debian.org/qemu-team/edk2/blob/debian/debian/rules
-    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
+  postBuild = lib.optionalString stdenv.hostPlatform.isAarch ''
+    (
+    cd ${buildPrefix}/FV
+    cp QEMU_EFI.fd ${fwPrefix}_CODE.fd
+    cp QEMU_VARS.fd ${fwPrefix}_VARS.fd
+
+    # QEMU expects 64MiB CODE and VARS files on ARM/AARCH64 architectures
+    # Truncate the firmware files to the expected size
+    truncate -s 64M ${fwPrefix}_CODE.fd
+    truncate -s 64M ${fwPrefix}_VARS.fd
+    )
+  '' + lib.optionalString stdenv.hostPlatform.isRiscV ''
+    truncate -s 32M ${buildPrefix}/FV/${fwPrefix}_CODE.fd
+    truncate -s 32M ${buildPrefix}/FV/${fwPrefix}_VARS.fd
+  '' + lib.optionalString msVarsTemplate ''
+    (
+    cd ${buildPrefix}
+    python3 $NIX_BUILD_TOP/debian/edk2-vars-generator.py \
+      --flavor ${msVarsArgs.flavor} \
+      --enrolldefaultkeys ${msVarsArgs.archDir}/EnrollDefaultKeys.efi \
+      --shell ${msVarsArgs.archDir}/Shell.efi \
+      --code FV/${fwPrefix}_CODE.fd \
+      --vars-template FV/${fwPrefix}_VARS.fd \
+      --certificate `< $NIX_BUILD_TOP/$sourceRoot/vendor-cert-string` \
+      --out-file FV/${fwPrefix}_VARS.ms.fd
+    )
+  '';
 
-    # Also add symlinks for Fedora dir layout: https://src.fedoraproject.org/cgit/rpms/edk2.git/tree/edk2.spec
+  postInstall = ''
+    mkdir -vp $fd/FV
+    mv -v $out/FV/${fwPrefix}_{CODE,VARS}.fd $fd/FV
+  '' + lib.optionalString msVarsTemplate ''
+    mv -v $out/FV/${fwPrefix}_VARS.ms.fd $fd/FV
+    ln -sv $fd/FV/${fwPrefix}_CODE{,.ms}.fd
+  '' + lib.optionalString stdenv.hostPlatform.isAarch ''
+    mv -v $out/FV/QEMU_{EFI,VARS}.fd $fd/FV
+    # Add symlinks for Fedora dir layout: https://src.fedoraproject.org/cgit/rpms/edk2.git/tree/edk2.spec
+    mkdir -vp $fd/AAVMF
     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 if stdenv.hostPlatform.isRiscV then ''
-    mkdir -vp $fd/FV
-
-    mv -v $out/FV/RISCV_VIRT_{CODE,VARS}.fd $fd/FV/
-    truncate -s 32M $fd/FV/RISCV_VIRT_CODE.fd
-    truncate -s 32M $fd/FV/RISCV_VIRT_VARS.fd
-  ''
-  else ''
-    mkdir -vp $fd/FV
-    mv -v $out/FV/OVMF{,_CODE,_VARS}.fd $fd/FV
-  '');
+  '';
 
   dontPatchELF = true;
 
   passthru =
   let
-    cpuName = stdenv.hostPlatform.parsed.cpu.name;
-    suffix = suffixes."${cpuName}" or (throw "Host cpu name `${cpuName}` is not supported in this OVMF derivation!");
-    prefix = "${finalAttrs.finalPackage.fd}/${suffix}";
+    prefix = "${finalAttrs.finalPackage.fd}/FV/${fwPrefix}";
   in {
     firmware  = "${prefix}_CODE.fd";
     variables = "${prefix}_VARS.fd";
     # This will test the EFI firmware for the host platform as part of the NixOS Tests setup.
     tests.basic-systemd-boot = nixosTests.systemd-boot.basic;
+    tests.secureBoot-systemd-boot = nixosTests.systemd-boot.secureBoot;
+    inherit secureBoot systemManagementModeRequired;
   };
 
   meta = {