Skip to main content

ostd/vm/
guest_mode.rs

1// SPDX-License-Identifier: MPL-2.0
2
3use super::GuestPhysMemSpace;
4use crate::{
5    arch::vm::{GuestContext, GuestExitInfo, VcpuArchState, guest_mode::ArchGuestMode},
6    irq::DisabledLocalIrqGuard,
7    prelude::*,
8    task,
9};
10
11/// An execution mode for isolated guests.
12///
13/// [`Self::execute`] runs a guest vCPU until a VM exit or kernel event needs handling outside OSTD.
14///
15/// # Examples
16///
17/// ```no_run
18/// # fn handle_vm_exit(reason: ostd::vm::GuestReturnReason) {}
19/// #
20/// use ostd::{
21///     arch::vm::GuestContext,
22///     prelude::*,
23///     vm::{GuestMode, GuestModeHooks, GuestPhysMemSpace},
24/// };
25///
26/// fn run_guest(
27///     context: &mut GuestContext,
28///     guest_mem: &GuestPhysMemSpace,
29///     hooks: &impl GuestModeHooks,
30/// ) -> Result<()> {
31///     let guest_mode = GuestMode::new()?;
32///
33///     loop {
34///         let return_reason = guest_mode.execute(context, guest_mem, hooks)?;
35///         // Handle VM exit according to the exit reason recorded in `return_reason`.
36///         handle_vm_exit(return_reason);
37///     }
38/// }
39/// ```
40pub struct GuestMode {
41    arch: ArchGuestMode,
42}
43
44impl GuestMode {
45    /// Creates a guest execution object without entering a guest.
46    ///
47    /// # Panics
48    ///
49    /// Panics if called in [atomic mode](crate::task::atomic_mode).
50    pub fn new() -> Result<Self> {
51        Ok(Self {
52            arch: ArchGuestMode::new()?,
53        })
54    }
55
56    /// Runs the guest until a VM exit or kernel event needs handling by the kernel client.
57    ///
58    /// The `hooks` inspect and update guest state before and after each guest-entry attempt.
59    /// They also provide pending interrupt requests and timer deadlines through that state.
60    ///
61    /// # Panics
62    ///
63    /// Must be called in task context with local IRQs and preemption enabled.
64    #[track_caller]
65    pub fn execute<H: GuestModeHooks + ?Sized>(
66        &self,
67        context: &mut GuestContext,
68        guest_mem: &GuestPhysMemSpace,
69        hooks: &H,
70    ) -> Result<GuestReturnReason> {
71        task::atomic_mode::might_sleep();
72
73        loop {
74            task::scheduler::might_preempt();
75
76            if let Some(exit) = self.arch.execute_once(context, guest_mem, hooks)? {
77                return Ok(GuestReturnReason::VmExit(exit));
78            }
79
80            if hooks.has_kernel_event() {
81                return Ok(GuestReturnReason::KernelEvent);
82            }
83        }
84    }
85}
86
87/// Hooks called around each guest-entry attempt.
88///
89/// The pre-run and post-run hooks execute with local IRQs disabled and must not block.
90pub trait GuestModeHooks {
91    /// Prepares guest state and execution requests before they are loaded.
92    fn pre_guest_run(&self, state: &mut VcpuArchState, guard: &DisabledLocalIrqGuard);
93
94    /// Processes guest state after the host state has been restored.
95    fn post_guest_run(&self, state: &mut VcpuArchState, guard: &DisabledLocalIrqGuard);
96
97    /// Checks for a kernel event after an internally handled exit, with local IRQs enabled.
98    fn has_kernel_event(&self) -> bool;
99}
100
101/// A set of no-op guest mode hooks.
102pub struct DummyGuestHooks;
103
104impl GuestModeHooks for DummyGuestHooks {
105    fn pre_guest_run(&self, _state: &mut VcpuArchState, _guard: &DisabledLocalIrqGuard) {}
106
107    fn post_guest_run(&self, _state: &mut VcpuArchState, _guard: &DisabledLocalIrqGuard) {}
108
109    fn has_kernel_event(&self) -> bool {
110        false
111    }
112}
113
114/// The reason guest execution returned to the kernel client.
115#[derive(Debug)]
116pub enum GuestReturnReason {
117    /// An exit that requires higher-level handling.
118    VmExit(GuestExitInfo),
119    /// A pending kernel event reported by [`GuestModeHooks::has_kernel_event`].
120    KernelEvent,
121}