Alioth Code Coverage

hv.rs8.57%

1// Copyright 2024 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#[cfg(target_os = "macos")]
16#[path = "hvf/hvf.rs"]
17mod hvf;
18#[cfg(target_os = "linux")]
19#[path = "kvm/kvm.rs"]
20mod kvm;
21
22#[cfg(target_arch = "x86_64")]
23use std::arch::x86_64::CpuidResult;
24#[cfg(target_arch = "x86_64")]
25use std::collections::HashMap;
26use std::fmt::Debug;
27use std::os::fd::AsFd;
28use std::sync::Arc;
29use std::thread::JoinHandle;
30
31use serde::Deserialize;
32use serde_aco::Help;
33use snafu::{AsErrorSource, Snafu};
34
35#[cfg(target_arch = "x86_64")]
36use crate::arch::cpuid::CpuidIn;
37#[cfg(target_arch = "x86_64")]
38use crate::arch::reg::{DtReg, DtRegVal, SegReg, SegRegVal};
39use crate::arch::reg::{Reg, SReg};
40#[cfg(target_arch = "x86_64")]
41use crate::arch::sev::{SevPolicy, SevStatus, SnpPageType, SnpPolicy};
42#[cfg(target_arch = "x86_64")]
43use crate::arch::tdx::TdAttr;
44use crate::errors::{DebugTrace, trace_error};
45
46#[cfg(target_os = "macos")]
47pub use self::hvf::Hvf;
48#[cfg(target_os = "linux")]
49pub use self::kvm::{Kvm, KvmConfig, KvmError};
50
51#[trace_error]
52#[derive(Snafu, DebugTrace)]
53#[snafu(module, context(suffix(false)))]
54pub enum Error {
55 #[snafu(display("Failed to map hva {hva:#x} to gpa {gpa:#x}, size {size:#x}"))]
56 GuestMap {
57 hva: usize,
58 gpa: u64,
59 size: u64,
60 error: std::io::Error,
61 },
62 #[snafu(display("Failed to unmap gpa {gpa:#x}, size {size:#x}"))]
63 GuestUnmap {
64 gpa: u64,
65 size: u64,
66 error: std::io::Error,
67 },
68 #[snafu(display("Hypervisor is missing capability: {cap}"))]
69 Capability { cap: &'static str },
70 #[snafu(display("Failed to setup signal handlers"))]
71 SetupSignal { error: std::io::Error },
72 #[snafu(display("Failed to create a VM"))]
73 CreateVm { error: std::io::Error },
74 #[snafu(display("Failed to create a VCPU"))]
75 CreateVcpu { error: std::io::Error },
76 #[snafu(display("Failed to create a device"))]
77 CreateDevice { error: std::io::Error },
78 #[snafu(display("Failed to configure VM parameters"))]
79 SetVmParam { error: std::io::Error },
80 #[snafu(display("Failed to configure VCPU registers"))]
81 VcpuReg { error: std::io::Error },
82 #[snafu(display("Failed to configure the guest CPUID"))]
83 GuestCpuid { error: std::io::Error },
84 #[cfg(target_arch = "x86_64")]
85 #[snafu(display("Failed to configure guest MSRs"))]
86 GuestMsr { error: std::io::Error },
87 #[snafu(display("Failed to configure memory encryption"))]
88 MemEncrypt { error: std::io::Error },
89 #[snafu(display("Cannot create multiple VM memories"))]
90 MemoryCreated,
91 #[snafu(display("Failed to configure an IrqFd"))]
92 IrqFd { error: std::io::Error },
93 #[snafu(display("Failed to configure an IoeventFd"))]
94 IoeventFd { error: std::io::Error },
95 #[snafu(display("Failed to create an IrqSender for pin {pin}"))]
96 CreateIrq { pin: u8, error: std::io::Error },
97 #[snafu(display("Failed to send an interrupt"))]
98 SendInterrupt { error: std::io::Error },
99 #[snafu(display("Failed to run a VCPU"))]
100 RunVcpu { error: std::io::Error },
101 #[snafu(display("Failed to stop a VCPU"))]
102 StopVcpu { error: std::io::Error },
103 #[snafu(display("Failed to handle VM exit: {msg}"))]
104 VmExit { msg: String },
105 #[snafu(display("Broken channel"))]
106 BrokenChannel,
107 #[cfg(target_os = "linux")]
108 #[snafu(display("KVM internal error"), context(false))]
109 KvmErr { source: Box<KvmError> },
110 #[cfg(target_arch = "x86_64")]
111 #[snafu(display("SEV command error code {code:#x?}"))]
112 SevErr { code: SevStatus },
113 #[cfg(target_arch = "x86_64")]
114 #[snafu(display("TDX command error code {code:#x?}"))]
115 TdxErr { code: u64 },
116}
117
118impl From<std::sync::mpsc::RecvError> for Error {
119 fn from(error: std::sync::mpsc::RecvError) -> Self {
120 let source = error.as_error_source();
121 Error::BrokenChannel {
122 _location: snafu::GenerateImplicitData::generate_with_source(source),
123 }
124 }
125}
126
127impl<T: 'static> From<std::sync::mpsc::SendError<T>> for Error {
128 fn from(error: std::sync::mpsc::SendError<T>) -> Self {
129 let source = error.as_error_source();
130 Error::BrokenChannel {
131 _location: snafu::GenerateImplicitData::generate_with_source(source),
132 }
133 }
134}
135
136pub type Result<T, E = Error> = std::result::Result<T, E>;
137
138#[derive(Debug, Deserialize, Clone, Help)]
139#[cfg_attr(target_os = "macos", derive(Default))]
140pub enum HvConfig {
141 /// KVM backed by the Linux kernel.
142 #[cfg(target_os = "linux")]
143 #[serde(alias = "kvm")]
144 Kvm(KvmConfig),
145 /// macOS Hypervisor Framework.
146 #[cfg(target_os = "macos")]
147 #[default]
148 #[serde(alias = "hvf")]
149 Hvf,
150}
151
152#[cfg(target_os = "linux")]
153impl Default for HvConfig {
154 fn default() -> Self {
155 HvConfig::Kvm(KvmConfig::default())
156 }
157}
158
159#[derive(Debug, Clone, Copy, PartialEq, Eq)]
160pub struct MemMapOption {
161 pub read: bool,
162 pub write: bool,
163 pub exec: bool,
164 pub log_dirty: bool,
165}
166
167impl Default for MemMapOption {
168 fn default() -> Self {
169 Self {
170 read: true,
171 write: true,
172 exec: true,
173 log_dirty: false,
174 }
175 }
176}
177
178pub trait Vcpu {
179 #[cfg(target_arch = "aarch64")]
180 fn reset(&mut self, is_bsp: bool) -> Result<()>;
181
182 fn get_reg(&self, reg: Reg) -> Result<u64, Error>;
183 fn set_regs(&mut self, vals: &[(Reg, u64)]) -> Result<(), Error>;
184
185 #[cfg(target_arch = "x86_64")]
186 fn get_seg_reg(&self, reg: SegReg) -> Result<SegRegVal, Error>;
187
188 #[cfg(target_arch = "x86_64")]
189 fn get_dt_reg(&self, reg: DtReg) -> Result<DtRegVal, Error>;
190
191 fn get_sreg(&self, reg: SReg) -> Result<u64, Error>;
192
193 #[cfg(target_arch = "x86_64")]
194 fn set_sregs(
195 &mut self,
196 sregs: &[(SReg, u64)],
197 seg_regs: &[(SegReg, SegRegVal)],
198 dt_regs: &[(DtReg, DtRegVal)],
199 ) -> Result<(), Error>;
200
201 #[cfg(target_arch = "aarch64")]
202 fn set_sregs(&mut self, sregs: &[(SReg, u64)]) -> Result<(), Error>;
203
204 fn run(&mut self, entry: VmEntry) -> Result<VmExit, Error>;
205
206 #[cfg(target_arch = "x86_64")]
207 fn set_cpuids(&mut self, cpuids: HashMap<CpuidIn, CpuidResult>) -> Result<(), Error>;
208
209 #[cfg(target_arch = "x86_64")]
210 fn set_msrs(&mut self, msrs: &[(u32, u64)]) -> Result<()>;
211
212 fn dump(&self) -> Result<(), Error>;
213
214 #[cfg(target_arch = "aarch64")]
215 fn advance_pc(&mut self) -> Result<()> {
216 let pc = self.get_reg(Reg::Pc)?;
217 self.set_regs(&[(Reg::Pc, pc + 4)])
218 }
219
220 #[cfg(target_arch = "x86_64")]
221 fn tdx_init_vcpu(&self, hob: u64) -> Result<()>;
222
223 #[cfg(target_arch = "x86_64")]
224 fn tdx_init_mem_region(&self, data: &[u8], gpa: u64, measure: bool) -> Result<()>;
225}
226
227pub trait IrqSender: Debug + Send + Sync + 'static {
228 fn send(&self) -> Result<(), Error>;
229}
230
231impl<T> IrqSender for Arc<T>
232where
233 T: IrqSender,
234{
235 fn send(&self) -> Result<(), Error> {3x
236 IrqSender::send(self.as_ref())3x
237 }3x
238}
239
240pub trait MsiSender: Debug + Send + Sync + 'static {
241 type IrqFd: IrqFd;
242 fn send(&self, addr: u64, data: u32) -> Result<()>;
243 fn create_irqfd(&self) -> Result<Self::IrqFd>;
244}
245
246pub trait VmMemory: Debug + Send + Sync + 'static {
247 fn mem_map(&self, gpa: u64, size: u64, hva: usize, option: MemMapOption) -> Result<(), Error>;
248
249 fn unmap(&self, gpa: u64, size: u64) -> Result<(), Error>;
250
251 fn reset(&self) -> Result<()>;
252
253 fn register_encrypted_range(&self, _range: &[u8]) -> Result<()> {
254 unimplemented!()
255 }
256 fn deregister_encrypted_range(&self, _range: &[u8]) -> Result<()> {
257 unimplemented!()
258 }
259
260 fn mark_private_memory(&self, gpa: u64, size: u64, private: bool) -> Result<()>;
261}
262
263pub trait IoeventFd: Debug + Send + Sync + AsFd + 'static {}
264
265pub trait IoeventFdRegistry: Debug + Send + Sync + 'static {
266 type IoeventFd: IoeventFd;
267 fn create(&self) -> Result<Self::IoeventFd>;
268 fn register(&self, fd: &Self::IoeventFd, gpa: u64, len: u8, data: Option<u64>) -> Result<()>;
269 fn deregister(&self, fd: &Self::IoeventFd) -> Result<()>;
270}
271
272pub trait IrqFd: Debug + Send + Sync + AsFd + 'static {
273 fn set_addr_lo(&self, val: u32) -> Result<()>;
274 fn get_addr_lo(&self) -> u32;
275 fn set_addr_hi(&self, val: u32) -> Result<()>;
276 fn get_addr_hi(&self) -> u32;
277 fn set_data(&self, val: u32) -> Result<()>;
278 fn get_data(&self) -> u32;
279 fn set_masked(&self, val: bool) -> Result<bool>;
280 fn get_masked(&self) -> bool;
281}
282
283#[cfg(target_arch = "aarch64")]
284pub trait GicV2: Debug + Send + Sync + 'static {
285 fn init(&self) -> Result<()>;
286 fn get_dist_reg(&self, cpu_index: u32, offset: u16) -> Result<u32>;
287 fn set_dist_reg(&self, cpu_index: u32, offset: u16, val: u32) -> Result<()>;
288 fn get_cpu_reg(&self, cpu_index: u32, offset: u16) -> Result<u32>;
289 fn set_cpu_reg(&self, cpu_index: u32, offset: u16, val: u32) -> Result<()>;
290 fn get_num_irqs(&self) -> Result<u32>;
291 fn set_num_irqs(&self, val: u32) -> Result<()>;
292}
293
294#[cfg(target_arch = "aarch64")]
295pub trait GicV2m: Debug + Send + Sync + 'static {
296 fn init(&self) -> Result<()>;
297}
298
299#[cfg(target_arch = "aarch64")]
300pub trait GicV3: Debug + Send + Sync + 'static {
301 fn init(&self) -> Result<()>;
302}
303
304#[cfg(target_arch = "aarch64")]
305pub trait Its: Debug + Send + Sync + 'static {
306 fn init(&self) -> Result<()>;
307}
308
309#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Help)]
310pub enum Coco {
311 /// Enable AMD SEV or SEV-ES.
312 #[cfg(target_arch = "x86_64")]
313 #[serde(alias = "sev")]
314 AmdSev {
315 /// SEV policy, 0x1 for SEV, 0x5 for SEV-ES.
316 /// SEV API Ver 0.24, Rev 3.24, Ch.2, Table 2.
317 policy: SevPolicy,
318 },
319 /// Enable AMD SEV-SNP.
320 #[cfg(target_arch = "x86_64")]
321 #[serde(alias = "snp", alias = "sev-snp")]
322 AmdSnp {
323 /// SEV-SNP policy, e.g. 0x30000.
324 /// SNP Firmware ABI Spec, Rev 1.55, Sec.4.3, Table 9.
325 policy: SnpPolicy,
326 },
327 /// Enable Intel TDX.
328 #[cfg(target_arch = "x86_64")]
329 #[serde(alias = "tdx")]
330 IntelTdx {
331 /// TD attribute,
332 /// Intel TDX Module ABI Spec, Sec.3.4.1, Table 3.22.
333 attr: TdAttr,
334 },
335}
336
337#[derive(Debug)]
338pub struct VmConfig {
339 pub coco: Option<Coco>,
340}
341
342pub trait Vm {
343 type Vcpu: Vcpu;
344 type Memory: VmMemory;
345 type IrqSender: IrqSender + Send + Sync;
346 type MsiSender: MsiSender;
347 type IoeventFdRegistry: IoeventFdRegistry;
348 fn create_vcpu(&self, index: u16, identity: u64) -> Result<Self::Vcpu, Error>;
349 fn create_irq_sender(&self, pin: u8) -> Result<Self::IrqSender, Error>;
350 fn create_msi_sender(
351 &self,
352 #[cfg(target_arch = "aarch64")] devid: u32,
353 ) -> Result<Self::MsiSender>;
354 fn create_vm_memory(&mut self) -> Result<Self::Memory, Error>;
355 fn create_ioeventfd_registry(&self) -> Result<Self::IoeventFdRegistry>;
356 fn stop_vcpu<T>(&self, identity: u64, handle: &JoinHandle<T>) -> Result<(), Error>;
357
358 #[cfg(target_arch = "x86_64")]
359 fn sev_launch_start(&self, policy: SevPolicy) -> Result<()>;
360
361 #[cfg(target_arch = "x86_64")]
362 fn sev_launch_update_vmsa(&self) -> Result<()>;
363
364 #[cfg(target_arch = "x86_64")]
365 fn sev_launch_update_data(&self, range: &mut [u8]) -> Result<()>;
366
367 #[cfg(target_arch = "x86_64")]
368 fn sev_launch_measure(&self) -> Result<Vec<u8>>;
369
370 #[cfg(target_arch = "x86_64")]
371 fn sev_launch_finish(&self) -> Result<()>;
372
373 #[cfg(target_arch = "x86_64")]
374 fn snp_launch_start(&self, policy: SnpPolicy) -> Result<()>;
375
376 #[cfg(target_arch = "x86_64")]
377 fn snp_launch_update(&self, range: &mut [u8], gpa: u64, type_: SnpPageType) -> Result<()>;
378
379 #[cfg(target_arch = "x86_64")]
380 fn snp_launch_finish(&self) -> Result<()>;
381
382 #[cfg(target_arch = "x86_64")]
383 fn tdx_init_vm(&self, attr: TdAttr, cpuids: &HashMap<CpuidIn, CpuidResult>) -> Result<()>;
384
385 #[cfg(target_arch = "x86_64")]
386 fn tdx_finalize_vm(&self) -> Result<()>;
387
388 #[cfg(target_arch = "aarch64")]
389 type GicV2: GicV2;
390 #[cfg(target_arch = "aarch64")]
391 fn create_gic_v2(&self, distributor_base: u64, cpu_interface_base: u64) -> Result<Self::GicV2>;
392
393 #[cfg(target_arch = "aarch64")]
394 type GicV3: GicV3;
395 #[cfg(target_arch = "aarch64")]
396 fn create_gic_v3(
397 &self,
398 distributor_base: u64,
399 redistributor_base: u64,
400 redistributor_count: u16,
401 ) -> Result<Self::GicV3>;
402
403 #[cfg(target_arch = "aarch64")]
404 type GicV2m: GicV2m;
405 #[cfg(target_arch = "aarch64")]
406 fn create_gic_v2m(&self, base: u64) -> Result<Self::GicV2m>;
407
408 #[cfg(target_arch = "aarch64")]
409 type Its: Its;
410 #[cfg(target_arch = "aarch64")]
411 fn create_its(&self, base: u64) -> Result<Self::Its>;
412}
413
414pub trait Hypervisor {
415 type Vm: Vm + Sync + Send + 'static;
416
417 fn create_vm(&self, config: &VmConfig) -> Result<Self::Vm, Error>;
418
419 #[cfg(target_arch = "x86_64")]
420 fn get_supported_cpuids(&self, coco: Option<&Coco>) -> Result<HashMap<CpuidIn, CpuidResult>>;
421}
422
423#[derive(Debug, Clone, PartialEq, Eq)]
424pub enum VmExit {
425 #[cfg(target_arch = "x86_64")]
426 Io {
427 port: u16,
428 write: Option<u32>,
429 size: u8,
430 },
431 Mmio {
432 addr: u64,
433 write: Option<u64>,
434 size: u8,
435 },
436 ConvertMemory {
437 gpa: u64,
438 size: u64,
439 private: bool,
440 },
441 Shutdown,
442 Reboot,
443 Paused,
444 Interrupted,
445}
446
447#[derive(Debug, Clone, PartialEq, Eq)]
448pub enum VmEntry {
449 None,
450 Pause,
451 Shutdown,
452 Reboot,
453 #[cfg(target_arch = "x86_64")]
454 Io {
455 data: Option<u32>,
456 },
457 Mmio {
458 data: u64,
459 },
460}
461
462#[cfg(test)]
463#[path = "hv_test.rs"]
464pub(crate) mod tests;
465