hv.rs8.57%
1
// Copyright 2024 Google LLC2
//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 at6
//7
// https://www.apache.org/licenses/LICENSE-2.08
//9
// Unless required by applicable law or agreed to in writing, software10
// 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 and13
// limitations under the License.14
15
#[cfg(target_os = "macos")]16
#[path = "hvf/hvf.rs"]17
mod hvf;18
#[cfg(target_os = "linux")]19
#[path = "kvm/kvm.rs"]20
mod kvm;21
22
#[cfg(target_arch = "x86_64")]23
use std::arch::x86_64::CpuidResult;24
#[cfg(target_arch = "x86_64")]25
use std::collections::HashMap;26
use std::fmt::Debug;27
use std::os::fd::AsFd;28
use std::sync::Arc;29
use std::thread::JoinHandle;30
31
use serde::Deserialize;32
use serde_aco::Help;33
use snafu::{AsErrorSource, Snafu};34
35
#[cfg(target_arch = "x86_64")]36
use crate::arch::cpuid::CpuidIn;37
#[cfg(target_arch = "x86_64")]38
use crate::arch::reg::{DtReg, DtRegVal, SegReg, SegRegVal};39
use crate::arch::reg::{Reg, SReg};40
#[cfg(target_arch = "x86_64")]41
use crate::arch::sev::{SevPolicy, SevStatus, SnpPageType, SnpPolicy};42
#[cfg(target_arch = "x86_64")]43
use crate::arch::tdx::TdAttr;44
use crate::errors::{DebugTrace, trace_error};45
46
#[cfg(target_os = "macos")]47
pub use self::hvf::Hvf;48
#[cfg(target_os = "linux")]49
pub use self::kvm::{Kvm, KvmConfig, KvmError};50
51
#[trace_error]52
#[derive(Snafu, DebugTrace)]53
#[snafu(module, context(suffix(false)))]54
pub 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
118
impl 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
127
impl<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
136
pub 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))]140
pub 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")]153
impl Default for HvConfig {154
fn default() -> Self {155
HvConfig::Kvm(KvmConfig::default())156
}157
}158
159
#[derive(Debug, Clone, Copy, PartialEq, Eq)]160
pub struct MemMapOption {161
pub read: bool,162
pub write: bool,163
pub exec: bool,164
pub log_dirty: bool,165
}166
167
impl 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
178
pub 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
227
pub trait IrqSender: Debug + Send + Sync + 'static {228
fn send(&self) -> Result<(), Error>;229
}230
231
impl<T> IrqSender for Arc<T>232
where233
T: IrqSender,234
{235
fn send(&self) -> Result<(), Error> {3x236
IrqSender::send(self.as_ref())3x237
}3x238
}239
240
pub 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
246
pub 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
263
pub trait IoeventFd: Debug + Send + Sync + AsFd + 'static {}264
265
pub 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
272
pub 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")]284
pub 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")]295
pub trait GicV2m: Debug + Send + Sync + 'static {296
fn init(&self) -> Result<()>;297
}298
299
#[cfg(target_arch = "aarch64")]300
pub trait GicV3: Debug + Send + Sync + 'static {301
fn init(&self) -> Result<()>;302
}303
304
#[cfg(target_arch = "aarch64")]305
pub trait Its: Debug + Send + Sync + 'static {306
fn init(&self) -> Result<()>;307
}308
309
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Help)]310
pub 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)]338
pub struct VmConfig {339
pub coco: Option<Coco>,340
}341
342
pub 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
414
pub 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)]424
pub 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)]448
pub 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"]464
pub(crate) mod tests;465