1use kvm_bindings::KVM_API_VERSION;
5use kvm_ioctls::Kvm as KvmFd;
6use serde::{Deserialize, Serialize};
7
8pub use crate::arch::{Kvm, KvmArchError};
9use crate::cpu_config::templates::KvmCapability;
10
11#[rustfmt::skip]
14#[derive(Debug, thiserror::Error, displaydoc::Display)]
15pub enum KvmError {
16 ApiVersion(i32),
18 Capabilities(u32),
20 Kvm(kvm_ioctls::Error),
23 ArchError(#[from] KvmArchError)
25}
26
27impl Kvm {
28 pub fn new(kvm_cap_modifiers: Vec<KvmCapability>) -> Result<Self, KvmError> {
30 let kvm_fd = KvmFd::new().map_err(KvmError::Kvm)?;
31
32 #[allow(clippy::cast_possible_wrap)]
35 if kvm_fd.get_api_version() != KVM_API_VERSION as i32 {
36 return Err(KvmError::ApiVersion(kvm_fd.get_api_version()));
37 }
38
39 let total_caps = Self::combine_capabilities(&kvm_cap_modifiers);
40 Self::check_capabilities(&kvm_fd, &total_caps).map_err(KvmError::Capabilities)?;
42
43 Ok(Kvm::init_arch(kvm_fd, kvm_cap_modifiers)?)
44 }
45
46 fn combine_capabilities(kvm_cap_modifiers: &[KvmCapability]) -> Vec<u32> {
47 let mut total_caps = Self::DEFAULT_CAPABILITIES.to_vec();
48 for modifier in kvm_cap_modifiers.iter() {
49 match modifier {
50 KvmCapability::Add(cap) => {
51 if !total_caps.contains(cap) {
52 total_caps.push(*cap);
53 }
54 }
55 KvmCapability::Remove(cap) => {
56 if let Some(pos) = total_caps.iter().position(|c| c == cap) {
57 total_caps.swap_remove(pos);
58 }
59 }
60 }
61 }
62 total_caps
63 }
64
65 fn check_capabilities(kvm_fd: &KvmFd, capabilities: &[u32]) -> Result<(), u32> {
66 for cap in capabilities {
67 if kvm_fd.check_extension_raw(u64::from(*cap)) == 0 {
69 return Err(*cap);
70 }
71 }
72 Ok(())
73 }
74
75 pub fn save_state(&self) -> KvmState {
77 KvmState {
78 kvm_cap_modifiers: self.kvm_cap_modifiers.clone(),
79 }
80 }
81
82 pub fn max_nr_memslots(&self) -> u32 {
84 self.fd
85 .get_nr_memslots()
86 .try_into()
87 .expect("Number of vcpus reported by KVM exceeds u32::MAX")
88 }
89}
90
91#[derive(Debug, Default, Serialize, Deserialize)]
93pub struct KvmState {
94 pub kvm_cap_modifiers: Vec<KvmCapability>,
96}
97
98#[cfg(test)]
99pub(crate) mod tests {
100 use super::*;
101
102 #[test]
103 fn test_combine_capabilities() {
104 let additional_capabilities = vec![
107 KvmCapability::Add(kvm_bindings::KVM_CAP_IOMMU),
108 KvmCapability::Remove(kvm_bindings::KVM_CAP_IOEVENTFD),
109 ];
110
111 let combined_caps = Kvm::combine_capabilities(&additional_capabilities);
112 assert!(combined_caps.contains(&kvm_bindings::KVM_CAP_IOMMU));
113 assert!(!combined_caps.contains(&kvm_bindings::KVM_CAP_IOEVENTFD));
114 }
115}