1use crate::Options;
7use crate::load;
8use anyhow::Context as _;
9use futures::StreamExt as _;
10use guestmem::GuestMemory;
11use hvdef::HvError;
12use hvdef::Vtl;
13use pal_async::DefaultDriver;
14use std::sync::Arc;
15use virt::PartitionCapabilities;
16use virt::Processor;
17use virt::StopVpSource;
18use virt::VpIndex;
19use virt::io::CpuIo;
20use virt::vp::AccessVpState as _;
21use vm_topology::memory::MemoryLayout;
22use vm_topology::processor::ProcessorTopology;
23use vm_topology::processor::TopologyBuilder;
24use vmcore::vmtime::VmTime;
25use vmcore::vmtime::VmTimeKeeper;
26use vmcore::vmtime::VmTimeSource;
27use zerocopy::TryFromBytes as _;
28
29pub const COMMAND_ADDRESS: u64 = 0xffff_0000;
30
31pub struct CommonState {
32 pub driver: DefaultDriver,
33 pub opts: Options,
34 pub processor_topology: ProcessorTopology,
35 pub memory_layout: MemoryLayout,
36}
37
38pub struct RunContext<'a> {
39 pub state: &'a CommonState,
40 pub vmtime_source: &'a VmTimeSource,
41}
42
43#[derive(Debug, Clone)]
44pub enum TestResult {
45 Passed,
46 Failed,
47 Faulted {
48 vp_index: VpIndex,
49 reason: String,
50 regs: Option<Box<virt::vp::Registers>>,
51 },
52}
53
54impl CommonState {
55 pub async fn new(driver: DefaultDriver, opts: Options) -> anyhow::Result<Self> {
56 #[cfg(guest_arch = "x86_64")]
57 let processor_topology = TopologyBuilder::new_x86()
58 .x2apic(vm_topology::processor::x86::X2ApicState::Supported)
59 .build(1)
60 .context("failed to build processor topology")?;
61
62 #[cfg(guest_arch = "aarch64")]
63 let processor_topology =
64 TopologyBuilder::new_aarch64(vm_topology::processor::arch::Aarch64PlatformConfig {
65 gic_distributor_base: 0xff000000,
66 gic_redistributors_base: 0xff020000,
67 gic_v2m: None,
68 pmu_gsiv: None,
69 virt_timer_ppi: 20, })
71 .build(1)
72 .context("failed to build processor topology")?;
73
74 let ram_size = 0x400000;
75 let memory_layout =
76 MemoryLayout::new(ram_size, &[], &[], &[], None).context("bad memory layout")?;
77
78 Ok(Self {
79 driver,
80 opts,
81 processor_topology,
82 memory_layout,
83 })
84 }
85
86 pub async fn for_each_test(
87 &mut self,
88 mut f: impl AsyncFnMut(&mut RunContext<'_>, &load::TestInfo) -> anyhow::Result<TestResult>,
89 ) -> anyhow::Result<()> {
90 let tmk = fs_err::File::open(&self.opts.tmk).context("failed to open tmk")?;
91 let available_tests = load::enumerate_tests(&tmk)?;
92 let tests = if self.opts.tests.is_empty() {
93 available_tests
94 } else {
95 self.opts
96 .tests
97 .iter()
98 .map(|name| {
99 available_tests
100 .iter()
101 .find(|test| test.name == *name)
102 .cloned()
103 .with_context(|| format!("test {} not found", name))
104 })
105 .collect::<anyhow::Result<Vec<_>>>()?
106 };
107 let mut success = true;
108 for test in &tests {
109 tracing::info!(target: "test", name = test.name, "test started");
110
111 let mut vmtime_keeper = VmTimeKeeper::new(&self.driver, VmTime::from_100ns(0));
112 let vmtime_source = vmtime_keeper.builder().build(&self.driver).await.unwrap();
113 let mut ctx = RunContext {
114 state: self,
115 vmtime_source: &vmtime_source,
116 };
117
118 vmtime_keeper.start().await;
119
120 let r = f(&mut ctx, test)
121 .await
122 .with_context(|| format!("failed to run test {}", test.name))?;
123
124 vmtime_keeper.stop().await;
125
126 match r {
127 TestResult::Passed => {
128 tracing::info!(target: "test", name = test.name, "test passed");
129 }
130 TestResult::Failed => {
131 tracing::error!(target: "test", name = test.name, reason = "explicit failure", "test failed");
132 success = false;
133 }
134 TestResult::Faulted {
135 vp_index,
136 reason,
137 regs,
138 } => {
139 tracing::error!(
140 target: "test",
141 name = test.name,
142 vp_index = vp_index.index(),
143 reason,
144 regs = format_args!("{:#x?}", regs),
145 "test failed"
146 );
147 success = false;
148 }
149 }
150 }
151 if !success {
152 anyhow::bail!("some tests failed");
153 }
154 Ok(())
155 }
156}
157
158impl RunContext<'_> {
159 pub async fn run(
160 &mut self,
161 guest_memory: &GuestMemory,
162 caps: &PartitionCapabilities,
163 test: &load::TestInfo,
164 start_vp: impl AsyncFnOnce(&mut Self, RunnerBuilder) -> anyhow::Result<()>,
165 ) -> anyhow::Result<TestResult> {
166 let (event_send, mut event_recv) = mesh::channel();
167
168 let tmk = fs_err::File::open(&self.state.opts.tmk).context("failed to open tmk")?;
170 let regs = {
171 #[cfg(guest_arch = "x86_64")]
172 {
173 load::load_x86(
174 &self.state.memory_layout,
175 guest_memory,
176 &self.state.processor_topology,
177 caps,
178 &tmk,
179 test,
180 )?
181 }
182 #[cfg(guest_arch = "aarch64")]
183 {
184 load::load_aarch64(
185 &self.state.memory_layout,
186 guest_memory,
187 &self.state.processor_topology,
188 caps,
189 &tmk,
190 test,
191 )?
192 }
193 };
194
195 start_vp(
196 self,
197 RunnerBuilder::new(
198 VpIndex::BSP,
199 Arc::clone(®s),
200 guest_memory.clone(),
201 event_send.clone(),
202 ),
203 )
204 .await?;
205
206 let event = event_recv.next().await.unwrap();
207 let r = match event {
208 VpEvent::TestComplete { success } => {
209 if success {
210 TestResult::Passed
211 } else {
212 TestResult::Failed
213 }
214 }
215 VpEvent::Halt {
216 vp_index,
217 reason,
218 regs,
219 } => TestResult::Faulted {
220 vp_index,
221 reason,
222 regs,
223 },
224 };
225
226 Ok(r)
227 }
228}
229
230enum VpEvent {
231 TestComplete {
232 success: bool,
233 },
234 Halt {
235 vp_index: VpIndex,
236 reason: String,
237 regs: Option<Box<virt::vp::Registers>>,
238 },
239}
240
241struct IoHandler<'a> {
242 guest_memory: &'a GuestMemory,
243 event_send: &'a mesh::Sender<VpEvent>,
244 stop: &'a StopVpSource,
245}
246
247fn widen(d: &[u8]) -> u64 {
248 let mut v = [0; 8];
249 v[..d.len()].copy_from_slice(d);
250 u64::from_ne_bytes(v)
251}
252
253impl CpuIo for IoHandler<'_> {
254 fn is_mmio(&self, _address: u64) -> bool {
255 false
256 }
257
258 fn acknowledge_pic_interrupt(&self) -> Option<u8> {
259 None
260 }
261
262 fn handle_eoi(&self, irq: u32) {
263 tracing::info!(irq, "eoi");
264 }
265
266 fn signal_synic_event(&self, vtl: Vtl, connection_id: u32, flag: u16) -> hvdef::HvResult<()> {
267 let _ = (vtl, connection_id, flag);
268 Err(HvError::InvalidConnectionId)
269 }
270
271 fn post_synic_message(
272 &self,
273 vtl: Vtl,
274 connection_id: u32,
275 secure: bool,
276 message: &[u8],
277 ) -> hvdef::HvResult<()> {
278 let _ = (vtl, connection_id, secure, message);
279 Err(HvError::InvalidConnectionId)
280 }
281
282 async fn read_mmio(&self, vp: VpIndex, address: u64, data: &mut [u8]) {
283 tracing::info!(vp = vp.index(), address, "read mmio");
284 data.fill(!0);
285 }
286
287 async fn write_mmio(&self, vp: VpIndex, address: u64, data: &[u8]) {
288 if address == COMMAND_ADDRESS {
289 let p = widen(data);
290 let r = self.handle_command(p);
291 if let Err(e) = r {
292 tracing::error!(
293 error = e.as_ref() as &dyn std::error::Error,
294 p,
295 "failed to handle command"
296 );
297 }
298 } else {
299 tracing::info!(vp = vp.index(), address, data = widen(data), "write mmio");
300 }
301 }
302
303 async fn read_io(&self, vp: VpIndex, port: u16, data: &mut [u8]) {
304 tracing::info!(vp = vp.index(), port, "read io");
305 data.fill(!0);
306 }
307
308 async fn write_io(&self, vp: VpIndex, port: u16, data: &[u8]) {
309 tracing::info!(vp = vp.index(), port, data = widen(data), "write io");
310 }
311
312 #[track_caller]
313 fn fatal_error(&self, error: Box<dyn std::error::Error + Send + Sync>) -> virt::VpHaltReason {
314 tracing::error!(
315 err = error.as_ref() as &dyn std::error::Error,
316 "fatal error"
317 );
318 virt::VpHaltReason::TripleFault { vtl: Vtl::Vtl0 }
319 }
320}
321
322impl IoHandler<'_> {
323 fn read_str(&self, s: tmk_protocol::StrDescriptor) -> anyhow::Result<String> {
324 let mut buf = vec![0; s.len as usize];
325 self.guest_memory
326 .read_at(s.gpa, &mut buf)
327 .context("failed to read string")?;
328 String::from_utf8(buf).context("string not utf-8")
329 }
330
331 fn handle_command(&self, gpa: u64) -> anyhow::Result<()> {
332 let buf = self
333 .guest_memory
334 .read_plain::<[u8; size_of::<tmk_protocol::Command>()]>(gpa)
335 .context("failed to read command")?;
336 let cmd = tmk_protocol::Command::try_read_from_bytes(&buf)
337 .ok()
338 .context("bad command")?;
339 match cmd {
340 tmk_protocol::Command::Log(s) => {
341 let message = self.read_str(s)?;
342 tracing::info!(target: "tmk", message);
343 }
344 tmk_protocol::Command::Panic {
345 message,
346 filename,
347 line,
348 } => {
349 let message = self.read_str(message)?;
350 let location = if filename.len > 0 {
351 Some(format!("{}:{}", self.read_str(filename)?, line))
352 } else {
353 None
354 };
355 tracing::error!(target: "tmk", location, panic = message);
356 self.event_send
357 .send(VpEvent::TestComplete { success: false });
358 self.stop.stop();
359 }
360 tmk_protocol::Command::Complete { success } => {
361 self.event_send.send(VpEvent::TestComplete { success });
362 self.stop.stop();
363 }
364 }
365 Ok(())
366 }
367}
368
369pub struct RunnerBuilder {
370 vp_index: VpIndex,
371 regs: Arc<virt::InitialRegs>,
372 guest_memory: GuestMemory,
373 event_send: mesh::Sender<VpEvent>,
374}
375
376impl RunnerBuilder {
377 fn new(
378 vp_index: VpIndex,
379 regs: Arc<virt::InitialRegs>,
380 guest_memory: GuestMemory,
381 event_send: mesh::Sender<VpEvent>,
382 ) -> Self {
383 Self {
384 vp_index,
385 regs,
386 guest_memory,
387 event_send,
388 }
389 }
390
391 pub fn build<P: Processor>(&mut self, mut vp: P) -> anyhow::Result<Runner<'_, P>> {
392 {
393 let mut state = vp.access_state(Vtl::Vtl0);
394 #[cfg(guest_arch = "x86_64")]
395 {
396 let virt::x86::X86InitialRegs {
397 registers,
398 mtrrs,
399 pat,
400 } = self.regs.as_ref();
401 state.set_registers(registers)?;
402 state.set_mtrrs(mtrrs)?;
403 state.set_pat(pat)?;
404 }
405 #[cfg(guest_arch = "aarch64")]
406 {
407 let virt::aarch64::Aarch64InitialRegs {
408 registers,
409 system_registers,
410 } = self.regs.as_ref();
411 state.set_registers(registers)?;
412 state.set_system_registers(system_registers)?;
413 }
414 state.commit()?;
415 }
416 Ok(Runner {
417 vp,
418 vp_index: self.vp_index,
419 guest_memory: &self.guest_memory,
420 event_send: &self.event_send,
421 })
422 }
423}
424
425pub struct Runner<'a, P> {
426 vp: P,
427 vp_index: VpIndex,
428 guest_memory: &'a GuestMemory,
429 event_send: &'a mesh::Sender<VpEvent>,
430}
431
432impl<P: Processor> Runner<'_, P> {
433 pub async fn run_vp(&mut self) {
434 let stop = StopVpSource::new();
435 let Err(err) = self
436 .vp
437 .run_vp(
438 stop.checker(),
439 &IoHandler {
440 guest_memory: self.guest_memory,
441 event_send: self.event_send,
442 stop: &stop,
443 },
444 )
445 .await;
446 let regs = self
447 .vp
448 .access_state(Vtl::Vtl0)
449 .registers()
450 .map(Box::new)
451 .ok();
452 self.event_send.send(VpEvent::Halt {
453 vp_index: self.vp_index,
454 reason: format!("{:?}", err),
455 regs,
456 });
457 }
458}