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 = 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        // Load the TMK.
167        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(&regs),
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    #[track_caller]
311    fn fatal_error(&self, error: Box<dyn std::error::Error + Send + Sync>) -> virt::VpHaltReason {
312        tracing::error!(
313            err = error.as_ref() as &dyn std::error::Error,
314            "fatal error"
315        );
316        virt::VpHaltReason::TripleFault { vtl: Vtl::Vtl0 }
317    }
318}
319
320impl IoHandler<'_> {
321    fn read_str(&self, s: tmk_protocol::StrDescriptor) -> anyhow::Result<String> {
322        let mut buf = vec![0; s.len as usize];
323        self.guest_memory
324            .read_at(s.gpa, &mut buf)
325            .context("failed to read string")?;
326        String::from_utf8(buf).context("string not utf-8")
327    }
328
329    fn handle_command(&self, gpa: u64) -> anyhow::Result<()> {
330        let buf = self
331            .guest_memory
332            .read_plain::<[u8; size_of::<tmk_protocol::Command>()]>(gpa)
333            .context("failed to read command")?;
334        let cmd = tmk_protocol::Command::try_read_from_bytes(&buf)
335            .ok()
336            .context("bad command")?;
337        match cmd {
338            tmk_protocol::Command::Log(s) => {
339                let message = self.read_str(s)?;
340                tracing::info!(target: "tmk", message);
341            }
342            tmk_protocol::Command::Panic {
343                message,
344                filename,
345                line,
346            } => {
347                let message = self.read_str(message)?;
348                let location = if filename.len > 0 {
349                    Some(format!("{}:{}", self.read_str(filename)?, line))
350                } else {
351                    None
352                };
353                tracing::error!(target: "tmk", location, panic = message);
354                self.event_send
355                    .send(VpEvent::TestComplete { success: false });
356                self.stop.stop();
357            }
358            tmk_protocol::Command::Complete { success } => {
359                self.event_send.send(VpEvent::TestComplete { success });
360                self.stop.stop();
361            }
362        }
363        Ok(())
364    }
365}
366
367pub struct RunnerBuilder {
368    vp_index: VpIndex,
369    regs: Arc<virt::InitialRegs>,
370    guest_memory: GuestMemory,
371    event_send: mesh::Sender<VpEvent>,
372}
373
374impl RunnerBuilder {
375    fn new(
376        vp_index: VpIndex,
377        regs: Arc<virt::InitialRegs>,
378        guest_memory: GuestMemory,
379        event_send: mesh::Sender<VpEvent>,
380    ) -> Self {
381        Self {
382            vp_index,
383            regs,
384            guest_memory,
385            event_send,
386        }
387    }
388
389    pub fn build<P: Processor>(&mut self, mut vp: P) -> anyhow::Result<Runner<'_, P>> {
390        {
391            let mut state = vp.access_state(Vtl::Vtl0);
392            #[cfg(guest_arch = "x86_64")]
393            {
394                let virt::x86::X86InitialRegs {
395                    registers,
396                    mtrrs,
397                    pat,
398                } = self.regs.as_ref();
399                state.set_registers(registers)?;
400                state.set_mtrrs(mtrrs)?;
401                state.set_pat(pat)?;
402            }
403            #[cfg(guest_arch = "aarch64")]
404            {
405                let virt::aarch64::Aarch64InitialRegs {
406                    registers,
407                    system_registers,
408                } = self.regs.as_ref();
409                state.set_registers(registers)?;
410                state.set_system_registers(system_registers)?;
411            }
412            state.commit()?;
413        }
414        Ok(Runner {
415            vp,
416            vp_index: self.vp_index,
417            guest_memory: &self.guest_memory,
418            event_send: &self.event_send,
419        })
420    }
421}
422
423pub struct Runner<'a, P> {
424    vp: P,
425    vp_index: VpIndex,
426    guest_memory: &'a GuestMemory,
427    event_send: &'a mesh::Sender<VpEvent>,
428}
429
430impl<P: Processor> Runner<'_, P> {
431    pub async fn run_vp(&mut self) {
432        let stop = StopVpSource::new();
433        let Err(err) = self
434            .vp
435            .run_vp(
436                stop.checker(),
437                &IoHandler {
438                    guest_memory: self.guest_memory,
439                    event_send: self.event_send,
440                    stop: &stop,
441                },
442            )
443            .await;
444        let regs = self
445            .vp
446            .access_state(Vtl::Vtl0)
447            .registers()
448            .map(Box::new)
449            .ok();
450        self.event_send.send(VpEvent::Halt {
451            vp_index: self.vp_index,
452            reason: format!("{:?}", err),
453            regs,
454        });
455    }
456}