Skip to main content

ostd/arch/x86/vm/
context.rs

1// SPDX-License-Identifier: MPL-2.0
2
3use x86::msr::{IA32_FMASK, IA32_LSTAR, IA32_STAR, wrmsr};
4use x86_64::{VirtAddr, registers::control::Cr2};
5
6use super::{
7    GuestInterrupt, GuestTimerInstant, VcpuMsrs, VcpuRegs, VcpuSregs,
8    guest_mode::SyscallMsrsRestorer,
9    vmx::{VmxGuard, vmcs::Vmcs},
10    x86::write_cr2_raw,
11};
12use crate::{Error, arch::cpu::context::GsBase, irq::DisabledLocalIrqGuard, prelude::*};
13
14/// The guest-visible architectural state of an x86 vCPU.
15///
16/// Register values can be modified without affecting the host CPU. The caller
17/// is responsible for initializing them for the guest's execution mode.
18///
19/// This context must be dropped outside [atomic mode](crate::task::atomic_mode).
20pub struct GuestContext {
21    arch: VcpuArchState,
22    vmcs: Vmcs,
23}
24
25impl GuestContext {
26    /// Creates a guest vCPU context from initialized register state.
27    ///
28    /// # Panics
29    ///
30    /// Panics if called in [atomic mode](crate::task::atomic_mode).
31    pub fn new(regs: VcpuRegs, sregs: VcpuSregs, msrs: VcpuMsrs) -> Result<Self> {
32        let vmx_guard = VmxGuard::acquire_vmx()?;
33        Ok(Self {
34            arch: VcpuArchState {
35                regs,
36                sregs,
37                msrs,
38                pending_interrupt: None,
39                timer_deadline: None,
40            },
41            vmcs: Vmcs::new(&vmx_guard)?,
42        })
43    }
44
45    /// Returns the guest's general-purpose registers, instruction pointer, and flags.
46    pub fn regs(&self) -> &VcpuRegs {
47        &self.arch.regs
48    }
49
50    /// Returns a mutable reference to the guest's general-purpose registers,
51    /// instruction pointer, and flags.
52    pub fn regs_mut(&mut self) -> &mut VcpuRegs {
53        &mut self.arch.regs
54    }
55
56    /// Returns the guest's special registers.
57    pub fn sregs(&self) -> &VcpuSregs {
58        &self.arch.sregs
59    }
60
61    /// Returns a mutable reference to the guest's special registers.
62    pub fn sregs_mut(&mut self) -> &mut VcpuSregs {
63        &mut self.arch.sregs
64    }
65
66    /// Returns the guest's MSRs that are not part of [`VcpuSregs`].
67    pub fn msrs(&self) -> &VcpuMsrs {
68        &self.arch.msrs
69    }
70
71    /// Returns a mutable reference to the guest's MSRs that are not part of [`VcpuSregs`].
72    pub fn msrs_mut(&mut self) -> &mut VcpuMsrs {
73        &mut self.arch.msrs
74    }
75
76    /// Returns the mutable architectural state and the associated VMCS.
77    pub(super) fn arch_and_vmcs(&mut self) -> (&mut VcpuArchState, &Vmcs) {
78        (&mut self.arch, &self.vmcs)
79    }
80}
81
82impl Drop for GuestContext {
83    fn drop(&mut self) {
84        if let Err(err) =
85            VmxGuard::acquire_vmx().and_then(|vmx_guard| self.vmcs.deactivate(&vmx_guard))
86        {
87            // The active set retains the VMCS until a later clear succeeds.
88            error!("failed to clear VMCS during context destruction: {:?}", err);
89        }
90    }
91}
92
93/// The register state and execution requests of an x86 vCPU.
94pub struct VcpuArchState {
95    /// The general-purpose registers, instruction pointer, and flags.
96    pub regs: VcpuRegs,
97    /// The special registers and segment state.
98    pub sregs: VcpuSregs,
99    /// The model-specific registers.
100    pub msrs: VcpuMsrs,
101    /// An external interrupt awaiting injection by OSTD.
102    pub pending_interrupt: Option<GuestInterrupt>,
103    /// The guest TSC deadline for returning from guest execution.
104    pub timer_deadline: Option<GuestTimerInstant>,
105}
106
107impl VcpuArchState {
108    /// Loads guest state not handled by VMX.
109    ///
110    /// # Safety
111    ///
112    /// The caller must ensure that the returned restorer is not forgotten.
113    pub(super) unsafe fn load_run_state<'a>(
114        &self,
115        irq_guard: &'a DisabledLocalIrqGuard,
116    ) -> Result<SyscallMsrsRestorer<'a>> {
117        // These values are loaded in host mode, outside VM-entry's guest-state
118        // checks. Reject values that would cause a host #GP.
119        for value in [self.msrs.kernel_gs_base, self.msrs.lstar] {
120            if VirtAddr::try_new(value).is_err() {
121                return Err(Error::InvalidArgs);
122            }
123        }
124        if self.msrs.syscall_mask > u32::MAX as u64 {
125            return Err(Error::InvalidArgs);
126        }
127
128        let syscall_msrs_restorer = SyscallMsrsRestorer::new(irq_guard);
129        // SAFETY: These MSRs are architectural on x86-64. The checks above
130        // ensure canonical addresses and no reserved `IA32_FMASK` bits;
131        // `IA32_STAR` has no reserved bits. The caller restores the host state.
132        unsafe {
133            wrmsr(IA32_STAR, self.msrs.star);
134            wrmsr(IA32_LSTAR, self.msrs.lstar);
135            wrmsr(IA32_FMASK, self.msrs.syscall_mask);
136        }
137
138        write_cr2_raw(self.sregs.cr2);
139        GsBase::new(self.msrs.kernel_gs_base as usize).load(irq_guard);
140        Ok(syscall_msrs_restorer)
141    }
142
143    /// Saves guest state not handled by VMX.
144    pub(super) fn save_run_state(&mut self, irq_guard: &DisabledLocalIrqGuard) {
145        self.sregs.cr2 = Cr2::read_raw();
146        // `SWAPGS` can change `IA32_KERNEL_GSBASE` without an MSR-access exit.
147        self.msrs.kernel_gs_base = {
148            let mut kernel_gs_base = GsBase::default();
149            kernel_gs_base.save(irq_guard);
150            kernel_gs_base.addr() as u64
151        };
152    }
153}