Skip to main content

kvm_ioctls/ioctls/
system.rs

1// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3//
4// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
5// Use of this source code is governed by a BSD-style license that can be
6// found in the THIRD-PARTY file.
7use libc::{O_CLOEXEC, O_RDWR, open};
8use std::ffi::CStr;
9use std::fs::File;
10use std::os::raw::{c_char, c_ulong};
11use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
12
13use crate::cap::Cap;
14use crate::ioctls::Result;
15use crate::ioctls::vm::{VmFd, new_vmfd};
16use crate::kvm_ioctls::*;
17#[cfg(target_arch = "aarch64")]
18use kvm_bindings::KVM_VM_TYPE_ARM_IPA_SIZE_MASK;
19#[cfg(target_arch = "x86_64")]
20use kvm_bindings::{CpuId, KVM_MAX_CPUID_ENTRIES, KVM_MAX_MSR_ENTRIES, MsrList, Msrs};
21use vmm_sys_util::errno;
22#[cfg(target_arch = "x86_64")]
23use vmm_sys_util::ioctl::ioctl_with_mut_ptr;
24use vmm_sys_util::ioctl::{ioctl, ioctl_with_val};
25
26/// Wrapper over KVM system ioctls.
27#[derive(Debug)]
28pub struct Kvm {
29    kvm: File,
30}
31
32impl Kvm {
33    /// Opens `/dev/kvm` and returns a `Kvm` object on success.
34    ///
35    /// # Example
36    ///
37    /// ```
38    /// use kvm_ioctls::Kvm;
39    /// let kvm = Kvm::new().unwrap();
40    /// ```
41    #[allow(clippy::new_ret_no_self)]
42    pub fn new() -> Result<Self> {
43        // Open `/dev/kvm` using `O_CLOEXEC` flag.
44        let fd = Self::open_with_cloexec(true)?;
45        // SAFETY: Safe because we verify that the fd is valid in `open_with_cloexec` and we own
46        // the fd.
47        Ok(unsafe { Self::from_raw_fd(fd) })
48    }
49
50    /// Opens the KVM device at `kvm_path` and returns a `Kvm` object on success.
51    ///
52    /// # Arguments
53    ///
54    /// * `kvm_path`: path to the KVM device. Usually it is `/dev/kvm`.
55    ///
56    /// # Example
57    ///
58    /// ```
59    /// use kvm_ioctls::Kvm;
60    /// use std::ffi::CString;
61    /// let kvm_path = CString::new("/dev/kvm").unwrap();
62    /// let kvm = Kvm::new_with_path(&kvm_path).unwrap();
63    /// ```
64    #[allow(clippy::new_ret_no_self)]
65    pub fn new_with_path<P>(kvm_path: P) -> Result<Self>
66    where
67        P: AsRef<CStr>,
68    {
69        // Open `kvm_path` using `O_CLOEXEC` flag.
70        let fd = Self::open_with_cloexec_at(kvm_path, true)?;
71        // SAFETY: Safe because we verify that the fd is valid in `open_with_cloexec_at`
72        // and we own the fd.
73        Ok(unsafe { Self::from_raw_fd(fd) })
74    }
75
76    /// Opens `/dev/kvm` and returns the fd number on success.
77    ///
78    /// One usecase for this method is opening `/dev/kvm` before exec-ing into a
79    /// process with seccomp filters enabled that blacklist the `sys_open` syscall.
80    /// For this usecase `open_with_cloexec` must be called with the `close_on_exec`
81    /// parameter set to false.
82    ///
83    /// # Arguments
84    ///
85    /// * `close_on_exec`: If true opens `/dev/kvm` using the `O_CLOEXEC` flag.
86    ///
87    /// # Example
88    ///
89    /// ```
90    /// # use kvm_ioctls::Kvm;
91    /// # use std::os::unix::io::FromRawFd;
92    /// let kvm_fd = Kvm::open_with_cloexec(false).unwrap();
93    /// // The `kvm_fd` can now be passed to another process where we can use
94    /// // `from_raw_fd` for creating a `Kvm` object:
95    /// let kvm = unsafe { Kvm::from_raw_fd(kvm_fd) };
96    /// ```
97    pub fn open_with_cloexec(close_on_exec: bool) -> Result<RawFd> {
98        // SAFETY: Safe because we give a constant nul-terminated string.
99        let kvm_path = c"/dev/kvm";
100        Self::open_with_cloexec_at(kvm_path, close_on_exec)
101    }
102
103    /// Opens the KVM device at `kvm_path` and returns the fd number on success.
104    /// Same as [open_with_cloexec()](struct.Kvm.html#method.open_with_cloexec)
105    /// except this method opens `kvm_path` instead of `/dev/kvm`.
106    ///
107    /// # Arguments
108    ///
109    /// * `kvm_path`: path to the KVM device. Usually it is `/dev/kvm`.
110    /// * `close_on_exec`: If true opens `kvm_path` using the `O_CLOEXEC` flag.
111    ///
112    /// # Example
113    ///
114    /// ```
115    /// # use kvm_ioctls::Kvm;
116    /// # use std::ffi::CString;
117    /// # use std::os::unix::io::FromRawFd;
118    /// let kvm_path = CString::new("/dev/kvm").unwrap();
119    /// let kvm_fd = Kvm::open_with_cloexec_at(kvm_path, false).unwrap();
120    /// // The `kvm_fd` can now be passed to another process where we can use
121    /// // `from_raw_fd` for creating a `Kvm` object:
122    /// let kvm = unsafe { Kvm::from_raw_fd(kvm_fd) };
123    /// ```
124    pub fn open_with_cloexec_at<P>(path: P, close_on_exec: bool) -> Result<RawFd>
125    where
126        P: AsRef<CStr>,
127    {
128        let open_flags = O_RDWR | if close_on_exec { O_CLOEXEC } else { 0 };
129        // SAFETY: Safe because we verify the result.
130        let ret = unsafe { open(path.as_ref().as_ptr() as *const c_char, open_flags) };
131        if ret < 0 {
132            Err(errno::Error::last())
133        } else {
134            Ok(ret)
135        }
136    }
137
138    /// Returns the KVM API version.
139    ///
140    /// See the documentation for `KVM_GET_API_VERSION`.
141    ///
142    /// # Example
143    ///
144    /// ```
145    /// # use kvm_ioctls::Kvm;
146    /// let kvm = Kvm::new().unwrap();
147    /// assert_eq!(kvm.get_api_version(), 12);
148    /// ```
149    pub fn get_api_version(&self) -> i32 {
150        // SAFETY: Safe because we know that our file is a KVM fd and that the request is one of
151        // the ones defined by kernel.
152        unsafe { ioctl(self, KVM_GET_API_VERSION()) }
153    }
154
155    /// AArch64 specific call to get the host Intermediate Physical Address space limit.
156    ///
157    /// Returns 0 if the capability is not available and an integer >= 32 otherwise.
158    #[cfg(target_arch = "aarch64")]
159    pub fn get_host_ipa_limit(&self) -> i32 {
160        self.check_extension_int(Cap::ArmVmIPASize)
161    }
162
163    /// AArch64 specific call to get the number of supported hardware breakpoints.
164    ///
165    /// Returns 0 if the capability is not available and a positive integer otherwise.
166    #[cfg(target_arch = "aarch64")]
167    pub fn get_guest_debug_hw_bps(&self) -> i32 {
168        self.check_extension_int(Cap::DebugHwBps)
169    }
170
171    /// AArch64 specific call to get the number of supported hardware watchpoints.
172    ///
173    /// Returns 0 if the capability is not available and a positive integer otherwise.
174    #[cfg(target_arch = "aarch64")]
175    pub fn get_guest_debug_hw_wps(&self) -> i32 {
176        self.check_extension_int(Cap::DebugHwWps)
177    }
178
179    /// Wrapper over `KVM_CHECK_EXTENSION`.
180    ///
181    /// Returns 0 if the capability is not available and a positive integer otherwise.
182    /// See the documentation for `KVM_CHECK_EXTENSION`.
183    ///
184    /// # Arguments
185    ///
186    /// * `c` - KVM capability to check in a form of a raw integer.
187    ///
188    /// # Example
189    ///
190    /// ```
191    /// # use kvm_ioctls::Kvm;
192    /// # use std::os::raw::c_ulong;
193    /// use kvm_ioctls::Cap;
194    ///
195    /// let kvm = Kvm::new().unwrap();
196    /// assert!(kvm.check_extension_raw(Cap::MaxVcpus as c_ulong) > 0);
197    /// ```
198    pub fn check_extension_raw(&self, c: c_ulong) -> i32 {
199        // SAFETY: Safe because we know that our file is a KVM fd.
200        // If `c` is not a known kernel extension, kernel will return 0.
201        unsafe { ioctl_with_val(self, KVM_CHECK_EXTENSION(), c) }
202    }
203
204    /// Wrapper over `KVM_CHECK_EXTENSION`.
205    ///
206    /// Returns 0 if the capability is not available and a positive integer otherwise.
207    /// See the documentation for `KVM_CHECK_EXTENSION`.
208    ///
209    /// # Arguments
210    ///
211    /// * `c` - KVM capability to check.
212    ///
213    /// # Example
214    ///
215    /// ```
216    /// # use kvm_ioctls::Kvm;
217    /// use kvm_ioctls::Cap;
218    ///
219    /// let kvm = Kvm::new().unwrap();
220    /// assert!(kvm.check_extension_int(Cap::MaxVcpus) > 0);
221    /// ```
222    pub fn check_extension_int(&self, c: Cap) -> i32 {
223        self.check_extension_raw(c as c_ulong)
224    }
225
226    /// Checks if a particular `Cap` is available.
227    ///
228    /// Returns true if the capability is supported and false otherwise.
229    /// See the documentation for `KVM_CHECK_EXTENSION`.
230    ///
231    /// # Arguments
232    ///
233    /// * `c` - KVM capability to check.
234    ///
235    /// # Example
236    ///
237    /// ```
238    /// # use kvm_ioctls::Kvm;
239    /// use kvm_ioctls::Cap;
240    ///
241    /// let kvm = Kvm::new().unwrap();
242    /// // Check if `KVM_CAP_USER_MEMORY` is supported.
243    /// assert!(kvm.check_extension(Cap::UserMemory));
244    /// ```
245    pub fn check_extension(&self, c: Cap) -> bool {
246        self.check_extension_int(c) > 0
247    }
248
249    ///  Returns the size of the memory mapping required to use the vcpu's `kvm_run` structure.
250    ///
251    /// See the documentation for `KVM_GET_VCPU_MMAP_SIZE`.
252    ///
253    /// # Example
254    ///
255    /// ```
256    /// # use kvm_ioctls::Kvm;
257    /// let kvm = Kvm::new().unwrap();
258    /// assert!(kvm.get_vcpu_mmap_size().unwrap() > 0);
259    /// ```
260    pub fn get_vcpu_mmap_size(&self) -> Result<usize> {
261        // SAFETY: Safe because we know that our file is a KVM fd and we verify the return result.
262        let res = unsafe { ioctl(self, KVM_GET_VCPU_MMAP_SIZE()) };
263        if res > 0 {
264            Ok(res as usize)
265        } else {
266            Err(errno::Error::last())
267        }
268    }
269
270    /// Gets the recommended number of VCPUs per VM.
271    ///
272    /// See the documentation for `KVM_CAP_NR_VCPUS`.
273    /// Default to 4 when `KVM_CAP_NR_VCPUS` is not implemented.
274    ///
275    /// # Example
276    ///
277    /// ```
278    /// # use kvm_ioctls::Kvm;
279    /// let kvm = Kvm::new().unwrap();
280    /// // We expect the number of vCPUs to be > 0 as per KVM API documentation.
281    /// assert!(kvm.get_nr_vcpus() > 0);
282    /// ```
283    pub fn get_nr_vcpus(&self) -> usize {
284        let x = self.check_extension_int(Cap::NrVcpus);
285        if x > 0 { x as usize } else { 4 }
286    }
287
288    /// Returns the maximum allowed memory slots per VM.
289    ///
290    /// KVM reports the number of available memory slots (`KVM_CAP_NR_MEMSLOTS`)
291    /// using the extension interface.  Both x86 and s390 implement this, ARM
292    /// and powerpc do not yet enable it.
293    /// Default to 32 when `KVM_CAP_NR_MEMSLOTS` is not implemented.
294    ///
295    /// # Example
296    ///
297    /// ```
298    /// # use kvm_ioctls::Kvm;
299    /// let kvm = Kvm::new().unwrap();
300    /// assert!(kvm.get_nr_memslots() > 0);
301    /// ```
302    pub fn get_nr_memslots(&self) -> usize {
303        let x = self.check_extension_int(Cap::NrMemslots);
304        if x > 0 { x as usize } else { 32 }
305    }
306
307    /// Gets the recommended maximum number of VCPUs per VM.
308    ///
309    /// See the documentation for `KVM_CAP_MAX_VCPUS`.
310    /// Returns [get_nr_vcpus()](struct.Kvm.html#method.get_nr_vcpus) when
311    /// `KVM_CAP_MAX_VCPUS` is not implemented.
312    ///
313    /// # Example
314    ///
315    /// ```
316    /// # use kvm_ioctls::Kvm;
317    /// let kvm = Kvm::new().unwrap();
318    /// assert!(kvm.get_max_vcpus() > 0);
319    /// ```
320    pub fn get_max_vcpus(&self) -> usize {
321        match self.check_extension_int(Cap::MaxVcpus) {
322            0 => self.get_nr_vcpus(),
323            x => x as usize,
324        }
325    }
326
327    /// Gets the Maximum VCPU ID per VM.
328    ///
329    /// See the documentation for `KVM_CAP_MAX_VCPU_ID`
330    /// Returns [get_max_vcpus()](struct.Kvm.html#method.get_max_vcpus) when
331    /// `KVM_CAP_MAX_VCPU_ID` is not implemented
332    ///
333    /// # Example
334    ///
335    /// ```
336    /// # use kvm_ioctls::Kvm;
337    /// let kvm = Kvm::new().unwrap();
338    /// assert!(kvm.get_max_vcpu_id() > 0);
339    /// ```
340    pub fn get_max_vcpu_id(&self) -> usize {
341        match self.check_extension_int(Cap::MaxVcpuId) {
342            0 => self.get_max_vcpus(),
343            x => x as usize,
344        }
345    }
346
347    #[cfg(target_arch = "x86_64")]
348    fn get_cpuid(&self, kind: u64, num_entries: usize) -> Result<CpuId> {
349        if num_entries > KVM_MAX_CPUID_ENTRIES {
350            // Returns the same error the underlying `ioctl` would have sent.
351            return Err(errno::Error::new(libc::ENOMEM));
352        }
353
354        let mut cpuid = CpuId::new(num_entries).map_err(|_| errno::Error::new(libc::ENOMEM))?;
355        // SAFETY: The kernel is trusted not to write beyond the bounds of the memory
356        // allocated for the struct. The limit is read from nent, which is set to the allocated
357        // size(num_entries) above.
358        let ret = unsafe { ioctl_with_mut_ptr(self, kind, cpuid.as_mut_fam_struct_ptr()) };
359        if ret < 0 {
360            return Err(errno::Error::last());
361        }
362
363        Ok(cpuid)
364    }
365
366    /// X86 specific call to get the system emulated CPUID values.
367    ///
368    /// See the documentation for `KVM_GET_EMULATED_CPUID`.
369    ///
370    /// # Arguments
371    ///
372    /// * `num_entries` - Maximum number of CPUID entries. This function can return less than
373    ///   this when the hardware does not support so many CPUID entries.
374    ///
375    /// Returns Error `errno::Error(libc::ENOMEM)` when the input `num_entries` is greater than
376    /// `KVM_MAX_CPUID_ENTRIES`.
377    ///
378    /// # Example
379    ///
380    /// ```
381    /// extern crate kvm_bindings;
382    /// use kvm_bindings::KVM_MAX_CPUID_ENTRIES;
383    /// use kvm_ioctls::Kvm;
384    ///
385    /// let kvm = Kvm::new().unwrap();
386    /// let mut cpuid = kvm.get_emulated_cpuid(KVM_MAX_CPUID_ENTRIES).unwrap();
387    /// let cpuid_entries = cpuid.as_mut_slice();
388    /// assert!(cpuid_entries.len() <= KVM_MAX_CPUID_ENTRIES);
389    /// ```
390    #[cfg(target_arch = "x86_64")]
391    pub fn get_emulated_cpuid(&self, num_entries: usize) -> Result<CpuId> {
392        self.get_cpuid(KVM_GET_EMULATED_CPUID(), num_entries)
393    }
394
395    /// X86 specific call to get the system supported CPUID values.
396    ///
397    /// See the documentation for `KVM_GET_SUPPORTED_CPUID`.
398    ///
399    /// # Arguments
400    ///
401    /// * `num_entries` - Maximum number of CPUID entries. This function can return less than
402    ///   this when the hardware does not support so many CPUID entries.
403    ///
404    /// Returns Error `errno::Error(libc::ENOMEM)` when the input `num_entries` is greater than
405    /// `KVM_MAX_CPUID_ENTRIES`.
406    ///
407    /// # Example
408    ///
409    /// ```
410    /// extern crate kvm_bindings;
411    /// use kvm_bindings::KVM_MAX_CPUID_ENTRIES;
412    /// use kvm_ioctls::Kvm;
413    ///
414    /// let kvm = Kvm::new().unwrap();
415    /// let mut cpuid = kvm.get_supported_cpuid(KVM_MAX_CPUID_ENTRIES).unwrap();
416    /// let cpuid_entries = cpuid.as_mut_slice();
417    /// assert!(cpuid_entries.len() <= KVM_MAX_CPUID_ENTRIES);
418    /// ```
419    #[cfg(target_arch = "x86_64")]
420    pub fn get_supported_cpuid(&self, num_entries: usize) -> Result<CpuId> {
421        self.get_cpuid(KVM_GET_SUPPORTED_CPUID(), num_entries)
422    }
423
424    /// X86 specific call to get list of supported MSRS
425    ///
426    /// See the documentation for `KVM_GET_MSR_INDEX_LIST`.
427    ///
428    /// # Example
429    ///
430    /// ```
431    /// use kvm_ioctls::Kvm;
432    ///
433    /// let kvm = Kvm::new().unwrap();
434    /// let msr_index_list = kvm.get_msr_index_list().unwrap();
435    /// ```
436    #[cfg(target_arch = "x86_64")]
437    pub fn get_msr_index_list(&self) -> Result<MsrList> {
438        let mut msr_list =
439            MsrList::new(KVM_MAX_MSR_ENTRIES).map_err(|_| errno::Error::new(libc::ENOMEM))?;
440
441        // SAFETY: The kernel is trusted not to write beyond the bounds of the memory
442        // allocated for the struct. The limit is read from nmsrs, which is set to the allocated
443        // size (KVM_MAX_MSR_ENTRIES) above.
444        let ret = unsafe {
445            ioctl_with_mut_ptr(
446                self,
447                KVM_GET_MSR_INDEX_LIST(),
448                msr_list.as_mut_fam_struct_ptr(),
449            )
450        };
451        if ret < 0 {
452            return Err(errno::Error::last());
453        }
454
455        // The ioctl will also update the internal `nmsrs` with the actual count.
456        Ok(msr_list)
457    }
458
459    /// X86 specific call to get a list of MSRs that can be passed to the KVM_GET_MSRS system ioctl.
460    ///
461    /// See the documentation for `KVM_GET_MSR_FEATURE_INDEX_LIST`.
462    ///
463    /// # Example
464    ///
465    /// ```
466    /// use kvm_bindings::{Msrs, kvm_msr_entry};
467    /// use kvm_ioctls::Kvm;
468    ///
469    /// let kvm = Kvm::new().unwrap();
470    /// let msr_feature_index_list = kvm.get_msr_feature_index_list().unwrap();
471    /// ```
472    #[cfg(target_arch = "x86_64")]
473    pub fn get_msr_feature_index_list(&self) -> Result<MsrList> {
474        let mut msr_list =
475            MsrList::new(KVM_MAX_MSR_ENTRIES).map_err(|_| errno::Error::new(libc::ENOMEM))?;
476
477        // SAFETY: The kernel is trusted not to write beyond the bounds of the memory
478        // allocated for the struct. The limit is read from nmsrs, which is set to the allocated
479        // size (KVM_MAX_MSR_ENTRIES) above.
480        let ret = unsafe {
481            ioctl_with_mut_ptr(
482                self,
483                KVM_GET_MSR_FEATURE_INDEX_LIST(),
484                msr_list.as_mut_fam_struct_ptr(),
485            )
486        };
487        if ret < 0 {
488            return Err(errno::Error::last());
489        }
490
491        Ok(msr_list)
492    }
493
494    /// X86 specific call to read the values of MSR-based features that are available for the VM.
495    /// As opposed to `VcpuFd::get_msrs()`, this call returns all the MSRs supported by the
496    /// system, similar to `get_supported_cpuid()` for CPUID.
497    ///
498    /// See the documentation for `KVM_GET_MSRS`.
499    ///
500    /// # Arguments
501    ///
502    /// * `msrs`  - MSRs (input/output). For details check the `kvm_msrs` structure in the
503    ///   [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt).
504    ///
505    /// # Example
506    ///
507    /// ```
508    /// use kvm_bindings::{Msrs, kvm_msr_entry};
509    /// use kvm_ioctls::Kvm;
510    ///
511    /// let kvm = Kvm::new().unwrap();
512    /// let msr_feature_index_list = kvm.get_msr_feature_index_list().unwrap();
513    /// let mut msrs = Msrs::from_entries(
514    ///     &msr_feature_index_list
515    ///         .as_slice()
516    ///         .iter()
517    ///         .map(|&idx| kvm_msr_entry {
518    ///             index: idx,
519    ///             ..Default::default()
520    ///         })
521    ///         .collect::<Vec<_>>(),
522    /// )
523    /// .unwrap();
524    /// let ret = kvm.get_msrs(&mut msrs).unwrap();
525    /// ```
526    #[cfg(target_arch = "x86_64")]
527    pub fn get_msrs(&self, msrs: &mut Msrs) -> Result<usize> {
528        // SAFETY: Here we trust the kernel not to read past the end of the kvm_msrs struct.
529        let ret = unsafe { ioctl_with_mut_ptr(self, KVM_GET_MSRS(), msrs.as_mut_fam_struct_ptr()) };
530        if ret < 0 {
531            return Err(errno::Error::last());
532        }
533        Ok(ret as usize)
534    }
535
536    /// Creates a VM fd using the KVM fd.
537    ///
538    /// See the documentation for `KVM_CREATE_VM`.
539    /// A call to this function will also initialize the size of the vcpu mmap area using the
540    /// `KVM_GET_VCPU_MMAP_SIZE` ioctl.
541    ///
542    /// # Example
543    ///
544    /// ```
545    /// # use kvm_ioctls::Kvm;
546    /// let kvm = Kvm::new().unwrap();
547    /// let vm = kvm.create_vm().unwrap();
548    /// // Check that the VM mmap size is the same reported by `KVM_GET_VCPU_MMAP_SIZE`.
549    /// assert!(vm.run_size() == kvm.get_vcpu_mmap_size().unwrap());
550    /// ```
551    #[cfg(not(target_arch = "aarch64"))]
552    pub fn create_vm(&self) -> Result<VmFd> {
553        self.create_vm_with_type(0) // Create using default VM type
554    }
555
556    /// AArch64 specific create_vm to create a VM fd using the KVM fd using the host's maximum IPA size.
557    ///
558    /// See the arm64 section of KVM documentation for `KVM_CREATE_VM`.
559    /// A call to this function will also initialize the size of the vcpu mmap area using the
560    /// `KVM_GET_VCPU_MMAP_SIZE` ioctl.
561    ///
562    /// # Example
563    ///
564    /// ```
565    /// # use kvm_ioctls::Kvm;
566    /// let kvm = Kvm::new().unwrap();
567    /// let vm = kvm.create_vm().unwrap();
568    /// // Check that the VM mmap size is the same reported by `KVM_GET_VCPU_MMAP_SIZE`.
569    /// assert_eq!(vm.run_size(), kvm.get_vcpu_mmap_size().unwrap());
570    /// ```
571    #[cfg(target_arch = "aarch64")]
572    pub fn create_vm(&self) -> Result<VmFd> {
573        let mut ipa_size = 0; // Create using default VM type
574        if self.check_extension(Cap::ArmVmIPASize) {
575            ipa_size = self.get_host_ipa_limit();
576        }
577        self.create_vm_with_type(ipa_size as u64)
578    }
579
580    /// AArch64 specific function to create a VM fd using the KVM fd with flexible IPA size.
581    ///
582    /// See the arm64 section of KVM documentation for `KVM_CREATE_VM`.
583    /// A call to this function will also initialize the size of the vcpu mmap area using the
584    /// `KVM_GET_VCPU_MMAP_SIZE` ioctl.
585    ///
586    /// Note: `Cap::ArmVmIPASize` should be checked using `check_extension` before calling
587    /// this function to determine if the host machine supports the IPA size capability.
588    ///
589    /// # Arguments
590    ///
591    /// * `ipa_size` - Guest VM IPA size, 32 <= ipa_size <= Host_IPA_Limit.
592    ///   The value of `Host_IPA_Limit` may be different between hardware
593    ///   implementations and can be extracted by calling `get_host_ipa_limit`.
594    ///   Possible values can be found in documentation of registers `TCR_EL2`
595    ///   and `VTCR_EL2`.
596    ///
597    /// # Example
598    ///
599    /// ```
600    /// # use kvm_ioctls::{Kvm, Cap};
601    /// let kvm = Kvm::new().unwrap();
602    /// // Check if the ArmVmIPASize cap is supported.
603    /// if kvm.check_extension(Cap::ArmVmIPASize) {
604    ///     let host_ipa_limit = kvm.get_host_ipa_limit();
605    ///     let vm = kvm.create_vm_with_ipa_size(host_ipa_limit as u32).unwrap();
606    ///     // Check that the VM mmap size is the same reported by `KVM_GET_VCPU_MMAP_SIZE`.
607    ///     assert_eq!(vm.run_size(), kvm.get_vcpu_mmap_size().unwrap());
608    /// }
609    /// ```
610    #[cfg(target_arch = "aarch64")]
611    pub fn create_vm_with_ipa_size(&self, ipa_size: u32) -> Result<VmFd> {
612        self.create_vm_with_type((ipa_size & KVM_VM_TYPE_ARM_IPA_SIZE_MASK).into())
613    }
614
615    /// Creates a VM fd using the KVM fd of a specific type.
616    ///
617    /// See the documentation for `KVM_CREATE_VM`.
618    /// A call to this function will also initialize the size of the vcpu mmap area using the
619    /// `KVM_GET_VCPU_MMAP_SIZE` ioctl.
620    ///
621    /// * `vm_type` - Platform and architecture specific platform VM type. A value of 0 is the equivalent
622    ///   to using the default VM type.
623    /// # Example
624    ///
625    /// ```
626    /// # use kvm_ioctls::Kvm;
627    /// let kvm = Kvm::new().unwrap();
628    /// let vm = kvm.create_vm_with_type(0).unwrap();
629    /// // Check that the VM mmap size is the same reported by `KVM_GET_VCPU_MMAP_SIZE`.
630    /// assert_eq!(vm.run_size(), kvm.get_vcpu_mmap_size().unwrap());
631    /// ```
632    pub fn create_vm_with_type(&self, vm_type: u64) -> Result<VmFd> {
633        // SAFETY: Safe because we know `self.kvm` is a real KVM fd as this module is the only one
634        // that create Kvm objects.
635        let ret = unsafe { ioctl_with_val(&self.kvm, KVM_CREATE_VM(), vm_type) };
636        if ret >= 0 {
637            // SAFETY: Safe because we verify the value of ret and we are the owners of the fd.
638            let vm_file = unsafe { File::from_raw_fd(ret) };
639            let run_mmap_size = self.get_vcpu_mmap_size()?;
640            Ok(new_vmfd(vm_file, run_mmap_size))
641        } else {
642            Err(errno::Error::last())
643        }
644    }
645
646    /// Creates a VmFd object from a VM RawFd.
647    ///
648    /// # Arguments
649    ///
650    /// * `fd` - the RawFd used for creating the VmFd object.
651    ///
652    /// # Safety
653    ///
654    /// This function is unsafe as the primitives currently returned have the contract that
655    /// they are the sole owner of the file descriptor they are wrapping. Usage of this function
656    /// could accidentally allow violating this contract which can cause memory unsafety in code
657    /// that relies on it being true.
658    ///
659    /// The caller of this method must make sure the fd is valid and nothing else uses it.
660    ///
661    /// # Example
662    ///
663    /// ```rust
664    /// # extern crate kvm_ioctls;
665    /// # use std::os::unix::io::AsRawFd;
666    /// # use kvm_ioctls::Kvm;
667    /// let kvm = Kvm::new().unwrap();
668    /// let vm = kvm.create_vm().unwrap();
669    /// let rawfd = unsafe { libc::dup(vm.as_raw_fd()) };
670    /// assert!(rawfd >= 0);
671    /// let vm = unsafe { kvm.create_vmfd_from_rawfd(rawfd).unwrap() };
672    /// ```
673    pub unsafe fn create_vmfd_from_rawfd(&self, fd: RawFd) -> Result<VmFd> {
674        let run_mmap_size = self.get_vcpu_mmap_size()?;
675        // SAFETY: we trust the kernel and verified parameters
676        Ok(new_vmfd(unsafe { File::from_raw_fd(fd) }, run_mmap_size))
677    }
678}
679
680impl AsRawFd for Kvm {
681    fn as_raw_fd(&self) -> RawFd {
682        self.kvm.as_raw_fd()
683    }
684}
685
686impl FromRawFd for Kvm {
687    /// Creates a new Kvm object assuming `fd` represents an existing open file descriptor
688    /// associated with `/dev/kvm`.
689    ///
690    /// For usage examples check [open_with_cloexec()](struct.Kvm.html#method.open_with_cloexec).
691    ///
692    /// # Arguments
693    ///
694    /// * `fd` - File descriptor for `/dev/kvm`.
695    ///
696    /// # Safety
697    ///
698    /// This function is unsafe as the primitives currently returned have the contract that
699    /// they are the sole owner of the file descriptor they are wrapping. Usage of this function
700    /// could accidentally allow violating this contract which can cause memory unsafety in code
701    /// that relies on it being true.
702    ///
703    /// The caller of this method must make sure the fd is valid and nothing else uses it.
704    ///
705    /// # Example
706    ///
707    /// ```
708    /// # use kvm_ioctls::Kvm;
709    /// # use std::os::unix::io::FromRawFd;
710    /// let kvm_fd = Kvm::open_with_cloexec(true).unwrap();
711    /// // Safe because we verify that the fd is valid in `open_with_cloexec` and we own the fd.
712    /// let kvm = unsafe { Kvm::from_raw_fd(kvm_fd) };
713    /// ```
714    unsafe fn from_raw_fd(fd: RawFd) -> Self {
715        Kvm {
716            // SAFETY: we trust the kernel and verified parameters
717            kvm: unsafe { File::from_raw_fd(fd) },
718        }
719    }
720}
721
722#[cfg(test)]
723mod tests {
724    #![allow(clippy::undocumented_unsafe_blocks)]
725    use super::*;
726    use libc::{F_GETFD, FD_CLOEXEC, fcntl};
727    use std::os::fd::IntoRawFd;
728    #[cfg(target_arch = "x86_64")]
729    use vmm_sys_util::fam::FamStruct;
730
731    #[test]
732    fn test_kvm_new() {
733        Kvm::new().unwrap();
734    }
735
736    #[test]
737    fn test_kvm_new_with_path() {
738        let kvm_path = c"/dev/kvm";
739        Kvm::new_with_path(kvm_path).unwrap();
740    }
741
742    #[test]
743    fn test_open_with_cloexec() {
744        let fd = Kvm::open_with_cloexec(false).unwrap();
745        let flags = unsafe { fcntl(fd, F_GETFD, 0) };
746        assert_eq!(flags & FD_CLOEXEC, 0);
747        let fd = Kvm::open_with_cloexec(true).unwrap();
748        let flags = unsafe { fcntl(fd, F_GETFD, 0) };
749        assert_eq!(flags & FD_CLOEXEC, FD_CLOEXEC);
750    }
751
752    #[test]
753    fn test_open_with_cloexec_at() {
754        let kvm_path = std::ffi::CString::new("/dev/kvm").unwrap();
755        let fd = Kvm::open_with_cloexec_at(&kvm_path, false).unwrap();
756        let flags = unsafe { fcntl(fd, F_GETFD, 0) };
757        assert_eq!(flags & FD_CLOEXEC, 0);
758        let fd = Kvm::open_with_cloexec_at(&kvm_path, true).unwrap();
759        let flags = unsafe { fcntl(fd, F_GETFD, 0) };
760        assert_eq!(flags & FD_CLOEXEC, FD_CLOEXEC);
761    }
762
763    #[test]
764    fn test_kvm_api_version() {
765        let kvm = Kvm::new().unwrap();
766        assert_eq!(kvm.get_api_version(), 12);
767        assert!(kvm.check_extension(Cap::UserMemory));
768    }
769
770    #[test]
771    fn test_kvm_check_extension() {
772        let kvm = Kvm::new().unwrap();
773        // unsupported extension will return 0
774        assert_eq!(kvm.check_extension_raw(696969), 0);
775    }
776
777    #[test]
778    #[cfg(target_arch = "aarch64")]
779    fn test_get_host_ipa_limit() {
780        let kvm = Kvm::new().unwrap();
781        let host_ipa_limit = kvm.get_host_ipa_limit();
782
783        if host_ipa_limit > 0 {
784            assert!(host_ipa_limit >= 32);
785        } else {
786            // if unsupported, the return value should be 0.
787            assert_eq!(host_ipa_limit, 0);
788        }
789    }
790
791    #[test]
792    #[cfg(target_arch = "aarch64")]
793    fn test_guest_debug_hw_capacity() {
794        let kvm = Kvm::new().unwrap();
795        // The number of supported breakpoints and watchpoints may vary on
796        // different platforms.
797        // It could be 0 if no supported, or any positive integer otherwise.
798        assert!(kvm.get_guest_debug_hw_bps() >= 0);
799        assert!(kvm.get_guest_debug_hw_wps() >= 0);
800    }
801
802    #[test]
803    fn test_kvm_getters() {
804        let kvm = Kvm::new().unwrap();
805
806        // vCPU related getters
807        let nr_vcpus = kvm.get_nr_vcpus();
808        assert!(nr_vcpus >= 4);
809
810        assert!(kvm.get_max_vcpus() >= nr_vcpus);
811
812        // Memory related getters
813        assert!(kvm.get_vcpu_mmap_size().unwrap() > 0);
814        assert!(kvm.get_nr_memslots() >= 32);
815    }
816
817    #[test]
818    fn test_create_vm() {
819        let kvm = Kvm::new().unwrap();
820        let vm = kvm.create_vm().unwrap();
821
822        // Test create_vmfd_from_rawfd()
823        let rawfd = unsafe { libc::dup(vm.as_raw_fd()) };
824        assert!(rawfd >= 0);
825        let vm = unsafe { kvm.create_vmfd_from_rawfd(rawfd).unwrap() };
826
827        assert_eq!(vm.run_size(), kvm.get_vcpu_mmap_size().unwrap());
828    }
829
830    #[test]
831    fn test_create_vm_with_type() {
832        let kvm = Kvm::new().unwrap();
833        let vm = kvm.create_vm_with_type(0).unwrap();
834
835        // Test create_vmfd_from_rawfd()
836        let rawfd = unsafe { libc::dup(vm.as_raw_fd()) };
837        assert!(rawfd >= 0);
838        let vm = unsafe { kvm.create_vmfd_from_rawfd(rawfd).unwrap() };
839
840        assert_eq!(vm.run_size(), kvm.get_vcpu_mmap_size().unwrap());
841    }
842
843    #[test]
844    #[cfg(target_arch = "aarch64")]
845    fn test_create_vm_with_ipa_size() {
846        let kvm = Kvm::new().unwrap();
847        if kvm.check_extension(Cap::ArmVmIPASize) {
848            let host_ipa_limit = kvm.get_host_ipa_limit();
849            // Here we test with the maximum value that the host supports to both test the
850            // discoverability of supported IPA sizes and likely some other values than 40.
851            kvm.create_vm_with_ipa_size(host_ipa_limit as u32).unwrap();
852            // Test invalid input values
853            // Case 1: IPA size is smaller than 32.
854            kvm.create_vm_with_ipa_size(31).unwrap_err();
855            // Case 2: IPA size is bigger than Host_IPA_Limit.
856            kvm.create_vm_with_ipa_size((host_ipa_limit + 1) as u32)
857                .unwrap_err();
858        } else {
859            // Unsupported, we can't provide an IPA size. Only KVM type=0 works.
860            kvm.create_vm_with_type(0).unwrap_err();
861        }
862    }
863
864    #[cfg(target_arch = "x86_64")]
865    #[test]
866    fn test_get_supported_cpuid() {
867        let kvm = Kvm::new().unwrap();
868        let mut cpuid = kvm.get_supported_cpuid(KVM_MAX_CPUID_ENTRIES).unwrap();
869        let cpuid_entries = cpuid.as_mut_slice();
870        assert!(!cpuid_entries.is_empty());
871        assert!(cpuid_entries.len() <= KVM_MAX_CPUID_ENTRIES);
872
873        // Test case for more than MAX entries
874        let cpuid_err = kvm.get_emulated_cpuid(KVM_MAX_CPUID_ENTRIES + 1_usize);
875        cpuid_err.unwrap_err();
876    }
877
878    #[test]
879    #[cfg(target_arch = "x86_64")]
880    fn test_get_emulated_cpuid() {
881        let kvm = Kvm::new().unwrap();
882        let mut cpuid = kvm.get_emulated_cpuid(KVM_MAX_CPUID_ENTRIES).unwrap();
883        let cpuid_entries = cpuid.as_mut_slice();
884        assert!(!cpuid_entries.is_empty());
885        assert!(cpuid_entries.len() <= KVM_MAX_CPUID_ENTRIES);
886
887        // Test case for more than MAX entries
888        let cpuid_err = kvm.get_emulated_cpuid(KVM_MAX_CPUID_ENTRIES + 1_usize);
889        cpuid_err.unwrap_err();
890    }
891
892    #[cfg(target_arch = "x86_64")]
893    #[test]
894    fn test_cpuid_clone() {
895        let kvm = Kvm::new().unwrap();
896
897        // Test from_raw_fd()
898        let rawfd = unsafe { libc::dup(kvm.as_raw_fd()) };
899        assert!(rawfd >= 0);
900        let kvm = unsafe { Kvm::from_raw_fd(rawfd) };
901
902        let cpuid_1 = kvm.get_supported_cpuid(KVM_MAX_CPUID_ENTRIES).unwrap();
903        let _ = CpuId::new(cpuid_1.as_fam_struct_ref().len()).unwrap();
904    }
905
906    #[test]
907    #[cfg(target_arch = "x86_64")]
908    fn get_msr_index_list() {
909        let kvm = Kvm::new().unwrap();
910        let msr_list = kvm.get_msr_index_list().unwrap();
911        assert!(msr_list.as_slice().len() >= 2);
912    }
913
914    #[test]
915    #[cfg(target_arch = "x86_64")]
916    fn get_msr_feature_index_list() {
917        let kvm = Kvm::new().unwrap();
918        let msr_feature_index_list = kvm.get_msr_feature_index_list().unwrap();
919        assert!(!msr_feature_index_list.as_slice().is_empty());
920    }
921
922    #[test]
923    #[cfg(target_arch = "x86_64")]
924    fn get_msrs() {
925        use kvm_bindings::kvm_msr_entry;
926
927        let kvm = Kvm::new().unwrap();
928        let mut msrs = Msrs::from_entries(&[
929            kvm_msr_entry {
930                index: 0x0000010a, // MSR_IA32_ARCH_CAPABILITIES
931                ..Default::default()
932            },
933            kvm_msr_entry {
934                index: 0x00000345, // MSR_IA32_PERF_CAPABILITIES
935                ..Default::default()
936            },
937        ])
938        .unwrap();
939        let nmsrs = kvm.get_msrs(&mut msrs).unwrap();
940
941        assert_eq!(nmsrs, 2);
942    }
943
944    #[test]
945    fn test_bad_kvm_fd() {
946        let badf_errno = libc::EBADF;
947
948        let faulty_kvm = Kvm {
949            kvm: unsafe { File::from_raw_fd(-2) },
950        };
951
952        assert_eq!(
953            faulty_kvm.get_vcpu_mmap_size().unwrap_err().errno(),
954            badf_errno
955        );
956        assert_eq!(faulty_kvm.get_nr_vcpus(), 4);
957        assert_eq!(faulty_kvm.get_nr_memslots(), 32);
958        #[cfg(target_arch = "x86_64")]
959        {
960            assert_eq!(
961                faulty_kvm.get_emulated_cpuid(4).err().unwrap().errno(),
962                badf_errno
963            );
964            assert_eq!(
965                faulty_kvm.get_supported_cpuid(4).err().unwrap().errno(),
966                badf_errno
967            );
968
969            assert_eq!(
970                faulty_kvm.get_msr_index_list().err().unwrap().errno(),
971                badf_errno
972            );
973        }
974        assert_eq!(faulty_kvm.create_vm().err().unwrap().errno(), badf_errno);
975
976        // Don't drop the File object, or it'll notice the file it's trying to close is
977        // invalid and abort the process.
978        let _ = faulty_kvm.kvm.into_raw_fd();
979    }
980}