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