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