1{ stdenv, nixosTests, lib, edk2, util-linux, nasm, acpica-tools, llvmPackages
2, fetchurl, python3, pexpect, xorriso, qemu, dosfstools, mtools
3, fdSize2MB ? false
4, fdSize4MB ? secureBoot
5, secureBoot ? false
6, systemManagementModeRequired ? secureBoot && stdenv.hostPlatform.isx86
7# Whether to create an nvram variables template
8# which includes the MSFT secure boot keys
9, msVarsTemplate ? false
10# When creating the nvram variables template with
11# the MSFT keys, we also must provide a certificate
12# to use as the PK and first KEK for the keystore.
13#
14# By default, we use Debian's cert. This default
15# should chnage to a NixOS cert once we have our
16# own secure boot signing infrastructure.
17#
18# Ignored if msVarsTemplate is false.
19, vendorPkKek ? "$NIX_BUILD_TOP/debian/PkKek-1-Debian.pem"
20, httpSupport ? false
21, tpmSupport ? false
22, tlsSupport ? false
23, debug ? false
24# Usually, this option is broken, do not use it except if you know what you are
25# doing.
26, sourceDebug ? false
27, projectDscPath ? {
28 i686 = "OvmfPkg/OvmfPkgIa32.dsc";
29 x86_64 = "OvmfPkg/OvmfPkgX64.dsc";
30 aarch64 = "ArmVirtPkg/ArmVirtQemu.dsc";
31 riscv64 = "OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc";
32 }.${stdenv.hostPlatform.parsed.cpu.name}
33 or (throw "Unsupported OVMF `projectDscPath` on ${stdenv.hostPlatform.parsed.cpu.name}")
34, fwPrefix ? {
35 i686 = "OVMF";
36 x86_64 = "OVMF";
37 aarch64 = "AAVMF";
38 riscv64 = "RISCV_VIRT";
39 }.${stdenv.hostPlatform.parsed.cpu.name}
40 or (throw "Unsupported OVMF `fwPrefix` on ${stdenv.hostPlatform.parsed.cpu.name}")
41, metaPlatforms ? edk2.meta.platforms
42}:
43
44let
45
46 platformSpecific = {
47 i686.msVarsArgs = {
48 flavor = "OVMF";
49 archDir = "Ia32";
50 };
51 x86_64.msVarsArgs = {
52 flavor = "OVMF_4M";
53 archDir = "X64";
54 };
55 aarch64.msVarsArgs = {
56 flavor = "AAVMF";
57 archDir = "AARCH64";
58 };
59 };
60
61 cpuName = stdenv.hostPlatform.parsed.cpu.name;
62
63 inherit (platformSpecific.${cpuName}) msVarsArgs;
64
65 version = lib.getVersion edk2;
66
67 OvmfPkKek1AppPrefix = "4e32566d-8e9e-4f52-81d3-5bb9715f9727";
68
69 debian-edk-src = fetchurl {
70 url = "http://deb.debian.org/debian/pool/main/e/edk2/edk2_2023.11-5.debian.tar.xz";
71 sha256 = "1yxlab4md30pxvjadr6b4xn6cyfw0c292q63pyfv4vylvhsb24g4";
72 };
73
74 buildPrefix = "Build/*/*";
75
76in
77
78assert platformSpecific ? ${cpuName};
79assert msVarsTemplate -> fdSize4MB;
80assert msVarsTemplate -> platformSpecific.${cpuName} ? msVarsArgs;
81
82edk2.mkDerivation projectDscPath (finalAttrs: {
83 pname = "OVMF";
84 inherit version;
85
86 outputs = [ "out" "fd" ];
87
88 nativeBuildInputs = [ util-linux nasm acpica-tools ]
89 ++ lib.optionals stdenv.cc.isClang [ llvmPackages.bintools llvmPackages.llvm ]
90 ++ lib.optionals msVarsTemplate [ python3 pexpect xorriso qemu dosfstools mtools ];
91 strictDeps = true;
92
93 hardeningDisable = [ "format" "stackprotector" "pic" "fortify" ];
94
95 buildFlags =
96 # IPv6 has no reason to be disabled.
97 [ "-D NETWORK_IP6_ENABLE=TRUE" ]
98 ++ lib.optionals debug [ "-D DEBUG_ON_SERIAL_PORT=TRUE" ]
99 ++ lib.optionals sourceDebug [ "-D SOURCE_DEBUG_ENABLE=TRUE" ]
100 ++ lib.optionals secureBoot [ "-D SECURE_BOOT_ENABLE=TRUE" ]
101 ++ lib.optionals systemManagementModeRequired [ "-D SMM_REQUIRE=TRUE" ]
102 ++ lib.optionals fdSize2MB ["-D FD_SIZE_2MB"]
103 ++ lib.optionals fdSize4MB ["-D FD_SIZE_4MB"]
104 ++ lib.optionals httpSupport [ "-D NETWORK_HTTP_ENABLE=TRUE" "-D NETWORK_HTTP_BOOT_ENABLE=TRUE" ]
105 ++ lib.optionals tlsSupport [ "-D NETWORK_TLS_ENABLE=TRUE" ]
106 ++ lib.optionals tpmSupport [ "-D TPM_ENABLE" "-D TPM2_ENABLE" "-D TPM2_CONFIG_ENABLE"];
107
108 buildConfig = if debug then "DEBUG" else "RELEASE";
109 env.NIX_CFLAGS_COMPILE = lib.optionalString stdenv.cc.isClang "-Qunused-arguments";
110
111 env.PYTHON_COMMAND = "python3";
112
113 postUnpack = lib.optionalDrvAttr msVarsTemplate ''
114 unpackFile ${debian-edk-src}
115 '';
116
117 postConfigure = lib.optionalDrvAttr msVarsTemplate ''
118 tr -d '\n' < ${vendorPkKek} | sed \
119 -e 's/.*-----BEGIN CERTIFICATE-----/${OvmfPkKek1AppPrefix}:/' \
120 -e 's/-----END CERTIFICATE-----//' > vendor-cert-string
121 export PYTHONPATH=$NIX_BUILD_TOP/debian/python:$PYTHONPATH
122 '';
123
124 postBuild = lib.optionalString stdenv.hostPlatform.isAarch ''
125 (
126 cd ${buildPrefix}/FV
127 cp QEMU_EFI.fd ${fwPrefix}_CODE.fd
128 cp QEMU_VARS.fd ${fwPrefix}_VARS.fd
129
130 # QEMU expects 64MiB CODE and VARS files on ARM/AARCH64 architectures
131 # Truncate the firmware files to the expected size
132 truncate -s 64M ${fwPrefix}_CODE.fd
133 truncate -s 64M ${fwPrefix}_VARS.fd
134 )
135 '' + lib.optionalString stdenv.hostPlatform.isRiscV ''
136 truncate -s 32M ${buildPrefix}/FV/${fwPrefix}_CODE.fd
137 truncate -s 32M ${buildPrefix}/FV/${fwPrefix}_VARS.fd
138 '' + lib.optionalString msVarsTemplate ''
139 (
140 cd ${buildPrefix}
141 python3 $NIX_BUILD_TOP/debian/edk2-vars-generator.py \
142 --flavor ${msVarsArgs.flavor} \
143 --enrolldefaultkeys ${msVarsArgs.archDir}/EnrollDefaultKeys.efi \
144 --shell ${msVarsArgs.archDir}/Shell.efi \
145 --code FV/${fwPrefix}_CODE.fd \
146 --vars-template FV/${fwPrefix}_VARS.fd \
147 --certificate `< $NIX_BUILD_TOP/$sourceRoot/vendor-cert-string` \
148 --out-file FV/${fwPrefix}_VARS.ms.fd
149 )
150 '';
151
152 # TODO: Usage of -bios OVMF.fd is discouraged: https://lists.katacontainers.io/pipermail/kata-dev/2021-January/001650.html
153 # We should remove the isx86-specifc block here once we're ready to update nixpkgs to stop using that and update the
154 # release notes accordingly.
155 postInstall = ''
156 mkdir -vp $fd/FV
157 '' + lib.optionalString (builtins.elem fwPrefix [
158 "OVMF" "AAVMF" "RISCV_VIRT"
159 ]) ''
160 mv -v $out/FV/${fwPrefix}_{CODE,VARS}.fd $fd/FV
161 '' + lib.optionalString stdenv.hostPlatform.isx86 ''
162 mv -v $out/FV/${fwPrefix}.fd $fd/FV
163 '' + lib.optionalString msVarsTemplate ''
164 mv -v $out/FV/${fwPrefix}_VARS.ms.fd $fd/FV
165 ln -sv $fd/FV/${fwPrefix}_CODE{,.ms}.fd
166 '' + lib.optionalString stdenv.hostPlatform.isAarch ''
167 mv -v $out/FV/QEMU_{EFI,VARS}.fd $fd/FV
168 # Add symlinks for Fedora dir layout: https://src.fedoraproject.org/cgit/rpms/edk2.git/tree/edk2.spec
169 mkdir -vp $fd/AAVMF
170 ln -s $fd/FV/AAVMF_CODE.fd $fd/AAVMF/QEMU_EFI-pflash.raw
171 ln -s $fd/FV/AAVMF_VARS.fd $fd/AAVMF/vars-template-pflash.raw
172 '';
173
174 dontPatchELF = true;
175
176 passthru =
177 let
178 prefix = "${finalAttrs.finalPackage.fd}/FV/${fwPrefix}";
179 in {
180 firmware = "${prefix}_CODE.fd";
181 variables = "${prefix}_VARS.fd";
182 # This will test the EFI firmware for the host platform as part of the NixOS Tests setup.
183 tests.basic-systemd-boot = nixosTests.systemd-boot.basic;
184 tests.secureBoot-systemd-boot = nixosTests.systemd-boot.secureBoot;
185 inherit secureBoot systemManagementModeRequired;
186 };
187
188 meta = {
189 description = "Sample UEFI firmware for QEMU and KVM";
190 homepage = "https://github.com/tianocore/tianocore.github.io/wiki/OVMF";
191 license = lib.licenses.bsd2;
192 platforms = metaPlatforms;
193 maintainers = with lib.maintainers; [ adamcstephens raitobezarius ];
194 broken = stdenv.isDarwin;
195 };
196})