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}