Alioth Code Coverage

hv.rs0.00%

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