Alioth Code Coverage

kvm.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_arch = "x86_64")]
16#[path = "kvm_x86_64.rs"]
17mod x86_64;
18
19#[cfg(target_arch = "aarch64")]
20mod device;
21#[cfg(target_arch = "x86_64")]
22mod sev;
23#[cfg(target_arch = "x86_64")]
24mod tdx;
25#[path = "vcpu/vcpu.rs"]
26mod vcpu;
27#[path = "vm/vm.rs"]
28mod vm;
29
30#[cfg(target_arch = "x86_64")]
31use std::arch::x86_64::CpuidResult;
32#[cfg(target_arch = "x86_64")]
33use std::collections::HashMap;
34use std::fs::File;
35use std::mem::{size_of, transmute};
36use std::num::NonZero;
37use std::os::fd::OwnedFd;
38use std::path::Path;
39use std::ptr::null_mut;
40
41use libc::SIGRTMIN;
42use serde::Deserialize;
43use serde_aco::Help;
44use snafu::{ResultExt, Snafu};
45
46#[cfg(target_arch = "x86_64")]
47use crate::arch::cpuid::CpuidIn;
48use crate::errors::{DebugTrace, trace_error};
49use crate::ffi;
50use crate::hv::{Hypervisor, MemMapOption, Result, VmConfig, error};
51#[cfg(target_arch = "aarch64")]
52use crate::sys::kvm::KvmDevType;
53use crate::sys::kvm::{KVM_API_VERSION, KvmCap, kvm_check_extension, kvm_get_api_version};
54
55use self::vm::KvmVm;
56
57#[trace_error]
58#[derive(DebugTrace, Snafu)]
59#[snafu(module, context(suffix(false)))]
60pub 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)]
98pub struct Kvm {
99 fd: OwnedFd,
100 #[cfg(target_arch = "x86_64")]
101 config: KvmConfig,
102}
103
104#[derive(Debug, Deserialize, Default, Clone, Help)]
105pub 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
113extern "C" fn sigrtmin_handler(_: libc::c_int, _: *mut libc::siginfo_t, _: *mut libc::c_void) {}
114
115impl 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
148impl 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
161fn 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