Merge pull request #284874 from shlevy/ovmf-ms
Enable MS-compatible secure boot with OVMF
This commit is contained in:
commit
af810fc67e
5 changed files with 153 additions and 43 deletions
|
@ -536,6 +536,9 @@ let format' = format; in let
|
||||||
concatStringsSep " " (lib.optional useEFIBoot "-drive if=pflash,format=raw,unit=0,readonly=on,file=${efiFirmware}"
|
concatStringsSep " " (lib.optional useEFIBoot "-drive if=pflash,format=raw,unit=0,readonly=on,file=${efiFirmware}"
|
||||||
++ lib.optionals touchEFIVars [
|
++ lib.optionals touchEFIVars [
|
||||||
"-drive if=pflash,format=raw,unit=1,file=$efiVars"
|
"-drive if=pflash,format=raw,unit=1,file=$efiVars"
|
||||||
|
] ++ lib.optionals (OVMF.systemManagementModeRequired or false) [
|
||||||
|
"-machine" "q35,smm=on"
|
||||||
|
"-global" "driver=cfi.pflash01,property=secure,value=on"
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
inherit memSize;
|
inherit memSize;
|
||||||
|
|
|
@ -877,9 +877,11 @@ in
|
||||||
type = types.package;
|
type = types.package;
|
||||||
default = (pkgs.OVMF.override {
|
default = (pkgs.OVMF.override {
|
||||||
secureBoot = cfg.useSecureBoot;
|
secureBoot = cfg.useSecureBoot;
|
||||||
|
systemManagementModeRequired = cfg.useSecureBoot;
|
||||||
}).fd;
|
}).fd;
|
||||||
defaultText = ''(pkgs.OVMF.override {
|
defaultText = ''(pkgs.OVMF.override {
|
||||||
secureBoot = cfg.useSecureBoot;
|
secureBoot = cfg.useSecureBoot;
|
||||||
|
systemManagementModeRequired = cfg.useSecureBoot;
|
||||||
}).fd'';
|
}).fd'';
|
||||||
description =
|
description =
|
||||||
lib.mdDoc "OVMF firmware package, defaults to OVMF configured with secure boot if needed.";
|
lib.mdDoc "OVMF firmware package, defaults to OVMF configured with secure boot if needed.";
|
||||||
|
@ -1183,6 +1185,10 @@ in
|
||||||
"-tpmdev emulator,id=tpm_dev_0,chardev=chrtpm"
|
"-tpmdev emulator,id=tpm_dev_0,chardev=chrtpm"
|
||||||
"-device ${cfg.tpm.deviceModel},tpmdev=tpm_dev_0"
|
"-device ${cfg.tpm.deviceModel},tpmdev=tpm_dev_0"
|
||||||
])
|
])
|
||||||
|
(mkIf (cfg.efi.OVMF.systemManagementModeRequired or false) [
|
||||||
|
"-machine" "q35,smm=on"
|
||||||
|
"-global" "driver=cfi.pflash01,property=secure,value=on"
|
||||||
|
])
|
||||||
];
|
];
|
||||||
|
|
||||||
virtualisation.qemu.drives = mkMerge [
|
virtualisation.qemu.drives = mkMerge [
|
||||||
|
|
|
@ -39,6 +39,32 @@ in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Test that systemd-boot works with secure boot
|
||||||
|
secureBoot = makeTest {
|
||||||
|
name = "systemd-boot-secure-boot";
|
||||||
|
|
||||||
|
nodes.machine = {
|
||||||
|
imports = [ common ];
|
||||||
|
environment.systemPackages = [ pkgs.sbctl ];
|
||||||
|
virtualisation.useSecureBoot = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript = ''
|
||||||
|
machine.start(allow_reboot=True)
|
||||||
|
machine.wait_for_unit("multi-user.target")
|
||||||
|
|
||||||
|
machine.succeed("sbctl create-keys")
|
||||||
|
machine.succeed("sbctl enroll-keys --yes-this-might-brick-my-machine")
|
||||||
|
machine.succeed('sbctl sign /boot/EFI/systemd/systemd-bootx64.efi')
|
||||||
|
machine.succeed('sbctl sign /boot/EFI/BOOT/BOOTX64.EFI')
|
||||||
|
machine.succeed('sbctl sign /boot/EFI/nixos/*bzImage.efi')
|
||||||
|
|
||||||
|
machine.reboot()
|
||||||
|
|
||||||
|
assert "Secure Boot: enabled (user)" in machine.succeed("bootctl status")
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
# Check that specialisations create corresponding boot entries.
|
# Check that specialisations create corresponding boot entries.
|
||||||
specialisation = makeTest {
|
specialisation = makeTest {
|
||||||
name = "systemd-boot-specialisation";
|
name = "systemd-boot-specialisation";
|
||||||
|
|
|
@ -1,8 +1,23 @@
|
||||||
{ stdenv, nixosTests, lib, edk2, util-linux, nasm, acpica-tools, llvmPackages
|
{ stdenv, nixosTests, lib, edk2, util-linux, nasm, acpica-tools, llvmPackages
|
||||||
|
, fetchurl, python3, pexpect, xorriso, qemu, dosfstools, mtools
|
||||||
, csmSupport ? false, seabios
|
, csmSupport ? false, seabios
|
||||||
, fdSize2MB ? csmSupport
|
, fdSize2MB ? csmSupport
|
||||||
, fdSize4MB ? false
|
, fdSize4MB ? secureBoot
|
||||||
, secureBoot ? false
|
, 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
|
, httpSupport ? false
|
||||||
, tpmSupport ? false
|
, tpmSupport ? false
|
||||||
, tlsSupport ? false
|
, tlsSupport ? false
|
||||||
|
@ -14,28 +29,56 @@
|
||||||
|
|
||||||
let
|
let
|
||||||
|
|
||||||
projectDscPath = if stdenv.isi686 then
|
platformSpecific = {
|
||||||
"OvmfPkg/OvmfPkgIa32.dsc"
|
i686 = {
|
||||||
else if stdenv.isx86_64 then
|
projectDscPath = "OvmfPkg/OvmfPkgIa32.dsc";
|
||||||
"OvmfPkg/OvmfPkgX64.dsc"
|
fwPrefix = "OVMF";
|
||||||
else if stdenv.hostPlatform.isAarch then
|
};
|
||||||
"ArmVirtPkg/ArmVirtQemu.dsc"
|
x86_64 = {
|
||||||
else if stdenv.hostPlatform.isRiscV then
|
projectDscPath = "OvmfPkg/OvmfPkgX64.dsc";
|
||||||
"OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc"
|
fwPrefix = "OVMF";
|
||||||
else
|
msVarsArgs = {
|
||||||
throw "Unsupported architecture";
|
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;
|
version = lib.getVersion edk2;
|
||||||
|
|
||||||
suffixes = {
|
OvmfPkKek1AppPrefix = "4e32566d-8e9e-4f52-81d3-5bb9715f9727";
|
||||||
i686 = "FV/OVMF";
|
|
||||||
x86_64 = "FV/OVMF";
|
debian-edk-src = fetchurl {
|
||||||
aarch64 = "FV/AAVMF";
|
url = "http://deb.debian.org/debian/pool/main/e/edk2/edk2_2023.11-5.debian.tar.xz";
|
||||||
riscv64 = "FV/RISCV_VIRT";
|
sha256 = "1yxlab4md30pxvjadr6b4xn6cyfw0c292q63pyfv4vylvhsb24g4";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
buildPrefix = "Build/*/*";
|
||||||
|
|
||||||
in
|
in
|
||||||
|
|
||||||
|
assert platformSpecific ? ${cpuName};
|
||||||
|
assert systemManagementModeRequired -> stdenv.hostPlatform.isx86;
|
||||||
|
assert msVarsTemplate -> fdSize4MB;
|
||||||
|
assert msVarsTemplate -> platformSpecific.${cpuName} ? msVarsArgs;
|
||||||
|
|
||||||
edk2.mkDerivation projectDscPath (finalAttrs: {
|
edk2.mkDerivation projectDscPath (finalAttrs: {
|
||||||
pname = "OVMF";
|
pname = "OVMF";
|
||||||
inherit version;
|
inherit version;
|
||||||
|
@ -43,7 +86,8 @@ edk2.mkDerivation projectDscPath (finalAttrs: {
|
||||||
outputs = [ "out" "fd" ];
|
outputs = [ "out" "fd" ];
|
||||||
|
|
||||||
nativeBuildInputs = [ util-linux nasm acpica-tools ]
|
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;
|
strictDeps = true;
|
||||||
|
|
||||||
hardeningDisable = [ "format" "stackprotector" "pic" "fortify" ];
|
hardeningDisable = [ "format" "stackprotector" "pic" "fortify" ];
|
||||||
|
@ -54,6 +98,7 @@ edk2.mkDerivation projectDscPath (finalAttrs: {
|
||||||
++ lib.optionals debug [ "-D DEBUG_ON_SERIAL_PORT=TRUE" ]
|
++ lib.optionals debug [ "-D DEBUG_ON_SERIAL_PORT=TRUE" ]
|
||||||
++ lib.optionals sourceDebug [ "-D SOURCE_DEBUG_ENABLE=TRUE" ]
|
++ lib.optionals sourceDebug [ "-D SOURCE_DEBUG_ENABLE=TRUE" ]
|
||||||
++ lib.optionals secureBoot [ "-D SECURE_BOOT_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 csmSupport [ "-D CSM_ENABLE" ]
|
||||||
++ lib.optionals fdSize2MB ["-D FD_SIZE_2MB"]
|
++ lib.optionals fdSize2MB ["-D FD_SIZE_2MB"]
|
||||||
++ lib.optionals fdSize4MB ["-D FD_SIZE_4MB"]
|
++ lib.optionals fdSize4MB ["-D FD_SIZE_4MB"]
|
||||||
|
@ -66,49 +111,75 @@ edk2.mkDerivation projectDscPath (finalAttrs: {
|
||||||
|
|
||||||
env.PYTHON_COMMAND = "python3";
|
env.PYTHON_COMMAND = "python3";
|
||||||
|
|
||||||
|
postUnpack = lib.optionalDrvAttr msVarsTemplate ''
|
||||||
|
unpackFile ${debian-edk-src}
|
||||||
|
'';
|
||||||
|
|
||||||
postPatch = lib.optionalString csmSupport ''
|
postPatch = lib.optionalString csmSupport ''
|
||||||
cp ${seabios}/share/seabios/Csm16.bin OvmfPkg/Csm/Csm16/Csm16.bin
|
cp ${seabios}/share/seabios/Csm16.bin OvmfPkg/Csm/Csm16/Csm16.bin
|
||||||
'';
|
'';
|
||||||
|
|
||||||
postFixup = (
|
postConfigure = lib.optionalDrvAttr msVarsTemplate ''
|
||||||
if stdenv.hostPlatform.isAarch then ''
|
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
|
||||||
|
'';
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
'';
|
||||||
|
|
||||||
|
postInstall = ''
|
||||||
mkdir -vp $fd/FV
|
mkdir -vp $fd/FV
|
||||||
mkdir -vp $fd/AAVMF
|
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
|
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
|
||||||
# Use Debian dir layout: https://salsa.debian.org/qemu-team/edk2/blob/debian/debian/rules
|
mkdir -vp $fd/AAVMF
|
||||||
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_CODE.fd $fd/AAVMF/QEMU_EFI-pflash.raw
|
||||||
ln -s $fd/FV/AAVMF_VARS.fd $fd/AAVMF/vars-template-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;
|
dontPatchELF = true;
|
||||||
|
|
||||||
passthru =
|
passthru =
|
||||||
let
|
let
|
||||||
cpuName = stdenv.hostPlatform.parsed.cpu.name;
|
prefix = "${finalAttrs.finalPackage.fd}/FV/${fwPrefix}";
|
||||||
suffix = suffixes."${cpuName}" or (throw "Host cpu name `${cpuName}` is not supported in this OVMF derivation!");
|
|
||||||
prefix = "${finalAttrs.finalPackage.fd}/${suffix}";
|
|
||||||
in {
|
in {
|
||||||
firmware = "${prefix}_CODE.fd";
|
firmware = "${prefix}_CODE.fd";
|
||||||
variables = "${prefix}_VARS.fd";
|
variables = "${prefix}_VARS.fd";
|
||||||
# This will test the EFI firmware for the host platform as part of the NixOS Tests setup.
|
# 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.basic-systemd-boot = nixosTests.systemd-boot.basic;
|
||||||
|
tests.secureBoot-systemd-boot = nixosTests.systemd-boot.secureBoot;
|
||||||
|
inherit secureBoot systemManagementModeRequired;
|
||||||
};
|
};
|
||||||
|
|
||||||
meta = {
|
meta = {
|
||||||
|
|
|
@ -26824,12 +26824,16 @@ with pkgs;
|
||||||
|
|
||||||
rust-hypervisor-firmware = callPackage ../applications/virtualization/rust-hypervisor-firmware { };
|
rust-hypervisor-firmware = callPackage ../applications/virtualization/rust-hypervisor-firmware { };
|
||||||
|
|
||||||
OVMF = callPackage ../applications/virtualization/OVMF { };
|
OVMF = callPackage ../applications/virtualization/OVMF {
|
||||||
|
inherit (python3Packages) pexpect;
|
||||||
|
};
|
||||||
OVMFFull = callPackage ../applications/virtualization/OVMF {
|
OVMFFull = callPackage ../applications/virtualization/OVMF {
|
||||||
|
inherit (python3Packages) pexpect;
|
||||||
secureBoot = true;
|
secureBoot = true;
|
||||||
httpSupport = true;
|
httpSupport = true;
|
||||||
tpmSupport = true;
|
tpmSupport = true;
|
||||||
tlsSupport = true;
|
tlsSupport = true;
|
||||||
|
msVarsTemplate = stdenv.isx86_64 || stdenv.isAarch64;
|
||||||
};
|
};
|
||||||
|
|
||||||
ops = callPackage ../applications/virtualization/ops { };
|
ops = callPackage ../applications/virtualization/ops { };
|
||||||
|
|
Loading…
Reference in a new issue