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