kvm.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_arch = "x86_64")]16
#[path = "kvm_x86_64.rs"]17
mod x86_64;18
19
#[cfg(target_arch = "aarch64")]20
mod device;21
#[cfg(target_arch = "x86_64")]22
mod sev;23
#[cfg(target_arch = "x86_64")]24
mod tdx;25
#[path = "vcpu/vcpu.rs"]26
mod vcpu;27
#[path = "vm/vm.rs"]28
mod vm;29
30
#[cfg(target_arch = "x86_64")]31
use std::arch::x86_64::CpuidResult;32
#[cfg(target_arch = "x86_64")]33
use std::collections::HashMap;34
use std::fs::File;35
use std::mem::{size_of, transmute};36
use std::num::NonZero;37
use std::os::fd::OwnedFd;38
use std::path::Path;39
use std::ptr::null_mut;40
41
use libc::SIGRTMIN;42
use serde::Deserialize;43
use serde_aco::Help;44
use snafu::{ResultExt, Snafu};45
46
#[cfg(target_arch = "x86_64")]47
use crate::arch::cpuid::CpuidIn;48
use crate::errors::{DebugTrace, trace_error};49
use crate::ffi;50
use crate::hv::{Hypervisor, MemMapOption, Result, VmConfig, error};51
#[cfg(target_arch = "aarch64")]52
use crate::sys::kvm::KvmDevType;53
use crate::sys::kvm::{KVM_API_VERSION, KvmCap, kvm_check_extension, kvm_get_api_version};54
55
use self::vm::KvmVm;56
57
#[trace_error]58
#[derive(DebugTrace, Snafu)]59
#[snafu(module, context(suffix(false)))]60
pub enum KvmError {61
#[snafu(display("Failed to update GSI routing table"))]62
GsiRouting { error: std::io::Error },63
#[snafu(display("Failed to allocate a GSI number"))]64
AllocateGsi,65
#[snafu(display("CPUID table too long"))]66
CpuidTableTooLong,67
#[snafu(display("Failed to get KVM API version"))]68
KvmApi { error: std::io::Error },69
#[snafu(display("Failed to open {path:?}"))]70
OpenFile {71
path: Box<Path>,72
error: std::io::Error,73
},74
#[snafu(display("Invalid memory map option {option:?}"))]75
MmapOption { option: MemMapOption },76
#[snafu(display("Failed to mmap a VCPU fd"))]77
MmapVcpuFd { error: std::io::Error },78
#[snafu(display("Failed to check KVM capability"))]79
CheckCap { error: std::io::Error },80
#[snafu(display("Failed to enable capability {cap:?}"))]81
EnableCap { cap: KvmCap, error: std::io::Error },82
#[snafu(display("Failed to create guest memfd"))]83
GuestMemfd { error: std::io::Error },84
#[cfg(target_arch = "aarch64")]85
#[snafu(display("Failed to create in-kernel device {type_:?}"))]86
CreateDevice {87
type_: KvmDevType,88
error: std::io::Error,89
},90
#[cfg(target_arch = "aarch64")]91
#[snafu(display("Failed to configure device attributes"))]92
DeviceAttr { error: std::io::Error },93
#[snafu(display("Failed to configure kvmclock"))]94
KvmClockCtrl { error: std::io::Error },95
}96
97
#[derive(Debug)]98
pub struct Kvm {99
fd: OwnedFd,100
#[cfg(target_arch = "x86_64")]101
config: KvmConfig,102
}103
104
#[derive(Debug, Deserialize, Default, Clone, Help)]105
pub struct KvmConfig {106
/// Path to the KVM device. [default: /dev/kvm]107
pub dev_kvm: Option<Box<Path>>,108
/// Path to the AMD SEV device. [default: /dev/sev]109
#[cfg(target_arch = "x86_64")]110
pub dev_sev: Option<Box<Path>>,111
}112
113
extern "C" fn sigrtmin_handler(_: libc::c_int, _: *mut libc::siginfo_t, _: *mut libc::c_void) {}114
115
impl Kvm {116
pub fn new(config: KvmConfig) -> Result<Self> {117
let path = match &config.dev_kvm {118
Some(dev_kvm) => dev_kvm,119
None => Path::new("/dev/kvm"),120
};121
let kvm_file = File::open(path).context(kvm_error::OpenFile { path })?;122
let kvm_fd = OwnedFd::from(kvm_file);123
let version = unsafe { kvm_get_api_version(&kvm_fd) }.context(kvm_error::KvmApi)?;124
if version != KVM_API_VERSION {125
return Err(error::Capability {126
cap: "KVM_API_VERSION (12)",127
}128
.build());129
}130
let mut action: libc::sigaction = unsafe { transmute([0u8; size_of::<libc::sigaction>()]) };131
action.sa_flags = libc::SA_SIGINFO;132
action.sa_sigaction = sigrtmin_handler as *const () as _;133
ffi!(unsafe { libc::sigfillset(&mut action.sa_mask) }).context(error::SetupSignal)?;134
ffi!(unsafe { libc::sigaction(SIGRTMIN(), &action, null_mut()) })135
.context(error::SetupSignal)?;136
Ok(Kvm {137
fd: kvm_fd,138
#[cfg(target_arch = "x86_64")]139
config,140
})141
}142
143
pub fn check_extension(&self, id: KvmCap) -> Result<NonZero<i32>> {144
check_extension(&self.fd, id)145
}146
}147
148
impl Hypervisor for Kvm {149
type Vm = KvmVm;150
151
fn create_vm(&self, config: &VmConfig) -> Result<Self::Vm> {152
KvmVm::new(self, config)153
}154
155
#[cfg(target_arch = "x86_64")]156
fn get_supported_cpuids(&self) -> Result<HashMap<CpuidIn, CpuidResult>> {157
Kvm::get_supported_cpuids(self)158
}159
}160
161
fn check_extension(fd: &OwnedFd, id: KvmCap) -> Result<NonZero<i32>> {162
let ret = unsafe { kvm_check_extension(fd, id) }.context(kvm_error::CheckCap)?;163
if let Some(v) = NonZero::new(ret) {164
Ok(v)165
} else {166
error::Capability { cap: id.name() }.fail()167
}168
}169