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::Aarch64PlatformConfig {
65                gic_distributor_base: 0xff000000,
66                gic_redistributors_base: 0xff020000,
67                gic_v2m: None,
68                pmu_gsiv: None,
69                virt_timer_ppi: 20, // DEFAULT_VIRT_TIMER_PPI
70            })
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        // Load the TMK.
169        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(&regs),
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}