Skip to content

petri: Disable secure boot by default for HyperV tests and introduce secure boot tests #1386

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 38 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
769124a
Initial commit: Add ca template function and without secure boot
maheeraeron May 20, 2025
9b570b0
Add secure_boot_template field to PetriConfigOpenVmm
maheeraeron May 20, 2025
295f36c
Revert return statement in PetriVmConfigOpenVmm::new()
maheeraeron May 20, 2025
fd217f2
Revert plumbing and add new with_* functions
maheeraeron May 20, 2025
13bf127
Resolve secure boot templates with with_secure_boot(), disable on def…
maheeraeron May 20, 2025
6b57eb9
Only enable linux uefi tests for now
maheeraeron May 22, 2025
90b3454
Restrict secure-boot disabling to gen2, expand secure boot test profiles
maheeraeron May 22, 2025
9c1f5f4
Panic on unsupported secure_boot os flavors
maheeraeron May 22, 2025
e0262a4
Fix enum variant name
maheeraeron May 22, 2025
2602339
Modify secure boot test to wait for successful boot event, add negati…
maheeraeron May 22, 2025
eea0e1e
Use agent to shutdown
maheeraeron May 30, 2025
f5864a3
Fix agent usage and defer to automatic shutdowns on tests
maheeraeron May 30, 2025
95392fc
Merge branch 'main' into user/maheeraeron/secureboot-tests
maheeraeron May 30, 2025
120a962
Fix mismatched tests by calling with_secure_boot()
maheeraeron Jun 2, 2025
daec5ff
Merge branch 'main' into user/maheeraeron/secureboot-tests
maheeraeron Jun 2, 2025
f117dd2
New plumbing to separate secure boot enablement from template setting…
maheeraeron Jun 2, 2025
848b5c3
Merge branch 'main' into user/maheeraeron/secureboot-tests
maheeraeron Jun 2, 2025
5c11b60
Forgot to add self for return
maheeraeron Jun 2, 2025
6d6a931
Fix clippy warnings
maheeraeron Jun 2, 2025
efb563c
Propogate error when setting secure boot state
maheeraeron Jun 2, 2025
9e773ea
Fix clippy warning with self
maheeraeron Jun 3, 2025
4e0a138
Recognize 18604 as boot failed
maheeraeron Jun 3, 2025
e13d29a
Merge branch 'main' into user/maheeraeron/secureboot-tests
maheeraeron Jun 3, 2025
257ce03
Restrict secure boot toggling to gen2 in run_core, use enlightened s…
maheeraeron Jun 3, 2025
95fafd1
Remove no_template tests
maheeraeron Jun 3, 2025
dd97482
Refined with_* functions per feedback and doc comments
maheeraeron Jun 3, 2025
492c7f0
Forgot to enable secure boot for openvmm
maheeraeron Jun 3, 2025
8cab37e
Remove unnecessary param in with* calls
maheeraeron Jun 3, 2025
a9393a2
Try mismatched tests with frontpage disabled
maheeraeron Jun 3, 2025
bc441f3
with_*_template only installs template now, allow guest_test_uefi to …
maheeraeron Jun 4, 2025
59c57d8
os flavor function in hyperv moved to impl PetriVmConfig for PetriVmC…
maheeraeron Jun 4, 2025
f40243b
Merge branch 'main' into user/maheeraeron/secureboot-tests
maheeraeron Jun 4, 2025
ffd901e
Merge branch 'main' into user/maheeraeron/secureboot-tests
maheeraeron Jun 5, 2025
2023093
Undo set_secure_boot_template + feedback
maheeraeron Jun 5, 2025
25749af
Forgot to remove mentions of secure_boot_enabled for hyperv
maheeraeron Jun 5, 2025
76aae98
Forgot to add ?;
maheeraeron Jun 5, 2025
3b5eb78
Merge branch 'main' into user/maheeraeron/secureboot-tests
maheeraeron Jun 5, 2025
97bb6cd
Clippy fixes
maheeraeron Jun 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 37 additions & 11 deletions petri/src/vm/hyperv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,18 @@ impl PetriVmConfig for PetriVmConfigHyperV {
Ok((Box::new(vm), client))
}

fn with_secure_boot(self: Box<Self>) -> Box<dyn PetriVmConfig> {
Box::new(Self::with_secure_boot(*self))
}

fn with_windows_secure_boot_template(self: Box<Self>) -> Box<dyn PetriVmConfig> {
Box::new(Self::with_windows_secure_boot_template(*self))
}

fn with_uefi_ca_secure_boot_template(self: Box<Self>) -> Box<dyn PetriVmConfig> {
Box::new(Self::with_uefi_ca_secure_boot_template(*self))
}

fn with_processor_topology(
self: Box<Self>,
topology: ProcessorTopology,
Expand Down Expand Up @@ -128,6 +136,10 @@ impl PetriVmConfig for PetriVmConfigHyperV {
fn with_uefi_frontpage(self: Box<Self>, enable: bool) -> Box<dyn PetriVmConfig> {
Box::new(Self::with_uefi_frontpage(*self, enable))
}

fn os_flavor(&self) -> OsFlavor {
self.os_flavor
}
}

/// A running VM that tests can interact with.
Expand Down Expand Up @@ -278,16 +290,7 @@ impl PetriVmConfigHyperV {
memory: 0x1_0000_0000,
proc_topology: ProcessorTopology::default(),
vhd_paths,
secure_boot_template: matches!(generation, powershell::HyperVGeneration::Two)
.then_some(match firmware.os_flavor() {
OsFlavor::Windows => powershell::HyperVSecureBootTemplate::MicrosoftWindows,
OsFlavor::Linux => {
powershell::HyperVSecureBootTemplate::MicrosoftUEFICertificateAuthority
}
OsFlavor::FreeBsd | OsFlavor::Uefi => {
powershell::HyperVSecureBootTemplate::SecureBootDisabled
}
}),
secure_boot_template: None,
openhcl_igvm,
agent_image,
openhcl_agent_image,
Expand Down Expand Up @@ -535,15 +538,38 @@ impl PetriVmConfigHyperV {
self
}

/// Set the VM to enable secure boot and inject the templates per OS flavor.
pub fn with_secure_boot(self) -> Self {
if !matches!(self.generation, powershell::HyperVGeneration::Two) {
panic!("Secure boot is only supported for UEFI firmware.");
}

match self.os_flavor {
OsFlavor::Windows => self.with_windows_secure_boot_template(),
OsFlavor::Linux => self.with_uefi_ca_secure_boot_template(),
_ => panic!("Secure boot unsupported for OS flavor {:?}", self.os_flavor),
}
}

/// Inject Windows secure boot templates into the VM's UEFI.
pub fn with_windows_secure_boot_template(mut self) -> Self {
if !matches!(self.generation, powershell::HyperVGeneration::Two) {
panic!("Secure boot templates are only supported for UEFI firmware.");
panic!("Secure boot is only supported for UEFI firmware.");
}
self.secure_boot_template = Some(powershell::HyperVSecureBootTemplate::MicrosoftWindows);
self
}

/// Inject UEFI CA secure boot templates into the VM's UEFI.
pub fn with_uefi_ca_secure_boot_template(mut self) -> Self {
if !matches!(self.generation, powershell::HyperVGeneration::Two) {
panic!("Secure boot is only supported for UEFI firmware.");
}
self.secure_boot_template =
Some(powershell::HyperVSecureBootTemplate::MicrosoftUEFICertificateAuthority);
self
}

/// Sets a custom OpenHCL IGVM image to use.
pub fn with_custom_openhcl(mut self, artifact: ResolvedArtifact) -> Self {
self.openhcl_igvm = Some(artifact);
Expand Down
5 changes: 1 addition & 4 deletions petri/src/vm/hyperv/powershell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,6 @@ impl ps::AsVal for HyperVGuestStateIsolationType {
/// Hyper-V Secure Boot Template
#[derive(Clone, Copy)]
pub enum HyperVSecureBootTemplate {
/// Secure Boot Disabled
SecureBootDisabled,
/// Windows Secure Boot Template
MicrosoftWindows,
/// Microsoft UEFI Certificate Authority Template
Expand All @@ -79,7 +77,6 @@ pub enum HyperVSecureBootTemplate {
impl ps::AsVal for HyperVSecureBootTemplate {
fn as_val(&self) -> impl '_ + AsRef<OsStr> {
match self {
HyperVSecureBootTemplate::SecureBootDisabled => "SecureBootDisabled",
HyperVSecureBootTemplate::MicrosoftWindows => "MicrosoftWindows",
HyperVSecureBootTemplate::MicrosoftUEFICertificateAuthority => {
"MicrosoftUEFICertificateAuthority"
Expand Down Expand Up @@ -409,7 +406,7 @@ pub fn run_set_vm_firmware(args: HyperVSetVMFirmwareArgs<'_>) -> anyhow::Result<
.pipeline();

builder = match args.secure_boot_template {
Some(HyperVSecureBootTemplate::SecureBootDisabled) | None => builder
None => builder
.cmdlet("Set-VMFirmware")
.arg("EnableSecureBoot", ps::RawVal::new("Off"))
.finish(),
Expand Down
11 changes: 11 additions & 0 deletions petri/src/vm/hyperv/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,14 @@ impl HyperVVM {
},
)?;

// Disable secure boot for generation 2 VMs
if generation == powershell::HyperVGeneration::Two {
powershell::run_set_vm_firmware(powershell::HyperVSetVMFirmwareArgs {
vmid: &vmid,
secure_boot_template: None,
})?;
}

Ok(this)
}

Expand Down Expand Up @@ -186,6 +194,9 @@ impl HyperVVM {
powershell::EVENT_ID_BOOT_FAILURE => Ok(FirmwareEvent::BootFailed),
powershell::EVENT_ID_NO_BOOT_DEVICE => Ok(FirmwareEvent::NoBootDevice),
powershell::EVENT_ID_BOOT_ATTEMPT => Ok(FirmwareEvent::BootAttempt),
powershell::EVENT_ID_BOOT_FAILURE_SECURE_BOOT_FAILED => {
Ok(FirmwareEvent::BootFailed)
}
id => anyhow::bail!("Unexpected event id: {id}"),
})
.transpose()
Expand Down
7 changes: 7 additions & 0 deletions petri/src/vm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,12 @@ pub trait PetriVmConfig: Send {
/// Run the VM, launching pipette and returning a client to it.
async fn run(self: Box<Self>) -> anyhow::Result<(Box<dyn PetriVm>, PipetteClient)>;

/// Set the VM to enable secure boot and inject the templates per OS flavor.
fn with_secure_boot(self: Box<Self>) -> Box<dyn PetriVmConfig>;
/// Inject Windows secure boot templates into the VM's UEFI.
fn with_windows_secure_boot_template(self: Box<Self>) -> Box<dyn PetriVmConfig>;
/// Inject UEFI CA secure boot templates into the VM's UEFI.
fn with_uefi_ca_secure_boot_template(self: Box<Self>) -> Box<dyn PetriVmConfig>;
/// Set the VM to use the specified processor topology.
fn with_processor_topology(
self: Box<Self>,
Expand All @@ -61,6 +65,9 @@ pub trait PetriVmConfig: Send {
) -> Box<dyn PetriVmConfig>;
/// Sets whether UEFI frontpage is enabled.
fn with_uefi_frontpage(self: Box<Self>, enable: bool) -> Box<dyn PetriVmConfig>;

/// Get the OS that the VM will boot into.
fn os_flavor(&self) -> OsFlavor;
}

/// Common processor topology information for the VM.
Expand Down
12 changes: 12 additions & 0 deletions petri/src/vm/openvmm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,18 @@ impl PetriVmConfig for PetriVmConfigOpenVmm {
Ok((Box::new(vm), client))
}

fn with_secure_boot(self: Box<Self>) -> Box<dyn PetriVmConfig> {
Box::new(Self::with_secure_boot(*self))
}

fn with_windows_secure_boot_template(self: Box<Self>) -> Box<dyn PetriVmConfig> {
Box::new(Self::with_windows_secure_boot_template(*self))
}

fn with_uefi_ca_secure_boot_template(self: Box<Self>) -> Box<dyn PetriVmConfig> {
Box::new(Self::with_uefi_ca_secure_boot_template(*self))
}

fn with_processor_topology(
self: Box<Self>,
topology: ProcessorTopology,
Expand Down Expand Up @@ -190,6 +198,10 @@ impl PetriVmConfig for PetriVmConfigOpenVmm {
fn with_uefi_frontpage(self: Box<Self>, enable: bool) -> Box<dyn PetriVmConfig> {
Box::new(Self::with_uefi_frontpage(*self, enable))
}

fn os_flavor(&self) -> OsFlavor {
self.firmware.os_flavor()
}
}

/// Various channels and resources used to interact with the VM while it is running.
Expand Down
34 changes: 31 additions & 3 deletions petri/src/vm/openvmm/modify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use hvlite_defs::config::VpciDeviceConfig;
use hvlite_defs::config::Vtl2BaseAddressType;
use petri_artifacts_common::tags::IsTestVmgs;
use petri_artifacts_common::tags::MachineArch;
use petri_artifacts_common::tags::OsFlavor;
use petri_artifacts_core::ResolvedArtifact;
use tpm_resources::TpmDeviceHandle;
use tpm_resources::TpmRegisterLayout;
Expand Down Expand Up @@ -127,30 +128,57 @@ impl PetriVmConfigOpenVmm {
self
}

/// Enable secure boot for the VM.
/// Set the VM to enable secure boot and inject the templates per OS flavor.
pub fn with_secure_boot(mut self) -> Self {
if !self.firmware.is_uefi() {
panic!("Secure boot is only supported for UEFI firmware.");
}

if self.firmware.is_openhcl() {
self.ged.as_mut().unwrap().secure_boot_enabled = true;
} else {
self.config.secure_boot_enabled = true;
}
self

match self.os_flavor() {
OsFlavor::Windows => self.with_windows_secure_boot_template(),
OsFlavor::Linux => self.with_uefi_ca_secure_boot_template(),
_ => panic!(
"Secure boot unsupported for OS flavor {:?}",
self.os_flavor()
),
}
}

/// Inject Windows secure boot templates into the VM's UEFI.
pub fn with_windows_secure_boot_template(mut self) -> Self {
if !self.firmware.is_uefi() {
panic!("Secure boot templates are only supported for UEFI firmware.");
panic!("Secure boot is only supported for UEFI firmware.");
}

if self.firmware.is_openhcl() {
self.ged.as_mut().unwrap().secure_boot_template =
get_resources::ged::GuestSecureBootTemplateType::MicrosoftWindows;
} else {
self.config.custom_uefi_vars = hyperv_secure_boot_templates::x64::microsoft_windows();
}

self
}

/// Inject UEFI CA secure boot templates into the VM's UEFI.
pub fn with_uefi_ca_secure_boot_template(mut self) -> Self {
if !self.firmware.is_uefi() {
panic!("Secure boot is only supported for UEFI firmware.");
}

if self.firmware.is_openhcl() {
self.ged.as_mut().unwrap().secure_boot_template =
get_resources::ged::GuestSecureBootTemplateType::MicrosoftUefiCertificateAuthoritiy;
} else {
self.config.custom_uefi_vars = hyperv_secure_boot_templates::x64::microsoft_uefi_ca();
}

self
}

Expand Down
66 changes: 66 additions & 0 deletions vmm_tests/vmm_tests/tests/tests/multiarch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use petri::ShutdownKind;
use petri::openvmm::NIC_MAC_ADDRESS;
use petri::openvmm::PetriVmConfigOpenVmm;
use petri_artifacts_common::tags::MachineArch;
use petri_artifacts_common::tags::OsFlavor;
use petri_artifacts_vmm_test::artifacts::test_vmgs::VMGS_WITH_BOOT_ENTRY;
use std::time::Duration;
use vmm_core_defs::HaltReason;
Expand Down Expand Up @@ -67,6 +68,71 @@ async fn boot(config: Box<dyn PetriVmConfig>) -> anyhow::Result<()> {
Ok(())
}

/// Basic boot test with secure boot enabled and a valid template.
#[vmm_test(
openvmm_uefi_aarch64(vhd(ubuntu_2404_server_aarch64)),
openvmm_uefi_x64(vhd(windows_datacenter_core_2022_x64)),
openvmm_uefi_x64(vhd(ubuntu_2204_server_x64)),
openvmm_openhcl_uefi_x64(vhd(windows_datacenter_core_2022_x64)),
openvmm_openhcl_uefi_x64(vhd(ubuntu_2204_server_x64)),
hyperv_uefi_aarch64(vhd(windows_11_enterprise_aarch64)),
hyperv_uefi_aarch64(vhd(ubuntu_2404_server_aarch64)),
hyperv_uefi_x64(vhd(windows_datacenter_core_2022_x64)),
hyperv_uefi_x64(vhd(ubuntu_2204_server_x64)),
hyperv_openhcl_uefi_aarch64(vhd(windows_11_enterprise_aarch64)),
hyperv_openhcl_uefi_aarch64(vhd(ubuntu_2404_server_aarch64)),
hyperv_openhcl_uefi_x64(vhd(windows_datacenter_core_2022_x64)),
hyperv_openhcl_uefi_x64(vhd(ubuntu_2204_server_x64))
)]
async fn secure_boot(config: Box<dyn PetriVmConfig>) -> anyhow::Result<()> {
let (vm, agent) = config.with_secure_boot().run().await?;
agent.power_off().await?;
assert_eq!(vm.wait_for_teardown().await?, HaltReason::PowerOff);
Ok(())
}

/// Verify that secure boot fails with a mismatched template.
/// TODO: Allow Hyper-V VMs to load a UEFI firmware per VM, not system wide.
#[vmm_test(
openvmm_uefi_aarch64(vhd(ubuntu_2404_server_aarch64)),
openvmm_uefi_x64(vhd(windows_datacenter_core_2022_x64)),
openvmm_uefi_x64(vhd(ubuntu_2204_server_x64)),
openvmm_openhcl_uefi_x64(vhd(windows_datacenter_core_2022_x64)),
openvmm_openhcl_uefi_x64(vhd(ubuntu_2204_server_x64)),
// hyperv_uefi_aarch64(vhd(windows_11_enterprise_aarch64)),
// hyperv_uefi_aarch64(vhd(ubuntu_2404_server_aarch64)),
// hyperv_uefi_x64(vhd(windows_datacenter_core_2022_x64)),
// hyperv_uefi_x64(vhd(ubuntu_2204_server_x64)),
hyperv_openhcl_uefi_aarch64(vhd(windows_11_enterprise_aarch64)),
hyperv_openhcl_uefi_aarch64(vhd(ubuntu_2404_server_aarch64)),
hyperv_openhcl_uefi_x64(vhd(windows_datacenter_core_2022_x64)),
hyperv_openhcl_uefi_x64(vhd(ubuntu_2204_server_x64))
)]
async fn secure_boot_mismatched_template(config: Box<dyn PetriVmConfig>) -> anyhow::Result<()> {
let mut vm = match config.os_flavor() {
OsFlavor::Windows => {
config
.with_secure_boot()
.with_uefi_ca_secure_boot_template()
.with_uefi_frontpage(false)
.run_without_agent()
.await?
}
OsFlavor::Linux => {
config
.with_secure_boot()
.with_windows_secure_boot_template()
.with_uefi_frontpage(false)
.run_without_agent()
.await?
}
_ => anyhow::bail!("Unsupported OS flavor for test: {:?}", config.os_flavor()),
};
assert_eq!(vm.wait_for_boot_event().await?, FirmwareEvent::BootFailed);
assert_eq!(vm.wait_for_teardown().await?, HaltReason::PowerOff);
Ok(())
}

/// Basic boot test for guests that are expected to reboot
// TODO: Remove this test and other enable Windows 11 ARM OpenVMM tests
// once we figure out how to get the guest to not reboot via IMC or other
Expand Down