nub_host_kvm/hypervisor/virtual_machine/
mod.rs1use std::fmt::Debug;
18use std::sync::OnceLock;
19
20use tracing::{Span, instrument};
21
22use crate::hypervisor::regs::{CommonFpu, CommonRegisters, CommonSpecialRegisters};
23use crate::mem::memory_region::MemoryRegion;
24
25#[cfg(kvm)]
27pub(crate) mod kvm;
28
29static AVAILABLE_HYPERVISOR: OnceLock<Option<HypervisorType>> = OnceLock::new();
30
31pub fn get_available_hypervisor() -> &'static Option<HypervisorType> {
33 AVAILABLE_HYPERVISOR.get_or_init(|| {
34 #[cfg(kvm)]
35 {
36 if kvm::is_hypervisor_present() {
37 Some(HypervisorType::Kvm)
38 } else {
39 None
40 }
41 }
42 #[cfg(not(kvm))]
43 {
44 None
45 }
46 })
47}
48
49#[instrument(skip_all, parent = Span::current())]
52pub fn is_hypervisor_present() -> bool {
53 get_available_hypervisor().is_some()
54}
55
56#[derive(PartialEq, Eq, Debug, Copy, Clone)]
58pub(crate) enum HypervisorType {
59 #[cfg(kvm)]
60 Kvm,
61}
62
63#[cfg(not(kvm))]
65compile_error!(
66 "No hypervisor type is available for the current platform. Please enable the `kvm` cargo feature."
67);
68
69pub(crate) enum VmExit {
71 Halt(),
73 IoOut(u16, Vec<u8>),
75 MmioRead(u64),
77 MmioWrite(u64),
79 Cancelled(),
81 Unknown(String),
83 Retry(),
85}
86
87#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
89pub(crate) struct VcpuLane(usize);
90
91impl VcpuLane {
92 pub(crate) const PRIMARY: Self = Self(0);
94
95 pub(crate) fn new(index: usize) -> Self {
96 Self(index)
97 }
98
99 pub(crate) fn index(self) -> usize {
100 self.0
101 }
102}
103
104#[derive(Debug, Clone, thiserror::Error)]
106pub enum VmError {
107 #[error("Failed to create vm: {0}")]
108 CreateVm(#[from] CreateVmError),
109 #[error("Map memory operation failed: {0}")]
110 MapMemory(#[from] MapMemoryError),
111 #[error("Register operation failed: {0}")]
112 Register(#[from] RegisterError),
113 #[error("Failed to run vcpu: {0}")]
114 RunVcpu(#[from] RunVcpuError),
115 #[error("Unmap memory operation failed: {0}")]
116 UnmapMemory(#[from] UnmapMemoryError),
117}
118
119#[derive(Debug, Clone, thiserror::Error)]
121pub enum CreateVmError {
122 #[error("VCPU creation failed: {0}")]
123 CreateVcpuFd(HypervisorError),
124 #[error("VM creation failed: {0}")]
125 CreateVmFd(HypervisorError),
126 #[error("Hypervisor is not available: {0}")]
127 HypervisorNotAvailable(HypervisorError),
128 #[error("Initialize VM failed: {0}")]
129 InitializeVm(HypervisorError),
130 #[error("Set Partition Property failed: {0}")]
131 SetPartitionProperty(HypervisorError),
132}
133
134#[derive(Debug, Clone, thiserror::Error)]
136pub enum RunVcpuError {
137 #[error("Invalid vCPU lane: {0}")]
138 InvalidVcpuLane(usize),
139 #[error("vCPU lane lock poisoned: {0}")]
140 VcpuLanePoisoned(usize),
141 #[error("Failed to decode message type: {0}")]
142 DecodeIOMessage(u32),
143 #[error("Increment RIP failed: {0}")]
144 IncrementRip(HypervisorError),
145 #[error("Parse GPA access info failed")]
146 ParseGpaAccessInfo,
147 #[error("Unknown error: {0}")]
148 Unknown(HypervisorError),
149}
150
151#[derive(Debug, Clone, thiserror::Error)]
153pub enum RegisterError {
154 #[error("Invalid vCPU lane: {0}")]
155 InvalidVcpuLane(usize),
156 #[error("vCPU lane lock poisoned: {0}")]
157 VcpuLanePoisoned(usize),
158 #[error("Failed to get registers: {0}")]
159 GetRegs(HypervisorError),
160 #[error("Failed to set registers: {0}")]
161 SetRegs(HypervisorError),
162 #[error("Failed to set FPU registers: {0}")]
163 SetFpu(HypervisorError),
164 #[error("Failed to set special registers: {0}")]
165 SetSregs(HypervisorError),
166}
167
168#[derive(Debug, Clone, thiserror::Error)]
170pub enum MapMemoryError {
171 #[error("Hypervisor error: {0}")]
172 Hypervisor(HypervisorError),
173}
174
175#[derive(Debug, Clone, thiserror::Error)]
177pub enum UnmapMemoryError {
178 #[error("Hypervisor error: {0}")]
179 Hypervisor(HypervisorError),
180}
181
182#[derive(Debug, Clone, thiserror::Error)]
184pub enum HypervisorError {
185 #[cfg(kvm)]
186 #[error("KVM error: {0}")]
187 KvmError(#[from] kvm_ioctls::Error),
188}
189
190pub(crate) trait VirtualMachine: Debug + Send + Sync {
192 unsafe fn map_memory(
201 &mut self,
202 region: (u32, &MemoryRegion),
203 ) -> std::result::Result<(), MapMemoryError>;
204
205 fn vcpu_count(&self) -> usize;
207
208 fn run_vcpu_on(&self, lane: VcpuLane) -> std::result::Result<VmExit, RunVcpuError>;
212
213 fn run_vcpu(&self) -> std::result::Result<VmExit, RunVcpuError> {
215 self.run_vcpu_on(VcpuLane::PRIMARY)
216 }
217
218 fn regs_on(&self, lane: VcpuLane) -> std::result::Result<CommonRegisters, RegisterError>;
220
221 fn regs(&self) -> std::result::Result<CommonRegisters, RegisterError> {
223 self.regs_on(VcpuLane::PRIMARY)
224 }
225
226 fn set_regs_on(
228 &self,
229 lane: VcpuLane,
230 regs: &CommonRegisters,
231 ) -> std::result::Result<(), RegisterError>;
232
233 fn set_regs(&self, regs: &CommonRegisters) -> std::result::Result<(), RegisterError> {
235 self.set_regs_on(VcpuLane::PRIMARY, regs)
236 }
237
238 fn set_fpu_on(&self, lane: VcpuLane, fpu: &CommonFpu)
240 -> std::result::Result<(), RegisterError>;
241
242 fn set_fpu(&self, fpu: &CommonFpu) -> std::result::Result<(), RegisterError> {
244 self.set_fpu_on(VcpuLane::PRIMARY, fpu)
245 }
246
247 fn set_sregs_on(
249 &self,
250 lane: VcpuLane,
251 sregs: &CommonSpecialRegisters,
252 ) -> std::result::Result<(), RegisterError>;
253
254 fn set_sregs(&self, sregs: &CommonSpecialRegisters) -> std::result::Result<(), RegisterError> {
256 self.set_sregs_on(VcpuLane::PRIMARY, sregs)
257 }
258
259 fn sregs_on(
261 &self,
262 lane: VcpuLane,
263 ) -> std::result::Result<CommonSpecialRegisters, RegisterError>;
264
265 fn sregs(&self) -> std::result::Result<CommonSpecialRegisters, RegisterError> {
267 self.sregs_on(VcpuLane::PRIMARY)
268 }
269}
270
271#[cfg(test)]
272mod tests {
273 #[test]
274 #[cfg(kvm)]
275 fn is_hypervisor_present() {
276 use std::path::Path;
277 assert_eq!(
278 Path::new("/dev/kvm").exists(),
279 super::is_hypervisor_present()
280 );
281 }
282}