tmk_vmm/
run.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Support for running a VM's VPs.
5
6use 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        // Load the TMK.
165        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(&regs),
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}