Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 10 additions & 1 deletion petri/petri_artifacts_common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,19 @@ pub mod capabilities {
/// Software VPCI device emulation support.
pub const VPCI: &str = "vpci";

/// Support for resetting a partition running Windows.
pub const WINDOWS_PARTITION_RESET: &str = "windows_partition_reset";

/// All capability names known to petri, including those defined by
/// incubators. Incubator device capabilities are the device's profile
/// `name` with `-` replaced by `_` (e.g. `edu-initiator` → `edu_initiator`).
pub const KNOWN_CAPABILITIES: &[&str] = &[VPCI, "test_disk", "edu_initiator", "ivshmem_target"];
pub const KNOWN_CAPABILITIES: &[&str] = &[
VPCI,
WINDOWS_PARTITION_RESET,
"test_disk",
"edu_initiator",
"ivshmem_target",
];

/// Returns `name` if it is a known capability name.
pub fn known(name: &str) -> Option<&'static str> {
Expand Down
106 changes: 90 additions & 16 deletions petri/src/requirements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,19 @@ pub enum VmmType {
HyperV,
}

/// Hypervisor backends that OpenVMM can use.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OpenVmmHypervisor {
/// Linux Microsoft Hypervisor backend.
Mshv,
/// Linux KVM backend.
Kvm,
/// Windows Hypervisor Platform backend.
Whp,
/// macOS Hypervisor Framework backend.
Hvf,
}

/// Information about the VM host, retrieved via PowerShell on Windows.
#[derive(Debug, Clone)]
pub struct VmHostInfo {
Expand All @@ -93,6 +106,8 @@ pub struct HostContext {
pub execution_environment: ExecutionEnvironment,
/// Whether the host hypervisor supports software VPCI device emulation
pub vpci_supported: bool,
/// Hypervisor backend that OpenVMM will select on this host.
pub openvmm_hypervisor: Option<OpenVmmHypervisor>,
}

impl HostContext {
Expand Down Expand Up @@ -146,6 +161,27 @@ impl HostContext {
// VPCI support: only Windows (virt_whp and Hyper-V) supports it for now.
let vpci_supported = cfg!(windows);

let openvmm_hypervisor = if cfg!(target_os = "linux") {
if fs_err::File::open("/dev/mshv").is_ok() {
Some(OpenVmmHypervisor::Mshv)
} else if fs_err::File::options()
.read(true)
.write(true)
.open("/dev/kvm")
.is_ok()
{
Some(OpenVmmHypervisor::Kvm)
} else {
None
}
} else if cfg!(windows) {
Some(OpenVmmHypervisor::Whp)
} else if cfg!(target_os = "macos") {
Some(OpenVmmHypervisor::Hvf)
} else {
None
};

Self {
vm_host_info,
vendor,
Expand All @@ -155,6 +191,7 @@ impl HostContext {
ExecutionEnvironment::Baremetal
},
vpci_supported,
openvmm_hypervisor,
}
}
}
Expand All @@ -177,7 +214,12 @@ pub enum TestRequirement {
/// add capabilities that it detects itself. A test requiring a capability
/// that is not available is skipped, so such tests automatically
/// self-exclude on any host that cannot satisfy them.
RequiresCapability(&'static str),
RequiresCapability {
/// Capability name.
name: &'static str,
/// VMM used by the test, which may affect capability availability.
vmm: VmmType,
},
/// Logical AND of two requirements.
And(Box<TestRequirement>, Box<TestRequirement>),
/// Logical OR of two requirements.
Expand Down Expand Up @@ -207,14 +249,6 @@ impl TestRequirement {

/// Evaluate if this requirement is satisfied with the given host context
pub fn is_satisfied(&self, context: &HostContext) -> bool {
self.is_satisfied_with_capabilities(context, &available_capabilities(context))
}

fn is_satisfied_with_capabilities(
&self,
context: &HostContext,
capabilities: &BTreeSet<&'static str>,
) -> bool {
match self {
TestRequirement::ExecutionEnvironment(env) => context.execution_environment == *env,
TestRequirement::Vendor(vendor) => context.vendor == *vendor,
Expand All @@ -229,16 +263,16 @@ impl TestRequirement {
false
}
}
TestRequirement::RequiresCapability(name) => capabilities.contains(name),
TestRequirement::RequiresCapability { name, vmm } => {
available_capabilities(context, *vmm).contains(name)
}
TestRequirement::And(req1, req2) => {
req1.is_satisfied_with_capabilities(context, capabilities)
&& req2.is_satisfied_with_capabilities(context, capabilities)
req1.is_satisfied(context) && req2.is_satisfied(context)
}
TestRequirement::Or(req1, req2) => {
req1.is_satisfied_with_capabilities(context, capabilities)
|| req2.is_satisfied_with_capabilities(context, capabilities)
req1.is_satisfied(context) || req2.is_satisfied(context)
}
TestRequirement::Not(req) => !req.is_satisfied_with_capabilities(context, capabilities),
TestRequirement::Not(req) => !req.is_satisfied(context),
TestRequirement::Any => true,
}
}
Expand All @@ -254,13 +288,25 @@ pub fn is_known_capability(name: &str) -> bool {
known_capability(name).is_some()
}

fn available_capabilities(context: &HostContext) -> BTreeSet<&'static str> {
fn available_capabilities(context: &HostContext, vmm: VmmType) -> BTreeSet<&'static str> {
let mut capabilities = BTreeSet::new();

if context.vpci_supported {
capabilities.insert(capabilities::VPCI);
}

// virt_mshv cannot currently reset partitions running Windows.
// This is due to two issues:
// * A hypervisor issue that prevents locked hv#1 MSRs from being set by the host VMM.
// * Missing support for the HvScrubPartition hypercall.
// Once either of these are fixed, we can remove this check and feature.
if !matches!(
(vmm, context.openvmm_hypervisor),
(VmmType::OpenVmm, Some(OpenVmmHypervisor::Mshv))
) {
capabilities.insert(capabilities::WINDOWS_PARTITION_RESET);
}

match std::env::var("PETRI_CAPABILITIES") {
Ok(env_capabilities) => {
for capability in env_capabilities.split(',').map(str::trim) {
Expand Down Expand Up @@ -323,3 +369,31 @@ pub fn can_run_test_with_context(
true
}
}

#[cfg(test)]
mod tests {
use super::*;

fn host_context(openvmm_hypervisor: OpenVmmHypervisor) -> HostContext {
HostContext {
vm_host_info: None,
vendor: Vendor::Intel,
execution_environment: ExecutionEnvironment::Baremetal,
vpci_supported: false,
openvmm_hypervisor: Some(openvmm_hypervisor),
}
}

#[test]
fn capabilities_are_evaluated_for_the_selected_vmm() {
let requirement = |vmm| TestRequirement::RequiresCapability {
name: capabilities::WINDOWS_PARTITION_RESET,
vmm,
};
let mshv = host_context(OpenVmmHypervisor::Mshv);

assert!(!requirement(VmmType::OpenVmm).is_satisfied(&mshv));
assert!(requirement(VmmType::HyperV).is_satisfied(&mshv));
assert!(requirement(VmmType::OpenVmm).is_satisfied(&host_context(OpenVmmHypervisor::Kvm)));
}
}
Loading
Loading