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