hv.rs0.00%
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
#[cfg(not(target_arch = "x86_64"))]29
use std::sync::Arc;30
use std::thread::JoinHandle;31
32
use serde::Deserialize;33
use serde_aco::Help;34
use snafu::{AsErrorSource, Snafu};35
36
#[cfg(target_arch = "x86_64")]37
use crate::arch::cpuid::CpuidIn;38
#[cfg(target_arch = "x86_64")]39
use crate::arch::reg::{DtReg, DtRegVal, SegReg, SegRegVal};40
use crate::arch::reg::{Reg, SReg};41
#[cfg(target_arch = "x86_64")]42
use crate::arch::sev::{SevPolicy, SevStatus, SnpPageType, SnpPolicy};43
#[cfg(target_arch = "x86_64")]44
use crate::arch::tdx::TdAttr;45
use crate::errors::{DebugTrace, trace_error};46
47
#[cfg(target_os = "macos")]48
pub use self::hvf::Hvf;49
#[cfg(target_os = "linux")]50
pub use self::kvm::{Kvm, KvmConfig, KvmError};51
52
#[trace_error]53
#[derive(Snafu, DebugTrace)]54
#[snafu(module, context(suffix(false)))]55
pub 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
119
impl 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
128
impl<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
137
pub 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))]141
pub 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")]154
impl Default for HvConfig {155
fn default() -> Self {156
HvConfig::Kvm(KvmConfig::default())157
}158
}159
160
#[derive(Debug, Clone, Copy, PartialEq, Eq)]161
pub struct MemMapOption {162
pub read: bool,163
pub write: bool,164
pub exec: bool,165
pub log_dirty: bool,166
}167
168
impl 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
179
pub 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"))]229
pub trait IrqSender: Debug + Send + Sync + 'static {230
fn send(&self) -> Result<(), Error>;231
}232
233
#[cfg(not(target_arch = "x86_64"))]234
impl<T> IrqSender for Arc<T>235
where236
T: IrqSender,237
{238
fn send(&self) -> Result<(), Error> {239
IrqSender::send(self.as_ref())240
}241
}242
243
pub 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
249
pub 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
266
pub trait IoeventFd: Debug + Send + Sync + AsFd + 'static {}267
268
pub 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
283
pub 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")]295
pub 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")]306
pub trait GicV2m: Debug + Send + Sync + 'static {307
fn init(&self) -> Result<()>;308
}309
310
#[cfg(target_arch = "aarch64")]311
pub trait GicV3: Debug + Send + Sync + 'static {312
fn init(&self) -> Result<()>;313
}314
315
#[cfg(target_arch = "aarch64")]316
pub trait Its: Debug + Send + Sync + 'static {317
fn init(&self) -> Result<()>;318
}319
320
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Help)]321
pub 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)]350
pub struct VmConfig {351
pub coco: Option<Coco>,352
}353
354
pub 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
428
pub 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) -> Result<HashMap<CpuidIn, CpuidResult>>;435
}436
437
#[derive(Debug, Clone, PartialEq, Eq)]438
pub 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)]462
pub 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"]478
pub(crate) mod tests;479