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