1mod host_vmm;
11mod load;
12mod paravisor_vmm;
13mod run;
14
15use anyhow::Context;
16use clap::Parser;
17use pal_async::DefaultDriver;
18use pal_async::DefaultPool;
19use run::CommonState;
20use std::path::PathBuf;
21use tracing::level_filters::LevelFilter;
22use tracing_subscriber::fmt::format::FmtSpan;
23use tracing_subscriber::layer::SubscriberExt;
24use tracing_subscriber::util::SubscriberInitExt;
25
26fn main() -> anyhow::Result<()> {
27 tracing_subscriber::registry()
28 .with(
29 tracing_subscriber::fmt::layer()
30 .pretty()
31 .map_event_format(|e| e.with_source_location(false))
32 .fmt_fields(tracing_helpers::formatter::FieldFormatter)
33 .with_span_events(FmtSpan::NEW | FmtSpan::CLOSE),
34 )
35 .with(
36 tracing_subscriber::EnvFilter::builder()
37 .with_default_directive(LevelFilter::INFO.into())
38 .with_env_var("TMK_LOG")
39 .from_env_lossy(),
40 )
41 .init();
42
43 DefaultPool::run_with(do_main)
44}
45
46#[derive(Parser)]
53struct Options {
54 #[clap(long)]
56 hv: Option<HypervisorOpt>,
57 #[clap(long)]
60 disable_offloads: bool,
61 #[clap(long)]
63 tmk: PathBuf,
64 #[clap(long)]
66 list: bool,
67 #[clap(conflicts_with("list"))]
69 tests: Vec<String>,
70}
71
72#[derive(clap::ValueEnum, Copy, Clone)]
73enum HypervisorOpt {
74 #[cfg(target_os = "linux")]
76 Kvm,
77 #[cfg(all(target_os = "linux", guest_arch = "x86_64"))]
79 Mshv,
80 #[cfg(target_os = "linux")]
83 MshvVtl,
84 #[cfg(target_os = "windows")]
86 Whp,
87 #[cfg(target_os = "macos")]
89 Hvf,
90}
91
92async fn do_main(driver: DefaultDriver) -> anyhow::Result<()> {
93 let opts = Options::parse();
94
95 if opts.list {
96 let tmk = fs_err::File::open(&opts.tmk).context("failed to open TMK")?;
97 let tests = load::enumerate_tests(&tmk)?;
98 for test in tests {
99 println!("{}", test.name);
100 }
101 Ok(())
102 } else {
103 let hv = match opts.hv {
104 Some(hv) => hv,
105 None => choose_hypervisor()?,
106 };
107 let mut state = CommonState::new(driver, opts).await?;
108
109 state
110 .for_each_test(async |state, test| match hv {
111 #[cfg(target_os = "linux")]
112 HypervisorOpt::Kvm => state.run_host_vmm(virt_kvm::Kvm, test).await,
113 #[cfg(all(target_os = "linux", guest_arch = "x86_64"))]
114 HypervisorOpt::Mshv => state.run_host_vmm(virt_mshv::LinuxMshv, test).await,
115 #[cfg(target_os = "linux")]
116 HypervisorOpt::MshvVtl => {
117 state
118 .run_paravisor_vmm(virt::IsolationType::None, test)
119 .await
120 }
121 #[cfg(windows)]
122 HypervisorOpt::Whp => state.run_host_vmm(virt_whp::Whp, test).await,
123 #[cfg(target_os = "macos")]
124 HypervisorOpt::Hvf => state.run_host_vmm(virt_hvf::HvfHypervisor, test).await,
125 })
126 .await
127 }
128}
129
130fn choose_hypervisor() -> anyhow::Result<HypervisorOpt> {
131 #[cfg(all(target_os = "linux", guest_arch = "x86_64"))]
132 {
133 if virt::Hypervisor::is_available(&virt_mshv::LinuxMshv)? {
134 return Ok(HypervisorOpt::Mshv);
135 }
136 }
137 #[cfg(target_os = "linux")]
138 {
139 if virt::Hypervisor::is_available(&virt_kvm::Kvm)? {
140 return Ok(HypervisorOpt::Kvm);
141 }
142 }
143 #[cfg(windows)]
144 {
145 if virt::Hypervisor::is_available(&virt_whp::Whp)? {
146 return Ok(HypervisorOpt::Whp);
147 }
148 }
149 #[cfg(target_os = "macos")]
150 {
151 if virt::Hypervisor::is_available(&virt_hvf::HvfHypervisor)? {
152 return Ok(HypervisorOpt::Hvf);
153 }
154 }
155
156 anyhow::bail!("no hypervisor available");
157}