From 00412250c3a379ee3689989f0378246ff533e246 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Thu, 21 Feb 2019 13:41:49 +0200 Subject: [PATCH 001/332] initial commit - added empty README.md --- README.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..32362ef --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# kvm-ioctls From e20f2f31aefdeaab621a8d8d448f5ad2429fedb5 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Wed, 27 Feb 2019 19:53:01 +0200 Subject: [PATCH 002/332] init crate Created a dummy crate with version 0.0.1 so we can publish it on crates.io and reserve the kvm-ioctls name. Added Apache-v2.0 License. Signed-off-by: Andreea Florescu --- Cargo.lock | 4 ++ Cargo.toml | 11 +++ LICENSE | 202 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 0 4 files changed, 217 insertions(+) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 LICENSE create mode 100644 src/lib.rs diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..c489f44 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,4 @@ +[[package]] +name = "kvm-ioctls" +version = "0.0.1" + diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..da2ac84 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "kvm-ioctls" +version = "0.0.1" +authors = ["Amazon Firecracker Team "] +description = "Safe wrappers over KVM ioctls" +repository = "https://github.com/rust-vmm/kvm-ioctls" +readme = "README.md" +keywords = ["kvm"] +license = "Apache-2.0" + +[dependencies] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..e69de29 From b64aa55bc5ab19ff0155b7c8b70b1935f0bcc5cf Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Tue, 5 Mar 2019 12:43:48 +0200 Subject: [PATCH 003/332] sys_ioctl: added ioctl macros This is a temporary state until we have the ioctls defines in a separate crate. Signed-off-by: Andreea Florescu --- src/sys_ioctl.rs | 163 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 src/sys_ioctl.rs diff --git a/src/sys_ioctl.rs b/src/sys_ioctl.rs new file mode 100644 index 0000000..ec3b63e --- /dev/null +++ b/src/sys_ioctl.rs @@ -0,0 +1,163 @@ +#![allow(dead_code)] +// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +// +// Portions Copyright 2017 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the THIRD-PARTY file. + +//! Macros and wrapper functions for dealing with ioctls. +use libc; +use std::os::raw::{c_int, c_uint, c_ulong, c_void}; +use std::os::unix::io::AsRawFd; + +/// Raw macro to declare a function that returns an ioctl number. +#[macro_export] +macro_rules! ioctl_ioc_nr { + ($name:ident, $dir:expr, $ty:expr, $nr:expr, $size:expr) => { + #[allow(non_snake_case)] + pub fn $name() -> ::std::os::raw::c_ulong { + u64::from( + ($dir << $crate::sys_ioctl::_IOC_DIRSHIFT) + | ($ty << $crate::sys_ioctl::_IOC_TYPESHIFT) + | ($nr << $crate::sys_ioctl::_IOC_NRSHIFT) + | ($size << $crate::sys_ioctl::_IOC_SIZESHIFT), + ) + } + }; +} + +/// Declare an ioctl that transfers no data. +#[macro_export] +macro_rules! ioctl_io_nr { + ($name:ident, $ty:expr, $nr:expr) => { + ioctl_ioc_nr!($name, $crate::sys_ioctl::_IOC_NONE, $ty, $nr, 0); + }; +} + +/// Declare an ioctl that reads data. +#[macro_export] +macro_rules! ioctl_ior_nr { + ($name:ident, $ty:expr, $nr:expr, $size:ty) => { + ioctl_ioc_nr!( + $name, + $crate::sys_ioctl::_IOC_READ, + $ty, + $nr, + ::std::mem::size_of::<$size>() as u32 + ); + }; +} + +/// Declare an ioctl that writes data. +#[macro_export] +macro_rules! ioctl_iow_nr { + ($name:ident, $ty:expr, $nr:expr, $size:ty) => { + ioctl_ioc_nr!( + $name, + $crate::sys_ioctl::_IOC_WRITE, + $ty, + $nr, + ::std::mem::size_of::<$size>() as u32 + ); + }; +} + +/// Declare an ioctl that reads and writes data. +#[macro_export] +macro_rules! ioctl_iowr_nr { + ($name:ident, $ty:expr, $nr:expr, $size:ty) => { + ioctl_ioc_nr!( + $name, + $crate::sys_ioctl::_IOC_READ | $crate::sys_ioctl::_IOC_WRITE, + $ty, + $nr, + ::std::mem::size_of::<$size>() as u32 + ); + }; +} + +pub const _IOC_NRBITS: c_uint = 8; +pub const _IOC_TYPEBITS: c_uint = 8; +pub const _IOC_SIZEBITS: c_uint = 14; +pub const _IOC_DIRBITS: c_uint = 2; +pub const _IOC_NRMASK: c_uint = 255; +pub const _IOC_TYPEMASK: c_uint = 255; +pub const _IOC_SIZEMASK: c_uint = 16383; +pub const _IOC_DIRMASK: c_uint = 3; +pub const _IOC_NRSHIFT: c_uint = 0; +pub const _IOC_TYPESHIFT: c_uint = 8; +pub const _IOC_SIZESHIFT: c_uint = 16; +pub const _IOC_DIRSHIFT: c_uint = 30; +pub const _IOC_NONE: c_uint = 0; +pub const _IOC_WRITE: c_uint = 1; +pub const _IOC_READ: c_uint = 2; +pub const IOC_IN: c_uint = 1_073_741_824; +pub const IOC_OUT: c_uint = 2_147_483_648; +pub const IOC_INOUT: c_uint = 3_221_225_472; +pub const IOCSIZE_MASK: c_uint = 1_073_676_288; +pub const IOCSIZE_SHIFT: c_uint = 16; + +// The type of the `req` parameter is different for the `musl` library. This will enable +// successful build for other non-musl libraries. +#[cfg(target_env = "musl")] +type IoctlRequest = c_int; +#[cfg(not(target_env = "musl"))] +type IoctlRequest = c_ulong; + +/// Run an ioctl with no arguments. +pub unsafe fn ioctl(fd: &F, req: c_ulong) -> c_int { + libc::ioctl(fd.as_raw_fd(), req as IoctlRequest, 0) +} + +/// Run an ioctl with a single value argument. +pub unsafe fn ioctl_with_val(fd: &F, req: c_ulong, arg: c_ulong) -> c_int { + libc::ioctl(fd.as_raw_fd(), req as IoctlRequest, arg) +} + +/// Run an ioctl with an immutable reference. +pub unsafe fn ioctl_with_ref(fd: &F, req: c_ulong, arg: &T) -> c_int { + libc::ioctl( + fd.as_raw_fd(), + req as IoctlRequest, + arg as *const T as *const c_void, + ) +} + +/// Run an ioctl with a mutable reference. +pub unsafe fn ioctl_with_mut_ref(fd: &F, req: c_ulong, arg: &mut T) -> c_int { + libc::ioctl( + fd.as_raw_fd(), + req as IoctlRequest, + arg as *mut T as *mut c_void, + ) +} + +/// Run an ioctl with a raw pointer. +pub unsafe fn ioctl_with_ptr(fd: &F, req: c_ulong, arg: *const T) -> c_int { + libc::ioctl(fd.as_raw_fd(), req as IoctlRequest, arg as *const c_void) +} + +/// Run an ioctl with a mutable raw pointer. +pub unsafe fn ioctl_with_mut_ptr(fd: &F, req: c_ulong, arg: *mut T) -> c_int { + libc::ioctl(fd.as_raw_fd(), req as IoctlRequest, arg as *mut c_void) +} + +#[cfg(test)] +mod tests { + const TUNTAP: ::std::os::raw::c_uint = 0x54; + const KVMIO: ::std::os::raw::c_uint = 0xAE; + + ioctl_io_nr!(KVM_CREATE_VM, KVMIO, 0x01); + ioctl_ior_nr!(TUNGETFEATURES, TUNTAP, 0xcf, ::std::os::raw::c_uint); + ioctl_iow_nr!(TUNSETQUEUE, TUNTAP, 0xd9, ::std::os::raw::c_int); + ioctl_iowr_nr!(KVM_GET_MSR_INDEX_LIST, KVMIO, 0x2, ::std::os::raw::c_int); + + #[test] + fn ioctl_macros() { + assert_eq!(0x0000AE01, KVM_CREATE_VM()); + assert_eq!(0x800454CF, TUNGETFEATURES()); + assert_eq!(0x400454D9, TUNSETQUEUE()); + assert_eq!(0xC004AE02, KVM_GET_MSR_INDEX_LIST()); + } +} From 2723b09564c64f7b81ecefcab7bb7f3d3edee740 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Tue, 5 Mar 2019 12:46:59 +0200 Subject: [PATCH 004/332] added kvm ioctls Added the list of kvm specific ioctls. Signed-off-by: Andreea Florescu --- src/kvm_ioctls.rs | 124 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 src/kvm_ioctls.rs diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs new file mode 100644 index 0000000..d9009d3 --- /dev/null +++ b/src/kvm_ioctls.rs @@ -0,0 +1,124 @@ +// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +// +// Portions Copyright 2017 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the THIRD-PARTY file. + +use super::sys_ioctl::*; +use kvm_bindings::*; + +// Declares necessary ioctls specific to their platform. + +ioctl_io_nr!(KVM_GET_API_VERSION, KVMIO, 0x00); +ioctl_io_nr!(KVM_CREATE_VM, KVMIO, 0x01); +ioctl_io_nr!(KVM_CHECK_EXTENSION, KVMIO, 0x03); +ioctl_io_nr!(KVM_GET_VCPU_MMAP_SIZE, KVMIO, 0x04); +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_iowr_nr!(KVM_GET_SUPPORTED_CPUID, KVMIO, 0x05, kvm_cpuid2); +ioctl_io_nr!(KVM_CREATE_VCPU, KVMIO, 0x41); +ioctl_iow_nr!(KVM_GET_DIRTY_LOG, KVMIO, 0x42, kvm_dirty_log); +ioctl_iow_nr!( + KVM_SET_USER_MEMORY_REGION, + KVMIO, + 0x46, + kvm_userspace_memory_region +); +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_io_nr!(KVM_SET_TSS_ADDR, KVMIO, 0x47); +#[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64", + target_arch = "s390" +))] +ioctl_io_nr!(KVM_CREATE_IRQCHIP, KVMIO, 0x60); +#[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64", + target_arch = "s390" +))] +ioctl_iow_nr!(KVM_IRQFD, KVMIO, 0x76, kvm_irqfd); +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_iow_nr!(KVM_CREATE_PIT2, KVMIO, 0x77, kvm_pit_config); +ioctl_iow_nr!(KVM_IOEVENTFD, KVMIO, 0x79, kvm_ioeventfd); +ioctl_io_nr!(KVM_RUN, KVMIO, 0x80); +#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] +ioctl_ior_nr!(KVM_GET_REGS, KVMIO, 0x81, kvm_regs); +#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] +ioctl_iow_nr!(KVM_SET_REGS, KVMIO, 0x82, kvm_regs); +#[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "powerpc", + target_arch = "powerpc64" +))] +ioctl_ior_nr!(KVM_GET_SREGS, KVMIO, 0x83, kvm_sregs); +#[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "powerpc", + target_arch = "powerpc64" +))] +ioctl_iow_nr!(KVM_SET_SREGS, KVMIO, 0x84, kvm_sregs); +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_iowr_nr!(KVM_GET_MSRS, KVMIO, 0x88, kvm_msrs); +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_iow_nr!(KVM_SET_MSRS, KVMIO, 0x89, kvm_msrs); +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_iow_nr!(KVM_SET_CPUID2, KVMIO, 0x90, kvm_cpuid2); +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_ior_nr!(KVM_GET_FPU, KVMIO, 0x8c, kvm_fpu); +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_iow_nr!(KVM_SET_FPU, KVMIO, 0x8d, kvm_fpu); +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_ior_nr!(KVM_GET_LAPIC, KVMIO, 0x8e, kvm_lapic_state); +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_iow_nr!(KVM_SET_LAPIC, KVMIO, 0x8f, kvm_lapic_state); + +#[cfg(test)] +mod tests { + use std::fs::File; + use std::os::unix::io::FromRawFd; + + use libc::{c_char, open, O_RDWR}; + + use super::*; + const KVM_PATH: &'static str = "/dev/kvm\0"; + + #[test] + fn get_version() { + let sys_fd = unsafe { open(KVM_PATH.as_ptr() as *const c_char, O_RDWR) }; + assert!(sys_fd >= 0); + + let ret = unsafe { ioctl(&File::from_raw_fd(sys_fd), KVM_GET_API_VERSION()) }; + assert_eq!(ret as u32, KVM_API_VERSION); + } + + #[test] + fn create_vm_fd() { + let sys_fd = unsafe { open(KVM_PATH.as_ptr() as *const c_char, O_RDWR) }; + assert!(sys_fd >= 0); + + let vm_fd = unsafe { ioctl(&File::from_raw_fd(sys_fd), KVM_CREATE_VM()) }; + assert!(vm_fd >= 0); + } + + #[test] + fn check_vm_extension() { + let sys_fd = unsafe { open(KVM_PATH.as_ptr() as *const c_char, O_RDWR) }; + assert!(sys_fd >= 0); + + let has_user_memory = unsafe { + ioctl_with_val( + &File::from_raw_fd(sys_fd), + KVM_CHECK_EXTENSION(), + KVM_CAP_USER_MEMORY.into(), + ) + }; + assert_eq!(has_user_memory, 1); + } +} From 1db993ebdb457ca3f46827bffa945934d61b80f3 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Tue, 5 Mar 2019 12:49:46 +0200 Subject: [PATCH 005/332] added wrapper over KVM capabilities The capabilities defined in kvm-bindings are re-exported as part of the Cap enum so they can be easily passed as function parameters. Signed-off-by: Andreea Florescu --- src/cap.rs | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/cap.rs diff --git a/src/cap.rs b/src/cap.rs new file mode 100644 index 0000000..7b38ef3 --- /dev/null +++ b/src/cap.rs @@ -0,0 +1,128 @@ +// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +// +// Portions Copyright 2017 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the THIRD-PARTY file. + +use kvm_bindings::*; + +/// A capability the kernel's KVM interface can possibly expose. +#[derive(Clone, Copy, Debug)] +#[repr(u32)] +// We are allowing docs to be missing here because this enum is a wrapper +// over auto-generated code. +#[allow(missing_docs)] +pub enum Cap { + Irqchip = KVM_CAP_IRQCHIP, + Hlt = KVM_CAP_HLT, + MmuShadowCacheControl = KVM_CAP_MMU_SHADOW_CACHE_CONTROL, + UserMemory = KVM_CAP_USER_MEMORY, + SetTssAddr = KVM_CAP_SET_TSS_ADDR, + Vapic = KVM_CAP_VAPIC, + ExtCpuid = KVM_CAP_EXT_CPUID, + Clocksource = KVM_CAP_CLOCKSOURCE, + NrVcpus = KVM_CAP_NR_VCPUS, + NrMemslots = KVM_CAP_NR_MEMSLOTS, + Pit = KVM_CAP_PIT, + NopIoDelay = KVM_CAP_NOP_IO_DELAY, + PvMmu = KVM_CAP_PV_MMU, + MpState = KVM_CAP_MP_STATE, + CoalescedMmio = KVM_CAP_COALESCED_MMIO, + SyncMmu = KVM_CAP_SYNC_MMU, + Iommu = KVM_CAP_IOMMU, + DestroyMemoryRegionWorks = KVM_CAP_DESTROY_MEMORY_REGION_WORKS, + UserNmi = KVM_CAP_USER_NMI, + SetGuestDebug = KVM_CAP_SET_GUEST_DEBUG, + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + ReinjectControl = KVM_CAP_REINJECT_CONTROL, + IrqRouting = KVM_CAP_IRQ_ROUTING, + IrqInjectStatus = KVM_CAP_IRQ_INJECT_STATUS, + AssignDevIrq = KVM_CAP_ASSIGN_DEV_IRQ, + JoinMemoryRegionsWorks = KVM_CAP_JOIN_MEMORY_REGIONS_WORKS, + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + Mce = KVM_CAP_MCE, + Irqfd = KVM_CAP_IRQFD, + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + Pit2 = KVM_CAP_PIT2, + SetBootCpuId = KVM_CAP_SET_BOOT_CPU_ID, + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + PitState2 = KVM_CAP_PIT_STATE2, + Ioeventfd = KVM_CAP_IOEVENTFD, + SetIdentityMapAddr = KVM_CAP_SET_IDENTITY_MAP_ADDR, + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + XenHvm = KVM_CAP_XEN_HVM, + AdjustClock = KVM_CAP_ADJUST_CLOCK, + InternalErrorData = KVM_CAP_INTERNAL_ERROR_DATA, + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + VcpuEvents = KVM_CAP_VCPU_EVENTS, + S390Psw = KVM_CAP_S390_PSW, + PpcSegstate = KVM_CAP_PPC_SEGSTATE, + Hyperv = KVM_CAP_HYPERV, + HypervVapic = KVM_CAP_HYPERV_VAPIC, + HypervSpin = KVM_CAP_HYPERV_SPIN, + PciSegment = KVM_CAP_PCI_SEGMENT, + PpcPairedSingles = KVM_CAP_PPC_PAIRED_SINGLES, + IntrShadow = KVM_CAP_INTR_SHADOW, + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + Debugregs = KVM_CAP_DEBUGREGS, + X86RobustSinglestep = KVM_CAP_X86_ROBUST_SINGLESTEP, + PpcOsi = KVM_CAP_PPC_OSI, + PpcUnsetIrq = KVM_CAP_PPC_UNSET_IRQ, + EnableCap = KVM_CAP_ENABLE_CAP, + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + Xsave = KVM_CAP_XSAVE, + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + Xcrs = KVM_CAP_XCRS, + PpcGetPvinfo = KVM_CAP_PPC_GET_PVINFO, + PpcIrqLevel = KVM_CAP_PPC_IRQ_LEVEL, + AsyncPf = KVM_CAP_ASYNC_PF, + TscControl = KVM_CAP_TSC_CONTROL, + GetTscKhz = KVM_CAP_GET_TSC_KHZ, + PpcBookeSregs = KVM_CAP_PPC_BOOKE_SREGS, + SpaprTce = KVM_CAP_SPAPR_TCE, + PpcSmt = KVM_CAP_PPC_SMT, + PpcRma = KVM_CAP_PPC_RMA, + MaxVcpus = KVM_CAP_MAX_VCPUS, + PpcHior = KVM_CAP_PPC_HIOR, + PpcPapr = KVM_CAP_PPC_PAPR, + SwTlb = KVM_CAP_SW_TLB, + OneReg = KVM_CAP_ONE_REG, + S390Gmap = KVM_CAP_S390_GMAP, + TscDeadlineTimer = KVM_CAP_TSC_DEADLINE_TIMER, + S390Ucontrol = KVM_CAP_S390_UCONTROL, + SyncRegs = KVM_CAP_SYNC_REGS, + Pci23 = KVM_CAP_PCI_2_3, + KvmclockCtrl = KVM_CAP_KVMCLOCK_CTRL, + SignalMsi = KVM_CAP_SIGNAL_MSI, + PpcGetSmmuInfo = KVM_CAP_PPC_GET_SMMU_INFO, + S390Cow = KVM_CAP_S390_COW, + PpcAllocHtab = KVM_CAP_PPC_ALLOC_HTAB, + ReadonlyMem = KVM_CAP_READONLY_MEM, + IrqfdResample = KVM_CAP_IRQFD_RESAMPLE, + PpcBookeWatchdog = KVM_CAP_PPC_BOOKE_WATCHDOG, + PpcHtabFd = KVM_CAP_PPC_HTAB_FD, + S390CssSupport = KVM_CAP_S390_CSS_SUPPORT, + PpcEpr = KVM_CAP_PPC_EPR, + ArmPsci = KVM_CAP_ARM_PSCI, + ArmSetDeviceAddr = KVM_CAP_ARM_SET_DEVICE_ADDR, + DeviceCtrl = KVM_CAP_DEVICE_CTRL, + IrqMpic = KVM_CAP_IRQ_MPIC, + PpcRtas = KVM_CAP_PPC_RTAS, + IrqXics = KVM_CAP_IRQ_XICS, + ArmEl132bit = KVM_CAP_ARM_EL1_32BIT, + SpaprMultitce = KVM_CAP_SPAPR_MULTITCE, + ExtEmulCpuid = KVM_CAP_EXT_EMUL_CPUID, + HypervTime = KVM_CAP_HYPERV_TIME, + IoapicPolarityIgnored = KVM_CAP_IOAPIC_POLARITY_IGNORED, + EnableCapVm = KVM_CAP_ENABLE_CAP_VM, + S390Irqchip = KVM_CAP_S390_IRQCHIP, + IoeventfdNoLength = KVM_CAP_IOEVENTFD_NO_LENGTH, + VmAttributes = KVM_CAP_VM_ATTRIBUTES, + ArmPsci02 = KVM_CAP_ARM_PSCI_0_2, + PpcFixupHcall = KVM_CAP_PPC_FIXUP_HCALL, + PpcEnableHcall = KVM_CAP_PPC_ENABLE_HCALL, + CheckExtensionVm = KVM_CAP_CHECK_EXTENSION_VM, + S390UserSigp = KVM_CAP_S390_USER_SIGP, + ImmediateExit = KVM_CAP_IMMEDIATE_EXIT, +} From 001126007319ad5ed490aa5a8b0be2791e05e498 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Tue, 5 Mar 2019 12:51:58 +0200 Subject: [PATCH 006/332] added wrappers over KVM system ioctls Created a Kvm structure to wrap system ioctls. Signed-off-by: Andreea Florescu --- Cargo.lock | 17 +++ Cargo.toml | 2 + src/lib.rs | 401 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 420 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index c489f44..62cbacc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,4 +1,21 @@ +[[package]] +name = "kvm-bindings" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "kvm-ioctls" version = "0.0.1" +dependencies = [ + "kvm-bindings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libc" +version = "0.2.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +[metadata] +"checksum kvm-bindings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c223e8703d2eb76d990c5f58e29c85b0f6f50e24b823babde927948e7c71fc03" +"checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047" diff --git a/Cargo.toml b/Cargo.toml index da2ac84..a5a7939 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,5 @@ keywords = ["kvm"] license = "Apache-2.0" [dependencies] +libc = ">=0.2.39" +kvm-bindings = ">=0.1.0" diff --git a/src/lib.rs b/src/lib.rs index e69de29..e908ea9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -0,0 +1,401 @@ +// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +// +// Portions Copyright 2017 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the THIRD-PARTY file. +#![allow(unused)] +#![deny(missing_docs)] + +//! A safe wrapper around the kernel's KVM interface. + +extern crate kvm_bindings; +extern crate libc; + +#[macro_use] +mod sys_ioctl; +#[macro_use] +mod kvm_ioctls; +mod cap; + +use kvm_bindings::*; +use libc::{open, O_CLOEXEC, O_RDWR}; +use std::fs::File; +use std::mem::size_of; +use std::os::raw::{c_char, c_ulong}; +use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; +use std::{io, result}; + +pub use self::cap::Cap; +use self::kvm_ioctls::*; +use self::sys_ioctl::*; + +/// Wrapper over possible Kvm Result. +pub type Result = result::Result; + +/// Taken from Linux Kernel v4.14.13 (arch/x86/include/asm/kvm_host.h) +pub const MAX_KVM_CPUID_ENTRIES: usize = 80; + +// Returns a `Vec` with a size in bytes at least as large as `size_in_bytes`. +fn vec_with_size_in_bytes(size_in_bytes: usize) -> Vec { + let rounded_size = (size_in_bytes + size_of::() - 1) / size_of::(); + let mut v = Vec::with_capacity(rounded_size); + for _ in 0..rounded_size { + v.push(T::default()) + } + v +} + +// The kvm API has many structs that resemble the following `Foo` structure: +// +// ``` +// #[repr(C)] +// struct Foo { +// some_data: u32 +// entries: __IncompleteArrayField<__u32>, +// } +// ``` +// +// In order to allocate such a structure, `size_of::()` would be too small because it would not +// include any space for `entries`. To make the allocation large enough while still being aligned +// for `Foo`, a `Vec` is created. Only the first element of `Vec` would actually be used +// as a `Foo`. The remaining memory in the `Vec` is for `entries`, which must be contiguous +// with `Foo`. This function is used to make the `Vec` with enough space for `count` entries. +fn vec_with_array_field(count: usize) -> Vec { + let element_space = count * size_of::(); + let vec_size_bytes = size_of::() + element_space; + vec_with_size_in_bytes(vec_size_bytes) +} + +/// A wrapper around opening and using `/dev/kvm`. +/// +/// The handle is used to issue system ioctls. +pub struct Kvm { + kvm: File, +} + +impl Kvm { + /// Opens `/dev/kvm/` and returns a `Kvm` object on success. + /// + #[allow(clippy::new_ret_no_self)] + pub fn new() -> Result { + // Open `/dev/kvm` using `O_CLOEXEC` flag. + let fd = Self::open_with_cloexec(true)?; + // Safe because we verify that ret is valid and we own the fd. + Ok(unsafe { Self::new_with_fd_number(fd) }) + } + + /// Creates a new Kvm object assuming `fd` represents an existing open file descriptor + /// associated with `/dev/kvm`. + /// + /// # Arguments + /// + /// * `fd` - File descriptor for `/dev/kvm`. + /// + pub unsafe fn new_with_fd_number(fd: RawFd) -> Self { + Kvm { + kvm: File::from_raw_fd(fd), + } + } + + /// Opens `/dev/kvm` and returns the fd number on success. + /// + /// # Arguments + /// + /// * `close_on_exec`: If true opens `/dev/kvm` using the `O_CLOEXEC` flag. + /// + pub fn open_with_cloexec(close_on_exec: bool) -> Result { + let open_flags = O_RDWR | if close_on_exec { O_CLOEXEC } else { 0 }; + // Safe because we give a constant nul-terminated string and verify the result. + let ret = unsafe { open("/dev/kvm\0".as_ptr() as *const c_char, open_flags) }; + if ret < 0 { + Err(io::Error::last_os_error()) + } else { + Ok(ret) + } + } + + /// Returns the KVM API version. + pub fn get_api_version(&self) -> i32 { + // Safe because we know that our file is a KVM fd and that the request is one of the ones + // defined by kernel. + unsafe { ioctl(self, KVM_GET_API_VERSION()) } + } + + /// Query the availability of a particular kvm capability. + /// Returns 0 if the capability is not available and > 0 otherwise. + /// + fn check_extension_int(&self, c: Cap) -> i32 { + // Safe because we know that our file is a KVM fd and that the extension is one of the ones + // defined by kernel. + unsafe { ioctl_with_val(self, KVM_CHECK_EXTENSION(), c as c_ulong) } + } + + /// Checks if a particular `Cap` is available. + /// + /// According to the KVM API doc, KVM_CHECK_EXTENSION returns "0 if unsupported; 1 (or some + /// other positive integer) if supported". + /// + /// # Arguments + /// + /// * `c` - KVM capability. + /// + pub fn check_extension(&self, c: Cap) -> bool { + self.check_extension_int(c) >= 1 + } + + /// Gets the size of the mmap required to use vcpu's `kvm_run` structure. + pub fn get_vcpu_mmap_size(&self) -> Result { + // Safe because we know that our file is a KVM fd and we verify the return result. + let res = unsafe { ioctl(self, KVM_GET_VCPU_MMAP_SIZE()) }; + if res > 0 { + Ok(res as usize) + } else { + Err(io::Error::last_os_error()) + } + } + + /// Gets the recommended number of VCPUs per VM. + /// + pub fn get_nr_vcpus(&self) -> usize { + let x = self.check_extension_int(Cap::NrVcpus); + if x > 0 { + x as usize + } else { + 4 + } + } + + /// Gets the maximum allowed memory slots per VM. + /// + /// KVM reports the number of available memory slots (`KVM_CAP_NR_MEMSLOTS`) + /// using the extension interface. Both x86 and s390 implement this, ARM + /// and powerpc do not yet enable it. + /// Default to 32 when `KVM_CAP_NR_MEMSLOTS` is not implemented. + /// + pub fn get_nr_memslots(&self) -> usize { + let x = self.check_extension_int(Cap::NrMemslots); + if x > 0 { + x as usize + } else { + 32 + } + } + + /// Gets the recommended maximum number of VCPUs per VM. + /// + pub fn get_max_vcpus(&self) -> usize { + match self.check_extension_int(Cap::MaxVcpus) { + 0 => self.get_nr_vcpus(), + x => x as usize, + } + } + + /// X86 specific call to get the system supported CPUID values. + /// + /// # Arguments + /// + /// * `max_entries_count` - Maximum number of CPUID entries. This function can return less than + /// this when the hardware does not support so many CPUID entries. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn get_supported_cpuid(&self, max_entries_count: usize) -> Result { + let mut cpuid = CpuId::new(max_entries_count); + + let ret = unsafe { + // ioctl is unsafe. The kernel is trusted not to write beyond the bounds of the memory + // allocated for the struct. The limit is read from nent, which is set to the allocated + // size(max_entries_count) above. + ioctl_with_mut_ptr(self, KVM_GET_SUPPORTED_CPUID(), cpuid.as_mut_ptr()) + }; + if ret < 0 { + return Err(io::Error::last_os_error()); + } + + Ok(cpuid) + } + + /// Creates a VM fd using the KVM fd (`KVM_CREATE_VM`). + /// + /// A call to this function will also initialize the size of the vcpu mmap area + /// (`KVM_GET_VCPU_MMAP_SIZE`). + /// + pub fn create_vm(&self) -> Result { + // Safe because we know kvm is a real kvm fd as this module is the only one that can make + // Kvm objects. + let ret = unsafe { ioctl(&self.kvm, KVM_CREATE_VM()) }; + if ret >= 0 { + // Safe because we verify the value of ret and we are the owners of the fd. + let vm_file = unsafe { File::from_raw_fd(ret) }; + let run_mmap_size = self.get_vcpu_mmap_size()?; + Ok(VmFd { + vm: vm_file, + run_size: run_mmap_size, + }) + } else { + Err(io::Error::last_os_error()) + } + } +} + +impl AsRawFd for Kvm { + fn as_raw_fd(&self) -> RawFd { + self.kvm.as_raw_fd() + } +} + +/// A wrapper around creating and using a VM. +pub struct VmFd { + vm: File, + run_size: usize, +} + +/// Wrapper for `kvm_cpuid2` which has a zero length array at the end. +/// Hides the zero length array behind a bounds check. +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +pub struct CpuId { + /// Wrapper over `kvm_cpuid2` from which we only use the first element. + kvm_cpuid: Vec, + // Number of `kvm_cpuid_entry2` structs at the end of kvm_cpuid2. + allocated_len: usize, +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +impl Clone for CpuId { + fn clone(&self) -> Self { + let mut kvm_cpuid = Vec::with_capacity(self.kvm_cpuid.len()); + for _ in 0..self.kvm_cpuid.len() { + kvm_cpuid.push(kvm_cpuid2::default()); + } + + let num_bytes = self.kvm_cpuid.len() * size_of::(); + + let src_byte_slice = + unsafe { std::slice::from_raw_parts(self.kvm_cpuid.as_ptr() as *const u8, num_bytes) }; + + let dst_byte_slice = + unsafe { std::slice::from_raw_parts_mut(kvm_cpuid.as_mut_ptr() as *mut u8, num_bytes) }; + + dst_byte_slice.copy_from_slice(src_byte_slice); + + CpuId { + kvm_cpuid, + allocated_len: self.allocated_len, + } + } +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +impl CpuId { + /// Creates a new `CpuId` structure that can contain at most `array_len` KVM CPUID entries. + /// + /// # Arguments + /// + /// * `array_len` - Maximum number of CPUID entries. + /// + pub fn new(array_len: usize) -> CpuId { + let mut kvm_cpuid = vec_with_array_field::(array_len); + kvm_cpuid[0].nent = array_len as u32; + + CpuId { + kvm_cpuid, + allocated_len: array_len, + } + } + + /// Get the mutable entries slice so they can be modified before passing to the VCPU. + /// + pub fn mut_entries_slice(&mut self) -> &mut [kvm_cpuid_entry2] { + // Mapping the unsized array to a slice is unsafe because the length isn't known. Using + // the length we originally allocated with eliminates the possibility of overflow. + if self.kvm_cpuid[0].nent as usize > self.allocated_len { + self.kvm_cpuid[0].nent = self.allocated_len as u32; + } + let nent = self.kvm_cpuid[0].nent as usize; + unsafe { self.kvm_cpuid[0].entries.as_mut_slice(nent) } + } + + /// Get a pointer so it can be passed to the kernel. Using this pointer is unsafe. + /// + pub fn as_ptr(&self) -> *const kvm_cpuid2 { + &self.kvm_cpuid[0] + } + + /// Get a mutable pointer so it can be passed to the kernel. Using this pointer is unsafe. + /// + pub fn as_mut_ptr(&mut self) -> *mut kvm_cpuid2 { + &mut self.kvm_cpuid[0] + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + impl PartialEq for CpuId { + fn eq(&self, other: &CpuId) -> bool { + let entries: &[kvm_cpuid_entry2] = + unsafe { self.kvm_cpuid[0].entries.as_slice(self.allocated_len) }; + let other_entries: &[kvm_cpuid_entry2] = + unsafe { self.kvm_cpuid[0].entries.as_slice(other.allocated_len) }; + self.allocated_len == other.allocated_len && entries == other_entries + } + } + + #[test] + fn test_kvm_new() { + Kvm::new().unwrap(); + } + + #[test] + fn test_kvm_api_version() { + let kvm = Kvm::new().unwrap(); + assert_eq!(kvm.get_api_version(), 12); + assert!(kvm.check_extension(Cap::UserMemory)); + } + + #[test] + fn test_kvm_getters() { + let kvm = Kvm::new().unwrap(); + + // vCPU related getters + let nr_vcpus = kvm.get_nr_vcpus(); + assert!(nr_vcpus >= 4); + + assert!(kvm.get_max_vcpus() >= nr_vcpus); + + // Memory related getters + assert!(kvm.get_vcpu_mmap_size().unwrap() > 0); + assert!(kvm.get_nr_memslots() >= 32); + } + + #[test] + fn test_create_vm() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + + assert_eq!(vm.run_size, kvm.get_vcpu_mmap_size().unwrap()); + } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[test] + fn test_get_supported_cpuid() { + let kvm = Kvm::new().unwrap(); + let mut cpuid = kvm.get_supported_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); + let cpuid_entries = cpuid.mut_entries_slice(); + assert!(cpuid_entries.len() > 0); + assert!(cpuid_entries.len() <= MAX_KVM_CPUID_ENTRIES); + } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[test] + fn test_cpuid_clone() { + let kvm = Kvm::new().unwrap(); + let cpuid_1 = kvm.get_supported_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); + let mut cpuid_2 = cpuid_1.clone(); + assert!(cpuid_1 == cpuid_2); + cpuid_2 = unsafe { std::mem::zeroed() }; + assert!(cpuid_1 != cpuid_2); + } +} From 194a411764e901694e5d1a2b39bbd8e549b39f3d Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Tue, 5 Mar 2019 12:54:10 +0200 Subject: [PATCH 007/332] added integration tests The integration tests are implemented on top of pytest. We cannot used Travis (at least not out of the box) because we need a bare metal instance that has access to /dev/kvm. Signed-off-by: Andreea Florescu --- README.md | 40 ++++++++++++++++ tests/coverage | 1 + tests/test_build.py | 23 ++++++++++ tests/test_correctness.py | 19 ++++++++ tests/test_coverage.py | 97 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 180 insertions(+) create mode 100644 tests/coverage create mode 100644 tests/test_build.py create mode 100644 tests/test_correctness.py create mode 100644 tests/test_coverage.py diff --git a/README.md b/README.md index 32362ef..167a785 100644 --- a/README.md +++ b/README.md @@ -1 +1,41 @@ # kvm-ioctls + +TODO + +## Running the tests + +### Dependencies +**[NOTE]** This is a temporary state of affairs; Most likely we will manage +the dependencies using a container. +- python >= 3.5 +- pytest +- kcov +- cargo kcov +- cargo fmt +- cargo clippy +- target x86_64-unknown-linux-musl + +``` +$ pytest tests/ -vv +============================= test session starts ============================== +platform linux -- Python 3.6.8, pytest-3.8.0, py-1.6.0, pluggy-0.7.1 -- /usr/bin/python3.6 +cachedir: .pytest_cache +rootdir: /home/local/ANT/fandree/sources/work/rust-vmm/kvm-ioctls, inifile: +collected 6 items +tests/test_build.py::test_build PASSED [ 16%] +tests/test_build.py::test_build_musl PASSED [ 33%] +tests/test_correctness.py::test_style PASSED [ 50%] +tests/test_correctness.py::test_clippy PASSED [ 66%] +tests/test_correctness.py::test_unittests PASSED [ 83%] +tests/test_coverage.py::test_coverage PASSED [100%] + +=========================== 6 passed in 7.08 seconds =========================== +``` + +### Adaptive Coverage + +The line coverage is saved in [tests/coverage](tests/coverage). To update the +coverage before submitting a PR, run the coverage test: +```bash +pytest tests/test_coverage.py +``` diff --git a/tests/coverage b/tests/coverage new file mode 100644 index 0000000..ee0cc0c --- /dev/null +++ b/tests/coverage @@ -0,0 +1 @@ +80.1 \ No newline at end of file diff --git a/tests/test_build.py b/tests/test_build.py new file mode 100644 index 0000000..8acb0ef --- /dev/null +++ b/tests/test_build.py @@ -0,0 +1,23 @@ +# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +import subprocess + + +def test_build(): + """Test release build using the default gnu target.""" + subprocess.run(['cargo', 'build', '--release'], check=True) + + +def test_build_musl(): + """Test release build using the musl target.""" + subprocess.run( + [ + 'cargo', + 'build', + '--release', + '--target', + 'x86_64-unknown-linux-musl' + ], + check=True + ) diff --git a/tests/test_correctness.py b/tests/test_correctness.py new file mode 100644 index 0000000..9bbbdfb --- /dev/null +++ b/tests/test_correctness.py @@ -0,0 +1,19 @@ +# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test the correctness of the code using linters and unit tests.""" + +import subprocess + +def test_style(): + """Test rust style using `cargo fmt`.""" + subprocess.run(['cargo', 'fmt', '--all', '--', '--check'], check=True) + + +def test_clippy(): + """Run clippy.""" + subprocess.run(['cargo', 'clippy'], check=True) + + +def test_unittests(): + """Run unit tests.""" + subprocess.run(['cargo', 'test', '--all'], check=True) diff --git a/tests/test_coverage.py b/tests/test_coverage.py new file mode 100644 index 0000000..a08ee27 --- /dev/null +++ b/tests/test_coverage.py @@ -0,0 +1,97 @@ +# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test the coverage and update the threshold when coverage is increased.""" + +import os, re, shutil, subprocess + +def _get_current_coverage(): + """Helper function that returns the coverage computed with kcov.""" + kcov_ouput_dir = os.path.join( + os.path.dirname(os.path.realpath(__file__)), + "kcov_output" + ) + + # By default the build output for kcov and unit tests are both in the debug + # directory. This causes some linker errors that I haven't investigated. + # Error: error: linking with `cc` failed: exit code: 1 + # An easy fix is to have separate build directories for kcov & unit tests. + kcov_build_dir = os.path.join( + os.path.dirname(os.path.realpath(__file__)), + "kcov_build" + ) + + # Remove kcov output and build directory to be sure we are always working + # on a clean environment. + shutil.rmtree(kcov_ouput_dir, ignore_errors=True) + shutil.rmtree(kcov_build_dir, ignore_errors=True) + + exclude_pattern = ( + '${CARGO_HOME:-$HOME/.cargo/},' + 'usr/lib/,' + 'lib/' + ) + exclude_region = "'mod tests {'" + + kcov_cmd = "CARGO_TARGET_DIR={} cargo kcov --all " \ + "--output {} -- " \ + "--exclude-region={} " \ + "--exclude-pattern={} " \ + "--verify".format( + kcov_build_dir, + kcov_ouput_dir, + exclude_region, + exclude_pattern + ) + + subprocess.run(kcov_cmd, shell=True, check=True) + + # Read the coverage reported by kcov. + coverage_file = os.path.join(kcov_ouput_dir, 'index.json') + with open(coverage_file) as cov_output: + coverage = float(re.findall( + r'"covered":"(\d+\.\d)"', + cov_output.read() + )[0]) + + # Remove coverage related directories. + shutil.rmtree(kcov_ouput_dir, ignore_errors=True) + shutil.rmtree(kcov_build_dir, ignore_errors=True) + + return coverage + + +def _get_previous_coverage(): + """Helper function that returns the last reported coverage.""" + coverage_path = os.path.join( + os.path.dirname(os.path.realpath(__file__)), + 'coverage' + ) + + # The first and only line of the file contains the coverage. + with open(coverage_path) as f: + coverage = f.readline() + return float(coverage.strip()) + +def _update_coverage(cov_value): + """Updates the coverage in the coverage file.""" + coverage_path = os.path.join( + os.path.dirname(os.path.realpath(__file__)), + 'coverage' + ) + + with open(coverage_path, "w") as f: + f.write(str(cov_value)) + +def test_coverage(): + current_coverage = _get_current_coverage() + previous_coverage = _get_previous_coverage() + + # When coverage is increased, we have to update the value in the file. + if previous_coverage < current_coverage: + # TODO check if the test runs as part of the CI. If that's the case + # we have to fail the test here. + _update_coverage(current_coverage) + elif previous_coverage > current_coverage: + diff = float(previous_coverage - current_coverage) + assert False, "Coverage drops by {:.2f}%. Please add unit tests for" \ + "the uncovered lines.".format(diff) From 30a0c801f593bda1dfe5e4a0b8324535027b1e75 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Tue, 5 Mar 2019 12:56:45 +0200 Subject: [PATCH 008/332] added .gitignore Signed-off-by: Andreea Florescu --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..53eaa21 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +**/*.rs.bk From 874f5bedc61cc184044a59dcd4cc0b3f3fdb8dbd Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Tue, 5 Mar 2019 12:57:17 +0200 Subject: [PATCH 009/332] added TODO file to track missing functionality Signed-off-by: Andreea Florescu --- TODO | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 TODO diff --git a/TODO b/TODO new file mode 100644 index 0000000..60f1159 --- /dev/null +++ b/TODO @@ -0,0 +1,8 @@ +- High Level docs with usage +- add comment on each method with the KVM ioctl that it is used +- write cleanup functions for tests +- create test profiles (when running as part of the development process, + update the coverage threshold, otherwise fail the test if the current + coverage > previous coverage) +- Add copyright files +- move sys_util ioctls to a different crate. From bee80fbb9b2dab8db9c8de96cbfd7c6923c948b9 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Tue, 5 Mar 2019 13:14:14 +0200 Subject: [PATCH 010/332] Added wrappers over system ioctls used by crosvm Added wrappers over the following ioctls: - KVM_GET_EMULATED_CPUID - KVM_GET_MSR_INDEX_LIST Signed-off-by: Andreea Florescu --- src/kvm_ioctls.rs | 4 +++ src/lib.rs | 80 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 79 insertions(+), 5 deletions(-) diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index d9009d3..b3d2dc1 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -16,6 +16,8 @@ ioctl_io_nr!(KVM_CHECK_EXTENSION, KVMIO, 0x03); ioctl_io_nr!(KVM_GET_VCPU_MMAP_SIZE, KVMIO, 0x04); #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_iowr_nr!(KVM_GET_SUPPORTED_CPUID, KVMIO, 0x05, kvm_cpuid2); +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_iowr_nr!(KVM_GET_EMULATED_CPUID, KVMIO, 0x09, kvm_cpuid2); ioctl_io_nr!(KVM_CREATE_VCPU, KVMIO, 0x41); ioctl_iow_nr!(KVM_GET_DIRTY_LOG, KVMIO, 0x42, kvm_dirty_log); ioctl_iow_nr!( @@ -65,6 +67,8 @@ ioctl_ior_nr!(KVM_GET_SREGS, KVMIO, 0x83, kvm_sregs); ))] ioctl_iow_nr!(KVM_SET_SREGS, KVMIO, 0x84, kvm_sregs); #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_iowr_nr!(KVM_GET_MSR_INDEX_LIST, KVMIO, 0x02, kvm_msr_list); +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_iowr_nr!(KVM_GET_MSRS, KVMIO, 0x88, kvm_msrs); #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_iow_nr!(KVM_SET_MSRS, KVMIO, 0x89, kvm_msrs); diff --git a/src/lib.rs b/src/lib.rs index e908ea9..4b50736 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -191,6 +191,35 @@ impl Kvm { } } + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn get_cpuid(&self, kind: u64, max_entries_count: usize) -> Result { + let mut cpuid = CpuId::new(max_entries_count); + + let ret = unsafe { + // ioctl is unsafe. The kernel is trusted not to write beyond the bounds of the memory + // allocated for the struct. The limit is read from nent, which is set to the allocated + // size(max_entries_count) above. + ioctl_with_mut_ptr(self, kind, cpuid.as_mut_ptr()) + }; + if ret < 0 { + return Err(io::Error::last_os_error()); + } + + Ok(cpuid) + } + + /// X86 specific call to get the system emulated CPUID values. + /// + /// # Arguments + /// + /// * `max_entries_count` - Maximum number of CPUID entries. This function can return less than + /// this when the hardware does not support so many CPUID entries. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn get_emulated_cpuid(&self, max_entries_count: usize) -> Result { + self.get_cpuid(KVM_GET_EMULATED_CPUID(), max_entries_count) + } + /// X86 specific call to get the system supported CPUID values. /// /// # Arguments @@ -200,19 +229,41 @@ impl Kvm { /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_supported_cpuid(&self, max_entries_count: usize) -> Result { - let mut cpuid = CpuId::new(max_entries_count); + self.get_cpuid(KVM_GET_SUPPORTED_CPUID(), max_entries_count) + } + + /// X86 specific call to get list of supported MSRS + /// + /// See the documentation for KVM_GET_MSR_INDEX_LIST. + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn get_msr_index_list(&self) -> Result> { + const MAX_KVM_MSR_ENTRIES: usize = 256; + + let mut msr_list = vec_with_array_field::(MAX_KVM_MSR_ENTRIES); + msr_list[0].nmsrs = MAX_KVM_MSR_ENTRIES as u32; let ret = unsafe { // ioctl is unsafe. The kernel is trusted not to write beyond the bounds of the memory - // allocated for the struct. The limit is read from nent, which is set to the allocated - // size(max_entries_count) above. - ioctl_with_mut_ptr(self, KVM_GET_SUPPORTED_CPUID(), cpuid.as_mut_ptr()) + // allocated for the struct. The limit is read from nmsrs, which is set to the allocated + // size (MAX_KVM_MSR_ENTRIES) above. + ioctl_with_mut_ref(self, KVM_GET_MSR_INDEX_LIST(), &mut msr_list[0]) }; if ret < 0 { return Err(io::Error::last_os_error()); } - Ok(cpuid) + let mut nmsrs = msr_list[0].nmsrs; + + // Mapping the unsized array to a slice is unsafe because the length isn't known. Using + // the length we originally allocated with eliminates the possibility of overflow. + let indices: &[u32] = unsafe { + if nmsrs > MAX_KVM_MSR_ENTRIES as u32 { + nmsrs = MAX_KVM_MSR_ENTRIES as u32; + } + msr_list[0].indices.as_slice(nmsrs as usize) + }; + + Ok(indices.to_vec()) } /// Creates a VM fd using the KVM fd (`KVM_CREATE_VM`). @@ -388,6 +439,16 @@ mod tests { assert!(cpuid_entries.len() <= MAX_KVM_CPUID_ENTRIES); } + #[test] + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn test_get_emulated_cpuid() { + let kvm = Kvm::new().unwrap(); + let mut cpuid = kvm.get_emulated_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); + let cpuid_entries = cpuid.mut_entries_slice(); + assert!(cpuid_entries.len() > 0); + assert!(cpuid_entries.len() <= MAX_KVM_CPUID_ENTRIES); + } + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] #[test] fn test_cpuid_clone() { @@ -398,4 +459,13 @@ mod tests { cpuid_2 = unsafe { std::mem::zeroed() }; assert!(cpuid_1 != cpuid_2); } + + #[test] + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn get_msr_index_list() { + let kvm = Kvm::new().unwrap(); + let msr_list = kvm.get_msr_index_list().unwrap(); + assert!(msr_list.len() >= 2); + } + } From dd838394522b605e25f7326622b2b405c86a1d7f Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Tue, 5 Mar 2019 19:45:59 +0200 Subject: [PATCH 011/332] docs: added the KVM ioctl used by each function Signed-off-by: Andreea Florescu --- src/lib.rs | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4b50736..52e09a8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -116,6 +116,8 @@ impl Kvm { } /// Returns the KVM API version. + /// + /// See the documentation for `KVM_GET_API_VERSION`. pub fn get_api_version(&self) -> i32 { // Safe because we know that our file is a KVM fd and that the request is one of the ones // defined by kernel. @@ -123,6 +125,8 @@ impl Kvm { } /// Query the availability of a particular kvm capability. + /// + /// See the documentation for `KVM_CHECK_EXTENSION`. /// Returns 0 if the capability is not available and > 0 otherwise. /// fn check_extension_int(&self, c: Cap) -> i32 { @@ -133,8 +137,8 @@ impl Kvm { /// Checks if a particular `Cap` is available. /// - /// According to the KVM API doc, KVM_CHECK_EXTENSION returns "0 if unsupported; 1 (or some - /// other positive integer) if supported". + /// According to the KVM API doc, `KVM_CHECK_EXTENSION` returns "0 if unsupported; 1 (or some + /// other positive integer) if supported. /// /// # Arguments /// @@ -145,6 +149,9 @@ impl Kvm { } /// Gets the size of the mmap required to use vcpu's `kvm_run` structure. + /// + /// See the documentation for `KVM_GET_VCPU_MMAP_SIZE`. + /// pub fn get_vcpu_mmap_size(&self) -> Result { // Safe because we know that our file is a KVM fd and we verify the return result. let res = unsafe { ioctl(self, KVM_GET_VCPU_MMAP_SIZE()) }; @@ -157,6 +164,8 @@ impl Kvm { /// Gets the recommended number of VCPUs per VM. /// + /// See the documentation for `KVM_CAP_NR_VCPUS`. + /// Default to 4 when `KVM_CAP_NR_VCPUS` is not implemented. pub fn get_nr_vcpus(&self) -> usize { let x = self.check_extension_int(Cap::NrVcpus); if x > 0 { @@ -184,6 +193,9 @@ impl Kvm { /// Gets the recommended maximum number of VCPUs per VM. /// + /// See the documentation for `KVM_CAP_MAX_VCPUS`. + /// Default to `KVM_CAP_NR_VCPUS` when `KVM_CAP_MAX_VCPUS` is not implemented. + /// pub fn get_max_vcpus(&self) -> usize { match self.check_extension_int(Cap::MaxVcpus) { 0 => self.get_nr_vcpus(), @@ -210,6 +222,8 @@ impl Kvm { /// X86 specific call to get the system emulated CPUID values. /// + /// See the documentation for KVM_GET_EMULATED_CPUID. + /// /// # Arguments /// /// * `max_entries_count` - Maximum number of CPUID entries. This function can return less than @@ -222,6 +236,8 @@ impl Kvm { /// X86 specific call to get the system supported CPUID values. /// + /// See the documentation for KVM_GET_SUPPORTED_CPUID. + /// /// # Arguments /// /// * `max_entries_count` - Maximum number of CPUID entries. This function can return less than @@ -235,6 +251,7 @@ impl Kvm { /// X86 specific call to get list of supported MSRS /// /// See the documentation for KVM_GET_MSR_INDEX_LIST. + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_msr_index_list(&self) -> Result> { const MAX_KVM_MSR_ENTRIES: usize = 256; @@ -266,10 +283,11 @@ impl Kvm { Ok(indices.to_vec()) } - /// Creates a VM fd using the KVM fd (`KVM_CREATE_VM`). + /// Creates a VM fd using the KVM fd. /// - /// A call to this function will also initialize the size of the vcpu mmap area - /// (`KVM_GET_VCPU_MMAP_SIZE`). + /// See the documentation for `KVM_CREATE_VM`. + /// A call to this function will also initialize the size of the vcpu mmap area using the + /// `KVM_GET_VCPU_MMAP_SIZE` ioctl. /// pub fn create_vm(&self) -> Result { // Safe because we know kvm is a real kvm fd as this module is the only one that can make From ed85818a8916b1de8f698376d1e5050c2d98bcf1 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Tue, 5 Mar 2019 20:07:03 +0200 Subject: [PATCH 012/332] added KVM VM ioctl wrappers Signed-off-by: Andreea Florescu --- src/lib.rs | 402 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 401 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 52e09a8..04a473f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,8 +22,9 @@ use kvm_bindings::*; use libc::{open, O_CLOEXEC, O_RDWR}; use std::fs::File; use std::mem::size_of; -use std::os::raw::{c_char, c_ulong}; +use std::os::raw::{c_char, c_ulong, c_void}; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; +use std::ptr::null_mut; use std::{io, result}; pub use self::cap::Cap; @@ -319,6 +320,304 @@ pub struct VmFd { run_size: usize, } +impl VmFd { + /// Creates/modifies a guest physical memory slot. + /// + /// See the documentation for `KVM_SET_USER_MEMORY_REGION`. + /// + pub fn set_user_memory_region( + &self, + user_memory_region: kvm_userspace_memory_region, + ) -> Result<()> { + let ret = + unsafe { ioctl_with_ref(self, KVM_SET_USER_MEMORY_REGION(), &user_memory_region) }; + if ret == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + + /// Sets the address of the three-page region in the VM's address space. + /// + /// See the documentation on the `KVM_SET_TSS_ADDR` ioctl. + /// + /// # Arguments + /// + /// * `offset` - Physical address of a three-page region in the guest's physical address space. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn set_tss_address(&self, offset: usize) -> Result<()> { + // Safe because we know that our file is a VM fd and we verify the return result. + let ret = unsafe { ioctl_with_val(self, KVM_SET_TSS_ADDR(), offset as c_ulong) }; + if ret == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + + /// Creates an in-kernel interrupt controller. + /// + /// See the documentation for `KVM_CREATE_IRQCHIP`. + /// + #[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" + ))] + pub fn create_irq_chip(&self) -> Result<()> { + // Safe because we know that our file is a VM fd and we verify the return result. + let ret = unsafe { ioctl(self, KVM_CREATE_IRQCHIP()) }; + if ret == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + + /// Creates a PIT as per the `KVM_CREATE_PIT2` ioctl. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn create_pit2(&self, pit_config: kvm_pit_config) -> Result<()> { + // Safe because we know that our file is a VM fd, we know the kernel will only read the + // correct amount of memory from our pointer, and we verify the return result. + let ret = unsafe { ioctl_with_ref(self, KVM_CREATE_PIT2(), &pit_config) }; + if ret == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + + /// Registers an event to be signaled whenever a certain address is written to. + /// + /// # Arguments + /// + /// * `evt` - EventFd which will be signaled. When signaling, the usual `vmexit` to userspace + /// is prevented. + /// * `addr` - Address being written to. + /// * `datamatch` - Limits signaling `evt` to only the cases where the value being written is + /// equal to this parameter. The size of `datamatch` is important and it must + /// match the expected size of the guest's write. + /// + pub fn register_ioevent>( + &self, + eventfd: RawFd, + addr: &IoEventAddress, + datamatch: T, + ) -> Result<()> { + let mut flags = 0; + if std::mem::size_of::() > 0 { + flags |= 1 << kvm_ioeventfd_flag_nr_datamatch + } + if let IoEventAddress::Pio(_) = *addr { + flags |= 1 << kvm_ioeventfd_flag_nr_pio + } + + let ioeventfd = kvm_ioeventfd { + datamatch: datamatch.into(), + len: std::mem::size_of::() as u32, + addr: match addr { + IoEventAddress::Pio(ref p) => *p as u64, + IoEventAddress::Mmio(ref m) => *m, + }, + fd: eventfd, + flags, + ..Default::default() + }; + // Safe because we know that our file is a VM fd, we know the kernel will only read the + // correct amount of memory from our pointer, and we verify the return result. + let ret = unsafe { ioctl_with_ref(self, KVM_IOEVENTFD(), &ioeventfd) }; + if ret == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + + /// Gets the bitmap of pages dirtied since the last call of this function. + /// + /// Leverages the dirty page logging feature in KVM. As a side-effect, this also resets the + /// bitmap inside the kernel. + /// + /// # Arguments + /// + /// * `slot` - Guest memory slot identifier. + /// * `memory_size` - Size of the memory region. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn get_dirty_log(&self, slot: u32, memory_size: usize) -> Result> { + // Compute the length of the bitmap needed for all dirty pages in one memory slot. + // One memory page is 4KiB (4096 bits) and KVM_GET_DIRTY_LOG returns one dirty bit for + // each page. + let page_size = 4 << 10; + + let div_round_up = |dividend, divisor| (dividend + divisor - 1) / divisor; + // For ease of access we are saving the bitmap in a u64 vector. We are using ceil to + // make sure we count all dirty pages even when `mem_size` is not a multiple of + // page_size * 64. + let bitmap_size = div_round_up(memory_size, page_size * 64); + let mut bitmap = vec![0; bitmap_size]; + let b_data = bitmap.as_mut_ptr() as *mut c_void; + let dirtylog = kvm_dirty_log { + slot, + padding1: 0, + __bindgen_anon_1: kvm_dirty_log__bindgen_ty_1 { + dirty_bitmap: b_data, + }, + }; + // Safe because we know that our file is a VM fd, and we know that the amount of memory + // we allocated for the bitmap is at least one bit per page. + let ret = unsafe { ioctl_with_ref(self, KVM_GET_DIRTY_LOG(), &dirtylog) }; + if ret == 0 { + Ok(bitmap) + } else { + Err(io::Error::last_os_error()) + } + } + + /// Registers an event that will, when signaled, trigger the `gsi` IRQ. + /// + /// # Arguments + /// + /// * `eventfd` - Event to be signaled. + /// * `gsi` - IRQ to be triggered. + /// + #[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" + ))] + pub fn register_irqfd(&self, eventfd: RawFd, gsi: u32) -> Result<()> { + let irqfd = kvm_irqfd { + fd: eventfd as u32, + gsi, + ..Default::default() + }; + // Safe because we know that our file is a VM fd, we know the kernel will only read the + // correct amount of memory from our pointer, and we verify the return result. + let ret = unsafe { ioctl_with_ref(self, KVM_IRQFD(), &irqfd) }; + if ret == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + + /// Constructs a new kvm VCPU fd. + /// + /// # Arguments + /// + /// * `id` - The CPU number between [0, max vcpus). + /// + /// # Errors + /// Returns an error when the VM fd is invalid or the VCPU memory cannot be mapped correctly. + /// + pub fn create_vcpu(&self, id: u8) -> Result { + // Safe because we know that vm is a VM fd and we verify the return result. + #[allow(clippy::cast_lossless)] + let vcpu_fd = unsafe { ioctl_with_val(&self.vm, KVM_CREATE_VCPU(), id as c_ulong) }; + if vcpu_fd < 0 { + return Err(io::Error::last_os_error()); + } + + // Wrap the vcpu now in case the following ? returns early. This is safe because we verified + // the value of the fd and we own the fd. + let vcpu = unsafe { File::from_raw_fd(vcpu_fd) }; + + let kvm_run_ptr = KvmRunWrapper::from_fd(&vcpu, self.run_size)?; + + Ok(VcpuFd { vcpu, kvm_run_ptr }) + } +} + +impl AsRawFd for VmFd { + fn as_raw_fd(&self) -> RawFd { + self.vm.as_raw_fd() + } +} + +/// An address either in programmable I/O space or in memory mapped I/O space. +pub enum IoEventAddress { + /// Representation of an programmable I/O address. + Pio(u64), + /// Representation of an memory mapped I/O address. + Mmio(u64), +} + +/// Used in `VmFd::register_ioevent` to indicate that no datamatch is requested. +pub struct NoDatamatch; +impl Into for NoDatamatch { + fn into(self) -> u64 { + 0 + } +} + +/// A safe wrapper over the `kvm_run` struct. +/// +/// The wrapper is needed for sending the pointer to `kvm_run` between +/// threads as raw pointers do not implement `Send` and `Sync`. +pub struct KvmRunWrapper { + kvm_run_ptr: *mut u8, +} + +// Send and Sync aren't automatically inherited for the raw address pointer. +// Accessing that pointer is only done through the stateless interface which +// allows the object to be shared by multiple threads without a decrease in +// safety. +unsafe impl Send for KvmRunWrapper {} +unsafe impl Sync for KvmRunWrapper {} + +impl KvmRunWrapper { + /// Maps the first `size` bytes of the given `fd`. + /// + /// # Arguments + /// * `fd` - File descriptor to mmap from. + /// * `size` - Size of memory region in bytes. + pub fn from_fd(fd: &AsRawFd, size: usize) -> Result { + // This is safe because we are creating a mapping in a place not already used by any other + // area in this process. + let addr = unsafe { + libc::mmap( + null_mut(), + size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_SHARED, + fd.as_raw_fd(), + 0, + ) + }; + if addr == libc::MAP_FAILED { + return Err(io::Error::last_os_error()); + } + + Ok(KvmRunWrapper { + kvm_run_ptr: addr as *mut u8, + }) + } + + /// Returns a mutable reference to `kvm_run`. + /// + #[allow(clippy::mut_from_ref)] + pub fn as_mut_ref(&self) -> &mut kvm_run { + // Safe because we know we mapped enough memory to hold the kvm_run struct because the + // kernel told us how large it was. + #[allow(clippy::cast_ptr_alignment)] + unsafe { + &mut *(self.kvm_run_ptr as *mut kvm_run) + } + } +} + +/// A wrapper around creating and using a kvm related VCPU fd +pub struct VcpuFd { + vcpu: File, + kvm_run_ptr: KvmRunWrapper, +} + /// Wrapper for `kvm_cpuid2` which has a zero length array at the end. /// Hides the zero length array behind a bounds check. #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] @@ -400,6 +699,7 @@ impl CpuId { #[cfg(test)] mod tests { use super::*; + use libc::{eventfd, EFD_NONBLOCK}; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] impl PartialEq for CpuId { @@ -486,4 +786,104 @@ mod tests { assert!(msr_list.len() >= 2); } + #[test] + fn test_set_invalid_memory() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let invalid_mem_region = kvm_userspace_memory_region { + slot: 0, + guest_phys_addr: 0, + memory_size: 0, + userspace_addr: 0, + flags: 0, + }; + assert!(vm.set_user_memory_region(invalid_mem_region).is_err()); + } + + #[test] + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn test_set_tss_address() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + assert!(vm.set_tss_address(0xfffb_d000).is_ok()); + } + + #[test] + #[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" + ))] + fn test_create_irq_chip() { + let kvm = Kvm::new().unwrap(); + assert!(kvm.check_extension(Cap::Irqchip)); + let vm = kvm.create_vm().unwrap(); + assert!(vm.create_irq_chip().is_ok()); + } + + #[test] + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn test_create_pit2() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + assert!(vm.create_pit2(kvm_pit_config::default()).is_ok()); + } + + #[test] + fn test_register_ioevent() { + assert_eq!(std::mem::size_of::(), 0); + + let kvm = Kvm::new().unwrap(); + let vm_fd = kvm.create_vm().unwrap(); + let evtfd = unsafe { eventfd(0, EFD_NONBLOCK) }; + assert!(vm_fd + .register_ioevent(evtfd, &IoEventAddress::Pio(0xf4), NoDatamatch) + .is_ok()); + assert!(vm_fd + .register_ioevent(evtfd, &IoEventAddress::Mmio(0x1000), NoDatamatch) + .is_ok()); + assert!(vm_fd + .register_ioevent(evtfd, &IoEventAddress::Pio(0xc1), 0x7fu8) + .is_ok()); + assert!(vm_fd + .register_ioevent(evtfd, &IoEventAddress::Pio(0xc2), 0x1337u16) + .is_ok()); + assert!(vm_fd + .register_ioevent(evtfd, &IoEventAddress::Pio(0xc4), 0xdead_beefu32) + .is_ok()); + assert!(vm_fd + .register_ioevent(evtfd, &IoEventAddress::Pio(0xc8), 0xdead_beef_dead_beefu64) + .is_ok()); + } + + #[test] + #[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" + ))] + fn test_register_irqfd() { + let kvm = Kvm::new().unwrap(); + let vm_fd = kvm.create_vm().unwrap(); + let evtfd1 = unsafe { eventfd(0, EFD_NONBLOCK) }; + let evtfd2 = unsafe { eventfd(0, EFD_NONBLOCK) }; + let evtfd3 = unsafe { eventfd(0, EFD_NONBLOCK) }; + + assert!(vm_fd.register_irqfd(evtfd1, 4).is_ok()); + assert!(vm_fd.register_irqfd(evtfd2, 8).is_ok()); + assert!(vm_fd.register_irqfd(evtfd3, 4).is_ok()); + + assert!(vm_fd.register_irqfd(evtfd3, 4).is_err()); + assert!(vm_fd.register_irqfd(evtfd3, 5).is_err()); + } + + #[test] + fn test_create_vcpu() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + + assert!(vm.create_vcpu(0).is_ok()); + } } From bbc7998276d14ddf7aee60b44aef0e36ff8683ab Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Fri, 8 Mar 2019 20:13:34 +0200 Subject: [PATCH 013/332] added KVM vCPU ioctls Added all KVM vCPU ioctls used by Firecracker. Signed-off-by: Andreea Florescu --- src/lib.rs | 475 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 474 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 04a473f..4e8c4ba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,7 +19,7 @@ mod kvm_ioctls; mod cap; use kvm_bindings::*; -use libc::{open, O_CLOEXEC, O_RDWR}; +use libc::{open, EINVAL, O_CLOEXEC, O_RDWR}; use std::fs::File; use std::mem::size_of; use std::os::raw::{c_char, c_ulong, c_void}; @@ -612,12 +612,371 @@ impl KvmRunWrapper { } } +/// Reasons for vcpu exits. The exit reasons are mapped to the `KVM_EXIT_*` defines +/// from `include/uapi/linux/kvm.h`. +#[derive(Debug)] +pub enum VcpuExit<'a> { + /// An out port instruction was run on the given port with the given data. + IoOut(u16 /* port */, &'a [u8] /* data */), + /// An in port instruction was run on the given port. + /// + /// The given slice should be filled in before `Vcpu::run` is called again. + IoIn(u16 /* port */, &'a mut [u8] /* data */), + /// A read instruction was run against the given MMIO address. + /// + /// The given slice should be filled in before `Vcpu::run` is called again. + MmioRead(u64 /* address */, &'a mut [u8]), + /// A write instruction was run against the given MMIO address with the given data. + MmioWrite(u64 /* address */, &'a [u8]), + /// Corresponds to KVM_EXIT_UNKNOWN. + Unknown, + /// Corresponds to KVM_EXIT_EXCEPTION. + Exception, + /// Corresponds to KVM_EXIT_HYPERCALL. + Hypercall, + /// Corresponds to KVM_EXIT_DEBUG. + Debug, + /// Corresponds to KVM_EXIT_HLT. + Hlt, + /// Corresponds to KVM_EXIT_IRQ_WINDOW_OPEN. + IrqWindowOpen, + /// Corresponds to KVM_EXIT_SHUTDOWN. + Shutdown, + /// Corresponds to KVM_EXIT_FAIL_ENTRY. + FailEntry, + /// Corresponds to KVM_EXIT_INTR. + Intr, + /// Corresponds to KVM_EXIT_SET_TPR. + SetTpr, + /// Corresponds to KVM_EXIT_TPR_ACCESS. + TprAccess, + /// Corresponds to KVM_EXIT_S390_SIEIC. + S390Sieic, + /// Corresponds to KVM_EXIT_S390_RESET. + S390Reset, + /// Corresponds to KVM_EXIT_DCR. + Dcr, + /// Corresponds to KVM_EXIT_NMI. + Nmi, + /// Corresponds to KVM_EXIT_INTERNAL_ERROR. + InternalError, + /// Corresponds to KVM_EXIT_OSI. + Osi, + /// Corresponds to KVM_EXIT_PAPR_HCALL. + PaprHcall, + /// Corresponds to KVM_EXIT_S390_UCONTROL. + S390Ucontrol, + /// Corresponds to KVM_EXIT_WATCHDOG. + Watchdog, + /// Corresponds to KVM_EXIT_S390_TSCH. + S390Tsch, + /// Corresponds to KVM_EXIT_EPR. + Epr, + /// Corresponds to KVM_EXIT_SYSTEM_EVENT. + SystemEvent, + /// Corresponds to KVM_EXIT_S390_STSI. + S390Stsi, + /// Corresponds to KVM_EXIT_IOAPIC_EOI. + IoapicEoi, + /// Corresponds to KVM_EXIT_HYPERV. + Hyperv, +} + /// A wrapper around creating and using a kvm related VCPU fd pub struct VcpuFd { vcpu: File, kvm_run_ptr: KvmRunWrapper, } +impl VcpuFd { + /// Gets the VCPU registers using the `KVM_GET_REGS` ioctl. + /// + #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] + pub fn get_regs(&self) -> Result { + // Safe because we know that our file is a VCPU fd, we know the kernel will only read the + // correct amount of memory from our pointer, and we verify the return result. + let mut regs = unsafe { std::mem::zeroed() }; + let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_REGS(), &mut regs) }; + if ret != 0 { + return Err(io::Error::last_os_error()); + } + Ok(regs) + } + + /// Sets the VCPU registers using `KVM_SET_REGS` ioctl. + /// + /// # Arguments + /// + /// * `regs` - Registers being set. + /// + #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] + pub fn set_regs(&self, regs: &kvm_regs) -> Result<()> { + // Safe because we know that our file is a VCPU fd, we know the kernel will only read the + // correct amount of memory from our pointer, and we verify the return result. + let ret = unsafe { ioctl_with_ref(self, KVM_SET_REGS(), regs) }; + if ret != 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) + } + + /// Gets the VCPU special registers using `KVM_GET_SREGS` ioctl. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn get_sregs(&self) -> Result { + // Safe because we know that our file is a VCPU fd, we know the kernel will only write the + // correct amount of memory to our pointer, and we verify the return result. + let mut regs = kvm_sregs::default(); + + let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_SREGS(), &mut regs) }; + if ret != 0 { + return Err(io::Error::last_os_error()); + } + Ok(regs) + } + + /// Sets the VCPU special registers using `KVM_SET_SREGS` ioctl. + /// + /// # Arguments + /// + /// * `sregs` - Special registers to be set. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn set_sregs(&self, sregs: &kvm_sregs) -> Result<()> { + // Safe because we know that our file is a VCPU fd, we know the kernel will only read the + // correct amount of memory from our pointer, and we verify the return result. + let ret = unsafe { ioctl_with_ref(self, KVM_SET_SREGS(), sregs) }; + if ret != 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) + } + + /// X86 specific call that gets the FPU-related structure. + /// + /// See the documentation for `KVM_GET_FPU`. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn get_fpu(&self) -> Result { + let mut fpu = kvm_fpu::default(); + + let ret = unsafe { + // Here we trust the kernel not to read past the end of the kvm_fpu struct. + ioctl_with_mut_ref(self, KVM_GET_FPU(), &mut fpu) + }; + if ret != 0 { + return Err(io::Error::last_os_error()); + } + Ok(fpu) + } + + /// X86 specific call to setup the FPU. + /// + /// See the documentation for `KVM_SET_FPU`. + /// + /// # Arguments + /// + /// * `fpu` - FPU configurations struct. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn set_fpu(&self, fpu: &kvm_fpu) -> Result<()> { + let ret = unsafe { + // Here we trust the kernel not to read past the end of the kvm_fpu struct. + ioctl_with_ref(self, KVM_SET_FPU(), fpu) + }; + if ret < 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) + } + + /// X86 specific call to setup the CPUID registers. + /// + /// See the documentation for `KVM_SET_CPUID2`. + /// + /// # Arguments + /// + /// * `cpuid` - CPUID registers. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn set_cpuid2(&self, cpuid: &CpuId) -> Result<()> { + let ret = unsafe { + // Here we trust the kernel not to read past the end of the kvm_msrs struct. + ioctl_with_ptr(self, KVM_SET_CPUID2(), cpuid.as_ptr()) + }; + if ret < 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) + } + + /// X86 specific call to get the state of the LAPIC (Local Advanced Programmable Interrupt + /// Controller). + /// + /// See the documentation for `KVM_GET_LAPIC`. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn get_lapic(&self) -> Result { + let mut klapic = kvm_lapic_state::default(); + + let ret = unsafe { + // The ioctl is unsafe unless you trust the kernel not to write past the end of the + // local_apic struct. + ioctl_with_mut_ref(self, KVM_GET_LAPIC(), &mut klapic) + }; + if ret < 0 { + return Err(io::Error::last_os_error()); + } + Ok(klapic) + } + + /// X86 specific call to set the state of the LAPIC (Local Advanced Programmable Interrupt + /// Controller). + /// + /// See the documentation for `KVM_SET_LAPIC`. + /// + /// # Arguments + /// + /// * `klapic` - LAPIC state registers. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn set_lapic(&self, klapic: &kvm_lapic_state) -> Result<()> { + let ret = unsafe { + // The ioctl is safe because the kernel will only read from the klapic struct. + ioctl_with_ref(self, KVM_SET_LAPIC(), klapic) + }; + if ret < 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) + } + + /// X86 specific call to read model-specific registers for this VCPU. + /// + /// It emulates `KVM_GET_MSRS` ioctl's behavior by returning the number of MSRs + /// successfully read upon success or the last error number in case of failure. + /// + /// # Arguments + /// + /// * `msrs` - MSRs to be read. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn get_msrs(&self, msrs: &mut kvm_msrs) -> Result<(i32)> { + let ret = unsafe { + // Here we trust the kernel not to read past the end of the kvm_msrs struct. + ioctl_with_mut_ref(self, KVM_GET_MSRS(), msrs) + }; + if ret < 0 { + return Err(io::Error::last_os_error()); + } + Ok(ret) + } + + /// X86 specific call to setup the MSRS. + /// + /// See the documentation for `KVM_SET_MSRS`. + /// + /// # Arguments + /// + /// * `kvm_msrs` - MSRs to be written. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn set_msrs(&self, msrs: &kvm_msrs) -> Result<()> { + let ret = unsafe { + // Here we trust the kernel not to read past the end of the kvm_msrs struct. + ioctl_with_ref(self, KVM_SET_MSRS(), msrs) + }; + if ret < 0 { + // KVM_SET_MSRS actually returns the number of msr entries written. + return Err(io::Error::last_os_error()); + } + Ok(()) + } + + /// Triggers the running of the current virtual CPU returning an exit reason. + /// + pub fn run(&self) -> Result { + // Safe because we know that our file is a VCPU fd and we verify the return result. + let ret = unsafe { ioctl(self, KVM_RUN()) }; + if ret == 0 { + let run = self.kvm_run_ptr.as_mut_ref(); + match run.exit_reason { + // make sure you treat all possible exit reasons from include/uapi/linux/kvm.h corresponding + // when upgrading to a different kernel version + KVM_EXIT_UNKNOWN => Ok(VcpuExit::Unknown), + KVM_EXIT_EXCEPTION => Ok(VcpuExit::Exception), + KVM_EXIT_IO => { + let run_start = run as *mut kvm_run as *mut u8; + // Safe because the exit_reason (which comes from the kernel) told us which + // union field to use. + let io = unsafe { run.__bindgen_anon_1.io }; + let port = io.port; + let data_size = io.count as usize * io.size as usize; + // The data_offset is defined by the kernel to be some number of bytes into the + // kvm_run stucture, which we have fully mmap'd. + let data_ptr = unsafe { run_start.offset(io.data_offset as isize) }; + // The slice's lifetime is limited to the lifetime of this Vcpu, which is equal + // to the mmap of the kvm_run struct that this is slicing from + let data_slice = unsafe { + std::slice::from_raw_parts_mut::(data_ptr as *mut u8, data_size) + }; + match u32::from(io.direction) { + KVM_EXIT_IO_IN => Ok(VcpuExit::IoIn(port, data_slice)), + KVM_EXIT_IO_OUT => Ok(VcpuExit::IoOut(port, data_slice)), + _ => Err(io::Error::from_raw_os_error(EINVAL)), + } + } + KVM_EXIT_HYPERCALL => Ok(VcpuExit::Hypercall), + KVM_EXIT_DEBUG => Ok(VcpuExit::Debug), + KVM_EXIT_HLT => Ok(VcpuExit::Hlt), + KVM_EXIT_MMIO => { + // Safe because the exit_reason (which comes from the kernel) told us which + // union field to use. + let mmio = unsafe { &mut run.__bindgen_anon_1.mmio }; + let addr = mmio.phys_addr; + let len = mmio.len as usize; + let data_slice = &mut mmio.data[..len]; + if mmio.is_write != 0 { + Ok(VcpuExit::MmioWrite(addr, data_slice)) + } else { + Ok(VcpuExit::MmioRead(addr, data_slice)) + } + } + KVM_EXIT_IRQ_WINDOW_OPEN => Ok(VcpuExit::IrqWindowOpen), + KVM_EXIT_SHUTDOWN => Ok(VcpuExit::Shutdown), + KVM_EXIT_FAIL_ENTRY => Ok(VcpuExit::FailEntry), + KVM_EXIT_INTR => Ok(VcpuExit::Intr), + KVM_EXIT_SET_TPR => Ok(VcpuExit::SetTpr), + KVM_EXIT_TPR_ACCESS => Ok(VcpuExit::TprAccess), + KVM_EXIT_S390_SIEIC => Ok(VcpuExit::S390Sieic), + KVM_EXIT_S390_RESET => Ok(VcpuExit::S390Reset), + KVM_EXIT_DCR => Ok(VcpuExit::Dcr), + KVM_EXIT_NMI => Ok(VcpuExit::Nmi), + KVM_EXIT_INTERNAL_ERROR => Ok(VcpuExit::InternalError), + KVM_EXIT_OSI => Ok(VcpuExit::Osi), + KVM_EXIT_PAPR_HCALL => Ok(VcpuExit::PaprHcall), + KVM_EXIT_S390_UCONTROL => Ok(VcpuExit::S390Ucontrol), + KVM_EXIT_WATCHDOG => Ok(VcpuExit::Watchdog), + KVM_EXIT_S390_TSCH => Ok(VcpuExit::S390Tsch), + KVM_EXIT_EPR => Ok(VcpuExit::Epr), + KVM_EXIT_SYSTEM_EVENT => Ok(VcpuExit::SystemEvent), + KVM_EXIT_S390_STSI => Ok(VcpuExit::S390Stsi), + KVM_EXIT_IOAPIC_EOI => Ok(VcpuExit::IoapicEoi), + KVM_EXIT_HYPERV => Ok(VcpuExit::Hyperv), + r => panic!("unknown kvm exit reason: {}", r), + } + } else { + Err(io::Error::last_os_error()) + } + } +} + +impl AsRawFd for VcpuFd { + fn as_raw_fd(&self) -> RawFd { + self.vcpu.as_raw_fd() + } +} + /// Wrapper for `kvm_cpuid2` which has a zero length array at the end. /// Hides the zero length array behind a bounds check. #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] @@ -712,6 +1071,26 @@ mod tests { } } + // Helper function for mmap an anonymous memory of `size`. + // Panics if the mmap fails. + fn mmap_anonymous(size: usize) -> *mut u8 { + let addr = unsafe { + libc::mmap( + null_mut(), + size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_ANONYMOUS | libc::MAP_SHARED | libc::MAP_NORESERVE, + -1, + 0, + ) + }; + if addr == libc::MAP_FAILED { + panic!("mmap failed."); + } + + return addr as *mut u8; + } + #[test] fn test_kvm_new() { Kvm::new().unwrap(); @@ -886,4 +1265,98 @@ mod tests { assert!(vm.create_vcpu(0).is_ok()); } + + #[cfg(target_arch = "x86_64")] + #[test] + fn test_run_code() { + use std::io::Write; + + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + // This example based on https://lwn.net/Articles/658511/ + let code = [ + 0xba, 0xf8, 0x03, /* mov $0x3f8, %dx */ + 0x00, 0xd8, /* add %bl, %al */ + 0x04, b'0', /* add $'0', %al */ + 0xee, /* out %al, %dx */ + 0xec, /* in %dx, %al */ + 0xc6, 0x06, 0x00, 0x80, 0x00, /* movl $0, (0x8000) */ + 0x8a, 0x16, 0x00, 0x80, /* movl (0x8000), %dl */ + 0xc6, 0x06, 0x00, 0x20, 0x00, /* movl $0, (0x2000) */ + 0xf4, /* hlt */ + ]; + + let mem_size = 0x4000; + let load_addr = mmap_anonymous(mem_size); + let guest_addr: u64 = 0x1000; + let slot: u32 = 0; + let mem_region = kvm_userspace_memory_region { + slot, + guest_phys_addr: guest_addr, + memory_size: mem_size as u64, + userspace_addr: load_addr as u64, + flags: KVM_MEM_LOG_DIRTY_PAGES, + }; + vm.set_user_memory_region(mem_region).unwrap(); + + unsafe { + // Get a mutable slice of `mem_size` from `load_addr`. + // This is safe because we mapped it before. + let mut slice = std::slice::from_raw_parts_mut(load_addr, mem_size); + slice.write(&code).unwrap(); + } + + let vcpu_fd = vm.create_vcpu(0).unwrap(); + + let mut vcpu_sregs = vcpu_fd.get_sregs().unwrap(); + assert_ne!(vcpu_sregs.cs.base, 0); + assert_ne!(vcpu_sregs.cs.selector, 0); + vcpu_sregs.cs.base = 0; + vcpu_sregs.cs.selector = 0; + vcpu_fd.set_sregs(&vcpu_sregs).unwrap(); + + let mut vcpu_regs = vcpu_fd.get_regs().unwrap(); + // Set the Instruction Pointer to the guest address where we loaded the code. + vcpu_regs.rip = guest_addr; + vcpu_regs.rax = 2; + vcpu_regs.rbx = 3; + vcpu_regs.rflags = 2; + vcpu_fd.set_regs(&vcpu_regs).unwrap(); + + loop { + match vcpu_fd.run().expect("run failed") { + VcpuExit::IoIn(addr, data) => { + assert_eq!(addr, 0x3f8); + assert_eq!(data.len(), 1); + } + VcpuExit::IoOut(addr, data) => { + assert_eq!(addr, 0x3f8); + assert_eq!(data.len(), 1); + assert_eq!(data[0], b'5'); + } + VcpuExit::MmioRead(addr, data) => { + assert_eq!(addr, 0x8000); + assert_eq!(data.len(), 1); + } + VcpuExit::MmioWrite(addr, data) => { + assert_eq!(addr, 0x8000); + assert_eq!(data.len(), 1); + assert_eq!(data[0], 0); + } + VcpuExit::Hlt => { + // The code snippet dirties 2 pages: + // * one when the code itself is loaded in memory; + // * and one more from the `movl` that writes to address 0x8000 + let dirty_pages_bitmap = vm.get_dirty_log(slot, mem_size).unwrap(); + let dirty_pages = dirty_pages_bitmap + .into_iter() + .map(|page| page.count_ones()) + .fold(0, |dirty_page_count, i| dirty_page_count + i); + assert_eq!(dirty_pages, 2); + break; + } + r => panic!("unexpected exit reason: {:?}", r), + } + } + } } From 4aa122fb14bcb32b493f48b369b36365fc91b93c Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Mon, 11 Mar 2019 19:13:40 +0200 Subject: [PATCH 014/332] split ioctls wrappers in 3 modules Split ioctls in system, vcpu and vm ioctls so we do not have a huge file with them all. Signed-off-by: Andreea Florescu --- TODO | 3 + src/ioctls/mod.rs | 193 ++++++ src/ioctls/system.rs | 337 +++++++++++ src/ioctls/vcpu.rs | 513 ++++++++++++++++ src/ioctls/vm.rs | 369 ++++++++++++ src/lib.rs | 1345 +----------------------------------------- 6 files changed, 1421 insertions(+), 1339 deletions(-) create mode 100644 src/ioctls/mod.rs create mode 100644 src/ioctls/system.rs create mode 100644 src/ioctls/vcpu.rs create mode 100644 src/ioctls/vm.rs diff --git a/TODO b/TODO index 60f1159..c53a67b 100644 --- a/TODO +++ b/TODO @@ -6,3 +6,6 @@ coverage > previous coverage) - Add copyright files - move sys_util ioctls to a different crate. +- ARM support +- CrosVM ioctls +- missing unit tests (almost everything is testable) diff --git a/src/ioctls/mod.rs b/src/ioctls/mod.rs new file mode 100644 index 0000000..f913057 --- /dev/null +++ b/src/ioctls/mod.rs @@ -0,0 +1,193 @@ +use kvm_bindings::{kvm_cpuid2, kvm_cpuid_entry2, kvm_run}; +use std::io; +use std::mem::size_of; +use std::os::unix::io::AsRawFd; +use std::ptr::null_mut; +use std::result; + +/// Wrappers over KVM system ioctls. +pub mod system; +/// Wrappers over KVM VCPU ioctls. +pub mod vcpu; +/// Wrappers over KVM Virtual Machine ioctls. +pub mod vm; + +/// Wrapper over possible Kvm Result. +pub type Result = result::Result; + +// Returns a `Vec` with a size in bytes at least as large as `size_in_bytes`. +fn vec_with_size_in_bytes(size_in_bytes: usize) -> Vec { + let rounded_size = (size_in_bytes + size_of::() - 1) / size_of::(); + let mut v = Vec::with_capacity(rounded_size); + for _ in 0..rounded_size { + v.push(T::default()) + } + v +} + +// The kvm API has many structs that resemble the following `Foo` structure: +// +// ``` +// #[repr(C)] +// struct Foo { +// some_data: u32 +// entries: __IncompleteArrayField<__u32>, +// } +// ``` +// +// In order to allocate such a structure, `size_of::()` would be too small because it would not +// include any space for `entries`. To make the allocation large enough while still being aligned +// for `Foo`, a `Vec` is created. Only the first element of `Vec` would actually be used +// as a `Foo`. The remaining memory in the `Vec` is for `entries`, which must be contiguous +// with `Foo`. This function is used to make the `Vec` with enough space for `count` entries. +fn vec_with_array_field(count: usize) -> Vec { + let element_space = count * size_of::(); + let vec_size_bytes = size_of::() + element_space; + vec_with_size_in_bytes(vec_size_bytes) +} + +/// Wrapper for `kvm_cpuid2` which has a zero length array at the end. +/// Hides the zero length array behind a bounds check. +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +pub struct CpuId { + /// Wrapper over `kvm_cpuid2` from which we only use the first element. + kvm_cpuid: Vec, + // Number of `kvm_cpuid_entry2` structs at the end of kvm_cpuid2. + allocated_len: usize, +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +impl Clone for CpuId { + fn clone(&self) -> Self { + let mut kvm_cpuid = Vec::with_capacity(self.kvm_cpuid.len()); + for _ in 0..self.kvm_cpuid.len() { + kvm_cpuid.push(kvm_cpuid2::default()); + } + + let num_bytes = self.kvm_cpuid.len() * size_of::(); + + let src_byte_slice = + unsafe { std::slice::from_raw_parts(self.kvm_cpuid.as_ptr() as *const u8, num_bytes) }; + + let dst_byte_slice = + unsafe { std::slice::from_raw_parts_mut(kvm_cpuid.as_mut_ptr() as *mut u8, num_bytes) }; + + dst_byte_slice.copy_from_slice(src_byte_slice); + + CpuId { + kvm_cpuid, + allocated_len: self.allocated_len, + } + } +} + +#[cfg(test)] +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +impl PartialEq for CpuId { + fn eq(&self, other: &CpuId) -> bool { + let entries: &[kvm_cpuid_entry2] = + unsafe { self.kvm_cpuid[0].entries.as_slice(self.allocated_len) }; + let other_entries: &[kvm_cpuid_entry2] = + unsafe { self.kvm_cpuid[0].entries.as_slice(other.allocated_len) }; + self.allocated_len == other.allocated_len && entries == other_entries + } +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +impl CpuId { + /// Creates a new `CpuId` structure that can contain at most `array_len` KVM CPUID entries. + /// + /// # Arguments + /// + /// * `array_len` - Maximum number of CPUID entries. + /// + pub fn new(array_len: usize) -> CpuId { + let mut kvm_cpuid = vec_with_array_field::(array_len); + kvm_cpuid[0].nent = array_len as u32; + + CpuId { + kvm_cpuid, + allocated_len: array_len, + } + } + + /// Get the mutable entries slice so they can be modified before passing to the VCPU. + /// + pub fn mut_entries_slice(&mut self) -> &mut [kvm_cpuid_entry2] { + // Mapping the unsized array to a slice is unsafe because the length isn't known. Using + // the length we originally allocated with eliminates the possibility of overflow. + if self.kvm_cpuid[0].nent as usize > self.allocated_len { + self.kvm_cpuid[0].nent = self.allocated_len as u32; + } + let nent = self.kvm_cpuid[0].nent as usize; + unsafe { self.kvm_cpuid[0].entries.as_mut_slice(nent) } + } + + /// Get a pointer so it can be passed to the kernel. Using this pointer is unsafe. + /// + pub fn as_ptr(&self) -> *const kvm_cpuid2 { + &self.kvm_cpuid[0] + } + + /// Get a mutable pointer so it can be passed to the kernel. Using this pointer is unsafe. + /// + pub fn as_mut_ptr(&mut self) -> *mut kvm_cpuid2 { + &mut self.kvm_cpuid[0] + } +} + +/// A safe wrapper over the `kvm_run` struct. +/// +/// The wrapper is needed for sending the pointer to `kvm_run` between +/// threads as raw pointers do not implement `Send` and `Sync`. +pub struct KvmRunWrapper { + kvm_run_ptr: *mut u8, +} + +// Send and Sync aren't automatically inherited for the raw address pointer. +// Accessing that pointer is only done through the stateless interface which +// allows the object to be shared by multiple threads without a decrease in +// safety. +unsafe impl Send for KvmRunWrapper {} +unsafe impl Sync for KvmRunWrapper {} + +impl KvmRunWrapper { + /// Maps the first `size` bytes of the given `fd`. + /// + /// # Arguments + /// * `fd` - File descriptor to mmap from. + /// * `size` - Size of memory region in bytes. + pub fn mmap_from_fd(fd: &AsRawFd, size: usize) -> Result { + // This is safe because we are creating a mapping in a place not already used by any other + // area in this process. + let addr = unsafe { + libc::mmap( + null_mut(), + size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_SHARED, + fd.as_raw_fd(), + 0, + ) + }; + if addr == libc::MAP_FAILED { + return Err(io::Error::last_os_error()); + } + + Ok(KvmRunWrapper { + kvm_run_ptr: addr as *mut u8, + }) + } + + /// Returns a mutable reference to `kvm_run`. + /// + #[allow(clippy::mut_from_ref)] + pub fn as_mut_ref(&self) -> &mut kvm_run { + // Safe because we know we mapped enough memory to hold the kvm_run struct because the + // kernel told us how large it was. + #[allow(clippy::cast_ptr_alignment)] + unsafe { + &mut *(self.kvm_run_ptr as *mut kvm_run) + } + } +} diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs new file mode 100644 index 0000000..ff171b8 --- /dev/null +++ b/src/ioctls/system.rs @@ -0,0 +1,337 @@ +use kvm_bindings::*; + +use libc::{open, O_CLOEXEC, O_RDWR}; +use std::fs::File; +use std::io; +use std::os::raw::{c_char, c_ulong}; +use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; + +use cap::Cap; +use ioctls::vec_with_array_field; +use ioctls::vm::{new_vmfd, VmFd}; +use ioctls::{CpuId, Result}; +use kvm_ioctls::*; +use sys_ioctl::*; + +/// A wrapper around opening and using `/dev/kvm`. +/// +/// The handle is used to issue system ioctls. +pub struct Kvm { + kvm: File, +} + +impl Kvm { + /// Opens `/dev/kvm/` and returns a `Kvm` object on success. + /// + #[allow(clippy::new_ret_no_self)] + pub fn new() -> Result { + // Open `/dev/kvm` using `O_CLOEXEC` flag. + let fd = Self::open_with_cloexec(true)?; + // Safe because we verify that ret is valid and we own the fd. + Ok(unsafe { Self::new_with_fd_number(fd) }) + } + + /// Creates a new Kvm object assuming `fd` represents an existing open file descriptor + /// associated with `/dev/kvm`. + /// + /// # Arguments + /// + /// * `fd` - File descriptor for `/dev/kvm`. + /// + pub unsafe fn new_with_fd_number(fd: RawFd) -> Self { + Kvm { + kvm: File::from_raw_fd(fd), + } + } + + /// Opens `/dev/kvm` and returns the fd number on success. + /// + /// # Arguments + /// + /// * `close_on_exec`: If true opens `/dev/kvm` using the `O_CLOEXEC` flag. + /// + pub fn open_with_cloexec(close_on_exec: bool) -> Result { + let open_flags = O_RDWR | if close_on_exec { O_CLOEXEC } else { 0 }; + // Safe because we give a constant nul-terminated string and verify the result. + let ret = unsafe { open("/dev/kvm\0".as_ptr() as *const c_char, open_flags) }; + if ret < 0 { + Err(io::Error::last_os_error()) + } else { + Ok(ret) + } + } + + /// Returns the KVM API version. + /// + /// See the documentation for `KVM_GET_API_VERSION`. + pub fn get_api_version(&self) -> i32 { + // Safe because we know that our file is a KVM fd and that the request is one of the ones + // defined by kernel. + unsafe { ioctl(self, KVM_GET_API_VERSION()) } + } + + /// Query the availability of a particular kvm capability. + /// + /// See the documentation for `KVM_CHECK_EXTENSION`. + /// Returns 0 if the capability is not available and > 0 otherwise. + /// + fn check_extension_int(&self, c: Cap) -> i32 { + // Safe because we know that our file is a KVM fd and that the extension is one of the ones + // defined by kernel. + unsafe { ioctl_with_val(self, KVM_CHECK_EXTENSION(), c as c_ulong) } + } + + /// Checks if a particular `Cap` is available. + /// + /// According to the KVM API doc, `KVM_CHECK_EXTENSION` returns "0 if unsupported; 1 (or some + /// other positive integer) if supported. + /// + /// # Arguments + /// + /// * `c` - KVM capability. + /// + pub fn check_extension(&self, c: Cap) -> bool { + self.check_extension_int(c) >= 1 + } + + /// Gets the size of the mmap required to use vcpu's `kvm_run` structure. + /// + /// See the documentation for `KVM_GET_VCPU_MMAP_SIZE`. + /// + pub fn get_vcpu_mmap_size(&self) -> Result { + // Safe because we know that our file is a KVM fd and we verify the return result. + let res = unsafe { ioctl(self, KVM_GET_VCPU_MMAP_SIZE()) }; + if res > 0 { + Ok(res as usize) + } else { + Err(io::Error::last_os_error()) + } + } + + /// Gets the recommended number of VCPUs per VM. + /// + /// See the documentation for `KVM_CAP_NR_VCPUS`. + /// Default to 4 when `KVM_CAP_NR_VCPUS` is not implemented. + pub fn get_nr_vcpus(&self) -> usize { + let x = self.check_extension_int(Cap::NrVcpus); + if x > 0 { + x as usize + } else { + 4 + } + } + + /// Gets the maximum allowed memory slots per VM. + /// + /// KVM reports the number of available memory slots (`KVM_CAP_NR_MEMSLOTS`) + /// using the extension interface. Both x86 and s390 implement this, ARM + /// and powerpc do not yet enable it. + /// Default to 32 when `KVM_CAP_NR_MEMSLOTS` is not implemented. + /// + pub fn get_nr_memslots(&self) -> usize { + let x = self.check_extension_int(Cap::NrMemslots); + if x > 0 { + x as usize + } else { + 32 + } + } + + /// Gets the recommended maximum number of VCPUs per VM. + /// + /// See the documentation for `KVM_CAP_MAX_VCPUS`. + /// Default to `KVM_CAP_NR_VCPUS` when `KVM_CAP_MAX_VCPUS` is not implemented. + /// + pub fn get_max_vcpus(&self) -> usize { + match self.check_extension_int(Cap::MaxVcpus) { + 0 => self.get_nr_vcpus(), + x => x as usize, + } + } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn get_cpuid(&self, kind: u64, max_entries_count: usize) -> Result { + let mut cpuid = CpuId::new(max_entries_count); + + let ret = unsafe { + // ioctl is unsafe. The kernel is trusted not to write beyond the bounds of the memory + // allocated for the struct. The limit is read from nent, which is set to the allocated + // size(max_entries_count) above. + ioctl_with_mut_ptr(self, kind, cpuid.as_mut_ptr()) + }; + if ret < 0 { + return Err(io::Error::last_os_error()); + } + + Ok(cpuid) + } + + /// X86 specific call to get the system emulated CPUID values. + /// + /// See the documentation for KVM_GET_EMULATED_CPUID. + /// + /// # Arguments + /// + /// * `max_entries_count` - Maximum number of CPUID entries. This function can return less than + /// this when the hardware does not support so many CPUID entries. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn get_emulated_cpuid(&self, max_entries_count: usize) -> Result { + self.get_cpuid(KVM_GET_EMULATED_CPUID(), max_entries_count) + } + + /// X86 specific call to get the system supported CPUID values. + /// + /// See the documentation for KVM_GET_SUPPORTED_CPUID. + /// + /// # Arguments + /// + /// * `max_entries_count` - Maximum number of CPUID entries. This function can return less than + /// this when the hardware does not support so many CPUID entries. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn get_supported_cpuid(&self, max_entries_count: usize) -> Result { + self.get_cpuid(KVM_GET_SUPPORTED_CPUID(), max_entries_count) + } + + /// X86 specific call to get list of supported MSRS + /// + /// See the documentation for KVM_GET_MSR_INDEX_LIST. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn get_msr_index_list(&self) -> Result> { + const MAX_KVM_MSR_ENTRIES: usize = 256; + + let mut msr_list = vec_with_array_field::(MAX_KVM_MSR_ENTRIES); + msr_list[0].nmsrs = MAX_KVM_MSR_ENTRIES as u32; + + let ret = unsafe { + // ioctl is unsafe. The kernel is trusted not to write beyond the bounds of the memory + // allocated for the struct. The limit is read from nmsrs, which is set to the allocated + // size (MAX_KVM_MSR_ENTRIES) above. + ioctl_with_mut_ref(self, KVM_GET_MSR_INDEX_LIST(), &mut msr_list[0]) + }; + if ret < 0 { + return Err(io::Error::last_os_error()); + } + + let mut nmsrs = msr_list[0].nmsrs; + + // Mapping the unsized array to a slice is unsafe because the length isn't known. Using + // the length we originally allocated with eliminates the possibility of overflow. + let indices: &[u32] = unsafe { + if nmsrs > MAX_KVM_MSR_ENTRIES as u32 { + nmsrs = MAX_KVM_MSR_ENTRIES as u32; + } + msr_list[0].indices.as_slice(nmsrs as usize) + }; + + Ok(indices.to_vec()) + } + + /// Creates a VM fd using the KVM fd. + /// + /// See the documentation for `KVM_CREATE_VM`. + /// A call to this function will also initialize the size of the vcpu mmap area using the + /// `KVM_GET_VCPU_MMAP_SIZE` ioctl. + /// + pub fn create_vm(&self) -> Result { + // Safe because we know kvm is a real kvm fd as this module is the only one that can make + // Kvm objects. + let ret = unsafe { ioctl(&self.kvm, KVM_CREATE_VM()) }; + if ret >= 0 { + // Safe because we verify the value of ret and we are the owners of the fd. + let vm_file = unsafe { File::from_raw_fd(ret) }; + let run_mmap_size = self.get_vcpu_mmap_size()?; + Ok(new_vmfd(vm_file, run_mmap_size)) + } else { + Err(io::Error::last_os_error()) + } + } +} + +impl AsRawFd for Kvm { + fn as_raw_fd(&self) -> RawFd { + self.kvm.as_raw_fd() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use MAX_KVM_CPUID_ENTRIES; + + #[test] + fn test_kvm_new() { + Kvm::new().unwrap(); + } + + #[test] + fn test_kvm_api_version() { + let kvm = Kvm::new().unwrap(); + assert_eq!(kvm.get_api_version(), 12); + assert!(kvm.check_extension(Cap::UserMemory)); + } + + #[test] + fn test_kvm_getters() { + let kvm = Kvm::new().unwrap(); + + // vCPU related getters + let nr_vcpus = kvm.get_nr_vcpus(); + assert!(nr_vcpus >= 4); + + assert!(kvm.get_max_vcpus() >= nr_vcpus); + + // Memory related getters + assert!(kvm.get_vcpu_mmap_size().unwrap() > 0); + assert!(kvm.get_nr_memslots() >= 32); + } + + #[test] + fn test_create_vm() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + + assert_eq!(vm.get_run_size(), kvm.get_vcpu_mmap_size().unwrap()); + } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[test] + fn test_get_supported_cpuid() { + let kvm = Kvm::new().unwrap(); + let mut cpuid = kvm.get_supported_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); + let cpuid_entries = cpuid.mut_entries_slice(); + assert!(cpuid_entries.len() > 0); + assert!(cpuid_entries.len() <= MAX_KVM_CPUID_ENTRIES); + } + + #[test] + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn test_get_emulated_cpuid() { + let kvm = Kvm::new().unwrap(); + let mut cpuid = kvm.get_emulated_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); + let cpuid_entries = cpuid.mut_entries_slice(); + assert!(cpuid_entries.len() > 0); + assert!(cpuid_entries.len() <= MAX_KVM_CPUID_ENTRIES); + } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[test] + fn test_cpuid_clone() { + let kvm = Kvm::new().unwrap(); + let cpuid_1 = kvm.get_supported_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); + let mut cpuid_2 = cpuid_1.clone(); + assert!(cpuid_1 == cpuid_2); + cpuid_2 = unsafe { std::mem::zeroed() }; + assert!(cpuid_1 != cpuid_2); + } + + #[test] + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn get_msr_index_list() { + let kvm = Kvm::new().unwrap(); + let msr_list = kvm.get_msr_index_list().unwrap(); + assert!(msr_list.len() >= 2); + } +} diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs new file mode 100644 index 0000000..0bb4963 --- /dev/null +++ b/src/ioctls/vcpu.rs @@ -0,0 +1,513 @@ +use kvm_bindings::*; +use libc::EINVAL; +use std::fs::File; +use std::io; +use std::os::unix::io::{AsRawFd, RawFd}; + +use ioctls::{CpuId, KvmRunWrapper, Result}; +use kvm_ioctls::*; +use sys_ioctl::*; + +/// Reasons for vcpu exits. The exit reasons are mapped to the `KVM_EXIT_*` defines +/// from `include/uapi/linux/kvm.h`. +#[derive(Debug)] +pub enum VcpuExit<'a> { + /// An out port instruction was run on the given port with the given data. + IoOut(u16 /* port */, &'a [u8] /* data */), + /// An in port instruction was run on the given port. + /// + /// The given slice should be filled in before `Vcpu::run` is called again. + IoIn(u16 /* port */, &'a mut [u8] /* data */), + /// A read instruction was run against the given MMIO address. + /// + /// The given slice should be filled in before `Vcpu::run` is called again. + MmioRead(u64 /* address */, &'a mut [u8]), + /// A write instruction was run against the given MMIO address with the given data. + MmioWrite(u64 /* address */, &'a [u8]), + /// Corresponds to KVM_EXIT_UNKNOWN. + Unknown, + /// Corresponds to KVM_EXIT_EXCEPTION. + Exception, + /// Corresponds to KVM_EXIT_HYPERCALL. + Hypercall, + /// Corresponds to KVM_EXIT_DEBUG. + Debug, + /// Corresponds to KVM_EXIT_HLT. + Hlt, + /// Corresponds to KVM_EXIT_IRQ_WINDOW_OPEN. + IrqWindowOpen, + /// Corresponds to KVM_EXIT_SHUTDOWN. + Shutdown, + /// Corresponds to KVM_EXIT_FAIL_ENTRY. + FailEntry, + /// Corresponds to KVM_EXIT_INTR. + Intr, + /// Corresponds to KVM_EXIT_SET_TPR. + SetTpr, + /// Corresponds to KVM_EXIT_TPR_ACCESS. + TprAccess, + /// Corresponds to KVM_EXIT_S390_SIEIC. + S390Sieic, + /// Corresponds to KVM_EXIT_S390_RESET. + S390Reset, + /// Corresponds to KVM_EXIT_DCR. + Dcr, + /// Corresponds to KVM_EXIT_NMI. + Nmi, + /// Corresponds to KVM_EXIT_INTERNAL_ERROR. + InternalError, + /// Corresponds to KVM_EXIT_OSI. + Osi, + /// Corresponds to KVM_EXIT_PAPR_HCALL. + PaprHcall, + /// Corresponds to KVM_EXIT_S390_UCONTROL. + S390Ucontrol, + /// Corresponds to KVM_EXIT_WATCHDOG. + Watchdog, + /// Corresponds to KVM_EXIT_S390_TSCH. + S390Tsch, + /// Corresponds to KVM_EXIT_EPR. + Epr, + /// Corresponds to KVM_EXIT_SYSTEM_EVENT. + SystemEvent, + /// Corresponds to KVM_EXIT_S390_STSI. + S390Stsi, + /// Corresponds to KVM_EXIT_IOAPIC_EOI. + IoapicEoi, + /// Corresponds to KVM_EXIT_HYPERV. + Hyperv, +} + +/// A wrapper around creating and using a kvm related VCPU fd +pub struct VcpuFd { + vcpu: File, + kvm_run_ptr: KvmRunWrapper, +} + +impl VcpuFd { + /// Gets the VCPU registers using the `KVM_GET_REGS` ioctl. + /// + #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] + pub fn get_regs(&self) -> Result { + // Safe because we know that our file is a VCPU fd, we know the kernel will only read the + // correct amount of memory from our pointer, and we verify the return result. + let mut regs = unsafe { std::mem::zeroed() }; + let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_REGS(), &mut regs) }; + if ret != 0 { + return Err(io::Error::last_os_error()); + } + Ok(regs) + } + + /// Sets the VCPU registers using `KVM_SET_REGS` ioctl. + /// + /// # Arguments + /// + /// * `regs` - Registers being set. + /// + #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] + pub fn set_regs(&self, regs: &kvm_regs) -> Result<()> { + // Safe because we know that our file is a VCPU fd, we know the kernel will only read the + // correct amount of memory from our pointer, and we verify the return result. + let ret = unsafe { ioctl_with_ref(self, KVM_SET_REGS(), regs) }; + if ret != 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) + } + + /// Gets the VCPU special registers using `KVM_GET_SREGS` ioctl. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn get_sregs(&self) -> Result { + // Safe because we know that our file is a VCPU fd, we know the kernel will only write the + // correct amount of memory to our pointer, and we verify the return result. + let mut regs = kvm_sregs::default(); + + let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_SREGS(), &mut regs) }; + if ret != 0 { + return Err(io::Error::last_os_error()); + } + Ok(regs) + } + + /// Sets the VCPU special registers using `KVM_SET_SREGS` ioctl. + /// + /// # Arguments + /// + /// * `sregs` - Special registers to be set. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn set_sregs(&self, sregs: &kvm_sregs) -> Result<()> { + // Safe because we know that our file is a VCPU fd, we know the kernel will only read the + // correct amount of memory from our pointer, and we verify the return result. + let ret = unsafe { ioctl_with_ref(self, KVM_SET_SREGS(), sregs) }; + if ret != 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) + } + + /// X86 specific call that gets the FPU-related structure. + /// + /// See the documentation for `KVM_GET_FPU`. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn get_fpu(&self) -> Result { + let mut fpu = kvm_fpu::default(); + + let ret = unsafe { + // Here we trust the kernel not to read past the end of the kvm_fpu struct. + ioctl_with_mut_ref(self, KVM_GET_FPU(), &mut fpu) + }; + if ret != 0 { + return Err(io::Error::last_os_error()); + } + Ok(fpu) + } + + /// X86 specific call to setup the FPU. + /// + /// See the documentation for `KVM_SET_FPU`. + /// + /// # Arguments + /// + /// * `fpu` - FPU configurations struct. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn set_fpu(&self, fpu: &kvm_fpu) -> Result<()> { + let ret = unsafe { + // Here we trust the kernel not to read past the end of the kvm_fpu struct. + ioctl_with_ref(self, KVM_SET_FPU(), fpu) + }; + if ret < 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) + } + + /// X86 specific call to setup the CPUID registers. + /// + /// See the documentation for `KVM_SET_CPUID2`. + /// + /// # Arguments + /// + /// * `cpuid` - CPUID registers. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn set_cpuid2(&self, cpuid: &CpuId) -> Result<()> { + let ret = unsafe { + // Here we trust the kernel not to read past the end of the kvm_msrs struct. + ioctl_with_ptr(self, KVM_SET_CPUID2(), cpuid.as_ptr()) + }; + if ret < 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) + } + + /// X86 specific call to get the state of the LAPIC (Local Advanced Programmable Interrupt + /// Controller). + /// + /// See the documentation for `KVM_GET_LAPIC`. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn get_lapic(&self) -> Result { + let mut klapic = kvm_lapic_state::default(); + + let ret = unsafe { + // The ioctl is unsafe unless you trust the kernel not to write past the end of the + // local_apic struct. + ioctl_with_mut_ref(self, KVM_GET_LAPIC(), &mut klapic) + }; + if ret < 0 { + return Err(io::Error::last_os_error()); + } + Ok(klapic) + } + + /// X86 specific call to set the state of the LAPIC (Local Advanced Programmable Interrupt + /// Controller). + /// + /// See the documentation for `KVM_SET_LAPIC`. + /// + /// # Arguments + /// + /// * `klapic` - LAPIC state registers. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn set_lapic(&self, klapic: &kvm_lapic_state) -> Result<()> { + let ret = unsafe { + // The ioctl is safe because the kernel will only read from the klapic struct. + ioctl_with_ref(self, KVM_SET_LAPIC(), klapic) + }; + if ret < 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) + } + + /// X86 specific call to read model-specific registers for this VCPU. + /// + /// It emulates `KVM_GET_MSRS` ioctl's behavior by returning the number of MSRs + /// successfully read upon success or the last error number in case of failure. + /// + /// # Arguments + /// + /// * `msrs` - MSRs to be read. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn get_msrs(&self, msrs: &mut kvm_msrs) -> Result<(i32)> { + let ret = unsafe { + // Here we trust the kernel not to read past the end of the kvm_msrs struct. + ioctl_with_mut_ref(self, KVM_GET_MSRS(), msrs) + }; + if ret < 0 { + return Err(io::Error::last_os_error()); + } + Ok(ret) + } + + /// X86 specific call to setup the MSRS. + /// + /// See the documentation for `KVM_SET_MSRS`. + /// + /// # Arguments + /// + /// * `kvm_msrs` - MSRs to be written. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn set_msrs(&self, msrs: &kvm_msrs) -> Result<()> { + let ret = unsafe { + // Here we trust the kernel not to read past the end of the kvm_msrs struct. + ioctl_with_ref(self, KVM_SET_MSRS(), msrs) + }; + if ret < 0 { + // KVM_SET_MSRS actually returns the number of msr entries written. + return Err(io::Error::last_os_error()); + } + Ok(()) + } + + /// Triggers the running of the current virtual CPU returning an exit reason. + /// + pub fn run(&self) -> Result { + // Safe because we know that our file is a VCPU fd and we verify the return result. + let ret = unsafe { ioctl(self, KVM_RUN()) }; + if ret == 0 { + let run = self.kvm_run_ptr.as_mut_ref(); + match run.exit_reason { + // make sure you treat all possible exit reasons from include/uapi/linux/kvm.h corresponding + // when upgrading to a different kernel version + KVM_EXIT_UNKNOWN => Ok(VcpuExit::Unknown), + KVM_EXIT_EXCEPTION => Ok(VcpuExit::Exception), + KVM_EXIT_IO => { + let run_start = run as *mut kvm_run as *mut u8; + // Safe because the exit_reason (which comes from the kernel) told us which + // union field to use. + let io = unsafe { run.__bindgen_anon_1.io }; + let port = io.port; + let data_size = io.count as usize * io.size as usize; + // The data_offset is defined by the kernel to be some number of bytes into the + // kvm_run stucture, which we have fully mmap'd. + let data_ptr = unsafe { run_start.offset(io.data_offset as isize) }; + // The slice's lifetime is limited to the lifetime of this Vcpu, which is equal + // to the mmap of the kvm_run struct that this is slicing from + let data_slice = unsafe { + std::slice::from_raw_parts_mut::(data_ptr as *mut u8, data_size) + }; + match u32::from(io.direction) { + KVM_EXIT_IO_IN => Ok(VcpuExit::IoIn(port, data_slice)), + KVM_EXIT_IO_OUT => Ok(VcpuExit::IoOut(port, data_slice)), + _ => Err(io::Error::from_raw_os_error(EINVAL)), + } + } + KVM_EXIT_HYPERCALL => Ok(VcpuExit::Hypercall), + KVM_EXIT_DEBUG => Ok(VcpuExit::Debug), + KVM_EXIT_HLT => Ok(VcpuExit::Hlt), + KVM_EXIT_MMIO => { + // Safe because the exit_reason (which comes from the kernel) told us which + // union field to use. + let mmio = unsafe { &mut run.__bindgen_anon_1.mmio }; + let addr = mmio.phys_addr; + let len = mmio.len as usize; + let data_slice = &mut mmio.data[..len]; + if mmio.is_write != 0 { + Ok(VcpuExit::MmioWrite(addr, data_slice)) + } else { + Ok(VcpuExit::MmioRead(addr, data_slice)) + } + } + KVM_EXIT_IRQ_WINDOW_OPEN => Ok(VcpuExit::IrqWindowOpen), + KVM_EXIT_SHUTDOWN => Ok(VcpuExit::Shutdown), + KVM_EXIT_FAIL_ENTRY => Ok(VcpuExit::FailEntry), + KVM_EXIT_INTR => Ok(VcpuExit::Intr), + KVM_EXIT_SET_TPR => Ok(VcpuExit::SetTpr), + KVM_EXIT_TPR_ACCESS => Ok(VcpuExit::TprAccess), + KVM_EXIT_S390_SIEIC => Ok(VcpuExit::S390Sieic), + KVM_EXIT_S390_RESET => Ok(VcpuExit::S390Reset), + KVM_EXIT_DCR => Ok(VcpuExit::Dcr), + KVM_EXIT_NMI => Ok(VcpuExit::Nmi), + KVM_EXIT_INTERNAL_ERROR => Ok(VcpuExit::InternalError), + KVM_EXIT_OSI => Ok(VcpuExit::Osi), + KVM_EXIT_PAPR_HCALL => Ok(VcpuExit::PaprHcall), + KVM_EXIT_S390_UCONTROL => Ok(VcpuExit::S390Ucontrol), + KVM_EXIT_WATCHDOG => Ok(VcpuExit::Watchdog), + KVM_EXIT_S390_TSCH => Ok(VcpuExit::S390Tsch), + KVM_EXIT_EPR => Ok(VcpuExit::Epr), + KVM_EXIT_SYSTEM_EVENT => Ok(VcpuExit::SystemEvent), + KVM_EXIT_S390_STSI => Ok(VcpuExit::S390Stsi), + KVM_EXIT_IOAPIC_EOI => Ok(VcpuExit::IoapicEoi), + KVM_EXIT_HYPERV => Ok(VcpuExit::Hyperv), + r => panic!("unknown kvm exit reason: {}", r), + } + } else { + Err(io::Error::last_os_error()) + } + } +} + +/// Helper function to create a new VcpuFd. +/// +/// This should not be exported as a public function because the preferred way is to use +/// `create_vcpu` from `VmFd`. The function cannot be part of the Vcpu implementation because +/// then it would be exported with the public VcpuFd interface. +pub fn new_vcpu(vcpu: File, kvm_run_ptr: KvmRunWrapper) -> VcpuFd { + VcpuFd { vcpu, kvm_run_ptr } +} + +impl AsRawFd for VcpuFd { + fn as_raw_fd(&self) -> RawFd { + self.vcpu.as_raw_fd() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ioctls::system::Kvm; + + use std::ptr::null_mut; + + // Helper function for mmap an anonymous memory of `size`. + // Panics if the mmap fails. + fn mmap_anonymous(size: usize) -> *mut u8 { + let addr = unsafe { + libc::mmap( + null_mut(), + size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_ANONYMOUS | libc::MAP_SHARED | libc::MAP_NORESERVE, + -1, + 0, + ) + }; + if addr == libc::MAP_FAILED { + panic!("mmap failed."); + } + + return addr as *mut u8; + } + + #[test] + fn test_create_vcpu() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + + assert!(vm.create_vcpu(0).is_ok()); + } + + #[cfg(target_arch = "x86_64")] + #[test] + fn test_run_code() { + use std::io::Write; + + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + // This example based on https://lwn.net/Articles/658511/ + let code = [ + 0xba, 0xf8, 0x03, /* mov $0x3f8, %dx */ + 0x00, 0xd8, /* add %bl, %al */ + 0x04, b'0', /* add $'0', %al */ + 0xee, /* out %al, %dx */ + 0xec, /* in %dx, %al */ + 0xc6, 0x06, 0x00, 0x80, 0x00, /* movl $0, (0x8000) */ + 0x8a, 0x16, 0x00, 0x80, /* movl (0x8000), %dl */ + 0xc6, 0x06, 0x00, 0x20, 0x00, /* movl $0, (0x2000) */ + 0xf4, /* hlt */ + ]; + + let mem_size = 0x4000; + let load_addr = mmap_anonymous(mem_size); + let guest_addr: u64 = 0x1000; + let slot: u32 = 0; + let mem_region = kvm_userspace_memory_region { + slot, + guest_phys_addr: guest_addr, + memory_size: mem_size as u64, + userspace_addr: load_addr as u64, + flags: KVM_MEM_LOG_DIRTY_PAGES, + }; + vm.set_user_memory_region(mem_region).unwrap(); + + unsafe { + // Get a mutable slice of `mem_size` from `load_addr`. + // This is safe because we mapped it before. + let mut slice = std::slice::from_raw_parts_mut(load_addr, mem_size); + slice.write(&code).unwrap(); + } + + let vcpu_fd = vm.create_vcpu(0).unwrap(); + + let mut vcpu_sregs = vcpu_fd.get_sregs().unwrap(); + assert_ne!(vcpu_sregs.cs.base, 0); + assert_ne!(vcpu_sregs.cs.selector, 0); + vcpu_sregs.cs.base = 0; + vcpu_sregs.cs.selector = 0; + vcpu_fd.set_sregs(&vcpu_sregs).unwrap(); + + let mut vcpu_regs = vcpu_fd.get_regs().unwrap(); + // Set the Instruction Pointer to the guest address where we loaded the code. + vcpu_regs.rip = guest_addr; + vcpu_regs.rax = 2; + vcpu_regs.rbx = 3; + vcpu_regs.rflags = 2; + vcpu_fd.set_regs(&vcpu_regs).unwrap(); + + loop { + match vcpu_fd.run().expect("run failed") { + VcpuExit::IoIn(addr, data) => { + assert_eq!(addr, 0x3f8); + assert_eq!(data.len(), 1); + } + VcpuExit::IoOut(addr, data) => { + assert_eq!(addr, 0x3f8); + assert_eq!(data.len(), 1); + assert_eq!(data[0], b'5'); + } + VcpuExit::MmioRead(addr, data) => { + assert_eq!(addr, 0x8000); + assert_eq!(data.len(), 1); + } + VcpuExit::MmioWrite(addr, data) => { + assert_eq!(addr, 0x8000); + assert_eq!(data.len(), 1); + assert_eq!(data[0], 0); + } + VcpuExit::Hlt => { + // The code snippet dirties 2 pages: + // * one when the code itself is loaded in memory; + // * and one more from the `movl` that writes to address 0x8000 + let dirty_pages_bitmap = vm.get_dirty_log(slot, mem_size).unwrap(); + let dirty_pages = dirty_pages_bitmap + .into_iter() + .map(|page| page.count_ones()) + .fold(0, |dirty_page_count, i| dirty_page_count + i); + assert_eq!(dirty_pages, 2); + break; + } + r => panic!("unexpected exit reason: {:?}", r), + } + } + } +} diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs new file mode 100644 index 0000000..6dfddea --- /dev/null +++ b/src/ioctls/vm.rs @@ -0,0 +1,369 @@ +use ioctls::Result; +use kvm_bindings::*; +use std::fs::File; +use std::io; +use std::os::raw::{c_ulong, c_void}; +use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; + +use ioctls::vcpu::new_vcpu; +use ioctls::vcpu::VcpuFd; +use ioctls::KvmRunWrapper; +use kvm_ioctls::*; +use sys_ioctl::*; + +/// An address either in programmable I/O space or in memory mapped I/O space. +pub enum IoEventAddress { + /// Representation of an programmable I/O address. + Pio(u64), + /// Representation of an memory mapped I/O address. + Mmio(u64), +} + +/// Used in `VmFd::register_ioevent` to indicate that no datamatch is requested. +pub struct NoDatamatch; +impl Into for NoDatamatch { + fn into(self) -> u64 { + 0 + } +} + +/// A wrapper around creating and using a VM. +pub struct VmFd { + vm: File, + run_size: usize, +} + +impl VmFd { + /// Creates/modifies a guest physical memory slot. + /// + /// See the documentation for `KVM_SET_USER_MEMORY_REGION`. + /// + pub fn set_user_memory_region( + &self, + user_memory_region: kvm_userspace_memory_region, + ) -> Result<()> { + let ret = + unsafe { ioctl_with_ref(self, KVM_SET_USER_MEMORY_REGION(), &user_memory_region) }; + if ret == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + + /// Sets the address of the three-page region in the VM's address space. + /// + /// See the documentation on the `KVM_SET_TSS_ADDR` ioctl. + /// + /// # Arguments + /// + /// * `offset` - Physical address of a three-page region in the guest's physical address space. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn set_tss_address(&self, offset: usize) -> Result<()> { + // Safe because we know that our file is a VM fd and we verify the return result. + let ret = unsafe { ioctl_with_val(self, KVM_SET_TSS_ADDR(), offset as c_ulong) }; + if ret == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + + /// Creates an in-kernel interrupt controller. + /// + /// See the documentation for `KVM_CREATE_IRQCHIP`. + /// + #[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" + ))] + pub fn create_irq_chip(&self) -> Result<()> { + // Safe because we know that our file is a VM fd and we verify the return result. + let ret = unsafe { ioctl(self, KVM_CREATE_IRQCHIP()) }; + if ret == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + + /// Creates a PIT as per the `KVM_CREATE_PIT2` ioctl. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn create_pit2(&self, pit_config: kvm_pit_config) -> Result<()> { + // Safe because we know that our file is a VM fd, we know the kernel will only read the + // correct amount of memory from our pointer, and we verify the return result. + let ret = unsafe { ioctl_with_ref(self, KVM_CREATE_PIT2(), &pit_config) }; + if ret == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + + /// Registers an event to be signaled whenever a certain address is written to. + /// + /// # Arguments + /// + /// * `fd` - FD which will be signaled. When signaling, the usual `vmexit` to userspace + /// is prevented. + /// * `addr` - Address being written to. + /// * `datamatch` - Limits signaling `fd` to only the cases where the value being written is + /// equal to this parameter. The size of `datamatch` is important and it must + /// match the expected size of the guest's write. + /// + pub fn register_ioevent>( + &self, + fd: RawFd, + addr: &IoEventAddress, + datamatch: T, + ) -> Result<()> { + let mut flags = 0; + if std::mem::size_of::() > 0 { + flags |= 1 << kvm_ioeventfd_flag_nr_datamatch + } + if let IoEventAddress::Pio(_) = *addr { + flags |= 1 << kvm_ioeventfd_flag_nr_pio + } + + let ioeventfd = kvm_ioeventfd { + datamatch: datamatch.into(), + len: std::mem::size_of::() as u32, + addr: match addr { + IoEventAddress::Pio(ref p) => *p as u64, + IoEventAddress::Mmio(ref m) => *m, + }, + fd, + flags, + ..Default::default() + }; + // Safe because we know that our file is a VM fd, we know the kernel will only read the + // correct amount of memory from our pointer, and we verify the return result. + let ret = unsafe { ioctl_with_ref(self, KVM_IOEVENTFD(), &ioeventfd) }; + if ret == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + + /// Gets the bitmap of pages dirtied since the last call of this function. + /// + /// Leverages the dirty page logging feature in KVM. As a side-effect, this also resets the + /// bitmap inside the kernel. + /// + /// # Arguments + /// + /// * `slot` - Guest memory slot identifier. + /// * `memory_size` - Size of the memory region. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn get_dirty_log(&self, slot: u32, memory_size: usize) -> Result> { + // Compute the length of the bitmap needed for all dirty pages in one memory slot. + // One memory page is 4KiB (4096 bits) and KVM_GET_DIRTY_LOG returns one dirty bit for + // each page. + let page_size = 4 << 10; + + let div_round_up = |dividend, divisor| (dividend + divisor - 1) / divisor; + // For ease of access we are saving the bitmap in a u64 vector. We are using ceil to + // make sure we count all dirty pages even when `mem_size` is not a multiple of + // page_size * 64. + let bitmap_size = div_round_up(memory_size, page_size * 64); + let mut bitmap = vec![0; bitmap_size]; + let b_data = bitmap.as_mut_ptr() as *mut c_void; + let dirtylog = kvm_dirty_log { + slot, + padding1: 0, + __bindgen_anon_1: kvm_dirty_log__bindgen_ty_1 { + dirty_bitmap: b_data, + }, + }; + // Safe because we know that our file is a VM fd, and we know that the amount of memory + // we allocated for the bitmap is at least one bit per page. + let ret = unsafe { ioctl_with_ref(self, KVM_GET_DIRTY_LOG(), &dirtylog) }; + if ret == 0 { + Ok(bitmap) + } else { + Err(io::Error::last_os_error()) + } + } + + /// Registers an event that will, when signaled, trigger the `gsi` IRQ. + /// + /// # Arguments + /// + /// * `fd` - Event to be signaled. + /// * `gsi` - IRQ to be triggered. + /// + #[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" + ))] + pub fn register_irqfd(&self, fd: RawFd, gsi: u32) -> Result<()> { + let irqfd = kvm_irqfd { + fd: fd as u32, + gsi, + ..Default::default() + }; + // Safe because we know that our file is a VM fd, we know the kernel will only read the + // correct amount of memory from our pointer, and we verify the return result. + let ret = unsafe { ioctl_with_ref(self, KVM_IRQFD(), &irqfd) }; + if ret == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + + /// Creates a new KVM VCPU fd. + /// + /// # Arguments + /// + /// * `id` - The CPU number between [0, max vcpus). + /// + /// # Errors + /// Returns an error when the VM fd is invalid or the VCPU memory cannot be mapped correctly. + /// + pub fn create_vcpu(&self, id: u8) -> Result { + // Safe because we know that vm is a VM fd and we verify the return result. + #[allow(clippy::cast_lossless)] + let vcpu_fd = unsafe { ioctl_with_val(&self.vm, KVM_CREATE_VCPU(), id as c_ulong) }; + if vcpu_fd < 0 { + return Err(io::Error::last_os_error()); + } + + // Wrap the vcpu now in case the following ? returns early. This is safe because we verified + // the value of the fd and we own the fd. + let vcpu = unsafe { File::from_raw_fd(vcpu_fd) }; + + let kvm_run_ptr = KvmRunWrapper::mmap_from_fd(&vcpu, self.run_size)?; + + Ok(new_vcpu(vcpu, kvm_run_ptr)) + } + + /// Get the `kvm_run` size. + pub fn get_run_size(&self) -> usize { + self.run_size + } +} + +/// Helper function to create a new VcpuFd. +/// +/// This should not be exported as a public function because the preferred way is to use +/// `create_vm` from `Kvm`. The function cannot be part of the Vcpu implementation because +/// then it would be exported with the public VcpuFd interface. +pub fn new_vmfd(vm: File, run_size: usize) -> VmFd { + VmFd { vm, run_size } +} + +impl AsRawFd for VmFd { + fn as_raw_fd(&self) -> RawFd { + self.vm.as_raw_fd() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use {Cap, Kvm, MAX_KVM_CPUID_ENTRIES}; + + use libc::{eventfd, EFD_NONBLOCK}; + + #[test] + fn test_set_invalid_memory() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let invalid_mem_region = kvm_userspace_memory_region { + slot: 0, + guest_phys_addr: 0, + memory_size: 0, + userspace_addr: 0, + flags: 0, + }; + assert!(vm.set_user_memory_region(invalid_mem_region).is_err()); + } + + #[test] + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn test_set_tss_address() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + assert!(vm.set_tss_address(0xfffb_d000).is_ok()); + } + + #[test] + #[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" + ))] + fn test_create_irq_chip() { + let kvm = Kvm::new().unwrap(); + assert!(kvm.check_extension(Cap::Irqchip)); + let vm = kvm.create_vm().unwrap(); + assert!(vm.create_irq_chip().is_ok()); + } + + #[test] + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn test_create_pit2() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + assert!(vm.create_pit2(kvm_pit_config::default()).is_ok()); + } + + #[test] + fn test_register_ioevent() { + assert_eq!(std::mem::size_of::(), 0); + + let kvm = Kvm::new().unwrap(); + let vm_fd = kvm.create_vm().unwrap(); + let evtfd = unsafe { eventfd(0, EFD_NONBLOCK) }; + assert!(vm_fd + .register_ioevent(evtfd, &IoEventAddress::Pio(0xf4), NoDatamatch) + .is_ok()); + assert!(vm_fd + .register_ioevent(evtfd, &IoEventAddress::Mmio(0x1000), NoDatamatch) + .is_ok()); + assert!(vm_fd + .register_ioevent(evtfd, &IoEventAddress::Pio(0xc1), 0x7fu8) + .is_ok()); + assert!(vm_fd + .register_ioevent(evtfd, &IoEventAddress::Pio(0xc2), 0x1337u16) + .is_ok()); + assert!(vm_fd + .register_ioevent(evtfd, &IoEventAddress::Pio(0xc4), 0xdead_beefu32) + .is_ok()); + assert!(vm_fd + .register_ioevent(evtfd, &IoEventAddress::Pio(0xc8), 0xdead_beef_dead_beefu64) + .is_ok()); + } + + #[test] + #[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" + ))] + fn test_register_irqfd() { + let kvm = Kvm::new().unwrap(); + let vm_fd = kvm.create_vm().unwrap(); + let evtfd1 = unsafe { eventfd(0, EFD_NONBLOCK) }; + let evtfd2 = unsafe { eventfd(0, EFD_NONBLOCK) }; + let evtfd3 = unsafe { eventfd(0, EFD_NONBLOCK) }; + + assert!(vm_fd.register_irqfd(evtfd1, 4).is_ok()); + assert!(vm_fd.register_irqfd(evtfd2, 8).is_ok()); + assert!(vm_fd.register_irqfd(evtfd3, 4).is_ok()); + + assert!(vm_fd.register_irqfd(evtfd3, 4).is_err()); + assert!(vm_fd.register_irqfd(evtfd3, 5).is_err()); + } +} diff --git a/src/lib.rs b/src/lib.rs index 4e8c4ba..afb9856 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,1346 +17,13 @@ mod sys_ioctl; #[macro_use] mod kvm_ioctls; mod cap; +mod ioctls; -use kvm_bindings::*; -use libc::{open, EINVAL, O_CLOEXEC, O_RDWR}; -use std::fs::File; -use std::mem::size_of; -use std::os::raw::{c_char, c_ulong, c_void}; -use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; -use std::ptr::null_mut; -use std::{io, result}; - -pub use self::cap::Cap; -use self::kvm_ioctls::*; -use self::sys_ioctl::*; - -/// Wrapper over possible Kvm Result. -pub type Result = result::Result; +pub use cap::Cap; +pub use ioctls::system::Kvm; +pub use ioctls::vcpu::VcpuFd; +pub use ioctls::vm::VmFd; +pub use ioctls::{CpuId, KvmRunWrapper, Result}; /// Taken from Linux Kernel v4.14.13 (arch/x86/include/asm/kvm_host.h) pub const MAX_KVM_CPUID_ENTRIES: usize = 80; - -// Returns a `Vec` with a size in bytes at least as large as `size_in_bytes`. -fn vec_with_size_in_bytes(size_in_bytes: usize) -> Vec { - let rounded_size = (size_in_bytes + size_of::() - 1) / size_of::(); - let mut v = Vec::with_capacity(rounded_size); - for _ in 0..rounded_size { - v.push(T::default()) - } - v -} - -// The kvm API has many structs that resemble the following `Foo` structure: -// -// ``` -// #[repr(C)] -// struct Foo { -// some_data: u32 -// entries: __IncompleteArrayField<__u32>, -// } -// ``` -// -// In order to allocate such a structure, `size_of::()` would be too small because it would not -// include any space for `entries`. To make the allocation large enough while still being aligned -// for `Foo`, a `Vec` is created. Only the first element of `Vec` would actually be used -// as a `Foo`. The remaining memory in the `Vec` is for `entries`, which must be contiguous -// with `Foo`. This function is used to make the `Vec` with enough space for `count` entries. -fn vec_with_array_field(count: usize) -> Vec { - let element_space = count * size_of::(); - let vec_size_bytes = size_of::() + element_space; - vec_with_size_in_bytes(vec_size_bytes) -} - -/// A wrapper around opening and using `/dev/kvm`. -/// -/// The handle is used to issue system ioctls. -pub struct Kvm { - kvm: File, -} - -impl Kvm { - /// Opens `/dev/kvm/` and returns a `Kvm` object on success. - /// - #[allow(clippy::new_ret_no_self)] - pub fn new() -> Result { - // Open `/dev/kvm` using `O_CLOEXEC` flag. - let fd = Self::open_with_cloexec(true)?; - // Safe because we verify that ret is valid and we own the fd. - Ok(unsafe { Self::new_with_fd_number(fd) }) - } - - /// Creates a new Kvm object assuming `fd` represents an existing open file descriptor - /// associated with `/dev/kvm`. - /// - /// # Arguments - /// - /// * `fd` - File descriptor for `/dev/kvm`. - /// - pub unsafe fn new_with_fd_number(fd: RawFd) -> Self { - Kvm { - kvm: File::from_raw_fd(fd), - } - } - - /// Opens `/dev/kvm` and returns the fd number on success. - /// - /// # Arguments - /// - /// * `close_on_exec`: If true opens `/dev/kvm` using the `O_CLOEXEC` flag. - /// - pub fn open_with_cloexec(close_on_exec: bool) -> Result { - let open_flags = O_RDWR | if close_on_exec { O_CLOEXEC } else { 0 }; - // Safe because we give a constant nul-terminated string and verify the result. - let ret = unsafe { open("/dev/kvm\0".as_ptr() as *const c_char, open_flags) }; - if ret < 0 { - Err(io::Error::last_os_error()) - } else { - Ok(ret) - } - } - - /// Returns the KVM API version. - /// - /// See the documentation for `KVM_GET_API_VERSION`. - pub fn get_api_version(&self) -> i32 { - // Safe because we know that our file is a KVM fd and that the request is one of the ones - // defined by kernel. - unsafe { ioctl(self, KVM_GET_API_VERSION()) } - } - - /// Query the availability of a particular kvm capability. - /// - /// See the documentation for `KVM_CHECK_EXTENSION`. - /// Returns 0 if the capability is not available and > 0 otherwise. - /// - fn check_extension_int(&self, c: Cap) -> i32 { - // Safe because we know that our file is a KVM fd and that the extension is one of the ones - // defined by kernel. - unsafe { ioctl_with_val(self, KVM_CHECK_EXTENSION(), c as c_ulong) } - } - - /// Checks if a particular `Cap` is available. - /// - /// According to the KVM API doc, `KVM_CHECK_EXTENSION` returns "0 if unsupported; 1 (or some - /// other positive integer) if supported. - /// - /// # Arguments - /// - /// * `c` - KVM capability. - /// - pub fn check_extension(&self, c: Cap) -> bool { - self.check_extension_int(c) >= 1 - } - - /// Gets the size of the mmap required to use vcpu's `kvm_run` structure. - /// - /// See the documentation for `KVM_GET_VCPU_MMAP_SIZE`. - /// - pub fn get_vcpu_mmap_size(&self) -> Result { - // Safe because we know that our file is a KVM fd and we verify the return result. - let res = unsafe { ioctl(self, KVM_GET_VCPU_MMAP_SIZE()) }; - if res > 0 { - Ok(res as usize) - } else { - Err(io::Error::last_os_error()) - } - } - - /// Gets the recommended number of VCPUs per VM. - /// - /// See the documentation for `KVM_CAP_NR_VCPUS`. - /// Default to 4 when `KVM_CAP_NR_VCPUS` is not implemented. - pub fn get_nr_vcpus(&self) -> usize { - let x = self.check_extension_int(Cap::NrVcpus); - if x > 0 { - x as usize - } else { - 4 - } - } - - /// Gets the maximum allowed memory slots per VM. - /// - /// KVM reports the number of available memory slots (`KVM_CAP_NR_MEMSLOTS`) - /// using the extension interface. Both x86 and s390 implement this, ARM - /// and powerpc do not yet enable it. - /// Default to 32 when `KVM_CAP_NR_MEMSLOTS` is not implemented. - /// - pub fn get_nr_memslots(&self) -> usize { - let x = self.check_extension_int(Cap::NrMemslots); - if x > 0 { - x as usize - } else { - 32 - } - } - - /// Gets the recommended maximum number of VCPUs per VM. - /// - /// See the documentation for `KVM_CAP_MAX_VCPUS`. - /// Default to `KVM_CAP_NR_VCPUS` when `KVM_CAP_MAX_VCPUS` is not implemented. - /// - pub fn get_max_vcpus(&self) -> usize { - match self.check_extension_int(Cap::MaxVcpus) { - 0 => self.get_nr_vcpus(), - x => x as usize, - } - } - - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn get_cpuid(&self, kind: u64, max_entries_count: usize) -> Result { - let mut cpuid = CpuId::new(max_entries_count); - - let ret = unsafe { - // ioctl is unsafe. The kernel is trusted not to write beyond the bounds of the memory - // allocated for the struct. The limit is read from nent, which is set to the allocated - // size(max_entries_count) above. - ioctl_with_mut_ptr(self, kind, cpuid.as_mut_ptr()) - }; - if ret < 0 { - return Err(io::Error::last_os_error()); - } - - Ok(cpuid) - } - - /// X86 specific call to get the system emulated CPUID values. - /// - /// See the documentation for KVM_GET_EMULATED_CPUID. - /// - /// # Arguments - /// - /// * `max_entries_count` - Maximum number of CPUID entries. This function can return less than - /// this when the hardware does not support so many CPUID entries. - /// - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - pub fn get_emulated_cpuid(&self, max_entries_count: usize) -> Result { - self.get_cpuid(KVM_GET_EMULATED_CPUID(), max_entries_count) - } - - /// X86 specific call to get the system supported CPUID values. - /// - /// See the documentation for KVM_GET_SUPPORTED_CPUID. - /// - /// # Arguments - /// - /// * `max_entries_count` - Maximum number of CPUID entries. This function can return less than - /// this when the hardware does not support so many CPUID entries. - /// - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - pub fn get_supported_cpuid(&self, max_entries_count: usize) -> Result { - self.get_cpuid(KVM_GET_SUPPORTED_CPUID(), max_entries_count) - } - - /// X86 specific call to get list of supported MSRS - /// - /// See the documentation for KVM_GET_MSR_INDEX_LIST. - /// - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - pub fn get_msr_index_list(&self) -> Result> { - const MAX_KVM_MSR_ENTRIES: usize = 256; - - let mut msr_list = vec_with_array_field::(MAX_KVM_MSR_ENTRIES); - msr_list[0].nmsrs = MAX_KVM_MSR_ENTRIES as u32; - - let ret = unsafe { - // ioctl is unsafe. The kernel is trusted not to write beyond the bounds of the memory - // allocated for the struct. The limit is read from nmsrs, which is set to the allocated - // size (MAX_KVM_MSR_ENTRIES) above. - ioctl_with_mut_ref(self, KVM_GET_MSR_INDEX_LIST(), &mut msr_list[0]) - }; - if ret < 0 { - return Err(io::Error::last_os_error()); - } - - let mut nmsrs = msr_list[0].nmsrs; - - // Mapping the unsized array to a slice is unsafe because the length isn't known. Using - // the length we originally allocated with eliminates the possibility of overflow. - let indices: &[u32] = unsafe { - if nmsrs > MAX_KVM_MSR_ENTRIES as u32 { - nmsrs = MAX_KVM_MSR_ENTRIES as u32; - } - msr_list[0].indices.as_slice(nmsrs as usize) - }; - - Ok(indices.to_vec()) - } - - /// Creates a VM fd using the KVM fd. - /// - /// See the documentation for `KVM_CREATE_VM`. - /// A call to this function will also initialize the size of the vcpu mmap area using the - /// `KVM_GET_VCPU_MMAP_SIZE` ioctl. - /// - pub fn create_vm(&self) -> Result { - // Safe because we know kvm is a real kvm fd as this module is the only one that can make - // Kvm objects. - let ret = unsafe { ioctl(&self.kvm, KVM_CREATE_VM()) }; - if ret >= 0 { - // Safe because we verify the value of ret and we are the owners of the fd. - let vm_file = unsafe { File::from_raw_fd(ret) }; - let run_mmap_size = self.get_vcpu_mmap_size()?; - Ok(VmFd { - vm: vm_file, - run_size: run_mmap_size, - }) - } else { - Err(io::Error::last_os_error()) - } - } -} - -impl AsRawFd for Kvm { - fn as_raw_fd(&self) -> RawFd { - self.kvm.as_raw_fd() - } -} - -/// A wrapper around creating and using a VM. -pub struct VmFd { - vm: File, - run_size: usize, -} - -impl VmFd { - /// Creates/modifies a guest physical memory slot. - /// - /// See the documentation for `KVM_SET_USER_MEMORY_REGION`. - /// - pub fn set_user_memory_region( - &self, - user_memory_region: kvm_userspace_memory_region, - ) -> Result<()> { - let ret = - unsafe { ioctl_with_ref(self, KVM_SET_USER_MEMORY_REGION(), &user_memory_region) }; - if ret == 0 { - Ok(()) - } else { - Err(io::Error::last_os_error()) - } - } - - /// Sets the address of the three-page region in the VM's address space. - /// - /// See the documentation on the `KVM_SET_TSS_ADDR` ioctl. - /// - /// # Arguments - /// - /// * `offset` - Physical address of a three-page region in the guest's physical address space. - /// - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - pub fn set_tss_address(&self, offset: usize) -> Result<()> { - // Safe because we know that our file is a VM fd and we verify the return result. - let ret = unsafe { ioctl_with_val(self, KVM_SET_TSS_ADDR(), offset as c_ulong) }; - if ret == 0 { - Ok(()) - } else { - Err(io::Error::last_os_error()) - } - } - - /// Creates an in-kernel interrupt controller. - /// - /// See the documentation for `KVM_CREATE_IRQCHIP`. - /// - #[cfg(any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64" - ))] - pub fn create_irq_chip(&self) -> Result<()> { - // Safe because we know that our file is a VM fd and we verify the return result. - let ret = unsafe { ioctl(self, KVM_CREATE_IRQCHIP()) }; - if ret == 0 { - Ok(()) - } else { - Err(io::Error::last_os_error()) - } - } - - /// Creates a PIT as per the `KVM_CREATE_PIT2` ioctl. - /// - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - pub fn create_pit2(&self, pit_config: kvm_pit_config) -> Result<()> { - // Safe because we know that our file is a VM fd, we know the kernel will only read the - // correct amount of memory from our pointer, and we verify the return result. - let ret = unsafe { ioctl_with_ref(self, KVM_CREATE_PIT2(), &pit_config) }; - if ret == 0 { - Ok(()) - } else { - Err(io::Error::last_os_error()) - } - } - - /// Registers an event to be signaled whenever a certain address is written to. - /// - /// # Arguments - /// - /// * `evt` - EventFd which will be signaled. When signaling, the usual `vmexit` to userspace - /// is prevented. - /// * `addr` - Address being written to. - /// * `datamatch` - Limits signaling `evt` to only the cases where the value being written is - /// equal to this parameter. The size of `datamatch` is important and it must - /// match the expected size of the guest's write. - /// - pub fn register_ioevent>( - &self, - eventfd: RawFd, - addr: &IoEventAddress, - datamatch: T, - ) -> Result<()> { - let mut flags = 0; - if std::mem::size_of::() > 0 { - flags |= 1 << kvm_ioeventfd_flag_nr_datamatch - } - if let IoEventAddress::Pio(_) = *addr { - flags |= 1 << kvm_ioeventfd_flag_nr_pio - } - - let ioeventfd = kvm_ioeventfd { - datamatch: datamatch.into(), - len: std::mem::size_of::() as u32, - addr: match addr { - IoEventAddress::Pio(ref p) => *p as u64, - IoEventAddress::Mmio(ref m) => *m, - }, - fd: eventfd, - flags, - ..Default::default() - }; - // Safe because we know that our file is a VM fd, we know the kernel will only read the - // correct amount of memory from our pointer, and we verify the return result. - let ret = unsafe { ioctl_with_ref(self, KVM_IOEVENTFD(), &ioeventfd) }; - if ret == 0 { - Ok(()) - } else { - Err(io::Error::last_os_error()) - } - } - - /// Gets the bitmap of pages dirtied since the last call of this function. - /// - /// Leverages the dirty page logging feature in KVM. As a side-effect, this also resets the - /// bitmap inside the kernel. - /// - /// # Arguments - /// - /// * `slot` - Guest memory slot identifier. - /// * `memory_size` - Size of the memory region. - /// - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - pub fn get_dirty_log(&self, slot: u32, memory_size: usize) -> Result> { - // Compute the length of the bitmap needed for all dirty pages in one memory slot. - // One memory page is 4KiB (4096 bits) and KVM_GET_DIRTY_LOG returns one dirty bit for - // each page. - let page_size = 4 << 10; - - let div_round_up = |dividend, divisor| (dividend + divisor - 1) / divisor; - // For ease of access we are saving the bitmap in a u64 vector. We are using ceil to - // make sure we count all dirty pages even when `mem_size` is not a multiple of - // page_size * 64. - let bitmap_size = div_round_up(memory_size, page_size * 64); - let mut bitmap = vec![0; bitmap_size]; - let b_data = bitmap.as_mut_ptr() as *mut c_void; - let dirtylog = kvm_dirty_log { - slot, - padding1: 0, - __bindgen_anon_1: kvm_dirty_log__bindgen_ty_1 { - dirty_bitmap: b_data, - }, - }; - // Safe because we know that our file is a VM fd, and we know that the amount of memory - // we allocated for the bitmap is at least one bit per page. - let ret = unsafe { ioctl_with_ref(self, KVM_GET_DIRTY_LOG(), &dirtylog) }; - if ret == 0 { - Ok(bitmap) - } else { - Err(io::Error::last_os_error()) - } - } - - /// Registers an event that will, when signaled, trigger the `gsi` IRQ. - /// - /// # Arguments - /// - /// * `eventfd` - Event to be signaled. - /// * `gsi` - IRQ to be triggered. - /// - #[cfg(any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64" - ))] - pub fn register_irqfd(&self, eventfd: RawFd, gsi: u32) -> Result<()> { - let irqfd = kvm_irqfd { - fd: eventfd as u32, - gsi, - ..Default::default() - }; - // Safe because we know that our file is a VM fd, we know the kernel will only read the - // correct amount of memory from our pointer, and we verify the return result. - let ret = unsafe { ioctl_with_ref(self, KVM_IRQFD(), &irqfd) }; - if ret == 0 { - Ok(()) - } else { - Err(io::Error::last_os_error()) - } - } - - /// Constructs a new kvm VCPU fd. - /// - /// # Arguments - /// - /// * `id` - The CPU number between [0, max vcpus). - /// - /// # Errors - /// Returns an error when the VM fd is invalid or the VCPU memory cannot be mapped correctly. - /// - pub fn create_vcpu(&self, id: u8) -> Result { - // Safe because we know that vm is a VM fd and we verify the return result. - #[allow(clippy::cast_lossless)] - let vcpu_fd = unsafe { ioctl_with_val(&self.vm, KVM_CREATE_VCPU(), id as c_ulong) }; - if vcpu_fd < 0 { - return Err(io::Error::last_os_error()); - } - - // Wrap the vcpu now in case the following ? returns early. This is safe because we verified - // the value of the fd and we own the fd. - let vcpu = unsafe { File::from_raw_fd(vcpu_fd) }; - - let kvm_run_ptr = KvmRunWrapper::from_fd(&vcpu, self.run_size)?; - - Ok(VcpuFd { vcpu, kvm_run_ptr }) - } -} - -impl AsRawFd for VmFd { - fn as_raw_fd(&self) -> RawFd { - self.vm.as_raw_fd() - } -} - -/// An address either in programmable I/O space or in memory mapped I/O space. -pub enum IoEventAddress { - /// Representation of an programmable I/O address. - Pio(u64), - /// Representation of an memory mapped I/O address. - Mmio(u64), -} - -/// Used in `VmFd::register_ioevent` to indicate that no datamatch is requested. -pub struct NoDatamatch; -impl Into for NoDatamatch { - fn into(self) -> u64 { - 0 - } -} - -/// A safe wrapper over the `kvm_run` struct. -/// -/// The wrapper is needed for sending the pointer to `kvm_run` between -/// threads as raw pointers do not implement `Send` and `Sync`. -pub struct KvmRunWrapper { - kvm_run_ptr: *mut u8, -} - -// Send and Sync aren't automatically inherited for the raw address pointer. -// Accessing that pointer is only done through the stateless interface which -// allows the object to be shared by multiple threads without a decrease in -// safety. -unsafe impl Send for KvmRunWrapper {} -unsafe impl Sync for KvmRunWrapper {} - -impl KvmRunWrapper { - /// Maps the first `size` bytes of the given `fd`. - /// - /// # Arguments - /// * `fd` - File descriptor to mmap from. - /// * `size` - Size of memory region in bytes. - pub fn from_fd(fd: &AsRawFd, size: usize) -> Result { - // This is safe because we are creating a mapping in a place not already used by any other - // area in this process. - let addr = unsafe { - libc::mmap( - null_mut(), - size, - libc::PROT_READ | libc::PROT_WRITE, - libc::MAP_SHARED, - fd.as_raw_fd(), - 0, - ) - }; - if addr == libc::MAP_FAILED { - return Err(io::Error::last_os_error()); - } - - Ok(KvmRunWrapper { - kvm_run_ptr: addr as *mut u8, - }) - } - - /// Returns a mutable reference to `kvm_run`. - /// - #[allow(clippy::mut_from_ref)] - pub fn as_mut_ref(&self) -> &mut kvm_run { - // Safe because we know we mapped enough memory to hold the kvm_run struct because the - // kernel told us how large it was. - #[allow(clippy::cast_ptr_alignment)] - unsafe { - &mut *(self.kvm_run_ptr as *mut kvm_run) - } - } -} - -/// Reasons for vcpu exits. The exit reasons are mapped to the `KVM_EXIT_*` defines -/// from `include/uapi/linux/kvm.h`. -#[derive(Debug)] -pub enum VcpuExit<'a> { - /// An out port instruction was run on the given port with the given data. - IoOut(u16 /* port */, &'a [u8] /* data */), - /// An in port instruction was run on the given port. - /// - /// The given slice should be filled in before `Vcpu::run` is called again. - IoIn(u16 /* port */, &'a mut [u8] /* data */), - /// A read instruction was run against the given MMIO address. - /// - /// The given slice should be filled in before `Vcpu::run` is called again. - MmioRead(u64 /* address */, &'a mut [u8]), - /// A write instruction was run against the given MMIO address with the given data. - MmioWrite(u64 /* address */, &'a [u8]), - /// Corresponds to KVM_EXIT_UNKNOWN. - Unknown, - /// Corresponds to KVM_EXIT_EXCEPTION. - Exception, - /// Corresponds to KVM_EXIT_HYPERCALL. - Hypercall, - /// Corresponds to KVM_EXIT_DEBUG. - Debug, - /// Corresponds to KVM_EXIT_HLT. - Hlt, - /// Corresponds to KVM_EXIT_IRQ_WINDOW_OPEN. - IrqWindowOpen, - /// Corresponds to KVM_EXIT_SHUTDOWN. - Shutdown, - /// Corresponds to KVM_EXIT_FAIL_ENTRY. - FailEntry, - /// Corresponds to KVM_EXIT_INTR. - Intr, - /// Corresponds to KVM_EXIT_SET_TPR. - SetTpr, - /// Corresponds to KVM_EXIT_TPR_ACCESS. - TprAccess, - /// Corresponds to KVM_EXIT_S390_SIEIC. - S390Sieic, - /// Corresponds to KVM_EXIT_S390_RESET. - S390Reset, - /// Corresponds to KVM_EXIT_DCR. - Dcr, - /// Corresponds to KVM_EXIT_NMI. - Nmi, - /// Corresponds to KVM_EXIT_INTERNAL_ERROR. - InternalError, - /// Corresponds to KVM_EXIT_OSI. - Osi, - /// Corresponds to KVM_EXIT_PAPR_HCALL. - PaprHcall, - /// Corresponds to KVM_EXIT_S390_UCONTROL. - S390Ucontrol, - /// Corresponds to KVM_EXIT_WATCHDOG. - Watchdog, - /// Corresponds to KVM_EXIT_S390_TSCH. - S390Tsch, - /// Corresponds to KVM_EXIT_EPR. - Epr, - /// Corresponds to KVM_EXIT_SYSTEM_EVENT. - SystemEvent, - /// Corresponds to KVM_EXIT_S390_STSI. - S390Stsi, - /// Corresponds to KVM_EXIT_IOAPIC_EOI. - IoapicEoi, - /// Corresponds to KVM_EXIT_HYPERV. - Hyperv, -} - -/// A wrapper around creating and using a kvm related VCPU fd -pub struct VcpuFd { - vcpu: File, - kvm_run_ptr: KvmRunWrapper, -} - -impl VcpuFd { - /// Gets the VCPU registers using the `KVM_GET_REGS` ioctl. - /// - #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] - pub fn get_regs(&self) -> Result { - // Safe because we know that our file is a VCPU fd, we know the kernel will only read the - // correct amount of memory from our pointer, and we verify the return result. - let mut regs = unsafe { std::mem::zeroed() }; - let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_REGS(), &mut regs) }; - if ret != 0 { - return Err(io::Error::last_os_error()); - } - Ok(regs) - } - - /// Sets the VCPU registers using `KVM_SET_REGS` ioctl. - /// - /// # Arguments - /// - /// * `regs` - Registers being set. - /// - #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] - pub fn set_regs(&self, regs: &kvm_regs) -> Result<()> { - // Safe because we know that our file is a VCPU fd, we know the kernel will only read the - // correct amount of memory from our pointer, and we verify the return result. - let ret = unsafe { ioctl_with_ref(self, KVM_SET_REGS(), regs) }; - if ret != 0 { - return Err(io::Error::last_os_error()); - } - Ok(()) - } - - /// Gets the VCPU special registers using `KVM_GET_SREGS` ioctl. - /// - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - pub fn get_sregs(&self) -> Result { - // Safe because we know that our file is a VCPU fd, we know the kernel will only write the - // correct amount of memory to our pointer, and we verify the return result. - let mut regs = kvm_sregs::default(); - - let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_SREGS(), &mut regs) }; - if ret != 0 { - return Err(io::Error::last_os_error()); - } - Ok(regs) - } - - /// Sets the VCPU special registers using `KVM_SET_SREGS` ioctl. - /// - /// # Arguments - /// - /// * `sregs` - Special registers to be set. - /// - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - pub fn set_sregs(&self, sregs: &kvm_sregs) -> Result<()> { - // Safe because we know that our file is a VCPU fd, we know the kernel will only read the - // correct amount of memory from our pointer, and we verify the return result. - let ret = unsafe { ioctl_with_ref(self, KVM_SET_SREGS(), sregs) }; - if ret != 0 { - return Err(io::Error::last_os_error()); - } - Ok(()) - } - - /// X86 specific call that gets the FPU-related structure. - /// - /// See the documentation for `KVM_GET_FPU`. - /// - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - pub fn get_fpu(&self) -> Result { - let mut fpu = kvm_fpu::default(); - - let ret = unsafe { - // Here we trust the kernel not to read past the end of the kvm_fpu struct. - ioctl_with_mut_ref(self, KVM_GET_FPU(), &mut fpu) - }; - if ret != 0 { - return Err(io::Error::last_os_error()); - } - Ok(fpu) - } - - /// X86 specific call to setup the FPU. - /// - /// See the documentation for `KVM_SET_FPU`. - /// - /// # Arguments - /// - /// * `fpu` - FPU configurations struct. - /// - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - pub fn set_fpu(&self, fpu: &kvm_fpu) -> Result<()> { - let ret = unsafe { - // Here we trust the kernel not to read past the end of the kvm_fpu struct. - ioctl_with_ref(self, KVM_SET_FPU(), fpu) - }; - if ret < 0 { - return Err(io::Error::last_os_error()); - } - Ok(()) - } - - /// X86 specific call to setup the CPUID registers. - /// - /// See the documentation for `KVM_SET_CPUID2`. - /// - /// # Arguments - /// - /// * `cpuid` - CPUID registers. - /// - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - pub fn set_cpuid2(&self, cpuid: &CpuId) -> Result<()> { - let ret = unsafe { - // Here we trust the kernel not to read past the end of the kvm_msrs struct. - ioctl_with_ptr(self, KVM_SET_CPUID2(), cpuid.as_ptr()) - }; - if ret < 0 { - return Err(io::Error::last_os_error()); - } - Ok(()) - } - - /// X86 specific call to get the state of the LAPIC (Local Advanced Programmable Interrupt - /// Controller). - /// - /// See the documentation for `KVM_GET_LAPIC`. - /// - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - pub fn get_lapic(&self) -> Result { - let mut klapic = kvm_lapic_state::default(); - - let ret = unsafe { - // The ioctl is unsafe unless you trust the kernel not to write past the end of the - // local_apic struct. - ioctl_with_mut_ref(self, KVM_GET_LAPIC(), &mut klapic) - }; - if ret < 0 { - return Err(io::Error::last_os_error()); - } - Ok(klapic) - } - - /// X86 specific call to set the state of the LAPIC (Local Advanced Programmable Interrupt - /// Controller). - /// - /// See the documentation for `KVM_SET_LAPIC`. - /// - /// # Arguments - /// - /// * `klapic` - LAPIC state registers. - /// - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - pub fn set_lapic(&self, klapic: &kvm_lapic_state) -> Result<()> { - let ret = unsafe { - // The ioctl is safe because the kernel will only read from the klapic struct. - ioctl_with_ref(self, KVM_SET_LAPIC(), klapic) - }; - if ret < 0 { - return Err(io::Error::last_os_error()); - } - Ok(()) - } - - /// X86 specific call to read model-specific registers for this VCPU. - /// - /// It emulates `KVM_GET_MSRS` ioctl's behavior by returning the number of MSRs - /// successfully read upon success or the last error number in case of failure. - /// - /// # Arguments - /// - /// * `msrs` - MSRs to be read. - /// - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - pub fn get_msrs(&self, msrs: &mut kvm_msrs) -> Result<(i32)> { - let ret = unsafe { - // Here we trust the kernel not to read past the end of the kvm_msrs struct. - ioctl_with_mut_ref(self, KVM_GET_MSRS(), msrs) - }; - if ret < 0 { - return Err(io::Error::last_os_error()); - } - Ok(ret) - } - - /// X86 specific call to setup the MSRS. - /// - /// See the documentation for `KVM_SET_MSRS`. - /// - /// # Arguments - /// - /// * `kvm_msrs` - MSRs to be written. - /// - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - pub fn set_msrs(&self, msrs: &kvm_msrs) -> Result<()> { - let ret = unsafe { - // Here we trust the kernel not to read past the end of the kvm_msrs struct. - ioctl_with_ref(self, KVM_SET_MSRS(), msrs) - }; - if ret < 0 { - // KVM_SET_MSRS actually returns the number of msr entries written. - return Err(io::Error::last_os_error()); - } - Ok(()) - } - - /// Triggers the running of the current virtual CPU returning an exit reason. - /// - pub fn run(&self) -> Result { - // Safe because we know that our file is a VCPU fd and we verify the return result. - let ret = unsafe { ioctl(self, KVM_RUN()) }; - if ret == 0 { - let run = self.kvm_run_ptr.as_mut_ref(); - match run.exit_reason { - // make sure you treat all possible exit reasons from include/uapi/linux/kvm.h corresponding - // when upgrading to a different kernel version - KVM_EXIT_UNKNOWN => Ok(VcpuExit::Unknown), - KVM_EXIT_EXCEPTION => Ok(VcpuExit::Exception), - KVM_EXIT_IO => { - let run_start = run as *mut kvm_run as *mut u8; - // Safe because the exit_reason (which comes from the kernel) told us which - // union field to use. - let io = unsafe { run.__bindgen_anon_1.io }; - let port = io.port; - let data_size = io.count as usize * io.size as usize; - // The data_offset is defined by the kernel to be some number of bytes into the - // kvm_run stucture, which we have fully mmap'd. - let data_ptr = unsafe { run_start.offset(io.data_offset as isize) }; - // The slice's lifetime is limited to the lifetime of this Vcpu, which is equal - // to the mmap of the kvm_run struct that this is slicing from - let data_slice = unsafe { - std::slice::from_raw_parts_mut::(data_ptr as *mut u8, data_size) - }; - match u32::from(io.direction) { - KVM_EXIT_IO_IN => Ok(VcpuExit::IoIn(port, data_slice)), - KVM_EXIT_IO_OUT => Ok(VcpuExit::IoOut(port, data_slice)), - _ => Err(io::Error::from_raw_os_error(EINVAL)), - } - } - KVM_EXIT_HYPERCALL => Ok(VcpuExit::Hypercall), - KVM_EXIT_DEBUG => Ok(VcpuExit::Debug), - KVM_EXIT_HLT => Ok(VcpuExit::Hlt), - KVM_EXIT_MMIO => { - // Safe because the exit_reason (which comes from the kernel) told us which - // union field to use. - let mmio = unsafe { &mut run.__bindgen_anon_1.mmio }; - let addr = mmio.phys_addr; - let len = mmio.len as usize; - let data_slice = &mut mmio.data[..len]; - if mmio.is_write != 0 { - Ok(VcpuExit::MmioWrite(addr, data_slice)) - } else { - Ok(VcpuExit::MmioRead(addr, data_slice)) - } - } - KVM_EXIT_IRQ_WINDOW_OPEN => Ok(VcpuExit::IrqWindowOpen), - KVM_EXIT_SHUTDOWN => Ok(VcpuExit::Shutdown), - KVM_EXIT_FAIL_ENTRY => Ok(VcpuExit::FailEntry), - KVM_EXIT_INTR => Ok(VcpuExit::Intr), - KVM_EXIT_SET_TPR => Ok(VcpuExit::SetTpr), - KVM_EXIT_TPR_ACCESS => Ok(VcpuExit::TprAccess), - KVM_EXIT_S390_SIEIC => Ok(VcpuExit::S390Sieic), - KVM_EXIT_S390_RESET => Ok(VcpuExit::S390Reset), - KVM_EXIT_DCR => Ok(VcpuExit::Dcr), - KVM_EXIT_NMI => Ok(VcpuExit::Nmi), - KVM_EXIT_INTERNAL_ERROR => Ok(VcpuExit::InternalError), - KVM_EXIT_OSI => Ok(VcpuExit::Osi), - KVM_EXIT_PAPR_HCALL => Ok(VcpuExit::PaprHcall), - KVM_EXIT_S390_UCONTROL => Ok(VcpuExit::S390Ucontrol), - KVM_EXIT_WATCHDOG => Ok(VcpuExit::Watchdog), - KVM_EXIT_S390_TSCH => Ok(VcpuExit::S390Tsch), - KVM_EXIT_EPR => Ok(VcpuExit::Epr), - KVM_EXIT_SYSTEM_EVENT => Ok(VcpuExit::SystemEvent), - KVM_EXIT_S390_STSI => Ok(VcpuExit::S390Stsi), - KVM_EXIT_IOAPIC_EOI => Ok(VcpuExit::IoapicEoi), - KVM_EXIT_HYPERV => Ok(VcpuExit::Hyperv), - r => panic!("unknown kvm exit reason: {}", r), - } - } else { - Err(io::Error::last_os_error()) - } - } -} - -impl AsRawFd for VcpuFd { - fn as_raw_fd(&self) -> RawFd { - self.vcpu.as_raw_fd() - } -} - -/// Wrapper for `kvm_cpuid2` which has a zero length array at the end. -/// Hides the zero length array behind a bounds check. -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -pub struct CpuId { - /// Wrapper over `kvm_cpuid2` from which we only use the first element. - kvm_cpuid: Vec, - // Number of `kvm_cpuid_entry2` structs at the end of kvm_cpuid2. - allocated_len: usize, -} - -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -impl Clone for CpuId { - fn clone(&self) -> Self { - let mut kvm_cpuid = Vec::with_capacity(self.kvm_cpuid.len()); - for _ in 0..self.kvm_cpuid.len() { - kvm_cpuid.push(kvm_cpuid2::default()); - } - - let num_bytes = self.kvm_cpuid.len() * size_of::(); - - let src_byte_slice = - unsafe { std::slice::from_raw_parts(self.kvm_cpuid.as_ptr() as *const u8, num_bytes) }; - - let dst_byte_slice = - unsafe { std::slice::from_raw_parts_mut(kvm_cpuid.as_mut_ptr() as *mut u8, num_bytes) }; - - dst_byte_slice.copy_from_slice(src_byte_slice); - - CpuId { - kvm_cpuid, - allocated_len: self.allocated_len, - } - } -} - -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -impl CpuId { - /// Creates a new `CpuId` structure that can contain at most `array_len` KVM CPUID entries. - /// - /// # Arguments - /// - /// * `array_len` - Maximum number of CPUID entries. - /// - pub fn new(array_len: usize) -> CpuId { - let mut kvm_cpuid = vec_with_array_field::(array_len); - kvm_cpuid[0].nent = array_len as u32; - - CpuId { - kvm_cpuid, - allocated_len: array_len, - } - } - - /// Get the mutable entries slice so they can be modified before passing to the VCPU. - /// - pub fn mut_entries_slice(&mut self) -> &mut [kvm_cpuid_entry2] { - // Mapping the unsized array to a slice is unsafe because the length isn't known. Using - // the length we originally allocated with eliminates the possibility of overflow. - if self.kvm_cpuid[0].nent as usize > self.allocated_len { - self.kvm_cpuid[0].nent = self.allocated_len as u32; - } - let nent = self.kvm_cpuid[0].nent as usize; - unsafe { self.kvm_cpuid[0].entries.as_mut_slice(nent) } - } - - /// Get a pointer so it can be passed to the kernel. Using this pointer is unsafe. - /// - pub fn as_ptr(&self) -> *const kvm_cpuid2 { - &self.kvm_cpuid[0] - } - - /// Get a mutable pointer so it can be passed to the kernel. Using this pointer is unsafe. - /// - pub fn as_mut_ptr(&mut self) -> *mut kvm_cpuid2 { - &mut self.kvm_cpuid[0] - } -} - -#[cfg(test)] -mod tests { - use super::*; - use libc::{eventfd, EFD_NONBLOCK}; - - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - impl PartialEq for CpuId { - fn eq(&self, other: &CpuId) -> bool { - let entries: &[kvm_cpuid_entry2] = - unsafe { self.kvm_cpuid[0].entries.as_slice(self.allocated_len) }; - let other_entries: &[kvm_cpuid_entry2] = - unsafe { self.kvm_cpuid[0].entries.as_slice(other.allocated_len) }; - self.allocated_len == other.allocated_len && entries == other_entries - } - } - - // Helper function for mmap an anonymous memory of `size`. - // Panics if the mmap fails. - fn mmap_anonymous(size: usize) -> *mut u8 { - let addr = unsafe { - libc::mmap( - null_mut(), - size, - libc::PROT_READ | libc::PROT_WRITE, - libc::MAP_ANONYMOUS | libc::MAP_SHARED | libc::MAP_NORESERVE, - -1, - 0, - ) - }; - if addr == libc::MAP_FAILED { - panic!("mmap failed."); - } - - return addr as *mut u8; - } - - #[test] - fn test_kvm_new() { - Kvm::new().unwrap(); - } - - #[test] - fn test_kvm_api_version() { - let kvm = Kvm::new().unwrap(); - assert_eq!(kvm.get_api_version(), 12); - assert!(kvm.check_extension(Cap::UserMemory)); - } - - #[test] - fn test_kvm_getters() { - let kvm = Kvm::new().unwrap(); - - // vCPU related getters - let nr_vcpus = kvm.get_nr_vcpus(); - assert!(nr_vcpus >= 4); - - assert!(kvm.get_max_vcpus() >= nr_vcpus); - - // Memory related getters - assert!(kvm.get_vcpu_mmap_size().unwrap() > 0); - assert!(kvm.get_nr_memslots() >= 32); - } - - #[test] - fn test_create_vm() { - let kvm = Kvm::new().unwrap(); - let vm = kvm.create_vm().unwrap(); - - assert_eq!(vm.run_size, kvm.get_vcpu_mmap_size().unwrap()); - } - - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - #[test] - fn test_get_supported_cpuid() { - let kvm = Kvm::new().unwrap(); - let mut cpuid = kvm.get_supported_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); - let cpuid_entries = cpuid.mut_entries_slice(); - assert!(cpuid_entries.len() > 0); - assert!(cpuid_entries.len() <= MAX_KVM_CPUID_ENTRIES); - } - - #[test] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn test_get_emulated_cpuid() { - let kvm = Kvm::new().unwrap(); - let mut cpuid = kvm.get_emulated_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); - let cpuid_entries = cpuid.mut_entries_slice(); - assert!(cpuid_entries.len() > 0); - assert!(cpuid_entries.len() <= MAX_KVM_CPUID_ENTRIES); - } - - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - #[test] - fn test_cpuid_clone() { - let kvm = Kvm::new().unwrap(); - let cpuid_1 = kvm.get_supported_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); - let mut cpuid_2 = cpuid_1.clone(); - assert!(cpuid_1 == cpuid_2); - cpuid_2 = unsafe { std::mem::zeroed() }; - assert!(cpuid_1 != cpuid_2); - } - - #[test] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn get_msr_index_list() { - let kvm = Kvm::new().unwrap(); - let msr_list = kvm.get_msr_index_list().unwrap(); - assert!(msr_list.len() >= 2); - } - - #[test] - fn test_set_invalid_memory() { - let kvm = Kvm::new().unwrap(); - let vm = kvm.create_vm().unwrap(); - let invalid_mem_region = kvm_userspace_memory_region { - slot: 0, - guest_phys_addr: 0, - memory_size: 0, - userspace_addr: 0, - flags: 0, - }; - assert!(vm.set_user_memory_region(invalid_mem_region).is_err()); - } - - #[test] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn test_set_tss_address() { - let kvm = Kvm::new().unwrap(); - let vm = kvm.create_vm().unwrap(); - assert!(vm.set_tss_address(0xfffb_d000).is_ok()); - } - - #[test] - #[cfg(any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64" - ))] - fn test_create_irq_chip() { - let kvm = Kvm::new().unwrap(); - assert!(kvm.check_extension(Cap::Irqchip)); - let vm = kvm.create_vm().unwrap(); - assert!(vm.create_irq_chip().is_ok()); - } - - #[test] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn test_create_pit2() { - let kvm = Kvm::new().unwrap(); - let vm = kvm.create_vm().unwrap(); - assert!(vm.create_pit2(kvm_pit_config::default()).is_ok()); - } - - #[test] - fn test_register_ioevent() { - assert_eq!(std::mem::size_of::(), 0); - - let kvm = Kvm::new().unwrap(); - let vm_fd = kvm.create_vm().unwrap(); - let evtfd = unsafe { eventfd(0, EFD_NONBLOCK) }; - assert!(vm_fd - .register_ioevent(evtfd, &IoEventAddress::Pio(0xf4), NoDatamatch) - .is_ok()); - assert!(vm_fd - .register_ioevent(evtfd, &IoEventAddress::Mmio(0x1000), NoDatamatch) - .is_ok()); - assert!(vm_fd - .register_ioevent(evtfd, &IoEventAddress::Pio(0xc1), 0x7fu8) - .is_ok()); - assert!(vm_fd - .register_ioevent(evtfd, &IoEventAddress::Pio(0xc2), 0x1337u16) - .is_ok()); - assert!(vm_fd - .register_ioevent(evtfd, &IoEventAddress::Pio(0xc4), 0xdead_beefu32) - .is_ok()); - assert!(vm_fd - .register_ioevent(evtfd, &IoEventAddress::Pio(0xc8), 0xdead_beef_dead_beefu64) - .is_ok()); - } - - #[test] - #[cfg(any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64" - ))] - fn test_register_irqfd() { - let kvm = Kvm::new().unwrap(); - let vm_fd = kvm.create_vm().unwrap(); - let evtfd1 = unsafe { eventfd(0, EFD_NONBLOCK) }; - let evtfd2 = unsafe { eventfd(0, EFD_NONBLOCK) }; - let evtfd3 = unsafe { eventfd(0, EFD_NONBLOCK) }; - - assert!(vm_fd.register_irqfd(evtfd1, 4).is_ok()); - assert!(vm_fd.register_irqfd(evtfd2, 8).is_ok()); - assert!(vm_fd.register_irqfd(evtfd3, 4).is_ok()); - - assert!(vm_fd.register_irqfd(evtfd3, 4).is_err()); - assert!(vm_fd.register_irqfd(evtfd3, 5).is_err()); - } - - #[test] - fn test_create_vcpu() { - let kvm = Kvm::new().unwrap(); - let vm = kvm.create_vm().unwrap(); - - assert!(vm.create_vcpu(0).is_ok()); - } - - #[cfg(target_arch = "x86_64")] - #[test] - fn test_run_code() { - use std::io::Write; - - let kvm = Kvm::new().unwrap(); - let vm = kvm.create_vm().unwrap(); - // This example based on https://lwn.net/Articles/658511/ - let code = [ - 0xba, 0xf8, 0x03, /* mov $0x3f8, %dx */ - 0x00, 0xd8, /* add %bl, %al */ - 0x04, b'0', /* add $'0', %al */ - 0xee, /* out %al, %dx */ - 0xec, /* in %dx, %al */ - 0xc6, 0x06, 0x00, 0x80, 0x00, /* movl $0, (0x8000) */ - 0x8a, 0x16, 0x00, 0x80, /* movl (0x8000), %dl */ - 0xc6, 0x06, 0x00, 0x20, 0x00, /* movl $0, (0x2000) */ - 0xf4, /* hlt */ - ]; - - let mem_size = 0x4000; - let load_addr = mmap_anonymous(mem_size); - let guest_addr: u64 = 0x1000; - let slot: u32 = 0; - let mem_region = kvm_userspace_memory_region { - slot, - guest_phys_addr: guest_addr, - memory_size: mem_size as u64, - userspace_addr: load_addr as u64, - flags: KVM_MEM_LOG_DIRTY_PAGES, - }; - vm.set_user_memory_region(mem_region).unwrap(); - - unsafe { - // Get a mutable slice of `mem_size` from `load_addr`. - // This is safe because we mapped it before. - let mut slice = std::slice::from_raw_parts_mut(load_addr, mem_size); - slice.write(&code).unwrap(); - } - - let vcpu_fd = vm.create_vcpu(0).unwrap(); - - let mut vcpu_sregs = vcpu_fd.get_sregs().unwrap(); - assert_ne!(vcpu_sregs.cs.base, 0); - assert_ne!(vcpu_sregs.cs.selector, 0); - vcpu_sregs.cs.base = 0; - vcpu_sregs.cs.selector = 0; - vcpu_fd.set_sregs(&vcpu_sregs).unwrap(); - - let mut vcpu_regs = vcpu_fd.get_regs().unwrap(); - // Set the Instruction Pointer to the guest address where we loaded the code. - vcpu_regs.rip = guest_addr; - vcpu_regs.rax = 2; - vcpu_regs.rbx = 3; - vcpu_regs.rflags = 2; - vcpu_fd.set_regs(&vcpu_regs).unwrap(); - - loop { - match vcpu_fd.run().expect("run failed") { - VcpuExit::IoIn(addr, data) => { - assert_eq!(addr, 0x3f8); - assert_eq!(data.len(), 1); - } - VcpuExit::IoOut(addr, data) => { - assert_eq!(addr, 0x3f8); - assert_eq!(data.len(), 1); - assert_eq!(data[0], b'5'); - } - VcpuExit::MmioRead(addr, data) => { - assert_eq!(addr, 0x8000); - assert_eq!(data.len(), 1); - } - VcpuExit::MmioWrite(addr, data) => { - assert_eq!(addr, 0x8000); - assert_eq!(data.len(), 1); - assert_eq!(data[0], 0); - } - VcpuExit::Hlt => { - // The code snippet dirties 2 pages: - // * one when the code itself is loaded in memory; - // * and one more from the `movl` that writes to address 0x8000 - let dirty_pages_bitmap = vm.get_dirty_log(slot, mem_size).unwrap(); - let dirty_pages = dirty_pages_bitmap - .into_iter() - .map(|page| page.count_ones()) - .fold(0, |dirty_page_count, i| dirty_page_count + i); - assert_eq!(dirty_pages, 2); - break; - } - r => panic!("unexpected exit reason: {:?}", r), - } - } - } -} From d5b34322a2dfb5bb061430044d76896e0e915433 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Mon, 11 Mar 2019 19:42:21 +0200 Subject: [PATCH 015/332] adding more unit tests - Vcpu related unit tests - error cases for all ioctls Signed-off-by: Andreea Florescu --- Cargo.lock | 7 ++ Cargo.toml | 3 + src/ioctls/system.rs | 27 ++++++ src/ioctls/vcpu.rs | 203 +++++++++++++++++++++++++++++++++++++++++++ src/ioctls/vm.rs | 47 ++++++++++ tests/coverage | 2 +- 6 files changed, 288 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 62cbacc..444ae1b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,3 +1,8 @@ +[[package]] +name = "byteorder" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "kvm-bindings" version = "0.1.1" @@ -7,6 +12,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "kvm-ioctls" version = "0.0.1" dependencies = [ + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "kvm-bindings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -17,5 +23,6 @@ version = "0.2.48" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] +"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" "checksum kvm-bindings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c223e8703d2eb76d990c5f58e29c85b0f6f50e24b823babde927948e7c71fc03" "checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047" diff --git a/Cargo.toml b/Cargo.toml index a5a7939..6bf4439 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,3 +11,6 @@ license = "Apache-2.0" [dependencies] libc = ">=0.2.39" kvm-bindings = ">=0.1.0" + +[dev-dependencies] +byteorder = ">=1.2.1" diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index ff171b8..b8565c7 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -334,4 +334,31 @@ mod tests { let msr_list = kvm.get_msr_index_list().unwrap(); assert!(msr_list.len() >= 2); } + + fn get_raw_errno(result: super::Result) -> i32 { + result.err().unwrap().raw_os_error().unwrap() + } + + #[test] + fn test_bad_kvm_fd() { + let badf_errno = libc::EBADF; + + let faulty_kvm = Kvm { + kvm: unsafe { File::from_raw_fd(-1) }, + }; + + assert_eq!(get_raw_errno(faulty_kvm.get_vcpu_mmap_size()), badf_errno); + + assert_eq!(faulty_kvm.get_nr_vcpus(), 4); + + assert_eq!(faulty_kvm.get_nr_memslots(), 32); + + assert_eq!(get_raw_errno(faulty_kvm.get_emulated_cpuid(4)), badf_errno); + + assert_eq!(get_raw_errno(faulty_kvm.get_supported_cpuid(4)), badf_errno); + + assert_eq!(get_raw_errno(faulty_kvm.get_msr_index_list()), badf_errno); + + assert_eq!(get_raw_errno(faulty_kvm.create_vm()), badf_errno); + } } diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 0bb4963..5935bb4 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -384,9 +384,14 @@ impl AsRawFd for VcpuFd { #[cfg(test)] mod tests { + extern crate byteorder; + use super::*; use ioctls::system::Kvm; + use Cap; + use MAX_KVM_CPUID_ENTRIES; + use std::os::unix::io::FromRawFd; use std::ptr::null_mut; // Helper function for mmap an anonymous memory of `size`. @@ -417,6 +422,144 @@ mod tests { assert!(vm.create_vcpu(0).is_ok()); } + #[cfg(target_arch = "x86_64")] + #[test] + fn test_set_cpuid2() { + let kvm = Kvm::new().unwrap(); + if kvm.check_extension(Cap::ExtCpuid) { + let vm = kvm.create_vm().unwrap(); + let mut cpuid = kvm.get_supported_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); + assert!(cpuid.mut_entries_slice().len() <= MAX_KVM_CPUID_ENTRIES); + let nr_vcpus = kvm.get_nr_vcpus(); + for cpu_id in 0..nr_vcpus { + let vcpu = vm.create_vcpu(cpu_id as u8).unwrap(); + vcpu.set_cpuid2(&cpuid).unwrap(); + } + } + } + + #[cfg(target_arch = "x86_64")] + #[allow(non_snake_case)] + #[test] + fn test_fpu() { + // as per https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/fpu/internal.h + let KVM_FPU_CWD: usize = 0x37f; + let KVM_FPU_MXCSR: usize = 0x1f80; + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let vcpu = vm.create_vcpu(0).unwrap(); + let mut fpu: kvm_fpu = kvm_fpu { + fcw: KVM_FPU_CWD as u16, + mxcsr: KVM_FPU_MXCSR as u32, + ..Default::default() + }; + + fpu.fcw = KVM_FPU_CWD as u16; + fpu.mxcsr = KVM_FPU_MXCSR as u32; + + vcpu.set_fpu(&fpu).unwrap(); + assert_eq!(vcpu.get_fpu().unwrap().fcw, KVM_FPU_CWD as u16); + } + + #[cfg(target_arch = "x86_64")] + #[test] + fn lapic_test() { + use std::io::Cursor; + //we might get read of byteorder if we replace 5h3 mem::transmute with something safer + use self::byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; + //as per https://github.com/torvalds/linux/arch/x86/kvm/lapic.c + //try to write and read the APIC_ICR (0x300) register which is non-read only and + //one can simply write to it + let kvm = Kvm::new().unwrap(); + assert!(kvm.check_extension(Cap::Irqchip)); + let vm = kvm.create_vm().unwrap(); + //the get_lapic ioctl will fail if there is no irqchip created beforehand + assert!(vm.create_irq_chip().is_ok()); + let vcpu = vm.create_vcpu(0).unwrap(); + let mut klapic: kvm_lapic_state = vcpu.get_lapic().unwrap(); + + let reg_offset = 0x300; + let value = 2 as u32; + //try to write and read the APIC_ICR 0x300 + let write_slice = + unsafe { &mut *(&mut klapic.regs[reg_offset..] as *mut [i8] as *mut [u8]) }; + let mut writer = Cursor::new(write_slice); + writer.write_u32::(value).unwrap(); + vcpu.set_lapic(&klapic).unwrap(); + klapic = vcpu.get_lapic().unwrap(); + let read_slice = unsafe { &*(&klapic.regs[reg_offset..] as *const [i8] as *const [u8]) }; + let mut reader = Cursor::new(read_slice); + assert_eq!(reader.read_u32::().unwrap(), value); + } + + #[cfg(target_arch = "x86_64")] + #[test] + fn msrs_test() { + use std::mem; + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let vcpu = vm.create_vcpu(0).unwrap(); + let mut configured_entry_vec = Vec::::new(); + + configured_entry_vec.push(kvm_msr_entry { + index: 0x0000_0174, + data: 0x0, + ..Default::default() + }); + configured_entry_vec.push(kvm_msr_entry { + index: 0x0000_0175, + data: 0x1, + ..Default::default() + }); + + let vec_size_bytes = mem::size_of::() + + (configured_entry_vec.len() * mem::size_of::()); + let vec: Vec = Vec::with_capacity(vec_size_bytes); + let msrs: &mut kvm_msrs = unsafe { &mut *(vec.as_ptr() as *mut kvm_msrs) }; + unsafe { + let entries: &mut [kvm_msr_entry] = + msrs.entries.as_mut_slice(configured_entry_vec.len()); + entries.copy_from_slice(&configured_entry_vec); + } + msrs.nmsrs = configured_entry_vec.len() as u32; + vcpu.set_msrs(msrs).unwrap(); + + //now test that GET_MSRS returns the same + let wanted_kvm_msrs_entries = [ + kvm_msr_entry { + index: 0x0000_0174, + ..Default::default() + }, + kvm_msr_entry { + index: 0x0000_0175, + ..Default::default() + }, + ]; + let vec2: Vec = Vec::with_capacity(vec_size_bytes); + let mut msrs2: &mut kvm_msrs = unsafe { + // Converting the vector's memory to a struct is unsafe. Carefully using the read-only + // vector to size and set the members ensures no out-of-bounds errors below. + &mut *(vec2.as_ptr() as *mut kvm_msrs) + }; + + unsafe { + let entries: &mut [kvm_msr_entry] = + msrs2.entries.as_mut_slice(configured_entry_vec.len()); + entries.copy_from_slice(&wanted_kvm_msrs_entries); + } + msrs2.nmsrs = configured_entry_vec.len() as u32; + + let read_msrs = vcpu.get_msrs(&mut msrs2).unwrap(); + assert_eq!(read_msrs, configured_entry_vec.len() as i32); + + let returned_kvm_msr_entries: &mut [kvm_msr_entry] = + unsafe { msrs2.entries.as_mut_slice(msrs2.nmsrs as usize) }; + + for (i, entry) in returned_kvm_msr_entries.iter_mut().enumerate() { + assert_eq!(entry, &mut configured_entry_vec[i]); + } + } + #[cfg(target_arch = "x86_64")] #[test] fn test_run_code() { @@ -510,4 +653,64 @@ mod tests { } } } + + fn get_raw_errno(result: super::Result) -> i32 { + result.err().unwrap().raw_os_error().unwrap() + } + + #[test] + #[cfg(target_arch = "x86_64")] + fn test_faulty_vcpu_fd() { + let badf_errno = libc::EBADF; + + let faulty_vcpu_fd = VcpuFd { + vcpu: unsafe { File::from_raw_fd(-1) }, + kvm_run_ptr: KvmRunWrapper { + kvm_run_ptr: mmap_anonymous(10), + }, + }; + + assert_eq!(get_raw_errno(faulty_vcpu_fd.get_regs()), badf_errno); + assert_eq!( + get_raw_errno(faulty_vcpu_fd.set_regs(&unsafe { std::mem::zeroed() })), + badf_errno + ); + assert_eq!(get_raw_errno(faulty_vcpu_fd.get_sregs()), badf_errno); + assert_eq!( + get_raw_errno(faulty_vcpu_fd.set_sregs(&unsafe { std::mem::zeroed() })), + badf_errno + ); + assert_eq!(get_raw_errno(faulty_vcpu_fd.get_fpu()), badf_errno); + assert_eq!( + get_raw_errno(faulty_vcpu_fd.set_fpu(&unsafe { std::mem::zeroed() })), + badf_errno + ); + assert_eq!( + get_raw_errno( + faulty_vcpu_fd.set_cpuid2( + &Kvm::new() + .unwrap() + .get_supported_cpuid(MAX_KVM_CPUID_ENTRIES) + .unwrap() + ) + ), + badf_errno + ); + // kvm_lapic_state does not implement debug by default so we cannot + // use unwrap_err here. + assert!(faulty_vcpu_fd.get_lapic().is_err()); + assert_eq!( + get_raw_errno(faulty_vcpu_fd.set_lapic(&unsafe { std::mem::zeroed() })), + badf_errno + ); + assert_eq!( + get_raw_errno(faulty_vcpu_fd.get_msrs(&mut kvm_msrs::default())), + badf_errno + ); + assert_eq!( + get_raw_errno(faulty_vcpu_fd.set_msrs(&unsafe { std::mem::zeroed() })), + badf_errno + ); + assert_eq!(get_raw_errno(faulty_vcpu_fd.run()), badf_errno); + } } diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 6dfddea..294395b 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -366,4 +366,51 @@ mod tests { assert!(vm_fd.register_irqfd(evtfd3, 4).is_err()); assert!(vm_fd.register_irqfd(evtfd3, 5).is_err()); } + + fn get_raw_errno(result: super::Result) -> i32 { + result.err().unwrap().raw_os_error().unwrap() + } + + #[test] + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn test_faulty_vm_fd() { + let badf_errno = libc::EBADF; + + let faulty_vm_fd = VmFd { + vm: unsafe { File::from_raw_fd(-1) }, + run_size: 0, + }; + + let invalid_mem_region = kvm_userspace_memory_region { + slot: 0, + guest_phys_addr: 0, + memory_size: 0, + userspace_addr: 0, + flags: 0, + }; + + assert_eq!( + get_raw_errno(faulty_vm_fd.set_user_memory_region(invalid_mem_region)), + badf_errno + ); + assert_eq!(get_raw_errno(faulty_vm_fd.set_tss_address(0)), badf_errno); + assert_eq!(get_raw_errno(faulty_vm_fd.create_irq_chip()), badf_errno); + assert_eq!( + get_raw_errno(faulty_vm_fd.create_pit2(kvm_pit_config::default())), + badf_errno + ); + let event_fd = unsafe { eventfd(0, EFD_NONBLOCK) }; + assert_eq!( + get_raw_errno(faulty_vm_fd.register_ioevent(event_fd, &IoEventAddress::Pio(0), 0u64)), + badf_errno + ); + assert_eq!( + get_raw_errno(faulty_vm_fd.register_irqfd(event_fd, 0)), + badf_errno + ); + + assert_eq!(get_raw_errno(faulty_vm_fd.create_vcpu(0)), badf_errno); + + assert_eq!(get_raw_errno(faulty_vm_fd.get_dirty_log(0, 0)), badf_errno); + } } diff --git a/tests/coverage b/tests/coverage index ee0cc0c..0a2914d 100644 --- a/tests/coverage +++ b/tests/coverage @@ -1 +1 @@ -80.1 \ No newline at end of file +91.1 \ No newline at end of file From 69eba41cf59f4d5c8b1b617ad0c4afd7a3f04772 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Mon, 25 Mar 2019 11:44:49 +0100 Subject: [PATCH 016/332] Updated documentation Enhanced the documentation for various public structures and constants. Mostly added links and added more details around what are the structures or constants used for. Signed-off-by: Andreea Florescu --- src/cap.rs | 6 ++++- src/ioctls/mod.rs | 5 +++- src/ioctls/system.rs | 28 +++++++------------- src/ioctls/vcpu.rs | 63 +++++++++++++++++++++++--------------------- src/ioctls/vm.rs | 16 +++++------ src/lib.rs | 8 ++++-- 6 files changed, 66 insertions(+), 60 deletions(-) diff --git a/src/cap.rs b/src/cap.rs index 7b38ef3..98f7e8e 100644 --- a/src/cap.rs +++ b/src/cap.rs @@ -7,7 +7,11 @@ use kvm_bindings::*; -/// A capability the kernel's KVM interface can possibly expose. +/// Enumeration of the extension capability list that KVM exposes. +/// +/// For details check the +/// [Linux KVM header](https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/kvm.h). +/// #[derive(Clone, Copy, Debug)] #[repr(u32)] // We are allowing docs to be missing here because this enum is a wrapper diff --git a/src/ioctls/mod.rs b/src/ioctls/mod.rs index f913057..e85b6e5 100644 --- a/src/ioctls/mod.rs +++ b/src/ioctls/mod.rs @@ -12,7 +12,10 @@ pub mod vcpu; /// Wrappers over KVM Virtual Machine ioctls. pub mod vm; -/// Wrapper over possible Kvm Result. +/// A specialized `Result` type for KVM ioctls. +/// +/// This typedef is generally used to avoid writing out io::Error directly and +/// is otherwise a direct mapping to Result. pub type Result = result::Result; // Returns a `Vec` with a size in bytes at least as large as `size_in_bytes`. diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index b8565c7..237f0eb 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -13,9 +13,9 @@ use ioctls::{CpuId, Result}; use kvm_ioctls::*; use sys_ioctl::*; -/// A wrapper around opening and using `/dev/kvm`. +/// A wrapper over the `/dev/kvm` file. /// -/// The handle is used to issue system ioctls. +/// The handle is used to issue KVM system ioctls. pub struct Kvm { kvm: File, } @@ -70,10 +70,8 @@ impl Kvm { unsafe { ioctl(self, KVM_GET_API_VERSION()) } } - /// Query the availability of a particular kvm capability. - /// - /// See the documentation for `KVM_CHECK_EXTENSION`. - /// Returns 0 if the capability is not available and > 0 otherwise. + /// Wrapper over `KVM_CHECK_EXTENSION`. + /// Returns 0 if the capability is not available and a positive integer otherwise. /// fn check_extension_int(&self, c: Cap) -> i32 { // Safe because we know that our file is a KVM fd and that the extension is one of the ones @@ -83,18 +81,18 @@ impl Kvm { /// Checks if a particular `Cap` is available. /// - /// According to the KVM API doc, `KVM_CHECK_EXTENSION` returns "0 if unsupported; 1 (or some - /// other positive integer) if supported. + /// See the documentation for `KVM_CHECK_EXTENSION`. + /// Returns "0 if the capability is unsupported or a positive integer otherwise. /// /// # Arguments /// /// * `c` - KVM capability. /// pub fn check_extension(&self, c: Cap) -> bool { - self.check_extension_int(c) >= 1 + self.check_extension_int(c) > 0 } - /// Gets the size of the mmap required to use vcpu's `kvm_run` structure. + /// Returns the size of the memory mapping required to use the vcpu's `kvm_run` structure. /// /// See the documentation for `KVM_GET_VCPU_MMAP_SIZE`. /// @@ -236,8 +234,8 @@ impl Kvm { /// `KVM_GET_VCPU_MMAP_SIZE` ioctl. /// pub fn create_vm(&self) -> Result { - // Safe because we know kvm is a real kvm fd as this module is the only one that can make - // Kvm objects. + // Safe because we know `self.kvm` is a real KVM fd as this module is the only one that + // create Kvm objects. let ret = unsafe { ioctl(&self.kvm, KVM_CREATE_VM()) }; if ret >= 0 { // Safe because we verify the value of ret and we are the owners of the fd. @@ -348,17 +346,11 @@ mod tests { }; assert_eq!(get_raw_errno(faulty_kvm.get_vcpu_mmap_size()), badf_errno); - assert_eq!(faulty_kvm.get_nr_vcpus(), 4); - assert_eq!(faulty_kvm.get_nr_memslots(), 32); - assert_eq!(get_raw_errno(faulty_kvm.get_emulated_cpuid(4)), badf_errno); - assert_eq!(get_raw_errno(faulty_kvm.get_supported_cpuid(4)), badf_errno); - assert_eq!(get_raw_errno(faulty_kvm.get_msr_index_list()), badf_errno); - assert_eq!(get_raw_errno(faulty_kvm.create_vm()), badf_errno); } } diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 5935bb4..5d956db 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -8,7 +8,7 @@ use ioctls::{CpuId, KvmRunWrapper, Result}; use kvm_ioctls::*; use sys_ioctl::*; -/// Reasons for vcpu exits. The exit reasons are mapped to the `KVM_EXIT_*` defines +/// Reasons for vCPU exits. The exit reasons are mapped to the `KVM_EXIT_*` defines /// from `include/uapi/linux/kvm.h`. #[derive(Debug)] pub enum VcpuExit<'a> { @@ -16,11 +16,13 @@ pub enum VcpuExit<'a> { IoOut(u16 /* port */, &'a [u8] /* data */), /// An in port instruction was run on the given port. /// - /// The given slice should be filled in before `Vcpu::run` is called again. + /// The given slice should be filled in before [run()](struct.VcpuFd.html#method.run) + /// is called again. IoIn(u16 /* port */, &'a mut [u8] /* data */), /// A read instruction was run against the given MMIO address. /// - /// The given slice should be filled in before `Vcpu::run` is called again. + /// The given slice should be filled in before [run()](struct.VcpuFd.html#method.run) + /// is called again. MmioRead(u64 /* address */, &'a mut [u8]), /// A write instruction was run against the given MMIO address with the given data. MmioWrite(u64 /* address */, &'a [u8]), @@ -78,18 +80,18 @@ pub enum VcpuExit<'a> { Hyperv, } -/// A wrapper around creating and using a kvm related VCPU fd +/// A wrapper around creating and using a kvm related vCPU file descriptor. pub struct VcpuFd { vcpu: File, kvm_run_ptr: KvmRunWrapper, } impl VcpuFd { - /// Gets the VCPU registers using the `KVM_GET_REGS` ioctl. + /// Gets the vCPU registers using the `KVM_GET_REGS` ioctl. /// #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] pub fn get_regs(&self) -> Result { - // Safe because we know that our file is a VCPU fd, we know the kernel will only read the + // Safe because we know that our file is a vCPU fd, we know the kernel will only read the // correct amount of memory from our pointer, and we verify the return result. let mut regs = unsafe { std::mem::zeroed() }; let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_REGS(), &mut regs) }; @@ -99,7 +101,7 @@ impl VcpuFd { Ok(regs) } - /// Sets the VCPU registers using `KVM_SET_REGS` ioctl. + /// Sets the vCPU registers using `KVM_SET_REGS` ioctl. /// /// # Arguments /// @@ -107,7 +109,7 @@ impl VcpuFd { /// #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] pub fn set_regs(&self, regs: &kvm_regs) -> Result<()> { - // Safe because we know that our file is a VCPU fd, we know the kernel will only read the + // Safe because we know that our file is a vCPU fd, we know the kernel will only read the // correct amount of memory from our pointer, and we verify the return result. let ret = unsafe { ioctl_with_ref(self, KVM_SET_REGS(), regs) }; if ret != 0 { @@ -116,11 +118,11 @@ impl VcpuFd { Ok(()) } - /// Gets the VCPU special registers using `KVM_GET_SREGS` ioctl. + /// Gets the vCPU special registers using `KVM_GET_SREGS` ioctl. /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_sregs(&self) -> Result { - // Safe because we know that our file is a VCPU fd, we know the kernel will only write the + // Safe because we know that our file is a vCPU fd, we know the kernel will only write the // correct amount of memory to our pointer, and we verify the return result. let mut regs = kvm_sregs::default(); @@ -131,7 +133,7 @@ impl VcpuFd { Ok(regs) } - /// Sets the VCPU special registers using `KVM_SET_SREGS` ioctl. + /// Sets the vCPU special registers using `KVM_SET_SREGS` ioctl. /// /// # Arguments /// @@ -139,7 +141,7 @@ impl VcpuFd { /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_sregs(&self, sregs: &kvm_sregs) -> Result<()> { - // Safe because we know that our file is a VCPU fd, we know the kernel will only read the + // Safe because we know that our file is a vCPU fd, we know the kernel will only read the // correct amount of memory from our pointer, and we verify the return result. let ret = unsafe { ioctl_with_ref(self, KVM_SET_SREGS(), sregs) }; if ret != 0 { @@ -247,7 +249,7 @@ impl VcpuFd { Ok(()) } - /// X86 specific call to read model-specific registers for this VCPU. + /// X86 specific call to read model-specific registers for this vCPU. /// /// It emulates `KVM_GET_MSRS` ioctl's behavior by returning the number of MSRs /// successfully read upon success or the last error number in case of failure. @@ -292,7 +294,7 @@ impl VcpuFd { /// Triggers the running of the current virtual CPU returning an exit reason. /// pub fn run(&self) -> Result { - // Safe because we know that our file is a VCPU fd and we verify the return result. + // Safe because we know that our file is a vCPU fd and we verify the return result. let ret = unsafe { ioctl(self, KVM_RUN()) }; if ret == 0 { let run = self.kvm_run_ptr.as_mut_ref(); @@ -311,8 +313,8 @@ impl VcpuFd { // The data_offset is defined by the kernel to be some number of bytes into the // kvm_run stucture, which we have fully mmap'd. let data_ptr = unsafe { run_start.offset(io.data_offset as isize) }; - // The slice's lifetime is limited to the lifetime of this Vcpu, which is equal - // to the mmap of the kvm_run struct that this is slicing from + // The slice's lifetime is limited to the lifetime of this vCPU, which is equal + // to the mmap of the `kvm_run` struct that this is slicing from. let data_slice = unsafe { std::slice::from_raw_parts_mut::(data_ptr as *mut u8, data_size) }; @@ -367,11 +369,11 @@ impl VcpuFd { } } -/// Helper function to create a new VcpuFd. +/// Helper function to create a new `VcpuFd`. /// /// This should not be exported as a public function because the preferred way is to use -/// `create_vcpu` from `VmFd`. The function cannot be part of the Vcpu implementation because -/// then it would be exported with the public VcpuFd interface. +/// `create_vcpu` from `VmFd`. The function cannot be part of the `VcpuFd` implementation because +/// then it would be exported with the public `VcpuFd` interface. pub fn new_vcpu(vcpu: File, kvm_run_ptr: KvmRunWrapper) -> VcpuFd { VcpuFd { vcpu, kvm_run_ptr } } @@ -394,7 +396,7 @@ mod tests { use std::os::unix::io::FromRawFd; use std::ptr::null_mut; - // Helper function for mmap an anonymous memory of `size`. + // Helper function for memory mapping `size` bytes of anonymous memory. // Panics if the mmap fails. fn mmap_anonymous(size: usize) -> *mut u8 { let addr = unsafe { @@ -465,15 +467,15 @@ mod tests { #[test] fn lapic_test() { use std::io::Cursor; - //we might get read of byteorder if we replace 5h3 mem::transmute with something safer + // We might get read of byteorder if we replace mem::transmute with something safer. use self::byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; - //as per https://github.com/torvalds/linux/arch/x86/kvm/lapic.c - //try to write and read the APIC_ICR (0x300) register which is non-read only and - //one can simply write to it + // As per https://github.com/torvalds/linux/arch/x86/kvm/lapic.c + // Try to write and read the APIC_ICR (0x300) register which is non-read only and + // one can simply write to it. let kvm = Kvm::new().unwrap(); assert!(kvm.check_extension(Cap::Irqchip)); let vm = kvm.create_vm().unwrap(); - //the get_lapic ioctl will fail if there is no irqchip created beforehand + // The get_lapic ioctl will fail if there is no irqchip created beforehand. assert!(vm.create_irq_chip().is_ok()); let vcpu = vm.create_vcpu(0).unwrap(); let mut klapic: kvm_lapic_state = vcpu.get_lapic().unwrap(); @@ -567,16 +569,17 @@ mod tests { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); - // This example based on https://lwn.net/Articles/658511/ + // This example is based on https://lwn.net/Articles/658511/ + #[rustfmt::skip] let code = [ 0xba, 0xf8, 0x03, /* mov $0x3f8, %dx */ 0x00, 0xd8, /* add %bl, %al */ 0x04, b'0', /* add $'0', %al */ 0xee, /* out %al, %dx */ 0xec, /* in %dx, %al */ - 0xc6, 0x06, 0x00, 0x80, 0x00, /* movl $0, (0x8000) */ - 0x8a, 0x16, 0x00, 0x80, /* movl (0x8000), %dl */ - 0xc6, 0x06, 0x00, 0x20, 0x00, /* movl $0, (0x2000) */ + 0xc6, 0x06, 0x00, 0x80, 0x00, /* movl $0, (0x8000); This generates a MMIO Write.*/ + 0x8a, 0x16, 0x00, 0x80, /* movl (0x8000), %dl; This generates a MMIO Read.*/ + 0xc6, 0x06, 0x00, 0x20, 0x00, /* movl $0, (0x2000); Dirty one page in guest mem. */ 0xf4, /* hlt */ ]; @@ -696,7 +699,7 @@ mod tests { ), badf_errno ); - // kvm_lapic_state does not implement debug by default so we cannot + // `kvm_lapic_state` does not implement debug by default so we cannot // use unwrap_err here. assert!(faulty_vcpu_fd.get_lapic().is_err()); assert_eq!( diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 294395b..9a70dff 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -163,7 +163,7 @@ impl VmFd { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_dirty_log(&self, slot: u32, memory_size: usize) -> Result> { // Compute the length of the bitmap needed for all dirty pages in one memory slot. - // One memory page is 4KiB (4096 bits) and KVM_GET_DIRTY_LOG returns one dirty bit for + // One memory page is 4KiB (4096 bits) and `KVM_GET_DIRTY_LOG` returns one dirty bit for // each page. let page_size = 4 << 10; @@ -220,14 +220,14 @@ impl VmFd { } } - /// Creates a new KVM VCPU fd. + /// Creates a new KVM vCPU fd. /// /// # Arguments /// - /// * `id` - The CPU number between [0, max vcpus). + /// * `id` - The CPU number between [0, max vs). /// /// # Errors - /// Returns an error when the VM fd is invalid or the VCPU memory cannot be mapped correctly. + /// Returns an error when the VM fd is invalid or the vCPU memory cannot be mapped correctly. /// pub fn create_vcpu(&self, id: u8) -> Result { // Safe because we know that vm is a VM fd and we verify the return result. @@ -237,7 +237,7 @@ impl VmFd { return Err(io::Error::last_os_error()); } - // Wrap the vcpu now in case the following ? returns early. This is safe because we verified + // Wrap the vCPU now in case the following ? returns early. This is safe because we verified // the value of the fd and we own the fd. let vcpu = unsafe { File::from_raw_fd(vcpu_fd) }; @@ -252,11 +252,11 @@ impl VmFd { } } -/// Helper function to create a new VcpuFd. +/// Helper function to create a new `VmFd`. /// /// This should not be exported as a public function because the preferred way is to use -/// `create_vm` from `Kvm`. The function cannot be part of the Vcpu implementation because -/// then it would be exported with the public VcpuFd interface. +/// `create_vm` from `Kvm`. The function cannot be part of the `VmFd` implementation because +/// then it would be exported with the public `VmFd` interface. pub fn new_vmfd(vm: File, run_size: usize) -> VmFd { VmFd { vm, run_size } } diff --git a/src/lib.rs b/src/lib.rs index afb9856..b922b6f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,9 +21,13 @@ mod ioctls; pub use cap::Cap; pub use ioctls::system::Kvm; -pub use ioctls::vcpu::VcpuFd; +pub use ioctls::vcpu::{VcpuExit, VcpuFd}; pub use ioctls::vm::VmFd; pub use ioctls::{CpuId, KvmRunWrapper, Result}; -/// Taken from Linux Kernel v4.14.13 (arch/x86/include/asm/kvm_host.h) +/// Maximum number of CPUID entries that can be returned by a call to KVM ioctls. +/// +/// This value is taken from Linux Kernel v4.14.13 (arch/x86/include/asm/kvm_host.h). +/// It can be used for calls to [get_supported_cpuid](struct.Kvm.html#method.get_supported_cpuid) and +/// [get_emulated_cpuid](struct.Kvm.html#method.get_emulated_cpuid). pub const MAX_KVM_CPUID_ENTRIES: usize = 80; From 8b4676c0a4e38988fbbba16380cb1d98268b1d7f Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Mon, 25 Mar 2019 15:20:03 +0100 Subject: [PATCH 017/332] updated .gitignore to ignore pycache Signed-off-by: Andreea Florescu --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 53eaa21..d827bd6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ /target **/*.rs.bk +**/.pytest_cache/ +**/__pycache__/* From 952799830119748062de3d84e7ba4127efd12a7f Mon Sep 17 00:00:00 2001 From: Diana Popa Date: Fri, 29 Mar 2019 17:40:47 +0200 Subject: [PATCH 018/332] correctly label x86/x86_64 specific code Signed-off-by: Diana Popa --- src/ioctls/mod.rs | 4 +++- src/ioctls/system.rs | 4 +++- src/ioctls/vcpu.rs | 4 +++- src/lib.rs | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/ioctls/mod.rs b/src/ioctls/mod.rs index e85b6e5..36be292 100644 --- a/src/ioctls/mod.rs +++ b/src/ioctls/mod.rs @@ -1,4 +1,6 @@ -use kvm_bindings::{kvm_cpuid2, kvm_cpuid_entry2, kvm_run}; +use kvm_bindings::kvm_run; +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +use kvm_bindings::{kvm_cpuid2, kvm_cpuid_entry2}; use std::io; use std::mem::size_of; use std::os::unix::io::AsRawFd; diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index 237f0eb..b2702d6 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -9,7 +9,9 @@ use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use cap::Cap; use ioctls::vec_with_array_field; use ioctls::vm::{new_vmfd, VmFd}; -use ioctls::{CpuId, Result}; +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +use ioctls::CpuId; +use ioctls::Result; use kvm_ioctls::*; use sys_ioctl::*; diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 5d956db..c176646 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -4,7 +4,9 @@ use std::fs::File; use std::io; use std::os::unix::io::{AsRawFd, RawFd}; -use ioctls::{CpuId, KvmRunWrapper, Result}; +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +use ioctls::CpuId; +use ioctls::{KvmRunWrapper, Result}; use kvm_ioctls::*; use sys_ioctl::*; diff --git a/src/lib.rs b/src/lib.rs index b922b6f..ebcc792 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,7 +23,9 @@ pub use cap::Cap; pub use ioctls::system::Kvm; pub use ioctls::vcpu::{VcpuExit, VcpuFd}; pub use ioctls::vm::VmFd; -pub use ioctls::{CpuId, KvmRunWrapper, Result}; +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +use ioctls::CpuId; +use ioctls::{KvmRunWrapper, Result}; /// Maximum number of CPUID entries that can be returned by a call to KVM ioctls. /// From 64e79ccce2bad61046591950dfa998ae51ba7910 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Mon, 1 Apr 2019 12:16:51 +0300 Subject: [PATCH 019/332] update coverage report path With newer versions of kcov the coverage report is saved to `index.js` instead of `index.json`. Signed-off-by: Andreea Florescu --- tests/test_coverage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_coverage.py b/tests/test_coverage.py index a08ee27..3a0d423 100644 --- a/tests/test_coverage.py +++ b/tests/test_coverage.py @@ -46,7 +46,7 @@ def _get_current_coverage(): subprocess.run(kcov_cmd, shell=True, check=True) # Read the coverage reported by kcov. - coverage_file = os.path.join(kcov_ouput_dir, 'index.json') + coverage_file = os.path.join(kcov_ouput_dir, 'index.js') with open(coverage_file) as cov_output: coverage = float(re.findall( r'"covered":"(\d+\.\d)"', From 3483f6989970e78028ac5a137b9af9c26eb7666e Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Wed, 3 Apr 2019 11:39:48 +0200 Subject: [PATCH 020/332] lib: Export CpuId, KvmRunWrapper and Result They were unexported by commit 95279983. Fixes: #14 Signed-off-by: Samuel Ortiz --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ebcc792..ae78061 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,8 +24,8 @@ pub use ioctls::system::Kvm; pub use ioctls::vcpu::{VcpuExit, VcpuFd}; pub use ioctls::vm::VmFd; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -use ioctls::CpuId; -use ioctls::{KvmRunWrapper, Result}; +pub use ioctls::CpuId; +pub use ioctls::{KvmRunWrapper, Result}; /// Maximum number of CPUID entries that can be returned by a call to KVM ioctls. /// From 47a2525996694d21e6dc1480a9c2e090de18bdf6 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Wed, 3 Apr 2019 15:13:22 +0200 Subject: [PATCH 021/332] lib: Example code to check our public structure exposure The cargo tests run outside of the crate scope and allow us to verify that our public structures are properly exported. Fixes: #14 Signed-off-by: Samuel Ortiz --- src/ioctls/mod.rs | 7 +++++++ src/lib.rs | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/src/ioctls/mod.rs b/src/ioctls/mod.rs index 36be292..429a05f 100644 --- a/src/ioctls/mod.rs +++ b/src/ioctls/mod.rs @@ -106,6 +106,13 @@ impl CpuId { /// /// * `array_len` - Maximum number of CPUID entries. /// + /// # Example + /// + /// ``` + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// use kvm_ioctls::CpuId; + /// let cpu_id = CpuId::new(32); + /// ``` pub fn new(array_len: usize) -> CpuId { let mut kvm_cpuid = vec_with_array_field::(array_len); kvm_cpuid[0].nent = array_len as u32; diff --git a/src/lib.rs b/src/lib.rs index ae78061..89ede52 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,6 +25,14 @@ pub use ioctls::vcpu::{VcpuExit, VcpuFd}; pub use ioctls::vm::VmFd; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub use ioctls::CpuId; +// The following example is used to verify that our public +// structures are exported properly. +/// # Example +/// +/// ``` +/// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +/// use kvm_ioctls::{KvmRunWrapper, Result}; +/// ``` pub use ioctls::{KvmRunWrapper, Result}; /// Maximum number of CPUID entries that can be returned by a call to KVM ioctls. From a6c6a1f83d69123a976dace4696f6cc6693c310f Mon Sep 17 00:00:00 2001 From: Diana Popa Date: Mon, 8 Apr 2019 15:20:23 +0300 Subject: [PATCH 022/332] arch64: add necessary kvm ioctls Tested that cargo test is functional on aarch64 too. Signed-off-by: Diana Popa --- src/ioctls/device.rs | 97 ++++++++++++++++++++++++++++++++++++++++++++ src/ioctls/mod.rs | 9 ++-- src/ioctls/system.rs | 10 +++-- src/ioctls/vcpu.rs | 83 +++++++++++++++++++++++++++++++++++++ src/ioctls/vm.rs | 52 +++++++++++++++++++++--- src/kvm_ioctls.rs | 8 ++++ src/lib.rs | 1 + tests/coverage | 2 +- 8 files changed, 250 insertions(+), 12 deletions(-) create mode 100644 src/ioctls/device.rs diff --git a/src/ioctls/device.rs b/src/ioctls/device.rs new file mode 100644 index 0000000..23630e2 --- /dev/null +++ b/src/ioctls/device.rs @@ -0,0 +1,97 @@ +// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use std::fs::File; +use std::io; +use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; + +use kvm_bindings::kvm_device_attr; + +use ioctls::Result; +use kvm_ioctls::KVM_SET_DEVICE_ATTR; +use sys_ioctl::ioctl_with_ref; + +/// Wrapper over the file descriptor obtained when creating an emulated device in the kernel. +pub struct DeviceFd { + fd: File, +} + +impl DeviceFd { + /// Sets a specified piece of device configuration and/or state. + /// + /// See the documentation for `KVM_SET_DEVICE_ATTR`. + /// # Arguments + /// + /// * `device_attr` - The device attribute to be set. + /// + pub fn set_device_attr(&self, device_attr: &kvm_device_attr) -> Result<()> { + let ret = unsafe { ioctl_with_ref(self, KVM_SET_DEVICE_ATTR(), device_attr) }; + if ret != 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) + } +} + +/// Helper function for creating a new device. +pub fn new_device(dev_fd: File) -> DeviceFd { + DeviceFd { fd: dev_fd } +} + +impl AsRawFd for DeviceFd { + fn as_raw_fd(&self) -> RawFd { + self.fd.as_raw_fd() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ioctls::system::Kvm; + use kvm_bindings::{ + kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, kvm_device_type_KVM_DEV_TYPE_FSL_MPIC_20, + kvm_device_type_KVM_DEV_TYPE_VFIO, KVM_CREATE_DEVICE_TEST, KVM_DEV_VFIO_GROUP, + KVM_DEV_VFIO_GROUP_ADD, + }; + use std::io::{Error, ErrorKind}; + + #[test] + fn test_create_device() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + + let mut gic_device = kvm_bindings::kvm_create_device { + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + type_: kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, + #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + type_: kvm_device_type_KVM_DEV_TYPE_FSL_MPIC_20, + fd: 0, + flags: KVM_CREATE_DEVICE_TEST, + }; + // This fails on x86_64 because there is no VGIC there. + // This fails on aarch64 as it does not use MPIC (MultiProcessor Interrupt Controller), it uses + // the VGIC. + assert!(vm.create_device(&mut gic_device).is_err()); + + if cfg!(any(target_arch = "x86", target_arch = "x86_64")) { + gic_device.type_ = kvm_device_type_KVM_DEV_TYPE_VFIO; + } else if cfg!(any(target_arch = "arm", target_arch = "aarch64")) { + gic_device.type_ = kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3; + } + let device_fd = vm + .create_device(&mut gic_device) + .expect("Cannot create KVM device"); + + let dist_attr = kvm_bindings::kvm_device_attr { + group: KVM_DEV_VFIO_GROUP, + attr: KVM_DEV_VFIO_GROUP_ADD as u64, + addr: 0x0, + flags: 0, + }; + device_fd.set_device_attr(&dist_attr); + // We are just creating a test device. Creating a real device would make the CI dependent + // on host configuration (like having /dev/vfio). We expect this to fail. + assert!(device_fd.set_device_attr(&dist_attr).is_err()); + assert_eq!(io::Error::last_os_error().raw_os_error().unwrap(), 25); + } +} diff --git a/src/ioctls/mod.rs b/src/ioctls/mod.rs index 429a05f..cae4af3 100644 --- a/src/ioctls/mod.rs +++ b/src/ioctls/mod.rs @@ -1,12 +1,15 @@ -use kvm_bindings::kvm_run; -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -use kvm_bindings::{kvm_cpuid2, kvm_cpuid_entry2}; use std::io; use std::mem::size_of; use std::os::unix::io::AsRawFd; use std::ptr::null_mut; use std::result; +use kvm_bindings::kvm_run; +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +use kvm_bindings::{kvm_cpuid2, kvm_cpuid_entry2}; + +/// Wrappers over KVM device ioctls. +pub mod device; /// Wrappers over KVM system ioctls. pub mod system; /// Wrappers over KVM VCPU ioctls. diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index b2702d6..24f8964 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -350,9 +350,13 @@ mod tests { assert_eq!(get_raw_errno(faulty_kvm.get_vcpu_mmap_size()), badf_errno); assert_eq!(faulty_kvm.get_nr_vcpus(), 4); assert_eq!(faulty_kvm.get_nr_memslots(), 32); - assert_eq!(get_raw_errno(faulty_kvm.get_emulated_cpuid(4)), badf_errno); - assert_eq!(get_raw_errno(faulty_kvm.get_supported_cpuid(4)), badf_errno); - assert_eq!(get_raw_errno(faulty_kvm.get_msr_index_list()), badf_errno); + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + { + assert_eq!(get_raw_errno(faulty_kvm.get_emulated_cpuid(4)), badf_errno); + assert_eq!(get_raw_errno(faulty_kvm.get_supported_cpuid(4)), badf_errno); + + assert_eq!(get_raw_errno(faulty_kvm.get_msr_index_list()), badf_errno); + } assert_eq!(get_raw_errno(faulty_kvm.create_vm()), badf_errno); } } diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index c176646..0a6211c 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -293,6 +293,52 @@ impl VcpuFd { Ok(()) } + /// Sets the type of CPU to be exposed to the guest and optional features. + /// + /// This initializes an ARM vCPU to the specified type with the specified features + /// and resets the values of all of its registers to defaults. + /// + /// # Arguments + /// + /// * `kvi` - information about preferred CPU target type and recommended features for it. + /// + #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + pub fn vcpu_init(&self, kvi: &kvm_vcpu_init) -> Result<()> { + // This is safe because we allocated the struct and we know the kernel will read + // exactly the size of the struct. + let ret = unsafe { ioctl_with_ref(self, KVM_ARM_VCPU_INIT(), kvi) }; + if ret < 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) + } + + /// Sets the value of one register for this vCPU. + /// + /// The id of the register is encoded as specified in the kernel documentation + /// for `KVM_SET_ONE_REG`. + /// + /// # Arguments + /// + /// * `reg_id` - ID of the register for which we are setting the value. + /// * `data` - value for the specified register. + /// + #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + pub fn set_one_reg(&self, reg_id: u64, data: u64) -> Result<()> { + let data_ref = &data as *const u64; + let onereg = kvm_one_reg { + id: reg_id, + addr: data_ref as u64, + }; + // This is safe because we allocated the struct and we know the kernel will read + // exactly the size of the struct. + let ret = unsafe { ioctl_with_ref(self, KVM_SET_ONE_REG(), &onereg) }; + if ret < 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) + } + /// Triggers the running of the current virtual CPU returning an exit reason. /// pub fn run(&self) -> Result { @@ -718,4 +764,41 @@ mod tests { ); assert_eq!(get_raw_errno(faulty_vcpu_fd.run()), badf_errno); } + + #[test] + #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + fn test_get_preferred_target() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let vcpu = vm.create_vcpu(0).unwrap(); + + let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default(); + assert!(vcpu.vcpu_init(&kvi).is_err()); + + vm.get_preferred_target(&mut kvi) + .expect("Cannot get preferred target"); + assert!(vcpu.vcpu_init(&kvi).is_ok()); + } + + #[test] + #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + fn test_set_one_reg() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let vcpu = vm.create_vcpu(0).unwrap(); + + let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default(); + vm.get_preferred_target(&mut kvi) + .expect("Cannot get preferred target"); + vcpu.vcpu_init(&kvi).expect("Cannot initialize vcpu"); + let mut data: u64 = 0; + let mut reg_id: u64 = 0; + + assert!(vcpu.set_one_reg(reg_id, data).is_err()); + // Exercising KVM_SET_ONE_REG by trying to alter the data inside the PSTATE register (which is a + // specific aarch64 register). + const PSTATE_REG_ID: u64 = 0x6030_0000_0010_0042; + vcpu.set_one_reg(PSTATE_REG_ID, data) + .expect("Failed to set pstate register"); + } } diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 9a70dff..40535f1 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -5,6 +5,8 @@ use std::io; use std::os::raw::{c_ulong, c_void}; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; +use ioctls::device::new_device; +use ioctls::device::DeviceFd; use ioctls::vcpu::new_vcpu; use ioctls::vcpu::VcpuFd; use ioctls::KvmRunWrapper; @@ -246,6 +248,28 @@ impl VmFd { Ok(new_vcpu(vcpu, kvm_run_ptr)) } + /// Creates an emulated device in the kernel. + pub fn create_device(&self, device: &mut kvm_create_device) -> Result { + let ret = unsafe { ioctl_with_ref(self, KVM_CREATE_DEVICE(), device) }; + if ret == 0 { + Ok((new_device(unsafe { File::from_raw_fd(device.fd as i32) }))) + } else { + return Err(io::Error::last_os_error()); + } + } + + /// This queries the kernel for the preferred target CPU type. + #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + pub fn get_preferred_target(&self, kvi: &mut kvm_vcpu_init) -> Result<()> { + // The ioctl is safe because we allocated the struct and we know the + // kernel will write exactly the size of the struct. + let ret = unsafe { ioctl_with_mut_ref(self, KVM_ARM_PREFERRED_TARGET(), kvi) }; + if ret != 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) + } + /// Get the `kvm_run` size. pub fn get_run_size(&self) -> usize { self.run_size @@ -307,7 +331,12 @@ mod tests { let kvm = Kvm::new().unwrap(); assert!(kvm.check_extension(Cap::Irqchip)); let vm = kvm.create_vm().unwrap(); - assert!(vm.create_irq_chip().is_ok()); + if cfg!(any(target_arch = "x86", target_arch = "x86_64")) { + assert!(vm.create_irq_chip().is_ok()); + } else if cfg!(any(target_arch = "arm", target_arch = "aarch64")) { + // On arm, we expect this to fail as the irq chip needs to be created after the vcpus. + assert!(vm.create_irq_chip().is_err()); + } } #[test] @@ -358,11 +387,15 @@ mod tests { let evtfd1 = unsafe { eventfd(0, EFD_NONBLOCK) }; let evtfd2 = unsafe { eventfd(0, EFD_NONBLOCK) }; let evtfd3 = unsafe { eventfd(0, EFD_NONBLOCK) }; + if cfg!(any(target_arch = "x86", target_arch = "x86_64")) { + assert!(vm_fd.register_irqfd(evtfd1, 4).is_ok()); + assert!(vm_fd.register_irqfd(evtfd2, 8).is_ok()); + assert!(vm_fd.register_irqfd(evtfd3, 4).is_ok()); + } - assert!(vm_fd.register_irqfd(evtfd1, 4).is_ok()); - assert!(vm_fd.register_irqfd(evtfd2, 8).is_ok()); - assert!(vm_fd.register_irqfd(evtfd3, 4).is_ok()); - + // On aarch64, this fails because setting up the interrupt controller is mandatory before + // registering any IRQ. + // On x86_64 this fails as the event fd was already matched with a GSI. assert!(vm_fd.register_irqfd(evtfd3, 4).is_err()); assert!(vm_fd.register_irqfd(evtfd3, 5).is_err()); } @@ -413,4 +446,13 @@ mod tests { assert_eq!(get_raw_errno(faulty_vm_fd.get_dirty_log(0, 0)), badf_errno); } + + #[test] + #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + fn test_get_preferred_target() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default(); + assert!(vm.get_preferred_target(&mut kvi).is_ok()); + } } diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index b3d2dc1..6305ae1 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -82,6 +82,14 @@ ioctl_iow_nr!(KVM_SET_FPU, KVMIO, 0x8d, kvm_fpu); ioctl_ior_nr!(KVM_GET_LAPIC, KVMIO, 0x8e, kvm_lapic_state); #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_iow_nr!(KVM_SET_LAPIC, KVMIO, 0x8f, kvm_lapic_state); +#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] +ioctl_iow_nr!(KVM_SET_ONE_REG, KVMIO, 0xac, kvm_one_reg); +#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] +ioctl_iow_nr!(KVM_ARM_VCPU_INIT, KVMIO, 0xae, kvm_vcpu_init); +#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] +ioctl_ior_nr!(KVM_ARM_PREFERRED_TARGET, KVMIO, 0xaf, kvm_vcpu_init); +ioctl_iowr_nr!(KVM_CREATE_DEVICE, KVMIO, 0xe0, kvm_create_device); +ioctl_iow_nr!(KVM_SET_DEVICE_ATTR, KVMIO, 0xe1, kvm_device_attr); #[cfg(test)] mod tests { diff --git a/src/lib.rs b/src/lib.rs index 89ede52..2248498 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,7 @@ mod cap; mod ioctls; pub use cap::Cap; +pub use ioctls::device::DeviceFd; pub use ioctls::system::Kvm; pub use ioctls::vcpu::{VcpuExit, VcpuFd}; pub use ioctls::vm::VmFd; diff --git a/tests/coverage b/tests/coverage index 0a2914d..9d1fca3 100644 --- a/tests/coverage +++ b/tests/coverage @@ -1 +1 @@ -91.1 \ No newline at end of file +91.3 \ No newline at end of file From 681ca52542288a6e3bc6e14af466b7ca6210a782 Mon Sep 17 00:00:00 2001 From: Diana Popa Date: Mon, 8 Apr 2019 15:21:54 +0300 Subject: [PATCH 023/332] add license header for the "ioctls" module Signed-off-by: Diana Popa --- src/ioctls/mod.rs | 7 +++++++ src/ioctls/system.rs | 7 +++++++ src/ioctls/vcpu.rs | 7 +++++++ src/ioctls/vm.rs | 7 +++++++ 4 files changed, 28 insertions(+) diff --git a/src/ioctls/mod.rs b/src/ioctls/mod.rs index cae4af3..f1e320a 100644 --- a/src/ioctls/mod.rs +++ b/src/ioctls/mod.rs @@ -1,3 +1,10 @@ +// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +// +// Portions Copyright 2017 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the THIRD-PARTY file. + use std::io; use std::mem::size_of; use std::os::unix::io::AsRawFd; diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index 24f8964..f833db0 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -1,3 +1,10 @@ +// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +// +// Portions Copyright 2017 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the THIRD-PARTY file. + use kvm_bindings::*; use libc::{open, O_CLOEXEC, O_RDWR}; diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 0a6211c..4fa19e1 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1,3 +1,10 @@ +// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +// +// Portions Copyright 2017 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the THIRD-PARTY file. + use kvm_bindings::*; use libc::EINVAL; use std::fs::File; diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 40535f1..30feb89 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -1,3 +1,10 @@ +// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +// +// Portions Copyright 2017 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the THIRD-PARTY file. + use ioctls::Result; use kvm_bindings::*; use std::fs::File; From 83c98b2724332c312cba7f79a2c1dbf3d089bf24 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Fri, 5 Apr 2019 16:13:00 +0300 Subject: [PATCH 024/332] fixed link issues on aarch64 musl The linker was unable to find __addtf3, __multf3 and __subtf3. Added target-feature=+crt-static and link-arg=-lgcc as a temporary workaround. This seems to be the accepted fix in the Rust community: https://github.com/rust-lang-nursery/compiler-builtins/issues/201 A permanent fix is yet to be implemented in the Rust compiler. Signed-off-by: Andreea Florescu --- .cargo/config | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .cargo/config diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 0000000..ba63e46 --- /dev/null +++ b/.cargo/config @@ -0,0 +1,3 @@ +[target.aarch64-unknown-linux-musl] +rustflags = [ "-C", "target-feature=+crt-static", "-C", "link-arg=-lgcc" ] + From 6b40632f8fd63030e5702bebd722539193b19c66 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Mon, 8 Apr 2019 15:41:45 +0300 Subject: [PATCH 025/332] added documentation for Cap Signed-off-by: Andreea Florescu --- src/cap.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/cap.rs b/src/cap.rs index 98f7e8e..4a3a990 100644 --- a/src/cap.rs +++ b/src/cap.rs @@ -7,9 +7,13 @@ use kvm_bindings::*; -/// Enumeration of the extension capability list that KVM exposes. +/// Capabilities exposed by KVM. /// -/// For details check the +/// The capabilities list can be used in conjunction with +/// [Kvm::check_extension()](struct.Kvm.html#method.check_extension) to check if a particular +/// capability is available. +/// +/// The list of capabilities is based on the the KVM_CAP_* defines from the /// [Linux KVM header](https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/kvm.h). /// #[derive(Clone, Copy, Debug)] From 785767467d518ce0c1e5099b5c0bcd250a236eec Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Mon, 8 Apr 2019 15:42:59 +0300 Subject: [PATCH 026/332] renamed get_run_size to run_size Signed-off-by: Andreea Florescu --- src/ioctls/system.rs | 2 +- src/ioctls/vm.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index f833db0..596e044 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -300,7 +300,7 @@ mod tests { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); - assert_eq!(vm.get_run_size(), kvm.get_vcpu_mmap_size().unwrap()); + assert_eq!(vm.run_size(), kvm.get_vcpu_mmap_size().unwrap()); } #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 30feb89..60e15ad 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -278,7 +278,7 @@ impl VmFd { } /// Get the `kvm_run` size. - pub fn get_run_size(&self) -> usize { + pub fn run_size(&self) -> usize { self.run_size } } From 19493f6feadff734ab4ce928b7c67340d9313626 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Tue, 9 Apr 2019 12:19:45 +0300 Subject: [PATCH 027/332] export NoDatamatch and IoEventAddress These are used for calling VM ioctls from external crates. Signed-off-by: Andreea Florescu --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 2248498..d353da1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,7 +23,7 @@ pub use cap::Cap; pub use ioctls::device::DeviceFd; pub use ioctls::system::Kvm; pub use ioctls::vcpu::{VcpuExit, VcpuFd}; -pub use ioctls::vm::VmFd; +pub use ioctls::vm::{IoEventAddress, NoDatamatch, VmFd}; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub use ioctls::CpuId; // The following example is used to verify that our public From a2ad4f401c068c7b1b2442db00e424589c1c272e Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Mon, 8 Apr 2019 15:44:14 +0300 Subject: [PATCH 028/332] improved documentation on system ioctls - Added examples for all ioctls. - Added links to KVM documentation and internal links to KVM structures and functions for ease of access. - Split documentation into summary and description. Signed-off-by: Andreea Florescu --- src/ioctls/system.rs | 141 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 129 insertions(+), 12 deletions(-) diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index 596e044..47d8773 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -22,15 +22,20 @@ use ioctls::Result; use kvm_ioctls::*; use sys_ioctl::*; -/// A wrapper over the `/dev/kvm` file. -/// -/// The handle is used to issue KVM system ioctls. +/// Wrapper over KVM system ioctls. pub struct Kvm { kvm: File, } impl Kvm { - /// Opens `/dev/kvm/` and returns a `Kvm` object on success. + /// Opens `/dev/kvm` and returns a `Kvm` object on success. + /// + /// # Example + /// + /// ``` + /// use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// ``` /// #[allow(clippy::new_ret_no_self)] pub fn new() -> Result { @@ -43,6 +48,8 @@ impl Kvm { /// Creates a new Kvm object assuming `fd` represents an existing open file descriptor /// associated with `/dev/kvm`. /// + /// For usage examples check [open_with_cloexec()](struct.Kvm.html#method.open_with_cloexec). + /// /// # Arguments /// /// * `fd` - File descriptor for `/dev/kvm`. @@ -55,10 +62,25 @@ impl Kvm { /// Opens `/dev/kvm` and returns the fd number on success. /// + /// One usecase for this method is opening `/dev/kvm` before exec-ing into a + /// process with seccomp filters enabled that blacklist the `sys_open` syscall. + /// For this usecase `open_with_cloexec` must be called with the `close_on_exec` + /// parameter set to false. + /// /// # Arguments /// /// * `close_on_exec`: If true opens `/dev/kvm` using the `O_CLOEXEC` flag. /// + /// # Example + /// + /// ``` + /// # use kvm_ioctls::Kvm; + /// let kvm_fd = Kvm::open_with_cloexec(false).unwrap(); + /// // The `kvm_fd` can now be passed to another process where we can use + /// // `new_with_fd_number` for creating a `Kvm` object: + /// let kvm = unsafe { Kvm::new_with_fd_number(kvm_fd) }; + /// ``` + /// pub fn open_with_cloexec(close_on_exec: bool) -> Result { let open_flags = O_RDWR | if close_on_exec { O_CLOEXEC } else { 0 }; // Safe because we give a constant nul-terminated string and verify the result. @@ -73,6 +95,15 @@ impl Kvm { /// Returns the KVM API version. /// /// See the documentation for `KVM_GET_API_VERSION`. + /// + /// # Example + /// + /// ``` + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// assert_eq!(kvm.get_api_version(), 12); + /// ``` + /// pub fn get_api_version(&self) -> i32 { // Safe because we know that our file is a KVM fd and that the request is one of the ones // defined by kernel. @@ -80,8 +111,8 @@ impl Kvm { } /// Wrapper over `KVM_CHECK_EXTENSION`. - /// Returns 0 if the capability is not available and a positive integer otherwise. /// + /// Returns 0 if the capability is not available and a positive integer otherwise. fn check_extension_int(&self, c: Cap) -> i32 { // Safe because we know that our file is a KVM fd and that the extension is one of the ones // defined by kernel. @@ -90,12 +121,23 @@ impl Kvm { /// Checks if a particular `Cap` is available. /// + /// Returns true if the capability is supported and false otherwise. /// See the documentation for `KVM_CHECK_EXTENSION`. - /// Returns "0 if the capability is unsupported or a positive integer otherwise. /// /// # Arguments /// - /// * `c` - KVM capability. + /// * `c` - KVM capability to check. + /// + /// # Example + /// + /// ``` + /// # use kvm_ioctls::Kvm; + /// use kvm_ioctls::Cap; + /// + /// let kvm = Kvm::new().unwrap(); + /// // Check if `KVM_CAP_USER_MEMORY` is supported. + /// assert!(kvm.check_extension(Cap::UserMemory)); + /// ``` /// pub fn check_extension(&self, c: Cap) -> bool { self.check_extension_int(c) > 0 @@ -105,6 +147,14 @@ impl Kvm { /// /// See the documentation for `KVM_GET_VCPU_MMAP_SIZE`. /// + /// # Example + /// + /// ``` + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// assert!(kvm.get_vcpu_mmap_size().unwrap() > 0); + /// ``` + /// pub fn get_vcpu_mmap_size(&self) -> Result { // Safe because we know that our file is a KVM fd and we verify the return result. let res = unsafe { ioctl(self, KVM_GET_VCPU_MMAP_SIZE()) }; @@ -119,6 +169,16 @@ impl Kvm { /// /// See the documentation for `KVM_CAP_NR_VCPUS`. /// Default to 4 when `KVM_CAP_NR_VCPUS` is not implemented. + /// + /// # Example + /// + /// ``` + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// // We expect the number of vCPUs to be > 0 as per KVM API documentation. + /// assert!(kvm.get_nr_vcpus() > 0); + /// ``` + /// pub fn get_nr_vcpus(&self) -> usize { let x = self.check_extension_int(Cap::NrVcpus); if x > 0 { @@ -128,13 +188,21 @@ impl Kvm { } } - /// Gets the maximum allowed memory slots per VM. + /// Returns the maximum allowed memory slots per VM. /// /// KVM reports the number of available memory slots (`KVM_CAP_NR_MEMSLOTS`) /// using the extension interface. Both x86 and s390 implement this, ARM /// and powerpc do not yet enable it. /// Default to 32 when `KVM_CAP_NR_MEMSLOTS` is not implemented. /// + /// # Example + /// + /// ``` + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// assert!(kvm.get_nr_memslots() > 0); + /// ``` + /// pub fn get_nr_memslots(&self) -> usize { let x = self.check_extension_int(Cap::NrMemslots); if x > 0 { @@ -147,7 +215,16 @@ impl Kvm { /// Gets the recommended maximum number of VCPUs per VM. /// /// See the documentation for `KVM_CAP_MAX_VCPUS`. - /// Default to `KVM_CAP_NR_VCPUS` when `KVM_CAP_MAX_VCPUS` is not implemented. + /// Returns [get_nr_vcpus()](struct.Kvm.html#method.get_nr_vcpus) when + /// `KVM_CAP_MAX_VCPUS` is not implemented. + /// + /// # Example + /// + /// ``` + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// assert!(kvm.get_max_vcpus() > 0); + /// ``` /// pub fn get_max_vcpus(&self) -> usize { match self.check_extension_int(Cap::MaxVcpus) { @@ -175,13 +252,24 @@ impl Kvm { /// X86 specific call to get the system emulated CPUID values. /// - /// See the documentation for KVM_GET_EMULATED_CPUID. + /// See the documentation for `KVM_GET_EMULATED_CPUID`. /// /// # Arguments /// /// * `max_entries_count` - Maximum number of CPUID entries. This function can return less than /// this when the hardware does not support so many CPUID entries. /// + /// # Example + /// + /// ``` + /// use kvm_ioctls::{Kvm, MAX_KVM_CPUID_ENTRIES}; + /// + /// let kvm = Kvm::new().unwrap(); + /// let mut cpuid = kvm.get_emulated_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); + /// let cpuid_entries = cpuid.mut_entries_slice(); + /// assert!(cpuid_entries.len() <= MAX_KVM_CPUID_ENTRIES); + /// ``` + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_emulated_cpuid(&self, max_entries_count: usize) -> Result { self.get_cpuid(KVM_GET_EMULATED_CPUID(), max_entries_count) @@ -189,13 +277,24 @@ impl Kvm { /// X86 specific call to get the system supported CPUID values. /// - /// See the documentation for KVM_GET_SUPPORTED_CPUID. + /// See the documentation for `KVM_GET_SUPPORTED_CPUID`. /// /// # Arguments /// /// * `max_entries_count` - Maximum number of CPUID entries. This function can return less than /// this when the hardware does not support so many CPUID entries. /// + /// # Example + /// + /// ``` + /// use kvm_ioctls::{Kvm, MAX_KVM_CPUID_ENTRIES}; + /// + /// let kvm = Kvm::new().unwrap(); + /// let mut cpuid = kvm.get_emulated_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); + /// let cpuid_entries = cpuid.mut_entries_slice(); + /// assert!(cpuid_entries.len() <= MAX_KVM_CPUID_ENTRIES); + /// ``` + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_supported_cpuid(&self, max_entries_count: usize) -> Result { self.get_cpuid(KVM_GET_SUPPORTED_CPUID(), max_entries_count) @@ -203,8 +302,16 @@ impl Kvm { /// X86 specific call to get list of supported MSRS /// - /// See the documentation for KVM_GET_MSR_INDEX_LIST. + /// See the documentation for `KVM_GET_MSR_INDEX_LIST`. /// + /// # Example + /// + /// ``` + /// use kvm_ioctls::{Kvm, MAX_KVM_CPUID_ENTRIES}; + /// + /// let kvm = Kvm::new().unwrap(); + /// let msr_index_list = kvm.get_msr_index_list().unwrap(); + /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_msr_index_list(&self) -> Result> { const MAX_KVM_MSR_ENTRIES: usize = 256; @@ -242,6 +349,16 @@ impl Kvm { /// A call to this function will also initialize the size of the vcpu mmap area using the /// `KVM_GET_VCPU_MMAP_SIZE` ioctl. /// + /// # Example + /// + /// ``` + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// // Check that the VM mmap size is the same reported by `KVM_GET_VCPU_MMAP_SIZE`. + /// assert!(vm.run_size() == kvm.get_vcpu_mmap_size().unwrap()); + /// ``` + /// pub fn create_vm(&self) -> Result { // Safe because we know `self.kvm` is a real KVM fd as this module is the only one that // create Kvm objects. From 88887a08ce36476b18d8adddfbdd447932f1e739 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Mon, 8 Apr 2019 15:47:00 +0300 Subject: [PATCH 029/332] added documentation for VM ioctls - Added documentation for methods that take arguments. - Added examples for public methods - Added some details for ioctls where information was missing. Signed-off-by: Andreea Florescu --- src/ioctls/vm.rs | 285 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 277 insertions(+), 8 deletions(-) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 60e15ad..85e66da 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -21,6 +21,10 @@ use kvm_ioctls::*; use sys_ioctl::*; /// An address either in programmable I/O space or in memory mapped I/O space. +/// +/// The `IoEventAddress` is used for specifying the type when registering an event +/// in [register_ioevent](struct.VmFd.html#method.register_ioevent). +/// pub enum IoEventAddress { /// Representation of an programmable I/O address. Pio(u64), @@ -28,7 +32,13 @@ pub enum IoEventAddress { Mmio(u64), } -/// Used in `VmFd::register_ioevent` to indicate that no datamatch is requested. +/// Helper structure for disabling datamatch. +/// +/// The structure can be used as a parameter to +/// [`register_ioevent`](struct.VmFd.html#method.register_ioevent) +/// to disable filtering of events based on the datamatch flag. For details check the +/// [KVM API documentation](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). +/// pub struct NoDatamatch; impl Into for NoDatamatch { fn into(self) -> u64 { @@ -36,7 +46,7 @@ impl Into for NoDatamatch { } } -/// A wrapper around creating and using a VM. +/// Wrapper over KVM VM ioctls. pub struct VmFd { vm: File, run_size: usize, @@ -47,6 +57,33 @@ impl VmFd { /// /// See the documentation for `KVM_SET_USER_MEMORY_REGION`. /// + /// # Arguments + /// + /// * `user_memory_region` - Guest physical memory slot. For details check the + /// `kvm_userspace_memory_region` structure in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// extern crate kvm_bindings; + /// + /// use kvm_ioctls::{Kvm, VmFd}; + /// use kvm_bindings::kvm_userspace_memory_region; + /// + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let mem_region = kvm_userspace_memory_region { + /// slot: 0, + /// guest_phys_addr: 0x1000 as u64, + /// memory_size: 0x4000 as u64, + /// userspace_addr: 0x0 as u64, + /// flags: 0, + /// }; + /// vm.set_user_memory_region(mem_region).unwrap(); + /// ``` + /// pub fn set_user_memory_region( &self, user_memory_region: kvm_userspace_memory_region, @@ -62,12 +99,22 @@ impl VmFd { /// Sets the address of the three-page region in the VM's address space. /// - /// See the documentation on the `KVM_SET_TSS_ADDR` ioctl. + /// See the documentation for `KVM_SET_TSS_ADDR`. /// /// # Arguments /// /// * `offset` - Physical address of a three-page region in the guest's physical address space. /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::{Kvm, VmFd}; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// vm.set_tss_address(0xfffb_d000).unwrap(); + /// ``` + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_tss_address(&self, offset: usize) -> Result<()> { // Safe because we know that our file is a VM fd and we verify the return result. @@ -83,6 +130,18 @@ impl VmFd { /// /// See the documentation for `KVM_CREATE_IRQCHIP`. /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::{Kvm, VmFd}; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// vm.create_irq_chip().unwrap(); + /// ``` + /// #[cfg(any( target_arch = "x86", target_arch = "x86_64", @@ -101,6 +160,24 @@ impl VmFd { /// Creates a PIT as per the `KVM_CREATE_PIT2` ioctl. /// + /// # Arguments + /// + /// * pit_config - PIT configuration. For details check the `kvm_pit_config` structure in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// extern crate kvm_bindings; + /// # use kvm_ioctls::{Kvm, VmFd}; + /// use kvm_bindings::kvm_pit_config; + /// + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let pit_config = kvm_pit_config::default(); + /// vm.create_pit2(pit_config).unwrap(); + /// ``` + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn create_pit2(&self, pit_config: kvm_pit_config) -> Result<()> { // Safe because we know that our file is a VM fd, we know the kernel will only read the @@ -115,6 +192,8 @@ impl VmFd { /// Registers an event to be signaled whenever a certain address is written to. /// + /// See the documentation for `KVM_IOEVENTFD`. + /// /// # Arguments /// /// * `fd` - FD which will be signaled. When signaling, the usual `vmexit` to userspace @@ -124,6 +203,25 @@ impl VmFd { /// equal to this parameter. The size of `datamatch` is important and it must /// match the expected size of the guest's write. /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// extern crate libc; + /// # use kvm_ioctls::{IoEventAddress, Kvm, NoDatamatch, VmFd}; + /// use libc::{eventfd, EFD_NONBLOCK}; + /// + /// let kvm = Kvm::new().unwrap(); + /// let vm_fd = kvm.create_vm().unwrap(); + /// let evtfd = unsafe { eventfd(0, EFD_NONBLOCK) }; + /// vm_fd + /// .register_ioevent(evtfd, &IoEventAddress::Pio(0xf4), NoDatamatch) + /// .unwrap(); + /// vm_fd + /// .register_ioevent(evtfd, &IoEventAddress::Mmio(0x1000), NoDatamatch) + /// .unwrap(); + /// ``` + /// pub fn register_ioevent>( &self, fd: RawFd, @@ -162,13 +260,95 @@ impl VmFd { /// Gets the bitmap of pages dirtied since the last call of this function. /// /// Leverages the dirty page logging feature in KVM. As a side-effect, this also resets the - /// bitmap inside the kernel. + /// bitmap inside the kernel. For the dirty log to be available, you have to set the flag + /// `KVM_MEM_LOG_DIRTY_PAGES` when creating guest memory regions. + /// + /// Check the documentation for `KVM_GET_DIRTY_LOG`. /// /// # Arguments /// /// * `slot` - Guest memory slot identifier. /// * `memory_size` - Size of the memory region. /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # extern crate kvm_bindings; + /// # use std::io::Write; + /// # use std::ptr::null_mut; + /// # use std::slice; + /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd, VcpuExit}; + /// # use kvm_bindings::{kvm_userspace_memory_region, KVM_MEM_LOG_DIRTY_PAGES}; + /// # let kvm = Kvm::new().unwrap(); + /// # let vm = kvm.create_vm().unwrap(); + /// // This examples is based on https://lwn.net/Articles/658511/. + /// let mem_size = 0x4000; + /// let guest_addr: u64 = 0x1000; + /// let load_addr: *mut u8 = unsafe { + /// libc::mmap( + /// null_mut(), + /// mem_size, + /// libc::PROT_READ | libc::PROT_WRITE, + /// libc::MAP_ANONYMOUS | libc::MAP_SHARED | libc::MAP_NORESERVE, + /// -1, + /// 0, + /// ) as *mut u8 + /// }; + /// + /// // Initialize a guest memory region using the flag `KVM_MEM_LOG_DIRTY_PAGES`. + /// let mem_region = kvm_userspace_memory_region { + /// slot: 0, + /// guest_phys_addr: guest_addr, + /// memory_size: mem_size as u64, + /// userspace_addr: load_addr as u64, + /// flags: KVM_MEM_LOG_DIRTY_PAGES, + /// }; + /// vm.set_user_memory_region(mem_region).unwrap(); + /// + /// // Dummy x86 code that just calls halt. + /// let x86_code = [ + /// 0xf4, /* hlt */ + /// ]; + /// + /// // Write the code in the guest memory. This will generate a dirty page. + /// unsafe { + /// let mut slice = slice::from_raw_parts_mut(load_addr, mem_size); + /// slice.write(&x86_code).unwrap(); + /// } + /// + /// let vcpu_fd = vm.create_vcpu(0).unwrap(); + /// + /// let mut vcpu_sregs = vcpu_fd.get_sregs().unwrap(); + /// vcpu_sregs.cs.base = 0; + /// vcpu_sregs.cs.selector = 0; + /// vcpu_fd.set_sregs(&vcpu_sregs).unwrap(); + /// + /// let mut vcpu_regs = vcpu_fd.get_regs().unwrap(); + /// // Set the Instruction Pointer to the guest address where we loaded the code. + /// vcpu_regs.rip = guest_addr; + /// vcpu_regs.rax = 2; + /// vcpu_regs.rbx = 3; + /// vcpu_regs.rflags = 2; + /// vcpu_fd.set_regs(&vcpu_regs).unwrap(); + /// + /// loop { + /// match vcpu_fd.run().expect("run failed") { + /// VcpuExit::Hlt => { + /// // The code snippet dirties 1 page when loading the code in memory. + /// let dirty_pages_bitmap = vm.get_dirty_log(0, mem_size).unwrap(); + /// let dirty_pages = dirty_pages_bitmap + /// .into_iter() + /// .map(|page| page.count_ones()) + /// .fold(0, |dirty_page_count, i| dirty_page_count + i); + /// assert_eq!(dirty_pages, 1); + /// break; + /// } + /// exit_reason => panic!("unexpected exit reason: {:?}", exit_reason), + /// } + /// } + /// ``` + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_dirty_log(&self, slot: u32, memory_size: usize) -> Result> { // Compute the length of the bitmap needed for all dirty pages in one memory slot. @@ -207,6 +387,20 @@ impl VmFd { /// * `fd` - Event to be signaled. /// * `gsi` - IRQ to be triggered. /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # extern crate libc; + /// # use kvm_ioctls::{Kvm, VmFd}; + /// # use libc::{eventfd, EFD_NONBLOCK}; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let evtfd = unsafe { eventfd(0, EFD_NONBLOCK) }; + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// vm.register_irqfd(evtfd, 0).unwrap(); + /// ``` + /// #[cfg(any( target_arch = "x86", target_arch = "x86_64", @@ -229,14 +423,30 @@ impl VmFd { } } - /// Creates a new KVM vCPU fd. + /// Creates a new KVM vCPU file descriptor and maps the memory corresponding + /// its `kvm_run` structure. + /// + /// See the documentation for `KVM_CREATE_VCPU`. /// /// # Arguments /// - /// * `id` - The CPU number between [0, max vs). + /// * `id` - The vCPU ID. /// /// # Errors - /// Returns an error when the VM fd is invalid or the vCPU memory cannot be mapped correctly. + /// + /// Returns an io::Error when the VM fd is invalid or the vCPU memory cannot + /// be mapped correctly. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// // Create one vCPU with the ID=0. + /// let vcpu = vm.create_vcpu(0); + /// ``` /// pub fn create_vcpu(&self, id: u8) -> Result { // Safe because we know that vm is a VM fd and we verify the return result. @@ -256,6 +466,43 @@ impl VmFd { } /// Creates an emulated device in the kernel. + /// + /// See the documentation for `KVM_CREATE_DEVICE`. + /// + /// # Arguments + /// + /// * `device`: device configuration. For details check the `kvm_create_device` structure in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # extern crate kvm_bindings; + /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// use kvm_bindings::{ + /// kvm_device_type_KVM_DEV_TYPE_VFIO, + /// kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, + /// KVM_CREATE_DEVICE_TEST, + /// }; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// + /// // Creating a device with the KVM_CREATE_DEVICE_TEST flag to check + /// // whether the device type is supported. This will not create the device. + /// // To create the device the flag needs to be removed. + /// let mut device = kvm_bindings::kvm_create_device { + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// type_: kvm_device_type_KVM_DEV_TYPE_VFIO, + /// #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + /// type_: kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, + /// fd: 0, + /// flags: KVM_CREATE_DEVICE_TEST, + /// }; + /// let device_fd = vm + /// .create_device(&mut device).unwrap(); + /// ``` + /// pub fn create_device(&self, device: &mut kvm_create_device) -> Result { let ret = unsafe { ioctl_with_ref(self, KVM_CREATE_DEVICE(), device) }; if ret == 0 { @@ -265,7 +512,29 @@ impl VmFd { } } - /// This queries the kernel for the preferred target CPU type. + /// Returns the preferred CPU target type which can be emulated by KVM on underlying host. + /// + /// The preferred CPU target is returned in the `kvi` parameter. + /// See documentation for `KVM_ARM_PREFERRED_TARGET`. + /// + /// # Arguments + /// * `kvi` - CPU target configuration (out). For details check the `kvm_vcpu_init` + /// structure in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # extern crate kvm_bindings; + /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// use kvm_bindings::kvm_vcpu_init; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let mut kvi = kvm_vcpu_init::default(); + /// vm.get_preferred_target(&mut kvi).unwrap(); + /// ``` + /// #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] pub fn get_preferred_target(&self, kvi: &mut kvm_vcpu_init) -> Result<()> { // The ioctl is safe because we allocated the struct and we know the From 4a091e51ab44cd2467da4bc068831a0db560d15d Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Tue, 9 Apr 2019 14:39:13 +0300 Subject: [PATCH 030/332] enhanced the documentation of CpuId - Added more usage examples - Rephrased existing documentation Signed-off-by: Andreea Florescu --- src/ioctls/mod.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/ioctls/mod.rs b/src/ioctls/mod.rs index f1e320a..99e7319 100644 --- a/src/ioctls/mod.rs +++ b/src/ioctls/mod.rs @@ -61,11 +61,12 @@ fn vec_with_array_field(count: usize) -> Vec { vec_with_size_in_bytes(vec_size_bytes) } -/// Wrapper for `kvm_cpuid2` which has a zero length array at the end. -/// Hides the zero length array behind a bounds check. +/// Wrapper over the `kvm_cpuid2` structure. +/// +/// The structure has a zero length array at the end, hidden behind bounds check. #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub struct CpuId { - /// Wrapper over `kvm_cpuid2` from which we only use the first element. + // Wrapper over `kvm_cpuid2` from which we only use the first element. kvm_cpuid: Vec, // Number of `kvm_cpuid_entry2` structs at the end of kvm_cpuid2. allocated_len: usize, @@ -110,7 +111,7 @@ impl PartialEq for CpuId { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] impl CpuId { - /// Creates a new `CpuId` structure that can contain at most `array_len` KVM CPUID entries. + /// Creates a new `CpuId` structure that contains at most `array_len` KVM CPUID entries. /// /// # Arguments /// @@ -119,7 +120,6 @@ impl CpuId { /// # Example /// /// ``` - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] /// use kvm_ioctls::CpuId; /// let cpu_id = CpuId::new(32); /// ``` @@ -133,7 +133,15 @@ impl CpuId { } } - /// Get the mutable entries slice so they can be modified before passing to the VCPU. + /// Returns the mutable entries slice so they can be modified before passing to the VCPU. + /// + /// # Example + /// ```rust + /// use kvm_ioctls::{CpuId, Kvm, MAX_KVM_CPUID_ENTRIES}; + /// let kvm = Kvm::new().unwrap(); + /// let mut cpuid = kvm.get_supported_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); + /// let cpuid_entries = cpuid.mut_entries_slice(); + /// ``` /// pub fn mut_entries_slice(&mut self) -> &mut [kvm_cpuid_entry2] { // Mapping the unsized array to a slice is unsafe because the length isn't known. Using @@ -158,7 +166,7 @@ impl CpuId { } } -/// A safe wrapper over the `kvm_run` struct. +/// Safe wrapper over the `kvm_run` struct. /// /// The wrapper is needed for sending the pointer to `kvm_run` between /// threads as raw pointers do not implement `Send` and `Sync`. From d5cc934eec3ddf278396eb82b99608641171f119 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Tue, 9 Apr 2019 16:03:20 +0300 Subject: [PATCH 031/332] enhanced documentation of vCPU ioctls - Added code examples - Added references to the KVM API and KVM structures - Added details for some ioctls Signed-off-by: Andreea Florescu --- src/ioctls/vcpu.rs | 361 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 336 insertions(+), 25 deletions(-) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 4fa19e1..925a5f0 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -17,8 +17,11 @@ use ioctls::{KvmRunWrapper, Result}; use kvm_ioctls::*; use sys_ioctl::*; -/// Reasons for vCPU exits. The exit reasons are mapped to the `KVM_EXIT_*` defines -/// from `include/uapi/linux/kvm.h`. +/// Reasons for vCPU exits. +/// +/// The exit reasons are mapped to the `KVM_EXIT_*` defines in the +/// [Linux KVM header](https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/kvm.h). +/// #[derive(Debug)] pub enum VcpuExit<'a> { /// An out port instruction was run on the given port with the given data. @@ -89,14 +92,30 @@ pub enum VcpuExit<'a> { Hyperv, } -/// A wrapper around creating and using a kvm related vCPU file descriptor. +/// Wrapper over KVM vCPU ioctls. pub struct VcpuFd { vcpu: File, kvm_run_ptr: KvmRunWrapper, } impl VcpuFd { - /// Gets the vCPU registers using the `KVM_GET_REGS` ioctl. + /// Returns the vCPU general purpose registers. + /// + /// The registers are returned in a `kvm_regs` structure as defined in the + /// [KVM API documentation](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// See documentation for `KVM_GET_REGS`. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] + /// let regs = vcpu.get_regs().unwrap(); + /// ``` /// #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] pub fn get_regs(&self) -> Result { @@ -110,11 +129,30 @@ impl VcpuFd { Ok(regs) } - /// Sets the vCPU registers using `KVM_SET_REGS` ioctl. + /// Sets the vCPU general purpose registers using the `KVM_SET_REGS` ioctl. /// /// # Arguments /// - /// * `regs` - Registers being set. + /// * `regs` - general purpose registers. For details check the `kvm_regs` structure in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// + /// #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] { + /// // Get the current vCPU registers. + /// let mut regs = vcpu.get_regs().unwrap(); + /// // Set a new value for the Instruction Pointer. + /// regs.rip = 0x100; + /// vcpu.set_regs(®s).unwrap(); + /// } + /// ``` /// #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] pub fn set_regs(&self, regs: &kvm_regs) -> Result<()> { @@ -127,7 +165,23 @@ impl VcpuFd { Ok(()) } - /// Gets the vCPU special registers using `KVM_GET_SREGS` ioctl. + /// Returns the vCPU special registers. + /// + /// The registers are returned in a `kvm_sregs` structure as defined in the + /// [KVM API documentation](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// See documentation for `KVM_GET_SREGS`. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] + /// let sregs = vcpu.get_sregs().unwrap(); + /// ``` /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_sregs(&self) -> Result { @@ -142,11 +196,29 @@ impl VcpuFd { Ok(regs) } - /// Sets the vCPU special registers using `KVM_SET_SREGS` ioctl. + /// Sets the vCPU special registers using the `KVM_SET_SREGS` ioctl. /// /// # Arguments /// - /// * `sregs` - Special registers to be set. + /// * `sregs` - Special registers. For details check the `kvm_sregs` structure in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] { + /// let mut sregs = vcpu.get_sregs().unwrap(); + /// // Update the code segment (cs). + /// sregs.cs.base = 0; + /// sregs.cs.selector = 0; + /// vcpu.set_sregs(&sregs).unwrap(); + /// } + /// ``` /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_sregs(&self, sregs: &kvm_sregs) -> Result<()> { @@ -159,10 +231,24 @@ impl VcpuFd { Ok(()) } - /// X86 specific call that gets the FPU-related structure. + /// Returns the floating point state (FPU) from the vCPU. /// + /// The state is returned in a `kvm_fpu` structure as defined in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). /// See the documentation for `KVM_GET_FPU`. /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// let fpu = vcpu.get_fpu().unwrap(); + /// ``` + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_fpu(&self) -> Result { let mut fpu = kvm_fpu::default(); @@ -177,13 +263,32 @@ impl VcpuFd { Ok(fpu) } - /// X86 specific call to setup the FPU. - /// - /// See the documentation for `KVM_SET_FPU`. + /// Set the floating point state (FPU) of a vCPU using the `KVM_SET_FPU` ioct. /// /// # Arguments /// - /// * `fpu` - FPU configurations struct. + /// * `fpu` - FPU configuration. For details check the `kvm_fpu` structure in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # extern crate kvm_bindings; + /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// # use kvm_bindings::kvm_fpu; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { + /// let KVM_FPU_CWD: u16 = 0x37f; + /// let fpu = kvm_fpu { + /// fcw: KVM_FPU_CWD, + /// ..Default::default() + /// }; + /// vcpu.set_fpu(&fpu).unwrap(); + /// } + /// ``` /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_fpu(&self, fpu: &kvm_fpu) -> Result<()> { @@ -205,10 +310,37 @@ impl VcpuFd { /// /// * `cpuid` - CPUID registers. /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # extern crate kvm_bindings; + /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd, MAX_KVM_CPUID_ENTRIES}; + /// # use kvm_bindings::kvm_fpu; + /// let kvm = Kvm::new().unwrap(); + /// let mut kvm_cpuid = kvm.get_supported_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// + /// // Update the CPUID entries to disable the EPB feature. + /// const ECX_EPB_SHIFT: u32 = 3; + /// { + /// let entries = kvm_cpuid.mut_entries_slice(); + /// for entry in entries.iter_mut() { + /// match entry.function { + /// 6 => entry.ecx &= !(1 << ECX_EPB_SHIFT), + /// _ => (), + /// } + /// } + /// } + /// + /// vcpu.set_cpuid2(&kvm_cpuid); + /// ``` + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_cpuid2(&self, cpuid: &CpuId) -> Result<()> { let ret = unsafe { - // Here we trust the kernel not to read past the end of the kvm_msrs struct. + // Here we trust the kernel not to read past the end of the kvm_cpuid2 struct. ioctl_with_ptr(self, KVM_SET_CPUID2(), cpuid.as_ptr()) }; if ret < 0 { @@ -217,11 +349,25 @@ impl VcpuFd { Ok(()) } - /// X86 specific call to get the state of the LAPIC (Local Advanced Programmable Interrupt - /// Controller). + /// Returns the state of the LAPIC (Local Advanced Programmable Interrupt Controller). /// + /// The state is returned in a `kvm_lapic_state` structure as defined in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). /// See the documentation for `KVM_GET_LAPIC`. /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// // For `get_lapic` to work, you first need to create a IRQ chip before creating the vCPU. + /// vm.create_irq_chip().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// let lapic = vcpu.get_lapic().unwrap(); + /// ``` + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_lapic(&self) -> Result { let mut klapic = kvm_lapic_state::default(); @@ -237,14 +383,38 @@ impl VcpuFd { Ok(klapic) } - /// X86 specific call to set the state of the LAPIC (Local Advanced Programmable Interrupt - /// Controller). + /// Sets the state of the LAPIC (Local Advanced Programmable Interrupt Controller). /// /// See the documentation for `KVM_SET_LAPIC`. /// /// # Arguments /// - /// * `klapic` - LAPIC state registers. + /// * `klapic` - LAPIC state. For details check the `kvm_lapic_state` structure in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// use std::io::Write; + /// + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// // For `get_lapic` to work, you first need to create a IRQ chip before creating the vCPU. + /// vm.create_irq_chip().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// let mut lapic = vcpu.get_lapic().unwrap(); + /// + /// // Write to APIC_ICR offset the value 2. + /// let apic_icr_offset = 0x300; + /// let write_value: &[u8] = &[2, 0, 0, 0]; + /// let mut apic_icr_slice = + /// unsafe { &mut *(&mut lapic.regs[apic_icr_offset..] as *mut [i8] as *mut [u8]) }; + /// apic_icr_slice.write(write_value).unwrap(); + /// + /// // Update the value of LAPIC. + ///vcpu.set_lapic(&lapic).unwrap(); + /// ``` /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_lapic(&self, klapic: &kvm_lapic_state) -> Result<()> { @@ -258,14 +428,30 @@ impl VcpuFd { Ok(()) } - /// X86 specific call to read model-specific registers for this vCPU. + /// Returns the model-specific registers (MSR) for this vCPU. /// /// It emulates `KVM_GET_MSRS` ioctl's behavior by returning the number of MSRs /// successfully read upon success or the last error number in case of failure. + /// The MSRs are returned in the `msr` method argument. /// /// # Arguments /// - /// * `msrs` - MSRs to be read. + /// * `msrs` - MSRs (input/output). For details check the `kvm_msrs` structure in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # extern crate kvm_bindings; + /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// # use kvm_bindings::kvm_msrs; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// let mut msrs = kvm_msrs::default(); + /// vcpu.get_msrs(&mut msrs).unwrap(); + /// ``` /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_msrs(&self, msrs: &mut kvm_msrs) -> Result<(i32)> { @@ -279,13 +465,45 @@ impl VcpuFd { Ok(ret) } - /// X86 specific call to setup the MSRS. + /// Setup the model-specific registers (MSR) for this vCPU. /// /// See the documentation for `KVM_SET_MSRS`. /// /// # Arguments /// - /// * `kvm_msrs` - MSRs to be written. + /// * `msrs` - MSRs. For details check the `kvm_msrs` structure in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # extern crate kvm_bindings; + /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// # use kvm_bindings::{kvm_msrs, kvm_msr_entry}; + /// # use std::mem; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// let mut msrs = kvm_msrs::default(); + /// vcpu.get_msrs(&mut msrs).unwrap(); + /// + /// let msrs_entries = { + /// kvm_msr_entry { + /// index: 0x0000_0174, + /// ..Default::default() + /// } + /// }; + /// + /// // Create a vector large enough to hold the MSR entry defined above in + /// // a `kvm_msrs`structure. + /// let msrs_vec: Vec = + /// Vec::with_capacity(mem::size_of::() + mem::size_of::()); + /// let mut msrs: &mut kvm_msrs = unsafe { + /// &mut *(msrs_vec.as_ptr() as *mut kvm_msrs) + /// }; + /// msrs.nmsrs = 1; + /// vcpu.set_msrs(msrs).unwrap(); + /// ``` /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_msrs(&self, msrs: &kvm_msrs) -> Result<()> { @@ -303,11 +521,29 @@ impl VcpuFd { /// Sets the type of CPU to be exposed to the guest and optional features. /// /// This initializes an ARM vCPU to the specified type with the specified features - /// and resets the values of all of its registers to defaults. + /// and resets the values of all of its registers to defaults. See the documentation for + /// `KVM_ARM_VCPU_INIT`. /// /// # Arguments /// /// * `kvi` - information about preferred CPU target type and recommended features for it. + /// For details check the `kvm_vcpu_init` structure in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Example + /// ```rust + /// # extern crate kvm_ioctls; + /// # extern crate kvm_bindings; + /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// use kvm_bindings::kvm_vcpu_init; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// + /// let mut kvi = kvm_vcpu_init::default(); + /// vm.get_preferred_target(&mut kvi).unwrap(); + /// vcpu.vcpu_init(&kvi).unwrap(); + /// ``` /// #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] pub fn vcpu_init(&self, kvi: &kvm_vcpu_init) -> Result<()> { @@ -348,6 +584,81 @@ impl VcpuFd { /// Triggers the running of the current virtual CPU returning an exit reason. /// + /// See documentation for `KVM_RUN`. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # extern crate kvm_bindings; + /// # use std::io::Write; + /// # use std::ptr::null_mut; + /// # use std::slice; + /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd, VcpuExit}; + /// # use kvm_bindings::{kvm_userspace_memory_region, KVM_MEM_LOG_DIRTY_PAGES}; + /// # let kvm = Kvm::new().unwrap(); + /// # let vm = kvm.create_vm().unwrap(); + /// // This is a dummy example for running on x86 based on https://lwn.net/Articles/658511/. + /// #[cfg(target_arch = "x86_64")] { + /// let mem_size = 0x4000; + /// let guest_addr: u64 = 0x1000; + /// let load_addr: *mut u8 = unsafe { + /// libc::mmap( + /// null_mut(), + /// mem_size, + /// libc::PROT_READ | libc::PROT_WRITE, + /// libc::MAP_ANONYMOUS | libc::MAP_SHARED | libc::MAP_NORESERVE, + /// -1, + /// 0, + /// ) as *mut u8 + /// }; + /// + /// let mem_region = kvm_userspace_memory_region { + /// slot: 0, + /// guest_phys_addr: guest_addr, + /// memory_size: mem_size as u64, + /// userspace_addr: load_addr as u64, + /// flags: 0, + /// }; + /// vm.set_user_memory_region(mem_region).unwrap(); + /// + /// // Dummy x86 code that just calls halt. + /// let x86_code = [ + /// 0xf4, /* hlt */ + /// ]; + /// + /// // Write the code in the guest memory. This will generate a dirty page. + /// unsafe { + /// let mut slice = slice::from_raw_parts_mut(load_addr, mem_size); + /// slice.write(&x86_code).unwrap(); + /// } + /// + /// let vcpu_fd = vm.create_vcpu(0).unwrap(); + /// + /// let mut vcpu_sregs = vcpu_fd.get_sregs().unwrap(); + /// vcpu_sregs.cs.base = 0; + /// vcpu_sregs.cs.selector = 0; + /// vcpu_fd.set_sregs(&vcpu_sregs).unwrap(); + /// + /// let mut vcpu_regs = vcpu_fd.get_regs().unwrap(); + /// // Set the Instruction Pointer to the guest address where we loaded the code. + /// vcpu_regs.rip = guest_addr; + /// vcpu_regs.rax = 2; + /// vcpu_regs.rbx = 3; + /// vcpu_regs.rflags = 2; + /// vcpu_fd.set_regs(&vcpu_regs).unwrap(); + /// + /// loop { + /// match vcpu_fd.run().expect("run failed") { + /// VcpuExit::Hlt => { + /// break; + /// } + /// exit_reason => panic!("unexpected exit reason: {:?}", exit_reason), + /// } + /// } + /// } + /// ``` + /// pub fn run(&self) -> Result { // Safe because we know that our file is a vCPU fd and we verify the return result. let ret = unsafe { ioctl(self, KVM_RUN()) }; From ff67d124f1e9a3d79a57921294813e18a86b6463 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Tue, 9 Apr 2019 18:28:37 +0300 Subject: [PATCH 032/332] removed TODO file All entries in the TODO file are now either issues or already in PRs. Signed-off-by: Andreea Florescu --- TODO | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 TODO diff --git a/TODO b/TODO deleted file mode 100644 index c53a67b..0000000 --- a/TODO +++ /dev/null @@ -1,11 +0,0 @@ -- High Level docs with usage -- add comment on each method with the KVM ioctl that it is used -- write cleanup functions for tests -- create test profiles (when running as part of the development process, - update the coverage threshold, otherwise fail the test if the current - coverage > previous coverage) -- Add copyright files -- move sys_util ioctls to a different crate. -- ARM support -- CrosVM ioctls -- missing unit tests (almost everything is testable) From 7f8b3d7f26cc0fe82fe7966a585ddddccc03fe39 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Tue, 9 Apr 2019 18:29:52 +0300 Subject: [PATCH 033/332] remove unused tests Pytest tests are replaced by the buildkite pipeline. No need to keep them around. Fixes https://github.com/rust-vmm/kvm-ioctls/issues/18 Signed-off-by: Andreea Florescu --- tests/test_build.py | 23 ----------------------- tests/test_correctness.py | 19 ------------------- 2 files changed, 42 deletions(-) delete mode 100644 tests/test_build.py delete mode 100644 tests/test_correctness.py diff --git a/tests/test_build.py b/tests/test_build.py deleted file mode 100644 index 8acb0ef..0000000 --- a/tests/test_build.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: Apache-2.0 - -import subprocess - - -def test_build(): - """Test release build using the default gnu target.""" - subprocess.run(['cargo', 'build', '--release'], check=True) - - -def test_build_musl(): - """Test release build using the musl target.""" - subprocess.run( - [ - 'cargo', - 'build', - '--release', - '--target', - 'x86_64-unknown-linux-musl' - ], - check=True - ) diff --git a/tests/test_correctness.py b/tests/test_correctness.py deleted file mode 100644 index 9bbbdfb..0000000 --- a/tests/test_correctness.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: Apache-2.0 -"""Test the correctness of the code using linters and unit tests.""" - -import subprocess - -def test_style(): - """Test rust style using `cargo fmt`.""" - subprocess.run(['cargo', 'fmt', '--all', '--', '--check'], check=True) - - -def test_clippy(): - """Run clippy.""" - subprocess.run(['cargo', 'clippy'], check=True) - - -def test_unittests(): - """Run unit tests.""" - subprocess.run(['cargo', 'test', '--all'], check=True) From 0c71bcae5a01e94936cbe83518faff9d15778096 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Tue, 9 Apr 2019 18:37:41 +0300 Subject: [PATCH 034/332] removed pytest tests from README.md We no longer run the tests using pytest. Removed that section from README.md. Also, updated running the coverage test using the dependency container. Signed-off-by: Andreea Florescu --- README.md | 35 ++++++++--------------------------- 1 file changed, 8 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 167a785..b2cf200 100644 --- a/README.md +++ b/README.md @@ -4,38 +4,19 @@ TODO ## Running the tests -### Dependencies -**[NOTE]** This is a temporary state of affairs; Most likely we will manage -the dependencies using a container. -- python >= 3.5 -- pytest -- kcov -- cargo kcov -- cargo fmt -- cargo clippy -- target x86_64-unknown-linux-musl - -``` -$ pytest tests/ -vv -============================= test session starts ============================== -platform linux -- Python 3.6.8, pytest-3.8.0, py-1.6.0, pluggy-0.7.1 -- /usr/bin/python3.6 -cachedir: .pytest_cache -rootdir: /home/local/ANT/fandree/sources/work/rust-vmm/kvm-ioctls, inifile: -collected 6 items -tests/test_build.py::test_build PASSED [ 16%] -tests/test_build.py::test_build_musl PASSED [ 33%] -tests/test_correctness.py::test_style PASSED [ 50%] -tests/test_correctness.py::test_clippy PASSED [ 66%] -tests/test_correctness.py::test_unittests PASSED [ 83%] -tests/test_coverage.py::test_coverage PASSED [100%] - -=========================== 6 passed in 7.08 seconds =========================== -``` +TODO ### Adaptive Coverage The line coverage is saved in [tests/coverage](tests/coverage). To update the coverage before submitting a PR, run the coverage test: + ```bash +docker run --device=/dev/kvm \ + -it \ + --security-opt seccomp=unconfined \ + --volume $(pwd)/kvm-ioctls:/kvm-ioctls \ + fandree/rust-vmm-dev +cd kvm-ioctls/ pytest tests/test_coverage.py ``` From be941d0123810b3ca40f5d6c0ebce43e6ebc15a2 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Wed, 10 Apr 2019 16:40:26 +0200 Subject: [PATCH 035/332] LICENSE: Add the MIT License And rename LICENSE to LICENSE-APACHE Fixes: #20 Signed-off-by: Samuel Ortiz --- LICENSE => LICENSE-APACHE | 0 LICENSE-MIT | 24 ++++++++++++++++++++++++ 2 files changed, 24 insertions(+) rename LICENSE => LICENSE-APACHE (100%) create mode 100644 LICENSE-MIT diff --git a/LICENSE b/LICENSE-APACHE similarity index 100% rename from LICENSE rename to LICENSE-APACHE diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..5c6a646 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,24 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + From 2aa0907976ff89cfb16102f6c5a37705dd838759 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Wed, 10 Apr 2019 16:41:43 +0200 Subject: [PATCH 036/332] LICENSE: Add crosvm THIRD-PARTY file Fixes: #22 Signed-off-by: Samuel Ortiz --- THIRD-PARTY | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 THIRD-PARTY diff --git a/THIRD-PARTY b/THIRD-PARTY new file mode 100644 index 0000000..8bafca3 --- /dev/null +++ b/THIRD-PARTY @@ -0,0 +1,27 @@ +// Copyright 2017 The Chromium OS Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From ddad6b4ef0f7f8488518351de2f20a28bdeccfbc Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Wed, 10 Apr 2019 16:52:08 +0200 Subject: [PATCH 037/332] LICENSE: Switch to Apache 2.0 or MIT dual licensing Fixes: #20 Signed-off-by: Samuel Ortiz --- Cargo.toml | 2 +- src/cap.rs | 2 +- src/ioctls/device.rs | 2 +- src/ioctls/mod.rs | 2 +- src/ioctls/system.rs | 2 +- src/ioctls/vcpu.rs | 2 +- src/ioctls/vm.rs | 2 +- src/kvm_ioctls.rs | 2 +- src/lib.rs | 2 +- src/sys_ioctl.rs | 2 +- tests/test_coverage.py | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6bf4439..4e0abf6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ description = "Safe wrappers over KVM ioctls" repository = "https://github.com/rust-vmm/kvm-ioctls" readme = "README.md" keywords = ["kvm"] -license = "Apache-2.0" +license = "Apache-2.0 OR MIT" [dependencies] libc = ">=0.2.39" diff --git a/src/cap.rs b/src/cap.rs index 4a3a990..36f03e6 100644 --- a/src/cap.rs +++ b/src/cap.rs @@ -1,5 +1,5 @@ // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: Apache-2.0 OR MIT // // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be diff --git a/src/ioctls/device.rs b/src/ioctls/device.rs index 23630e2..a523a79 100644 --- a/src/ioctls/device.rs +++ b/src/ioctls/device.rs @@ -1,5 +1,5 @@ // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: Apache-2.0 OR MIT use std::fs::File; use std::io; diff --git a/src/ioctls/mod.rs b/src/ioctls/mod.rs index 99e7319..9d24e57 100644 --- a/src/ioctls/mod.rs +++ b/src/ioctls/mod.rs @@ -1,5 +1,5 @@ // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: Apache-2.0 OR MIT // // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index 47d8773..2f668fb 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -1,5 +1,5 @@ // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: Apache-2.0 OR MIT // // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 925a5f0..129d2a9 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1,5 +1,5 @@ // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: Apache-2.0 OR MIT // // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 85e66da..6af73aa 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -1,5 +1,5 @@ // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: Apache-2.0 OR MIT // // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index 6305ae1..69e8117 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -1,5 +1,5 @@ // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: Apache-2.0 OR MIT // // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be diff --git a/src/lib.rs b/src/lib.rs index d353da1..9a3882b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: Apache-2.0 OR MIT // // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be diff --git a/src/sys_ioctl.rs b/src/sys_ioctl.rs index ec3b63e..3dd8f13 100644 --- a/src/sys_ioctl.rs +++ b/src/sys_ioctl.rs @@ -1,6 +1,6 @@ #![allow(dead_code)] // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: Apache-2.0 OR MIT // // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be diff --git a/tests/test_coverage.py b/tests/test_coverage.py index 3a0d423..55c1751 100644 --- a/tests/test_coverage.py +++ b/tests/test_coverage.py @@ -1,5 +1,5 @@ # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: Apache-2.0 OR MIT """Test the coverage and update the threshold when coverage is increased.""" import os, re, shutil, subprocess From 963e120d930a5957ad1c7793e2448ec69d08f999 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Thu, 11 Apr 2019 17:35:07 +0300 Subject: [PATCH 038/332] cpuid: create CpuId from entries Added method for creating a new CpuId from existing kvm_cpuid2_entries. This is useful for adding new entries to the list provided by `get_supported_cpuid`. Signed-off-by: Andreea Florescu --- src/ioctls/mod.rs | 81 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/src/ioctls/mod.rs b/src/ioctls/mod.rs index 9d24e57..d5a1cf3 100644 --- a/src/ioctls/mod.rs +++ b/src/ioctls/mod.rs @@ -133,6 +133,54 @@ impl CpuId { } } + /// Creates a new `CpuId` structure based on a supplied vector of `kvm_cpuid_entry2`. + /// + /// # Arguments + /// + /// * `entries` - The vector of `kvm_cpuid_entry2` entries. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// extern crate kvm_bindings; + /// + /// use kvm_bindings::kvm_cpuid_entry2; + /// use kvm_ioctls::CpuId; + /// // Create a Cpuid to hold one entry. + /// let mut cpuid = CpuId::new(1); + /// let mut entries = cpuid.mut_entries_slice().to_vec(); + /// let new_entry = kvm_cpuid_entry2 { + /// function: 0x4, + /// index: 0, + /// flags: 1, + /// eax: 0b1100000, + /// ebx: 0, + /// ecx: 0, + /// edx: 0, + /// padding: [0, 0, 0], + /// }; + /// entries.insert(0, new_entry); + /// cpuid = CpuId::from_entries(&entries); + /// ``` + /// + pub fn from_entries(entries: &[kvm_cpuid_entry2]) -> CpuId { + let mut kvm_cpuid = vec_with_array_field::(entries.len()); + kvm_cpuid[0].nent = entries.len() as u32; + + unsafe { + kvm_cpuid[0] + .entries + .as_mut_slice(entries.len()) + .copy_from_slice(entries); + } + + CpuId { + kvm_cpuid, + allocated_len: entries.len(), + } + } + /// Returns the mutable entries slice so they can be modified before passing to the VCPU. /// /// # Example @@ -221,3 +269,36 @@ impl KvmRunWrapper { } } } + +#[cfg(test)] +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +mod tests { + use super::*; + + #[test] + fn test_cpuid_from_entries() { + let num_entries = 4; + let mut cpuid = CpuId::new(num_entries); + + // add entry + let mut entries = cpuid.mut_entries_slice().to_vec(); + let new_entry = kvm_cpuid_entry2 { + function: 0x4, + index: 0, + flags: 1, + eax: 0b1100000, + ebx: 0, + ecx: 0, + edx: 0, + padding: [0, 0, 0], + }; + entries.insert(0, new_entry); + cpuid = CpuId::from_entries(&entries); + + // check that the cpuid contains the new entry + assert_eq!(cpuid.allocated_len, num_entries + 1); + assert_eq!(cpuid.kvm_cpuid[0].nent, (num_entries + 1) as u32); + assert_eq!(cpuid.mut_entries_slice().len(), num_entries + 1); + assert_eq!(cpuid.mut_entries_slice()[0], new_entry); + } +} From 82f31a65399ef17e94f89f21745ccb165c06f60f Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Fri, 29 Mar 2019 14:24:46 +0100 Subject: [PATCH 039/332] add development & CI test profiles Added two test profiles: one for development (devel) and one for running the integration tests as part of the CI. Currently the only difference between the two is the result of the coverage test. Specifically, in the `ci` profile the test fails if current_coverage > previous_coverage, while in the `devel` profile the coverage file is updated with the new value. Signed-off-by: Andreea Florescu --- README.md | 39 ++++++++++++++++++++++++++++++++++++++- tests/conftest.py | 28 ++++++++++++++++++++++++++++ tests/test_coverage.py | 23 +++++++++++++++++------ 3 files changed, 83 insertions(+), 7 deletions(-) create mode 100644 tests/conftest.py diff --git a/README.md b/README.md index b2cf200..ae2e22b 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,29 @@ TODO TODO +### Test Profiles + +The integration tests support two test profiles: +- **devel**: this is the recommended profile for running the integration tests + on a local development machine. +- **ci** (default option): this is the profile used when running the + integration tests as part of the the Continuous Integration (CI). + +The test profiles are applicable to tests that run using pytest. Currently only +the [coverage test](tests/test_coverage.py) follows this model as all the other +integration tests are run using the +[Buildkite pipeline](https://buildkite.com/rust-vmm/kvm-ioctls-ci). + +The difference between is declaring tests as passed or failed: +- with the **devel** profile the coverage test passes if the current coverage + is equal or higher than the upstream coverage value. In case the current + coverage is higher, the coverage file is updated to the new coverage value. +- with the **ci** profile the coverage test passes only if the current coverage + is equal to the upstream coverage value. + +Further details about the coverage test can be found in the +[Adaptive Coverage](#adaptive-coverage) section. + ### Adaptive Coverage The line coverage is saved in [tests/coverage](tests/coverage). To update the @@ -18,5 +41,19 @@ docker run --device=/dev/kvm \ --volume $(pwd)/kvm-ioctls:/kvm-ioctls \ fandree/rust-vmm-dev cd kvm-ioctls/ -pytest tests/test_coverage.py +pytest --profile=devel tests/test_coverage.py ``` + +If the PR coverage is higher than the upstream coverage, the coverage file +needs to be manually added to the commit before submitting the PR: + +```bash +git add tests/coverage +``` + +Failing to do so will generate a fail on the CI pipeline when publishing the +PR. + +**NOTE:** The coverage file is only updated in the `devel` test profile. In +the `ci` profile the coverage test will fail if the current coverage is higher +than the coverage reported in [tests/coverage](tests/coverage). diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..58aef2d --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,28 @@ +import pytest + + +PROFILE_CI="ci" +PROFILE_DEVEL="devel" + + +def pytest_addoption(parser): + parser.addoption( + "--profile", + default=PROFILE_CI, + choices=[PROFILE_CI, PROFILE_DEVEL], + help="Profile for running the test: {} or {}".format( + PROFILE_CI, + PROFILE_DEVEL + ) + ) + + +@pytest.fixture +def profile(request): + return request.config.getoption("--profile") + + +# This is used for defining global variables in pytest. +def pytest_configure(): + pytest.profile_ci = PROFILE_CI + pytest.profile_devel = PROFILE_DEVEL diff --git a/tests/test_coverage.py b/tests/test_coverage.py index 55c1751..672a734 100644 --- a/tests/test_coverage.py +++ b/tests/test_coverage.py @@ -3,6 +3,7 @@ """Test the coverage and update the threshold when coverage is increased.""" import os, re, shutil, subprocess +import pytest def _get_current_coverage(): """Helper function that returns the coverage computed with kcov.""" @@ -82,15 +83,25 @@ def _update_coverage(cov_value): with open(coverage_path, "w") as f: f.write(str(cov_value)) -def test_coverage(): +def test_coverage(profile): current_coverage = _get_current_coverage() previous_coverage = _get_previous_coverage() - - # When coverage is increased, we have to update the value in the file. if previous_coverage < current_coverage: - # TODO check if the test runs as part of the CI. If that's the case - # we have to fail the test here. - _update_coverage(current_coverage) + if profile == pytest.profile_ci: + # In the CI Profile we expect the coverage to be manually updated. + assert False, "Coverage is increased from {} to {}. " \ + "Please update the coverage in " \ + "tests/coverage.".format( + previous_coverage, + current_coverage + ) + elif profile == pytest.profile_devel: + _update_coverage(current_coverage) + else: + # This should never happen because pytest should only accept + # the valid test profiles specified with `choices` in + # `pytest_addoption`. + assert False, "Invalid test profile." elif previous_coverage > current_coverage: diff = float(previous_coverage - current_coverage) assert False, "Coverage drops by {:.2f}%. Please add unit tests for" \ From 337a7644b2fd8634179faa2ee5ad6f40c466abd1 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Wed, 10 Apr 2019 16:08:37 +0300 Subject: [PATCH 040/332] added high level crate documentation in lib.rs The documentation has details about supported platforms and ioctls as well as an x86 example. Fixes: https://github.com/rust-vmm/kvm-ioctls/issues/21 Signed-off-by: Andreea Florescu --- src/lib.rs | 169 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 9a3882b..99574e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,175 @@ #![deny(missing_docs)] //! A safe wrapper around the kernel's KVM interface. +//! +//! This crate offers safe wrappers for: +//! - [system ioctls](struct.Kvm.html) using the `Kvm` structure +//! - [VM ioctls](struct.VmFd.html) using the `VmFd` structure +//! - [vCPU ioctls](struct.VcpuFd.html) using the `VcpuFd` structure +//! - [device ioctls](struct.DeviceFd.html) using the `DeviceFd` structure +//! +//! # Platform support +//! +//! - x86_64 +//! - arm64 (experimental) +//! +//! **NOTE:** The list of available ioctls is not extensive. +//! +//! # Example - Running a VM on x86_64 +//! +//! In this example we are creating a Virtual Machine (VM) with one vCPU. +//! On the vCPU we are running x86_64 specific code. This example is based on +//! the [LWN article](https://lwn.net/Articles/658511/) on using the KVM API. +//! +//! To get code running on the vCPU we are going through the following steps: +//! +//! 1. Instantiate KVM. This is used for running +//! [system specific ioctls](struct.Kvm.html). +//! 2. Use the KVM object to create a VM. The VM is used for running +//! [VM specific ioctls](struct.VmFd.html). +//! 3. Initialize the guest memory for the created VM. In this dummy example we +//! are adding only one memory region and write the code in one memory page. +//! 4. Create a vCPU using the VM object. The vCPU is used for running +//! [vCPU specific ioctls](struct.VcpuFd.html). +//! 5. Setup x86 specific general purpose registers and special registers. For +//! details about how and why these registers are set, please check the +//! [LWN article](https://lwn.net/Articles/658511/) on which this example is +//! built. +//! 6. Run the vCPU code in a loop and check the +//! [exit reasons](enum.VcpuExit.html). +//! +//! +//! ```rust +//! extern crate kvm_ioctls; +//! extern crate kvm_bindings; +//! +//! use kvm_ioctls::{Kvm, VmFd, VcpuFd}; +//! use kvm_ioctls::VcpuExit; +//! +//! #[cfg(target_arch = "x86_64")] +//! fn main(){ +//! use std::io::Write; +//! use std::slice; +//! use std::ptr::null_mut; +//! +//! use kvm_bindings::KVM_MEM_LOG_DIRTY_PAGES; +//! use kvm_bindings::kvm_userspace_memory_region; +//! +//! // 1. Instantiate KVM. +//! let kvm = Kvm::new().unwrap(); +//! +//! // 2. Create a VM. +//! let vm = kvm.create_vm().unwrap(); +//! +//! // 3. Initialize Guest Memory. +//! let mem_size = 0x4000; +//! let guest_addr: u64 = 0x1000; +//! let load_addr: *mut u8 = unsafe { +//! libc::mmap( +//! null_mut(), +//! mem_size, +//! libc::PROT_READ | libc::PROT_WRITE, +//! libc::MAP_ANONYMOUS | libc::MAP_SHARED | libc::MAP_NORESERVE, +//! -1, +//! 0, +//! ) as *mut u8 +//! }; +//! +//! let slot = 0; +//! // When initializing the guest memory slot specify the +//! // `KVM_MEM_LOG_DIRTY_PAGES` to enable the dirty log. +//! let mem_region = kvm_userspace_memory_region { +//! slot, +//! guest_phys_addr: guest_addr, +//! memory_size: mem_size as u64, +//! userspace_addr: load_addr as u64, +//! flags: KVM_MEM_LOG_DIRTY_PAGES, +//! }; +//! vm.set_user_memory_region(mem_region).unwrap(); +//! +//! +//! let x86_code = [ +//! 0xba, 0xf8, 0x03, /* mov $0x3f8, %dx */ +//! 0x00, 0xd8, /* add %bl, %al */ +//! 0x04, b'0', /* add $'0', %al */ +//! 0xee, /* out %al, %dx */ +//! 0xec, /* in %dx, %al */ +//! 0xc6, 0x06, 0x00, 0x80, 0x00, /* movl $0, (0x8000); This generates a MMIO Write.*/ +//! 0x8a, 0x16, 0x00, 0x80, /* movl (0x8000), %dl; This generates a MMIO Read.*/ +//! 0xf4, /* hlt */ +//! ]; +//! +//! // Write the code in the guest memory. This will generate a dirty page. +//! unsafe { +//! let mut slice = slice::from_raw_parts_mut(load_addr, mem_size); +//! slice.write(&x86_code).unwrap(); +//! } +//! +//! // 4. Create one vCPU. +//! let vcpu_fd = vm.create_vcpu(0).unwrap(); +//! +//! // 5. Initialize general purpose and special registers. +//! let mut vcpu_sregs = vcpu_fd.get_sregs().unwrap(); +//! vcpu_sregs.cs.base = 0; +//! vcpu_sregs.cs.selector = 0; +//! vcpu_fd.set_sregs(&vcpu_sregs).unwrap(); +//! +//! let mut vcpu_regs = vcpu_fd.get_regs().unwrap(); +//! vcpu_regs.rip = guest_addr; +//! vcpu_regs.rax = 2; +//! vcpu_regs.rbx = 3; +//! vcpu_regs.rflags = 2; +//! vcpu_fd.set_regs(&vcpu_regs).unwrap(); +//! +//! // 6. Run code on the vCPU. +//! loop { +//! match vcpu_fd.run().expect("run failed") { +//! VcpuExit::IoIn(addr, data) => { +//! println!( +//! "Received an I/O in exit. Address: {:#x}. Data: {:#x}", +//! addr, +//! data[0], +//! ); +//! } +//! VcpuExit::IoOut(addr, data) => { +//! println!( +//! "Received an I/O out exit. Address: {:#x}. Data: {:#x}", +//! addr, +//! data[0], +//! ); +//! } +//! VcpuExit::MmioRead(addr, data) => { +//! println!( +//! "Received an MMIO Read Request for the address {:#x}.", +//! addr, +//! ); +//! } +//! VcpuExit::MmioWrite(addr, data) => { +//! println!( +//! "Received an MMIO Write Request to the address {:#x}.", +//! addr, +//! ); +//! } +//! VcpuExit::Hlt => { +//! // The code snippet dirties 1 page when it is loaded in memory +//! let dirty_pages_bitmap = vm.get_dirty_log(slot, mem_size).unwrap(); +//! let dirty_pages = dirty_pages_bitmap +//! .into_iter() +//! .map(|page| page.count_ones()) +//! .fold(0, |dirty_page_count, i| dirty_page_count + i); +//! assert_eq!(dirty_pages, 1); +//! break; +//! } +//! r => panic!("Unexpected exit reason: {:?}", r), +//! } +//! } +//! } +//! +//! #[cfg(not(target_arch = "x86_64"))] +//! fn main() { +//! println!("This code example only works on x86_64."); +//! } +//! ``` extern crate kvm_bindings; extern crate libc; From e24f72d98823281eace4ec327e893472ca16171e Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Fri, 12 Apr 2019 18:35:16 +0300 Subject: [PATCH 041/332] added missing sections in README.md Added general overview of the crate, supported platforms, and details about running the tests. Signed-off-by: Andreea Florescu --- README.md | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ae2e22b..d3b80c3 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,47 @@ +[![Build Status](https://badge.buildkite.com/9e0e6c88972a3248a0908506d6946624da84e4e18c0870c4d0.svg)](https://buildkite.com/rust-vmm/kvm-ioctls-ci) +![crates.io](https://img.shields.io/crates/v/kvm-ioctls.svg) + # kvm-ioctls -TODO +The kvm-ioctls crate provides safe wrappers over the +[KVM API](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt), a set +of ioctls used for creating and configuring Virtual Machines (VMs) on Linux. +The ioctls are accessible through four structures: +- `Kvm` - wrappers over system ioctls +- `VmFd` - wrappers over VM ioctls +- `VcpuFd` - wrappers over vCPU ioctls +- `DeviceFd` - wrappers over device ioctls + +For further details check the +[KVM API](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt) as well +as the code documentation. + +## Supported Platforms + +The kvm-ioctls can be used on x86_64 and aarch64. Right now the aarch64 support +is considered experimental. For a production ready version, please check the +progress in the corresponding +[GitHub issue](https://github.com/rust-vmm/kvm-ioctls/issues/8). ## Running the tests -TODO +Our Continuous Integration (CI) pipeline is implemented on top of +[Buildkite](https://buildkite.com/). +For the complete list of tests, check our +[CI pipeline](https://buildkite.com/rust-vmm/kvm-ioctls-ci). + +Each individual test runs in a container. To reproduce a test locally, you can +use the dev-container on both x86 and arm64. + +```bash +docker run --device=/dev/kvm \ + -it \ + --security-opt seccomp=unconfined \ + --volume $(pwd)/kvm-ioctls:/kvm-ioctls \ + fandree/rust-vmm-dev +cd kvm-ioctls/ +cargo test +``` ### Test Profiles From e0d6aefd28097446ee8fcd66160c583f165b5116 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Wed, 17 Apr 2019 11:23:17 +0300 Subject: [PATCH 042/332] update version from v0.0.1 to v0.1.0 Signed-off-by: Andreea Florescu --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 444ae1b..4652cd1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,7 +10,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "kvm-ioctls" -version = "0.0.1" +version = "0.1.0" dependencies = [ "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "kvm-bindings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 4e0abf6..5911fee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kvm-ioctls" -version = "0.0.1" +version = "0.1.0" authors = ["Amazon Firecracker Team "] description = "Safe wrappers over KVM ioctls" repository = "https://github.com/rust-vmm/kvm-ioctls" From 7b2413269d03bfb9db02f69e660095596a7fb383 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Mon, 6 May 2019 13:13:44 +0100 Subject: [PATCH 043/332] fix test_create_device The test was issuing a warning because the result of set_device_attr was not checked. Removed the line as it was a duplicate. We are expecting the `set_device_attr` to fail in that scenario because we aren't creating a real device. Signed-off-by: Andreea Florescu --- src/ioctls/device.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ioctls/device.rs b/src/ioctls/device.rs index a523a79..e065697 100644 --- a/src/ioctls/device.rs +++ b/src/ioctls/device.rs @@ -88,7 +88,7 @@ mod tests { addr: 0x0, flags: 0, }; - device_fd.set_device_attr(&dist_attr); + // We are just creating a test device. Creating a real device would make the CI dependent // on host configuration (like having /dev/vfio). We expect this to fail. assert!(device_fd.set_device_attr(&dist_attr).is_err()); From a6c9d920a6ee8a164ba9e47173e2646772dfc998 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Mon, 6 May 2019 13:08:24 +0100 Subject: [PATCH 044/332] fix build & test warnings Warnings were mostly generated by unused imports. We missed the warnings because we had the `#![allow(unused)]` macro in src/lib.rs. This macro unfortunately also hides the result unused warning which might have serious implications. Removed said macro and added a new one in sys_ioctls.rs where it is actually required because we aren't using all ioctl macros. To make sure we aren't leaving warnings behind the Buildkite pipeline will also be updated to use -D warnings when building the tests and the binaries. Signed-off-by: Andreea Florescu --- src/ioctls/device.rs | 11 ++++++----- src/ioctls/mod.rs | 3 +++ src/ioctls/system.rs | 6 ++++-- src/ioctls/vcpu.rs | 25 ++++++++++++++----------- src/ioctls/vm.rs | 16 +++++++++------- src/kvm_ioctls.rs | 2 +- src/lib.rs | 1 - 7 files changed, 37 insertions(+), 27 deletions(-) diff --git a/src/ioctls/device.rs b/src/ioctls/device.rs index e065697..23491fb 100644 --- a/src/ioctls/device.rs +++ b/src/ioctls/device.rs @@ -3,7 +3,7 @@ use std::fs::File; use std::io; -use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; +use std::os::unix::io::{AsRawFd, RawFd}; use kvm_bindings::kvm_device_attr; @@ -49,14 +49,15 @@ mod tests { use super::*; use ioctls::system::Kvm; use kvm_bindings::{ - kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, kvm_device_type_KVM_DEV_TYPE_FSL_MPIC_20, - kvm_device_type_KVM_DEV_TYPE_VFIO, KVM_CREATE_DEVICE_TEST, KVM_DEV_VFIO_GROUP, - KVM_DEV_VFIO_GROUP_ADD, + kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, kvm_device_type_KVM_DEV_TYPE_VFIO, + KVM_CREATE_DEVICE_TEST, KVM_DEV_VFIO_GROUP, KVM_DEV_VFIO_GROUP_ADD, }; - use std::io::{Error, ErrorKind}; #[test] fn test_create_device() { + #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + use kvm_bindings::kvm_device_type_KVM_DEV_TYPE_FSL_MPIC_20; + let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); diff --git a/src/ioctls/mod.rs b/src/ioctls/mod.rs index d5a1cf3..bbd4ab3 100644 --- a/src/ioctls/mod.rs +++ b/src/ioctls/mod.rs @@ -6,6 +6,7 @@ // found in the THIRD-PARTY file. use std::io; +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use std::mem::size_of; use std::os::unix::io::AsRawFd; use std::ptr::null_mut; @@ -31,6 +32,7 @@ pub mod vm; pub type Result = result::Result; // Returns a `Vec` with a size in bytes at least as large as `size_in_bytes`. +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn vec_with_size_in_bytes(size_in_bytes: usize) -> Vec { let rounded_size = (size_in_bytes + size_of::() - 1) / size_of::(); let mut v = Vec::with_capacity(rounded_size); @@ -55,6 +57,7 @@ fn vec_with_size_in_bytes(size_in_bytes: usize) -> Vec { // for `Foo`, a `Vec` is created. Only the first element of `Vec` would actually be used // as a `Foo`. The remaining memory in the `Vec` is for `entries`, which must be contiguous // with `Foo`. This function is used to make the `Vec` with enough space for `count` entries. +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn vec_with_array_field(count: usize) -> Vec { let element_space = count * size_of::(); let vec_size_bytes = size_of::() + element_space; diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index 2f668fb..9d0ef3c 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -4,8 +4,8 @@ // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the THIRD-PARTY file. - -use kvm_bindings::*; +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +use kvm_bindings::kvm_msr_list; use libc::{open, O_CLOEXEC, O_RDWR}; use std::fs::File; @@ -14,6 +14,7 @@ use std::os::raw::{c_char, c_ulong}; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use cap::Cap; +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use ioctls::vec_with_array_field; use ioctls::vm::{new_vmfd, VmFd}; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] @@ -383,6 +384,7 @@ impl AsRawFd for Kvm { #[cfg(test)] mod tests { use super::*; + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use MAX_KVM_CPUID_ENTRIES; #[test] diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 129d2a9..7b49a32 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -754,17 +754,18 @@ impl AsRawFd for VcpuFd { mod tests { extern crate byteorder; + #[cfg(target_arch = "x86_64")] use super::*; use ioctls::system::Kvm; - use Cap; - use MAX_KVM_CPUID_ENTRIES; - - use std::os::unix::io::FromRawFd; - use std::ptr::null_mut; + #[cfg(target_arch = "x86_64")] + use {Cap, MAX_KVM_CPUID_ENTRIES}; // Helper function for memory mapping `size` bytes of anonymous memory. // Panics if the mmap fails. + #[cfg(target_arch = "x86_64")] fn mmap_anonymous(size: usize) -> *mut u8 { + use std::ptr::null_mut; + let addr = unsafe { libc::mmap( null_mut(), @@ -1023,13 +1024,11 @@ mod tests { } } - fn get_raw_errno(result: super::Result) -> i32 { - result.err().unwrap().raw_os_error().unwrap() - } - #[test] #[cfg(target_arch = "x86_64")] fn test_faulty_vcpu_fd() { + use std::os::unix::io::FromRawFd; + let badf_errno = libc::EBADF; let faulty_vcpu_fd = VcpuFd { @@ -1039,6 +1038,10 @@ mod tests { }, }; + fn get_raw_errno(result: super::Result) -> i32 { + result.err().unwrap().raw_os_error().unwrap() + } + assert_eq!(get_raw_errno(faulty_vcpu_fd.get_regs()), badf_errno); assert_eq!( get_raw_errno(faulty_vcpu_fd.set_regs(&unsafe { std::mem::zeroed() })), @@ -1109,8 +1112,8 @@ mod tests { vm.get_preferred_target(&mut kvi) .expect("Cannot get preferred target"); vcpu.vcpu_init(&kvi).expect("Cannot initialize vcpu"); - let mut data: u64 = 0; - let mut reg_id: u64 = 0; + let data: u64 = 0; + let reg_id: u64 = 0; assert!(vcpu.set_one_reg(reg_id, data).is_err()); // Exercising KVM_SET_ONE_REG by trying to alter the data inside the PSTATE register (which is a diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 6af73aa..649e939 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -9,7 +9,9 @@ use ioctls::Result; use kvm_bindings::*; use std::fs::File; use std::io; -use std::os::raw::{c_ulong, c_void}; +use std::os::raw::c_ulong; +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +use std::os::raw::c_void; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use ioctls::device::new_device; @@ -506,7 +508,7 @@ impl VmFd { pub fn create_device(&self, device: &mut kvm_create_device) -> Result { let ret = unsafe { ioctl_with_ref(self, KVM_CREATE_DEVICE(), device) }; if ret == 0 { - Ok((new_device(unsafe { File::from_raw_fd(device.fd as i32) }))) + Ok(new_device(unsafe { File::from_raw_fd(device.fd as i32) })) } else { return Err(io::Error::last_os_error()); } @@ -570,7 +572,7 @@ impl AsRawFd for VmFd { #[cfg(test)] mod tests { use super::*; - use {Cap, Kvm, MAX_KVM_CPUID_ENTRIES}; + use {Cap, Kvm}; use libc::{eventfd, EFD_NONBLOCK}; @@ -676,10 +678,6 @@ mod tests { assert!(vm_fd.register_irqfd(evtfd3, 5).is_err()); } - fn get_raw_errno(result: super::Result) -> i32 { - result.err().unwrap().raw_os_error().unwrap() - } - #[test] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn test_faulty_vm_fd() { @@ -698,6 +696,10 @@ mod tests { flags: 0, }; + fn get_raw_errno(result: super::Result) -> i32 { + result.err().unwrap().raw_os_error().unwrap() + } + assert_eq!( get_raw_errno(faulty_vm_fd.set_user_memory_region(invalid_mem_region)), badf_errno diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index 69e8117..40741a1 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -4,7 +4,7 @@ // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the THIRD-PARTY file. - +#![allow(unused)] use super::sys_ioctl::*; use kvm_bindings::*; diff --git a/src/lib.rs b/src/lib.rs index 99574e1..19b68d5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,6 @@ // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the THIRD-PARTY file. -#![allow(unused)] #![deny(missing_docs)] //! A safe wrapper around the kernel's KVM interface. From 789d9626c0d3a3491b949564975f4f59d609b775 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Mon, 3 Jun 2019 11:50:32 +0300 Subject: [PATCH 045/332] add buildkite pipeline to repository Previously the pipeline steps were configured directly in the Buildkite web interface. Signed-off-by: Andreea Florescu --- .buildkite/pipeline.yml | 162 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 .buildkite/pipeline.yml diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml new file mode 100644 index 0000000..af69ad0 --- /dev/null +++ b/.buildkite/pipeline.yml @@ -0,0 +1,162 @@ +steps: + - label: "build-gnu-x86" + commands: + - cargo build --release + retry: + automatic: false + agents: + platform: x86_64.metal + plugins: + - docker#v3.0.1: + image: "fandree/rust-vmm-dev" + always-pull: true + + - label: "build-gnu-arm" + commands: + - cargo build --release + retry: + automatic: false + agents: + platform: arm.metal + plugins: + - docker#v3.0.1: + image: "fandree/rust-vmm-dev" + always-pull: true + + - label: "build-musl-x86" + commands: + - cargo build --release --target x86_64-unknown-linux-musl + retry: + automatic: false + agents: + platform: x86_64.metal + plugins: + - docker#v3.0.1: + image: "fandree/rust-vmm-dev" + always-pull: true + + - label: "build-musl-arm" + commands: + - cargo build --release --target aarch64-unknown-linux-musl + retry: + automatic: false + agents: + platform: arm.metal + plugins: + - docker#v3.0.1: + image: "fandree/rust-vmm-dev" + always-pull: true + + - label: "style" + command: cargo fmt --all -- --check + retry: + automatic: false + agents: + platform: x86_64.metal + plugins: + - docker#v3.0.1: + image: "fandree/rust-vmm-dev" + always-pull: true + + - label: "unittests-gnu-x86" + commands: + - cargo test + retry: + automatic: false + agents: + platform: x86_64.metal + plugins: + - docker#v3.0.1: + privileged: true + image: "fandree/rust-vmm-dev" + always-pull: true + + - label: "unittests-gnu-arm" + commands: + - cargo test + retry: + automatic: false + agents: + platform: arm.metal + plugins: + - docker#v3.0.1: + privileged: true + image: "fandree/rust-vmm-dev" + always-pull: true + + - label: "unittests-musl-x86" + command: + - cargo test --target x86_64-unknown-linux-musl + retry: + automatic: false + agents: + platform: x86_64.metal + plugins: + - docker#v3.0.1: + privileged: true + image: "fandree/rust-vmm-dev" + always-pull: true + + - label: "unittests-musl-arm" + command: + - cargo test --target aarch64-unknown-linux-musl + retry: + automatic: false + agents: + platform: arm.metal + plugins: + - docker#v3.0.1: + privileged: true + image: "fandree/rust-vmm-dev" + always-pull: true + + - label: "clippy-x86" + commands: + - cargo clippy --all + retry: + automatic: false + agents: + platform: x86_64.metal + plugins: + - docker#v3.0.1: + image: "fandree/rust-vmm-dev" + always-pull: true + + - label: "coverage-x86" + commands: + - pytest tests/test_coverage.py + retry: + automatic: false + agents: + platform: x86_64.metal + plugins: + - docker#v3.0.1: + privileged: true + image: "fandree/rust-vmm-dev" + always-pull: true + + - label: "check-warnings-x86" + commands: + - RUSTFLAGS="-D warnings" cargo check --all-targets + retry: + automatic: false + agents: + platform: x86_64.metal + plugins: + - docker#v3.0.1: + privileged: true + image: "fandree/rust-vmm-dev" + always-pull: true + + - label: "check-warnings-arm" + command: + - RUSTFLAGS="-D warnings" cargo check --all-targets + retry: + automatic: false + agents: + platform: arm.metal + plugins: + - docker#v3.0.1: + privileged: true + image: "fandree/rust-vmm-dev" + always-pull: true \ No newline at end of file From 643c33edb6ce861c28421a932f67e85c6beebddf Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Mon, 3 Jun 2019 11:52:06 +0300 Subject: [PATCH 046/332] [buildkite] switch to rustvmm container The v1 of the rustvmm/dev container is now available. Switch to this one instead of using the one hosted on fandree. Signed-off-by: Andreea Florescu --- .buildkite/pipeline.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index af69ad0..2dfcff1 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -8,7 +8,7 @@ steps: platform: x86_64.metal plugins: - docker#v3.0.1: - image: "fandree/rust-vmm-dev" + image: "rustvmm/dev:v1" always-pull: true - label: "build-gnu-arm" @@ -20,7 +20,7 @@ steps: platform: arm.metal plugins: - docker#v3.0.1: - image: "fandree/rust-vmm-dev" + image: "rustvmm/dev:v1" always-pull: true - label: "build-musl-x86" @@ -32,7 +32,7 @@ steps: platform: x86_64.metal plugins: - docker#v3.0.1: - image: "fandree/rust-vmm-dev" + image: "rustvmm/dev:v1" always-pull: true - label: "build-musl-arm" @@ -44,7 +44,7 @@ steps: platform: arm.metal plugins: - docker#v3.0.1: - image: "fandree/rust-vmm-dev" + image: "rustvmm/dev:v1" always-pull: true - label: "style" @@ -55,7 +55,7 @@ steps: platform: x86_64.metal plugins: - docker#v3.0.1: - image: "fandree/rust-vmm-dev" + image: "rustvmm/dev:v1" always-pull: true - label: "unittests-gnu-x86" @@ -68,7 +68,7 @@ steps: plugins: - docker#v3.0.1: privileged: true - image: "fandree/rust-vmm-dev" + image: "rustvmm/dev:v1" always-pull: true - label: "unittests-gnu-arm" @@ -81,7 +81,7 @@ steps: plugins: - docker#v3.0.1: privileged: true - image: "fandree/rust-vmm-dev" + image: "rustvmm/dev:v1" always-pull: true - label: "unittests-musl-x86" @@ -94,7 +94,7 @@ steps: plugins: - docker#v3.0.1: privileged: true - image: "fandree/rust-vmm-dev" + image: "rustvmm/dev:v1" always-pull: true - label: "unittests-musl-arm" @@ -107,7 +107,7 @@ steps: plugins: - docker#v3.0.1: privileged: true - image: "fandree/rust-vmm-dev" + image: "rustvmm/dev:v1" always-pull: true - label: "clippy-x86" @@ -119,7 +119,7 @@ steps: platform: x86_64.metal plugins: - docker#v3.0.1: - image: "fandree/rust-vmm-dev" + image: "rustvmm/dev:v1" always-pull: true - label: "coverage-x86" @@ -132,7 +132,7 @@ steps: plugins: - docker#v3.0.1: privileged: true - image: "fandree/rust-vmm-dev" + image: "rustvmm/dev:v1" always-pull: true - label: "check-warnings-x86" @@ -145,7 +145,7 @@ steps: plugins: - docker#v3.0.1: privileged: true - image: "fandree/rust-vmm-dev" + image: "rustvmm/dev:v1" always-pull: true - label: "check-warnings-arm" @@ -158,5 +158,5 @@ steps: plugins: - docker#v3.0.1: privileged: true - image: "fandree/rust-vmm-dev" + image: "rustvmm/dev:v1" always-pull: true \ No newline at end of file From 9134608f4eab351eb86c189ad57008d107d0dfbe Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Mon, 3 Jun 2019 16:57:07 +0300 Subject: [PATCH 047/332] mark `set_user_memory_region` as unsafe This function uses as parameter kvm_userspace_memory_region which is a wrapper over a raw pointer (userspace_addr) and the size (memory_size) so cannot know if the pointer refers to a valid memory. Fixes: https://github.com/rust-vmm/kvm-ioctls/issues/38 Signed-off-by: Andreea Florescu --- src/ioctls/vcpu.rs | 6 ++++-- src/ioctls/vm.rs | 15 ++++++++------- src/lib.rs | 2 +- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 7b49a32..c0570e8 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -620,7 +620,7 @@ impl VcpuFd { /// userspace_addr: load_addr as u64, /// flags: 0, /// }; - /// vm.set_user_memory_region(mem_region).unwrap(); + /// unsafe { vm.set_user_memory_region(mem_region).unwrap() }; /// /// // Dummy x86 code that just calls halt. /// let x86_code = [ @@ -961,7 +961,9 @@ mod tests { userspace_addr: load_addr as u64, flags: KVM_MEM_LOG_DIRTY_PAGES, }; - vm.set_user_memory_region(mem_region).unwrap(); + unsafe { + vm.set_user_memory_region(mem_region).unwrap(); + } unsafe { // Get a mutable slice of `mem_size` from `load_addr`. diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 649e939..493807d 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -83,15 +83,16 @@ impl VmFd { /// userspace_addr: 0x0 as u64, /// flags: 0, /// }; - /// vm.set_user_memory_region(mem_region).unwrap(); + /// unsafe { + /// vm.set_user_memory_region(mem_region).unwrap(); + /// }; /// ``` /// - pub fn set_user_memory_region( + pub unsafe fn set_user_memory_region( &self, user_memory_region: kvm_userspace_memory_region, ) -> Result<()> { - let ret = - unsafe { ioctl_with_ref(self, KVM_SET_USER_MEMORY_REGION(), &user_memory_region) }; + let ret = ioctl_with_ref(self, KVM_SET_USER_MEMORY_REGION(), &user_memory_region); if ret == 0 { Ok(()) } else { @@ -306,7 +307,7 @@ impl VmFd { /// userspace_addr: load_addr as u64, /// flags: KVM_MEM_LOG_DIRTY_PAGES, /// }; - /// vm.set_user_memory_region(mem_region).unwrap(); + /// unsafe { vm.set_user_memory_region(mem_region).unwrap() }; /// /// // Dummy x86 code that just calls halt. /// let x86_code = [ @@ -587,7 +588,7 @@ mod tests { userspace_addr: 0, flags: 0, }; - assert!(vm.set_user_memory_region(invalid_mem_region).is_err()); + assert!(unsafe { vm.set_user_memory_region(invalid_mem_region) }.is_err()); } #[test] @@ -701,7 +702,7 @@ mod tests { } assert_eq!( - get_raw_errno(faulty_vm_fd.set_user_memory_region(invalid_mem_region)), + get_raw_errno(unsafe { faulty_vm_fd.set_user_memory_region(invalid_mem_region) }), badf_errno ); assert_eq!(get_raw_errno(faulty_vm_fd.set_tss_address(0)), badf_errno); diff --git a/src/lib.rs b/src/lib.rs index 19b68d5..f8899e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -91,7 +91,7 @@ //! userspace_addr: load_addr as u64, //! flags: KVM_MEM_LOG_DIRTY_PAGES, //! }; -//! vm.set_user_memory_region(mem_region).unwrap(); +//! unsafe { vm.set_user_memory_region(mem_region).unwrap() }; //! //! //! let x86_code = [ From d378c95c8e26716840d0005ca11006eabf723606 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Tue, 4 Jun 2019 15:22:41 +0300 Subject: [PATCH 048/332] updated documentation for set_user_memory_region Added Safety section which explains why is the function unsafe and what guarantees the caller needs to provide. Signed-off-by: Andreea Florescu --- src/ioctls/vm.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 493807d..4566eda 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -65,6 +65,15 @@ impl VmFd { /// `kvm_userspace_memory_region` structure in the /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). /// + /// # Safety + /// + /// This function is unsafe because there is no guarantee `userspace_addr` points to a valid + /// memory region, nor the memory region lives as long as the kernel needs it to. + /// + /// The caller of this method must make sure that: + /// - the raw pointer (`userspace_addr`) points to valid memory + /// - the regions provided to KVM are not overlapping other memory regions. + /// /// # Example /// /// ```rust From b885ded2fc78a9b96bd487f3e4bff08f7967aecc Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Tue, 4 Jun 2019 15:29:53 +0300 Subject: [PATCH 049/332] run integration tests with rustvmm/dev:v2 Previously the tests were run with v1. Also updated README.md to use rustvmm/dev:v2 in examples. Fixes: https://github.com/rust-vmm/kvm-ioctls/issues/45 Signed-off-by: Andreea Florescu --- .buildkite/pipeline.yml | 26 +++++++++++++------------- README.md | 4 ++-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 2dfcff1..b7804f6 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -8,7 +8,7 @@ steps: platform: x86_64.metal plugins: - docker#v3.0.1: - image: "rustvmm/dev:v1" + image: "rustvmm/dev:v2" always-pull: true - label: "build-gnu-arm" @@ -20,7 +20,7 @@ steps: platform: arm.metal plugins: - docker#v3.0.1: - image: "rustvmm/dev:v1" + image: "rustvmm/dev:v2" always-pull: true - label: "build-musl-x86" @@ -32,7 +32,7 @@ steps: platform: x86_64.metal plugins: - docker#v3.0.1: - image: "rustvmm/dev:v1" + image: "rustvmm/dev:v2" always-pull: true - label: "build-musl-arm" @@ -44,7 +44,7 @@ steps: platform: arm.metal plugins: - docker#v3.0.1: - image: "rustvmm/dev:v1" + image: "rustvmm/dev:v2" always-pull: true - label: "style" @@ -55,7 +55,7 @@ steps: platform: x86_64.metal plugins: - docker#v3.0.1: - image: "rustvmm/dev:v1" + image: "rustvmm/dev:v2" always-pull: true - label: "unittests-gnu-x86" @@ -68,7 +68,7 @@ steps: plugins: - docker#v3.0.1: privileged: true - image: "rustvmm/dev:v1" + image: "rustvmm/dev:v2" always-pull: true - label: "unittests-gnu-arm" @@ -81,7 +81,7 @@ steps: plugins: - docker#v3.0.1: privileged: true - image: "rustvmm/dev:v1" + image: "rustvmm/dev:v2" always-pull: true - label: "unittests-musl-x86" @@ -94,7 +94,7 @@ steps: plugins: - docker#v3.0.1: privileged: true - image: "rustvmm/dev:v1" + image: "rustvmm/dev:v2" always-pull: true - label: "unittests-musl-arm" @@ -107,7 +107,7 @@ steps: plugins: - docker#v3.0.1: privileged: true - image: "rustvmm/dev:v1" + image: "rustvmm/dev:v2" always-pull: true - label: "clippy-x86" @@ -119,7 +119,7 @@ steps: platform: x86_64.metal plugins: - docker#v3.0.1: - image: "rustvmm/dev:v1" + image: "rustvmm/dev:v2" always-pull: true - label: "coverage-x86" @@ -132,7 +132,7 @@ steps: plugins: - docker#v3.0.1: privileged: true - image: "rustvmm/dev:v1" + image: "rustvmm/dev:v2" always-pull: true - label: "check-warnings-x86" @@ -145,7 +145,7 @@ steps: plugins: - docker#v3.0.1: privileged: true - image: "rustvmm/dev:v1" + image: "rustvmm/dev:v2" always-pull: true - label: "check-warnings-arm" @@ -158,5 +158,5 @@ steps: plugins: - docker#v3.0.1: privileged: true - image: "rustvmm/dev:v1" + image: "rustvmm/dev:v2" always-pull: true \ No newline at end of file diff --git a/README.md b/README.md index d3b80c3..6d861b3 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ docker run --device=/dev/kvm \ -it \ --security-opt seccomp=unconfined \ --volume $(pwd)/kvm-ioctls:/kvm-ioctls \ - fandree/rust-vmm-dev + rustvmm/dev:v2 cd kvm-ioctls/ cargo test ``` @@ -76,7 +76,7 @@ docker run --device=/dev/kvm \ -it \ --security-opt seccomp=unconfined \ --volume $(pwd)/kvm-ioctls:/kvm-ioctls \ - fandree/rust-vmm-dev + rustvmm/dev:v2 cd kvm-ioctls/ pytest --profile=devel tests/test_coverage.py ``` From 0042e308d7cc5d3069d05eb509e3b7a6d051f212 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Tue, 4 Jun 2019 15:31:28 +0300 Subject: [PATCH 050/332] added clippy test for aarch64 Signed-off-by: Andreea Florescu --- .buildkite/pipeline.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index b7804f6..3c39111 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -122,6 +122,18 @@ steps: image: "rustvmm/dev:v2" always-pull: true + - label: "clippy-arm" + commands: + - cargo clippy --all + retry: + automatic: false + agents: + platform: arm.metal + plugins: + - docker#v3.0.1: + image: "rustvmm/dev:v2" + always-pull: true + - label: "coverage-x86" commands: - pytest tests/test_coverage.py From 1102dc9b3401fcba9ad204a39e9457c77e5f2a7c Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Tue, 4 Jun 2019 15:59:13 +0300 Subject: [PATCH 051/332] fixed needless-return clippy warning Also updated the pipeline to deny warnings when running clippy so we don't miss them in the future. Signed-off-by: Andreea Florescu --- .buildkite/pipeline.yml | 4 ++-- src/ioctls/vm.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 3c39111..38e784b 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -112,7 +112,7 @@ steps: - label: "clippy-x86" commands: - - cargo clippy --all + - cargo clippy --all -- -D warnings retry: automatic: false agents: @@ -124,7 +124,7 @@ steps: - label: "clippy-arm" commands: - - cargo clippy --all + - cargo clippy --all -- -D warnings retry: automatic: false agents: diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 4566eda..4fc9e3f 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -520,7 +520,7 @@ impl VmFd { if ret == 0 { Ok(new_device(unsafe { File::from_raw_fd(device.fd as i32) })) } else { - return Err(io::Error::last_os_error()); + Err(io::Error::last_os_error()) } } From e7a0ccc463ab8d311aec2f7b2560fa18ed47a0eb Mon Sep 17 00:00:00 2001 From: Sebastien Boeuf Date: Fri, 31 May 2019 13:22:02 -0700 Subject: [PATCH 052/332] Add support for KVM_SIGNAL_MSI Any consumer of this crate who would like to implement MSI/MSI-X support will need KVM_SIGNAL_MSI to notify directly the local APIC emulated by KVM, using MSI/MSI-X vectors. Signed-off-by: Sebastien Boeuf --- src/ioctls/vm.rs | 71 ++++++++++++++++++++++++++++++++++++++++++++++- src/kvm_ioctls.rs | 7 +++++ tests/coverage | 2 +- 3 files changed, 78 insertions(+), 2 deletions(-) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 4fc9e3f..acd72fe 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -9,9 +9,9 @@ use ioctls::Result; use kvm_bindings::*; use std::fs::File; use std::io; -use std::os::raw::c_ulong; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use std::os::raw::c_void; +use std::os::raw::{c_int, c_ulong}; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use ioctls::device::new_device; @@ -202,6 +202,58 @@ impl VmFd { } } + /// Directly injects a MSI message as per the `KVM_SIGNAL_MSI` ioctl. + /// + /// See the documentation for `KVM_SIGNAL_MSI`. + /// + /// This ioctl returns > 0 when the MSI is successfully delivered and 0 + /// when the guest blocked the MSI. + /// + /// # Arguments + /// + /// * kvm_msi - MSI message configuration. For details check the `kvm_msi` structure in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// # Example + /// + /// In this example, the important function signal_msi() calling into + /// the actual ioctl is commented out. The reason is that MSI vectors are + /// not chosen from the HW side (VMM). The guest OS (or anything that runs + /// inside the VM) is supposed to allocate the MSI vectors, and usually + /// communicate back through PCI configuration space. Sending a random MSI + /// vector through this signal_msi() function will always result in a + /// failure, which is why it needs to be commented out. + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// extern crate kvm_bindings; + /// # use kvm_ioctls::{Kvm, VmFd}; + /// use kvm_bindings::kvm_msi; + /// + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let msi = kvm_msi::default(); + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// vm.create_irq_chip().unwrap(); + /// //vm.signal_msi(msi).unwrap(); + /// ``` + /// + #[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" + ))] + pub fn signal_msi(&self, msi: kvm_msi) -> Result { + // Safe because we allocated the structure and we know the kernel + // will read exactly the size of the structure. + let ret = unsafe { ioctl_with_ref(self, KVM_SIGNAL_MSI(), &msi) }; + if ret >= 0 { + Ok(ret) + } else { + Err(io::Error::last_os_error()) + } + } + /// Registers an event to be signaled whenever a certain address is written to. /// /// See the documentation for `KVM_IOEVENTFD`. @@ -743,4 +795,21 @@ mod tests { let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default(); assert!(vm.get_preferred_target(&mut kvi).is_ok()); } + + /// As explained in the example code related to signal_msi(), sending + /// a random MSI vector will always fail because no vector has been + /// previously allocated from the guest itself. + #[test] + #[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" + ))] + fn test_signal_msi_failure() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let msi = kvm_msi::default(); + assert!(vm.signal_msi(msi).is_err()); + } } diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index 40741a1..d031090 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -82,6 +82,13 @@ ioctl_iow_nr!(KVM_SET_FPU, KVMIO, 0x8d, kvm_fpu); ioctl_ior_nr!(KVM_GET_LAPIC, KVMIO, 0x8e, kvm_lapic_state); #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_iow_nr!(KVM_SET_LAPIC, KVMIO, 0x8f, kvm_lapic_state); +#[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" +))] +ioctl_iow_nr!(KVM_SIGNAL_MSI, KVMIO, 0xa5, kvm_msi); #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] ioctl_iow_nr!(KVM_SET_ONE_REG, KVMIO, 0xac, kvm_one_reg); #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] diff --git a/tests/coverage b/tests/coverage index 9d1fca3..37c5abc 100644 --- a/tests/coverage +++ b/tests/coverage @@ -1 +1 @@ -91.3 \ No newline at end of file +91.2 From 8589f89019906fd65042073bf13cfe6d4d1dbf5f Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Tue, 11 Jun 2019 15:16:19 +0300 Subject: [PATCH 053/332] added CODEOWNERS Fixes: https://github.com/rust-vmm/kvm-ioctls/issues/32 Signed-off-by: Andreea Florescu --- CODEOWNERS | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CODEOWNERS diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..b2c60d3 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,3 @@ +# These owners will be the default owners for everything in +# the repo. +* @acatangiu @aghecenco @andreeaflorescu @sameo From d48fdaa3315d401bff965a3709c91898c814dccf Mon Sep 17 00:00:00 2001 From: Sebastien Boeuf Date: Tue, 28 May 2019 11:00:56 -0700 Subject: [PATCH 054/332] Add support for KVM_ENABLE_CAP ioctl By adding the support for the ioctl KVM_ENABLE_CAP, this commit will allow consumers of the kvm-ioctls crate to enable any KVM capability they would be missing from their VMM implementation. One example of capability that can be interesting to enable is the newly added KVM_CAP_SPLIT_IRQCHIP, that allows the consumer to prevent KVM from creating a virtual IOAPIC and PIC. This capability can let a VMM provide its own userspace implementation of those components for security purposes. Unfortunately, we couldn't test enabling a capability for arm/aarch64 since there is no capability available for these architectures. Signed-off-by: Sebastien Boeuf --- src/cap.rs | 2 ++ src/ioctls/vm.rs | 89 +++++++++++++++++++++++++++++++++++++++++++++++ src/kvm_ioctls.rs | 2 ++ tests/coverage | 2 +- 4 files changed, 94 insertions(+), 1 deletion(-) diff --git a/src/cap.rs b/src/cap.rs index 36f03e6..5e2ac89 100644 --- a/src/cap.rs +++ b/src/cap.rs @@ -132,5 +132,7 @@ pub enum Cap { PpcEnableHcall = KVM_CAP_PPC_ENABLE_HCALL, CheckExtensionVm = KVM_CAP_CHECK_EXTENSION_VM, S390UserSigp = KVM_CAP_S390_USER_SIGP, + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + SplitIrqchip = KVM_CAP_SPLIT_IRQCHIP, ImmediateExit = KVM_CAP_IMMEDIATE_EXIT, } diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index acd72fe..390758b 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -610,6 +610,62 @@ impl VmFd { Ok(()) } + /// Enable the specified capability as per the `KVM_ENABLE_CAP` ioctl. + /// + /// See the documentation for `KVM_ENABLE_CAP`. + /// + /// Returns an io::Error when the capability could not be enabled. + /// + /// # Arguments + /// + /// * kvm_enable_cap - KVM capability structure. For details check the `kvm_enable_cap` + /// structure in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// extern crate kvm_bindings; + /// + /// # use kvm_ioctls::{Cap, Kvm, VmFd}; + /// use kvm_bindings::{kvm_enable_cap, KVM_CAP_SPLIT_IRQCHIP}; + /// + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let mut cap: kvm_enable_cap = Default::default(); + /// // This example cannot enable an arm/aarch64 capability since there + /// // is no capability available for these architectures. + /// if cfg!(target_arch = "x86") || cfg!(target_arch = "x86_64") { + /// cap.cap = KVM_CAP_SPLIT_IRQCHIP; + /// // As per the KVM documentation, KVM_CAP_SPLIT_IRQCHIP only emulates + /// // the local APIC in kernel, expecting that a userspace IOAPIC will + /// // be implemented by the VMM. + /// // Along with this capability, the user needs to specify the number + /// // of pins reserved for the userspace IOAPIC. This number needs to be + /// // provided through the first argument of the capability structure, as + /// // specified in KVM documentation: + /// // args[0] - number of routes reserved for userspace IOAPICs + /// // + /// // Because an IOAPIC supports 24 pins, that's the reason why this test + /// // picked this number as reference. + /// cap.args[0] = 24; + /// vm.enable_cap(&cap).unwrap(); + /// } + /// ``` + /// + #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] + pub fn enable_cap(&self, cap: &kvm_enable_cap) -> Result<()> { + // The ioctl is safe because we allocated the struct and we know the + // kernel will write exactly the size of the struct. + let ret = unsafe { ioctl_with_ref(self, KVM_ENABLE_CAP(), cap) }; + if ret == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + /// Get the `kvm_run` size. pub fn run_size(&self) -> usize { self.run_size @@ -812,4 +868,37 @@ mod tests { let msi = kvm_msi::default(); assert!(vm.signal_msi(msi).is_err()); } + + #[test] + #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] + fn test_enable_cap_failure() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let cap: kvm_enable_cap = Default::default(); + // Providing the `kvm_enable_cap` structure filled with default() should + // always result in a failure as it is not a valid capability. + assert!(vm.enable_cap(&cap).is_err()); + } + + #[test] + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn test_enable_split_irqchip_cap() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let mut cap: kvm_enable_cap = Default::default(); + cap.cap = KVM_CAP_SPLIT_IRQCHIP; + // As per the KVM documentation, KVM_CAP_SPLIT_IRQCHIP only emulates + // the local APIC in kernel, expecting that a userspace IOAPIC will + // be implemented by the VMM. + // Along with this capability, the user needs to specify the number + // of pins reserved for the userspace IOAPIC. This number needs to be + // provided through the first argument of the capability structure, as + // specified in KVM documentation: + // args[0] - number of routes reserved for userspace IOAPICs + // + // Because an IOAPIC supports 24 pins, that's the reason why this test + // picked this number as reference. + cap.args[0] = 24; + assert!(vm.enable_cap(&cap).is_ok()); + } } diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index d031090..1113527 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -82,6 +82,8 @@ ioctl_iow_nr!(KVM_SET_FPU, KVMIO, 0x8d, kvm_fpu); ioctl_ior_nr!(KVM_GET_LAPIC, KVMIO, 0x8e, kvm_lapic_state); #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_iow_nr!(KVM_SET_LAPIC, KVMIO, 0x8f, kvm_lapic_state); +#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] +ioctl_iow_nr!(KVM_ENABLE_CAP, KVMIO, 0xa3, kvm_enable_cap); #[cfg(any( target_arch = "x86", target_arch = "x86_64", diff --git a/tests/coverage b/tests/coverage index 37c5abc..098eeb5 100644 --- a/tests/coverage +++ b/tests/coverage @@ -1 +1 @@ -91.2 +91.3 From b7e33cfa4a1d039df35c5ee24e66d06f32c94602 Mon Sep 17 00:00:00 2001 From: Sebastien Boeuf Date: Wed, 29 May 2019 07:55:20 -0700 Subject: [PATCH 055/332] vcpu: Return proper vector when receiving EOI The vector value returned along with End Of Interrupt (EOI) VM exit is important as it indicates a userspace IOAPIC which interrupt needs to be deasserted. About KVM_EXIT_IOAPIC_EOI, KVM documentation mentions: ``` /* KVM_EXIT_IOAPIC_EOI */ struct { __u8 vector; } eoi; Indicates that the VCPU's in-kernel local APIC received an EOI for a level-triggered IOAPIC interrupt. This exit only triggers when the IOAPIC is implemented in userspace (i.e. KVM_CAP_SPLIT_IRQCHIP is enabled); the userspace IOAPIC should process the EOI and retrigger the interrupt if it is still asserted. Vector is the LAPIC interrupt vector for which the EOI was received. ``` Signed-off-by: Sebastien Boeuf --- src/ioctls/vcpu.rs | 9 +++++++-- tests/coverage | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index c0570e8..3b4f8a8 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -87,7 +87,7 @@ pub enum VcpuExit<'a> { /// Corresponds to KVM_EXIT_S390_STSI. S390Stsi, /// Corresponds to KVM_EXIT_IOAPIC_EOI. - IoapicEoi, + IoapicEoi(u8 /* vector */), /// Corresponds to KVM_EXIT_HYPERV. Hyperv, } @@ -725,7 +725,12 @@ impl VcpuFd { KVM_EXIT_EPR => Ok(VcpuExit::Epr), KVM_EXIT_SYSTEM_EVENT => Ok(VcpuExit::SystemEvent), KVM_EXIT_S390_STSI => Ok(VcpuExit::S390Stsi), - KVM_EXIT_IOAPIC_EOI => Ok(VcpuExit::IoapicEoi), + KVM_EXIT_IOAPIC_EOI => { + // Safe because the exit_reason (which comes from the kernel) told us which + // union field to use. + let eoi = unsafe { &mut run.__bindgen_anon_1.eoi }; + Ok(VcpuExit::IoapicEoi(eoi.vector)) + } KVM_EXIT_HYPERV => Ok(VcpuExit::Hyperv), r => panic!("unknown kvm exit reason: {}", r), } diff --git a/tests/coverage b/tests/coverage index 098eeb5..88e4045 100644 --- a/tests/coverage +++ b/tests/coverage @@ -1 +1 @@ -91.3 +90.9 From 1b2016b7ad35bbd10bf015917f2dee424135b568 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Fri, 21 Jun 2019 23:59:56 +0300 Subject: [PATCH 056/332] implement Drop for KvmRunWrapper In KvmRunWrapper we are mmaping memory for the kvm_run wrapper. The memory needs to be unmapped before dropping the reference to KvmRunWrapper. Signed-off-by: Andreea Florescu --- src/ioctls/mod.rs | 13 +++++++++++++ src/ioctls/vcpu.rs | 1 + tests/coverage | 2 +- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/ioctls/mod.rs b/src/ioctls/mod.rs index bbd4ab3..1861eb3 100644 --- a/src/ioctls/mod.rs +++ b/src/ioctls/mod.rs @@ -223,6 +223,8 @@ impl CpuId { /// threads as raw pointers do not implement `Send` and `Sync`. pub struct KvmRunWrapper { kvm_run_ptr: *mut u8, + // This field is need so we can `munmap` the memory mapped to hold `kvm_run`. + mmap_size: usize, } // Send and Sync aren't automatically inherited for the raw address pointer. @@ -257,6 +259,7 @@ impl KvmRunWrapper { Ok(KvmRunWrapper { kvm_run_ptr: addr as *mut u8, + mmap_size: size, }) } @@ -273,6 +276,16 @@ impl KvmRunWrapper { } } +impl Drop for KvmRunWrapper { + fn drop(&mut self) { + // This is safe because we mmap the area at kvm_run_ptr ourselves, + // and nobody else is holding a reference to it. + unsafe { + libc::munmap(self.kvm_run_ptr as *mut libc::c_void, self.mmap_size); + } + } +} + #[cfg(test)] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] mod tests { diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 3b4f8a8..4e01a7f 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1042,6 +1042,7 @@ mod tests { vcpu: unsafe { File::from_raw_fd(-1) }, kvm_run_ptr: KvmRunWrapper { kvm_run_ptr: mmap_anonymous(10), + mmap_size: 10, }, }; diff --git a/tests/coverage b/tests/coverage index 88e4045..7fe4e49 100644 --- a/tests/coverage +++ b/tests/coverage @@ -1 +1 @@ -90.9 +91 From 331eea68b067194a29448c9fa4ba90baaca2be22 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Mon, 24 Jun 2019 18:15:45 +0300 Subject: [PATCH 057/332] Update version to 0.2.0 This is needed for publishing a new version on crates.io. Changes since 0.1.0: - Fix bug in `KvmRunWrapper`. The memory for `kvm_run` struct was not unmapped after the `KvmRunWrapper` object got out of scope. - Return proper value when receiving the EOI KVM exit. - Add support for KVM_ENABLE_CAP. - Add support for KVM_SIGNAL_MSI. - Mark `set_user_memory_region` as unsafe. Signed-off-by: Andreea Florescu --- Cargo.lock | 4 +++- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4652cd1..7e9a439 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,3 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. [[package]] name = "byteorder" version = "1.3.1" @@ -10,7 +12,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "kvm-ioctls" -version = "0.1.0" +version = "0.2.0" dependencies = [ "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "kvm-bindings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 5911fee..bda7ac0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kvm-ioctls" -version = "0.1.0" +version = "0.2.0" authors = ["Amazon Firecracker Team "] description = "Safe wrappers over KVM ioctls" repository = "https://github.com/rust-vmm/kvm-ioctls" From 28e9e0a56266f915cc42d019e6aff38d5bc8a05d Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Thu, 6 Jun 2019 15:43:05 +0300 Subject: [PATCH 058/332] removed pipeline and integration tests The pipeline and integration tests are moved to rust-vmm-ci. Also removed the reference about the test profiles and adaptive coverage as these are moved to the rust-vmm-ci readme. Signed-off-by: Andreea Florescu --- .buildkite/pipeline.yml | 174 ---------------------------------------- README.md | 53 +----------- tests/conftest.py | 28 ------- tests/coverage | 1 - tests/test_coverage.py | 108 ------------------------- 5 files changed, 2 insertions(+), 362 deletions(-) delete mode 100644 .buildkite/pipeline.yml delete mode 100644 tests/conftest.py delete mode 100644 tests/coverage delete mode 100644 tests/test_coverage.py diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml deleted file mode 100644 index 38e784b..0000000 --- a/.buildkite/pipeline.yml +++ /dev/null @@ -1,174 +0,0 @@ -steps: - - label: "build-gnu-x86" - commands: - - cargo build --release - retry: - automatic: false - agents: - platform: x86_64.metal - plugins: - - docker#v3.0.1: - image: "rustvmm/dev:v2" - always-pull: true - - - label: "build-gnu-arm" - commands: - - cargo build --release - retry: - automatic: false - agents: - platform: arm.metal - plugins: - - docker#v3.0.1: - image: "rustvmm/dev:v2" - always-pull: true - - - label: "build-musl-x86" - commands: - - cargo build --release --target x86_64-unknown-linux-musl - retry: - automatic: false - agents: - platform: x86_64.metal - plugins: - - docker#v3.0.1: - image: "rustvmm/dev:v2" - always-pull: true - - - label: "build-musl-arm" - commands: - - cargo build --release --target aarch64-unknown-linux-musl - retry: - automatic: false - agents: - platform: arm.metal - plugins: - - docker#v3.0.1: - image: "rustvmm/dev:v2" - always-pull: true - - - label: "style" - command: cargo fmt --all -- --check - retry: - automatic: false - agents: - platform: x86_64.metal - plugins: - - docker#v3.0.1: - image: "rustvmm/dev:v2" - always-pull: true - - - label: "unittests-gnu-x86" - commands: - - cargo test - retry: - automatic: false - agents: - platform: x86_64.metal - plugins: - - docker#v3.0.1: - privileged: true - image: "rustvmm/dev:v2" - always-pull: true - - - label: "unittests-gnu-arm" - commands: - - cargo test - retry: - automatic: false - agents: - platform: arm.metal - plugins: - - docker#v3.0.1: - privileged: true - image: "rustvmm/dev:v2" - always-pull: true - - - label: "unittests-musl-x86" - command: - - cargo test --target x86_64-unknown-linux-musl - retry: - automatic: false - agents: - platform: x86_64.metal - plugins: - - docker#v3.0.1: - privileged: true - image: "rustvmm/dev:v2" - always-pull: true - - - label: "unittests-musl-arm" - command: - - cargo test --target aarch64-unknown-linux-musl - retry: - automatic: false - agents: - platform: arm.metal - plugins: - - docker#v3.0.1: - privileged: true - image: "rustvmm/dev:v2" - always-pull: true - - - label: "clippy-x86" - commands: - - cargo clippy --all -- -D warnings - retry: - automatic: false - agents: - platform: x86_64.metal - plugins: - - docker#v3.0.1: - image: "rustvmm/dev:v2" - always-pull: true - - - label: "clippy-arm" - commands: - - cargo clippy --all -- -D warnings - retry: - automatic: false - agents: - platform: arm.metal - plugins: - - docker#v3.0.1: - image: "rustvmm/dev:v2" - always-pull: true - - - label: "coverage-x86" - commands: - - pytest tests/test_coverage.py - retry: - automatic: false - agents: - platform: x86_64.metal - plugins: - - docker#v3.0.1: - privileged: true - image: "rustvmm/dev:v2" - always-pull: true - - - label: "check-warnings-x86" - commands: - - RUSTFLAGS="-D warnings" cargo check --all-targets - retry: - automatic: false - agents: - platform: x86_64.metal - plugins: - - docker#v3.0.1: - privileged: true - image: "rustvmm/dev:v2" - always-pull: true - - - label: "check-warnings-arm" - command: - - RUSTFLAGS="-D warnings" cargo check --all-targets - retry: - automatic: false - agents: - platform: arm.metal - plugins: - - docker#v3.0.1: - privileged: true - image: "rustvmm/dev:v2" - always-pull: true \ No newline at end of file diff --git a/README.md b/README.md index 6d861b3..e010da9 100644 --- a/README.md +++ b/README.md @@ -43,54 +43,5 @@ cd kvm-ioctls/ cargo test ``` -### Test Profiles - -The integration tests support two test profiles: -- **devel**: this is the recommended profile for running the integration tests - on a local development machine. -- **ci** (default option): this is the profile used when running the - integration tests as part of the the Continuous Integration (CI). - -The test profiles are applicable to tests that run using pytest. Currently only -the [coverage test](tests/test_coverage.py) follows this model as all the other -integration tests are run using the -[Buildkite pipeline](https://buildkite.com/rust-vmm/kvm-ioctls-ci). - -The difference between is declaring tests as passed or failed: -- with the **devel** profile the coverage test passes if the current coverage - is equal or higher than the upstream coverage value. In case the current - coverage is higher, the coverage file is updated to the new coverage value. -- with the **ci** profile the coverage test passes only if the current coverage - is equal to the upstream coverage value. - -Further details about the coverage test can be found in the -[Adaptive Coverage](#adaptive-coverage) section. - -### Adaptive Coverage - -The line coverage is saved in [tests/coverage](tests/coverage). To update the -coverage before submitting a PR, run the coverage test: - -```bash -docker run --device=/dev/kvm \ - -it \ - --security-opt seccomp=unconfined \ - --volume $(pwd)/kvm-ioctls:/kvm-ioctls \ - rustvmm/dev:v2 -cd kvm-ioctls/ -pytest --profile=devel tests/test_coverage.py -``` - -If the PR coverage is higher than the upstream coverage, the coverage file -needs to be manually added to the commit before submitting the PR: - -```bash -git add tests/coverage -``` - -Failing to do so will generate a fail on the CI pipeline when publishing the -PR. - -**NOTE:** The coverage file is only updated in the `devel` test profile. In -the `ci` profile the coverage test will fail if the current coverage is higher -than the coverage reported in [tests/coverage](tests/coverage). +For more details about the integration tests that are run for `kvm-ioctls`, +check the [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) readme. \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py deleted file mode 100644 index 58aef2d..0000000 --- a/tests/conftest.py +++ /dev/null @@ -1,28 +0,0 @@ -import pytest - - -PROFILE_CI="ci" -PROFILE_DEVEL="devel" - - -def pytest_addoption(parser): - parser.addoption( - "--profile", - default=PROFILE_CI, - choices=[PROFILE_CI, PROFILE_DEVEL], - help="Profile for running the test: {} or {}".format( - PROFILE_CI, - PROFILE_DEVEL - ) - ) - - -@pytest.fixture -def profile(request): - return request.config.getoption("--profile") - - -# This is used for defining global variables in pytest. -def pytest_configure(): - pytest.profile_ci = PROFILE_CI - pytest.profile_devel = PROFILE_DEVEL diff --git a/tests/coverage b/tests/coverage deleted file mode 100644 index 7fe4e49..0000000 --- a/tests/coverage +++ /dev/null @@ -1 +0,0 @@ -91 diff --git a/tests/test_coverage.py b/tests/test_coverage.py deleted file mode 100644 index 672a734..0000000 --- a/tests/test_coverage.py +++ /dev/null @@ -1,108 +0,0 @@ -# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: Apache-2.0 OR MIT -"""Test the coverage and update the threshold when coverage is increased.""" - -import os, re, shutil, subprocess -import pytest - -def _get_current_coverage(): - """Helper function that returns the coverage computed with kcov.""" - kcov_ouput_dir = os.path.join( - os.path.dirname(os.path.realpath(__file__)), - "kcov_output" - ) - - # By default the build output for kcov and unit tests are both in the debug - # directory. This causes some linker errors that I haven't investigated. - # Error: error: linking with `cc` failed: exit code: 1 - # An easy fix is to have separate build directories for kcov & unit tests. - kcov_build_dir = os.path.join( - os.path.dirname(os.path.realpath(__file__)), - "kcov_build" - ) - - # Remove kcov output and build directory to be sure we are always working - # on a clean environment. - shutil.rmtree(kcov_ouput_dir, ignore_errors=True) - shutil.rmtree(kcov_build_dir, ignore_errors=True) - - exclude_pattern = ( - '${CARGO_HOME:-$HOME/.cargo/},' - 'usr/lib/,' - 'lib/' - ) - exclude_region = "'mod tests {'" - - kcov_cmd = "CARGO_TARGET_DIR={} cargo kcov --all " \ - "--output {} -- " \ - "--exclude-region={} " \ - "--exclude-pattern={} " \ - "--verify".format( - kcov_build_dir, - kcov_ouput_dir, - exclude_region, - exclude_pattern - ) - - subprocess.run(kcov_cmd, shell=True, check=True) - - # Read the coverage reported by kcov. - coverage_file = os.path.join(kcov_ouput_dir, 'index.js') - with open(coverage_file) as cov_output: - coverage = float(re.findall( - r'"covered":"(\d+\.\d)"', - cov_output.read() - )[0]) - - # Remove coverage related directories. - shutil.rmtree(kcov_ouput_dir, ignore_errors=True) - shutil.rmtree(kcov_build_dir, ignore_errors=True) - - return coverage - - -def _get_previous_coverage(): - """Helper function that returns the last reported coverage.""" - coverage_path = os.path.join( - os.path.dirname(os.path.realpath(__file__)), - 'coverage' - ) - - # The first and only line of the file contains the coverage. - with open(coverage_path) as f: - coverage = f.readline() - return float(coverage.strip()) - -def _update_coverage(cov_value): - """Updates the coverage in the coverage file.""" - coverage_path = os.path.join( - os.path.dirname(os.path.realpath(__file__)), - 'coverage' - ) - - with open(coverage_path, "w") as f: - f.write(str(cov_value)) - -def test_coverage(profile): - current_coverage = _get_current_coverage() - previous_coverage = _get_previous_coverage() - if previous_coverage < current_coverage: - if profile == pytest.profile_ci: - # In the CI Profile we expect the coverage to be manually updated. - assert False, "Coverage is increased from {} to {}. " \ - "Please update the coverage in " \ - "tests/coverage.".format( - previous_coverage, - current_coverage - ) - elif profile == pytest.profile_devel: - _update_coverage(current_coverage) - else: - # This should never happen because pytest should only accept - # the valid test profiles specified with `choices` in - # `pytest_addoption`. - assert False, "Invalid test profile." - elif previous_coverage > current_coverage: - diff = float(previous_coverage - current_coverage) - assert False, "Coverage drops by {:.2f}%. Please add unit tests for" \ - "the uncovered lines.".format(diff) From 1a3383a78ad3af84d9bf8706926693c2c394493a Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Wed, 26 Jun 2019 15:21:35 +0300 Subject: [PATCH 059/332] switch CI to rust-vmm-ci Use the latest master branch for the Buildkite Pipeline and the integration tests run on kvm-ioctls. Signed-off-by: Andreea Florescu --- .gitmodules | 3 +++ coverage_config.json | 5 +++++ rust-vmm-ci | 1 + 3 files changed, 9 insertions(+) create mode 100644 .gitmodules create mode 100644 coverage_config.json create mode 160000 rust-vmm-ci diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..bda97eb --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "rust-vmm-ci"] + path = rust-vmm-ci + url = https://github.com/rust-vmm/rust-vmm-ci.git diff --git a/coverage_config.json b/coverage_config.json new file mode 100644 index 0000000..7651430 --- /dev/null +++ b/coverage_config.json @@ -0,0 +1,5 @@ +{ + "coverage_score": 91.0, + "exclude_path": "", + "crate_features": "" +} diff --git a/rust-vmm-ci b/rust-vmm-ci new file mode 160000 index 0000000..6c471c6 --- /dev/null +++ b/rust-vmm-ci @@ -0,0 +1 @@ +Subproject commit 6c471c6c3aa8d56f6ef8022840feede01f61c263 From c024055f7fe6a18508a05c0dbc905d300f91e003 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Mon, 1 Jul 2019 11:52:37 +0300 Subject: [PATCH 060/332] update rust-vmm-ci submodule rust-vmm-ci log: 8b60273 Run tests with rustvmm/dev:v3 Signed-off-by: Andreea Florescu --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 6c471c6..8b60273 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 6c471c6c3aa8d56f6ef8022840feede01f61c263 +Subproject commit 8b60273cef886444a5bbc27f9c809b6d8806a58e From 37669f60a04a65b9e81cc62ac4a484ea0ed421ca Mon Sep 17 00:00:00 2001 From: Sebastien Boeuf Date: Tue, 9 Jul 2019 14:47:45 -0700 Subject: [PATCH 061/332] Add support for KVM_SET_GSI_ROUTING ioctl By adding the support for the ioctl KVM_SET_GSI_ROUTING, this commit will allow consumers of the kvm-ioctls crate to set custom mappings for GSI entries. This would let a caller of this ioctl defines that GSI number X can trigger an interrupt by delivering an MSI directly to the local APIC or instead deliver a pin based interrupt to the in-kernel IOAPIC. One example that comes to mind is to use this ioctl to let a VMM interact with the VFIO API, as it requires an eventfd to be associated with each interrupt. By combining this ioctl with KVM_IRQFD, one could directly map an interrupt to an eventfd managed by KVM, and associated to a specific GSI. Signed-off-by: Sebastien Boeuf --- coverage_config.json | 2 +- src/ioctls/vm.rs | 62 ++++++++++++++++++++++++++++++++++++++++++++ src/kvm_ioctls.rs | 7 +++++ 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/coverage_config.json b/coverage_config.json index 7651430..8b79dcf 100644 --- a/coverage_config.json +++ b/coverage_config.json @@ -1,5 +1,5 @@ { - "coverage_score": 91.0, + "coverage_score": 91.1, "exclude_path": "", "crate_features": "" } diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 390758b..c4b4d60 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -254,6 +254,54 @@ impl VmFd { } } + /// Sets the GSI routing table entries, overwriting any previously set + /// entries, as per the `KVM_SET_GSI_ROUTING` ioctl. + /// + /// See the documentation for `KVM_SET_GSI_ROUTING`. + /// + /// Returns an io::Error when the table could not be updated. + /// + /// # Arguments + /// + /// * kvm_irq_routing - IRQ routing configuration. Describe all routes + /// associated with GSI entries. For details check + /// the `kvm_irq_routing` and `kvm_irq_routing_entry` + /// structures in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// extern crate kvm_bindings; + /// # use kvm_ioctls::{Kvm, VmFd}; + /// use kvm_bindings::kvm_irq_routing; + /// + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// vm.create_irq_chip().unwrap(); + /// + /// let irq_routing = kvm_irq_routing::default(); + /// vm.set_gsi_routing(&irq_routing).unwrap(); + /// ``` + /// + #[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" + ))] + pub fn set_gsi_routing(&self, irq_routing: &kvm_irq_routing) -> Result<()> { + // Safe because we allocated the structure and we know the kernel + // will read exactly the size of the structure. + let ret = unsafe { ioctl_with_ref(self, KVM_SET_GSI_ROUTING(), irq_routing) }; + if ret == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + /// Registers an event to be signaled whenever a certain address is written to. /// /// See the documentation for `KVM_IOEVENTFD`. @@ -901,4 +949,18 @@ mod tests { cap.args[0] = 24; assert!(vm.enable_cap(&cap).is_ok()); } + + #[test] + fn test_set_gsi_routing() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + if cfg!(target_arch = "x86") || cfg!(target_arch = "x86_64") { + let irq_routing = kvm_irq_routing::default(); + // Expect failure for x86 since the irqchip is not created yet. + assert!(vm.set_gsi_routing(&irq_routing).is_err()); + vm.create_irq_chip().unwrap(); + } + let irq_routing = kvm_irq_routing::default(); + assert!(vm.set_gsi_routing(&irq_routing).is_ok()); + } } diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index 1113527..80d6579 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -36,6 +36,13 @@ ioctl_io_nr!(KVM_SET_TSS_ADDR, KVMIO, 0x47); target_arch = "s390" ))] ioctl_io_nr!(KVM_CREATE_IRQCHIP, KVMIO, 0x60); +#[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" +))] +ioctl_iow_nr!(KVM_SET_GSI_ROUTING, KVMIO, 0x6a, kvm_irq_routing); #[cfg(any( target_arch = "x86", target_arch = "x86_64", From 30adb021584845eed4124bc8ef3b2b96cd9e159f Mon Sep 17 00:00:00 2001 From: Liu Jiang Date: Sun, 11 Aug 2019 00:52:22 +0800 Subject: [PATCH 062/332] Add unregister_irqfd() to VmFd Add unregister_irqfd() to VmFd to unregister an irqfd from the KVM, which will be needed when enabling supoort of MSI. Signed-off-by: Liu Jiang --- coverage_config.json | 2 +- src/ioctls/vm.rs | 82 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/coverage_config.json b/coverage_config.json index 8b79dcf..eba81b3 100644 --- a/coverage_config.json +++ b/coverage_config.json @@ -1,5 +1,5 @@ { - "coverage_score": 91.1, + "coverage_score": 91.3, "exclude_path": "", "crate_features": "" } diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index c4b4d60..57fb222 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -535,6 +535,52 @@ impl VmFd { } } + /// Unregisters an event that will, when signaled, trigger the `gsi` IRQ. + /// + /// # Arguments + /// + /// * `fd` - Event to be signaled. + /// * `gsi` - IRQ to be triggered. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # extern crate libc; + /// # use kvm_ioctls::{Kvm, VmFd}; + /// # use libc::{eventfd, EFD_NONBLOCK}; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let evtfd = unsafe { eventfd(0, EFD_NONBLOCK) }; + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// vm.register_irqfd(evtfd, 0).unwrap(); + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// vm.unregister_irqfd(evtfd, 0).unwrap(); + /// ``` + /// + #[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" + ))] + pub fn unregister_irqfd(&self, fd: RawFd, gsi: u32) -> Result<()> { + let irqfd = kvm_irqfd { + fd: fd as u32, + gsi, + flags: KVM_IRQFD_FLAG_DEASSIGN, + ..Default::default() + }; + // Safe because we know that our file is a VM fd, we know the kernel will only read the + // correct amount of memory from our pointer, and we verify the return result. + let ret = unsafe { ioctl_with_ref(self, KVM_IRQFD(), &irqfd) }; + if ret == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + /// Creates a new KVM vCPU file descriptor and maps the memory corresponding /// its `kvm_run` structure. /// @@ -844,6 +890,42 @@ mod tests { assert!(vm_fd.register_irqfd(evtfd3, 5).is_err()); } + #[test] + #[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" + ))] + fn test_unregister_irqfd() { + let kvm = Kvm::new().unwrap(); + let vm_fd = kvm.create_vm().unwrap(); + let evtfd1 = unsafe { eventfd(0, EFD_NONBLOCK) }; + let evtfd2 = unsafe { eventfd(0, EFD_NONBLOCK) }; + let evtfd3 = unsafe { eventfd(0, EFD_NONBLOCK) }; + if cfg!(any(target_arch = "x86", target_arch = "x86_64")) { + assert!(vm_fd.register_irqfd(evtfd1, 4).is_ok()); + assert!(vm_fd.register_irqfd(evtfd2, 8).is_ok()); + assert!(vm_fd.register_irqfd(evtfd3, 4).is_ok()); + assert!(vm_fd.unregister_irqfd(evtfd2, 8).is_ok()); + // KVM irqfd doesn't report failure on this case:( + assert!(vm_fd.unregister_irqfd(evtfd2, 8).is_ok()); + } + + // Duplicated eventfd registration. + // On aarch64, this fails because setting up the interrupt controller is mandatory before + // registering any IRQ. + // On x86_64 this fails as the event fd was already matched with a GSI. + assert!(vm_fd.register_irqfd(evtfd3, 4).is_err()); + assert!(vm_fd.register_irqfd(evtfd3, 5).is_err()); + + // KVM irqfd doesn't report failure on this case:( + assert!(vm_fd.unregister_irqfd(evtfd3, 5).is_ok()); + + // Check for invalid fd. + assert!(vm_fd.unregister_irqfd(-1, 5).is_err()); + } + #[test] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn test_faulty_vm_fd() { From 2b30729635a81f0820bc539d109377225067cf6e Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Tue, 8 Oct 2019 13:21:35 +0200 Subject: [PATCH 063/332] add a changelog Added a changelog with details about the 2 past releases of kvm-ioctls. The changelog follows the format defined in https://keepachangelog.com/en/1.0.0/. Signed-off-by: Andreea Florescu --- CHANGELOG.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..bcaca65 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,26 @@ +# v0.2.0 + +## Added +- Add support for `KVM_ENABLE_CAP`. +- Add support for `KVM_SIGNAL_MSI`. + +## Fixed +- Fix bug in KvmRunWrapper. The memory for kvm_run struct was not unmapped + after the KvmRunWrapper object got out of scope. +- Return proper value when receiving the EOI KVM exit. +- Mark set_user_memory_region as unsafe. + +# v0.1.0 + +First release of the kvm-ioctls crate. + +The kvm-ioctls crate provides safe wrappers over the KVM API, a set of ioctls +used for creating and configuring Virtual Machines (VMs) on Linux. +The ioctls are accessible through four structures: +- Kvm - wrappers over system ioctls +- VmFd - wrappers over VM ioctls +- VcpuFd - wrappers over vCPU ioctls +- DeviceFd - wrappers over device ioctls + +The kvm-ioctls can be used on x86_64 and aarch64. Right now the aarch64 +support is considered experimental. From 8ead44e3af3f803d3a1c228755db54fc004aa20d Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Wed, 14 Aug 2019 14:37:48 +0300 Subject: [PATCH 064/332] reimplement CpuId as a FamStructWrapper The CpuId type was implemented as a wrapper over the kvm_cpuid2 structure. Since kvm_cpuid2 contains a flexible array member, we can reimplement CpuId as a FamStructWrapper. Signed-off-by: Serban Iorga --- Cargo.lock | 10 ++ Cargo.toml | 1 + coverage_config.json | 2 +- src/ioctls/mod.rs | 214 ++++++++++--------------------------------- src/ioctls/system.rs | 14 +-- src/ioctls/vcpu.rs | 6 +- src/lib.rs | 10 +- 7 files changed, 75 insertions(+), 182 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7e9a439..177b0a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,7 @@ dependencies = [ "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "kvm-bindings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "vmm-sys-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -24,7 +25,16 @@ name = "libc" version = "0.2.48" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "vmm-sys-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", +] + [metadata] "checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" "checksum kvm-bindings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c223e8703d2eb76d990c5f58e29c85b0f6f50e24b823babde927948e7c71fc03" "checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047" +"checksum vmm-sys-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "46996f56aeae31fbc0532ae57a944e00089302f03b18c10c76eebfd9249f4a6c" diff --git a/Cargo.toml b/Cargo.toml index bda7ac0..584e7ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ license = "Apache-2.0 OR MIT" [dependencies] libc = ">=0.2.39" kvm-bindings = ">=0.1.0" +vmm-sys-util = ">=0.1.1" [dev-dependencies] byteorder = ">=1.2.1" diff --git a/coverage_config.json b/coverage_config.json index eba81b3..7651430 100644 --- a/coverage_config.json +++ b/coverage_config.json @@ -1,5 +1,5 @@ { - "coverage_score": 91.3, + "coverage_score": 91.0, "exclude_path": "", "crate_features": "" } diff --git a/src/ioctls/mod.rs b/src/ioctls/mod.rs index 1861eb3..c56a611 100644 --- a/src/ioctls/mod.rs +++ b/src/ioctls/mod.rs @@ -15,6 +15,8 @@ use std::result; use kvm_bindings::kvm_run; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use kvm_bindings::{kvm_cpuid2, kvm_cpuid_entry2}; +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +use vmm_sys_util::fam::{FamStruct, FamStructWrapper}; /// Wrappers over KVM device ioctls. pub mod device; @@ -64,159 +66,76 @@ fn vec_with_array_field(count: usize) -> Vec { vec_with_size_in_bytes(vec_size_bytes) } -/// Wrapper over the `kvm_cpuid2` structure. +/// Maximum number of CPUID entries that can be returned by a call to KVM ioctls. /// -/// The structure has a zero length array at the end, hidden behind bounds check. +/// This value is taken from Linux Kernel v4.14.13 (arch/x86/include/asm/kvm_host.h). +/// It can be used for calls to [get_supported_cpuid](struct.Kvm.html#method.get_supported_cpuid) and +/// [get_emulated_cpuid](struct.Kvm.html#method.get_emulated_cpuid). #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -pub struct CpuId { - // Wrapper over `kvm_cpuid2` from which we only use the first element. - kvm_cpuid: Vec, - // Number of `kvm_cpuid_entry2` structs at the end of kvm_cpuid2. - allocated_len: usize, -} +pub const MAX_KVM_CPUID_ENTRIES: usize = 80; +// We can't implement FamStruct directly for kvm_cpuid2. +// We would get an "impl doesn't use types inside crate" error. +// We have to create a shadow structure as a workaround. +#[derive(Default)] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -impl Clone for CpuId { - fn clone(&self) -> Self { - let mut kvm_cpuid = Vec::with_capacity(self.kvm_cpuid.len()); - for _ in 0..self.kvm_cpuid.len() { - kvm_cpuid.push(kvm_cpuid2::default()); - } - - let num_bytes = self.kvm_cpuid.len() * size_of::(); - - let src_byte_slice = - unsafe { std::slice::from_raw_parts(self.kvm_cpuid.as_ptr() as *const u8, num_bytes) }; - - let dst_byte_slice = - unsafe { std::slice::from_raw_parts_mut(kvm_cpuid.as_mut_ptr() as *mut u8, num_bytes) }; - - dst_byte_slice.copy_from_slice(src_byte_slice); - - CpuId { - kvm_cpuid, - allocated_len: self.allocated_len, - } - } -} +pub struct KvmCpuId(kvm_cpuid2); -#[cfg(test)] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -impl PartialEq for CpuId { - fn eq(&self, other: &CpuId) -> bool { - let entries: &[kvm_cpuid_entry2] = - unsafe { self.kvm_cpuid[0].entries.as_slice(self.allocated_len) }; - let other_entries: &[kvm_cpuid_entry2] = - unsafe { self.kvm_cpuid[0].entries.as_slice(other.allocated_len) }; - self.allocated_len == other.allocated_len && entries == other_entries +impl Clone for KvmCpuId { + fn clone(&self) -> Self { + let KvmCpuId(cpuid) = self; + KvmCpuId(kvm_cpuid2 { + nent: cpuid.nent, + padding: cpuid.padding, + entries: cpuid.entries.clone(), + }) } } #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -impl CpuId { - /// Creates a new `CpuId` structure that contains at most `array_len` KVM CPUID entries. - /// - /// # Arguments - /// - /// * `array_len` - Maximum number of CPUID entries. - /// - /// # Example - /// - /// ``` - /// use kvm_ioctls::CpuId; - /// let cpu_id = CpuId::new(32); - /// ``` - pub fn new(array_len: usize) -> CpuId { - let mut kvm_cpuid = vec_with_array_field::(array_len); - kvm_cpuid[0].nent = array_len as u32; +unsafe impl FamStruct for KvmCpuId { + type Entry = kvm_cpuid_entry2; - CpuId { - kvm_cpuid, - allocated_len: array_len, - } + fn len(&self) -> usize { + let KvmCpuId(cpuid) = self; + cpuid.nent as usize } - /// Creates a new `CpuId` structure based on a supplied vector of `kvm_cpuid_entry2`. - /// - /// # Arguments - /// - /// * `entries` - The vector of `kvm_cpuid_entry2` entries. - /// - /// # Example - /// - /// ```rust - /// # extern crate kvm_ioctls; - /// extern crate kvm_bindings; - /// - /// use kvm_bindings::kvm_cpuid_entry2; - /// use kvm_ioctls::CpuId; - /// // Create a Cpuid to hold one entry. - /// let mut cpuid = CpuId::new(1); - /// let mut entries = cpuid.mut_entries_slice().to_vec(); - /// let new_entry = kvm_cpuid_entry2 { - /// function: 0x4, - /// index: 0, - /// flags: 1, - /// eax: 0b1100000, - /// ebx: 0, - /// ecx: 0, - /// edx: 0, - /// padding: [0, 0, 0], - /// }; - /// entries.insert(0, new_entry); - /// cpuid = CpuId::from_entries(&entries); - /// ``` - /// - pub fn from_entries(entries: &[kvm_cpuid_entry2]) -> CpuId { - let mut kvm_cpuid = vec_with_array_field::(entries.len()); - kvm_cpuid[0].nent = entries.len() as u32; - - unsafe { - kvm_cpuid[0] - .entries - .as_mut_slice(entries.len()) - .copy_from_slice(entries); - } - - CpuId { - kvm_cpuid, - allocated_len: entries.len(), - } + fn set_len(&mut self, len: usize) { + let KvmCpuId(cpuid) = self; + cpuid.nent = len as u32; } - /// Returns the mutable entries slice so they can be modified before passing to the VCPU. - /// - /// # Example - /// ```rust - /// use kvm_ioctls::{CpuId, Kvm, MAX_KVM_CPUID_ENTRIES}; - /// let kvm = Kvm::new().unwrap(); - /// let mut cpuid = kvm.get_supported_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); - /// let cpuid_entries = cpuid.mut_entries_slice(); - /// ``` - /// - pub fn mut_entries_slice(&mut self) -> &mut [kvm_cpuid_entry2] { - // Mapping the unsized array to a slice is unsafe because the length isn't known. Using - // the length we originally allocated with eliminates the possibility of overflow. - if self.kvm_cpuid[0].nent as usize > self.allocated_len { - self.kvm_cpuid[0].nent = self.allocated_len as u32; - } - let nent = self.kvm_cpuid[0].nent as usize; - unsafe { self.kvm_cpuid[0].entries.as_mut_slice(nent) } + fn max_len() -> usize { + MAX_KVM_CPUID_ENTRIES } - /// Get a pointer so it can be passed to the kernel. Using this pointer is unsafe. - /// - pub fn as_ptr(&self) -> *const kvm_cpuid2 { - &self.kvm_cpuid[0] + fn as_slice(&self) -> &[Self::Entry] { + let len = self.len(); + let KvmCpuId(cpuid) = self; + // This is safe because the provided length is correct. + unsafe { cpuid.entries.as_slice(len) } } - /// Get a mutable pointer so it can be passed to the kernel. Using this pointer is unsafe. - /// - pub fn as_mut_ptr(&mut self) -> *mut kvm_cpuid2 { - &mut self.kvm_cpuid[0] + fn as_mut_slice(&mut self) -> &mut [Self::Entry] { + let len = self.len(); + let KvmCpuId(cpuid) = self; + // This is safe because the provided length is correct. + unsafe { cpuid.entries.as_mut_slice(len) } } } +/// Wrapper over the `kvm_cpuid2` structure. +/// +/// The `kvm_cpuid2` structure contains a flexible array member. For details check the +/// [KVM API](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt) +/// documentation on `kvm_cpuid2`. To provide safe access to +/// the array elements, this type is implemented using +/// [FamStructWrapper](../vmm_sys_util/fam/struct.FamStructWrapper.html). +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +pub type CpuId = FamStructWrapper; + /// Safe wrapper over the `kvm_run` struct. /// /// The wrapper is needed for sending the pointer to `kvm_run` between @@ -285,36 +204,3 @@ impl Drop for KvmRunWrapper { } } } - -#[cfg(test)] -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -mod tests { - use super::*; - - #[test] - fn test_cpuid_from_entries() { - let num_entries = 4; - let mut cpuid = CpuId::new(num_entries); - - // add entry - let mut entries = cpuid.mut_entries_slice().to_vec(); - let new_entry = kvm_cpuid_entry2 { - function: 0x4, - index: 0, - flags: 1, - eax: 0b1100000, - ebx: 0, - ecx: 0, - edx: 0, - padding: [0, 0, 0], - }; - entries.insert(0, new_entry); - cpuid = CpuId::from_entries(&entries); - - // check that the cpuid contains the new entry - assert_eq!(cpuid.allocated_len, num_entries + 1); - assert_eq!(cpuid.kvm_cpuid[0].nent, (num_entries + 1) as u32); - assert_eq!(cpuid.mut_entries_slice().len(), num_entries + 1); - assert_eq!(cpuid.mut_entries_slice()[0], new_entry); - } -} diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index 9d0ef3c..5f1e001 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -242,7 +242,7 @@ impl Kvm { // ioctl is unsafe. The kernel is trusted not to write beyond the bounds of the memory // allocated for the struct. The limit is read from nent, which is set to the allocated // size(max_entries_count) above. - ioctl_with_mut_ptr(self, kind, cpuid.as_mut_ptr()) + ioctl_with_mut_ptr(self, kind, cpuid.as_mut_fam_struct_ptr()) }; if ret < 0 { return Err(io::Error::last_os_error()); @@ -267,7 +267,7 @@ impl Kvm { /// /// let kvm = Kvm::new().unwrap(); /// let mut cpuid = kvm.get_emulated_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); - /// let cpuid_entries = cpuid.mut_entries_slice(); + /// let cpuid_entries = cpuid.as_mut_slice(); /// assert!(cpuid_entries.len() <= MAX_KVM_CPUID_ENTRIES); /// ``` /// @@ -292,7 +292,7 @@ impl Kvm { /// /// let kvm = Kvm::new().unwrap(); /// let mut cpuid = kvm.get_emulated_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); - /// let cpuid_entries = cpuid.mut_entries_slice(); + /// let cpuid_entries = cpuid.as_mut_slice(); /// assert!(cpuid_entries.len() <= MAX_KVM_CPUID_ENTRIES); /// ``` /// @@ -385,6 +385,8 @@ impl AsRawFd for Kvm { mod tests { use super::*; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + use vmm_sys_util::fam::FamStruct; + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use MAX_KVM_CPUID_ENTRIES; #[test] @@ -427,7 +429,7 @@ mod tests { fn test_get_supported_cpuid() { let kvm = Kvm::new().unwrap(); let mut cpuid = kvm.get_supported_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); - let cpuid_entries = cpuid.mut_entries_slice(); + let cpuid_entries = cpuid.as_mut_slice(); assert!(cpuid_entries.len() > 0); assert!(cpuid_entries.len() <= MAX_KVM_CPUID_ENTRIES); } @@ -437,7 +439,7 @@ mod tests { fn test_get_emulated_cpuid() { let kvm = Kvm::new().unwrap(); let mut cpuid = kvm.get_emulated_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); - let cpuid_entries = cpuid.mut_entries_slice(); + let cpuid_entries = cpuid.as_mut_slice(); assert!(cpuid_entries.len() > 0); assert!(cpuid_entries.len() <= MAX_KVM_CPUID_ENTRIES); } @@ -449,7 +451,7 @@ mod tests { let cpuid_1 = kvm.get_supported_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); let mut cpuid_2 = cpuid_1.clone(); assert!(cpuid_1 == cpuid_2); - cpuid_2 = unsafe { std::mem::zeroed() }; + cpuid_2 = CpuId::new(cpuid_1.as_fam_struct_ref().len()); assert!(cpuid_1 != cpuid_2); } diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 4e01a7f..06dcee5 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -325,7 +325,7 @@ impl VcpuFd { /// // Update the CPUID entries to disable the EPB feature. /// const ECX_EPB_SHIFT: u32 = 3; /// { - /// let entries = kvm_cpuid.mut_entries_slice(); + /// let entries = kvm_cpuid.as_mut_slice(); /// for entry in entries.iter_mut() { /// match entry.function { /// 6 => entry.ecx &= !(1 << ECX_EPB_SHIFT), @@ -341,7 +341,7 @@ impl VcpuFd { pub fn set_cpuid2(&self, cpuid: &CpuId) -> Result<()> { let ret = unsafe { // Here we trust the kernel not to read past the end of the kvm_cpuid2 struct. - ioctl_with_ptr(self, KVM_SET_CPUID2(), cpuid.as_ptr()) + ioctl_with_ptr(self, KVM_SET_CPUID2(), cpuid.as_fam_struct_ptr()) }; if ret < 0 { return Err(io::Error::last_os_error()); @@ -803,7 +803,7 @@ mod tests { if kvm.check_extension(Cap::ExtCpuid) { let vm = kvm.create_vm().unwrap(); let mut cpuid = kvm.get_supported_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); - assert!(cpuid.mut_entries_slice().len() <= MAX_KVM_CPUID_ENTRIES); + assert!(cpuid.as_mut_slice().len() <= MAX_KVM_CPUID_ENTRIES); let nr_vcpus = kvm.get_nr_vcpus(); for cpu_id in 0..nr_vcpus { let vcpu = vm.create_vcpu(cpu_id as u8).unwrap(); diff --git a/src/lib.rs b/src/lib.rs index f8899e6..dd65b27 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -179,6 +179,7 @@ extern crate kvm_bindings; extern crate libc; +extern crate vmm_sys_util; #[macro_use] mod sys_ioctl; @@ -193,7 +194,7 @@ pub use ioctls::system::Kvm; pub use ioctls::vcpu::{VcpuExit, VcpuFd}; pub use ioctls::vm::{IoEventAddress, NoDatamatch, VmFd}; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -pub use ioctls::CpuId; +pub use ioctls::{CpuId, MAX_KVM_CPUID_ENTRIES}; // The following example is used to verify that our public // structures are exported properly. /// # Example @@ -203,10 +204,3 @@ pub use ioctls::CpuId; /// use kvm_ioctls::{KvmRunWrapper, Result}; /// ``` pub use ioctls::{KvmRunWrapper, Result}; - -/// Maximum number of CPUID entries that can be returned by a call to KVM ioctls. -/// -/// This value is taken from Linux Kernel v4.14.13 (arch/x86/include/asm/kvm_host.h). -/// It can be used for calls to [get_supported_cpuid](struct.Kvm.html#method.get_supported_cpuid) and -/// [get_emulated_cpuid](struct.Kvm.html#method.get_emulated_cpuid). -pub const MAX_KVM_CPUID_ENTRIES: usize = 80; From 2073af4101047422918a257481b36bcbd962a5ef Mon Sep 17 00:00:00 2001 From: Liu Jiang Date: Mon, 16 Sep 2019 19:48:06 +0800 Subject: [PATCH 065/332] Create Kvm/VmFd/VcpuFd/DeviceFd objects from RawFd When doing live-upgrading, some raw file descriptors will be pass from the old instance to the new instance, so provide methods to create KVM, VmFd, VcpuFd and DeviceFd objects from raw file descriptors. Signed-off-by: Liu Jiang --- coverage_config.json | 2 +- src/ioctls/device.rs | 18 +++++++++++++++++- src/ioctls/system.rs | 30 ++++++++++++++++++++++++++++++ src/ioctls/vm.rs | 28 ++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 2 deletions(-) diff --git a/coverage_config.json b/coverage_config.json index 7651430..161963c 100644 --- a/coverage_config.json +++ b/coverage_config.json @@ -1,5 +1,5 @@ { - "coverage_score": 91.0, + "coverage_score": 90.3, "exclude_path": "", "crate_features": "" } diff --git a/src/ioctls/device.rs b/src/ioctls/device.rs index 23491fb..9b50672 100644 --- a/src/ioctls/device.rs +++ b/src/ioctls/device.rs @@ -3,7 +3,7 @@ use std::fs::File; use std::io; -use std::os::unix::io::{AsRawFd, RawFd}; +use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use kvm_bindings::kvm_device_attr; @@ -44,6 +44,18 @@ impl AsRawFd for DeviceFd { } } +impl FromRawFd for DeviceFd { + /// This function is also unsafe as the primitives currently returned have the contract that + /// they are the sole owner of the file descriptor they are wrapping. Usage of this function + /// could accidentally allow violating this contract which can cause memory unsafety in code + /// that relies on it being true. + unsafe fn from_raw_fd(fd: RawFd) -> Self { + DeviceFd { + fd: File::from_raw_fd(fd), + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -83,6 +95,10 @@ mod tests { .create_device(&mut gic_device) .expect("Cannot create KVM device"); + let raw_fd = unsafe { libc::dup(device_fd.as_raw_fd()) }; + assert!(raw_fd >= 0); + let device_fd = unsafe { DeviceFd::from_raw_fd(raw_fd) }; + let dist_attr = kvm_bindings::kvm_device_attr { group: KVM_DEV_VFIO_GROUP, attr: KVM_DEV_VFIO_GROUP_ADD as u64, diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index 5f1e001..54c9be5 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -373,6 +373,17 @@ impl Kvm { Err(io::Error::last_os_error()) } } + + /// Creates a VmFd object from a VM RawFd. + /// + /// This function is unsafe as the primitives currently returned have the contract that + /// they are the sole owner of the file descriptor they are wrapping. Usage of this function + /// could accidentally allow violating this contract which can cause memory unsafety in code + /// that relies on it being true. + pub unsafe fn create_vmfd_from_rawfd(&self, fd: RawFd) -> Result { + let run_mmap_size = self.get_vcpu_mmap_size()?; + Ok(new_vmfd(File::from_raw_fd(fd), run_mmap_size)) + } } impl AsRawFd for Kvm { @@ -381,6 +392,14 @@ impl AsRawFd for Kvm { } } +impl FromRawFd for Kvm { + unsafe fn from_raw_fd(fd: RawFd) -> Self { + Kvm { + kvm: File::from_raw_fd(fd), + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -421,6 +440,11 @@ mod tests { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); + // Test create_vmfd_from_rawfd() + let rawfd = unsafe { libc::dup(vm.as_raw_fd()) }; + assert!(rawfd >= 0); + let vm = unsafe { kvm.create_vmfd_from_rawfd(rawfd).unwrap() }; + assert_eq!(vm.run_size(), kvm.get_vcpu_mmap_size().unwrap()); } @@ -448,6 +472,12 @@ mod tests { #[test] fn test_cpuid_clone() { let kvm = Kvm::new().unwrap(); + + // Test from_raw_fd() + let rawfd = unsafe { libc::dup(kvm.as_raw_fd()) }; + assert!(rawfd >= 0); + let kvm = unsafe { Kvm::from_raw_fd(rawfd) }; + let cpuid_1 = kvm.get_supported_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); let mut cpuid_2 = cpuid_1.clone(); assert!(cpuid_1 == cpuid_2); diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 57fb222..7491499 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -623,6 +623,34 @@ impl VmFd { Ok(new_vcpu(vcpu, kvm_run_ptr)) } + /// Creates a VcpuFd object from a vcpu RawFd. + /// + /// This function is unsafe as the primitives currently returned have the contract that + /// they are the sole owner of the file descriptor they are wrapping. Usage of this function + /// could accidentally allow violating this contract which can cause memory unsafety in code + /// that relies on it being true. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use std::os::unix::io::AsRawFd; + /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// // Create one vCPU with the ID=0. + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// let rawfd = unsafe { libc::dup(vcpu.as_raw_fd()) }; + /// assert!(rawfd >= 0); + /// let vcpu = unsafe { vm.create_vcpu_from_rawfd(rawfd).unwrap() }; + /// ``` + /// + pub unsafe fn create_vcpu_from_rawfd(&self, fd: RawFd) -> Result { + let vcpu = File::from_raw_fd(fd); + let kvm_run_ptr = KvmRunWrapper::mmap_from_fd(&vcpu, self.run_size)?; + Ok(new_vcpu(vcpu, kvm_run_ptr)) + } + /// Creates an emulated device in the kernel. /// /// See the documentation for `KVM_CREATE_DEVICE`. From 9b8f60dfa0de7fb2691090db055f34b7a36e91e9 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Tue, 15 Oct 2019 16:21:32 +0300 Subject: [PATCH 066/332] add comments with required capabilities to ioctl list Also split the defined KVM ioctls in `/dev/kvm` (system), `vm` and `vcpu` depending on what type of resource they work with. Signed-off-by: Adrian Catangiu --- src/kvm_ioctls.rs | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index 80d6579..dd0fce8 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -4,30 +4,39 @@ // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the THIRD-PARTY file. -#![allow(unused)] -use super::sys_ioctl::*; + +//! Declares necessary ioctls specific to their platform. + use kvm_bindings::*; -// Declares necessary ioctls specific to their platform. +// Ioctls for /dev/kvm. ioctl_io_nr!(KVM_GET_API_VERSION, KVMIO, 0x00); ioctl_io_nr!(KVM_CREATE_VM, KVMIO, 0x01); ioctl_io_nr!(KVM_CHECK_EXTENSION, KVMIO, 0x03); ioctl_io_nr!(KVM_GET_VCPU_MMAP_SIZE, KVMIO, 0x04); +/* Available with KVM_CAP_EXT_CPUID */ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_iowr_nr!(KVM_GET_SUPPORTED_CPUID, KVMIO, 0x05, kvm_cpuid2); +/* Available with KVM_CAP_EXT_EMUL_CPUID */ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_iowr_nr!(KVM_GET_EMULATED_CPUID, KVMIO, 0x09, kvm_cpuid2); + +// Ioctls for VM fds. + ioctl_io_nr!(KVM_CREATE_VCPU, KVMIO, 0x41); ioctl_iow_nr!(KVM_GET_DIRTY_LOG, KVMIO, 0x42, kvm_dirty_log); +/* Available with KVM_CAP_USER_MEMORY */ ioctl_iow_nr!( KVM_SET_USER_MEMORY_REGION, KVMIO, 0x46, kvm_userspace_memory_region ); +/* Available with KVM_CAP_SET_TSS_ADDR */ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_io_nr!(KVM_SET_TSS_ADDR, KVMIO, 0x47); +/* Available with KVM_CAP_IRQCHIP */ #[cfg(any( target_arch = "x86", target_arch = "x86_64", @@ -36,6 +45,7 @@ ioctl_io_nr!(KVM_SET_TSS_ADDR, KVMIO, 0x47); target_arch = "s390" ))] ioctl_io_nr!(KVM_CREATE_IRQCHIP, KVMIO, 0x60); +/* Available with KVM_CAP_IRQ_ROUTING */ #[cfg(any( target_arch = "x86", target_arch = "x86_64", @@ -43,6 +53,7 @@ ioctl_io_nr!(KVM_CREATE_IRQCHIP, KVMIO, 0x60); target_arch = "aarch64" ))] ioctl_iow_nr!(KVM_SET_GSI_ROUTING, KVMIO, 0x6a, kvm_irq_routing); +/* Available with KVM_CAP_IRQFD */ #[cfg(any( target_arch = "x86", target_arch = "x86_64", @@ -51,9 +62,14 @@ ioctl_iow_nr!(KVM_SET_GSI_ROUTING, KVMIO, 0x6a, kvm_irq_routing); target_arch = "s390" ))] ioctl_iow_nr!(KVM_IRQFD, KVMIO, 0x76, kvm_irqfd); +/* Available with KVM_CAP_PIT2 */ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_iow_nr!(KVM_CREATE_PIT2, KVMIO, 0x77, kvm_pit_config); +/* Available with KVM_CAP_IOEVENTFD */ ioctl_iow_nr!(KVM_IOEVENTFD, KVMIO, 0x79, kvm_ioeventfd); + +// Ioctls for VCPU fds. + ioctl_io_nr!(KVM_RUN, KVMIO, 0x80); #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] ioctl_ior_nr!(KVM_GET_REGS, KVMIO, 0x81, kvm_regs); @@ -73,6 +89,7 @@ ioctl_ior_nr!(KVM_GET_SREGS, KVMIO, 0x83, kvm_sregs); target_arch = "powerpc64" ))] ioctl_iow_nr!(KVM_SET_SREGS, KVMIO, 0x84, kvm_sregs); +/* Available with KVM_CAP_GET_MSR_FEATURES */ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_iowr_nr!(KVM_GET_MSR_INDEX_LIST, KVMIO, 0x02, kvm_msr_list); #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] @@ -80,17 +97,22 @@ ioctl_iowr_nr!(KVM_GET_MSRS, KVMIO, 0x88, kvm_msrs); #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_iow_nr!(KVM_SET_MSRS, KVMIO, 0x89, kvm_msrs); #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -ioctl_iow_nr!(KVM_SET_CPUID2, KVMIO, 0x90, kvm_cpuid2); -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_ior_nr!(KVM_GET_FPU, KVMIO, 0x8c, kvm_fpu); #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_iow_nr!(KVM_SET_FPU, KVMIO, 0x8d, kvm_fpu); +/* Available with KVM_CAP_IRQCHIP */ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_ior_nr!(KVM_GET_LAPIC, KVMIO, 0x8e, kvm_lapic_state); +/* Available with KVM_CAP_IRQCHIP */ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_iow_nr!(KVM_SET_LAPIC, KVMIO, 0x8f, kvm_lapic_state); +/* Available with KVM_CAP_EXT_CPUID */ +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_iow_nr!(KVM_SET_CPUID2, KVMIO, 0x90, kvm_cpuid2); +/* Available with KVM_CAP_ENABLE_CAP */ #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] ioctl_iow_nr!(KVM_ENABLE_CAP, KVMIO, 0xa3, kvm_enable_cap); +/* Available with KVM_CAP_SIGNAL_MSI */ #[cfg(any( target_arch = "x86", target_arch = "x86_64", @@ -98,13 +120,19 @@ ioctl_iow_nr!(KVM_ENABLE_CAP, KVMIO, 0xa3, kvm_enable_cap); target_arch = "aarch64" ))] ioctl_iow_nr!(KVM_SIGNAL_MSI, KVMIO, 0xa5, kvm_msi); +/* Available with KVM_CAP_ONE_REG */ #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] ioctl_iow_nr!(KVM_SET_ONE_REG, KVMIO, 0xac, kvm_one_reg); #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] ioctl_iow_nr!(KVM_ARM_VCPU_INIT, KVMIO, 0xae, kvm_vcpu_init); #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] ioctl_ior_nr!(KVM_ARM_PREFERRED_TARGET, KVMIO, 0xaf, kvm_vcpu_init); + +// Device ioctls. + +/* Available with KVM_CAP_DEVICE_CTRL */ ioctl_iowr_nr!(KVM_CREATE_DEVICE, KVMIO, 0xe0, kvm_create_device); +/* Available with KVM_CAP_VM_ATTRIBUTES */ ioctl_iow_nr!(KVM_SET_DEVICE_ATTR, KVMIO, 0xe1, kvm_device_attr); #[cfg(test)] @@ -114,6 +142,7 @@ mod tests { use libc::{c_char, open, O_RDWR}; + use super::super::sys_ioctl::*; use super::*; const KVM_PATH: &'static str = "/dev/kvm\0"; From 8f37a450e1615fc426ae8d3f9695d30e4184ee1b Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Wed, 16 Oct 2019 16:51:26 +0300 Subject: [PATCH 067/332] make KVM_GET_DIRTY_LOG declaration x86 specific There is currently no support for KVM_GET_DIRTY_LOG on aarch64, therefore ARM builds get `unused KVM_GET_DIRTY_LOG` warnings. Mark KVM_GET_DIRTY_LOG as x86-specific until we add the aarch64 support for it. Signed-off-by: Adrian Catangiu --- src/kvm_ioctls.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index dd0fce8..b925726 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -25,6 +25,7 @@ ioctl_iowr_nr!(KVM_GET_EMULATED_CPUID, KVMIO, 0x09, kvm_cpuid2); // Ioctls for VM fds. ioctl_io_nr!(KVM_CREATE_VCPU, KVMIO, 0x41); +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_iow_nr!(KVM_GET_DIRTY_LOG, KVMIO, 0x42, kvm_dirty_log); /* Available with KVM_CAP_USER_MEMORY */ ioctl_iow_nr!( From 28997361f654f0d0fdee141e6ca768bfc942e295 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Tue, 15 Oct 2019 13:05:51 +0300 Subject: [PATCH 068/332] remove extraneous code from doc examples Signed-off-by: Adrian Catangiu --- src/ioctls/vcpu.rs | 29 ++++++++++++++--------------- src/ioctls/vm.rs | 28 ++++++++++++++-------------- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 06dcee5..bab7864 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -109,7 +109,7 @@ impl VcpuFd { /// /// ```rust /// # extern crate kvm_ioctls; - /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// # use kvm_ioctls::Kvm; /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// let vcpu = vm.create_vcpu(0).unwrap(); @@ -140,7 +140,7 @@ impl VcpuFd { /// /// ```rust /// # extern crate kvm_ioctls; - /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// # use kvm_ioctls::Kvm; /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// let vcpu = vm.create_vcpu(0).unwrap(); @@ -175,7 +175,7 @@ impl VcpuFd { /// /// ```rust /// # extern crate kvm_ioctls; - /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// # use kvm_ioctls::Kvm; /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// let vcpu = vm.create_vcpu(0).unwrap(); @@ -207,7 +207,7 @@ impl VcpuFd { /// /// ```rust /// # extern crate kvm_ioctls; - /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// # use kvm_ioctls::Kvm; /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// let vcpu = vm.create_vcpu(0).unwrap(); @@ -241,7 +241,7 @@ impl VcpuFd { /// /// ```rust /// # extern crate kvm_ioctls; - /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// # use kvm_ioctls::Kvm; /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// let vcpu = vm.create_vcpu(0).unwrap(); @@ -275,7 +275,7 @@ impl VcpuFd { /// ```rust /// # extern crate kvm_ioctls; /// # extern crate kvm_bindings; - /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// # use kvm_ioctls::Kvm; /// # use kvm_bindings::kvm_fpu; /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); @@ -315,8 +315,7 @@ impl VcpuFd { /// ```rust /// # extern crate kvm_ioctls; /// # extern crate kvm_bindings; - /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd, MAX_KVM_CPUID_ENTRIES}; - /// # use kvm_bindings::kvm_fpu; + /// # use kvm_ioctls::{Kvm, MAX_KVM_CPUID_ENTRIES}; /// let kvm = Kvm::new().unwrap(); /// let mut kvm_cpuid = kvm.get_supported_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); /// let vm = kvm.create_vm().unwrap(); @@ -334,7 +333,7 @@ impl VcpuFd { /// } /// } /// - /// vcpu.set_cpuid2(&kvm_cpuid); + /// vcpu.set_cpuid2(&kvm_cpuid).unwrap(); /// ``` /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] @@ -359,7 +358,7 @@ impl VcpuFd { /// /// ```rust /// # extern crate kvm_ioctls; - /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// # use kvm_ioctls::Kvm; /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// // For `get_lapic` to work, you first need to create a IRQ chip before creating the vCPU. @@ -395,7 +394,7 @@ impl VcpuFd { /// /// ```rust /// # extern crate kvm_ioctls; - /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// # use kvm_ioctls::Kvm; /// use std::io::Write; /// /// let kvm = Kvm::new().unwrap(); @@ -444,7 +443,7 @@ impl VcpuFd { /// ```rust /// # extern crate kvm_ioctls; /// # extern crate kvm_bindings; - /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// # use kvm_ioctls::Kvm; /// # use kvm_bindings::kvm_msrs; /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); @@ -478,7 +477,7 @@ impl VcpuFd { /// ```rust /// # extern crate kvm_ioctls; /// # extern crate kvm_bindings; - /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// # use kvm_ioctls::Kvm; /// # use kvm_bindings::{kvm_msrs, kvm_msr_entry}; /// # use std::mem; /// let kvm = Kvm::new().unwrap(); @@ -534,7 +533,7 @@ impl VcpuFd { /// ```rust /// # extern crate kvm_ioctls; /// # extern crate kvm_bindings; - /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// # use kvm_ioctls::Kvm; /// use kvm_bindings::kvm_vcpu_init; /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); @@ -594,7 +593,7 @@ impl VcpuFd { /// # use std::io::Write; /// # use std::ptr::null_mut; /// # use std::slice; - /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd, VcpuExit}; + /// # use kvm_ioctls::{Kvm, VcpuExit}; /// # use kvm_bindings::{kvm_userspace_memory_region, KVM_MEM_LOG_DIRTY_PAGES}; /// # let kvm = Kvm::new().unwrap(); /// # let vm = kvm.create_vm().unwrap(); diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 7491499..2912358 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -80,7 +80,7 @@ impl VmFd { /// # extern crate kvm_ioctls; /// extern crate kvm_bindings; /// - /// use kvm_ioctls::{Kvm, VmFd}; + /// use kvm_ioctls::Kvm; /// use kvm_bindings::kvm_userspace_memory_region; /// /// let kvm = Kvm::new().unwrap(); @@ -121,7 +121,7 @@ impl VmFd { /// /// ```rust /// # extern crate kvm_ioctls; - /// # use kvm_ioctls::{Kvm, VmFd}; + /// # use kvm_ioctls::Kvm; /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// vm.set_tss_address(0xfffb_d000).unwrap(); @@ -146,7 +146,7 @@ impl VmFd { /// /// ```rust /// # extern crate kvm_ioctls; - /// # use kvm_ioctls::{Kvm, VmFd}; + /// # use kvm_ioctls::Kvm; /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// @@ -181,7 +181,7 @@ impl VmFd { /// ```rust /// # extern crate kvm_ioctls; /// extern crate kvm_bindings; - /// # use kvm_ioctls::{Kvm, VmFd}; + /// # use kvm_ioctls::Kvm; /// use kvm_bindings::kvm_pit_config; /// /// let kvm = Kvm::new().unwrap(); @@ -226,7 +226,7 @@ impl VmFd { /// ```rust /// # extern crate kvm_ioctls; /// extern crate kvm_bindings; - /// # use kvm_ioctls::{Kvm, VmFd}; + /// # use kvm_ioctls::Kvm; /// use kvm_bindings::kvm_msi; /// /// let kvm = Kvm::new().unwrap(); @@ -273,7 +273,7 @@ impl VmFd { /// ```rust /// # extern crate kvm_ioctls; /// extern crate kvm_bindings; - /// # use kvm_ioctls::{Kvm, VmFd}; + /// # use kvm_ioctls::Kvm; /// use kvm_bindings::kvm_irq_routing; /// /// let kvm = Kvm::new().unwrap(); @@ -320,7 +320,7 @@ impl VmFd { /// ```rust /// # extern crate kvm_ioctls; /// extern crate libc; - /// # use kvm_ioctls::{IoEventAddress, Kvm, NoDatamatch, VmFd}; + /// # use kvm_ioctls::{IoEventAddress, Kvm, NoDatamatch}; /// use libc::{eventfd, EFD_NONBLOCK}; /// /// let kvm = Kvm::new().unwrap(); @@ -390,7 +390,7 @@ impl VmFd { /// # use std::io::Write; /// # use std::ptr::null_mut; /// # use std::slice; - /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd, VcpuExit}; + /// # use kvm_ioctls::{Kvm, VcpuExit}; /// # use kvm_bindings::{kvm_userspace_memory_region, KVM_MEM_LOG_DIRTY_PAGES}; /// # let kvm = Kvm::new().unwrap(); /// # let vm = kvm.create_vm().unwrap(); @@ -504,7 +504,7 @@ impl VmFd { /// ```rust /// # extern crate kvm_ioctls; /// # extern crate libc; - /// # use kvm_ioctls::{Kvm, VmFd}; + /// # use kvm_ioctls::Kvm; /// # use libc::{eventfd, EFD_NONBLOCK}; /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); @@ -547,7 +547,7 @@ impl VmFd { /// ```rust /// # extern crate kvm_ioctls; /// # extern crate libc; - /// # use kvm_ioctls::{Kvm, VmFd}; + /// # use kvm_ioctls::Kvm; /// # use libc::{eventfd, EFD_NONBLOCK}; /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); @@ -599,7 +599,7 @@ impl VmFd { /// /// ```rust /// # extern crate kvm_ioctls; - /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// # use kvm_ioctls::Kvm; /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// // Create one vCPU with the ID=0. @@ -665,7 +665,7 @@ impl VmFd { /// ```rust /// # extern crate kvm_ioctls; /// # extern crate kvm_bindings; - /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// # use kvm_ioctls::Kvm; /// use kvm_bindings::{ /// kvm_device_type_KVM_DEV_TYPE_VFIO, /// kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, @@ -713,7 +713,7 @@ impl VmFd { /// ```rust /// # extern crate kvm_ioctls; /// # extern crate kvm_bindings; - /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// # use kvm_ioctls::Kvm; /// use kvm_bindings::kvm_vcpu_init; /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); @@ -750,7 +750,7 @@ impl VmFd { /// # extern crate kvm_ioctls; /// extern crate kvm_bindings; /// - /// # use kvm_ioctls::{Cap, Kvm, VmFd}; + /// # use kvm_ioctls::Kvm; /// use kvm_bindings::{kvm_enable_cap, KVM_CAP_SPLIT_IRQCHIP}; /// /// let kvm = Kvm::new().unwrap(); From b130b3d3186cff0be74533d28168f203643c94f9 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Mon, 14 Oct 2019 17:58:03 +0300 Subject: [PATCH 069/332] vcpu: add support for setting `immediate_exit` Signed-off-by: Adrian Catangiu --- CHANGELOG.md | 5 +++++ src/ioctls/vcpu.rs | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bcaca65..a962664 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# Unreleased + +## Added +- Support for setting vcpu `kvm_immediate_exit` flag + # v0.2.0 ## Added diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index bab7864..4cb3e37 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -737,6 +737,12 @@ impl VcpuFd { Err(io::Error::last_os_error()) } } + + /// Sets the `immediate_exit` flag on the `kvm_run` struct associated with this vCPU to `val`. + pub fn set_kvm_immediate_exit(&self, val: u8) { + let kvm_run = self.kvm_run_ptr.as_mut_ref(); + kvm_run.immediate_exit = val; + } } /// Helper function to create a new `VcpuFd`. @@ -1129,4 +1135,14 @@ mod tests { vcpu.set_one_reg(PSTATE_REG_ID, data) .expect("Failed to set pstate register"); } + + #[test] + fn set_kvm_immediate_exit() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let vcpu = vm.create_vcpu(0).unwrap(); + assert_eq!(vcpu.kvm_run_ptr.as_mut_ref().immediate_exit, 0); + vcpu.set_kvm_immediate_exit(1); + assert_eq!(vcpu.kvm_run_ptr.as_mut_ref().immediate_exit, 1); + } } From 0da21c1bb7ac2df4a294bc58a318c9b1730aeeec Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Tue, 15 Oct 2019 13:53:04 +0300 Subject: [PATCH 070/332] vcpu: KVM_SET_MSRS returns the number of written MSRs Fix our wrapper over the KVM_SET_MSRS ioctl to also return the number of successfully written MSRs. Also fix the documentation example which was incorrect. Signed-off-by: Adrian Catangiu --- CHANGELOG.md | 4 ++++ src/ioctls/vcpu.rs | 22 +++++++++++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a962664..26eeaba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ ## Added - Support for setting vcpu `kvm_immediate_exit` flag +## Changed +- Function offering support for `KVM_SET_MSRS` also returns the number + of MSR entries successfully written. + # v0.2.0 ## Added diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 4cb3e37..1afa048 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -465,6 +465,7 @@ impl VcpuFd { } /// Setup the model-specific registers (MSR) for this vCPU. + /// Returns the number of MSR entries actually written. /// /// See the documentation for `KVM_SET_MSRS`. /// @@ -495,26 +496,33 @@ impl VcpuFd { /// /// // Create a vector large enough to hold the MSR entry defined above in /// // a `kvm_msrs`structure. - /// let msrs_vec: Vec = - /// Vec::with_capacity(mem::size_of::() + mem::size_of::()); + /// let mut msrs_vec: Vec = + /// vec![0u8; mem::size_of::() + mem::size_of::()]; + /// { + /// let kvm_msr_entries = unsafe { + /// &mut *(msrs_vec.as_mut_slice()[mem::size_of::()..].as_mut_ptr() + /// as *mut kvm_msr_entry) + /// }; + /// *kvm_msr_entries = msrs_entries; + /// } /// let mut msrs: &mut kvm_msrs = unsafe { /// &mut *(msrs_vec.as_ptr() as *mut kvm_msrs) /// }; /// msrs.nmsrs = 1; - /// vcpu.set_msrs(msrs).unwrap(); + /// assert_eq!(vcpu.set_msrs(msrs).unwrap(), 1); /// ``` /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - pub fn set_msrs(&self, msrs: &kvm_msrs) -> Result<()> { + pub fn set_msrs(&self, msrs: &kvm_msrs) -> Result { let ret = unsafe { // Here we trust the kernel not to read past the end of the kvm_msrs struct. ioctl_with_ref(self, KVM_SET_MSRS(), msrs) }; + // KVM_SET_MSRS actually returns the number of msr entries written. if ret < 0 { - // KVM_SET_MSRS actually returns the number of msr entries written. return Err(io::Error::last_os_error()); } - Ok(()) + Ok(ret) } /// Sets the type of CPU to be exposed to the guest and optional features. @@ -901,7 +909,7 @@ mod tests { entries.copy_from_slice(&configured_entry_vec); } msrs.nmsrs = configured_entry_vec.len() as u32; - vcpu.set_msrs(msrs).unwrap(); + assert_eq!(vcpu.set_msrs(msrs).unwrap(), msrs.nmsrs as i32); //now test that GET_MSRS returns the same let wanted_kvm_msrs_entries = [ From 3437afe6e99dae174505ecf354a40c8fc9d01f64 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Tue, 15 Oct 2019 12:56:26 +0300 Subject: [PATCH 071/332] vcpu: add wrappers for missing vcpu state KVM ioctls Added functionality for using the following KVM ioctls: - KVM_GET_CPUID2 - KVM_GET_MP_STATE - KVM_SET_MP_STATE - KVM_GET_VCPU_EVENTS - KVM_SET_VCPU_EVENTS - KVM_GET_DEBUGREGS - KVM_SET_DEBUGREGS - KVM_GET_XSAVE - KVM_SET_XSAVE - KVM_GET_XCRS - KVM_SET_XCRS Signed-off-by: Adrian Catangiu --- CHANGELOG.md | 11 + src/ioctls/vcpu.rs | 489 ++++++++++++++++++++++++++++++++++++++++++++- src/kvm_ioctls.rs | 46 +++++ 3 files changed, 541 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26eeaba..227bd69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ ## Added - Support for setting vcpu `kvm_immediate_exit` flag +- Support for vcpu `KVM_GET_CPUID2` +- Support for vcpu `KVM_GET_MP_STATE` +- Support for vcpu `KVM_SET_MP_STATE` +- Support for vcpu `KVM_GET_VCPU_EVENTS` +- Support for vcpu `KVM_SET_VCPU_EVENTS` +- Support for vcpu `KVM_GET_DEBUGREGS` +- Support for vcpu `KVM_SET_DEBUGREGS` +- Support for vcpu `KVM_GET_XSAVE` +- Support for vcpu `KVM_SET_XSAVE` +- Support for vcpu `KVM_GET_XCRS` +- Support for vcpu `KVM_SET_XCRS` ## Changed - Function offering support for `KVM_SET_MSRS` also returns the number diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 1afa048..4a567b8 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -348,6 +348,41 @@ impl VcpuFd { Ok(()) } + /// X86 specific call to retrieve the CPUID registers. + /// + /// It requires knowledge of how many `kvm_cpuid_entry2` entries there are to get. + /// See the documentation for `KVM_GET_CPUID2` in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Arguments + /// + /// * `num_entries` - Number of CPUID entries to be read. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # extern crate kvm_bindings; + /// # use kvm_ioctls::{Kvm, MAX_KVM_CPUID_ENTRIES}; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// let cpuid = vcpu.get_cpuid2(MAX_KVM_CPUID_ENTRIES).unwrap(); + /// ``` + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn get_cpuid2(&self, num_entries: usize) -> Result { + let mut cpuid = CpuId::new(num_entries); + let ret = unsafe { + // Here we trust the kernel not to read past the end of the kvm_cpuid2 struct. + ioctl_with_mut_ptr(self, KVM_GET_CPUID2(), cpuid.as_mut_fam_struct_ptr()) + }; + if ret != 0 { + return Err(io::Error::last_os_error()); + } + Ok(cpuid) + } + /// Returns the state of the LAPIC (Local Advanced Programmable Interrupt Controller). /// /// The state is returned in a `kvm_lapic_state` structure as defined in the @@ -525,6 +560,354 @@ impl VcpuFd { Ok(ret) } + /// Returns the vcpu's current "multiprocessing state". + /// + /// See the documentation for `KVM_GET_MP_STATE` in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Arguments + /// + /// * `kvm_mp_state` - multiprocessing state to be read. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// let mp_state = vcpu.get_mp_state().unwrap(); + /// ``` + /// + #[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64", + target_arch = "s390" + ))] + pub fn get_mp_state(&self) -> Result { + let mut mp_state = Default::default(); + let ret = unsafe { + // Here we trust the kernel not to read past the end of the kvm_mp_state struct. + ioctl_with_mut_ref(self, KVM_GET_MP_STATE(), &mut mp_state) + }; + if ret != 0 { + return Err(io::Error::last_os_error()); + } + Ok(mp_state) + } + + /// Sets the vcpu's current "multiprocessing state". + /// + /// See the documentation for `KVM_SET_MP_STATE` in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Arguments + /// + /// * `kvm_mp_state` - multiprocessing state to be written. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// let mp_state = Default::default(); + /// // Your `mp_state` manipulation here. + /// vcpu.set_mp_state(mp_state).unwrap(); + /// ``` + /// + #[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64", + target_arch = "s390" + ))] + pub fn set_mp_state(&self, mp_state: kvm_mp_state) -> Result<()> { + let ret = unsafe { + // Here we trust the kernel not to read past the end of the kvm_mp_state struct. + ioctl_with_ref(self, KVM_SET_MP_STATE(), &mp_state) + }; + if ret != 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) + } + + /// X86 specific call that returns the vcpu's current "xsave struct". + /// + /// See the documentation for `KVM_GET_XSAVE` in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Arguments + /// + /// * `kvm_xsave` - xsave struct to be read. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// let xsave = vcpu.get_xsave().unwrap(); + /// ``` + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn get_xsave(&self) -> Result { + let mut xsave = Default::default(); + let ret = unsafe { + // Here we trust the kernel not to read past the end of the kvm_xsave struct. + ioctl_with_mut_ref(self, KVM_GET_XSAVE(), &mut xsave) + }; + if ret != 0 { + return Err(io::Error::last_os_error()); + } + Ok(xsave) + } + + /// X86 specific call that sets the vcpu's current "xsave struct". + /// + /// See the documentation for `KVM_SET_XSAVE` in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Arguments + /// + /// * `kvm_xsave` - xsave struct to be written. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// let xsave = Default::default(); + /// // Your `xsave` manipulation here. + /// vcpu.set_xsave(&xsave).unwrap(); + /// ``` + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn set_xsave(&self, xsave: &kvm_xsave) -> Result<()> { + let ret = unsafe { + // Here we trust the kernel not to read past the end of the kvm_xsave struct. + ioctl_with_ref(self, KVM_SET_XSAVE(), xsave) + }; + if ret != 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) + } + + /// X86 specific call that returns the vcpu's current "xcrs". + /// + /// See the documentation for `KVM_GET_XCRS` in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Arguments + /// + /// * `kvm_xcrs` - xcrs to be read. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// let xcrs = vcpu.get_xcrs().unwrap(); + /// ``` + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn get_xcrs(&self) -> Result { + let mut xcrs = Default::default(); + let ret = unsafe { + // Here we trust the kernel not to read past the end of the kvm_xcrs struct. + ioctl_with_mut_ref(self, KVM_GET_XCRS(), &mut xcrs) + }; + if ret != 0 { + return Err(io::Error::last_os_error()); + } + Ok(xcrs) + } + + /// X86 specific call that sets the vcpu's current "xcrs". + /// + /// See the documentation for `KVM_SET_XCRS` in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Arguments + /// + /// * `kvm_xcrs` - xcrs to be written. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// let xcrs = Default::default(); + /// // Your `xcrs` manipulation here. + /// vcpu.set_xcrs(&xcrs).unwrap(); + /// ``` + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn set_xcrs(&self, xcrs: &kvm_xcrs) -> Result<()> { + let ret = unsafe { + // Here we trust the kernel not to read past the end of the kvm_xcrs struct. + ioctl_with_ref(self, KVM_SET_XCRS(), xcrs) + }; + if ret != 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) + } + + /// X86 specific call that returns the vcpu's current "debug registers". + /// + /// See the documentation for `KVM_GET_DEBUGREGS` in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Arguments + /// + /// * `kvm_debugregs` - debug registers to be read. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// let debug_regs = vcpu.get_debug_regs().unwrap(); + /// ``` + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn get_debug_regs(&self) -> Result { + let mut debug_regs = Default::default(); + let ret = unsafe { + // Here we trust the kernel not to read past the end of the kvm_debugregs struct. + ioctl_with_mut_ref(self, KVM_GET_DEBUGREGS(), &mut debug_regs) + }; + if ret != 0 { + return Err(io::Error::last_os_error()); + } + Ok(debug_regs) + } + + /// X86 specific call that sets the vcpu's current "debug registers". + /// + /// See the documentation for `KVM_SET_DEBUGREGS` in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Arguments + /// + /// * `kvm_debugregs` - debug registers to be written. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// let debug_regs = Default::default(); + /// // Your `debug_regs` manipulation here. + /// vcpu.set_debug_regs(&debug_regs).unwrap(); + /// ``` + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn set_debug_regs(&self, debug_regs: &kvm_debugregs) -> Result<()> { + let ret = unsafe { + // Here we trust the kernel not to read past the end of the kvm_debugregs struct. + ioctl_with_ref(self, KVM_SET_DEBUGREGS(), debug_regs) + }; + if ret != 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) + } + + /// Returns currently pending exceptions, interrupts, and NMIs as well as related + /// states of the vcpu. + /// + /// See the documentation for `KVM_GET_VCPU_EVENTS` in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Arguments + /// + /// * `kvm_vcpu_events` - vcpu events to be read. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// let vcpu_events = vcpu.get_vcpu_events().unwrap(); + /// ``` + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn get_vcpu_events(&self) -> Result { + let mut vcpu_events = Default::default(); + let ret = unsafe { + // Here we trust the kernel not to read past the end of the kvm_vcpu_events struct. + ioctl_with_mut_ref(self, KVM_GET_VCPU_EVENTS(), &mut vcpu_events) + }; + if ret != 0 { + return Err(io::Error::last_os_error()); + } + Ok(vcpu_events) + } + + /// Sets pending exceptions, interrupts, and NMIs as well as related states of the vcpu. + /// + /// See the documentation for `KVM_SET_VCPU_EVENTS` in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Arguments + /// + /// * `kvm_vcpu_events` - vcpu events to be written. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// let vcpu_events = Default::default(); + /// // Your `vcpu_events` manipulation here. + /// vcpu.set_vcpu_events(&vcpu_events).unwrap(); + /// ``` + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn set_vcpu_events(&self, vcpu_events: &kvm_vcpu_events) -> Result<()> { + let ret = unsafe { + // Here we trust the kernel not to read past the end of the kvm_vcpu_events struct. + ioctl_with_ref(self, KVM_SET_VCPU_EVENTS(), vcpu_events) + }; + if ret != 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) + } + /// Sets the type of CPU to be exposed to the guest and optional features. /// /// This initializes an ARM vCPU to the specified type with the specified features @@ -811,16 +1194,20 @@ mod tests { #[cfg(target_arch = "x86_64")] #[test] - fn test_set_cpuid2() { + fn cpuid2_test() { let kvm = Kvm::new().unwrap(); if kvm.check_extension(Cap::ExtCpuid) { let vm = kvm.create_vm().unwrap(); - let mut cpuid = kvm.get_supported_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); - assert!(cpuid.as_mut_slice().len() <= MAX_KVM_CPUID_ENTRIES); + let cpuid = kvm.get_supported_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); + let ncpuids = cpuid.as_slice().len(); + assert!(ncpuids <= MAX_KVM_CPUID_ENTRIES); let nr_vcpus = kvm.get_nr_vcpus(); - for cpu_id in 0..nr_vcpus { - let vcpu = vm.create_vcpu(cpu_id as u8).unwrap(); + for cpu_idx in 0..nr_vcpus { + let vcpu = vm.create_vcpu(cpu_idx as u8).unwrap(); vcpu.set_cpuid2(&cpuid).unwrap(); + let retrieved_cpuid = vcpu.get_cpuid2(ncpuids).unwrap(); + // Only check the first few leafs as some (e.g. 13) are reserved. + assert_eq!(cpuid.as_slice()[..3], retrieved_cpuid.as_slice()[..3]); } } } @@ -947,6 +1334,72 @@ mod tests { } } + #[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64", + target_arch = "s390" + ))] + #[test] + fn mpstate_test() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let vcpu = vm.create_vcpu(0).unwrap(); + let mp_state = vcpu.get_mp_state().unwrap(); + vcpu.set_mp_state(mp_state).unwrap(); + let other_mp_state = vcpu.get_mp_state().unwrap(); + assert_eq!(mp_state, other_mp_state); + } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[test] + fn xsave_test() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let vcpu = vm.create_vcpu(0).unwrap(); + let xsave = vcpu.get_xsave().unwrap(); + vcpu.set_xsave(&xsave).unwrap(); + let other_xsave = vcpu.get_xsave().unwrap(); + assert_eq!(&xsave.region[..], &other_xsave.region[..]); + } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[test] + fn xcrs_test() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let vcpu = vm.create_vcpu(0).unwrap(); + let xcrs = vcpu.get_xcrs().unwrap(); + vcpu.set_xcrs(&xcrs).unwrap(); + let other_xcrs = vcpu.get_xcrs().unwrap(); + assert_eq!(xcrs, other_xcrs); + } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[test] + fn debugregs_test() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let vcpu = vm.create_vcpu(0).unwrap(); + let debugregs = vcpu.get_debug_regs().unwrap(); + vcpu.set_debug_regs(&debugregs).unwrap(); + let other_debugregs = vcpu.get_debug_regs().unwrap(); + assert_eq!(debugregs, other_debugregs); + } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[test] + fn vcpu_events_test() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let vcpu = vm.create_vcpu(0).unwrap(); + let vcpu_events = vcpu.get_vcpu_events().unwrap(); + vcpu.set_vcpu_events(&vcpu_events).unwrap(); + let other_vcpu_events = vcpu.get_vcpu_events().unwrap(); + assert_eq!(vcpu_events, other_vcpu_events); + } + #[cfg(target_arch = "x86_64")] #[test] fn test_run_code() { @@ -1089,6 +1542,7 @@ mod tests { ), badf_errno ); + assert_eq!(get_raw_errno(faulty_vcpu_fd.get_cpuid2(1)), badf_errno); // `kvm_lapic_state` does not implement debug by default so we cannot // use unwrap_err here. assert!(faulty_vcpu_fd.get_lapic().is_err()); @@ -1104,6 +1558,31 @@ mod tests { get_raw_errno(faulty_vcpu_fd.set_msrs(&unsafe { std::mem::zeroed() })), badf_errno ); + assert_eq!(get_raw_errno(faulty_vcpu_fd.get_mp_state()), badf_errno); + assert_eq!( + get_raw_errno(faulty_vcpu_fd.set_mp_state(kvm_mp_state::default())), + badf_errno + ); + assert_eq!(get_raw_errno(faulty_vcpu_fd.get_xsave()), badf_errno); + assert_eq!( + get_raw_errno(faulty_vcpu_fd.set_xsave(&kvm_xsave::default())), + badf_errno + ); + assert_eq!(get_raw_errno(faulty_vcpu_fd.get_xcrs()), badf_errno); + assert_eq!( + get_raw_errno(faulty_vcpu_fd.set_xcrs(&kvm_xcrs::default())), + badf_errno + ); + assert_eq!(get_raw_errno(faulty_vcpu_fd.get_debug_regs()), badf_errno); + assert_eq!( + get_raw_errno(faulty_vcpu_fd.set_debug_regs(&kvm_debugregs::default())), + badf_errno + ); + assert_eq!(get_raw_errno(faulty_vcpu_fd.get_vcpu_events()), badf_errno); + assert_eq!( + get_raw_errno(faulty_vcpu_fd.set_vcpu_events(&kvm_vcpu_events::default())), + badf_errno + ); assert_eq!(get_raw_errno(faulty_vcpu_fd.run()), badf_errno); } diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index b925726..2c42f99 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -110,6 +110,52 @@ ioctl_iow_nr!(KVM_SET_LAPIC, KVMIO, 0x8f, kvm_lapic_state); /* Available with KVM_CAP_EXT_CPUID */ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_iow_nr!(KVM_SET_CPUID2, KVMIO, 0x90, kvm_cpuid2); +/* Available with KVM_CAP_EXT_CPUID */ +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_iowr_nr!(KVM_GET_CPUID2, KVMIO, 0x91, kvm_cpuid2); +/* Available with KVM_CAP_MP_STATE */ +#[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64", + target_arch = "s390" +))] +ioctl_ior_nr!(KVM_GET_MP_STATE, KVMIO, 0x98, kvm_mp_state); +/* Available with KVM_CAP_MP_STATE */ +#[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64", + target_arch = "s390" +))] +ioctl_iow_nr!(KVM_SET_MP_STATE, KVMIO, 0x99, kvm_mp_state); +/* Available with KVM_CAP_VCPU_EVENTS */ +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_ior_nr!(KVM_GET_VCPU_EVENTS, KVMIO, 0x9f, kvm_vcpu_events); +/* Available with KVM_CAP_VCPU_EVENTS */ +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_iow_nr!(KVM_SET_VCPU_EVENTS, KVMIO, 0xa0, kvm_vcpu_events); +/* Available with KVM_CAP_DEBUGREGS */ +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_ior_nr!(KVM_GET_DEBUGREGS, KVMIO, 0xa1, kvm_debugregs); +/* Available with KVM_CAP_DEBUGREGS */ +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_iow_nr!(KVM_SET_DEBUGREGS, KVMIO, 0xa2, kvm_debugregs); +/* Available with KVM_CAP_XSAVE */ +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_ior_nr!(KVM_GET_XSAVE, KVMIO, 0xa4, kvm_xsave); +/* Available with KVM_CAP_XSAVE */ +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_iow_nr!(KVM_SET_XSAVE, KVMIO, 0xa5, kvm_xsave); +/* Available with KVM_CAP_XCRS */ +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_ior_nr!(KVM_GET_XCRS, KVMIO, 0xa6, kvm_xcrs); +/* Available with KVM_CAP_XCRS */ +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_iow_nr!(KVM_SET_XCRS, KVMIO, 0xa7, kvm_xcrs); + /* Available with KVM_CAP_ENABLE_CAP */ #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] ioctl_iow_nr!(KVM_ENABLE_CAP, KVMIO, 0xa3, kvm_enable_cap); From c0ccfce0d3a2ffa37c5a2de816d106077231c528 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Tue, 15 Oct 2019 16:13:00 +0300 Subject: [PATCH 072/332] vm: add wrappers for ioctls related to VM state - KVM_GET_IRQCHIP - KVM_SET_IRQCHIP - KVM_GET_PIT2 - KVM_SET_PIT2 - KVM_GET_CLOCK - KVM_SET_CLOCK Signed-off-by: Adrian Catangiu --- CHANGELOG.md | 6 + coverage_config.json | 2 +- src/ioctls/mod.rs | 4 + src/ioctls/vm.rs | 287 ++++++++++++++++++++++++++++++++++++++++++- src/kvm_ioctls.rs | 18 +++ src/lib.rs | 2 +- 6 files changed, 315 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 227bd69..0e83ace 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,12 @@ - Support for vcpu `KVM_SET_XSAVE` - Support for vcpu `KVM_GET_XCRS` - Support for vcpu `KVM_SET_XCRS` +- Support for vm `KVM_GET_IRQCHIP` +- Support for vm `KVM_SET_IRQCHIP` +- Support for vm `KVM_GET_CLOCK` +- Support for vm `KVM_SET_CLOCK` +- Support for vm `KVM_GET_PIT2` +- Support for vm `KVM_SET_PIT2` ## Changed - Function offering support for `KVM_SET_MSRS` also returns the number diff --git a/coverage_config.json b/coverage_config.json index 161963c..5117be2 100644 --- a/coverage_config.json +++ b/coverage_config.json @@ -1,5 +1,5 @@ { - "coverage_score": 90.3, + "coverage_score": 92.3, "exclude_path": "", "crate_features": "" } diff --git a/src/ioctls/mod.rs b/src/ioctls/mod.rs index c56a611..0de8171 100644 --- a/src/ioctls/mod.rs +++ b/src/ioctls/mod.rs @@ -74,6 +74,10 @@ fn vec_with_array_field(count: usize) -> Vec { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub const MAX_KVM_CPUID_ENTRIES: usize = 80; +/// Maximum number of MSRs KVM supports (See arch/x86/kvm/x86.c). +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +pub const KVM_MAX_MSR_ENTRIES: usize = 256; + // We can't implement FamStruct directly for kvm_cpuid2. // We would get an "impl doesn't use types inside crate" error. // We have to create a shadow structure as a workaround. diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 2912358..225660d 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -170,6 +170,83 @@ impl VmFd { } } + /// X86 specific call to retrieve the state of a kernel interrupt controller. + /// + /// See the documentation for `KVM_GET_IRQCHIP` in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Arguments + /// + /// * `irqchip` - `kvm_irqchip` (input/output) to be read. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_bindings; + /// # extern crate kvm_ioctls; + /// # use kvm_bindings::{kvm_irqchip, KVM_IRQCHIP_PIC_MASTER}; + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// + /// vm.create_irq_chip().unwrap(); + /// let mut irqchip = kvm_irqchip::default(); + /// irqchip.chip_id = KVM_IRQCHIP_PIC_MASTER; + /// vm.get_irqchip(&mut irqchip).unwrap(); + /// ``` + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn get_irqchip(&self, irqchip: &mut kvm_irqchip) -> Result<()> { + let ret = unsafe { + // Here we trust the kernel not to read past the end of the kvm_irqchip struct. + ioctl_with_mut_ref(self, KVM_GET_IRQCHIP(), irqchip) + }; + if ret == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + + /// X86 specific call to set the state of a kernel interrupt controller. + /// + /// See the documentation for `KVM_SET_IRQCHIP` in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Arguments + /// + /// * `irqchip` - `kvm_irqchip` (input/output) to be written. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_bindings; + /// # extern crate kvm_ioctls; + /// # use kvm_bindings::{kvm_irqchip, KVM_IRQCHIP_PIC_MASTER}; + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// + /// vm.create_irq_chip().unwrap(); + /// let mut irqchip = kvm_irqchip::default(); + /// irqchip.chip_id = KVM_IRQCHIP_PIC_MASTER; + /// // Your `irqchip` manipulation here. + /// vm.set_irqchip(&mut irqchip).unwrap(); + /// ``` + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn set_irqchip(&self, irqchip: &kvm_irqchip) -> Result<()> { + let ret = unsafe { + // Here we trust the kernel not to read past the end of the kvm_irqchip struct. + ioctl_with_ref(self, KVM_SET_IRQCHIP(), irqchip) + }; + if ret == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + /// Creates a PIT as per the `KVM_CREATE_PIT2` ioctl. /// /// # Arguments @@ -202,6 +279,151 @@ impl VmFd { } } + /// X86 specific call to retrieve the state of the in-kernel PIT model. + /// + /// See the documentation for `KVM_GET_PIT2` in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Arguments + /// + /// * `pitstate` - `kvm_pit_state2` to be read. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_bindings; + /// # extern crate kvm_ioctls; + /// # use kvm_bindings::kvm_pit_config; + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// + /// let pit_config = kvm_pit_config::default(); + /// vm.create_pit2(pit_config).unwrap(); + /// let pitstate = vm.get_pit2().unwrap(); + /// ``` + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn get_pit2(&self) -> Result { + let mut pitstate = Default::default(); + let ret = unsafe { + // Here we trust the kernel not to read past the end of the kvm_pit_state2 struct. + ioctl_with_mut_ref(self, KVM_GET_PIT2(), &mut pitstate) + }; + if ret == 0 { + Ok(pitstate) + } else { + Err(io::Error::last_os_error()) + } + } + + /// X86 specific call to set the state of the in-kernel PIT model. + /// + /// See the documentation for `KVM_SET_PIT2` in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Arguments + /// + /// * `pitstate` - `kvm_pit_state2` to be written. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_bindings; + /// # extern crate kvm_ioctls; + /// # use kvm_bindings::{kvm_pit_config, kvm_pit_state2}; + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// + /// let pit_config = kvm_pit_config::default(); + /// vm.create_pit2(pit_config).unwrap(); + /// let mut pitstate = kvm_pit_state2::default(); + /// // Your `pitstate` manipulation here. + /// vm.set_pit2(&mut pitstate).unwrap(); + /// ``` + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn set_pit2(&self, pitstate: &kvm_pit_state2) -> Result<()> { + let ret = unsafe { + // Here we trust the kernel not to read past the end of the kvm_pit_state2 struct. + ioctl_with_ref(self, KVM_SET_PIT2(), pitstate) + }; + if ret == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + + /// X86 specific call to retrieve the current timestamp of kvmclock. + /// + /// See the documentation for `KVM_GET_CLOCK` in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Arguments + /// + /// * `clock` - `kvm_clock_data` to be read. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let clock = vm.get_clock().unwrap(); + /// ``` + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn get_clock(&self) -> Result { + let mut clock = Default::default(); + let ret = unsafe { + // Here we trust the kernel not to read past the end of the kvm_clock_data struct. + ioctl_with_mut_ref(self, KVM_GET_CLOCK(), &mut clock) + }; + if ret == 0 { + Ok(clock) + } else { + Err(io::Error::last_os_error()) + } + } + + /// X86 specific call to set the current timestamp of kvmclock. + /// + /// See the documentation for `KVM_SET_CLOCK` in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Arguments + /// + /// * `clock` - `kvm_clock_data` to be written. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_bindings; + /// # extern crate kvm_ioctls; + /// # use kvm_bindings::kvm_clock_data; + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let mut clock = kvm_clock_data::default(); + /// vm.set_clock(&mut clock).unwrap(); + /// ``` + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn set_clock(&self, clock: &kvm_clock_data) -> Result<()> { + let ret = unsafe { + // Here we trust the kernel not to read past the end of the kvm_clock_data struct. + ioctl_with_ref(self, KVM_SET_CLOCK(), clock) + }; + if ret == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + /// Directly injects a MSI message as per the `KVM_SIGNAL_MSI` ioctl. /// /// See the documentation for `KVM_SIGNAL_MSI`. @@ -845,7 +1067,7 @@ mod tests { target_arch = "arm", target_arch = "aarch64" ))] - fn test_create_irq_chip() { + fn test_irq_chip() { let kvm = Kvm::new().unwrap(); assert!(kvm.check_extension(Cap::Irqchip)); let vm = kvm.create_vm().unwrap(); @@ -855,14 +1077,57 @@ mod tests { // On arm, we expect this to fail as the irq chip needs to be created after the vcpus. assert!(vm.create_irq_chip().is_err()); } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + { + let mut irqchip = kvm_irqchip::default(); + irqchip.chip_id = KVM_IRQCHIP_PIC_MASTER; + vm.get_irqchip(&mut irqchip).unwrap(); + vm.set_irqchip(&irqchip).unwrap(); + let mut other_irqchip = kvm_irqchip::default(); + other_irqchip.chip_id = KVM_IRQCHIP_PIC_MASTER; + vm.get_irqchip(&mut other_irqchip).unwrap(); + unsafe { assert_eq!(irqchip.chip.dummy[..], other_irqchip.chip.dummy[..]) }; + } } #[test] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn test_create_pit2() { + fn test_pit2() { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); assert!(vm.create_pit2(kvm_pit_config::default()).is_ok()); + + let pit2 = vm.get_pit2().unwrap(); + vm.set_pit2(&pit2).unwrap(); + let mut other_pit2 = vm.get_pit2().unwrap(); + // Load time will differ, let's overwrite it so we can test equality. + other_pit2.channels[0].count_load_time = pit2.channels[0].count_load_time; + other_pit2.channels[1].count_load_time = pit2.channels[1].count_load_time; + other_pit2.channels[2].count_load_time = pit2.channels[2].count_load_time; + assert_eq!(pit2, other_pit2); + } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[test] + fn test_clock() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + + // Get current time. + let orig = vm.get_clock().unwrap(); + + // Reset time. + let mut fudged = kvm_clock_data::default(); + fudged.clock = 10; + vm.set_clock(&fudged).unwrap(); + + // Get new time. + let new = vm.get_clock().unwrap(); + + // Verify new time has progressed but is smaller than orig time. + assert!(fudged.clock < new.clock); + assert!(new.clock < orig.clock); } #[test] @@ -991,6 +1256,24 @@ mod tests { get_raw_errno(faulty_vm_fd.register_ioevent(event_fd, &IoEventAddress::Pio(0), 0u64)), badf_errno ); + assert_eq!( + get_raw_errno(faulty_vm_fd.get_irqchip(&mut kvm_irqchip::default())), + badf_errno + ); + assert_eq!( + get_raw_errno(faulty_vm_fd.set_irqchip(&kvm_irqchip::default())), + badf_errno + ); + assert_eq!(get_raw_errno(faulty_vm_fd.get_clock()), badf_errno); + assert_eq!( + get_raw_errno(faulty_vm_fd.set_clock(&kvm_clock_data::default())), + badf_errno + ); + assert_eq!(get_raw_errno(faulty_vm_fd.get_pit2()), badf_errno); + assert_eq!( + get_raw_errno(faulty_vm_fd.set_pit2(&kvm_pit_state2::default())), + badf_errno + ); assert_eq!( get_raw_errno(faulty_vm_fd.register_irqfd(event_fd, 0)), badf_errno diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index 2c42f99..59ea45d 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -68,6 +68,24 @@ ioctl_iow_nr!(KVM_IRQFD, KVMIO, 0x76, kvm_irqfd); ioctl_iow_nr!(KVM_CREATE_PIT2, KVMIO, 0x77, kvm_pit_config); /* Available with KVM_CAP_IOEVENTFD */ ioctl_iow_nr!(KVM_IOEVENTFD, KVMIO, 0x79, kvm_ioeventfd); +/* Available with KVM_CAP_IRQCHIP */ +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_iowr_nr!(KVM_GET_IRQCHIP, KVMIO, 0x62, kvm_irqchip); +/* Available with KVM_CAP_IRQCHIP */ +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_ior_nr!(KVM_SET_IRQCHIP, KVMIO, 0x63, kvm_irqchip); +/* Available with KVM_CAP_ADJUST_CLOCK */ +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_iow_nr!(KVM_SET_CLOCK, KVMIO, 0x7b, kvm_clock_data); +/* Available with KVM_CAP_ADJUST_CLOCK */ +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_ior_nr!(KVM_GET_CLOCK, KVMIO, 0x7c, kvm_clock_data); +/* Available with KVM_CAP_PIT_STATE2 */ +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_ior_nr!(KVM_GET_PIT2, KVMIO, 0x9f, kvm_pit_state2); +/* Available with KVM_CAP_PIT_STATE2 */ +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_iow_nr!(KVM_SET_PIT2, KVMIO, 0xa0, kvm_pit_state2); // Ioctls for VCPU fds. diff --git a/src/lib.rs b/src/lib.rs index dd65b27..c059b63 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -194,7 +194,7 @@ pub use ioctls::system::Kvm; pub use ioctls::vcpu::{VcpuExit, VcpuFd}; pub use ioctls::vm::{IoEventAddress, NoDatamatch, VmFd}; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -pub use ioctls::{CpuId, MAX_KVM_CPUID_ENTRIES}; +pub use ioctls::{CpuId, KVM_MAX_MSR_ENTRIES, MAX_KVM_CPUID_ENTRIES}; // The following example is used to verify that our public // structures are exported properly. /// # Example From 5f5edb2a8e51ece3833504b5b7919b0f17f67c22 Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Thu, 24 Oct 2019 15:55:11 +0300 Subject: [PATCH 073/332] vcpu: Add wrapper for KVM_GET_ONE_REG. - add ioctl definition in kvm_ioctls.rs - implement vcpu.get_one_reg() Signed-off-by: Andrei Sandu --- src/ioctls/vcpu.rs | 57 ++++++++++++++++++++++++++++++++++++++++++++++ src/kvm_ioctls.rs | 2 ++ 2 files changed, 59 insertions(+) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 4a567b8..4ea7e1c 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -972,6 +972,30 @@ impl VcpuFd { Ok(()) } + /// Returns the value of the specified vCPU register. + /// + /// The id of the register is encoded as specified in the kernel documentation + /// for `KVM_GET_ONE_REG`. + /// + /// # Arguments + /// + /// * `reg_id` - ID of the register. + /// + #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + pub fn get_one_reg(&self, reg_id: u64) -> Result { + let mut reg_value = 0; + let mut onereg = kvm_one_reg { + id: reg_id, + addr: &mut reg_value as *mut u64 as u64, + }; + + let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_ONE_REG(), &mut onereg) }; + if ret < 0 { + return Err(io::Error::last_os_error()); + } + Ok(reg_value) + } + /// Triggers the running of the current virtual CPU returning an exit reason. /// /// See documentation for `KVM_RUN`. @@ -1623,6 +1647,39 @@ mod tests { .expect("Failed to set pstate register"); } + #[test] + #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + fn test_get_one_reg() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let vcpu = vm.create_vcpu(0).unwrap(); + + let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default(); + vm.get_preferred_target(&mut kvi) + .expect("Cannot get preferred target"); + vcpu.vcpu_init(&kvi).expect("Cannot initialize vcpu"); + + // PSR (Processor State Register) bits. + // Taken from arch/arm64/include/uapi/asm/ptrace.h. + const PSR_MODE_EL1H: u64 = 0x0000_0005; + const PSR_F_BIT: u64 = 0x0000_0040; + const PSR_I_BIT: u64 = 0x0000_0080; + const PSR_A_BIT: u64 = 0x0000_0100; + const PSR_D_BIT: u64 = 0x0000_0200; + const PSTATE_FAULT_BITS_64: u64 = + (PSR_MODE_EL1H | PSR_A_BIT | PSR_F_BIT | PSR_I_BIT | PSR_D_BIT); + let data: u64 = PSTATE_FAULT_BITS_64; + const PSTATE_REG_ID: u64 = 0x6030_0000_0010_0042; + vcpu.set_one_reg(PSTATE_REG_ID, data) + .expect("Failed to set pstate register"); + + assert_eq!( + vcpu.get_one_reg(PSTATE_REG_ID) + .expect("Failed to get pstate register"), + PSTATE_FAULT_BITS_64 + ); + } + #[test] fn set_kvm_immediate_exit() { let kvm = Kvm::new().unwrap(); diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index 59ea45d..951fd1a 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -187,6 +187,8 @@ ioctl_iow_nr!(KVM_ENABLE_CAP, KVMIO, 0xa3, kvm_enable_cap); ioctl_iow_nr!(KVM_SIGNAL_MSI, KVMIO, 0xa5, kvm_msi); /* Available with KVM_CAP_ONE_REG */ #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] +ioctl_iow_nr!(KVM_GET_ONE_REG, KVMIO, 0xab, kvm_one_reg); +#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] ioctl_iow_nr!(KVM_SET_ONE_REG, KVMIO, 0xac, kvm_one_reg); #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] ioctl_iow_nr!(KVM_ARM_VCPU_INIT, KVMIO, 0xae, kvm_vcpu_init); From c7864cd4a875744f400f77d59e5d42323a059a31 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Thu, 24 Oct 2019 21:29:03 +0300 Subject: [PATCH 074/332] Release v0.3.0 Changes are in the CHANGELOG.md file. Signed-off-by: Andreea Florescu --- CHANGELOG.md | 37 +++++++++++++++++++------------------ Cargo.toml | 2 +- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e83ace..b55f3e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,24 +1,25 @@ -# Unreleased +# v0.3.0 ## Added - Support for setting vcpu `kvm_immediate_exit` flag -- Support for vcpu `KVM_GET_CPUID2` -- Support for vcpu `KVM_GET_MP_STATE` -- Support for vcpu `KVM_SET_MP_STATE` -- Support for vcpu `KVM_GET_VCPU_EVENTS` -- Support for vcpu `KVM_SET_VCPU_EVENTS` -- Support for vcpu `KVM_GET_DEBUGREGS` -- Support for vcpu `KVM_SET_DEBUGREGS` -- Support for vcpu `KVM_GET_XSAVE` -- Support for vcpu `KVM_SET_XSAVE` -- Support for vcpu `KVM_GET_XCRS` -- Support for vcpu `KVM_SET_XCRS` -- Support for vm `KVM_GET_IRQCHIP` -- Support for vm `KVM_SET_IRQCHIP` -- Support for vm `KVM_GET_CLOCK` -- Support for vm `KVM_SET_CLOCK` -- Support for vm `KVM_GET_PIT2` -- Support for vm `KVM_SET_PIT2` +- Support for the vcpu ioctl `KVM_GET_CPUID2` +- Support for the vcpu ioctl `KVM_GET_MP_STATE` +- Support for the vcpu ioctl `KVM_SET_MP_STATE` +- Support for the vcpu ioctl `KVM_GET_VCPU_EVENTS` +- Support for the vcpu ioctl `KVM_SET_VCPU_EVENTS` +- Support for the vcpu ioctl `KVM_GET_DEBUGREGS` +- Support for the vcpu ioctl `KVM_SET_DEBUGREGS` +- Support for the vcpu ioctl `KVM_GET_XSAVE` +- Support for the vcpu ioctl `KVM_SET_XSAVE` +- Support for the vcpu ioctl `KVM_GET_XCRS` +- Support for the vcpu ioctl `KVM_SET_XCRS` +- Support for the vm ioctl `KVM_GET_IRQCHIP` +- Support for the vm ioctl `KVM_SET_IRQCHIP` +- Support for the vm ioctl `KVM_GET_CLOCK` +- Support for the vm ioctl `KVM_SET_CLOCK` +- Support for the vm ioctl `KVM_GET_PIT2` +- Support for the vm ioctl `KVM_SET_PIT2` +- Support for the vcpu ioctl `KVM_GET_ONE_REG` ## Changed - Function offering support for `KVM_SET_MSRS` also returns the number diff --git a/Cargo.toml b/Cargo.toml index 584e7ad..2cc15be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kvm-ioctls" -version = "0.2.0" +version = "0.3.0" authors = ["Amazon Firecracker Team "] description = "Safe wrappers over KVM ioctls" repository = "https://github.com/rust-vmm/kvm-ioctls" From 86d8a9cdc01c4a4c1bec0441197dfe93912d575a Mon Sep 17 00:00:00 2001 From: Sebastien Boeuf Date: Tue, 29 Oct 2019 07:32:24 -0700 Subject: [PATCH 075/332] vm: Add a function to unregister ioeventfd The KVM_IOEVENTFD ioctl allows for both assigning and deassigning events to a specific PIO or MMIO guest address. This commit introduces the missing function to perform the deassignment. Signed-off-by: Sebastien Boeuf --- coverage_config.json | 2 +- src/ioctls/vm.rs | 95 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/coverage_config.json b/coverage_config.json index 5117be2..0b47f96 100644 --- a/coverage_config.json +++ b/coverage_config.json @@ -1,5 +1,5 @@ { - "coverage_score": 92.3, + "coverage_score": 92.5, "exclude_path": "", "crate_features": "" } diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 225660d..2a0bf56 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -591,6 +591,73 @@ impl VmFd { } } + /// Unregisters an event from a certain address it has been previously registered to. + /// + /// See the documentation for `KVM_IOEVENTFD`. + /// + /// # Arguments + /// + /// * `fd` - FD which will be unregistered. + /// * `addr` - Address being written to. + /// + /// # Safety + /// + /// This function is unsafe because it relies on RawFd. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// extern crate libc; + /// # use kvm_ioctls::{IoEventAddress, Kvm, NoDatamatch}; + /// use libc::{eventfd, EFD_NONBLOCK}; + /// + /// let kvm = Kvm::new().unwrap(); + /// let vm_fd = kvm.create_vm().unwrap(); + /// let evtfd = unsafe { eventfd(0, EFD_NONBLOCK) }; + /// let pio_addr = IoEventAddress::Pio(0xf4); + /// let mmio_addr = IoEventAddress::Mmio(0x1000); + /// vm_fd + /// .register_ioevent(evtfd, &pio_addr, NoDatamatch) + /// .unwrap(); + /// vm_fd + /// .register_ioevent(evtfd, &mmio_addr, NoDatamatch) + /// .unwrap(); + /// unsafe { + /// vm_fd + /// .unregister_ioevent(evtfd, &pio_addr) + /// .unwrap(); + /// vm_fd + /// .unregister_ioevent(evtfd, &mmio_addr) + /// .unwrap(); + /// }; + /// ``` + /// + pub unsafe fn unregister_ioevent(&self, fd: RawFd, addr: &IoEventAddress) -> Result<()> { + let mut flags = 1 << kvm_ioeventfd_flag_nr_deassign; + if let IoEventAddress::Pio(_) = *addr { + flags |= 1 << kvm_ioeventfd_flag_nr_pio + } + + let ioeventfd = kvm_ioeventfd { + addr: match addr { + IoEventAddress::Pio(ref p) => *p as u64, + IoEventAddress::Mmio(ref m) => *m, + }, + fd, + flags, + ..Default::default() + }; + // Safe because we know that our file is a VM fd, we know the kernel will only read the + // correct amount of memory from our pointer, and we verify the return result. + let ret = ioctl_with_ref(self, KVM_IOEVENTFD(), &ioeventfd); + if ret == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + /// Gets the bitmap of pages dirtied since the last call of this function. /// /// Leverages the dirty page logging feature in KVM. As a side-effect, this also resets the @@ -1157,6 +1224,34 @@ mod tests { .is_ok()); } + #[test] + fn test_unregister_ioevent() { + assert_eq!(std::mem::size_of::(), 0); + + let kvm = Kvm::new().unwrap(); + let vm_fd = kvm.create_vm().unwrap(); + let evtfd = unsafe { eventfd(0, EFD_NONBLOCK) }; + let pio_addr = IoEventAddress::Pio(0xf4); + let mmio_addr = IoEventAddress::Mmio(0x1000); + + // First try to unregister addresses which have not been registered. + assert!(unsafe { vm_fd.unregister_ioevent(evtfd, &pio_addr) }.is_err()); + assert!(unsafe { vm_fd.unregister_ioevent(evtfd, &mmio_addr) }.is_err()); + + // Now register the addresses + assert!(vm_fd + .register_ioevent(evtfd, &pio_addr, NoDatamatch) + .is_ok()); + assert!(vm_fd + .register_ioevent(evtfd, &mmio_addr, NoDatamatch) + .is_ok()); + + // Try again unregistering the addresses. This time it should work + // since they have been previously registered. + assert!(unsafe { vm_fd.unregister_ioevent(evtfd, &pio_addr) }.is_ok()); + assert!(unsafe { vm_fd.unregister_ioevent(evtfd, &mmio_addr) }.is_ok()); + } + #[test] #[cfg(any( target_arch = "x86", From df708d775d93cab5fdfc6374228d6e1b8f2457c2 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Mon, 28 Oct 2019 22:23:49 +0100 Subject: [PATCH 076/332] remove sys_ioctl.rs The ioctl macros are defined in vmm-sys-util. Use those defines instead. The coverage drops by 0.5% after removing the macros. Signed-off-by: Andreea Florescu --- coverage_config.json | 2 +- src/ioctls/device.rs | 2 +- src/ioctls/system.rs | 4 +- src/ioctls/vcpu.rs | 4 +- src/ioctls/vm.rs | 2 +- src/kvm_ioctls.rs | 2 +- src/lib.rs | 3 +- src/sys_ioctl.rs | 163 ------------------------------------------- 8 files changed, 11 insertions(+), 171 deletions(-) delete mode 100644 src/sys_ioctl.rs diff --git a/coverage_config.json b/coverage_config.json index 0b47f96..2ddf3a6 100644 --- a/coverage_config.json +++ b/coverage_config.json @@ -1,5 +1,5 @@ { - "coverage_score": 92.5, + "coverage_score": 92.0, "exclude_path": "", "crate_features": "" } diff --git a/src/ioctls/device.rs b/src/ioctls/device.rs index 9b50672..7d8820d 100644 --- a/src/ioctls/device.rs +++ b/src/ioctls/device.rs @@ -9,7 +9,7 @@ use kvm_bindings::kvm_device_attr; use ioctls::Result; use kvm_ioctls::KVM_SET_DEVICE_ATTR; -use sys_ioctl::ioctl_with_ref; +use vmm_sys_util::ioctl::ioctl_with_ref; /// Wrapper over the file descriptor obtained when creating an emulated device in the kernel. pub struct DeviceFd { diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index 54c9be5..60339d5 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -21,7 +21,9 @@ use ioctls::vm::{new_vmfd, VmFd}; use ioctls::CpuId; use ioctls::Result; use kvm_ioctls::*; -use sys_ioctl::*; +use vmm_sys_util::ioctl::{ioctl, ioctl_with_val}; +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +use vmm_sys_util::ioctl::{ioctl_with_mut_ptr, ioctl_with_mut_ref}; /// Wrapper over KVM system ioctls. pub struct Kvm { diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 4ea7e1c..1e14217 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -15,7 +15,9 @@ use std::os::unix::io::{AsRawFd, RawFd}; use ioctls::CpuId; use ioctls::{KvmRunWrapper, Result}; use kvm_ioctls::*; -use sys_ioctl::*; +use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ref}; +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +use vmm_sys_util::ioctl::{ioctl_with_mut_ptr, ioctl_with_ptr}; /// Reasons for vCPU exits. /// diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 2a0bf56..7974ae8 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -20,7 +20,7 @@ use ioctls::vcpu::new_vcpu; use ioctls::vcpu::VcpuFd; use ioctls::KvmRunWrapper; use kvm_ioctls::*; -use sys_ioctl::*; +use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_val}; /// An address either in programmable I/O space or in memory mapped I/O space. /// diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index 951fd1a..ac18c24 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -208,8 +208,8 @@ mod tests { use std::os::unix::io::FromRawFd; use libc::{c_char, open, O_RDWR}; + use vmm_sys_util::ioctl::{ioctl, ioctl_with_val}; - use super::super::sys_ioctl::*; use super::*; const KVM_PATH: &'static str = "/dev/kvm\0"; diff --git a/src/lib.rs b/src/lib.rs index c059b63..2570d9c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -179,10 +179,9 @@ extern crate kvm_bindings; extern crate libc; +#[macro_use] extern crate vmm_sys_util; -#[macro_use] -mod sys_ioctl; #[macro_use] mod kvm_ioctls; mod cap; diff --git a/src/sys_ioctl.rs b/src/sys_ioctl.rs deleted file mode 100644 index 3dd8f13..0000000 --- a/src/sys_ioctl.rs +++ /dev/null @@ -1,163 +0,0 @@ -#![allow(dead_code)] -// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 OR MIT -// -// Portions Copyright 2017 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the THIRD-PARTY file. - -//! Macros and wrapper functions for dealing with ioctls. -use libc; -use std::os::raw::{c_int, c_uint, c_ulong, c_void}; -use std::os::unix::io::AsRawFd; - -/// Raw macro to declare a function that returns an ioctl number. -#[macro_export] -macro_rules! ioctl_ioc_nr { - ($name:ident, $dir:expr, $ty:expr, $nr:expr, $size:expr) => { - #[allow(non_snake_case)] - pub fn $name() -> ::std::os::raw::c_ulong { - u64::from( - ($dir << $crate::sys_ioctl::_IOC_DIRSHIFT) - | ($ty << $crate::sys_ioctl::_IOC_TYPESHIFT) - | ($nr << $crate::sys_ioctl::_IOC_NRSHIFT) - | ($size << $crate::sys_ioctl::_IOC_SIZESHIFT), - ) - } - }; -} - -/// Declare an ioctl that transfers no data. -#[macro_export] -macro_rules! ioctl_io_nr { - ($name:ident, $ty:expr, $nr:expr) => { - ioctl_ioc_nr!($name, $crate::sys_ioctl::_IOC_NONE, $ty, $nr, 0); - }; -} - -/// Declare an ioctl that reads data. -#[macro_export] -macro_rules! ioctl_ior_nr { - ($name:ident, $ty:expr, $nr:expr, $size:ty) => { - ioctl_ioc_nr!( - $name, - $crate::sys_ioctl::_IOC_READ, - $ty, - $nr, - ::std::mem::size_of::<$size>() as u32 - ); - }; -} - -/// Declare an ioctl that writes data. -#[macro_export] -macro_rules! ioctl_iow_nr { - ($name:ident, $ty:expr, $nr:expr, $size:ty) => { - ioctl_ioc_nr!( - $name, - $crate::sys_ioctl::_IOC_WRITE, - $ty, - $nr, - ::std::mem::size_of::<$size>() as u32 - ); - }; -} - -/// Declare an ioctl that reads and writes data. -#[macro_export] -macro_rules! ioctl_iowr_nr { - ($name:ident, $ty:expr, $nr:expr, $size:ty) => { - ioctl_ioc_nr!( - $name, - $crate::sys_ioctl::_IOC_READ | $crate::sys_ioctl::_IOC_WRITE, - $ty, - $nr, - ::std::mem::size_of::<$size>() as u32 - ); - }; -} - -pub const _IOC_NRBITS: c_uint = 8; -pub const _IOC_TYPEBITS: c_uint = 8; -pub const _IOC_SIZEBITS: c_uint = 14; -pub const _IOC_DIRBITS: c_uint = 2; -pub const _IOC_NRMASK: c_uint = 255; -pub const _IOC_TYPEMASK: c_uint = 255; -pub const _IOC_SIZEMASK: c_uint = 16383; -pub const _IOC_DIRMASK: c_uint = 3; -pub const _IOC_NRSHIFT: c_uint = 0; -pub const _IOC_TYPESHIFT: c_uint = 8; -pub const _IOC_SIZESHIFT: c_uint = 16; -pub const _IOC_DIRSHIFT: c_uint = 30; -pub const _IOC_NONE: c_uint = 0; -pub const _IOC_WRITE: c_uint = 1; -pub const _IOC_READ: c_uint = 2; -pub const IOC_IN: c_uint = 1_073_741_824; -pub const IOC_OUT: c_uint = 2_147_483_648; -pub const IOC_INOUT: c_uint = 3_221_225_472; -pub const IOCSIZE_MASK: c_uint = 1_073_676_288; -pub const IOCSIZE_SHIFT: c_uint = 16; - -// The type of the `req` parameter is different for the `musl` library. This will enable -// successful build for other non-musl libraries. -#[cfg(target_env = "musl")] -type IoctlRequest = c_int; -#[cfg(not(target_env = "musl"))] -type IoctlRequest = c_ulong; - -/// Run an ioctl with no arguments. -pub unsafe fn ioctl(fd: &F, req: c_ulong) -> c_int { - libc::ioctl(fd.as_raw_fd(), req as IoctlRequest, 0) -} - -/// Run an ioctl with a single value argument. -pub unsafe fn ioctl_with_val(fd: &F, req: c_ulong, arg: c_ulong) -> c_int { - libc::ioctl(fd.as_raw_fd(), req as IoctlRequest, arg) -} - -/// Run an ioctl with an immutable reference. -pub unsafe fn ioctl_with_ref(fd: &F, req: c_ulong, arg: &T) -> c_int { - libc::ioctl( - fd.as_raw_fd(), - req as IoctlRequest, - arg as *const T as *const c_void, - ) -} - -/// Run an ioctl with a mutable reference. -pub unsafe fn ioctl_with_mut_ref(fd: &F, req: c_ulong, arg: &mut T) -> c_int { - libc::ioctl( - fd.as_raw_fd(), - req as IoctlRequest, - arg as *mut T as *mut c_void, - ) -} - -/// Run an ioctl with a raw pointer. -pub unsafe fn ioctl_with_ptr(fd: &F, req: c_ulong, arg: *const T) -> c_int { - libc::ioctl(fd.as_raw_fd(), req as IoctlRequest, arg as *const c_void) -} - -/// Run an ioctl with a mutable raw pointer. -pub unsafe fn ioctl_with_mut_ptr(fd: &F, req: c_ulong, arg: *mut T) -> c_int { - libc::ioctl(fd.as_raw_fd(), req as IoctlRequest, arg as *mut c_void) -} - -#[cfg(test)] -mod tests { - const TUNTAP: ::std::os::raw::c_uint = 0x54; - const KVMIO: ::std::os::raw::c_uint = 0xAE; - - ioctl_io_nr!(KVM_CREATE_VM, KVMIO, 0x01); - ioctl_ior_nr!(TUNGETFEATURES, TUNTAP, 0xcf, ::std::os::raw::c_uint); - ioctl_iow_nr!(TUNSETQUEUE, TUNTAP, 0xd9, ::std::os::raw::c_int); - ioctl_iowr_nr!(KVM_GET_MSR_INDEX_LIST, KVMIO, 0x2, ::std::os::raw::c_int); - - #[test] - fn ioctl_macros() { - assert_eq!(0x0000AE01, KVM_CREATE_VM()); - assert_eq!(0x800454CF, TUNGETFEATURES()); - assert_eq!(0x400454D9, TUNSETQUEUE()); - assert_eq!(0xC004AE02, KVM_GET_MSR_INDEX_LIST()); - } -} From 98ba7c9e36c49757fc6c669814dd4e7dff6f0370 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Mon, 28 Oct 2019 22:24:44 +0100 Subject: [PATCH 077/332] fix irqchip related tests On Linux Kernel 4.14 you could create an irq event before creating the irqchip. This was a bug which was fixed starting with 4.15. Signed-off-by: Andreea Florescu --- src/ioctls/vm.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 7974ae8..be00397 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -798,8 +798,10 @@ impl VmFd { /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// let evtfd = unsafe { eventfd(0, EFD_NONBLOCK) }; - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - /// vm.register_irqfd(evtfd, 0).unwrap(); + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { + /// vm.create_irq_chip().unwrap(); + /// vm.register_irqfd(evtfd, 0).unwrap(); + /// } /// ``` /// #[cfg(any( @@ -841,10 +843,11 @@ impl VmFd { /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// let evtfd = unsafe { eventfd(0, EFD_NONBLOCK) }; - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - /// vm.register_irqfd(evtfd, 0).unwrap(); - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - /// vm.unregister_irqfd(evtfd, 0).unwrap(); + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { + /// vm.create_irq_chip().unwrap(); + /// vm.register_irqfd(evtfd, 0).unwrap(); + /// vm.unregister_irqfd(evtfd, 0).unwrap(); + /// } /// ``` /// #[cfg(any( @@ -1266,6 +1269,7 @@ mod tests { let evtfd2 = unsafe { eventfd(0, EFD_NONBLOCK) }; let evtfd3 = unsafe { eventfd(0, EFD_NONBLOCK) }; if cfg!(any(target_arch = "x86", target_arch = "x86_64")) { + assert!(vm_fd.create_irq_chip().is_ok()); assert!(vm_fd.register_irqfd(evtfd1, 4).is_ok()); assert!(vm_fd.register_irqfd(evtfd2, 8).is_ok()); assert!(vm_fd.register_irqfd(evtfd3, 4).is_ok()); @@ -1292,6 +1296,7 @@ mod tests { let evtfd2 = unsafe { eventfd(0, EFD_NONBLOCK) }; let evtfd3 = unsafe { eventfd(0, EFD_NONBLOCK) }; if cfg!(any(target_arch = "x86", target_arch = "x86_64")) { + assert!(vm_fd.create_irq_chip().is_ok()); assert!(vm_fd.register_irqfd(evtfd1, 4).is_ok()); assert!(vm_fd.register_irqfd(evtfd2, 8).is_ok()); assert!(vm_fd.register_irqfd(evtfd3, 4).is_ok()); From f5c263c05abb6010529367a006292af311f0d649 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Tue, 29 Oct 2019 14:43:04 +0100 Subject: [PATCH 078/332] replace RawFd with EventFd Functions that receive RawFds need to be declared unsafe. In this particular case the functions were the ones handling EventFds for which we already have a safe wrapper defined in vmm-sys-util. Use that wrapper instead of marking them as unsafe. Signed-off-by: Andreea Florescu --- src/ioctls/vm.rs | 153 ++++++++++++++++++++++++----------------------- 1 file changed, 78 insertions(+), 75 deletions(-) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index be00397..9f4ddaf 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -20,6 +20,7 @@ use ioctls::vcpu::new_vcpu; use ioctls::vcpu::VcpuFd; use ioctls::KvmRunWrapper; use kvm_ioctls::*; +use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_val}; /// An address either in programmable I/O space or in memory mapped I/O space. @@ -530,7 +531,7 @@ impl VmFd { /// /// # Arguments /// - /// * `fd` - FD which will be signaled. When signaling, the usual `vmexit` to userspace + /// * `fd` - `EventFd` which will be signaled. When signaling, the usual `vmexit` to userspace /// is prevented. /// * `addr` - Address being written to. /// * `datamatch` - Limits signaling `fd` to only the cases where the value being written is @@ -542,23 +543,24 @@ impl VmFd { /// ```rust /// # extern crate kvm_ioctls; /// extern crate libc; + /// extern crate vmm_sys_util; /// # use kvm_ioctls::{IoEventAddress, Kvm, NoDatamatch}; /// use libc::{eventfd, EFD_NONBLOCK}; - /// + /// use vmm_sys_util::eventfd::EventFd; /// let kvm = Kvm::new().unwrap(); /// let vm_fd = kvm.create_vm().unwrap(); - /// let evtfd = unsafe { eventfd(0, EFD_NONBLOCK) }; + /// let evtfd = EventFd::new(EFD_NONBLOCK).unwrap(); /// vm_fd - /// .register_ioevent(evtfd, &IoEventAddress::Pio(0xf4), NoDatamatch) + /// .register_ioevent(&evtfd, &IoEventAddress::Pio(0xf4), NoDatamatch) /// .unwrap(); /// vm_fd - /// .register_ioevent(evtfd, &IoEventAddress::Mmio(0x1000), NoDatamatch) + /// .register_ioevent(&evtfd, &IoEventAddress::Mmio(0x1000), NoDatamatch) /// .unwrap(); /// ``` /// pub fn register_ioevent>( &self, - fd: RawFd, + fd: &EventFd, addr: &IoEventAddress, datamatch: T, ) -> Result<()> { @@ -577,7 +579,7 @@ impl VmFd { IoEventAddress::Pio(ref p) => *p as u64, IoEventAddress::Mmio(ref m) => *m, }, - fd, + fd: fd.as_raw_fd(), flags, ..Default::default() }; @@ -609,31 +611,31 @@ impl VmFd { /// ```rust /// # extern crate kvm_ioctls; /// extern crate libc; + /// extern crate vmm_sys_util; /// # use kvm_ioctls::{IoEventAddress, Kvm, NoDatamatch}; - /// use libc::{eventfd, EFD_NONBLOCK}; + /// use libc::EFD_NONBLOCK; + /// use vmm_sys_util::eventfd::EventFd; /// /// let kvm = Kvm::new().unwrap(); /// let vm_fd = kvm.create_vm().unwrap(); - /// let evtfd = unsafe { eventfd(0, EFD_NONBLOCK) }; + /// let evtfd = EventFd::new(EFD_NONBLOCK).unwrap(); /// let pio_addr = IoEventAddress::Pio(0xf4); /// let mmio_addr = IoEventAddress::Mmio(0x1000); /// vm_fd - /// .register_ioevent(evtfd, &pio_addr, NoDatamatch) + /// .register_ioevent(&evtfd, &pio_addr, NoDatamatch) /// .unwrap(); /// vm_fd - /// .register_ioevent(evtfd, &mmio_addr, NoDatamatch) + /// .register_ioevent(&evtfd, &mmio_addr, NoDatamatch) + /// .unwrap(); + /// vm_fd + /// .unregister_ioevent(&evtfd, &pio_addr) + /// .unwrap(); + /// vm_fd + /// .unregister_ioevent(&evtfd, &mmio_addr) /// .unwrap(); - /// unsafe { - /// vm_fd - /// .unregister_ioevent(evtfd, &pio_addr) - /// .unwrap(); - /// vm_fd - /// .unregister_ioevent(evtfd, &mmio_addr) - /// .unwrap(); - /// }; /// ``` /// - pub unsafe fn unregister_ioevent(&self, fd: RawFd, addr: &IoEventAddress) -> Result<()> { + pub fn unregister_ioevent(&self, fd: &EventFd, addr: &IoEventAddress) -> Result<()> { let mut flags = 1 << kvm_ioeventfd_flag_nr_deassign; if let IoEventAddress::Pio(_) = *addr { flags |= 1 << kvm_ioeventfd_flag_nr_pio @@ -644,13 +646,13 @@ impl VmFd { IoEventAddress::Pio(ref p) => *p as u64, IoEventAddress::Mmio(ref m) => *m, }, - fd, + fd: fd.as_raw_fd(), flags, ..Default::default() }; // Safe because we know that our file is a VM fd, we know the kernel will only read the // correct amount of memory from our pointer, and we verify the return result. - let ret = ioctl_with_ref(self, KVM_IOEVENTFD(), &ioeventfd); + let ret = unsafe { ioctl_with_ref(self, KVM_IOEVENTFD(), &ioeventfd) }; if ret == 0 { Ok(()) } else { @@ -785,7 +787,7 @@ impl VmFd { /// /// # Arguments /// - /// * `fd` - Event to be signaled. + /// * `fd` - `EventFd` to be signaled. /// * `gsi` - IRQ to be triggered. /// /// # Example @@ -793,14 +795,16 @@ impl VmFd { /// ```rust /// # extern crate kvm_ioctls; /// # extern crate libc; + /// # extern crate vmm_sys_util; /// # use kvm_ioctls::Kvm; - /// # use libc::{eventfd, EFD_NONBLOCK}; + /// # use libc::EFD_NONBLOCK; + /// # use vmm_sys_util::eventfd::EventFd; /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); - /// let evtfd = unsafe { eventfd(0, EFD_NONBLOCK) }; + /// let evtfd = EventFd::new(EFD_NONBLOCK).unwrap(); /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { /// vm.create_irq_chip().unwrap(); - /// vm.register_irqfd(evtfd, 0).unwrap(); + /// vm.register_irqfd(&evtfd, 0).unwrap(); /// } /// ``` /// @@ -810,9 +814,9 @@ impl VmFd { target_arch = "arm", target_arch = "aarch64" ))] - pub fn register_irqfd(&self, fd: RawFd, gsi: u32) -> Result<()> { + pub fn register_irqfd(&self, fd: &EventFd, gsi: u32) -> Result<()> { let irqfd = kvm_irqfd { - fd: fd as u32, + fd: fd.as_raw_fd() as u32, gsi, ..Default::default() }; @@ -830,7 +834,7 @@ impl VmFd { /// /// # Arguments /// - /// * `fd` - Event to be signaled. + /// * `fd` - `EventFd` to be signaled. /// * `gsi` - IRQ to be triggered. /// /// # Example @@ -838,15 +842,17 @@ impl VmFd { /// ```rust /// # extern crate kvm_ioctls; /// # extern crate libc; + /// # extern crate vmm_sys_util; /// # use kvm_ioctls::Kvm; - /// # use libc::{eventfd, EFD_NONBLOCK}; + /// # use libc::EFD_NONBLOCK; + /// # use vmm_sys_util::eventfd::EventFd; /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); - /// let evtfd = unsafe { eventfd(0, EFD_NONBLOCK) }; + /// let evtfd = EventFd::new(EFD_NONBLOCK).unwrap(); /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { /// vm.create_irq_chip().unwrap(); - /// vm.register_irqfd(evtfd, 0).unwrap(); - /// vm.unregister_irqfd(evtfd, 0).unwrap(); + /// vm.register_irqfd(&evtfd, 0).unwrap(); + /// vm.unregister_irqfd(&evtfd, 0).unwrap(); /// } /// ``` /// @@ -856,9 +862,9 @@ impl VmFd { target_arch = "arm", target_arch = "aarch64" ))] - pub fn unregister_irqfd(&self, fd: RawFd, gsi: u32) -> Result<()> { + pub fn unregister_irqfd(&self, fd: &EventFd, gsi: u32) -> Result<()> { let irqfd = kvm_irqfd { - fd: fd as u32, + fd: fd.as_raw_fd() as u32, gsi, flags: KVM_IRQFD_FLAG_DEASSIGN, ..Default::default() @@ -1106,7 +1112,7 @@ mod tests { use super::*; use {Cap, Kvm}; - use libc::{eventfd, EFD_NONBLOCK}; + use libc::EFD_NONBLOCK; #[test] fn test_set_invalid_memory() { @@ -1206,24 +1212,24 @@ mod tests { let kvm = Kvm::new().unwrap(); let vm_fd = kvm.create_vm().unwrap(); - let evtfd = unsafe { eventfd(0, EFD_NONBLOCK) }; + let evtfd = EventFd::new(EFD_NONBLOCK).unwrap(); assert!(vm_fd - .register_ioevent(evtfd, &IoEventAddress::Pio(0xf4), NoDatamatch) + .register_ioevent(&evtfd, &IoEventAddress::Pio(0xf4), NoDatamatch) .is_ok()); assert!(vm_fd - .register_ioevent(evtfd, &IoEventAddress::Mmio(0x1000), NoDatamatch) + .register_ioevent(&evtfd, &IoEventAddress::Mmio(0x1000), NoDatamatch) .is_ok()); assert!(vm_fd - .register_ioevent(evtfd, &IoEventAddress::Pio(0xc1), 0x7fu8) + .register_ioevent(&evtfd, &IoEventAddress::Pio(0xc1), 0x7fu8) .is_ok()); assert!(vm_fd - .register_ioevent(evtfd, &IoEventAddress::Pio(0xc2), 0x1337u16) + .register_ioevent(&evtfd, &IoEventAddress::Pio(0xc2), 0x1337u16) .is_ok()); assert!(vm_fd - .register_ioevent(evtfd, &IoEventAddress::Pio(0xc4), 0xdead_beefu32) + .register_ioevent(&evtfd, &IoEventAddress::Pio(0xc4), 0xdead_beefu32) .is_ok()); assert!(vm_fd - .register_ioevent(evtfd, &IoEventAddress::Pio(0xc8), 0xdead_beef_dead_beefu64) + .register_ioevent(&evtfd, &IoEventAddress::Pio(0xc8), 0xdead_beef_dead_beefu64) .is_ok()); } @@ -1233,26 +1239,26 @@ mod tests { let kvm = Kvm::new().unwrap(); let vm_fd = kvm.create_vm().unwrap(); - let evtfd = unsafe { eventfd(0, EFD_NONBLOCK) }; + let evtfd = EventFd::new(EFD_NONBLOCK).unwrap(); let pio_addr = IoEventAddress::Pio(0xf4); let mmio_addr = IoEventAddress::Mmio(0x1000); // First try to unregister addresses which have not been registered. - assert!(unsafe { vm_fd.unregister_ioevent(evtfd, &pio_addr) }.is_err()); - assert!(unsafe { vm_fd.unregister_ioevent(evtfd, &mmio_addr) }.is_err()); + assert!(vm_fd.unregister_ioevent(&evtfd, &pio_addr).is_err()); + assert!(vm_fd.unregister_ioevent(&evtfd, &mmio_addr).is_err()); // Now register the addresses assert!(vm_fd - .register_ioevent(evtfd, &pio_addr, NoDatamatch) + .register_ioevent(&evtfd, &pio_addr, NoDatamatch) .is_ok()); assert!(vm_fd - .register_ioevent(evtfd, &mmio_addr, NoDatamatch) + .register_ioevent(&evtfd, &mmio_addr, NoDatamatch) .is_ok()); // Try again unregistering the addresses. This time it should work // since they have been previously registered. - assert!(unsafe { vm_fd.unregister_ioevent(evtfd, &pio_addr) }.is_ok()); - assert!(unsafe { vm_fd.unregister_ioevent(evtfd, &mmio_addr) }.is_ok()); + assert!(vm_fd.unregister_ioevent(&evtfd, &pio_addr).is_ok()); + assert!(vm_fd.unregister_ioevent(&evtfd, &mmio_addr).is_ok()); } #[test] @@ -1265,21 +1271,21 @@ mod tests { fn test_register_irqfd() { let kvm = Kvm::new().unwrap(); let vm_fd = kvm.create_vm().unwrap(); - let evtfd1 = unsafe { eventfd(0, EFD_NONBLOCK) }; - let evtfd2 = unsafe { eventfd(0, EFD_NONBLOCK) }; - let evtfd3 = unsafe { eventfd(0, EFD_NONBLOCK) }; + let evtfd1 = EventFd::new(EFD_NONBLOCK).unwrap(); + let evtfd2 = EventFd::new(EFD_NONBLOCK).unwrap(); + let evtfd3 = EventFd::new(EFD_NONBLOCK).unwrap(); if cfg!(any(target_arch = "x86", target_arch = "x86_64")) { assert!(vm_fd.create_irq_chip().is_ok()); - assert!(vm_fd.register_irqfd(evtfd1, 4).is_ok()); - assert!(vm_fd.register_irqfd(evtfd2, 8).is_ok()); - assert!(vm_fd.register_irqfd(evtfd3, 4).is_ok()); + assert!(vm_fd.register_irqfd(&evtfd1, 4).is_ok()); + assert!(vm_fd.register_irqfd(&evtfd2, 8).is_ok()); + assert!(vm_fd.register_irqfd(&evtfd3, 4).is_ok()); } // On aarch64, this fails because setting up the interrupt controller is mandatory before // registering any IRQ. // On x86_64 this fails as the event fd was already matched with a GSI. - assert!(vm_fd.register_irqfd(evtfd3, 4).is_err()); - assert!(vm_fd.register_irqfd(evtfd3, 5).is_err()); + assert!(vm_fd.register_irqfd(&evtfd3, 4).is_err()); + assert!(vm_fd.register_irqfd(&evtfd3, 5).is_err()); } #[test] @@ -1292,31 +1298,28 @@ mod tests { fn test_unregister_irqfd() { let kvm = Kvm::new().unwrap(); let vm_fd = kvm.create_vm().unwrap(); - let evtfd1 = unsafe { eventfd(0, EFD_NONBLOCK) }; - let evtfd2 = unsafe { eventfd(0, EFD_NONBLOCK) }; - let evtfd3 = unsafe { eventfd(0, EFD_NONBLOCK) }; + let evtfd1 = EventFd::new(EFD_NONBLOCK).unwrap(); + let evtfd2 = EventFd::new(EFD_NONBLOCK).unwrap(); + let evtfd3 = EventFd::new(EFD_NONBLOCK).unwrap(); if cfg!(any(target_arch = "x86", target_arch = "x86_64")) { assert!(vm_fd.create_irq_chip().is_ok()); - assert!(vm_fd.register_irqfd(evtfd1, 4).is_ok()); - assert!(vm_fd.register_irqfd(evtfd2, 8).is_ok()); - assert!(vm_fd.register_irqfd(evtfd3, 4).is_ok()); - assert!(vm_fd.unregister_irqfd(evtfd2, 8).is_ok()); + assert!(vm_fd.register_irqfd(&evtfd1, 4).is_ok()); + assert!(vm_fd.register_irqfd(&evtfd2, 8).is_ok()); + assert!(vm_fd.register_irqfd(&evtfd3, 4).is_ok()); + assert!(vm_fd.unregister_irqfd(&evtfd2, 8).is_ok()); // KVM irqfd doesn't report failure on this case:( - assert!(vm_fd.unregister_irqfd(evtfd2, 8).is_ok()); + assert!(vm_fd.unregister_irqfd(&evtfd2, 8).is_ok()); } // Duplicated eventfd registration. // On aarch64, this fails because setting up the interrupt controller is mandatory before // registering any IRQ. // On x86_64 this fails as the event fd was already matched with a GSI. - assert!(vm_fd.register_irqfd(evtfd3, 4).is_err()); - assert!(vm_fd.register_irqfd(evtfd3, 5).is_err()); + assert!(vm_fd.register_irqfd(&evtfd3, 4).is_err()); + assert!(vm_fd.register_irqfd(&evtfd3, 5).is_err()); // KVM irqfd doesn't report failure on this case:( - assert!(vm_fd.unregister_irqfd(evtfd3, 5).is_ok()); - - // Check for invalid fd. - assert!(vm_fd.unregister_irqfd(-1, 5).is_err()); + assert!(vm_fd.unregister_irqfd(&evtfd3, 5).is_ok()); } #[test] @@ -1351,9 +1354,9 @@ mod tests { get_raw_errno(faulty_vm_fd.create_pit2(kvm_pit_config::default())), badf_errno ); - let event_fd = unsafe { eventfd(0, EFD_NONBLOCK) }; + let event_fd = EventFd::new(EFD_NONBLOCK).unwrap(); assert_eq!( - get_raw_errno(faulty_vm_fd.register_ioevent(event_fd, &IoEventAddress::Pio(0), 0u64)), + get_raw_errno(faulty_vm_fd.register_ioevent(&event_fd, &IoEventAddress::Pio(0), 0u64)), badf_errno ); assert_eq!( @@ -1375,7 +1378,7 @@ mod tests { badf_errno ); assert_eq!( - get_raw_errno(faulty_vm_fd.register_irqfd(event_fd, 0)), + get_raw_errno(faulty_vm_fd.register_irqfd(&event_fd, 0)), badf_errno ); From 681745a7776d60e390bcf62eefd48531a5f8ec47 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Tue, 29 Oct 2019 19:09:34 +0100 Subject: [PATCH 079/332] remove Cargo.lock from tracked files We decided against adding Cargo.lock to repositories. Removed the file from the repository and added Cargo.lock to .gitignore. Signed-off-by: Andreea Florescu --- .gitignore | 1 + Cargo.lock | 40 ---------------------------------------- 2 files changed, 1 insertion(+), 40 deletions(-) delete mode 100644 Cargo.lock diff --git a/.gitignore b/.gitignore index d827bd6..88b1076 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +Cargo.lock /target **/*.rs.bk **/.pytest_cache/ diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index 177b0a6..0000000 --- a/Cargo.lock +++ /dev/null @@ -1,40 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "byteorder" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "kvm-bindings" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "kvm-ioctls" -version = "0.2.0" -dependencies = [ - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "kvm-bindings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "vmm-sys-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libc" -version = "0.2.48" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "vmm-sys-util" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[metadata] -"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" -"checksum kvm-bindings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c223e8703d2eb76d990c5f58e29c85b0f6f50e24b823babde927948e7c71fc03" -"checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047" -"checksum vmm-sys-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "46996f56aeae31fbc0532ae57a944e00089302f03b18c10c76eebfd9249f4a6c" From c415f078a77d1cca36f8fcbfb426068dc961a4e8 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Thu, 7 Nov 2019 10:28:58 +0200 Subject: [PATCH 080/332] add .idea to .gitignore Signed-off-by: Adrian Catangiu --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 88b1076..ac6209e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ Cargo.lock /target +.idea **/*.rs.bk **/.pytest_cache/ **/__pycache__/* From f57c097b4ad5d2b5f7073ba3eb30692787526c29 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Thu, 7 Nov 2019 12:33:15 +0200 Subject: [PATCH 081/332] use safe fam-wrapper CpuId from kvm-bindings Use safe fam-wrapper over kvm_cpuid2 exported by the kvm-bindings crate instead of implementing our own. Coverage decreases by 0.1% because of the removed code. Signed-off-by: Adrian Catangiu --- Cargo.toml | 2 +- coverage_config.json | 2 +- src/ioctls/mod.rs | 78 -------------------------------------------- src/ioctls/system.rs | 41 +++++++++++------------ src/ioctls/vcpu.rs | 22 +++++++------ src/lib.rs | 2 -- 6 files changed, 35 insertions(+), 112 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2cc15be..d9d587f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ license = "Apache-2.0 OR MIT" [dependencies] libc = ">=0.2.39" -kvm-bindings = ">=0.1.0" +kvm-bindings = { version = ">=0.2.0", features = ["fam-wrappers"] } vmm-sys-util = ">=0.1.1" [dev-dependencies] diff --git a/coverage_config.json b/coverage_config.json index 2ddf3a6..b15b2eb 100644 --- a/coverage_config.json +++ b/coverage_config.json @@ -1,5 +1,5 @@ { - "coverage_score": 92.0, + "coverage_score": 91.9, "exclude_path": "", "crate_features": "" } diff --git a/src/ioctls/mod.rs b/src/ioctls/mod.rs index 0de8171..1e2b9fe 100644 --- a/src/ioctls/mod.rs +++ b/src/ioctls/mod.rs @@ -13,10 +13,6 @@ use std::ptr::null_mut; use std::result; use kvm_bindings::kvm_run; -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -use kvm_bindings::{kvm_cpuid2, kvm_cpuid_entry2}; -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -use vmm_sys_util::fam::{FamStruct, FamStructWrapper}; /// Wrappers over KVM device ioctls. pub mod device; @@ -66,80 +62,6 @@ fn vec_with_array_field(count: usize) -> Vec { vec_with_size_in_bytes(vec_size_bytes) } -/// Maximum number of CPUID entries that can be returned by a call to KVM ioctls. -/// -/// This value is taken from Linux Kernel v4.14.13 (arch/x86/include/asm/kvm_host.h). -/// It can be used for calls to [get_supported_cpuid](struct.Kvm.html#method.get_supported_cpuid) and -/// [get_emulated_cpuid](struct.Kvm.html#method.get_emulated_cpuid). -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -pub const MAX_KVM_CPUID_ENTRIES: usize = 80; - -/// Maximum number of MSRs KVM supports (See arch/x86/kvm/x86.c). -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -pub const KVM_MAX_MSR_ENTRIES: usize = 256; - -// We can't implement FamStruct directly for kvm_cpuid2. -// We would get an "impl doesn't use types inside crate" error. -// We have to create a shadow structure as a workaround. -#[derive(Default)] -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -pub struct KvmCpuId(kvm_cpuid2); - -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -impl Clone for KvmCpuId { - fn clone(&self) -> Self { - let KvmCpuId(cpuid) = self; - KvmCpuId(kvm_cpuid2 { - nent: cpuid.nent, - padding: cpuid.padding, - entries: cpuid.entries.clone(), - }) - } -} - -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -unsafe impl FamStruct for KvmCpuId { - type Entry = kvm_cpuid_entry2; - - fn len(&self) -> usize { - let KvmCpuId(cpuid) = self; - cpuid.nent as usize - } - - fn set_len(&mut self, len: usize) { - let KvmCpuId(cpuid) = self; - cpuid.nent = len as u32; - } - - fn max_len() -> usize { - MAX_KVM_CPUID_ENTRIES - } - - fn as_slice(&self) -> &[Self::Entry] { - let len = self.len(); - let KvmCpuId(cpuid) = self; - // This is safe because the provided length is correct. - unsafe { cpuid.entries.as_slice(len) } - } - - fn as_mut_slice(&mut self) -> &mut [Self::Entry] { - let len = self.len(); - let KvmCpuId(cpuid) = self; - // This is safe because the provided length is correct. - unsafe { cpuid.entries.as_mut_slice(len) } - } -} - -/// Wrapper over the `kvm_cpuid2` structure. -/// -/// The `kvm_cpuid2` structure contains a flexible array member. For details check the -/// [KVM API](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt) -/// documentation on `kvm_cpuid2`. To provide safe access to -/// the array elements, this type is implemented using -/// [FamStructWrapper](../vmm_sys_util/fam/struct.FamStructWrapper.html). -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -pub type CpuId = FamStructWrapper; - /// Safe wrapper over the `kvm_run` struct. /// /// The wrapper is needed for sending the pointer to `kvm_run` between diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index 60339d5..2af0f6a 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -14,12 +14,11 @@ use std::os::raw::{c_char, c_ulong}; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use cap::Cap; -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use ioctls::vec_with_array_field; use ioctls::vm::{new_vmfd, VmFd}; -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -use ioctls::CpuId; use ioctls::Result; +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +use kvm_bindings::CpuId; use kvm_ioctls::*; use vmm_sys_util::ioctl::{ioctl, ioctl_with_val}; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] @@ -265,12 +264,14 @@ impl Kvm { /// # Example /// /// ``` - /// use kvm_ioctls::{Kvm, MAX_KVM_CPUID_ENTRIES}; + /// extern crate kvm_bindings; + /// use kvm_bindings::KVM_MAX_CPUID_ENTRIES; + /// use kvm_ioctls::Kvm; /// /// let kvm = Kvm::new().unwrap(); - /// let mut cpuid = kvm.get_emulated_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); + /// let mut cpuid = kvm.get_emulated_cpuid(KVM_MAX_CPUID_ENTRIES).unwrap(); /// let cpuid_entries = cpuid.as_mut_slice(); - /// assert!(cpuid_entries.len() <= MAX_KVM_CPUID_ENTRIES); + /// assert!(cpuid_entries.len() <= KVM_MAX_CPUID_ENTRIES); /// ``` /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] @@ -290,12 +291,14 @@ impl Kvm { /// # Example /// /// ``` - /// use kvm_ioctls::{Kvm, MAX_KVM_CPUID_ENTRIES}; + /// extern crate kvm_bindings; + /// use kvm_bindings::KVM_MAX_CPUID_ENTRIES; + /// use kvm_ioctls::Kvm; /// /// let kvm = Kvm::new().unwrap(); - /// let mut cpuid = kvm.get_emulated_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); + /// let mut cpuid = kvm.get_emulated_cpuid(KVM_MAX_CPUID_ENTRIES).unwrap(); /// let cpuid_entries = cpuid.as_mut_slice(); - /// assert!(cpuid_entries.len() <= MAX_KVM_CPUID_ENTRIES); + /// assert!(cpuid_entries.len() <= KVM_MAX_CPUID_ENTRIES); /// ``` /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] @@ -310,7 +313,7 @@ impl Kvm { /// # Example /// /// ``` - /// use kvm_ioctls::{Kvm, MAX_KVM_CPUID_ENTRIES}; + /// use kvm_ioctls::Kvm; /// /// let kvm = Kvm::new().unwrap(); /// let msr_index_list = kvm.get_msr_index_list().unwrap(); @@ -406,9 +409,9 @@ impl FromRawFd for Kvm { mod tests { use super::*; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - use vmm_sys_util::fam::FamStruct; + use kvm_bindings::KVM_MAX_CPUID_ENTRIES; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - use MAX_KVM_CPUID_ENTRIES; + use vmm_sys_util::fam::FamStruct; #[test] fn test_kvm_new() { @@ -454,20 +457,20 @@ mod tests { #[test] fn test_get_supported_cpuid() { let kvm = Kvm::new().unwrap(); - let mut cpuid = kvm.get_supported_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); + let mut cpuid = kvm.get_supported_cpuid(KVM_MAX_CPUID_ENTRIES).unwrap(); let cpuid_entries = cpuid.as_mut_slice(); assert!(cpuid_entries.len() > 0); - assert!(cpuid_entries.len() <= MAX_KVM_CPUID_ENTRIES); + assert!(cpuid_entries.len() <= KVM_MAX_CPUID_ENTRIES); } #[test] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn test_get_emulated_cpuid() { let kvm = Kvm::new().unwrap(); - let mut cpuid = kvm.get_emulated_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); + let mut cpuid = kvm.get_emulated_cpuid(KVM_MAX_CPUID_ENTRIES).unwrap(); let cpuid_entries = cpuid.as_mut_slice(); assert!(cpuid_entries.len() > 0); - assert!(cpuid_entries.len() <= MAX_KVM_CPUID_ENTRIES); + assert!(cpuid_entries.len() <= KVM_MAX_CPUID_ENTRIES); } #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] @@ -480,10 +483,8 @@ mod tests { assert!(rawfd >= 0); let kvm = unsafe { Kvm::from_raw_fd(rawfd) }; - let cpuid_1 = kvm.get_supported_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); - let mut cpuid_2 = cpuid_1.clone(); - assert!(cpuid_1 == cpuid_2); - cpuid_2 = CpuId::new(cpuid_1.as_fam_struct_ref().len()); + let cpuid_1 = kvm.get_supported_cpuid(KVM_MAX_CPUID_ENTRIES).unwrap(); + let cpuid_2 = CpuId::new(cpuid_1.as_fam_struct_ref().len()); assert!(cpuid_1 != cpuid_2); } diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 1e14217..cd1f39f 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -11,9 +11,9 @@ use std::fs::File; use std::io; use std::os::unix::io::{AsRawFd, RawFd}; -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -use ioctls::CpuId; use ioctls::{KvmRunWrapper, Result}; +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +use kvm_bindings::CpuId; use kvm_ioctls::*; use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ref}; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] @@ -317,9 +317,10 @@ impl VcpuFd { /// ```rust /// # extern crate kvm_ioctls; /// # extern crate kvm_bindings; - /// # use kvm_ioctls::{Kvm, MAX_KVM_CPUID_ENTRIES}; + /// # use kvm_bindings::KVM_MAX_CPUID_ENTRIES; + /// # use kvm_ioctls::Kvm; /// let kvm = Kvm::new().unwrap(); - /// let mut kvm_cpuid = kvm.get_supported_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); + /// let mut kvm_cpuid = kvm.get_supported_cpuid(KVM_MAX_CPUID_ENTRIES).unwrap(); /// let vm = kvm.create_vm().unwrap(); /// let vcpu = vm.create_vcpu(0).unwrap(); /// @@ -365,11 +366,12 @@ impl VcpuFd { /// ```rust /// # extern crate kvm_ioctls; /// # extern crate kvm_bindings; - /// # use kvm_ioctls::{Kvm, MAX_KVM_CPUID_ENTRIES}; + /// # use kvm_bindings::KVM_MAX_CPUID_ENTRIES; + /// # use kvm_ioctls::Kvm; /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// let vcpu = vm.create_vcpu(0).unwrap(); - /// let cpuid = vcpu.get_cpuid2(MAX_KVM_CPUID_ENTRIES).unwrap(); + /// let cpuid = vcpu.get_cpuid2(KVM_MAX_CPUID_ENTRIES).unwrap(); /// ``` /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] @@ -1185,7 +1187,7 @@ mod tests { use super::*; use ioctls::system::Kvm; #[cfg(target_arch = "x86_64")] - use {Cap, MAX_KVM_CPUID_ENTRIES}; + use Cap; // Helper function for memory mapping `size` bytes of anonymous memory. // Panics if the mmap fails. @@ -1224,9 +1226,9 @@ mod tests { let kvm = Kvm::new().unwrap(); if kvm.check_extension(Cap::ExtCpuid) { let vm = kvm.create_vm().unwrap(); - let cpuid = kvm.get_supported_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); + let cpuid = kvm.get_supported_cpuid(KVM_MAX_CPUID_ENTRIES).unwrap(); let ncpuids = cpuid.as_slice().len(); - assert!(ncpuids <= MAX_KVM_CPUID_ENTRIES); + assert!(ncpuids <= KVM_MAX_CPUID_ENTRIES); let nr_vcpus = kvm.get_nr_vcpus(); for cpu_idx in 0..nr_vcpus { let vcpu = vm.create_vcpu(cpu_idx as u8).unwrap(); @@ -1562,7 +1564,7 @@ mod tests { faulty_vcpu_fd.set_cpuid2( &Kvm::new() .unwrap() - .get_supported_cpuid(MAX_KVM_CPUID_ENTRIES) + .get_supported_cpuid(KVM_MAX_CPUID_ENTRIES) .unwrap() ) ), diff --git a/src/lib.rs b/src/lib.rs index 2570d9c..1600160 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -192,8 +192,6 @@ pub use ioctls::device::DeviceFd; pub use ioctls::system::Kvm; pub use ioctls::vcpu::{VcpuExit, VcpuFd}; pub use ioctls::vm::{IoEventAddress, NoDatamatch, VmFd}; -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -pub use ioctls::{CpuId, KVM_MAX_MSR_ENTRIES, MAX_KVM_CPUID_ENTRIES}; // The following example is used to verify that our public // structures are exported properly. /// # Example From 652eb11dc0314d8800cb2bb25cc39ff1ad9367e4 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Thu, 7 Nov 2019 12:40:36 +0200 Subject: [PATCH 082/332] use safe wrappers Msrs and MsrList from kvm-bindings Refactor the unsafe code concerning kvm_msr_list and kvm_msrs, to work with the safe fam-wrappers MsrList and Msrs. Signed-off-by: Adrian Catangiu --- src/ioctls/mod.rs | 35 ----------- src/ioctls/system.rs | 39 ++++-------- src/ioctls/vcpu.rs | 139 ++++++++++++++++++------------------------- 3 files changed, 70 insertions(+), 143 deletions(-) diff --git a/src/ioctls/mod.rs b/src/ioctls/mod.rs index 1e2b9fe..2c84a74 100644 --- a/src/ioctls/mod.rs +++ b/src/ioctls/mod.rs @@ -6,8 +6,6 @@ // found in the THIRD-PARTY file. use std::io; -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -use std::mem::size_of; use std::os::unix::io::AsRawFd; use std::ptr::null_mut; use std::result; @@ -29,39 +27,6 @@ pub mod vm; /// is otherwise a direct mapping to Result. pub type Result = result::Result; -// Returns a `Vec` with a size in bytes at least as large as `size_in_bytes`. -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -fn vec_with_size_in_bytes(size_in_bytes: usize) -> Vec { - let rounded_size = (size_in_bytes + size_of::() - 1) / size_of::(); - let mut v = Vec::with_capacity(rounded_size); - for _ in 0..rounded_size { - v.push(T::default()) - } - v -} - -// The kvm API has many structs that resemble the following `Foo` structure: -// -// ``` -// #[repr(C)] -// struct Foo { -// some_data: u32 -// entries: __IncompleteArrayField<__u32>, -// } -// ``` -// -// In order to allocate such a structure, `size_of::()` would be too small because it would not -// include any space for `entries`. To make the allocation large enough while still being aligned -// for `Foo`, a `Vec` is created. Only the first element of `Vec` would actually be used -// as a `Foo`. The remaining memory in the `Vec` is for `entries`, which must be contiguous -// with `Foo`. This function is used to make the `Vec` with enough space for `count` entries. -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -fn vec_with_array_field(count: usize) -> Vec { - let element_space = count * size_of::(); - let vec_size_bytes = size_of::() + element_space; - vec_with_size_in_bytes(vec_size_bytes) -} - /// Safe wrapper over the `kvm_run` struct. /// /// The wrapper is needed for sending the pointer to `kvm_run` between diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index 2af0f6a..6290301 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -4,9 +4,6 @@ // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the THIRD-PARTY file. -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -use kvm_bindings::kvm_msr_list; - use libc::{open, O_CLOEXEC, O_RDWR}; use std::fs::File; use std::io; @@ -14,15 +11,14 @@ use std::os::raw::{c_char, c_ulong}; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use cap::Cap; -use ioctls::vec_with_array_field; use ioctls::vm::{new_vmfd, VmFd}; use ioctls::Result; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -use kvm_bindings::CpuId; +use kvm_bindings::{CpuId, MsrList, KVM_MAX_MSR_ENTRIES}; use kvm_ioctls::*; -use vmm_sys_util::ioctl::{ioctl, ioctl_with_val}; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -use vmm_sys_util::ioctl::{ioctl_with_mut_ptr, ioctl_with_mut_ref}; +use vmm_sys_util::ioctl::ioctl_with_mut_ptr; +use vmm_sys_util::ioctl::{ioctl, ioctl_with_val}; /// Wrapper over KVM system ioctls. pub struct Kvm { @@ -319,34 +315,25 @@ impl Kvm { /// let msr_index_list = kvm.get_msr_index_list().unwrap(); /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - pub fn get_msr_index_list(&self) -> Result> { - const MAX_KVM_MSR_ENTRIES: usize = 256; - - let mut msr_list = vec_with_array_field::(MAX_KVM_MSR_ENTRIES); - msr_list[0].nmsrs = MAX_KVM_MSR_ENTRIES as u32; + pub fn get_msr_index_list(&self) -> Result { + let mut msr_list = MsrList::new(KVM_MAX_MSR_ENTRIES); let ret = unsafe { // ioctl is unsafe. The kernel is trusted not to write beyond the bounds of the memory // allocated for the struct. The limit is read from nmsrs, which is set to the allocated // size (MAX_KVM_MSR_ENTRIES) above. - ioctl_with_mut_ref(self, KVM_GET_MSR_INDEX_LIST(), &mut msr_list[0]) + ioctl_with_mut_ptr( + self, + KVM_GET_MSR_INDEX_LIST(), + msr_list.as_mut_fam_struct_ptr(), + ) }; if ret < 0 { return Err(io::Error::last_os_error()); } - let mut nmsrs = msr_list[0].nmsrs; - - // Mapping the unsized array to a slice is unsafe because the length isn't known. Using - // the length we originally allocated with eliminates the possibility of overflow. - let indices: &[u32] = unsafe { - if nmsrs > MAX_KVM_MSR_ENTRIES as u32 { - nmsrs = MAX_KVM_MSR_ENTRIES as u32; - } - msr_list[0].indices.as_slice(nmsrs as usize) - }; - - Ok(indices.to_vec()) + // The ioctl will also update the internal `nmsrs` with the actual count. + Ok(msr_list) } /// Creates a VM fd using the KVM fd. @@ -493,7 +480,7 @@ mod tests { fn get_msr_index_list() { let kvm = Kvm::new().unwrap(); let msr_list = kvm.get_msr_index_list().unwrap(); - assert!(msr_list.len() >= 2); + assert!(msr_list.as_slice().len() >= 2); } fn get_raw_errno(result: super::Result) -> i32 { diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index cd1f39f..0a20578 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -13,7 +13,7 @@ use std::os::unix::io::{AsRawFd, RawFd}; use ioctls::{KvmRunWrapper, Result}; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -use kvm_bindings::CpuId; +use kvm_bindings::{CpuId, Msrs}; use kvm_ioctls::*; use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ref}; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] @@ -483,24 +483,35 @@ impl VcpuFd { /// # extern crate kvm_ioctls; /// # extern crate kvm_bindings; /// # use kvm_ioctls::Kvm; - /// # use kvm_bindings::kvm_msrs; + /// # use kvm_bindings::{kvm_msr_entry, Msrs}; /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// let vcpu = vm.create_vcpu(0).unwrap(); - /// let mut msrs = kvm_msrs::default(); - /// vcpu.get_msrs(&mut msrs).unwrap(); + /// // Configure the struct to say which entries we want to get. + /// let mut msrs = Msrs::from_entries(&[ + /// kvm_msr_entry { + /// index: 0x0000_0174, + /// ..Default::default() + /// }, + /// kvm_msr_entry { + /// index: 0x0000_0175, + /// ..Default::default() + /// }, + /// ]); + /// let read = vcpu.get_msrs(&mut msrs).unwrap(); + /// assert_eq!(read, 2); /// ``` /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - pub fn get_msrs(&self, msrs: &mut kvm_msrs) -> Result<(i32)> { + pub fn get_msrs(&self, msrs: &mut Msrs) -> Result { let ret = unsafe { // Here we trust the kernel not to read past the end of the kvm_msrs struct. - ioctl_with_mut_ref(self, KVM_GET_MSRS(), msrs) + ioctl_with_mut_ptr(self, KVM_GET_MSRS(), msrs.as_mut_fam_struct_ptr()) }; if ret < 0 { return Err(io::Error::last_os_error()); } - Ok(ret) + Ok(ret as usize) } /// Setup the model-specific registers (MSR) for this vCPU. @@ -518,50 +529,33 @@ impl VcpuFd { /// # extern crate kvm_ioctls; /// # extern crate kvm_bindings; /// # use kvm_ioctls::Kvm; - /// # use kvm_bindings::{kvm_msrs, kvm_msr_entry}; - /// # use std::mem; + /// # use kvm_bindings::{kvm_msr_entry, Msrs}; /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// let vcpu = vm.create_vcpu(0).unwrap(); - /// let mut msrs = kvm_msrs::default(); - /// vcpu.get_msrs(&mut msrs).unwrap(); /// - /// let msrs_entries = { + /// // Configure the entries we want to set. + /// let mut msrs = Msrs::from_entries(&[ /// kvm_msr_entry { /// index: 0x0000_0174, /// ..Default::default() - /// } - /// }; - /// - /// // Create a vector large enough to hold the MSR entry defined above in - /// // a `kvm_msrs`structure. - /// let mut msrs_vec: Vec = - /// vec![0u8; mem::size_of::() + mem::size_of::()]; - /// { - /// let kvm_msr_entries = unsafe { - /// &mut *(msrs_vec.as_mut_slice()[mem::size_of::()..].as_mut_ptr() - /// as *mut kvm_msr_entry) - /// }; - /// *kvm_msr_entries = msrs_entries; - /// } - /// let mut msrs: &mut kvm_msrs = unsafe { - /// &mut *(msrs_vec.as_ptr() as *mut kvm_msrs) - /// }; - /// msrs.nmsrs = 1; - /// assert_eq!(vcpu.set_msrs(msrs).unwrap(), 1); + /// }, + /// ]); + /// let written = vcpu.set_msrs(&msrs).unwrap(); + /// assert_eq!(written, 1); /// ``` /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - pub fn set_msrs(&self, msrs: &kvm_msrs) -> Result { + pub fn set_msrs(&self, msrs: &Msrs) -> Result { let ret = unsafe { // Here we trust the kernel not to read past the end of the kvm_msrs struct. - ioctl_with_ref(self, KVM_SET_MSRS(), msrs) + ioctl_with_ptr(self, KVM_SET_MSRS(), msrs.as_fam_struct_ptr()) }; // KVM_SET_MSRS actually returns the number of msr entries written. if ret < 0 { return Err(io::Error::last_os_error()); } - Ok(ret) + Ok(ret as usize) } /// Returns the vcpu's current "multiprocessing state". @@ -1297,68 +1291,49 @@ mod tests { #[cfg(target_arch = "x86_64")] #[test] fn msrs_test() { - use std::mem; + use vmm_sys_util::fam::FamStruct; let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); let vcpu = vm.create_vcpu(0).unwrap(); - let mut configured_entry_vec = Vec::::new(); - - configured_entry_vec.push(kvm_msr_entry { - index: 0x0000_0174, - data: 0x0, - ..Default::default() - }); - configured_entry_vec.push(kvm_msr_entry { - index: 0x0000_0175, - data: 0x1, - ..Default::default() - }); - - let vec_size_bytes = mem::size_of::() - + (configured_entry_vec.len() * mem::size_of::()); - let vec: Vec = Vec::with_capacity(vec_size_bytes); - let msrs: &mut kvm_msrs = unsafe { &mut *(vec.as_ptr() as *mut kvm_msrs) }; - unsafe { - let entries: &mut [kvm_msr_entry] = - msrs.entries.as_mut_slice(configured_entry_vec.len()); - entries.copy_from_slice(&configured_entry_vec); - } - msrs.nmsrs = configured_entry_vec.len() as u32; - assert_eq!(vcpu.set_msrs(msrs).unwrap(), msrs.nmsrs as i32); - //now test that GET_MSRS returns the same - let wanted_kvm_msrs_entries = [ + // Set the following MSRs. + let msrs_to_set = [ kvm_msr_entry { index: 0x0000_0174, + data: 0x0, ..Default::default() }, kvm_msr_entry { index: 0x0000_0175, + data: 0x1, ..Default::default() }, ]; - let vec2: Vec = Vec::with_capacity(vec_size_bytes); - let mut msrs2: &mut kvm_msrs = unsafe { - // Converting the vector's memory to a struct is unsafe. Carefully using the read-only - // vector to size and set the members ensures no out-of-bounds errors below. - &mut *(vec2.as_ptr() as *mut kvm_msrs) - }; + let msrs_wrapper = Msrs::from_entries(&msrs_to_set); + vcpu.set_msrs(&msrs_wrapper).unwrap(); - unsafe { - let entries: &mut [kvm_msr_entry] = - msrs2.entries.as_mut_slice(configured_entry_vec.len()); - entries.copy_from_slice(&wanted_kvm_msrs_entries); - } - msrs2.nmsrs = configured_entry_vec.len() as u32; - - let read_msrs = vcpu.get_msrs(&mut msrs2).unwrap(); - assert_eq!(read_msrs, configured_entry_vec.len() as i32); + // Now test that GET_MSRS returns the same. + // Configure the struct to say which entries we want. + let mut returned_kvm_msrs = Msrs::from_entries(&[ + kvm_msr_entry { + index: 0x0000_0174, + ..Default::default() + }, + kvm_msr_entry { + index: 0x0000_0175, + ..Default::default() + }, + ]); + let nmsrs = vcpu.get_msrs(&mut returned_kvm_msrs).unwrap(); - let returned_kvm_msr_entries: &mut [kvm_msr_entry] = - unsafe { msrs2.entries.as_mut_slice(msrs2.nmsrs as usize) }; + // Verify the lengths match. + assert_eq!(nmsrs, msrs_to_set.len()); + assert_eq!(nmsrs, returned_kvm_msrs.as_fam_struct_ref().len() as usize); - for (i, entry) in returned_kvm_msr_entries.iter_mut().enumerate() { - assert_eq!(entry, &mut configured_entry_vec[i]); + // Verify the contents match. + let returned_kvm_msr_entries = returned_kvm_msrs.as_slice(); + for (i, entry) in returned_kvm_msr_entries.iter().enumerate() { + assert_eq!(entry, &msrs_to_set[i]); } } @@ -1579,11 +1554,11 @@ mod tests { badf_errno ); assert_eq!( - get_raw_errno(faulty_vcpu_fd.get_msrs(&mut kvm_msrs::default())), + get_raw_errno(faulty_vcpu_fd.get_msrs(&mut Msrs::new(1))), badf_errno ); assert_eq!( - get_raw_errno(faulty_vcpu_fd.set_msrs(&unsafe { std::mem::zeroed() })), + get_raw_errno(faulty_vcpu_fd.set_msrs(&mut Msrs::new(1))), badf_errno ); assert_eq!(get_raw_errno(faulty_vcpu_fd.get_mp_state()), badf_errno); From 85c665a1529bffafbd43266ea19e83795c0a341b Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Fri, 22 Nov 2019 13:58:27 +0200 Subject: [PATCH 083/332] vcpu: use errno::Error wrapper instead of io::Error Signed-off-by: Adrian Catangiu --- Cargo.toml | 2 +- src/ioctls/vcpu.rs | 170 ++++++++++++++++++++++++++++++--------------- 2 files changed, 114 insertions(+), 58 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d9d587f..370d83d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ license = "Apache-2.0 OR MIT" [dependencies] libc = ">=0.2.39" kvm-bindings = { version = ">=0.2.0", features = ["fam-wrappers"] } -vmm-sys-util = ">=0.1.1" +vmm-sys-util = ">=0.2.1" [dev-dependencies] byteorder = ">=1.2.1" diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 0a20578..91f28f0 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -8,17 +8,27 @@ use kvm_bindings::*; use libc::EINVAL; use std::fs::File; -use std::io; use std::os::unix::io::{AsRawFd, RawFd}; -use ioctls::{KvmRunWrapper, Result}; +use ioctls::KvmRunWrapper; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use kvm_bindings::{CpuId, Msrs}; use kvm_ioctls::*; +use vmm_sys_util::errno; use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ref}; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use vmm_sys_util::ioctl::{ioctl_with_mut_ptr, ioctl_with_ptr}; +/// A specialized `Result` type for vcpu KVM ioctls. +/// +/// This typedef is generally used to avoid writing out errno::Error directly and +/// is otherwise a direct mapping to Result. +/// +/// This is temporary until all io::Errors have been converted to errno::Errors and will +/// be removed in a later commit. I've chosen to temporarily add it so that each individual +/// commit is buildable and functioning. +pub type Result = std::result::Result; + /// Reasons for vCPU exits. /// /// The exit reasons are mapped to the `KVM_EXIT_*` defines in the @@ -126,7 +136,7 @@ impl VcpuFd { let mut regs = unsafe { std::mem::zeroed() }; let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_REGS(), &mut regs) }; if ret != 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } Ok(regs) } @@ -162,7 +172,7 @@ impl VcpuFd { // correct amount of memory from our pointer, and we verify the return result. let ret = unsafe { ioctl_with_ref(self, KVM_SET_REGS(), regs) }; if ret != 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } Ok(()) } @@ -193,7 +203,7 @@ impl VcpuFd { let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_SREGS(), &mut regs) }; if ret != 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } Ok(regs) } @@ -228,7 +238,7 @@ impl VcpuFd { // correct amount of memory from our pointer, and we verify the return result. let ret = unsafe { ioctl_with_ref(self, KVM_SET_SREGS(), sregs) }; if ret != 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } Ok(()) } @@ -260,7 +270,7 @@ impl VcpuFd { ioctl_with_mut_ref(self, KVM_GET_FPU(), &mut fpu) }; if ret != 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } Ok(fpu) } @@ -299,7 +309,7 @@ impl VcpuFd { ioctl_with_ref(self, KVM_SET_FPU(), fpu) }; if ret < 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } Ok(()) } @@ -346,7 +356,7 @@ impl VcpuFd { ioctl_with_ptr(self, KVM_SET_CPUID2(), cpuid.as_fam_struct_ptr()) }; if ret < 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } Ok(()) } @@ -382,7 +392,7 @@ impl VcpuFd { ioctl_with_mut_ptr(self, KVM_GET_CPUID2(), cpuid.as_mut_fam_struct_ptr()) }; if ret != 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } Ok(cpuid) } @@ -416,7 +426,7 @@ impl VcpuFd { ioctl_with_mut_ref(self, KVM_GET_LAPIC(), &mut klapic) }; if ret < 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } Ok(klapic) } @@ -461,7 +471,7 @@ impl VcpuFd { ioctl_with_ref(self, KVM_SET_LAPIC(), klapic) }; if ret < 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } Ok(()) } @@ -509,7 +519,7 @@ impl VcpuFd { ioctl_with_mut_ptr(self, KVM_GET_MSRS(), msrs.as_mut_fam_struct_ptr()) }; if ret < 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } Ok(ret as usize) } @@ -553,7 +563,7 @@ impl VcpuFd { }; // KVM_SET_MSRS actually returns the number of msr entries written. if ret < 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } Ok(ret as usize) } @@ -592,7 +602,7 @@ impl VcpuFd { ioctl_with_mut_ref(self, KVM_GET_MP_STATE(), &mut mp_state) }; if ret != 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } Ok(mp_state) } @@ -632,7 +642,7 @@ impl VcpuFd { ioctl_with_ref(self, KVM_SET_MP_STATE(), &mp_state) }; if ret != 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } Ok(()) } @@ -665,7 +675,7 @@ impl VcpuFd { ioctl_with_mut_ref(self, KVM_GET_XSAVE(), &mut xsave) }; if ret != 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } Ok(xsave) } @@ -699,7 +709,7 @@ impl VcpuFd { ioctl_with_ref(self, KVM_SET_XSAVE(), xsave) }; if ret != 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } Ok(()) } @@ -732,7 +742,7 @@ impl VcpuFd { ioctl_with_mut_ref(self, KVM_GET_XCRS(), &mut xcrs) }; if ret != 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } Ok(xcrs) } @@ -766,7 +776,7 @@ impl VcpuFd { ioctl_with_ref(self, KVM_SET_XCRS(), xcrs) }; if ret != 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } Ok(()) } @@ -799,7 +809,7 @@ impl VcpuFd { ioctl_with_mut_ref(self, KVM_GET_DEBUGREGS(), &mut debug_regs) }; if ret != 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } Ok(debug_regs) } @@ -833,7 +843,7 @@ impl VcpuFd { ioctl_with_ref(self, KVM_SET_DEBUGREGS(), debug_regs) }; if ret != 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } Ok(()) } @@ -867,7 +877,7 @@ impl VcpuFd { ioctl_with_mut_ref(self, KVM_GET_VCPU_EVENTS(), &mut vcpu_events) }; if ret != 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } Ok(vcpu_events) } @@ -901,7 +911,7 @@ impl VcpuFd { ioctl_with_ref(self, KVM_SET_VCPU_EVENTS(), vcpu_events) }; if ret != 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } Ok(()) } @@ -939,7 +949,7 @@ impl VcpuFd { // exactly the size of the struct. let ret = unsafe { ioctl_with_ref(self, KVM_ARM_VCPU_INIT(), kvi) }; if ret < 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } Ok(()) } @@ -965,7 +975,7 @@ impl VcpuFd { // exactly the size of the struct. let ret = unsafe { ioctl_with_ref(self, KVM_SET_ONE_REG(), &onereg) }; if ret < 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } Ok(()) } @@ -989,7 +999,7 @@ impl VcpuFd { let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_ONE_REG(), &mut onereg) }; if ret < 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } Ok(reg_value) } @@ -1099,7 +1109,7 @@ impl VcpuFd { match u32::from(io.direction) { KVM_EXIT_IO_IN => Ok(VcpuExit::IoIn(port, data_slice)), KVM_EXIT_IO_OUT => Ok(VcpuExit::IoOut(port, data_slice)), - _ => Err(io::Error::from_raw_os_error(EINVAL)), + _ => Err(errno::Error::new(EINVAL)), } } KVM_EXIT_HYPERCALL => Ok(VcpuExit::Hypercall), @@ -1147,7 +1157,7 @@ impl VcpuFd { r => panic!("unknown kvm exit reason: {}", r), } } else { - Err(io::Error::last_os_error()) + Err(errno::Error::last()) } } @@ -1515,78 +1525,124 @@ mod tests { }, }; - fn get_raw_errno(result: super::Result) -> i32 { - result.err().unwrap().raw_os_error().unwrap() - } - - assert_eq!(get_raw_errno(faulty_vcpu_fd.get_regs()), badf_errno); + assert_eq!(faulty_vcpu_fd.get_regs().unwrap_err().errno(), badf_errno); assert_eq!( - get_raw_errno(faulty_vcpu_fd.set_regs(&unsafe { std::mem::zeroed() })), + faulty_vcpu_fd + .set_regs(&unsafe { std::mem::zeroed() }) + .unwrap_err() + .errno(), badf_errno ); - assert_eq!(get_raw_errno(faulty_vcpu_fd.get_sregs()), badf_errno); + assert_eq!(faulty_vcpu_fd.get_sregs().unwrap_err().errno(), badf_errno); assert_eq!( - get_raw_errno(faulty_vcpu_fd.set_sregs(&unsafe { std::mem::zeroed() })), + faulty_vcpu_fd + .set_sregs(&unsafe { std::mem::zeroed() }) + .unwrap_err() + .errno(), badf_errno ); - assert_eq!(get_raw_errno(faulty_vcpu_fd.get_fpu()), badf_errno); + assert_eq!(faulty_vcpu_fd.get_fpu().unwrap_err().errno(), badf_errno); assert_eq!( - get_raw_errno(faulty_vcpu_fd.set_fpu(&unsafe { std::mem::zeroed() })), + faulty_vcpu_fd + .set_fpu(&unsafe { std::mem::zeroed() }) + .unwrap_err() + .errno(), badf_errno ); assert_eq!( - get_raw_errno( - faulty_vcpu_fd.set_cpuid2( + faulty_vcpu_fd + .set_cpuid2( &Kvm::new() .unwrap() .get_supported_cpuid(KVM_MAX_CPUID_ENTRIES) .unwrap() ) - ), + .unwrap_err() + .errno(), + badf_errno + ); + assert_eq!( + faulty_vcpu_fd.get_cpuid2(1).err().unwrap().errno(), badf_errno ); - assert_eq!(get_raw_errno(faulty_vcpu_fd.get_cpuid2(1)), badf_errno); // `kvm_lapic_state` does not implement debug by default so we cannot // use unwrap_err here. assert!(faulty_vcpu_fd.get_lapic().is_err()); assert_eq!( - get_raw_errno(faulty_vcpu_fd.set_lapic(&unsafe { std::mem::zeroed() })), + faulty_vcpu_fd + .set_lapic(&unsafe { std::mem::zeroed() }) + .unwrap_err() + .errno(), + badf_errno + ); + assert_eq!( + faulty_vcpu_fd + .get_msrs(&mut Msrs::new(1)) + .unwrap_err() + .errno(), + badf_errno + ); + assert_eq!( + faulty_vcpu_fd + .set_msrs(&mut Msrs::new(1)) + .unwrap_err() + .errno(), + badf_errno + ); + assert_eq!( + faulty_vcpu_fd.get_mp_state().unwrap_err().errno(), + badf_errno + ); + assert_eq!( + faulty_vcpu_fd + .set_mp_state(kvm_mp_state::default()) + .unwrap_err() + .errno(), badf_errno ); assert_eq!( - get_raw_errno(faulty_vcpu_fd.get_msrs(&mut Msrs::new(1))), + faulty_vcpu_fd.get_xsave().err().unwrap().errno(), badf_errno ); assert_eq!( - get_raw_errno(faulty_vcpu_fd.set_msrs(&mut Msrs::new(1))), + faulty_vcpu_fd + .set_xsave(&kvm_xsave::default()) + .unwrap_err() + .errno(), badf_errno ); - assert_eq!(get_raw_errno(faulty_vcpu_fd.get_mp_state()), badf_errno); + assert_eq!(faulty_vcpu_fd.get_xcrs().unwrap_err().errno(), badf_errno); assert_eq!( - get_raw_errno(faulty_vcpu_fd.set_mp_state(kvm_mp_state::default())), + faulty_vcpu_fd + .set_xcrs(&kvm_xcrs::default()) + .err() + .unwrap() + .errno(), badf_errno ); - assert_eq!(get_raw_errno(faulty_vcpu_fd.get_xsave()), badf_errno); assert_eq!( - get_raw_errno(faulty_vcpu_fd.set_xsave(&kvm_xsave::default())), + faulty_vcpu_fd.get_debug_regs().unwrap_err().errno(), badf_errno ); - assert_eq!(get_raw_errno(faulty_vcpu_fd.get_xcrs()), badf_errno); assert_eq!( - get_raw_errno(faulty_vcpu_fd.set_xcrs(&kvm_xcrs::default())), + faulty_vcpu_fd + .set_debug_regs(&kvm_debugregs::default()) + .unwrap_err() + .errno(), badf_errno ); - assert_eq!(get_raw_errno(faulty_vcpu_fd.get_debug_regs()), badf_errno); assert_eq!( - get_raw_errno(faulty_vcpu_fd.set_debug_regs(&kvm_debugregs::default())), + faulty_vcpu_fd.get_vcpu_events().unwrap_err().errno(), badf_errno ); - assert_eq!(get_raw_errno(faulty_vcpu_fd.get_vcpu_events()), badf_errno); assert_eq!( - get_raw_errno(faulty_vcpu_fd.set_vcpu_events(&kvm_vcpu_events::default())), + faulty_vcpu_fd + .set_vcpu_events(&kvm_vcpu_events::default()) + .unwrap_err() + .errno(), badf_errno ); - assert_eq!(get_raw_errno(faulty_vcpu_fd.run()), badf_errno); + assert_eq!(faulty_vcpu_fd.run().unwrap_err().errno(), badf_errno); } #[test] From 0ec87bf3ce7ad882fe7264fa0242e924622905e7 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Fri, 22 Nov 2019 14:55:29 +0200 Subject: [PATCH 084/332] vm: use errno::Error wrapper instead of io::Error Signed-off-by: Adrian Catangiu --- src/ioctls/vm.rs | 125 +++++++++++++++++++++++++++++++---------------- 1 file changed, 84 insertions(+), 41 deletions(-) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 9f4ddaf..e33a7c4 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -5,10 +5,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the THIRD-PARTY file. -use ioctls::Result; use kvm_bindings::*; use std::fs::File; -use std::io; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use std::os::raw::c_void; use std::os::raw::{c_int, c_ulong}; @@ -20,9 +18,20 @@ use ioctls::vcpu::new_vcpu; use ioctls::vcpu::VcpuFd; use ioctls::KvmRunWrapper; use kvm_ioctls::*; +use vmm_sys_util::errno; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_val}; +/// A specialized `Result` type for vm KVM ioctls. +/// +/// This typedef is generally used to avoid writing out errno::Error directly and +/// is otherwise a direct mapping to Result. +/// +/// This is temporary until all io::Errors have been converted to errno::Errors and will +/// be removed in a later commit. I've chosen to temporarily add it so that each individual +/// commit is buildable and functioning. +pub type Result = std::result::Result; + /// An address either in programmable I/O space or in memory mapped I/O space. /// /// The `IoEventAddress` is used for specifying the type when registering an event @@ -106,7 +115,7 @@ impl VmFd { if ret == 0 { Ok(()) } else { - Err(io::Error::last_os_error()) + Err(errno::Error::last()) } } @@ -135,7 +144,7 @@ impl VmFd { if ret == 0 { Ok(()) } else { - Err(io::Error::last_os_error()) + Err(errno::Error::last()) } } @@ -167,7 +176,7 @@ impl VmFd { if ret == 0 { Ok(()) } else { - Err(io::Error::last_os_error()) + Err(errno::Error::last()) } } @@ -205,7 +214,7 @@ impl VmFd { if ret == 0 { Ok(()) } else { - Err(io::Error::last_os_error()) + Err(errno::Error::last()) } } @@ -244,7 +253,7 @@ impl VmFd { if ret == 0 { Ok(()) } else { - Err(io::Error::last_os_error()) + Err(errno::Error::last()) } } @@ -276,7 +285,7 @@ impl VmFd { if ret == 0 { Ok(()) } else { - Err(io::Error::last_os_error()) + Err(errno::Error::last()) } } @@ -314,7 +323,7 @@ impl VmFd { if ret == 0 { Ok(pitstate) } else { - Err(io::Error::last_os_error()) + Err(errno::Error::last()) } } @@ -353,7 +362,7 @@ impl VmFd { if ret == 0 { Ok(()) } else { - Err(io::Error::last_os_error()) + Err(errno::Error::last()) } } @@ -386,7 +395,7 @@ impl VmFd { if ret == 0 { Ok(clock) } else { - Err(io::Error::last_os_error()) + Err(errno::Error::last()) } } @@ -421,7 +430,7 @@ impl VmFd { if ret == 0 { Ok(()) } else { - Err(io::Error::last_os_error()) + Err(errno::Error::last()) } } @@ -473,7 +482,7 @@ impl VmFd { if ret >= 0 { Ok(ret) } else { - Err(io::Error::last_os_error()) + Err(errno::Error::last()) } } @@ -521,7 +530,7 @@ impl VmFd { if ret == 0 { Ok(()) } else { - Err(io::Error::last_os_error()) + Err(errno::Error::last()) } } @@ -589,7 +598,7 @@ impl VmFd { if ret == 0 { Ok(()) } else { - Err(io::Error::last_os_error()) + Err(errno::Error::last()) } } @@ -656,7 +665,7 @@ impl VmFd { if ret == 0 { Ok(()) } else { - Err(io::Error::last_os_error()) + Err(errno::Error::last()) } } @@ -779,7 +788,7 @@ impl VmFd { if ret == 0 { Ok(bitmap) } else { - Err(io::Error::last_os_error()) + Err(errno::Error::last()) } } @@ -826,7 +835,7 @@ impl VmFd { if ret == 0 { Ok(()) } else { - Err(io::Error::last_os_error()) + Err(errno::Error::last()) } } @@ -875,7 +884,7 @@ impl VmFd { if ret == 0 { Ok(()) } else { - Err(io::Error::last_os_error()) + Err(errno::Error::last()) } } @@ -909,7 +918,7 @@ impl VmFd { #[allow(clippy::cast_lossless)] let vcpu_fd = unsafe { ioctl_with_val(&self.vm, KVM_CREATE_VCPU(), id as c_ulong) }; if vcpu_fd < 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } // Wrap the vCPU now in case the following ? returns early. This is safe because we verified @@ -992,7 +1001,7 @@ impl VmFd { if ret == 0 { Ok(new_device(unsafe { File::from_raw_fd(device.fd as i32) })) } else { - Err(io::Error::last_os_error()) + Err(errno::Error::last()) } } @@ -1025,7 +1034,7 @@ impl VmFd { // kernel will write exactly the size of the struct. let ret = unsafe { ioctl_with_mut_ref(self, KVM_ARM_PREFERRED_TARGET(), kvi) }; if ret != 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } Ok(()) } @@ -1082,7 +1091,7 @@ impl VmFd { if ret == 0 { Ok(()) } else { - Err(io::Error::last_os_error()) + Err(errno::Error::last()) } } @@ -1340,51 +1349,85 @@ mod tests { flags: 0, }; - fn get_raw_errno(result: super::Result) -> i32 { - result.err().unwrap().raw_os_error().unwrap() - } - assert_eq!( - get_raw_errno(unsafe { faulty_vm_fd.set_user_memory_region(invalid_mem_region) }), + unsafe { + faulty_vm_fd + .set_user_memory_region(invalid_mem_region) + .unwrap_err() + .errno() + }, + badf_errno + ); + assert_eq!( + faulty_vm_fd.set_tss_address(0).unwrap_err().errno(), badf_errno ); - assert_eq!(get_raw_errno(faulty_vm_fd.set_tss_address(0)), badf_errno); - assert_eq!(get_raw_errno(faulty_vm_fd.create_irq_chip()), badf_errno); assert_eq!( - get_raw_errno(faulty_vm_fd.create_pit2(kvm_pit_config::default())), + faulty_vm_fd.create_irq_chip().unwrap_err().errno(), + badf_errno + ); + assert_eq!( + faulty_vm_fd + .create_pit2(kvm_pit_config::default()) + .unwrap_err() + .errno(), badf_errno ); let event_fd = EventFd::new(EFD_NONBLOCK).unwrap(); assert_eq!( - get_raw_errno(faulty_vm_fd.register_ioevent(&event_fd, &IoEventAddress::Pio(0), 0u64)), + faulty_vm_fd + .register_ioevent(&event_fd, &IoEventAddress::Pio(0), 0u64) + .unwrap_err() + .errno(), badf_errno ); assert_eq!( - get_raw_errno(faulty_vm_fd.get_irqchip(&mut kvm_irqchip::default())), + faulty_vm_fd + .get_irqchip(&mut kvm_irqchip::default()) + .unwrap_err() + .errno(), badf_errno ); assert_eq!( - get_raw_errno(faulty_vm_fd.set_irqchip(&kvm_irqchip::default())), + faulty_vm_fd + .set_irqchip(&kvm_irqchip::default()) + .unwrap_err() + .errno(), badf_errno ); - assert_eq!(get_raw_errno(faulty_vm_fd.get_clock()), badf_errno); + assert_eq!(faulty_vm_fd.get_clock().unwrap_err().errno(), badf_errno); assert_eq!( - get_raw_errno(faulty_vm_fd.set_clock(&kvm_clock_data::default())), + faulty_vm_fd + .set_clock(&kvm_clock_data::default()) + .unwrap_err() + .errno(), badf_errno ); - assert_eq!(get_raw_errno(faulty_vm_fd.get_pit2()), badf_errno); + assert_eq!(faulty_vm_fd.get_pit2().unwrap_err().errno(), badf_errno); assert_eq!( - get_raw_errno(faulty_vm_fd.set_pit2(&kvm_pit_state2::default())), + faulty_vm_fd + .set_pit2(&kvm_pit_state2::default()) + .unwrap_err() + .errno(), badf_errno ); assert_eq!( - get_raw_errno(faulty_vm_fd.register_irqfd(&event_fd, 0)), + faulty_vm_fd + .register_irqfd(&event_fd, 0) + .unwrap_err() + .errno(), badf_errno ); - assert_eq!(get_raw_errno(faulty_vm_fd.create_vcpu(0)), badf_errno); + assert_eq!( + faulty_vm_fd.create_vcpu(0).err().unwrap().errno(), + badf_errno + ); - assert_eq!(get_raw_errno(faulty_vm_fd.get_dirty_log(0, 0)), badf_errno); + assert_eq!( + faulty_vm_fd.get_dirty_log(0, 0).unwrap_err().errno(), + badf_errno + ); } #[test] From e183bc0d5d4a650216f84160bae32320ba417e63 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Fri, 22 Nov 2019 15:27:28 +0200 Subject: [PATCH 085/332] system: use errno::Error wrapper instead of io::Error Signed-off-by: Adrian Catangiu --- src/ioctls/system.rs | 51 +++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index 6290301..b0985e2 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -6,20 +6,29 @@ // found in the THIRD-PARTY file. use libc::{open, O_CLOEXEC, O_RDWR}; use std::fs::File; -use std::io; use std::os::raw::{c_char, c_ulong}; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use cap::Cap; use ioctls::vm::{new_vmfd, VmFd}; -use ioctls::Result; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use kvm_bindings::{CpuId, MsrList, KVM_MAX_MSR_ENTRIES}; use kvm_ioctls::*; +use vmm_sys_util::errno; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use vmm_sys_util::ioctl::ioctl_with_mut_ptr; use vmm_sys_util::ioctl::{ioctl, ioctl_with_val}; +/// A specialized `Result` type for system KVM ioctls. +/// +/// This typedef is generally used to avoid writing out errno::Error directly and +/// is otherwise a direct mapping to Result. +/// +/// This is temporary until all io::Errors have been converted to errno::Errors and will +/// be removed in a later commit. I've chosen to temporarily add it so that each individual +/// commit is buildable and functioning. +pub type Result = std::result::Result; + /// Wrapper over KVM system ioctls. pub struct Kvm { kvm: File, @@ -84,7 +93,7 @@ impl Kvm { // Safe because we give a constant nul-terminated string and verify the result. let ret = unsafe { open("/dev/kvm\0".as_ptr() as *const c_char, open_flags) }; if ret < 0 { - Err(io::Error::last_os_error()) + Err(errno::Error::last()) } else { Ok(ret) } @@ -159,7 +168,7 @@ impl Kvm { if res > 0 { Ok(res as usize) } else { - Err(io::Error::last_os_error()) + Err(errno::Error::last()) } } @@ -242,7 +251,7 @@ impl Kvm { ioctl_with_mut_ptr(self, kind, cpuid.as_mut_fam_struct_ptr()) }; if ret < 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } Ok(cpuid) @@ -329,7 +338,7 @@ impl Kvm { ) }; if ret < 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } // The ioctl will also update the internal `nmsrs` with the actual count. @@ -362,7 +371,7 @@ impl Kvm { let run_mmap_size = self.get_vcpu_mmap_size()?; Ok(new_vmfd(vm_file, run_mmap_size)) } else { - Err(io::Error::last_os_error()) + Err(errno::Error::last()) } } @@ -483,10 +492,6 @@ mod tests { assert!(msr_list.as_slice().len() >= 2); } - fn get_raw_errno(result: super::Result) -> i32 { - result.err().unwrap().raw_os_error().unwrap() - } - #[test] fn test_bad_kvm_fd() { let badf_errno = libc::EBADF; @@ -495,16 +500,28 @@ mod tests { kvm: unsafe { File::from_raw_fd(-1) }, }; - assert_eq!(get_raw_errno(faulty_kvm.get_vcpu_mmap_size()), badf_errno); + assert_eq!( + faulty_kvm.get_vcpu_mmap_size().unwrap_err().errno(), + badf_errno + ); assert_eq!(faulty_kvm.get_nr_vcpus(), 4); assert_eq!(faulty_kvm.get_nr_memslots(), 32); #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { - assert_eq!(get_raw_errno(faulty_kvm.get_emulated_cpuid(4)), badf_errno); - assert_eq!(get_raw_errno(faulty_kvm.get_supported_cpuid(4)), badf_errno); - - assert_eq!(get_raw_errno(faulty_kvm.get_msr_index_list()), badf_errno); + assert_eq!( + faulty_kvm.get_emulated_cpuid(4).err().unwrap().errno(), + badf_errno + ); + assert_eq!( + faulty_kvm.get_supported_cpuid(4).err().unwrap().errno(), + badf_errno + ); + + assert_eq!( + faulty_kvm.get_msr_index_list().err().unwrap().errno(), + badf_errno + ); } - assert_eq!(get_raw_errno(faulty_kvm.create_vm()), badf_errno); + assert_eq!(faulty_kvm.create_vm().err().unwrap().errno(), badf_errno); } } From da35c741eea679e63d93ecd9194bc028302197ea Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Fri, 22 Nov 2019 15:30:29 +0200 Subject: [PATCH 086/332] device: use errno::Error wrapper instead of io::Error Signed-off-by: Adrian Catangiu --- src/ioctls/device.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/ioctls/device.rs b/src/ioctls/device.rs index 7d8820d..00f04b6 100644 --- a/src/ioctls/device.rs +++ b/src/ioctls/device.rs @@ -2,15 +2,24 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT use std::fs::File; -use std::io; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use kvm_bindings::kvm_device_attr; -use ioctls::Result; use kvm_ioctls::KVM_SET_DEVICE_ATTR; +use vmm_sys_util::errno; use vmm_sys_util::ioctl::ioctl_with_ref; +/// A specialized `Result` type for device KVM ioctls. +/// +/// This typedef is generally used to avoid writing out errno::Error directly and +/// is otherwise a direct mapping to Result. +/// +/// This is temporary until all io::Errors have been converted to errno::Errors and will +/// be removed in a later commit. I've chosen to temporarily add it so that each individual +/// commit is buildable and functioning. +pub type Result = std::result::Result; + /// Wrapper over the file descriptor obtained when creating an emulated device in the kernel. pub struct DeviceFd { fd: File, @@ -27,7 +36,7 @@ impl DeviceFd { pub fn set_device_attr(&self, device_attr: &kvm_device_attr) -> Result<()> { let ret = unsafe { ioctl_with_ref(self, KVM_SET_DEVICE_ATTR(), device_attr) }; if ret != 0 { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } Ok(()) } @@ -109,6 +118,6 @@ mod tests { // We are just creating a test device. Creating a real device would make the CI dependent // on host configuration (like having /dev/vfio). We expect this to fail. assert!(device_fd.set_device_attr(&dist_attr).is_err()); - assert_eq!(io::Error::last_os_error().raw_os_error().unwrap(), 25); + assert_eq!(errno::Error::last().errno(), 25); } } From f988243e1fd382c73d947668724bbecfef10aa41 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Mon, 25 Nov 2019 17:44:27 +0200 Subject: [PATCH 087/332] completely switch from io::Error to errno::Error Changed `kvm_ioctls::Result` to work with `errno::Result` instead of `io::Result`. No longer exporting `kvm_ioctls::Result`. Users of this crate should not have to use `kvm_ioctls::Result` outside the crate. Now exporting `kvm_ioctls::Error` type definition so that users of this crate can create their own wrapping errors without having to know the `Error` type used internally by this crate. Signed-off-by: Adrian Catangiu --- CHANGELOG.md | 10 ++++++++++ src/ioctls/device.rs | 12 +----------- src/ioctls/mod.rs | 9 ++++----- src/ioctls/system.rs | 11 +---------- src/ioctls/vcpu.rs | 12 +----------- src/ioctls/vm.rs | 12 +----------- src/lib.rs | 5 +++-- 7 files changed, 21 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b55f3e8..e0fd51d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +# Unreleased + +## Changed +- Now exporting kvm_ioctls::Error type definition so that users of this + crate can create their own wrapping errors without having to know the + Error type used internally by this crate. +- No longer exporting kvm_ioctls::Result. Users of this crate should + not have to use kvm_ioctls::Result outside the crate. +- kvm_ioctls::Error now works with errno::Error instead of io::Error. + # v0.3.0 ## Added diff --git a/src/ioctls/device.rs b/src/ioctls/device.rs index 00f04b6..5810022 100644 --- a/src/ioctls/device.rs +++ b/src/ioctls/device.rs @@ -4,22 +4,12 @@ use std::fs::File; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; +use ioctls::Result; use kvm_bindings::kvm_device_attr; - use kvm_ioctls::KVM_SET_DEVICE_ATTR; use vmm_sys_util::errno; use vmm_sys_util::ioctl::ioctl_with_ref; -/// A specialized `Result` type for device KVM ioctls. -/// -/// This typedef is generally used to avoid writing out errno::Error directly and -/// is otherwise a direct mapping to Result. -/// -/// This is temporary until all io::Errors have been converted to errno::Errors and will -/// be removed in a later commit. I've chosen to temporarily add it so that each individual -/// commit is buildable and functioning. -pub type Result = std::result::Result; - /// Wrapper over the file descriptor obtained when creating an emulated device in the kernel. pub struct DeviceFd { fd: File, diff --git a/src/ioctls/mod.rs b/src/ioctls/mod.rs index 2c84a74..6eaf18d 100644 --- a/src/ioctls/mod.rs +++ b/src/ioctls/mod.rs @@ -5,12 +5,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the THIRD-PARTY file. -use std::io; use std::os::unix::io::AsRawFd; use std::ptr::null_mut; -use std::result; use kvm_bindings::kvm_run; +use vmm_sys_util::errno; /// Wrappers over KVM device ioctls. pub mod device; @@ -23,9 +22,9 @@ pub mod vm; /// A specialized `Result` type for KVM ioctls. /// -/// This typedef is generally used to avoid writing out io::Error directly and +/// This typedef is generally used to avoid writing out errno::Error directly and /// is otherwise a direct mapping to Result. -pub type Result = result::Result; +pub type Result = std::result::Result; /// Safe wrapper over the `kvm_run` struct. /// @@ -64,7 +63,7 @@ impl KvmRunWrapper { ) }; if addr == libc::MAP_FAILED { - return Err(io::Error::last_os_error()); + return Err(errno::Error::last()); } Ok(KvmRunWrapper { diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index b0985e2..3c6b9da 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -11,6 +11,7 @@ use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use cap::Cap; use ioctls::vm::{new_vmfd, VmFd}; +use ioctls::Result; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use kvm_bindings::{CpuId, MsrList, KVM_MAX_MSR_ENTRIES}; use kvm_ioctls::*; @@ -19,16 +20,6 @@ use vmm_sys_util::errno; use vmm_sys_util::ioctl::ioctl_with_mut_ptr; use vmm_sys_util::ioctl::{ioctl, ioctl_with_val}; -/// A specialized `Result` type for system KVM ioctls. -/// -/// This typedef is generally used to avoid writing out errno::Error directly and -/// is otherwise a direct mapping to Result. -/// -/// This is temporary until all io::Errors have been converted to errno::Errors and will -/// be removed in a later commit. I've chosen to temporarily add it so that each individual -/// commit is buildable and functioning. -pub type Result = std::result::Result; - /// Wrapper over KVM system ioctls. pub struct Kvm { kvm: File, diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 91f28f0..2496862 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -10,7 +10,7 @@ use libc::EINVAL; use std::fs::File; use std::os::unix::io::{AsRawFd, RawFd}; -use ioctls::KvmRunWrapper; +use ioctls::{KvmRunWrapper, Result}; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use kvm_bindings::{CpuId, Msrs}; use kvm_ioctls::*; @@ -19,16 +19,6 @@ use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ref}; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use vmm_sys_util::ioctl::{ioctl_with_mut_ptr, ioctl_with_ptr}; -/// A specialized `Result` type for vcpu KVM ioctls. -/// -/// This typedef is generally used to avoid writing out errno::Error directly and -/// is otherwise a direct mapping to Result. -/// -/// This is temporary until all io::Errors have been converted to errno::Errors and will -/// be removed in a later commit. I've chosen to temporarily add it so that each individual -/// commit is buildable and functioning. -pub type Result = std::result::Result; - /// Reasons for vCPU exits. /// /// The exit reasons are mapped to the `KVM_EXIT_*` defines in the diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index e33a7c4..abeedc7 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -16,22 +16,12 @@ use ioctls::device::new_device; use ioctls::device::DeviceFd; use ioctls::vcpu::new_vcpu; use ioctls::vcpu::VcpuFd; -use ioctls::KvmRunWrapper; +use ioctls::{KvmRunWrapper, Result}; use kvm_ioctls::*; use vmm_sys_util::errno; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_val}; -/// A specialized `Result` type for vm KVM ioctls. -/// -/// This typedef is generally used to avoid writing out errno::Error directly and -/// is otherwise a direct mapping to Result. -/// -/// This is temporary until all io::Errors have been converted to errno::Errors and will -/// be removed in a later commit. I've chosen to temporarily add it so that each individual -/// commit is buildable and functioning. -pub type Result = std::result::Result; - /// An address either in programmable I/O space or in memory mapped I/O space. /// /// The `IoEventAddress` is used for specifying the type when registering an event diff --git a/src/lib.rs b/src/lib.rs index 1600160..36459b2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -198,6 +198,7 @@ pub use ioctls::vm::{IoEventAddress, NoDatamatch, VmFd}; /// /// ``` /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -/// use kvm_ioctls::{KvmRunWrapper, Result}; +/// use kvm_ioctls::{KvmRunWrapper, Error}; /// ``` -pub use ioctls::{KvmRunWrapper, Result}; +pub use ioctls::KvmRunWrapper; +pub use vmm_sys_util::errno::Error; From 5a50456cd1afa18e079c97bf2e87be2572b4a3f9 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Mon, 25 Nov 2019 13:38:55 +0200 Subject: [PATCH 088/332] fix some clippy warnings Signed-off-by: Adrian Catangiu --- src/ioctls/device.rs | 2 +- src/ioctls/mod.rs | 2 +- src/ioctls/system.rs | 4 ++-- src/ioctls/vcpu.rs | 13 +++++-------- src/kvm_ioctls.rs | 2 +- 5 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/ioctls/device.rs b/src/ioctls/device.rs index 5810022..a990e2e 100644 --- a/src/ioctls/device.rs +++ b/src/ioctls/device.rs @@ -100,7 +100,7 @@ mod tests { let dist_attr = kvm_bindings::kvm_device_attr { group: KVM_DEV_VFIO_GROUP, - attr: KVM_DEV_VFIO_GROUP_ADD as u64, + attr: u64::from(KVM_DEV_VFIO_GROUP_ADD), addr: 0x0, flags: 0, }; diff --git a/src/ioctls/mod.rs b/src/ioctls/mod.rs index 6eaf18d..e28fc0f 100644 --- a/src/ioctls/mod.rs +++ b/src/ioctls/mod.rs @@ -49,7 +49,7 @@ impl KvmRunWrapper { /// # Arguments /// * `fd` - File descriptor to mmap from. /// * `size` - Size of memory region in bytes. - pub fn mmap_from_fd(fd: &AsRawFd, size: usize) -> Result { + pub fn mmap_from_fd(fd: &dyn AsRawFd, size: usize) -> Result { // This is safe because we are creating a mapping in a place not already used by any other // area in this process. let addr = unsafe { diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index 3c6b9da..a4de4f9 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -446,7 +446,7 @@ mod tests { let kvm = Kvm::new().unwrap(); let mut cpuid = kvm.get_supported_cpuid(KVM_MAX_CPUID_ENTRIES).unwrap(); let cpuid_entries = cpuid.as_mut_slice(); - assert!(cpuid_entries.len() > 0); + assert!(!cpuid_entries.is_empty()); assert!(cpuid_entries.len() <= KVM_MAX_CPUID_ENTRIES); } @@ -456,7 +456,7 @@ mod tests { let kvm = Kvm::new().unwrap(); let mut cpuid = kvm.get_emulated_cpuid(KVM_MAX_CPUID_ENTRIES).unwrap(); let cpuid_entries = cpuid.as_mut_slice(); - assert!(cpuid_entries.len() > 0); + assert!(!cpuid_entries.is_empty()); assert!(cpuid_entries.len() <= KVM_MAX_CPUID_ENTRIES); } diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 2496862..b67a8c9 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1203,7 +1203,7 @@ mod tests { panic!("mmap failed."); } - return addr as *mut u8; + addr as *mut u8 } #[test] @@ -1443,7 +1443,7 @@ mod tests { // Get a mutable slice of `mem_size` from `load_addr`. // This is safe because we mapped it before. let mut slice = std::slice::from_raw_parts_mut(load_addr, mem_size); - slice.write(&code).unwrap(); + slice.write_all(&code).unwrap(); } let vcpu_fd = vm.create_vcpu(0).unwrap(); @@ -1488,10 +1488,10 @@ mod tests { // * one when the code itself is loaded in memory; // * and one more from the `movl` that writes to address 0x8000 let dirty_pages_bitmap = vm.get_dirty_log(slot, mem_size).unwrap(); - let dirty_pages = dirty_pages_bitmap + let dirty_pages: u32 = dirty_pages_bitmap .into_iter() .map(|page| page.count_ones()) - .fold(0, |dirty_page_count, i| dirty_page_count + i); + .sum(); assert_eq!(dirty_pages, 2); break; } @@ -1573,10 +1573,7 @@ mod tests { badf_errno ); assert_eq!( - faulty_vcpu_fd - .set_msrs(&mut Msrs::new(1)) - .unwrap_err() - .errno(), + faulty_vcpu_fd.set_msrs(&Msrs::new(1)).unwrap_err().errno(), badf_errno ); assert_eq!( diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index ac18c24..e61b5aa 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -211,7 +211,7 @@ mod tests { use vmm_sys_util::ioctl::{ioctl, ioctl_with_val}; use super::*; - const KVM_PATH: &'static str = "/dev/kvm\0"; + const KVM_PATH: &str = "/dev/kvm\0"; #[test] fn get_version() { From 1b5939c168287ebf8c47ed278cbfa4f3ca590d03 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Wed, 27 Nov 2019 16:07:36 +0200 Subject: [PATCH 089/332] Release v0.4.0 Signed-off-by: Adrian Catangiu --- CHANGELOG.md | 17 ++++++++++++++++- Cargo.toml | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0fd51d..a77e080 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,15 @@ -# Unreleased +# v0.4.0 + +## Added +- Support for unregistering ioeventfds through `KVM_IOEVENTFD`. ## Changed +- Functions working with event FDs now require + vmm_sys_util::eventfd::EventFd in their interface instead of + RawFd. +- Functions working with FAM structs kvm_msr_list and kvm_msrs, were + changed to work with their respective safe counterparts MsrList and + respectively Msrs. - Now exporting kvm_ioctls::Error type definition so that users of this crate can create their own wrapping errors without having to know the Error type used internally by this crate. @@ -8,6 +17,12 @@ not have to use kvm_ioctls::Result outside the crate. - kvm_ioctls::Error now works with errno::Error instead of io::Error. +## Removed +- CpuId safe wrapper over FAM struct kvm_cpuid2. The safe wrapper is + now provided by the kvm_bindings crate starting with v0.2.0. +- KVM_MAX_MSR_ENTRIES and MAX_KVM_CPUID_ENTRIES. Equivalent constants + are provided by the kvm_bindings crate starting with v0.2.0. + # v0.3.0 ## Added diff --git a/Cargo.toml b/Cargo.toml index 370d83d..5ad3e20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kvm-ioctls" -version = "0.3.0" +version = "0.4.0" authors = ["Amazon Firecracker Team "] description = "Safe wrappers over KVM ioctls" repository = "https://github.com/rust-vmm/kvm-ioctls" From f372a9d046aa741860799e7003e65996d7734103 Mon Sep 17 00:00:00 2001 From: Michael Zhao Date: Tue, 26 Nov 2019 08:54:16 +0000 Subject: [PATCH 090/332] Enable KVM_SET/GET_VCPU_EVENTS ioctls for Aarch64. KVM_GET_VCPU_EVENTS and KVM_SET_VCPU_EVENTS ioctls have been supported on Aarch64 from a recent kernel version. This patch enables them on Aarch64 and checked cap KVM_CAP_VCPU_EVENTS before calling them in test code to avoid error in old kernel. Change-Id: I9526b48948cf0cd5d63444a82b8b09970311a3bb Signed-off-by: Michael Zhao --- src/cap.rs | 7 ++++- src/ioctls/vcpu.rs | 67 ++++++++++++++++++++++++++++++++-------------- src/kvm_ioctls.rs | 14 ++++++++-- 3 files changed, 65 insertions(+), 23 deletions(-) diff --git a/src/cap.rs b/src/cap.rs index 5e2ac89..6d21225 100644 --- a/src/cap.rs +++ b/src/cap.rs @@ -62,7 +62,12 @@ pub enum Cap { XenHvm = KVM_CAP_XEN_HVM, AdjustClock = KVM_CAP_ADJUST_CLOCK, InternalErrorData = KVM_CAP_INTERNAL_ERROR_DATA, - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" + ))] VcpuEvents = KVM_CAP_VCPU_EVENTS, S390Psw = KVM_CAP_S390_PSW, PpcSegstate = KVM_CAP_PPC_SEGSTATE, diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index b67a8c9..48eebf4 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -852,14 +852,21 @@ impl VcpuFd { /// /// ```rust /// # extern crate kvm_ioctls; - /// # use kvm_ioctls::Kvm; + /// # use kvm_ioctls::{Kvm, Cap}; /// let kvm = Kvm::new().unwrap(); - /// let vm = kvm.create_vm().unwrap(); - /// let vcpu = vm.create_vcpu(0).unwrap(); - /// let vcpu_events = vcpu.get_vcpu_events().unwrap(); + /// if kvm.check_extension(Cap::VcpuEvents) { + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// let vcpu_events = vcpu.get_vcpu_events().unwrap(); + /// } /// ``` /// - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" + ))] pub fn get_vcpu_events(&self) -> Result { let mut vcpu_events = Default::default(); let ret = unsafe { @@ -885,16 +892,24 @@ impl VcpuFd { /// /// ```rust /// # extern crate kvm_ioctls; - /// # use kvm_ioctls::Kvm; + /// # use kvm_ioctls::{Kvm, Cap}; /// let kvm = Kvm::new().unwrap(); - /// let vm = kvm.create_vm().unwrap(); - /// let vcpu = vm.create_vcpu(0).unwrap(); - /// let vcpu_events = Default::default(); - /// // Your `vcpu_events` manipulation here. - /// vcpu.set_vcpu_events(&vcpu_events).unwrap(); + /// if kvm.check_extension(Cap::VcpuEvents) { + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// let vcpu_events = Default::default(); + /// // Your `vcpu_events` manipulation here. + /// vcpu.set_vcpu_events(&vcpu_events).unwrap(); + /// } /// ``` /// - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" + ))] + pub fn set_vcpu_events(&self, vcpu_events: &kvm_vcpu_events) -> Result<()> { let ret = unsafe { // Here we trust the kernel not to read past the end of the kvm_vcpu_events struct. @@ -1180,7 +1195,12 @@ mod tests { #[cfg(target_arch = "x86_64")] use super::*; use ioctls::system::Kvm; - #[cfg(target_arch = "x86_64")] + #[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" + ))] use Cap; // Helper function for memory mapping `size` bytes of anonymous memory. @@ -1391,16 +1411,23 @@ mod tests { assert_eq!(debugregs, other_debugregs); } - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" + ))] #[test] fn vcpu_events_test() { let kvm = Kvm::new().unwrap(); - let vm = kvm.create_vm().unwrap(); - let vcpu = vm.create_vcpu(0).unwrap(); - let vcpu_events = vcpu.get_vcpu_events().unwrap(); - vcpu.set_vcpu_events(&vcpu_events).unwrap(); - let other_vcpu_events = vcpu.get_vcpu_events().unwrap(); - assert_eq!(vcpu_events, other_vcpu_events); + if kvm.check_extension(Cap::VcpuEvents) { + let vm = kvm.create_vm().unwrap(); + let vcpu = vm.create_vcpu(0).unwrap(); + let vcpu_events = vcpu.get_vcpu_events().unwrap(); + vcpu.set_vcpu_events(&vcpu_events).unwrap(); + let other_vcpu_events = vcpu.get_vcpu_events().unwrap(); + assert_eq!(vcpu_events, other_vcpu_events); + } } #[cfg(target_arch = "x86_64")] diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index e61b5aa..e38ff73 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -150,10 +150,20 @@ ioctl_ior_nr!(KVM_GET_MP_STATE, KVMIO, 0x98, kvm_mp_state); ))] ioctl_iow_nr!(KVM_SET_MP_STATE, KVMIO, 0x99, kvm_mp_state); /* Available with KVM_CAP_VCPU_EVENTS */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" +))] ioctl_ior_nr!(KVM_GET_VCPU_EVENTS, KVMIO, 0x9f, kvm_vcpu_events); /* Available with KVM_CAP_VCPU_EVENTS */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" +))] ioctl_iow_nr!(KVM_SET_VCPU_EVENTS, KVMIO, 0xa0, kvm_vcpu_events); /* Available with KVM_CAP_DEBUGREGS */ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] From 7b34e735c6e2963aab0cf40525e31d235ae9bd6f Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Wed, 18 Dec 2019 14:34:32 +0200 Subject: [PATCH 091/332] make test_irq_chip x86 specific The test was previously marked as x86_64 and aarch64 compatible. On aarch64 we were testing only the error case. On older kernel versions, creating an irqchip before creating the vCPU would return an error. This restrictions is not in place anymore on Linux Kernel 4.15. Since we cannot test this error path anymore on aarch64, the test is now labeled as x86 specific. Also updated the test to set the irq_base of the PIC and check that this value is appropriately updated. Signed-off-by: Andreea Florescu --- src/ioctls/vm.rs | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index abeedc7..f7b649d 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -1109,7 +1109,7 @@ impl AsRawFd for VmFd { #[cfg(test)] mod tests { use super::*; - use {Cap, Kvm}; + use Kvm; use libc::EFD_NONBLOCK; @@ -1136,34 +1136,31 @@ mod tests { } #[test] - #[cfg(any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64" - ))] + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn test_irq_chip() { + use Cap; + let kvm = Kvm::new().unwrap(); assert!(kvm.check_extension(Cap::Irqchip)); let vm = kvm.create_vm().unwrap(); - if cfg!(any(target_arch = "x86", target_arch = "x86_64")) { - assert!(vm.create_irq_chip().is_ok()); - } else if cfg!(any(target_arch = "arm", target_arch = "aarch64")) { - // On arm, we expect this to fail as the irq chip needs to be created after the vcpus. - assert!(vm.create_irq_chip().is_err()); - } + assert!(vm.create_irq_chip().is_ok()); - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - { - let mut irqchip = kvm_irqchip::default(); - irqchip.chip_id = KVM_IRQCHIP_PIC_MASTER; - vm.get_irqchip(&mut irqchip).unwrap(); - vm.set_irqchip(&irqchip).unwrap(); - let mut other_irqchip = kvm_irqchip::default(); - other_irqchip.chip_id = KVM_IRQCHIP_PIC_MASTER; - vm.get_irqchip(&mut other_irqchip).unwrap(); - unsafe { assert_eq!(irqchip.chip.dummy[..], other_irqchip.chip.dummy[..]) }; + let mut irqchip = kvm_irqchip::default(); + irqchip.chip_id = KVM_IRQCHIP_PIC_MASTER; + // Set the irq_base to a non-default value to check that set & get work. + unsafe { + irqchip.chip.pic.irq_base = 10; } + assert!(vm.set_irqchip(&irqchip).is_ok()); + + // We initialize a dummy irq chip (`other_irqchip`) in which the + // function `get_irqchip` returns its result. + let mut other_irqchip = kvm_irqchip::default(); + other_irqchip.chip_id = KVM_IRQCHIP_PIC_MASTER; + assert!(vm.get_irqchip(&mut other_irqchip).is_ok()); + + // Safe because we know that the irqchip type is PIC. + unsafe { assert_eq!(irqchip.chip.pic, other_irqchip.chip.pic) }; } #[test] From 9a15cb08194f49ec6446ffb58597cf25f2d25bfd Mon Sep 17 00:00:00 2001 From: Michael Zhao Date: Tue, 10 Dec 2019 04:53:57 +0000 Subject: [PATCH 092/332] Support GICv2 in testing KVM_CREATE_DEVICE. Retry to create VGICv2 device after failling to create VGICv3, in case GICv2 is equipped (like RaspBerry Pi 4). Divide test_create_device() into 2 architecture specific functions, to avoid too much confusing conditional compiling sentences. Change-Id: Ie43bf9fe50e5244f3cfcf061328beb267b8788b8 Signed-off-by: Michael Zhao --- src/ioctls/device.rs | 73 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 58 insertions(+), 15 deletions(-) diff --git a/src/ioctls/device.rs b/src/ioctls/device.rs index a990e2e..decf848 100644 --- a/src/ioctls/device.rs +++ b/src/ioctls/device.rs @@ -59,41 +59,84 @@ impl FromRawFd for DeviceFd { mod tests { use super::*; use ioctls::system::Kvm; + #[cfg(target_arch = "aarch64")] + use kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2; + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + use kvm_bindings::kvm_device_type_KVM_DEV_TYPE_VFIO; use kvm_bindings::{ - kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, kvm_device_type_KVM_DEV_TYPE_VFIO, - KVM_CREATE_DEVICE_TEST, KVM_DEV_VFIO_GROUP, KVM_DEV_VFIO_GROUP_ADD, + kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, KVM_CREATE_DEVICE_TEST, KVM_DEV_VFIO_GROUP, + KVM_DEV_VFIO_GROUP_ADD, }; #[test] + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn test_create_device() { - #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] - use kvm_bindings::kvm_device_type_KVM_DEV_TYPE_FSL_MPIC_20; - let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); let mut gic_device = kvm_bindings::kvm_create_device { - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] type_: kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, - #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] - type_: kvm_device_type_KVM_DEV_TYPE_FSL_MPIC_20, fd: 0, flags: KVM_CREATE_DEVICE_TEST, }; // This fails on x86_64 because there is no VGIC there. - // This fails on aarch64 as it does not use MPIC (MultiProcessor Interrupt Controller), it uses - // the VGIC. assert!(vm.create_device(&mut gic_device).is_err()); - if cfg!(any(target_arch = "x86", target_arch = "x86_64")) { - gic_device.type_ = kvm_device_type_KVM_DEV_TYPE_VFIO; - } else if cfg!(any(target_arch = "arm", target_arch = "aarch64")) { - gic_device.type_ = kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3; - } + gic_device.type_ = kvm_device_type_KVM_DEV_TYPE_VFIO; + let device_fd = vm .create_device(&mut gic_device) .expect("Cannot create KVM device"); + // Following lines to re-construct device_fd are used to test + // DeviceFd::from_raw_fd() and DeviceFd::as_raw_fd(). + let raw_fd = unsafe { libc::dup(device_fd.as_raw_fd()) }; + assert!(raw_fd >= 0); + let device_fd = unsafe { DeviceFd::from_raw_fd(raw_fd) }; + + let dist_attr = kvm_bindings::kvm_device_attr { + group: KVM_DEV_VFIO_GROUP, + attr: u64::from(KVM_DEV_VFIO_GROUP_ADD), + addr: 0x0, + flags: 0, + }; + + // We are just creating a test device. Creating a real device would make the CI dependent + // on host configuration (like having /dev/vfio). We expect this to fail. + assert!(device_fd.set_device_attr(&dist_attr).is_err()); + assert_eq!(errno::Error::last().errno(), 25); + } + + #[test] + #[cfg(target_arch = "aarch64")] + fn test_create_device() { + use kvm_bindings::kvm_device_type_KVM_DEV_TYPE_FSL_MPIC_20; + + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + + let mut gic_device = kvm_bindings::kvm_create_device { + type_: kvm_device_type_KVM_DEV_TYPE_FSL_MPIC_20, + fd: 0, + flags: KVM_CREATE_DEVICE_TEST, + }; + // This fails on aarch64 as it does not use MPIC (MultiProcessor Interrupt Controller), it uses + // the VGIC. + assert!(vm.create_device(&mut gic_device).is_err()); + + gic_device.type_ = kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3; + + let device_fd = match vm.create_device(&mut gic_device) { + Ok(fd) => fd, + Err(_) => { + gic_device.type_ = kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2; + vm.create_device(&mut gic_device) + .expect("Cannot create KVM device") + } + }; + + // Following lines to re-construct device_fd are used to test + // DeviceFd::from_raw_fd() and DeviceFd::as_raw_fd(). let raw_fd = unsafe { libc::dup(device_fd.as_raw_fd()) }; assert!(raw_fd >= 0); let device_fd = unsafe { DeviceFd::from_raw_fd(raw_fd) }; From b7eadd8632ae821ebdaaae0c7e1caf020115861a Mon Sep 17 00:00:00 2001 From: mrxinwang Date: Fri, 6 Dec 2019 14:52:00 +0800 Subject: [PATCH 093/332] Add unit tests for handling irqfd on aarch64 1. Added Unit tests for register and unregister irqfd on aarch64. 2. Added the new function create_gic_device which is used for removing duplicated code when creating GIC devices. Signed-off-by: Henry Wang --- src/ioctls/device.rs | 19 ++------ src/ioctls/vm.rs | 106 +++++++++++++++++++++++++++++-------------- 2 files changed, 77 insertions(+), 48 deletions(-) diff --git a/src/ioctls/device.rs b/src/ioctls/device.rs index decf848..56a8efd 100644 --- a/src/ioctls/device.rs +++ b/src/ioctls/device.rs @@ -59,14 +59,11 @@ impl FromRawFd for DeviceFd { mod tests { use super::*; use ioctls::system::Kvm; - #[cfg(target_arch = "aarch64")] - use kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - use kvm_bindings::kvm_device_type_KVM_DEV_TYPE_VFIO; use kvm_bindings::{ - kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, KVM_CREATE_DEVICE_TEST, KVM_DEV_VFIO_GROUP, - KVM_DEV_VFIO_GROUP_ADD, + kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, kvm_device_type_KVM_DEV_TYPE_VFIO, }; + use kvm_bindings::{KVM_CREATE_DEVICE_TEST, KVM_DEV_VFIO_GROUP, KVM_DEV_VFIO_GROUP_ADD}; #[test] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] @@ -110,6 +107,7 @@ mod tests { #[test] #[cfg(target_arch = "aarch64")] fn test_create_device() { + use ioctls::vm::create_gic_device; use kvm_bindings::kvm_device_type_KVM_DEV_TYPE_FSL_MPIC_20; let kvm = Kvm::new().unwrap(); @@ -124,16 +122,7 @@ mod tests { // the VGIC. assert!(vm.create_device(&mut gic_device).is_err()); - gic_device.type_ = kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3; - - let device_fd = match vm.create_device(&mut gic_device) { - Ok(fd) => fd, - Err(_) => { - gic_device.type_ = kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2; - vm.create_device(&mut gic_device) - .expect("Cannot create KVM device") - } - }; + let device_fd = create_gic_device(&vm, KVM_CREATE_DEVICE_TEST); // Following lines to re-construct device_fd are used to test // DeviceFd::from_raw_fd() and DeviceFd::as_raw_fd(). diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index f7b649d..362eb0c 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -1106,6 +1106,32 @@ impl AsRawFd for VmFd { } } +/// Creates a dummy GIC device. +/// +/// # Arguments +/// +/// * `vm` - The vm file descriptor. +/// * `flags` - Flags to be passed to `KVM_CREATE_DEVICE`. +/// +#[cfg(test)] +#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] +pub(crate) fn create_gic_device(vm: &VmFd, flags: u32) -> DeviceFd { + let mut gic_device = kvm_bindings::kvm_create_device { + type_: kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, + fd: 0, + flags, + }; + let device_fd = match vm.create_device(&mut gic_device) { + Ok(fd) => fd, + Err(_) => { + gic_device.type_ = kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2; + vm.create_device(&mut gic_device) + .expect("Cannot create KVM vGIC device") + } + }; + device_fd +} + #[cfg(test)] mod tests { use super::*; @@ -1258,62 +1284,76 @@ mod tests { } #[test] - #[cfg(any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64" - ))] - fn test_register_irqfd() { + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn test_register_unregister_irqfd() { let kvm = Kvm::new().unwrap(); let vm_fd = kvm.create_vm().unwrap(); let evtfd1 = EventFd::new(EFD_NONBLOCK).unwrap(); let evtfd2 = EventFd::new(EFD_NONBLOCK).unwrap(); let evtfd3 = EventFd::new(EFD_NONBLOCK).unwrap(); - if cfg!(any(target_arch = "x86", target_arch = "x86_64")) { - assert!(vm_fd.create_irq_chip().is_ok()); - assert!(vm_fd.register_irqfd(&evtfd1, 4).is_ok()); - assert!(vm_fd.register_irqfd(&evtfd2, 8).is_ok()); - assert!(vm_fd.register_irqfd(&evtfd3, 4).is_ok()); - } - // On aarch64, this fails because setting up the interrupt controller is mandatory before - // registering any IRQ. + assert!(vm_fd.create_irq_chip().is_ok()); + + assert!(vm_fd.register_irqfd(&evtfd1, 4).is_ok()); + assert!(vm_fd.register_irqfd(&evtfd2, 8).is_ok()); + assert!(vm_fd.register_irqfd(&evtfd3, 4).is_ok()); + assert!(vm_fd.unregister_irqfd(&evtfd2, 8).is_ok()); + // KVM irqfd doesn't report failure on this case:( + assert!(vm_fd.unregister_irqfd(&evtfd2, 8).is_ok()); + + // Duplicated eventfd registration. // On x86_64 this fails as the event fd was already matched with a GSI. assert!(vm_fd.register_irqfd(&evtfd3, 4).is_err()); assert!(vm_fd.register_irqfd(&evtfd3, 5).is_err()); + // KVM irqfd doesn't report failure on this case:( + assert!(vm_fd.unregister_irqfd(&evtfd3, 5).is_ok()); } #[test] - #[cfg(any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64" - ))] - fn test_unregister_irqfd() { + #[cfg(target_arch = "aarch64")] + fn test_register_unregister_irqfd() { let kvm = Kvm::new().unwrap(); let vm_fd = kvm.create_vm().unwrap(); let evtfd1 = EventFd::new(EFD_NONBLOCK).unwrap(); let evtfd2 = EventFd::new(EFD_NONBLOCK).unwrap(); let evtfd3 = EventFd::new(EFD_NONBLOCK).unwrap(); - if cfg!(any(target_arch = "x86", target_arch = "x86_64")) { - assert!(vm_fd.create_irq_chip().is_ok()); - assert!(vm_fd.register_irqfd(&evtfd1, 4).is_ok()); - assert!(vm_fd.register_irqfd(&evtfd2, 8).is_ok()); - assert!(vm_fd.register_irqfd(&evtfd3, 4).is_ok()); - assert!(vm_fd.unregister_irqfd(&evtfd2, 8).is_ok()); - // KVM irqfd doesn't report failure on this case:( - assert!(vm_fd.unregister_irqfd(&evtfd2, 8).is_ok()); - } + + // Create the vGIC device. + let vgic_fd = create_gic_device(&vm_fd, 0); + + // Dummy interrupt for testing on aarch64. + let nr_irqs: u32 = 128; + + // We need to tell the kernel how many irqs to support with this vgic. + let vgic_attr = kvm_bindings::kvm_device_attr { + group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS, + attr: 0, + addr: &nr_irqs as *const u32 as u64, + flags: 0, + }; + assert!(vgic_fd.set_device_attr(&vgic_attr).is_ok()); + + // Finalize the GIC. + let vgic_attr = kvm_bindings::kvm_device_attr { + group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, + attr: u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT), + addr: 0, + flags: 0, + }; + assert!(vgic_fd.set_device_attr(&vgic_attr).is_ok()); + + assert!(vm_fd.register_irqfd(&evtfd1, 4).is_ok()); + assert!(vm_fd.register_irqfd(&evtfd2, 8).is_ok()); + assert!(vm_fd.register_irqfd(&evtfd3, 4).is_ok()); + assert!(vm_fd.unregister_irqfd(&evtfd2, 8).is_ok()); + // KVM irqfd doesn't report failure on this case:( + assert!(vm_fd.unregister_irqfd(&evtfd2, 8).is_ok()); // Duplicated eventfd registration. // On aarch64, this fails because setting up the interrupt controller is mandatory before // registering any IRQ. - // On x86_64 this fails as the event fd was already matched with a GSI. assert!(vm_fd.register_irqfd(&evtfd3, 4).is_err()); assert!(vm_fd.register_irqfd(&evtfd3, 5).is_err()); - // KVM irqfd doesn't report failure on this case:( assert!(vm_fd.unregister_irqfd(&evtfd3, 5).is_ok()); } From 001403bc2d28855344fb3a7e94db83246fa84e64 Mon Sep 17 00:00:00 2001 From: Michael Zhao Date: Fri, 20 Dec 2019 02:27:11 +0000 Subject: [PATCH 094/332] Add unit test for KVM_CREATE_IRQCHIP on ARM. On ARM, KVM_CREATE_IRQCHIP is a bit more complex than on X86. KVM_CREATE_IRQCHIP only creates VGICv2. It usually succeed with a GICv2 hardware. But if the hardware is GICv3, it's probable that GICv2 can't be emulated, so KVM_CREATE_IRQCHIP would fail. An issue was created for the problem: https://github.com/rust-vmm/kvm-ioctls/issues/79 To avoid hardware dependency, in the test we'd better check the ability of creating VGICv2 in advance, and assert the result of KVM_CREATE_IRQCHIP correspondingly. Change-Id: Id17339dde28dbb4abd68bf09fe7f8f0d88c14edb Signed-off-by: Michael Zhao --- src/ioctls/vm.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 362eb0c..884880b 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -146,12 +146,25 @@ impl VmFd { /// /// ```rust /// # extern crate kvm_ioctls; + /// # extern crate kvm_bindings; /// # use kvm_ioctls::Kvm; /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] /// vm.create_irq_chip().unwrap(); + /// #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] { + /// use kvm_bindings::{kvm_create_device, + /// kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2, KVM_CREATE_DEVICE_TEST}; + /// let mut gic_device = kvm_bindings::kvm_create_device { + /// type_: kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2, + /// fd: 0, + /// flags: KVM_CREATE_DEVICE_TEST, + /// }; + /// if vm.create_device(&mut gic_device).is_ok() { + /// vm.create_irq_chip().unwrap(); + /// } + /// } /// ``` /// #[cfg(any( @@ -1189,6 +1202,28 @@ mod tests { unsafe { assert_eq!(irqchip.chip.pic, other_irqchip.chip.pic) }; } + #[test] + #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + fn test_irq_chip() { + use Cap; + + let kvm = Kvm::new().unwrap(); + assert!(kvm.check_extension(Cap::Irqchip)); + + let vm = kvm.create_vm().unwrap(); + + // On ARM/arm64, a GICv2 is created. It's better to check ahead whether GICv2 + // can be emulated or not. + let mut gic_device = kvm_bindings::kvm_create_device { + type_: kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2, + fd: 0, + flags: KVM_CREATE_DEVICE_TEST, + }; + + let vgic_v2_supported = vm.create_device(&mut gic_device).is_ok(); + assert_eq!(vm.create_irq_chip().is_ok(), vgic_v2_supported); + } + #[test] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn test_pit2() { From 129e2d26636a227f8d64a2a5fb55bc7314047d50 Mon Sep 17 00:00:00 2001 From: mrxinwang Date: Thu, 12 Dec 2019 17:26:21 +0800 Subject: [PATCH 095/332] Add ioctl KVM_IRQ_LINE from CrosVM to rust-vmm 1. Added implementation and documentation of ioctl KVM_IRQ_LINE as function set_irq_line for both x86 and Arm architectures. 2. Added unit tests of ioctl KVM_IRQ_LINE for x86, x86_64 and aarch64 architectures. 3. Move common code to helper functions `set_supported_nr_irqs` and `request_gic_init`. Signed-off-by: Henry Wang --- src/ioctls/vm.rs | 177 ++++++++++++++++++++++++++++++++++++++++------ src/kvm_ioctls.rs | 8 +++ 2 files changed, 164 insertions(+), 21 deletions(-) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 884880b..4a5b3b8 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -891,6 +891,74 @@ impl VmFd { } } + /// Sets the level on the given irq to 1 if `active` is true, and 0 otherwise. + /// + /// # Arguments + /// + /// * `irq` - IRQ to be set. + /// * `active` - Level of the IRQ input. + /// + /// # Errors + /// + /// Returns an io::Error when the irq field is invalid + /// + /// # Examples + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # extern crate libc; + /// # extern crate vmm_sys_util; + /// # use kvm_ioctls::{Kvm, VmFd}; + /// # use libc::EFD_NONBLOCK; + /// # use vmm_sys_util::eventfd::EventFd; + /// fn arch_setup(vm_fd: &VmFd) { + /// // Arch-specific setup: + /// // For x86 architectures, it simply means calling vm.create_irq_chip().unwrap(). + /// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// # vm_fd.create_irq_chip().unwrap(); + /// // For Arm architectures, the IRQ controllers need to be setup first. + /// // Details please refer to the kernel documentation. + /// // https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt + /// # #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] { + /// # vm_fd.create_vcpu(0).unwrap(); + /// # // ... rest of setup for Arm goes here + /// # } + /// } + /// + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// arch_setup(&vm); + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { + /// vm.set_irq_line(4, true); + /// // ... + /// } + /// #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] { + /// vm.set_irq_line(0x01_00_0020, true); + /// // .... + /// } + /// ``` + /// + #[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" + ))] + pub fn set_irq_line(&self, irq: u32, active: bool) -> Result<()> { + let mut irq_level = kvm_irq_level::default(); + irq_level.__bindgen_anon_1.irq = irq; + irq_level.level = if active { 1 } else { 0 }; + + // Safe because we know that our file is a VM fd, we know the kernel will only read the + // correct amount of memory from our pointer, and we verify the return result. + let ret = unsafe { ioctl_with_ref(self, KVM_IRQ_LINE(), &irq_level) }; + if ret == 0 { + Ok(()) + } else { + Err(errno::Error::last()) + } + } + /// Creates a new KVM vCPU file descriptor and maps the memory corresponding /// its `kvm_run` structure. /// @@ -1119,7 +1187,7 @@ impl AsRawFd for VmFd { } } -/// Creates a dummy GIC device. +/// Create a dummy GIC device. /// /// # Arguments /// @@ -1145,6 +1213,43 @@ pub(crate) fn create_gic_device(vm: &VmFd, flags: u32) -> DeviceFd { device_fd } +/// Set supported number of IRQs for vGIC. +/// +/// # Arguments +/// +/// * `vgic` - The vGIC file descriptor. +/// * `nr_irqs` - Number of IRQs. +/// +#[cfg(test)] +#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] +pub(crate) fn set_supported_nr_irqs(vgic: &DeviceFd, nr_irqs: u32) { + let vgic_attr = kvm_bindings::kvm_device_attr { + group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS, + attr: 0, + addr: &nr_irqs as *const u32 as u64, + flags: 0, + }; + assert!(vgic.set_device_attr(&vgic_attr).is_ok()); +} + +/// Request the initialization of the vGIC. +/// +/// # Arguments +/// +/// * `vgic` - The vGIC file descriptor. +/// +#[cfg(test)] +#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] +pub(crate) fn request_gic_init(vgic: &DeviceFd) { + let vgic_attr = kvm_bindings::kvm_device_attr { + group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, + attr: u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT), + addr: 0, + flags: 0, + }; + assert!(vgic.set_device_attr(&vgic_attr).is_ok()); +} + #[cfg(test)] mod tests { use super::*; @@ -1356,26 +1461,10 @@ mod tests { // Create the vGIC device. let vgic_fd = create_gic_device(&vm_fd, 0); - // Dummy interrupt for testing on aarch64. - let nr_irqs: u32 = 128; - - // We need to tell the kernel how many irqs to support with this vgic. - let vgic_attr = kvm_bindings::kvm_device_attr { - group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS, - attr: 0, - addr: &nr_irqs as *const u32 as u64, - flags: 0, - }; - assert!(vgic_fd.set_device_attr(&vgic_attr).is_ok()); - - // Finalize the GIC. - let vgic_attr = kvm_bindings::kvm_device_attr { - group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, - attr: u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT), - addr: 0, - flags: 0, - }; - assert!(vgic_fd.set_device_attr(&vgic_attr).is_ok()); + // Set supported number of IRQs. + set_supported_nr_irqs(&vgic_fd, 128); + // Request the initialization of the vGIC. + request_gic_init(&vgic_fd); assert!(vm_fd.register_irqfd(&evtfd1, 4).is_ok()); assert!(vm_fd.register_irqfd(&evtfd2, 8).is_ok()); @@ -1393,6 +1482,52 @@ mod tests { assert!(vm_fd.unregister_irqfd(&evtfd3, 5).is_ok()); } + #[test] + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn test_set_irq_line() { + let kvm = Kvm::new().unwrap(); + let vm_fd = kvm.create_vm().unwrap(); + + assert!(vm_fd.create_irq_chip().is_ok()); + + assert!(vm_fd.set_irq_line(4, true).is_ok()); + assert!(vm_fd.set_irq_line(4, false).is_ok()); + assert!(vm_fd.set_irq_line(4, true).is_ok()); + } + + #[test] + #[cfg(target_arch = "aarch64")] + fn test_set_irq_line() { + let kvm = Kvm::new().unwrap(); + let vm_fd = kvm.create_vm().unwrap(); + // Create a vcpu for test case 2 of the KVM_IRQ_LINE API on aarch64. + vm_fd.create_vcpu(0).unwrap(); + + // Create the vGIC device. + let vgic_fd = create_gic_device(&vm_fd, 0); + // Set supported number of IRQs. + set_supported_nr_irqs(&vgic_fd, 128); + // Request the initialization of the vGIC. + request_gic_init(&vgic_fd); + + // On arm/aarch64, irq field is interpreted like this: + // bits: | 31 ... 24 | 23 ... 16 | 15 ... 0 | + // field: | irq_type | vcpu_index | irq_id | + // The irq_type field has the following values: + // - irq_type[0]: out-of-kernel GIC: irq_id 0 is IRQ, irq_id 1 is FIQ + // - irq_type[1]: in-kernel GIC: SPI, irq_id between 32 and 1019 (incl.) (the vcpu_index field is ignored) + // - irq_type[2]: in-kernel GIC: PPI, irq_id between 16 and 31 (incl.) + // Hence, using irq_type = 1, irq_id = 32 (decimal), the irq field in hex is: 0x01_00_0020 + assert!(vm_fd.set_irq_line(0x01_00_0020, true).is_ok()); + assert!(vm_fd.set_irq_line(0x01_00_0020, false).is_ok()); + assert!(vm_fd.set_irq_line(0x01_00_0020, true).is_ok()); + + // Case 2: using irq_type = 2, vcpu_index = 0, irq_id = 16 (decimal), the irq field in hex is: 0x02_00_0010 + assert!(vm_fd.set_irq_line(0x02_00_0010, true).is_ok()); + assert!(vm_fd.set_irq_line(0x02_00_0010, false).is_ok()); + assert!(vm_fd.set_irq_line(0x02_00_0010, true).is_ok()); + } + #[test] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn test_faulty_vm_fd() { diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index e38ff73..1211a79 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -46,6 +46,14 @@ ioctl_io_nr!(KVM_SET_TSS_ADDR, KVMIO, 0x47); target_arch = "s390" ))] ioctl_io_nr!(KVM_CREATE_IRQCHIP, KVMIO, 0x60); +/* Available with KVM_CAP_IRQCHIP */ +#[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" +))] +ioctl_iow_nr!(KVM_IRQ_LINE, KVMIO, 0x61, kvm_irq_level); /* Available with KVM_CAP_IRQ_ROUTING */ #[cfg(any( target_arch = "x86", From f2c4d4986bfbd9a21570e785a54453cd368acfc5 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 26 Apr 2019 10:47:43 -0400 Subject: [PATCH 096/332] aarch64: use 64k aligned addresses in tests If we're on an AArch64 host that has been configured to use 64k pages, then we need to align our addresses to that larger granule. Signed-off-by: Andrew Jones --- src/ioctls/vm.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 4a5b3b8..0f17f73 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -87,8 +87,8 @@ impl VmFd { /// let vm = kvm.create_vm().unwrap(); /// let mem_region = kvm_userspace_memory_region { /// slot: 0, - /// guest_phys_addr: 0x1000 as u64, - /// memory_size: 0x4000 as u64, + /// guest_phys_addr: 0x10000 as u64, + /// memory_size: 0x10000 as u64, /// userspace_addr: 0x0 as u64, /// flags: 0, /// }; From e904ef3f333776fb95fdaa7432b7eb6ee65c084b Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 26 Apr 2019 12:57:03 -0400 Subject: [PATCH 097/332] aarch64: support for getting dirty logs Signed-off-by: Andrew Jones Signed-off-by: Diana Popa --- src/ioctls/vm.rs | 64 ++++++++++++++++++++++++++++++++--------------- src/kvm_ioctls.rs | 1 - 2 files changed, 44 insertions(+), 21 deletions(-) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 0f17f73..fcbba77 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -7,7 +7,6 @@ use kvm_bindings::*; use std::fs::File; -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use std::os::raw::c_void; use std::os::raw::{c_int, c_ulong}; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; @@ -697,7 +696,7 @@ impl VmFd { /// # use kvm_bindings::{kvm_userspace_memory_region, KVM_MEM_LOG_DIRTY_PAGES}; /// # let kvm = Kvm::new().unwrap(); /// # let vm = kvm.create_vm().unwrap(); - /// // This examples is based on https://lwn.net/Articles/658511/. + /// // This example is based on https://lwn.net/Articles/658511/. /// let mem_size = 0x4000; /// let guest_addr: u64 = 0x1000; /// let load_addr: *mut u8 = unsafe { @@ -721,36 +720,62 @@ impl VmFd { /// }; /// unsafe { vm.set_user_memory_region(mem_region).unwrap() }; /// - /// // Dummy x86 code that just calls halt. - /// let x86_code = [ - /// 0xf4, /* hlt */ + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// // ASM code that just forces a MMIO Write. + /// let asm_code = [ + /// 0xc6, 0x06, 0x00, 0x80, 0x00, + /// ]; + /// #[cfg(target_arch = "aarch64")] + /// let asm_code = [ + /// 0x01, 0x00, 0x00, 0x10, /* adr x1, */ + /// 0x22, 0x10, 0x00, 0xb9, /* str w2, [x1, #16]; write to this page */ + /// 0x02, 0x00, 0x00, 0xb9, /* str w2, [x0]; force MMIO exit */ + /// 0x00, 0x00, 0x00, 0x14, /* b ; shouldn't get here, but if so loop forever */ /// ]; /// /// // Write the code in the guest memory. This will generate a dirty page. /// unsafe { /// let mut slice = slice::from_raw_parts_mut(load_addr, mem_size); - /// slice.write(&x86_code).unwrap(); + /// slice.write(&asm_code).unwrap(); /// } /// /// let vcpu_fd = vm.create_vcpu(0).unwrap(); /// - /// let mut vcpu_sregs = vcpu_fd.get_sregs().unwrap(); - /// vcpu_sregs.cs.base = 0; - /// vcpu_sregs.cs.selector = 0; - /// vcpu_fd.set_sregs(&vcpu_sregs).unwrap(); + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// { + /// // x86_64 specific registry setup. + /// let mut vcpu_sregs = vcpu_fd.get_sregs().unwrap(); + /// vcpu_sregs.cs.base = 0; + /// vcpu_sregs.cs.selector = 0; + /// vcpu_fd.set_sregs(&vcpu_sregs).unwrap(); + /// + /// let mut vcpu_regs = vcpu_fd.get_regs().unwrap(); + /// // Set the Instruction Pointer to the guest address where we loaded the code. + /// vcpu_regs.rip = guest_addr; + /// vcpu_regs.rax = 2; + /// vcpu_regs.rbx = 3; + /// vcpu_regs.rflags = 2; + /// vcpu_fd.set_regs(&vcpu_regs).unwrap(); + /// } /// - /// let mut vcpu_regs = vcpu_fd.get_regs().unwrap(); - /// // Set the Instruction Pointer to the guest address where we loaded the code. - /// vcpu_regs.rip = guest_addr; - /// vcpu_regs.rax = 2; - /// vcpu_regs.rbx = 3; - /// vcpu_regs.rflags = 2; - /// vcpu_fd.set_regs(&vcpu_regs).unwrap(); + /// #[cfg(target_arch = "aarch64")] + /// { + /// // aarch64 specific registry setup. + /// let mut kvi = kvm_bindings::kvm_vcpu_init::default(); + /// vm.get_preferred_target(&mut kvi).unwrap(); + /// vcpu_fd.vcpu_init(&kvi).unwrap(); + /// + /// let core_reg_base: u64 = 0x6030_0000_0010_0000; + /// let mmio_addr: u64 = guest_addr + mem_size as u64; + /// vcpu_fd.set_one_reg(core_reg_base + 2 * 32, guest_addr); // set PC + /// vcpu_fd.set_one_reg(core_reg_base + 2 * 0, mmio_addr); // set X0 + /// } /// /// loop { /// match vcpu_fd.run().expect("run failed") { - /// VcpuExit::Hlt => { - /// // The code snippet dirties 1 page when loading the code in memory. + /// VcpuExit::MmioWrite(addr, data) => { + /// // On x86_64, the code snippet dirties 1 page when loading the code in memory + /// // while on aarch64 the dirty bit comes from writing to guest_addr (current PC). /// let dirty_pages_bitmap = vm.get_dirty_log(0, mem_size).unwrap(); /// let dirty_pages = dirty_pages_bitmap /// .into_iter() @@ -764,7 +789,6 @@ impl VmFd { /// } /// ``` /// - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_dirty_log(&self, slot: u32, memory_size: usize) -> Result> { // Compute the length of the bitmap needed for all dirty pages in one memory slot. // One memory page is 4KiB (4096 bits) and `KVM_GET_DIRTY_LOG` returns one dirty bit for diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index 1211a79..934f954 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -25,7 +25,6 @@ ioctl_iowr_nr!(KVM_GET_EMULATED_CPUID, KVMIO, 0x09, kvm_cpuid2); // Ioctls for VM fds. ioctl_io_nr!(KVM_CREATE_VCPU, KVMIO, 0x41); -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_iow_nr!(KVM_GET_DIRTY_LOG, KVMIO, 0x42, kvm_dirty_log); /* Available with KVM_CAP_USER_MEMORY */ ioctl_iow_nr!( From a1f642503155b1bb4abfebcb2de0ecd8e4ebaa13 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 26 Apr 2019 12:57:30 -0400 Subject: [PATCH 098/332] aarch64: add test_run_code Signed-off-by: Andrew Jones Signed-off-by: Diana Popa --- src/ioctls/vcpu.rs | 97 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 2 deletions(-) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 48eebf4..875b611 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1192,7 +1192,6 @@ impl AsRawFd for VcpuFd { mod tests { extern crate byteorder; - #[cfg(target_arch = "x86_64")] use super::*; use ioctls::system::Kvm; #[cfg(any( @@ -1205,7 +1204,6 @@ mod tests { // Helper function for memory mapping `size` bytes of anonymous memory. // Panics if the mmap fails. - #[cfg(target_arch = "x86_64")] fn mmap_anonymous(size: usize) -> *mut u8 { use std::ptr::null_mut; @@ -1430,6 +1428,101 @@ mod tests { } } + #[cfg(target_arch = "aarch64")] + #[test] + fn test_run_code() { + use std::io::Write; + + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + #[rustfmt::skip] + let code = [ + 0x40, 0x20, 0x80, 0x52, /* mov w0, #0x102 */ + 0x00, 0x01, 0x00, 0xb9, /* str w0, [x8]; test physical memory write */ + 0x81, 0x60, 0x80, 0x52, /* mov w1, #0x304 */ + 0x02, 0x00, 0x80, 0x52, /* mov w2, #0x0 */ + 0x20, 0x01, 0x40, 0xb9, /* ldr w0, [x9]; test MMIO read */ + 0x1f, 0x18, 0x14, 0x71, /* cmp w0, #0x506 */ + 0x20, 0x00, 0x82, 0x1a, /* csel w0, w1, w2, eq */ + 0x20, 0x01, 0x00, 0xb9, /* str w0, [x9]; test MMIO write */ + 0x00, 0x00, 0x00, 0x14, /* b ; shouldn't get here, but if so loop forever */ + ]; + + let mem_size = 0x20000; + let load_addr = mmap_anonymous(mem_size); + let guest_addr: u64 = 0x10000; + let slot: u32 = 0; + let mem_region = kvm_userspace_memory_region { + slot, + guest_phys_addr: guest_addr, + memory_size: mem_size as u64, + userspace_addr: load_addr as u64, + flags: KVM_MEM_LOG_DIRTY_PAGES, + }; + unsafe { + vm.set_user_memory_region(mem_region).unwrap(); + } + + unsafe { + // Get a mutable slice of `mem_size` from `load_addr`. + // This is safe because we mapped it before. + let mut slice = std::slice::from_raw_parts_mut(load_addr, mem_size); + slice.write(&code).unwrap(); + } + + let vcpu_fd = vm.create_vcpu(0).unwrap(); + let mut kvi = kvm_bindings::kvm_vcpu_init::default(); + vm.get_preferred_target(&mut kvi).unwrap(); + vcpu_fd.vcpu_init(&kvi).unwrap(); + + let core_reg_base: u64 = 0x6030_0000_0010_0000; + let mmio_addr: u64 = guest_addr + mem_size as u64; + + // Set the PC to the guest address where we loaded the code. + vcpu_fd + .set_one_reg(core_reg_base + 2 * 32, guest_addr) + .unwrap(); + + // Set x8 and x9 to the addresses the guest test code needs + vcpu_fd + .set_one_reg(core_reg_base + 2 * 8, guest_addr + 0x10000) + .unwrap(); + vcpu_fd + .set_one_reg(core_reg_base + 2 * 9, mmio_addr) + .unwrap(); + + loop { + match vcpu_fd.run().expect("run failed") { + VcpuExit::MmioRead(addr, data) => { + assert_eq!(addr, mmio_addr); + assert_eq!(data.len(), 4); + data[3] = 0x0; + data[2] = 0x0; + data[1] = 0x5; + data[0] = 0x6; + } + VcpuExit::MmioWrite(addr, data) => { + assert_eq!(addr, mmio_addr); + assert_eq!(data.len(), 4); + assert_eq!(data[3], 0x0); + assert_eq!(data[2], 0x0); + assert_eq!(data[1], 0x3); + assert_eq!(data[0], 0x4); + // The code snippet dirties one page at guest_addr + 0x10000. + // The code page should not be dirty, as it's not written by the guest. + let dirty_pages_bitmap = vm.get_dirty_log(slot, mem_size).unwrap(); + let dirty_pages = dirty_pages_bitmap + .into_iter() + .map(|page| page.count_ones()) + .fold(0, |dirty_page_count, i| dirty_page_count + i); + assert_eq!(dirty_pages, 1); + break; + } + r => panic!("unexpected exit reason: {:?}", r), + } + } + } + #[cfg(target_arch = "x86_64")] #[test] fn test_run_code() { From 18bc6b95a86df9feaa44d8a2ad3383a1f0d179eb Mon Sep 17 00:00:00 2001 From: Diana Popa Date: Thu, 10 Oct 2019 17:30:52 +0300 Subject: [PATCH 099/332] aarch64: update main doc The main doc example for running some machine code in a microVM is adapted to also support aarch64 architecture. Signed-off-by: Diana Popa --- src/lib.rs | 103 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 67 insertions(+), 36 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 36459b2..07c2842 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,8 +24,9 @@ //! # Example - Running a VM on x86_64 //! //! In this example we are creating a Virtual Machine (VM) with one vCPU. -//! On the vCPU we are running x86_64 specific code. This example is based on +//! On the vCPU we are running machine specific code. This example is based on //! the [LWN article](https://lwn.net/Articles/658511/) on using the KVM API. +//! The aarch64 example was modfied accordingly. //! //! To get code running on the vCPU we are going through the following steps: //! @@ -37,7 +38,7 @@ //! are adding only one memory region and write the code in one memory page. //! 4. Create a vCPU using the VM object. The vCPU is used for running //! [vCPU specific ioctls](struct.VcpuFd.html). -//! 5. Setup x86 specific general purpose registers and special registers. For +//! 5. Setup architectural specific general purpose registers and special registers. For //! details about how and why these registers are set, please check the //! [LWN article](https://lwn.net/Articles/658511/) on which this example is //! built. @@ -52,7 +53,6 @@ //! use kvm_ioctls::{Kvm, VmFd, VcpuFd}; //! use kvm_ioctls::VcpuExit; //! -//! #[cfg(target_arch = "x86_64")] //! fn main(){ //! use std::io::Write; //! use std::slice; @@ -61,6 +61,34 @@ //! use kvm_bindings::KVM_MEM_LOG_DIRTY_PAGES; //! use kvm_bindings::kvm_userspace_memory_region; //! +//! let mem_size = 0x4000; +//! let guest_addr = 0x1000; +//! let asm_code: &[u8]; +//! +//! // Setting up architectural dependent values. +//! #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +//! { +//! asm_code = &[ +//! 0xba, 0xf8, 0x03, /* mov $0x3f8, %dx */ +//! 0x00, 0xd8, /* add %bl, %al */ +//! 0x04, b'0', /* add $'0', %al */ +//! 0xee, /* out %al, %dx */ +//! 0xec, /* in %dx, %al */ +//! 0xc6, 0x06, 0x00, 0x80, 0x00, /* movl $0, (0x8000); This generates a MMIO Write.*/ +//! 0x8a, 0x16, 0x00, 0x80, /* movl (0x8000), %dl; This generates a MMIO Read.*/ +//! 0xf4, /* hlt */ +//! ]; +//! } +//! #[cfg(target_arch = "aarch64")] +//! { +//! asm_code = &[ +//! 0x01, 0x00, 0x00, 0x10, /* adr x1, */ +//! 0x22, 0x10, 0x00, 0xb9, /* str w2, [x1, #16]; write to this page */ +//! 0x02, 0x00, 0x00, 0xb9, /* str w2, [x0]; This generates a MMIO Write.*/ +//! 0x00, 0x00, 0x00, 0x14, /* b ; shouldn't get here, but if so loop forever */ +//! ]; +//! } +//! //! // 1. Instantiate KVM. //! let kvm = Kvm::new().unwrap(); //! @@ -68,8 +96,6 @@ //! let vm = kvm.create_vm().unwrap(); //! //! // 3. Initialize Guest Memory. -//! let mem_size = 0x4000; -//! let guest_addr: u64 = 0x1000; //! let load_addr: *mut u8 = unsafe { //! libc::mmap( //! null_mut(), @@ -93,39 +119,44 @@ //! }; //! unsafe { vm.set_user_memory_region(mem_region).unwrap() }; //! -//! -//! let x86_code = [ -//! 0xba, 0xf8, 0x03, /* mov $0x3f8, %dx */ -//! 0x00, 0xd8, /* add %bl, %al */ -//! 0x04, b'0', /* add $'0', %al */ -//! 0xee, /* out %al, %dx */ -//! 0xec, /* in %dx, %al */ -//! 0xc6, 0x06, 0x00, 0x80, 0x00, /* movl $0, (0x8000); This generates a MMIO Write.*/ -//! 0x8a, 0x16, 0x00, 0x80, /* movl (0x8000), %dl; This generates a MMIO Read.*/ -//! 0xf4, /* hlt */ -//! ]; -//! //! // Write the code in the guest memory. This will generate a dirty page. //! unsafe { //! let mut slice = slice::from_raw_parts_mut(load_addr, mem_size); -//! slice.write(&x86_code).unwrap(); +//! slice.write(&asm_code).unwrap(); //! } //! //! // 4. Create one vCPU. //! let vcpu_fd = vm.create_vcpu(0).unwrap(); //! //! // 5. Initialize general purpose and special registers. -//! let mut vcpu_sregs = vcpu_fd.get_sregs().unwrap(); -//! vcpu_sregs.cs.base = 0; -//! vcpu_sregs.cs.selector = 0; -//! vcpu_fd.set_sregs(&vcpu_sregs).unwrap(); -//! -//! let mut vcpu_regs = vcpu_fd.get_regs().unwrap(); -//! vcpu_regs.rip = guest_addr; -//! vcpu_regs.rax = 2; -//! vcpu_regs.rbx = 3; -//! vcpu_regs.rflags = 2; -//! vcpu_fd.set_regs(&vcpu_regs).unwrap(); +//! #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +//! { +//! // x86_64 specific registry setup. +//! let mut vcpu_sregs = vcpu_fd.get_sregs().unwrap(); +//! vcpu_sregs.cs.base = 0; +//! vcpu_sregs.cs.selector = 0; +//! vcpu_fd.set_sregs(&vcpu_sregs).unwrap(); +//! +//! let mut vcpu_regs = vcpu_fd.get_regs().unwrap(); +//! vcpu_regs.rip = guest_addr; +//! vcpu_regs.rax = 2; +//! vcpu_regs.rbx = 3; +//! vcpu_regs.rflags = 2; +//! vcpu_fd.set_regs(&vcpu_regs).unwrap(); +//! } +//! +//! #[cfg(target_arch = "aarch64")] +//! { +//! // aarch64 specific registry setup. +//! let mut kvi = kvm_bindings::kvm_vcpu_init::default(); +//! vm.get_preferred_target(&mut kvi).unwrap(); +//! vcpu_fd.vcpu_init(&kvi).unwrap(); +//! +//! let core_reg_base: u64 = 0x6030_0000_0010_0000; +//! let mmio_addr: u64 = guest_addr + mem_size as u64; +//! vcpu_fd.set_one_reg(core_reg_base + 2 * 32, guest_addr); // set PC +//! vcpu_fd.set_one_reg(core_reg_base + 2 * 0, mmio_addr); // set X0 +//! } //! //! // 6. Run code on the vCPU. //! loop { @@ -155,8 +186,6 @@ //! "Received an MMIO Write Request to the address {:#x}.", //! addr, //! ); -//! } -//! VcpuExit::Hlt => { //! // The code snippet dirties 1 page when it is loaded in memory //! let dirty_pages_bitmap = vm.get_dirty_log(slot, mem_size).unwrap(); //! let dirty_pages = dirty_pages_bitmap @@ -164,17 +193,19 @@ //! .map(|page| page.count_ones()) //! .fold(0, |dirty_page_count, i| dirty_page_count + i); //! assert_eq!(dirty_pages, 1); +//! // Since on aarch64 there is not halt instruction, +//! // we break immediately after the last known instruction +//! // of the asm code example so that we avoid an infinite loop. +//! #[cfg(target_arch = "aarch64")] +//! break; +//! } +//! VcpuExit::Hlt => { //! break; //! } //! r => panic!("Unexpected exit reason: {:?}", r), //! } //! } //! } -//! -//! #[cfg(not(target_arch = "x86_64"))] -//! fn main() { -//! println!("This code example only works on x86_64."); -//! } //! ``` extern crate kvm_bindings; From 4bdd6d5bd9c3055455fcb8f768d7001a74d6fc12 Mon Sep 17 00:00:00 2001 From: Michael Zhao Date: Wed, 8 Jan 2020 03:09:25 +0000 Subject: [PATCH 100/332] Improve example code of VmFd::create_device() for ARM. On ARM, the example code tried to create VGIC v3, it may fail due to hardware dependency. The error can be seen on machines with GIC v2, like Raspberry PI 4. Now we retry VGICv2 in that case. Change-Id: Ie4a5b2b86c234350e444f2f9e94630e4d68af7ab Signed-off-by: Michael Zhao --- src/ioctls/vm.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index fcbba77..ab9d614 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -1070,6 +1070,7 @@ impl VmFd { /// # use kvm_ioctls::Kvm; /// use kvm_bindings::{ /// kvm_device_type_KVM_DEV_TYPE_VFIO, + /// kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2, /// kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, /// KVM_CREATE_DEVICE_TEST, /// }; @@ -1087,8 +1088,17 @@ impl VmFd { /// fd: 0, /// flags: KVM_CREATE_DEVICE_TEST, /// }; - /// let device_fd = vm - /// .create_device(&mut device).unwrap(); + /// // On ARM, creating VGICv3 may fail due to hardware dependency. + /// // Retry to create VGICv2 in that case. + /// let device_fd = vm.create_device(&mut device).unwrap_or_else(|_| { + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// panic!("Cannot create VFIO device."); + /// #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + /// { + /// device.type_ = kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2; + /// vm.create_device(&mut device).expect("Cannot create vGIC device") + /// } + /// }); /// ``` /// pub fn create_device(&self, device: &mut kvm_create_device) -> Result { From ee9a81a1241b320b4067b7d9bcf911da438e4db6 Mon Sep 17 00:00:00 2001 From: Alexandra Iordache Date: Tue, 28 Jan 2020 18:44:33 +0200 Subject: [PATCH 101/332] release v0.5.0 Signed-off-by: Alexandra Iordache --- CHANGELOG.md | 7 +++++++ Cargo.toml | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a77e080..730d3dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# v0.5.0 + +## Added +- Support for the vcpu ioctls `KVM_GET/SET_VCPU_EVENTS` and `KVM_GET_DIRTY_LOG` + on `aarch64`. +- Support for the vcpu ioctl `KVM_IRQ_LINE`. + # v0.4.0 ## Added diff --git a/Cargo.toml b/Cargo.toml index 5ad3e20..f010446 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kvm-ioctls" -version = "0.4.0" +version = "0.5.0" authors = ["Amazon Firecracker Team "] description = "Safe wrappers over KVM ioctls" repository = "https://github.com/rust-vmm/kvm-ioctls" From 229cb5b00b98713e7072bc3519927690ca7c01ef Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Tue, 18 Feb 2020 10:32:56 +0100 Subject: [PATCH 102/332] update rust-vmm-ci The new container is using Rust 1.39. Fixed the warning of unneeded unsafe block and updated coverage. Signed-off-by: Andreea Florescu --- coverage_config.json | 2 +- rust-vmm-ci | 2 +- src/ioctls/vm.rs | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/coverage_config.json b/coverage_config.json index b15b2eb..2ddf3a6 100644 --- a/coverage_config.json +++ b/coverage_config.json @@ -1,5 +1,5 @@ { - "coverage_score": 91.9, + "coverage_score": 92.0, "exclude_path": "", "crate_features": "" } diff --git a/rust-vmm-ci b/rust-vmm-ci index 8b60273..c309d06 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 8b60273cef886444a5bbc27f9c809b6d8806a58e +Subproject commit c309d0627bde6b07db91201dd8b47007841c100a diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index ab9d614..d4cc419 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -1326,9 +1326,7 @@ mod tests { let mut irqchip = kvm_irqchip::default(); irqchip.chip_id = KVM_IRQCHIP_PIC_MASTER; // Set the irq_base to a non-default value to check that set & get work. - unsafe { - irqchip.chip.pic.irq_base = 10; - } + irqchip.chip.pic.irq_base = 10; assert!(vm.set_irqchip(&irqchip).is_ok()); // We initialize a dummy irq chip (`other_irqchip`) in which the From 6e3c76d5530bf898aad591d483c9100cbcb5bce0 Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Tue, 18 Feb 2020 11:33:29 +0200 Subject: [PATCH 103/332] use the configured OS page size in get_dirty_log() Right now get_dirty_log() acts on the assumption that the page size is always 4K. This is not necessarily true, since the page size is configurable. This commit improves the logic by calling sysconf() to get the actual page size. Signed-off-by: Serban Iorga --- coverage_config.json | 2 +- src/ioctls/vm.rs | 20 +++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/coverage_config.json b/coverage_config.json index 2ddf3a6..612e59e 100644 --- a/coverage_config.json +++ b/coverage_config.json @@ -1,5 +1,5 @@ { - "coverage_score": 92.0, + "coverage_score": 92.1, "exclude_path": "", "crate_features": "" } diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index d4cc419..702d997 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -791,22 +791,24 @@ impl VmFd { /// pub fn get_dirty_log(&self, slot: u32, memory_size: usize) -> Result> { // Compute the length of the bitmap needed for all dirty pages in one memory slot. - // One memory page is 4KiB (4096 bits) and `KVM_GET_DIRTY_LOG` returns one dirty bit for + // One memory page is `page_size` bytes and `KVM_GET_DIRTY_LOG` returns one dirty bit for // each page. - let page_size = 4 << 10; + let page_size = match unsafe { libc::sysconf(libc::_SC_PAGESIZE) } { + -1 => return Err(errno::Error::last()), + ps => ps as usize, + }; - let div_round_up = |dividend, divisor| (dividend + divisor - 1) / divisor; // For ease of access we are saving the bitmap in a u64 vector. We are using ceil to - // make sure we count all dirty pages even when `mem_size` is not a multiple of - // page_size * 64. - let bitmap_size = div_round_up(memory_size, page_size * 64); - let mut bitmap = vec![0; bitmap_size]; - let b_data = bitmap.as_mut_ptr() as *mut c_void; + // make sure we count all dirty pages even when `memory_size` is not a multiple of + // `page_size * 64`. + let div_ceil = |dividend, divisor| (dividend + divisor - 1) / divisor; + let bitmap_size = div_ceil(memory_size, page_size * 64); + let mut bitmap = vec![0u64; bitmap_size]; let dirtylog = kvm_dirty_log { slot, padding1: 0, __bindgen_anon_1: kvm_dirty_log__bindgen_ty_1 { - dirty_bitmap: b_data, + dirty_bitmap: bitmap.as_mut_ptr() as *mut c_void, }, }; // Safe because we know that our file is a VM fd, and we know that the amount of memory From 83035469facaf49de0c284cc365f5fd26edea515 Mon Sep 17 00:00:00 2001 From: mrxinwang Date: Sat, 22 Feb 2020 11:43:44 +0800 Subject: [PATCH 104/332] Enable arm coverage test for kvm-ioctl As kcov supports arm64 now, this commit will enable the arm coverage test in the CI of kvm-ioctl repository. This commit contains a workaround to avoid `test_create_device` failure caused by ioctl returning `EINVAL` instead of `ENOTTY` using gnu toolchain. Signed-off-by: Henry Wang --- coverage_config_aarch64.json | 5 +++++ coverage_config.json => coverage_config_x86_64.json | 0 rust-vmm-ci | 2 +- src/ioctls/device.rs | 5 ++++- 4 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 coverage_config_aarch64.json rename coverage_config.json => coverage_config_x86_64.json (100%) diff --git a/coverage_config_aarch64.json b/coverage_config_aarch64.json new file mode 100644 index 0000000..648083f --- /dev/null +++ b/coverage_config_aarch64.json @@ -0,0 +1,5 @@ +{ + "coverage_score": 76.3, + "exclude_path": "", + "crate_features": "" +} diff --git a/coverage_config.json b/coverage_config_x86_64.json similarity index 100% rename from coverage_config.json rename to coverage_config_x86_64.json diff --git a/rust-vmm-ci b/rust-vmm-ci index c309d06..cd7096e 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit c309d0627bde6b07db91201dd8b47007841c100a +Subproject commit cd7096e7a6b649b76ebea80c341fc020a89356a1 diff --git a/src/ioctls/device.rs b/src/ioctls/device.rs index 56a8efd..4a01244 100644 --- a/src/ioctls/device.rs +++ b/src/ioctls/device.rs @@ -140,6 +140,9 @@ mod tests { // We are just creating a test device. Creating a real device would make the CI dependent // on host configuration (like having /dev/vfio). We expect this to fail. assert!(device_fd.set_device_attr(&dist_attr).is_err()); - assert_eq!(errno::Error::last().errno(), 25); + // Comment this assertion as a workaround for arm coverage test CI, as it is testing the error + // case that cannot be reproduced in a real case scenario. This assertion will lead to failure + // caused by ioctl returning `EINVAL` instead of `ENOTTY` when using gnu toolchain. + //assert_eq!(errno::Error::last().errno(), 25); } } From b968736f78b3dba55b19d1be236cf103ad06d8f9 Mon Sep 17 00:00:00 2001 From: Michael Zhao Date: Wed, 4 Mar 2020 14:02:44 +0800 Subject: [PATCH 105/332] Add support for KVM_HAS_DEVICE_ATTR ioctl. Following modifications are included: 1. Add the wrapper for KVM_HAS_DEVICE_ATTR ioctl. 2. Update the test case of VGIC. 3. Add an example to show the usage of KVM_HAS_DEVICE_ATTR together with KVM_SET_DEVICE_ATTR. Change-Id: Idacae319c8ff5ace722e81a279560d823e2458cc Signed-off-by: Michael Zhao --- coverage_config_aarch64.json | 2 +- coverage_config_x86_64.json | 2 +- src/ioctls/device.rs | 87 +++++++++++++++++++++++++++++++----- src/kvm_ioctls.rs | 4 +- 4 files changed, 80 insertions(+), 15 deletions(-) diff --git a/coverage_config_aarch64.json b/coverage_config_aarch64.json index 648083f..00ada22 100644 --- a/coverage_config_aarch64.json +++ b/coverage_config_aarch64.json @@ -1,5 +1,5 @@ { - "coverage_score": 76.3, + "coverage_score": 76.5, "exclude_path": "", "crate_features": "" } diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index 612e59e..2ddf3a6 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 92.1, + "coverage_score": 92.0, "exclude_path": "", "crate_features": "" } diff --git a/src/ioctls/device.rs b/src/ioctls/device.rs index 4a01244..dde6d99 100644 --- a/src/ioctls/device.rs +++ b/src/ioctls/device.rs @@ -6,7 +6,7 @@ use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use ioctls::Result; use kvm_bindings::kvm_device_attr; -use kvm_ioctls::KVM_SET_DEVICE_ATTR; +use kvm_ioctls::{KVM_HAS_DEVICE_ATTR, KVM_SET_DEVICE_ATTR}; use vmm_sys_util::errno; use vmm_sys_util::ioctl::ioctl_with_ref; @@ -16,6 +16,21 @@ pub struct DeviceFd { } impl DeviceFd { + /// Tests whether a device supports a particular attribute. + /// + /// See the documentation for `KVM_HAS_DEVICE_ATTR`. + /// # Arguments + /// + /// * `device_attr` - The device attribute to be tested. `addr` field is ignored. + /// + pub fn has_device_attr(&self, device_attr: &kvm_device_attr) -> Result<()> { + let ret = unsafe { ioctl_with_ref(self, KVM_HAS_DEVICE_ATTR(), device_attr) }; + if ret != 0 { + return Err(errno::Error::last()); + } + Ok(()) + } + /// Sets a specified piece of device configuration and/or state. /// /// See the documentation for `KVM_SET_DEVICE_ATTR`. @@ -23,6 +38,41 @@ impl DeviceFd { /// /// * `device_attr` - The device attribute to be set. /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # extern crate kvm_bindings; + /// # use kvm_ioctls::Kvm; + /// # use kvm_bindings::{ + /// kvm_device_type_KVM_DEV_TYPE_VFIO, + /// KVM_DEV_VFIO_GROUP, KVM_DEV_VFIO_GROUP_ADD, KVM_CREATE_DEVICE_TEST + /// }; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// + /// let mut device = kvm_bindings::kvm_create_device { + /// type_: kvm_device_type_KVM_DEV_TYPE_VFIO, + /// fd: 0, + /// flags: KVM_CREATE_DEVICE_TEST, + /// }; + /// + /// let device_fd = vm + /// .create_device(&mut device) + /// .expect("Cannot create KVM device"); + /// + /// let dist_attr = kvm_bindings::kvm_device_attr { + /// group: KVM_DEV_VFIO_GROUP, + /// attr: u64::from(KVM_DEV_VFIO_GROUP_ADD), + /// addr: 0x0, + /// flags: 0, + /// }; + /// + /// if (device_fd.has_device_attr(&dist_attr).is_ok()) { + /// device_fd.set_device_attr(&dist_attr).unwrap(); + /// } + /// ``` + /// pub fn set_device_attr(&self, device_attr: &kvm_device_attr) -> Result<()> { let ret = unsafe { ioctl_with_ref(self, KVM_SET_DEVICE_ATTR(), device_attr) }; if ret != 0 { @@ -62,8 +112,15 @@ mod tests { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use kvm_bindings::{ kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, kvm_device_type_KVM_DEV_TYPE_VFIO, + KVM_DEV_VFIO_GROUP, KVM_DEV_VFIO_GROUP_ADD, }; - use kvm_bindings::{KVM_CREATE_DEVICE_TEST, KVM_DEV_VFIO_GROUP, KVM_DEV_VFIO_GROUP_ADD}; + #[cfg(target_arch = "aarch64")] + use kvm_bindings::{ + KVM_DEV_ARM_VGIC_CTRL_INIT, KVM_DEV_ARM_VGIC_GRP_CTRL, KVM_DEV_VFIO_GROUP, + KVM_DEV_VFIO_GROUP_ADD, + }; + + use kvm_bindings::KVM_CREATE_DEVICE_TEST; #[test] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] @@ -100,6 +157,7 @@ mod tests { // We are just creating a test device. Creating a real device would make the CI dependent // on host configuration (like having /dev/vfio). We expect this to fail. + assert!(device_fd.has_device_attr(&dist_attr).is_err()); assert!(device_fd.set_device_attr(&dist_attr).is_err()); assert_eq!(errno::Error::last().errno(), 25); } @@ -118,11 +176,11 @@ mod tests { fd: 0, flags: KVM_CREATE_DEVICE_TEST, }; - // This fails on aarch64 as it does not use MPIC (MultiProcessor Interrupt Controller), it uses - // the VGIC. + // This fails on aarch64 as it does not use MPIC (MultiProcessor Interrupt Controller), + // it uses the VGIC. assert!(vm.create_device(&mut gic_device).is_err()); - let device_fd = create_gic_device(&vm, KVM_CREATE_DEVICE_TEST); + let device_fd = create_gic_device(&vm, 0); // Following lines to re-construct device_fd are used to test // DeviceFd::from_raw_fd() and DeviceFd::as_raw_fd(). @@ -130,19 +188,24 @@ mod tests { assert!(raw_fd >= 0); let device_fd = unsafe { DeviceFd::from_raw_fd(raw_fd) }; + // Set some attribute that does not apply to VGIC, expect the test to fail. let dist_attr = kvm_bindings::kvm_device_attr { group: KVM_DEV_VFIO_GROUP, attr: u64::from(KVM_DEV_VFIO_GROUP_ADD), addr: 0x0, flags: 0, }; + assert!(device_fd.has_device_attr(&dist_attr).is_err()); - // We are just creating a test device. Creating a real device would make the CI dependent - // on host configuration (like having /dev/vfio). We expect this to fail. - assert!(device_fd.set_device_attr(&dist_attr).is_err()); - // Comment this assertion as a workaround for arm coverage test CI, as it is testing the error - // case that cannot be reproduced in a real case scenario. This assertion will lead to failure - // caused by ioctl returning `EINVAL` instead of `ENOTTY` when using gnu toolchain. - //assert_eq!(errno::Error::last().errno(), 25); + // Following attribute works with VGIC, they should be accepted. + let dist_attr = kvm_bindings::kvm_device_attr { + group: KVM_DEV_ARM_VGIC_GRP_CTRL, + attr: u64::from(KVM_DEV_ARM_VGIC_CTRL_INIT), + addr: 0x0, + flags: 0, + }; + + assert!(device_fd.has_device_attr(&dist_attr).is_ok()); + assert!(device_fd.set_device_attr(&dist_attr).is_ok()); } } diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index 934f954..35d1d83 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -216,8 +216,10 @@ ioctl_ior_nr!(KVM_ARM_PREFERRED_TARGET, KVMIO, 0xaf, kvm_vcpu_init); /* Available with KVM_CAP_DEVICE_CTRL */ ioctl_iowr_nr!(KVM_CREATE_DEVICE, KVMIO, 0xe0, kvm_create_device); -/* Available with KVM_CAP_VM_ATTRIBUTES */ +/* Available with KVM_CAP_DEVICE_CTRL */ ioctl_iow_nr!(KVM_SET_DEVICE_ATTR, KVMIO, 0xe1, kvm_device_attr); +/* Available with KVM_CAP_DEVICE_CTRL */ +ioctl_iow_nr!(KVM_HAS_DEVICE_ATTR, KVMIO, 0xe3, kvm_device_attr); #[cfg(test)] mod tests { From cbd6302607f507011d4f4751b129816ded1fc6a2 Mon Sep 17 00:00:00 2001 From: Qiu Wenbo Date: Sun, 22 Mar 2020 02:39:30 +0800 Subject: [PATCH 106/332] vcpu: expose type and flags on KVM_EXIT_SYSTEM_EVENT Signed-off-by: Qiu Wenbo --- coverage_config_aarch64.json | 2 +- coverage_config_x86_64.json | 2 +- src/ioctls/vcpu.rs | 20 ++++++++++++++++++-- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/coverage_config_aarch64.json b/coverage_config_aarch64.json index 00ada22..64d79b5 100644 --- a/coverage_config_aarch64.json +++ b/coverage_config_aarch64.json @@ -1,5 +1,5 @@ { - "coverage_score": 76.5, + "coverage_score": 77.2, "exclude_path": "", "crate_features": "" } diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index 2ddf3a6..eba81b3 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 92.0, + "coverage_score": 91.3, "exclude_path": "", "crate_features": "" } diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 875b611..9ce3986 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -85,7 +85,7 @@ pub enum VcpuExit<'a> { /// Corresponds to KVM_EXIT_EPR. Epr, /// Corresponds to KVM_EXIT_SYSTEM_EVENT. - SystemEvent, + SystemEvent(u32 /* type */, u64 /* flags */), /// Corresponds to KVM_EXIT_S390_STSI. S390Stsi, /// Corresponds to KVM_EXIT_IOAPIC_EOI. @@ -1150,7 +1150,15 @@ impl VcpuFd { KVM_EXIT_WATCHDOG => Ok(VcpuExit::Watchdog), KVM_EXIT_S390_TSCH => Ok(VcpuExit::S390Tsch), KVM_EXIT_EPR => Ok(VcpuExit::Epr), - KVM_EXIT_SYSTEM_EVENT => Ok(VcpuExit::SystemEvent), + KVM_EXIT_SYSTEM_EVENT => { + // Safe because the exit_reason (which comes from the kernel) told us which + // union field to use. + let system_event = unsafe { &mut run.__bindgen_anon_1.system_event }; + Ok(VcpuExit::SystemEvent( + system_event.type_, + system_event.flags, + )) + } KVM_EXIT_S390_STSI => Ok(VcpuExit::S390Stsi), KVM_EXIT_IOAPIC_EOI => { // Safe because the exit_reason (which comes from the kernel) told us which @@ -1445,6 +1453,9 @@ mod tests { 0x1f, 0x18, 0x14, 0x71, /* cmp w0, #0x506 */ 0x20, 0x00, 0x82, 0x1a, /* csel w0, w1, w2, eq */ 0x20, 0x01, 0x00, 0xb9, /* str w0, [x9]; test MMIO write */ + 0x00, 0x80, 0xb0, 0x52, /* mov w0, #0x84000000 */ + 0x00, 0x00, 0x1d, 0x32, /* orr w0, w0, #0x08 */ + 0x02, 0x00, 0x00, 0xd4, /* hvc #0x0 */ 0x00, 0x00, 0x00, 0x14, /* b ; shouldn't get here, but if so loop forever */ ]; @@ -1473,6 +1484,7 @@ mod tests { let vcpu_fd = vm.create_vcpu(0).unwrap(); let mut kvi = kvm_bindings::kvm_vcpu_init::default(); vm.get_preferred_target(&mut kvi).unwrap(); + kvi.features[0] |= 1 << KVM_ARM_VCPU_PSCI_0_2; vcpu_fd.vcpu_init(&kvi).unwrap(); let core_reg_base: u64 = 0x6030_0000_0010_0000; @@ -1516,6 +1528,10 @@ mod tests { .map(|page| page.count_ones()) .fold(0, |dirty_page_count, i| dirty_page_count + i); assert_eq!(dirty_pages, 1); + } + VcpuExit::SystemEvent(type_, flags) => { + assert_eq!(type_, KVM_SYSTEM_EVENT_SHUTDOWN); + assert_eq!(flags, 0); break; } r => panic!("unexpected exit reason: {:?}", r), From 26e62f9d5ee784da1917b26654efa6d39d64e0a8 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Mon, 27 Apr 2020 15:06:21 +0300 Subject: [PATCH 107/332] Updated coverage value on aarch64 For some weird reason (maybe some update on the host??) the coverage percentage on arm needs to be decreased. Signed-off-by: Andreea Florescu --- coverage_config_aarch64.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coverage_config_aarch64.json b/coverage_config_aarch64.json index 64d79b5..4589e5a 100644 --- a/coverage_config_aarch64.json +++ b/coverage_config_aarch64.json @@ -1,5 +1,5 @@ { - "coverage_score": 77.2, + "coverage_score": 76.2, "exclude_path": "", "crate_features": "" } From 55b9481d8c03f33676593ad661bb9cb2272014e0 Mon Sep 17 00:00:00 2001 From: Henry Wang Date: Sat, 9 May 2020 16:09:21 +0800 Subject: [PATCH 108/332] Add support to get Host_IPA_Limit for AArch64 Host_IPA_Limit is the maximum possible value for IPA_Bits on the host and is dependent on the CPU capability and the kernel configuration. The limit can be retrieved using KVM_CAP_ARM_VM_IPA_SIZE of the KVM_CHECK_EXTENSION ioctl. This commit adds support for getting Host_IPA_Limit for AArch64. Signed-off-by: Henry Wang --- coverage_config_aarch64.json | 2 +- src/cap.rs | 1 + src/ioctls/system.rs | 22 ++++++++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/coverage_config_aarch64.json b/coverage_config_aarch64.json index 4589e5a..2a71ba3 100644 --- a/coverage_config_aarch64.json +++ b/coverage_config_aarch64.json @@ -1,5 +1,5 @@ { - "coverage_score": 76.2, + "coverage_score": 76.9, "exclude_path": "", "crate_features": "" } diff --git a/src/cap.rs b/src/cap.rs index 6d21225..b1456f3 100644 --- a/src/cap.rs +++ b/src/cap.rs @@ -140,4 +140,5 @@ pub enum Cap { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] SplitIrqchip = KVM_CAP_SPLIT_IRQCHIP, ImmediateExit = KVM_CAP_IMMEDIATE_EXIT, + ArmVmIPASize = KVM_CAP_ARM_VM_IPA_SIZE, } diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index a4de4f9..32698df 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -108,6 +108,14 @@ impl Kvm { unsafe { ioctl(self, KVM_GET_API_VERSION()) } } + /// AArch64 specific call to get the host Intermediate Physical Address space limit. + /// + /// Returns 0 if the capability is not available and an integer larger than 32 otherwise. + #[cfg(any(target_arch = "aarch64"))] + pub fn get_host_ipa_limit(&self) -> i32 { + self.check_extension_int(Cap::ArmVmIPASize) + } + /// Wrapper over `KVM_CHECK_EXTENSION`. /// /// Returns 0 if the capability is not available and a positive integer otherwise. @@ -412,6 +420,20 @@ mod tests { assert!(kvm.check_extension(Cap::UserMemory)); } + #[test] + #[cfg(any(target_arch = "aarch64"))] + fn test_get_host_ipa_limit() { + let kvm = Kvm::new().unwrap(); + let host_ipa_limit = kvm.get_host_ipa_limit(); + + if host_ipa_limit > 0 { + assert!(host_ipa_limit >= 32); + } else { + // if unsupported, the return value should be 0. + assert_eq!(host_ipa_limit, 0); + } + } + #[test] fn test_kvm_getters() { let kvm = Kvm::new().unwrap(); From ef7aba82cc13382909cf8d704d88cbc87caf61d2 Mon Sep 17 00:00:00 2001 From: "Jason Cai (Xiang Feng)" Date: Wed, 11 Mar 2020 16:55:56 +0800 Subject: [PATCH 109/332] Support `datamatch` when unregister ioevent Whe unregistering an ioevent, the `datamatch` field should be set correctly, otherwise it may unregister undesired ioevent. Signed-off-by: Jason Cai (Xiang Feng) Signed-off-by: Liu Jiang --- coverage_config_x86_64.json | 2 +- src/ioctls/vm.rs | 36 +++++++++++++++++++++++++++--------- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index eba81b3..d0bb900 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 91.3, + "coverage_score": 91.4, "exclude_path": "", "crate_features": "" } diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 702d997..599516b 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -636,23 +636,33 @@ impl VmFd { /// .register_ioevent(&evtfd, &pio_addr, NoDatamatch) /// .unwrap(); /// vm_fd - /// .register_ioevent(&evtfd, &mmio_addr, NoDatamatch) + /// .register_ioevent(&evtfd, &mmio_addr, 0x1234u32) /// .unwrap(); /// vm_fd - /// .unregister_ioevent(&evtfd, &pio_addr) + /// .unregister_ioevent(&evtfd, &pio_addr, NoDatamatch) /// .unwrap(); /// vm_fd - /// .unregister_ioevent(&evtfd, &mmio_addr) + /// .unregister_ioevent(&evtfd, &mmio_addr, 0x1234u32) /// .unwrap(); /// ``` /// - pub fn unregister_ioevent(&self, fd: &EventFd, addr: &IoEventAddress) -> Result<()> { + pub fn unregister_ioevent>( + &self, + fd: &EventFd, + addr: &IoEventAddress, + datamatch: T, + ) -> Result<()> { let mut flags = 1 << kvm_ioeventfd_flag_nr_deassign; + if std::mem::size_of::() > 0 { + flags |= 1 << kvm_ioeventfd_flag_nr_datamatch + } if let IoEventAddress::Pio(_) = *addr { flags |= 1 << kvm_ioeventfd_flag_nr_pio } let ioeventfd = kvm_ioeventfd { + datamatch: datamatch.into(), + len: std::mem::size_of::() as u32, addr: match addr { IoEventAddress::Pio(ref p) => *p as u64, IoEventAddress::Mmio(ref m) => *m, @@ -1440,21 +1450,29 @@ mod tests { let mmio_addr = IoEventAddress::Mmio(0x1000); // First try to unregister addresses which have not been registered. - assert!(vm_fd.unregister_ioevent(&evtfd, &pio_addr).is_err()); - assert!(vm_fd.unregister_ioevent(&evtfd, &mmio_addr).is_err()); + assert!(vm_fd + .unregister_ioevent(&evtfd, &pio_addr, NoDatamatch) + .is_err()); + assert!(vm_fd + .unregister_ioevent(&evtfd, &mmio_addr, NoDatamatch) + .is_err()); // Now register the addresses assert!(vm_fd .register_ioevent(&evtfd, &pio_addr, NoDatamatch) .is_ok()); assert!(vm_fd - .register_ioevent(&evtfd, &mmio_addr, NoDatamatch) + .register_ioevent(&evtfd, &mmio_addr, 0x1337u16) .is_ok()); // Try again unregistering the addresses. This time it should work // since they have been previously registered. - assert!(vm_fd.unregister_ioevent(&evtfd, &pio_addr).is_ok()); - assert!(vm_fd.unregister_ioevent(&evtfd, &mmio_addr).is_ok()); + assert!(vm_fd + .unregister_ioevent(&evtfd, &pio_addr, NoDatamatch) + .is_ok()); + assert!(vm_fd + .unregister_ioevent(&evtfd, &mmio_addr, 0x1337u16) + .is_ok()); } #[test] From d1f6bcde9650fe220fd07cfe3194e39a57c5ef14 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Tue, 2 Jun 2020 11:16:16 +0300 Subject: [PATCH 110/332] update rust-vmm-ci The following commits are included in the submodule update: 7e3f307 skip coverage-arm test cd7096e Enable rust-vmm coverage test in CI c309d06 buildkite: Move to the rustvmm/dev v4 container c85a8da buildkite: Remove clippy test on aarch64 Signed-off-by: Andreea Florescu --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index cd7096e..7e3f307 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit cd7096e7a6b649b76ebea80c341fc020a89356a1 +Subproject commit 7e3f307bf0280bc58bf6be09bdbee8ae3375b076 From 1072239e47c6b5dc6e143be8712ecde4ecc1347e Mon Sep 17 00:00:00 2001 From: niting Date: Sat, 23 May 2020 16:40:46 -0700 Subject: [PATCH 111/332] Update README to use Docker image used in CI. Signed-off-by: niting --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e010da9..862117f 100644 --- a/README.md +++ b/README.md @@ -38,10 +38,10 @@ docker run --device=/dev/kvm \ -it \ --security-opt seccomp=unconfined \ --volume $(pwd)/kvm-ioctls:/kvm-ioctls \ - rustvmm/dev:v2 + rustvmm/dev:v5 cd kvm-ioctls/ cargo test ``` For more details about the integration tests that are run for `kvm-ioctls`, -check the [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) readme. \ No newline at end of file +check the [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) readme. From 6eaa36c78e6fd829e13829553a20bfce7ef18dff Mon Sep 17 00:00:00 2001 From: Henry Wang Date: Wed, 27 May 2020 09:47:58 +0800 Subject: [PATCH 112/332] Add support to flexibly configure AArch64 IPA size of the guest As `KVM_CREATE_VM` ioctl supports configuration of AArch64 IPA size of the guest after kernel v4.20, this commit adds support to flexibly configure AArch64 IPA size of the guest in rust-vmm. Signed-off-by: Henry Wang --- coverage_config_aarch64.json | 2 +- src/ioctls/system.rs | 76 ++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/coverage_config_aarch64.json b/coverage_config_aarch64.json index 2a71ba3..27587e4 100644 --- a/coverage_config_aarch64.json +++ b/coverage_config_aarch64.json @@ -1,5 +1,5 @@ { - "coverage_score": 76.9, + "coverage_score": 77.1, "exclude_path": "", "crate_features": "" } diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index 32698df..56f01d5 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -12,6 +12,8 @@ use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use cap::Cap; use ioctls::vm::{new_vmfd, VmFd}; use ioctls::Result; +#[cfg(any(target_arch = "aarch64"))] +use kvm_bindings::KVM_VM_TYPE_ARM_IPA_SIZE_MASK; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use kvm_bindings::{CpuId, MsrList, KVM_MAX_MSR_ENTRIES}; use kvm_ioctls::*; @@ -374,6 +376,58 @@ impl Kvm { } } + /// AArch64 specific function to create a VM fd using the KVM fd with flexible IPA size. + /// + /// See the arm64 section of KVM documentation for `KVM_CREATE_VM`. + /// A call to this function will also initialize the size of the vcpu mmap area using the + /// `KVM_GET_VCPU_MMAP_SIZE` ioctl. + /// + /// Note: `Cap::ArmVmIPASize` should be checked using `check_extension` before calling + /// this function to determine if the host machine supports the IPA size capability. + /// + /// # Arguments + /// + /// * `ipa_size` - Guest VM IPA size, 32 <= ipa_size <= Host_IPA_Limit. + /// The value of `Host_IPA_Limit` may be different between hardware + /// implementations and can be extracted by calling `get_host_ipa_limit`. + /// Possible values can be found in documentation of registers `TCR_EL2` + /// and `VTCR_EL2`. + /// + /// # Example + /// + /// ``` + /// # use kvm_ioctls::{Kvm, Cap}; + /// let kvm = Kvm::new().unwrap(); + /// // Check if the ArmVmIPASize cap is supported. + /// if kvm.check_extension(Cap::ArmVmIPASize) { + /// let host_ipa_limit = kvm.get_host_ipa_limit(); + /// let vm = kvm.create_vm_with_ipa_size(host_ipa_limit as u32).unwrap(); + /// // Check that the VM mmap size is the same reported by `KVM_GET_VCPU_MMAP_SIZE`. + /// assert!(vm.run_size() == kvm.get_vcpu_mmap_size().unwrap()); + /// } + /// ``` + /// + #[cfg(any(target_arch = "aarch64"))] + pub fn create_vm_with_ipa_size(&self, ipa_size: u32) -> Result { + // Safe because we know `self.kvm` is a real KVM fd as this module is the only one that + // create Kvm objects. + let ret = unsafe { + ioctl_with_val( + &self.kvm, + KVM_CREATE_VM(), + (ipa_size & KVM_VM_TYPE_ARM_IPA_SIZE_MASK).into(), + ) + }; + if ret >= 0 { + // Safe because we verify the value of ret and we are the owners of the fd. + let vm_file = unsafe { File::from_raw_fd(ret) }; + let run_mmap_size = self.get_vcpu_mmap_size()?; + Ok(new_vmfd(vm_file, run_mmap_size)) + } else { + Err(errno::Error::last()) + } + } + /// Creates a VmFd object from a VM RawFd. /// /// This function is unsafe as the primitives currently returned have the contract that @@ -462,6 +516,28 @@ mod tests { assert_eq!(vm.run_size(), kvm.get_vcpu_mmap_size().unwrap()); } + #[test] + #[cfg(any(target_arch = "aarch64"))] + fn test_create_vm_with_ipa_size() { + let kvm = Kvm::new().unwrap(); + if kvm.check_extension(Cap::ArmVmIPASize) { + let host_ipa_limit = kvm.get_host_ipa_limit(); + // Here we test with the maximum value that the host supports to both test the + // discoverability of supported IPA sizes and likely some other values than 40. + kvm.create_vm_with_ipa_size(host_ipa_limit as u32).unwrap(); + // Test invalid input values + // Case 1: IPA size is smaller than 32. + assert!(kvm.create_vm_with_ipa_size(31).is_err()); + // Case 2: IPA size is bigger than Host_IPA_Limit. + assert!(kvm + .create_vm_with_ipa_size((host_ipa_limit + 1) as u32) + .is_err()); + } else { + // Unsupported, here we can test with the default value 40. + assert!(kvm.create_vm_with_ipa_size(40).is_err()); + } + } + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] #[test] fn test_get_supported_cpuid() { From 8ea124b0b5c6048a07453f0312bf38eb20d8efbf Mon Sep 17 00:00:00 2001 From: Henry Wang Date: Mon, 22 Jun 2020 17:58:46 +0800 Subject: [PATCH 113/332] Add support for `KVM_GET_DEVICE_ATTR` ioctl The `KVM_GET_DEVICE_ATTR` ioctl is useful when we need to extract the state/information of devices in the VM. In AArch64 VMs, using this ioctl is the only method to get the vGIC states. This commit implements the `KVM_GET_DEVICE_ATTR` ioctl with its unit test on AArch64. Fixes: https://github.com/rust-vmm/kvm-ioctls/issues/99 Signed-off-by: Henry Wang --- coverage_config_x86_64.json | 2 +- src/ioctls/device.rs | 106 ++++++++++++++++++++++++++++++++++-- src/kvm_ioctls.rs | 2 + 3 files changed, 105 insertions(+), 5 deletions(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index d0bb900..eba81b3 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 91.4, + "coverage_score": 91.3, "exclude_path": "", "crate_features": "" } diff --git a/src/ioctls/device.rs b/src/ioctls/device.rs index dde6d99..6b4f68c 100644 --- a/src/ioctls/device.rs +++ b/src/ioctls/device.rs @@ -6,9 +6,9 @@ use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use ioctls::Result; use kvm_bindings::kvm_device_attr; -use kvm_ioctls::{KVM_HAS_DEVICE_ATTR, KVM_SET_DEVICE_ATTR}; +use kvm_ioctls::{KVM_GET_DEVICE_ATTR, KVM_HAS_DEVICE_ATTR, KVM_SET_DEVICE_ATTR}; use vmm_sys_util::errno; -use vmm_sys_util::ioctl::ioctl_with_ref; +use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref}; /// Wrapper over the file descriptor obtained when creating an emulated device in the kernel. pub struct DeviceFd { @@ -80,6 +80,76 @@ impl DeviceFd { } Ok(()) } + + /// Gets a specified piece of device configuration and/or state. + /// + /// See the documentation for `KVM_GET_DEVICE_ATTR`. + /// + /// # Arguments + /// + /// * `device_attr` - The device attribute to be get. + /// Note: This argument serves as both input and output. + /// When calling this function, the user should explicitly provide + /// valid values for the `group` and the `attr` field of the + /// `kvm_device_attr` structure, and a valid userspace address + /// (i.e. the `addr` field) to access the returned device attribute + /// data. + /// + /// # Returns + /// + /// * Returns the last occured `errno` wrapped in an `Err`. + /// * `device_attr` - The `addr` field of the `device_attr` structure will point to + /// the device attribute data. + /// + /// # Examples + /// ```rust + /// # extern crate kvm_ioctls; + /// # extern crate kvm_bindings; + /// # use kvm_ioctls::Kvm; + /// + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// + /// // As on x86_64, `get_device_attr` is not necessarily needed. Therefore here + /// // the code example is only for AArch64. + /// #[cfg(any(target_arch = "aarch64"))] + /// { + /// use kvm_bindings::{ + /// KVM_DEV_ARM_VGIC_GRP_NR_IRQS, kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2, + /// kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, + /// }; + /// + /// // Create a GIC device. + /// let mut gic_device = kvm_bindings::kvm_create_device { + /// type_: kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, + /// fd: 0, + /// flags: 0, + /// }; + /// let device_fd = match vm.create_device(&mut gic_device) { + /// Ok(fd) => fd, + /// Err(_) => { + /// gic_device.type_ = kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2; + /// vm.create_device(&mut gic_device) + /// .expect("Cannot create KVM vGIC device") + /// } + /// }; + /// + /// let mut data: u32 = 0; + /// let mut gic_attr = kvm_bindings::kvm_device_attr::default(); + /// gic_attr.group = KVM_DEV_ARM_VGIC_GRP_NR_IRQS; + /// gic_attr.addr = &mut data as *const u32 as u64; + /// + /// device_fd.get_device_attr(&mut gic_attr).unwrap(); + /// } + /// ``` + /// + pub fn get_device_attr(&self, device_attr: &mut kvm_device_attr) -> Result<()> { + let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_DEVICE_ATTR(), device_attr) }; + if ret != 0 { + return Err(errno::Error::last()); + } + Ok(()) + } } /// Helper function for creating a new device. @@ -155,9 +225,12 @@ mod tests { flags: 0, }; + let mut dist_attr_mut = dist_attr; + // We are just creating a test device. Creating a real device would make the CI dependent // on host configuration (like having /dev/vfio). We expect this to fail. assert!(device_fd.has_device_attr(&dist_attr).is_err()); + assert!(device_fd.get_device_attr(&mut dist_attr_mut).is_err()); assert!(device_fd.set_device_attr(&dist_attr).is_err()); assert_eq!(errno::Error::last().errno(), 25); } @@ -165,8 +238,11 @@ mod tests { #[test] #[cfg(target_arch = "aarch64")] fn test_create_device() { - use ioctls::vm::create_gic_device; - use kvm_bindings::kvm_device_type_KVM_DEV_TYPE_FSL_MPIC_20; + use ioctls::vm::{create_gic_device, set_supported_nr_irqs}; + use kvm_bindings::{ + kvm_device_type_KVM_DEV_TYPE_FSL_MPIC_20, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, + }; + use vmm_sys_util::errno::Error; let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); @@ -197,6 +273,9 @@ mod tests { }; assert!(device_fd.has_device_attr(&dist_attr).is_err()); + // Set maximum supported number of IRQs of the vGIC device to 128. + set_supported_nr_irqs(&device_fd, 128); + // Following attribute works with VGIC, they should be accepted. let dist_attr = kvm_bindings::kvm_device_attr { group: KVM_DEV_ARM_VGIC_GRP_CTRL, @@ -207,5 +286,24 @@ mod tests { assert!(device_fd.has_device_attr(&dist_attr).is_ok()); assert!(device_fd.set_device_attr(&dist_attr).is_ok()); + + // Test `get_device_attr`. Here we try to extract the maximum supported number of IRQs. + // This value should be saved in the address provided to the ioctl. + let mut data: u32 = 0; + + let mut gic_attr = kvm_bindings::kvm_device_attr::default(); + gic_attr.group = KVM_DEV_ARM_VGIC_GRP_NR_IRQS; + gic_attr.addr = data as u64; + + // Without properly providing the address to where the + // value will be stored, the ioctl fails with EFAULT. + let res = device_fd.get_device_attr(&mut gic_attr); + assert_eq!(res, Err(Error::new(libc::EFAULT))); + + gic_attr.addr = &mut data as *const u32 as u64; + assert!(device_fd.get_device_attr(&mut gic_attr).is_ok()); + // The maximum supported number of IRQs should be 128, same as the value + // when we initialize the GIC. + assert_eq!(data, 128); } } diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index 35d1d83..cf47bce 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -219,6 +219,8 @@ ioctl_iowr_nr!(KVM_CREATE_DEVICE, KVMIO, 0xe0, kvm_create_device); /* Available with KVM_CAP_DEVICE_CTRL */ ioctl_iow_nr!(KVM_SET_DEVICE_ATTR, KVMIO, 0xe1, kvm_device_attr); /* Available with KVM_CAP_DEVICE_CTRL */ +ioctl_iow_nr!(KVM_GET_DEVICE_ATTR, KVMIO, 0xe2, kvm_device_attr); +/* Available with KVM_CAP_DEVICE_CTRL */ ioctl_iow_nr!(KVM_HAS_DEVICE_ATTR, KVMIO, 0xe3, kvm_device_attr); #[cfg(test)] From 53a2cec1bfefaf687e20d200fcec76a0a7149d6d Mon Sep 17 00:00:00 2001 From: Michael Zhao Date: Tue, 30 Jun 2020 10:28:28 +0800 Subject: [PATCH 114/332] Add capability definition for KVM_CAP_MSI_DEVID The per-VM KVM_CAP_MSI_DEVID capability advertises the requirement to provide the device ID in kvm_irq_routing_msi. Signed-off-by: Michael Zhao --- src/cap.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cap.rs b/src/cap.rs index b1456f3..6540659 100644 --- a/src/cap.rs +++ b/src/cap.rs @@ -141,4 +141,5 @@ pub enum Cap { SplitIrqchip = KVM_CAP_SPLIT_IRQCHIP, ImmediateExit = KVM_CAP_IMMEDIATE_EXIT, ArmVmIPASize = KVM_CAP_ARM_VM_IPA_SIZE, + MsiDevid = KVM_CAP_MSI_DEVID, } From d8f78a19e1e66c6c80ea682e1b2435b3e06118fb Mon Sep 17 00:00:00 2001 From: Michael Zhao Date: Mon, 6 Jul 2020 22:29:05 +0800 Subject: [PATCH 115/332] Implement 'check_extension()' for Vm 'check_extension()' was defined in Kvm to perform KVM_CHECK_EXTENSION ioctl only, but it is also possible in Vm. Signed-off-by: Michael Zhao --- coverage_config_x86_64.json | 2 +- src/ioctls/vm.rs | 42 +++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index eba81b3..d0bb900 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 91.3, + "coverage_score": 91.4, "exclude_path": "", "crate_features": "" } diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 599516b..25df0a0 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -11,6 +11,7 @@ use std::os::raw::c_void; use std::os::raw::{c_int, c_ulong}; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; +use cap::Cap; use ioctls::device::new_device; use ioctls::device::DeviceFd; use ioctls::vcpu::new_vcpu; @@ -1216,6 +1217,40 @@ impl VmFd { pub fn run_size(&self) -> usize { self.run_size } + + /// Wrapper over `KVM_CHECK_EXTENSION`. + /// + /// Returns 0 if the capability is not available and a positive integer otherwise. + fn check_extension_int(&self, c: Cap) -> i32 { + // Safe because we know that our file is a VM fd and that the extension is one of the ones + // defined by kernel. + unsafe { ioctl_with_val(self, KVM_CHECK_EXTENSION(), c as c_ulong) } + } + + /// Checks if a particular `Cap` is available. + /// + /// Returns true if the capability is supported and false otherwise. + /// See the documentation for `KVM_CHECK_EXTENSION`. + /// + /// # Arguments + /// + /// * `c` - VM capability to check. + /// + /// # Example + /// + /// ``` + /// # use kvm_ioctls::Kvm; + /// use kvm_ioctls::Cap; + /// + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// // Check if `KVM_CAP_MP_STATE` is supported. + /// assert!(vm.check_extension(Cap::MpState)); + /// ``` + /// + pub fn check_extension(&self, c: Cap) -> bool { + self.check_extension_int(c) > 0 + } } /// Helper function to create a new `VmFd`. @@ -1751,4 +1786,11 @@ mod tests { let irq_routing = kvm_irq_routing::default(); assert!(vm.set_gsi_routing(&irq_routing).is_ok()); } + + #[test] + fn test_check_extension() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + assert!(vm.check_extension(Cap::MpState)); + } } From bc85674ceddd7ca9b259605138d24fab3a033327 Mon Sep 17 00:00:00 2001 From: Diana Popa Date: Tue, 21 Jul 2020 17:26:21 +0300 Subject: [PATCH 116/332] Add support for `KVM_GET_REG_LIST` ioctl The `KVM_GET_REG_LIST` ioctl is used to extract a list containing the id of all registers on aarch64 architecture. We need this in order to be able to save/restore them. Fixes: https://github.com/rust-vmm/kvm-ioctls/issues/104 Signed-off-by: Diana Popa --- src/ioctls/vcpu.rs | 69 ++++++++++++++++++++++++++++++++++++++++++++++ src/kvm_ioctls.rs | 2 ++ 2 files changed, 71 insertions(+) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 9ce3986..bbda58c 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -959,6 +959,46 @@ impl VcpuFd { Ok(()) } + /// Returns the guest registers that are supported for the + /// KVM_GET_ONE_REG/KVM_SET_ONE_REG calls. + /// + /// # Arguments + /// + /// * `reg_list` - list of registers (input/output). For details check the `kvm_reg_list` + /// structure in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # extern crate kvm_bindings; + /// # use kvm_ioctls::Kvm; + /// # use kvm_bindings::RegList; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// + /// // KVM_GET_REG_LIST demands that the vcpus be initalized. + /// let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default(); + /// vm.get_preferred_target(&mut kvi).unwrap(); + /// vcpu.vcpu_init(&kvi).expect("Cannot initialize vcpu"); + /// + /// let mut reg_list = RegList::new(500); + /// vcpu.get_reg_list(&mut reg_list).unwrap(); + /// assert!(reg_list.as_fam_struct_ref().n > 0); + /// ``` + /// + #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + pub fn get_reg_list(&self, reg_list: &mut RegList) -> Result<()> { + let ret = + unsafe { ioctl_with_mut_ref(self, KVM_GET_REG_LIST(), reg_list.as_mut_fam_struct()) }; + if ret < 0 { + return Err(errno::Error::last()); + } + Ok(()) + } + /// Sets the value of one register for this vCPU. /// /// The id of the register is encoded as specified in the kernel documentation @@ -1838,6 +1878,35 @@ mod tests { ); } + #[test] + #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + fn test_get_reg_list() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let vcpu = vm.create_vcpu(0).unwrap(); + + let mut reg_list = RegList::new(1); + // KVM_GET_REG_LIST demands that the vcpus be initalized, so we expect this to fail. + let err = vcpu.get_reg_list(&mut reg_list).unwrap_err(); + assert!(err.errno() == libc::ENOEXEC); + + let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default(); + vm.get_preferred_target(&mut kvi) + .expect("Cannot get preferred target"); + vcpu.vcpu_init(&kvi).expect("Cannot initialize vcpu"); + + // KVM_GET_REG_LIST offers us a number of registers for which we have + // not allocated memory, so the first time it fails. + let err = vcpu.get_reg_list(&mut reg_list).unwrap_err(); + assert!(err.errno() == libc::E2BIG); + assert!(reg_list.as_mut_fam_struct().n > 0); + + // We make use of the number of registers returned to allocate memory and + // try one more time. + let mut reg_list = RegList::new(reg_list.as_mut_fam_struct().n as usize); + assert!(vcpu.get_reg_list(&mut reg_list).is_ok()); + } + #[test] fn set_kvm_immediate_exit() { let kvm = Kvm::new().unwrap(); diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index cf47bce..f8a25a9 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -211,6 +211,8 @@ ioctl_iow_nr!(KVM_SET_ONE_REG, KVMIO, 0xac, kvm_one_reg); ioctl_iow_nr!(KVM_ARM_VCPU_INIT, KVMIO, 0xae, kvm_vcpu_init); #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] ioctl_ior_nr!(KVM_ARM_PREFERRED_TARGET, KVMIO, 0xaf, kvm_vcpu_init); +#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] +ioctl_iowr_nr!(KVM_GET_REG_LIST, KVMIO, 0xb0, kvm_reg_list); // Device ioctls. From c346b170971cefdb386f948fe0ee9b799c6d32da Mon Sep 17 00:00:00 2001 From: Laura Loghin Date: Wed, 22 Jul 2020 18:16:51 +0300 Subject: [PATCH 117/332] fix clippy warnings A more recent clippy version requires the public unsafe functions to have a #Safety section in their documentation. Fixes: #101. Signed-off-by: Laura Loghin --- src/ioctls/system.rs | 42 +++++++++++++++++++++++++++++++++++++++++- src/ioctls/vm.rs | 10 +++++++++- src/lib.rs | 6 +++--- 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index 56f01d5..b940975 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -41,7 +41,7 @@ impl Kvm { pub fn new() -> Result { // Open `/dev/kvm` using `O_CLOEXEC` flag. let fd = Self::open_with_cloexec(true)?; - // Safe because we verify that ret is valid and we own the fd. + // Safe because we verify that the fd is valid in `open_with_cloexec` and we own the fd. Ok(unsafe { Self::new_with_fd_number(fd) }) } @@ -54,6 +54,24 @@ impl Kvm { /// /// * `fd` - File descriptor for `/dev/kvm`. /// + /// # Safety + /// + /// This function is unsafe as the primitives currently returned have the contract that + /// they are the sole owner of the file descriptor they are wrapping. Usage of this function + /// could accidentally allow violating this contract which can cause memory unsafety in code + /// that relies on it being true. + /// + /// The caller of this method must make sure the fd is valid and nothing else uses it. + /// + /// # Example + /// + /// ``` + /// # use kvm_ioctls::Kvm; + /// let kvm_fd = Kvm::open_with_cloexec(true).unwrap(); + /// // Safe because we verify that the fd is valid in `open_with_cloexec` and we own the fd. + /// let kvm = unsafe { Kvm::new_with_fd_number(kvm_fd) }; + /// ``` + /// pub unsafe fn new_with_fd_number(fd: RawFd) -> Self { Kvm { kvm: File::from_raw_fd(fd), @@ -430,10 +448,32 @@ impl Kvm { /// Creates a VmFd object from a VM RawFd. /// + /// # Arguments + /// + /// * `fd` - the RawFd used for creating the VmFd object. + /// + /// # Safety + /// /// This function is unsafe as the primitives currently returned have the contract that /// they are the sole owner of the file descriptor they are wrapping. Usage of this function /// could accidentally allow violating this contract which can cause memory unsafety in code /// that relies on it being true. + /// + /// The caller of this method must make sure the fd is valid and nothing else uses it. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use std::os::unix::io::AsRawFd; + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let rawfd = unsafe { libc::dup(vm.as_raw_fd()) }; + /// assert!(rawfd >= 0); + /// let vm = unsafe { kvm.create_vmfd_from_rawfd(rawfd).unwrap() }; + /// ``` + /// pub unsafe fn create_vmfd_from_rawfd(&self, fd: RawFd) -> Result { let run_mmap_size = self.get_vcpu_mmap_size()?; Ok(new_vmfd(File::from_raw_fd(fd), run_mmap_size)) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 25df0a0..1de0c00 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -1040,17 +1040,25 @@ impl VmFd { /// Creates a VcpuFd object from a vcpu RawFd. /// + /// # Arguments + /// + /// * `fd` - the RawFd used for creating the VcpuFd object. + /// + /// # Safety + /// /// This function is unsafe as the primitives currently returned have the contract that /// they are the sole owner of the file descriptor they are wrapping. Usage of this function /// could accidentally allow violating this contract which can cause memory unsafety in code /// that relies on it being true. /// + /// The caller of this method must make sure the fd is valid and nothing else uses it. + /// /// # Example /// /// ```rust /// # extern crate kvm_ioctls; /// # use std::os::unix::io::AsRawFd; - /// # use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + /// # use kvm_ioctls::Kvm; /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// // Create one vCPU with the ID=0. diff --git a/src/lib.rs b/src/lib.rs index 07c2842..a380e23 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -61,9 +61,9 @@ //! use kvm_bindings::KVM_MEM_LOG_DIRTY_PAGES; //! use kvm_bindings::kvm_userspace_memory_region; //! -//! let mem_size = 0x4000; -//! let guest_addr = 0x1000; -//! let asm_code: &[u8]; +//! let mem_size = 0x4000; +//! let guest_addr = 0x1000; +//! let asm_code: &[u8]; //! //! // Setting up architectural dependent values. //! #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] From cc0c726529408fc91b58308ec4a16405e123838c Mon Sep 17 00:00:00 2001 From: Wey Gu Date: Fri, 31 Jul 2020 20:25:03 +0800 Subject: [PATCH 118/332] Some more Unit Tests for set_cpuid2 To introduce more UTs for set_cpuid2 * Setting Manufacturer ID * Disabling Intel SHA extensions Issue: #28 Signed-off-by: Wey Gu --- src/ioctls/vcpu.rs | 73 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index bbda58c..20a5323 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1282,7 +1282,7 @@ mod tests { #[cfg(target_arch = "x86_64")] #[test] - fn cpuid2_test() { + fn test_get_cpuid() { let kvm = Kvm::new().unwrap(); if kvm.check_extension(Cap::ExtCpuid) { let vm = kvm.create_vm().unwrap(); @@ -1300,6 +1300,77 @@ mod tests { } } + #[cfg(target_arch = "x86_64")] + #[test] + fn test_set_cpuid() { + let kvm = Kvm::new().unwrap(); + if kvm.check_extension(Cap::ExtCpuid) { + let vm = kvm.create_vm().unwrap(); + let mut cpuid = kvm.get_supported_cpuid(KVM_MAX_CPUID_ENTRIES).unwrap(); + let ncpuids = cpuid.as_slice().len(); + assert!(ncpuids <= KVM_MAX_CPUID_ENTRIES); + let vcpu = vm.create_vcpu(0).unwrap(); + + // Setting Manufacturer ID + { + let entries = cpuid.as_mut_slice(); + for entry in entries.iter_mut() { + match entry.function { + 0 => { + // " KVMKVMKVM " + entry.ebx = 0x4b4d564b; + entry.ecx = 0x564b4d56; + entry.edx = 0x4d; + } + _ => (), + } + } + } + vcpu.set_cpuid2(&cpuid).unwrap(); + let cpuid_0 = vcpu.get_cpuid2(ncpuids).unwrap(); + for entry in cpuid_0.as_slice() { + match entry.function { + 0 => { + assert_eq!(entry.ebx, 0x4b4d564b); + assert_eq!(entry.ecx, 0x564b4d56); + assert_eq!(entry.edx, 0x4d); + } + _ => (), + } + } + + // Disabling Intel SHA extensions. + const EBX_SHA_SHIFT: u32 = 29; + let mut ebx_sha_off = 0u32; + { + let entries = cpuid.as_mut_slice(); + for entry in entries.iter_mut() { + match entry.function { + 7 => { + if entry.ecx == 0 { + entry.ebx &= !(1 << EBX_SHA_SHIFT); + ebx_sha_off = entry.ebx; + } + } + _ => (), + } + } + } + vcpu.set_cpuid2(&cpuid).unwrap(); + let cpuid_1 = vcpu.get_cpuid2(ncpuids).unwrap(); + for entry in cpuid_1.as_slice() { + match entry.function { + 7 => { + if entry.ecx == 0 { + assert_eq!(entry.ebx, ebx_sha_off); + } + } + _ => (), + } + } + } + } + #[cfg(target_arch = "x86_64")] #[allow(non_snake_case)] #[test] From 166541d8cab2eaf12790ec45a22202885de1811e Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Tue, 11 Aug 2020 12:00:59 +0200 Subject: [PATCH 119/332] updated rust-vmm-ci 0fc8ced refactor test_benchmark.py 741b894 checkout to PR branch before finishing test_bench 645a5c3 test_bench: don't crash when no bench on master bd32544 Fetch origin in benchmark test 35beb91 Fix commit message test 53427aa benchmarks: add test that can run at every PR abd2c90 Add test for commit message format fe859f4 Update container image to v6 75d7254 run cargo check on all features Signed-off-by: Andreea Florescu --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 7e3f307..0fc8ced 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 7e3f307bf0280bc58bf6be09bdbee8ae3375b076 +Subproject commit 0fc8cede37faca848fd450ffa746cbf8afc00bff From 113e6aa9e84be454699522d568ba707689da60c9 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Tue, 11 Aug 2020 14:24:20 +0200 Subject: [PATCH 120/332] fixes needed for switching to rust 1.41.1 * Updated coverage score. Coverage drops by 0.1% when updating to Rust 1.41.1. * Remove some unused parentheses. Signed-off-by: Andreea Florescu --- coverage_config_x86_64.json | 2 +- src/ioctls/vcpu.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index d0bb900..eba81b3 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 91.4, + "coverage_score": 91.3, "exclude_path": "", "crate_features": "" } diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 20a5323..5db1d38 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1936,7 +1936,7 @@ mod tests { const PSR_A_BIT: u64 = 0x0000_0100; const PSR_D_BIT: u64 = 0x0000_0200; const PSTATE_FAULT_BITS_64: u64 = - (PSR_MODE_EL1H | PSR_A_BIT | PSR_F_BIT | PSR_I_BIT | PSR_D_BIT); + PSR_MODE_EL1H | PSR_A_BIT | PSR_F_BIT | PSR_I_BIT | PSR_D_BIT; let data: u64 = PSTATE_FAULT_BITS_64; const PSTATE_REG_ID: u64 = 0x6030_0000_0010_0042; vcpu.set_one_reg(PSTATE_REG_ID, data) From 334c9a352b7fe5551e6446682c3719028b05790a Mon Sep 17 00:00:00 2001 From: Sebastien Boeuf Date: Tue, 16 Jun 2020 16:45:34 +0200 Subject: [PATCH 121/332] ioctls: vcpu: Add KVM_KVMCLOCK_CTRL support The KVM_KVMCLOCK_CTRL lets the guest know that it has been paused, which will prevent from detecting soft lockups due to pausing and resuming operations. Signed-off-by: Sebastien Boeuf --- coverage_config_x86_64.json | 2 +- src/ioctls/vcpu.rs | 20 ++++++++++++++++++++ src/kvm_ioctls.rs | 3 +++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index eba81b3..debc8fb 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 91.3, + "coverage_score": 91.2, "exclude_path": "", "crate_features": "" } diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 5db1d38..dd1146f 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1049,6 +1049,22 @@ impl VcpuFd { Ok(reg_value) } + /// Notify the guest about the vCPU being paused. + /// + /// See the documentation for `KVM_KVMCLOCK_CTRL` in the + /// [KVM API documentation](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn kvmclock_ctrl(&self) -> Result<()> { + // Safe because we know that our file is a KVM fd and that the request + // is one of the ones defined by kernel. + let ret = unsafe { ioctl(self, KVM_KVMCLOCK_CTRL()) }; + if ret != 0 { + return Err(errno::Error::last()); + } + Ok(()) + } + /// Triggers the running of the current virtual CPU returning an exit reason. /// /// See documentation for `KVM_RUN`. @@ -1877,6 +1893,10 @@ mod tests { badf_errno ); assert_eq!(faulty_vcpu_fd.run().unwrap_err().errno(), badf_errno); + assert_eq!( + faulty_vcpu_fd.kvmclock_ctrl().unwrap_err().errno(), + badf_errno + ); } #[test] diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index f8a25a9..21d16b9 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -190,6 +190,9 @@ ioctl_ior_nr!(KVM_GET_XCRS, KVMIO, 0xa6, kvm_xcrs); /* Available with KVM_CAP_XCRS */ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_iow_nr!(KVM_SET_XCRS, KVMIO, 0xa7, kvm_xcrs); +/* Available with KVM_CAP_KVMCLOCK_CTRL */ +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_io_nr!(KVM_KVMCLOCK_CTRL, KVMIO, 0xad); /* Available with KVM_CAP_ENABLE_CAP */ #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] From 782cdcbb9d51f7d1f798fe76551f2e9b4e4c5afe Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Tue, 15 Sep 2020 10:59:31 +0100 Subject: [PATCH 122/332] cap: Add HypervSynic and HypervSynic2 to Caps enum This enum is used for the kvm.check_extension() functionality. Signed-off-by: Rob Bradford --- src/cap.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cap.rs b/src/cap.rs index 6540659..bd0177b 100644 --- a/src/cap.rs +++ b/src/cap.rs @@ -142,4 +142,6 @@ pub enum Cap { ImmediateExit = KVM_CAP_IMMEDIATE_EXIT, ArmVmIPASize = KVM_CAP_ARM_VM_IPA_SIZE, MsiDevid = KVM_CAP_MSI_DEVID, + HypervSynic = KVM_CAP_HYPERV_SYNIC, + HypervSynic2 = KVM_CAP_HYPERV_SYNIC2, } From a6380052496c2db83fbbc0936ea66afc87b742a8 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Mon, 3 Aug 2020 15:29:40 +0100 Subject: [PATCH 123/332] ioctls: vcpu: Expose KVM_ENABLE_CAP for vCPU Mirror the availability of the KVM_ENABLE_CAP ioctl() for the VM file descriptor to the vCPU file descriptor. Signed-off-by: Rob Bradford --- coverage_config_x86_64.json | 2 +- src/ioctls/vcpu.rs | 65 +++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index debc8fb..8b79dcf 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 91.2, + "coverage_score": 91.1, "exclude_path": "", "crate_features": "" } diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index dd1146f..a121d42 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -387,6 +387,52 @@ impl VcpuFd { Ok(cpuid) } + /// + /// See the documentation for `KVM_ENABLE_CAP`. + /// + /// # Arguments + /// + /// * kvm_enable_cap - KVM capability structure. For details check the `kvm_enable_cap` + /// structure in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # extern crate kvm_bindings; + /// # use kvm_bindings::{kvm_enable_cap, KVM_MAX_CPUID_ENTRIES, KVM_CAP_HYPERV_SYNIC, KVM_CAP_SPLIT_IRQCHIP}; + /// # use kvm_ioctls::{Kvm, Cap}; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let mut cap: kvm_enable_cap = Default::default(); + /// if cfg!(target_arch = "x86") || cfg!(target_arch = "x86_64") { + /// // KVM_CAP_HYPERV_SYNIC needs KVM_CAP_SPLIT_IRQCHIP enabled + /// cap.cap = KVM_CAP_SPLIT_IRQCHIP; + /// cap.args[0] = 24; + /// vm.enable_cap(&cap).unwrap(); + /// + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// if kvm.check_extension(Cap::HypervSynic) { + /// let mut cap: kvm_enable_cap = Default::default(); + /// cap.cap = KVM_CAP_HYPERV_SYNIC; + /// vcpu.enable_cap(&cap).unwrap(); + /// } + /// } + /// ``` + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn enable_cap(&self, cap: &kvm_enable_cap) -> Result<()> { + // The ioctl is safe because we allocated the struct and we know the + // kernel will write exactly the size of the struct. + let ret = unsafe { ioctl_with_ref(self, KVM_ENABLE_CAP(), cap) }; + if ret == 0 { + Ok(()) + } else { + Err(errno::Error::last()) + } + } + /// Returns the state of the LAPIC (Local Advanced Programmable Interrupt Controller). /// /// The state is returned in a `kvm_lapic_state` structure as defined in the @@ -2007,4 +2053,23 @@ mod tests { vcpu.set_kvm_immediate_exit(1); assert_eq!(vcpu.kvm_run_ptr.as_mut_ref().immediate_exit, 1); } + + #[test] + #[cfg(target_arch = "x86_64")] + fn test_enable_cap() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let mut cap: kvm_enable_cap = Default::default(); + // KVM_CAP_HYPERV_SYNIC needs KVM_CAP_SPLIT_IRQCHIP enabled + cap.cap = KVM_CAP_SPLIT_IRQCHIP; + cap.args[0] = 24; + vm.enable_cap(&cap).unwrap(); + + let vcpu = vm.create_vcpu(0).unwrap(); + if kvm.check_extension(Cap::HypervSynic) { + let mut cap: kvm_enable_cap = Default::default(); + cap.cap = KVM_CAP_HYPERV_SYNIC; + vcpu.enable_cap(&cap).unwrap(); + } + } } From aed8f654fdd2933965fdf72e4b4f8f12e8129e72 Mon Sep 17 00:00:00 2001 From: Schuyler Mortimer Date: Thu, 1 Oct 2020 19:35:01 -0700 Subject: [PATCH 124/332] Remove Kvm::new_with_fd_number function * replace new_with_fd_number with from_raw_fd * move documentation to from_raw_fd * update doc tests Fixes: https://github.com/rust-vmm/kvm-ioctls/issues/115 Signed-off-by: Schuyler Mortimer --- src/ioctls/system.rs | 68 +++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index b940975..18d7d57 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -42,40 +42,7 @@ impl Kvm { // Open `/dev/kvm` using `O_CLOEXEC` flag. let fd = Self::open_with_cloexec(true)?; // Safe because we verify that the fd is valid in `open_with_cloexec` and we own the fd. - Ok(unsafe { Self::new_with_fd_number(fd) }) - } - - /// Creates a new Kvm object assuming `fd` represents an existing open file descriptor - /// associated with `/dev/kvm`. - /// - /// For usage examples check [open_with_cloexec()](struct.Kvm.html#method.open_with_cloexec). - /// - /// # Arguments - /// - /// * `fd` - File descriptor for `/dev/kvm`. - /// - /// # Safety - /// - /// This function is unsafe as the primitives currently returned have the contract that - /// they are the sole owner of the file descriptor they are wrapping. Usage of this function - /// could accidentally allow violating this contract which can cause memory unsafety in code - /// that relies on it being true. - /// - /// The caller of this method must make sure the fd is valid and nothing else uses it. - /// - /// # Example - /// - /// ``` - /// # use kvm_ioctls::Kvm; - /// let kvm_fd = Kvm::open_with_cloexec(true).unwrap(); - /// // Safe because we verify that the fd is valid in `open_with_cloexec` and we own the fd. - /// let kvm = unsafe { Kvm::new_with_fd_number(kvm_fd) }; - /// ``` - /// - pub unsafe fn new_with_fd_number(fd: RawFd) -> Self { - Kvm { - kvm: File::from_raw_fd(fd), - } + Ok(unsafe { Self::from_raw_fd(fd) }) } /// Opens `/dev/kvm` and returns the fd number on success. @@ -93,10 +60,11 @@ impl Kvm { /// /// ``` /// # use kvm_ioctls::Kvm; + /// # use std::os::unix::io::FromRawFd; /// let kvm_fd = Kvm::open_with_cloexec(false).unwrap(); /// // The `kvm_fd` can now be passed to another process where we can use - /// // `new_with_fd_number` for creating a `Kvm` object: - /// let kvm = unsafe { Kvm::new_with_fd_number(kvm_fd) }; + /// // `from_raw_fd` for creating a `Kvm` object: + /// let kvm = unsafe { Kvm::from_raw_fd(kvm_fd) }; /// ``` /// pub fn open_with_cloexec(close_on_exec: bool) -> Result { @@ -487,6 +455,34 @@ impl AsRawFd for Kvm { } impl FromRawFd for Kvm { + /// Creates a new Kvm object assuming `fd` represents an existing open file descriptor + /// associated with `/dev/kvm`. + /// + /// For usage examples check [open_with_cloexec()](struct.Kvm.html#method.open_with_cloexec). + /// + /// # Arguments + /// + /// * `fd` - File descriptor for `/dev/kvm`. + /// + /// # Safety + /// + /// This function is unsafe as the primitives currently returned have the contract that + /// they are the sole owner of the file descriptor they are wrapping. Usage of this function + /// could accidentally allow violating this contract which can cause memory unsafety in code + /// that relies on it being true. + /// + /// The caller of this method must make sure the fd is valid and nothing else uses it. + /// + /// # Example + /// + /// ``` + /// # use kvm_ioctls::Kvm; + /// # use std::os::unix::io::FromRawFd; + /// let kvm_fd = Kvm::open_with_cloexec(true).unwrap(); + /// // Safe because we verify that the fd is valid in `open_with_cloexec` and we own the fd. + /// let kvm = unsafe { Kvm::from_raw_fd(kvm_fd) }; + /// ``` + /// unsafe fn from_raw_fd(fd: RawFd) -> Self { Kvm { kvm: File::from_raw_fd(fd), From f87ee798e2d782329b02942e2f9b4d0b5c0eb3c7 Mon Sep 17 00:00:00 2001 From: Marius-Cristian Baciu Date: Fri, 9 Oct 2020 15:59:25 +0000 Subject: [PATCH 125/332] Add support for KVM_SET_GUEST_DEBUG ioctl call This enables us to handle software breakpoints, perform single-stepping and possibly, implement hardware breakpoints Signed-off-by: Marius-Cristian Baciu --- coverage_config_x86_64.json | 2 +- src/ioctls/vcpu.rs | 78 +++++++++++++++++++++++++++++++++++++ src/kvm_ioctls.rs | 3 ++ 3 files changed, 82 insertions(+), 1 deletion(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index 8b79dcf..debc8fb 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 91.1, + "coverage_score": 91.2, "exclude_path": "", "crate_features": "" } diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index a121d42..b364f52 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1045,6 +1045,59 @@ impl VcpuFd { Ok(()) } + /// Sets processor-specific debug registers and configures the vcpu for handling + /// certain guest debug events using the `KVM_SET_GUEST_DEBUG` ioctl. + /// + /// # Arguments + /// + /// * `debug_struct` - control bitfields and debug registers, depending on the specific architecture. + /// For details check the `kvm_guest_debug` structure in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # extern crate kvm_bindings; + /// # use kvm_ioctls::Kvm; + /// # use kvm_bindings::{ + /// # KVM_GUESTDBG_ENABLE, KVM_GUESTDBG_USE_SW_BP, kvm_guest_debug_arch, kvm_guest_debug + /// # }; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { + /// let debug_struct = kvm_guest_debug { + /// // Configure the vcpu so that a KVM_DEBUG_EXIT would be generated + /// // when encountering a software breakpoint during execution + /// control: KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP, + /// pad: 0, + /// // Reset all x86-specific debug registers + /// arch: kvm_guest_debug_arch { + /// debugreg: [0, 0, 0, 0, 0, 0, 0, 0], + /// }, + /// }; + /// + /// vcpu.set_guest_debug(&debug_struct).unwrap(); + /// } + /// ``` + /// + #[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm64", + target_arch = "s390", + target_arch = "ppc" + ))] + pub fn set_guest_debug(&self, debug_struct: &kvm_guest_debug) -> Result<()> { + let ret = unsafe { ioctl_with_ref(self, KVM_SET_GUEST_DEBUG(), debug_struct) }; + if ret < 0 { + return Err(errno::Error::last()); + } + Ok(()) + } + /// Sets the value of one register for this vCPU. /// /// The id of the register is encoded as specified in the kernel documentation @@ -1732,6 +1785,7 @@ mod tests { 0xc6, 0x06, 0x00, 0x20, 0x00, /* movl $0, (0x2000); Dirty one page in guest mem. */ 0xf4, /* hlt */ ]; + let expected_rips: [u64; 3] = [0x1003, 0x1005, 0x1007]; let mem_size = 0x4000; let load_addr = mmap_anonymous(mem_size); @@ -1772,6 +1826,16 @@ mod tests { vcpu_regs.rflags = 2; vcpu_fd.set_regs(&vcpu_regs).unwrap(); + let mut debug_struct = kvm_guest_debug { + control: KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP, + pad: 0, + arch: kvm_guest_debug_arch { + debugreg: [0, 0, 0, 0, 0, 0, 0, 0], + }, + }; + vcpu_fd.set_guest_debug(&debug_struct).unwrap(); + + let mut instr_idx = 0; loop { match vcpu_fd.run().expect("run failed") { VcpuExit::IoIn(addr, data) => { @@ -1792,6 +1856,20 @@ mod tests { assert_eq!(data.len(), 1); assert_eq!(data[0], 0); } + VcpuExit::Debug => { + if instr_idx == expected_rips.len() - 1 { + // Disabling debugging/single-stepping + debug_struct.control = 0; + vcpu_fd.set_guest_debug(&debug_struct).unwrap(); + } else { + if instr_idx >= expected_rips.len() { + assert!(false); + } + } + let vcpu_regs = vcpu_fd.get_regs().unwrap(); + assert_eq!(vcpu_regs.rip, expected_rips[instr_idx]); + instr_idx += 1; + } VcpuExit::Hlt => { // The code snippet dirties 2 pages: // * one when the code itself is loaded in memory; diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index 21d16b9..08c7d41 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -217,6 +217,9 @@ ioctl_ior_nr!(KVM_ARM_PREFERRED_TARGET, KVMIO, 0xaf, kvm_vcpu_init); #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] ioctl_iowr_nr!(KVM_GET_REG_LIST, KVMIO, 0xb0, kvm_reg_list); +/* Available with KVM_CAP_SET_GUEST_DEBUG */ +ioctl_iow_nr!(KVM_SET_GUEST_DEBUG, KVMIO, 0x9b, kvm_guest_debug); + // Device ioctls. /* Available with KVM_CAP_DEVICE_CTRL */ From 771afdea00c009c5f71ddc6539359172ed3fe2d7 Mon Sep 17 00:00:00 2001 From: pierwill Date: Tue, 24 Nov 2020 19:47:13 -0800 Subject: [PATCH 126/332] Fix typo in crate documentation Signed-off-by: pierwill --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index a380e23..10c3cba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,7 +26,7 @@ //! In this example we are creating a Virtual Machine (VM) with one vCPU. //! On the vCPU we are running machine specific code. This example is based on //! the [LWN article](https://lwn.net/Articles/658511/) on using the KVM API. -//! The aarch64 example was modfied accordingly. +//! The aarch64 example was modified accordingly. //! //! To get code running on the vCPU we are going through the following steps: //! From b7d6af019ab8c5622e8bf890b7fb4a90924b8283 Mon Sep 17 00:00:00 2001 From: pierwill Date: Tue, 24 Nov 2020 11:28:14 -0800 Subject: [PATCH 127/332] Add Safety heading to from_raw_fd docs Signed-off-by: pierwill --- src/ioctls/device.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ioctls/device.rs b/src/ioctls/device.rs index 6b4f68c..7d64bb9 100644 --- a/src/ioctls/device.rs +++ b/src/ioctls/device.rs @@ -164,7 +164,9 @@ impl AsRawFd for DeviceFd { } impl FromRawFd for DeviceFd { - /// This function is also unsafe as the primitives currently returned have the contract that + /// # Safety + /// + /// This function is unsafe as the primitives currently returned have the contract that /// they are the sole owner of the file descriptor they are wrapping. Usage of this function /// could accidentally allow violating this contract which can cause memory unsafety in code /// that relies on it being true. From 1cf71b8e60c77dc537ead6b133745a3a4ed56b95 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Thu, 3 Dec 2020 16:29:51 +0100 Subject: [PATCH 128/332] update rust-vmm-ci 3b7377c (HEAD) fixed typo in readme c9430ee add gitignore file e1108f1 buildkite: re-enable cargo audit test 02004b5 Add a flag that saves the coverage output dir 97025bd buildkite: Skip lines should be shorter than 70 chars c83003c buildkite: Skip cargo audit check temporarily c8cf2b7 buildkite: Fix audit label indentation b3acb30 Fixes: https://github.com/rust-vmm/rust-vmm-ci/issues/8 bedc32b Add --workspace flag to cargo check too 3ea5f2b improve a bit error messages for commit test cd90a63 Add support for workspace tests 9dd386c readme update: cosmetic changes 265df53 Coverage test: keep stdin open 2d3bb05 add myself to codeowners e58ea74 Fix kcov_ouput_dir typo in test_coverage.py d62d781 fix buildkite typos in readme Signed-off-by: Andreea Florescu --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 0fc8ced..3b7377c 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 0fc8cede37faca848fd450ffa746cbf8afc00bff +Subproject commit 3b7377cb51b963c96cf6aba759d575b9ab9e8cc2 From 2ac3adc8606c3e96d83935a707943014d8080e5b Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Thu, 3 Dec 2020 17:04:29 +0100 Subject: [PATCH 129/332] release v0.6.0 Signed-off-by: Andreea Florescu --- CHANGELOG.md | 27 +++++++++++++++++++++++++++ Cargo.toml | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 730d3dc..f6044e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,30 @@ +# v0.6.0 + +## Added +- Support for the vcpu ioctls: `KVM_SET_GUEST_DEBUG`, `KVM_KVMCLOCK_CTRL`, and + `KVM_GET_REG_LIST`. +- Support for the vm ioctl `KVM_GET_DEVICE_ATTR`. +- Support for the device ioctl `KVM_HAS_DEVICE_ATTR`. +- Support for `VcpuExit::Debug`. +- Support for enabling vcpu capabilities using `Vcpu::enable_cap`. +- Support for checking Hyper-V (`HypervSynic` and `HypervSynic2`), MSI + (`MsiDevid`), and IPA Size (`ArmVmIPASize`) capabilities. + using `kvm.check_extension`. +- Support for checking the VM capabilities via `Vm::check_extension`. +- Create a VM with flexible IPA size using `Kvm::create_vm_with_ipa_size`. + +## Removed +- Removed `Kvm::new_with_fd_number`. The same functionality is offered by the + `Kvm` [FromRawFd](https://doc.rust-lang.org/std/os/unix/io/trait.FromRawFd.html) + trait implementation. + +## Changed +- The VM ioctl `unregister_ioevent` now correctly unregisters the events that + correspond to the data match passed as a parameter. +- The `SystemEvent` Vcpu Exit now also contains the relevant type and flags. +- Updated `get_dirty_log` such that it does not assume the page size is 4K, + but instead reads it using `libc::sysconf`. + # v0.5.0 ## Added diff --git a/Cargo.toml b/Cargo.toml index f010446..0b6bb41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kvm-ioctls" -version = "0.5.0" +version = "0.6.0" authors = ["Amazon Firecracker Team "] description = "Safe wrappers over KVM ioctls" repository = "https://github.com/rust-vmm/kvm-ioctls" From 6e965fb99589066a4aed0edc9d75aece9de065c9 Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Thu, 10 Dec 2020 13:37:01 +0000 Subject: [PATCH 130/332] Fix broken tests on aarch64. #89 Signed-off-by: Alex Williams --- src/ioctls/device.rs | 4 ++++ src/ioctls/vm.rs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/ioctls/device.rs b/src/ioctls/device.rs index 7d64bb9..4f25727 100644 --- a/src/ioctls/device.rs +++ b/src/ioctls/device.rs @@ -260,6 +260,10 @@ mod tests { let device_fd = create_gic_device(&vm, 0); + // GICv3 on arm/aarch64 requires an online vCPU prior to setting device attributes, + // see: https://www.kernel.org/doc/html/latest/virt/kvm/devices/arm-vgic-v3.html + vm.create_vcpu(0).unwrap(); + // Following lines to re-construct device_fd are used to test // DeviceFd::from_raw_fd() and DeviceFd::as_raw_fd(). let raw_fd = unsafe { libc::dup(device_fd.as_raw_fd()) }; diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 1de0c00..8d3c751 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -1556,6 +1556,10 @@ mod tests { // Create the vGIC device. let vgic_fd = create_gic_device(&vm_fd, 0); + // GICv3 on arm/aarch64 requires an online vCPU prior to setting device attributes, + // see: https://www.kernel.org/doc/html/latest/virt/kvm/devices/arm-vgic-v3.html + vm_fd.create_vcpu(0).unwrap(); + // Set supported number of IRQs. set_supported_nr_irqs(&vgic_fd, 128); // Request the initialization of the vGIC. From 366d4f9ed32078621f1d85f9836d7c196c3d3f41 Mon Sep 17 00:00:00 2001 From: Wainer dos Santos Moschetta Date: Thu, 31 Dec 2020 16:50:06 -0300 Subject: [PATCH 131/332] system: Add test_open_with_cloexec test Added tests for Kvm::open_with_cloexec(). Signed-off-by: Wainer dos Santos Moschetta --- src/ioctls/system.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index 18d7d57..f0fb7c3 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -495,6 +495,7 @@ mod tests { use super::*; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use kvm_bindings::KVM_MAX_CPUID_ENTRIES; + use libc::{fcntl, FD_CLOEXEC, F_GETFD}; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use vmm_sys_util::fam::FamStruct; @@ -503,6 +504,16 @@ mod tests { Kvm::new().unwrap(); } + #[test] + fn test_open_with_cloexec() { + let fd = Kvm::open_with_cloexec(false).unwrap(); + let flags = unsafe { fcntl(fd, F_GETFD, 0) }; + assert_eq!(flags & FD_CLOEXEC, 0); + let fd = Kvm::open_with_cloexec(true).unwrap(); + let flags = unsafe { fcntl(fd, F_GETFD, 0) }; + assert_eq!(flags & FD_CLOEXEC, FD_CLOEXEC); + } + #[test] fn test_kvm_api_version() { let kvm = Kvm::new().unwrap(); From 87b9c439571a32af8564e0c2982f217ccf39466f Mon Sep 17 00:00:00 2001 From: Abhijit Gadgil Date: Tue, 5 Jan 2021 16:08:56 +0530 Subject: [PATCH 132/332] Fix Check For num_entries for CpuId #119 Added a check to verify that the number of entires passed is less than or equal to `KVM_MAX_CPUID_ENTRIES` (`Kvm.get_cpuid`, `VcpuFd.get_cpuid2`). Added test cases for failure scenarios for `get_emulated_cpuid` and `get_supported_cpuid`. Renamed param `max_entries_count` to `num_entries` consistent with `VcpuFd.get_cpuid2` Signed-off-by: Abhijit Gadgil --- src/ioctls/system.rs | 33 +++++++++++++++++++++++---------- src/ioctls/vcpu.rs | 19 ++++++++++++++++++- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index f0fb7c3..5fe3d97 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -15,7 +15,7 @@ use ioctls::Result; #[cfg(any(target_arch = "aarch64"))] use kvm_bindings::KVM_VM_TYPE_ARM_IPA_SIZE_MASK; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -use kvm_bindings::{CpuId, MsrList, KVM_MAX_MSR_ENTRIES}; +use kvm_bindings::{CpuId, MsrList, KVM_MAX_CPUID_ENTRIES, KVM_MAX_MSR_ENTRIES}; use kvm_ioctls::*; use vmm_sys_util::errno; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] @@ -228,13 +228,18 @@ impl Kvm { } #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn get_cpuid(&self, kind: u64, max_entries_count: usize) -> Result { - let mut cpuid = CpuId::new(max_entries_count); + fn get_cpuid(&self, kind: u64, num_entries: usize) -> Result { + if num_entries > KVM_MAX_CPUID_ENTRIES { + // Returns the same error the underlying `ioctl` would have sent. + return Err(errno::Error::new(libc::ENOMEM)); + } + + let mut cpuid = CpuId::new(num_entries); let ret = unsafe { // ioctl is unsafe. The kernel is trusted not to write beyond the bounds of the memory // allocated for the struct. The limit is read from nent, which is set to the allocated - // size(max_entries_count) above. + // size(num_entries) above. ioctl_with_mut_ptr(self, kind, cpuid.as_mut_fam_struct_ptr()) }; if ret < 0 { @@ -250,7 +255,7 @@ impl Kvm { /// /// # Arguments /// - /// * `max_entries_count` - Maximum number of CPUID entries. This function can return less than + /// * `num_entries` - Maximum number of CPUID entries. This function can return less than /// this when the hardware does not support so many CPUID entries. /// /// # Example @@ -267,8 +272,8 @@ impl Kvm { /// ``` /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - pub fn get_emulated_cpuid(&self, max_entries_count: usize) -> Result { - self.get_cpuid(KVM_GET_EMULATED_CPUID(), max_entries_count) + pub fn get_emulated_cpuid(&self, num_entries: usize) -> Result { + self.get_cpuid(KVM_GET_EMULATED_CPUID(), num_entries) } /// X86 specific call to get the system supported CPUID values. @@ -277,7 +282,7 @@ impl Kvm { /// /// # Arguments /// - /// * `max_entries_count` - Maximum number of CPUID entries. This function can return less than + /// * `num_entries` - Maximum number of CPUID entries. This function can return less than /// this when the hardware does not support so many CPUID entries. /// /// # Example @@ -294,8 +299,8 @@ impl Kvm { /// ``` /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - pub fn get_supported_cpuid(&self, max_entries_count: usize) -> Result { - self.get_cpuid(KVM_GET_SUPPORTED_CPUID(), max_entries_count) + pub fn get_supported_cpuid(&self, num_entries: usize) -> Result { + self.get_cpuid(KVM_GET_SUPPORTED_CPUID(), num_entries) } /// X86 specific call to get list of supported MSRS @@ -593,6 +598,10 @@ mod tests { let cpuid_entries = cpuid.as_mut_slice(); assert!(!cpuid_entries.is_empty()); assert!(cpuid_entries.len() <= KVM_MAX_CPUID_ENTRIES); + + // Test case for more than MAX entries + let cpuid_err = kvm.get_emulated_cpuid(KVM_MAX_CPUID_ENTRIES + 1 as usize); + assert!(cpuid_err.is_err()); } #[test] @@ -603,6 +612,10 @@ mod tests { let cpuid_entries = cpuid.as_mut_slice(); assert!(!cpuid_entries.is_empty()); assert!(cpuid_entries.len() <= KVM_MAX_CPUID_ENTRIES); + + // Test case for more than MAX entries + let cpuid_err = kvm.get_emulated_cpuid(KVM_MAX_CPUID_ENTRIES + 1 as usize); + assert!(cpuid_err.is_err()); } #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index b364f52..6517735 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -12,7 +12,7 @@ use std::os::unix::io::{AsRawFd, RawFd}; use ioctls::{KvmRunWrapper, Result}; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -use kvm_bindings::{CpuId, Msrs}; +use kvm_bindings::{CpuId, Msrs, KVM_MAX_CPUID_ENTRIES}; use kvm_ioctls::*; use vmm_sys_util::errno; use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ref}; @@ -376,6 +376,11 @@ impl VcpuFd { /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_cpuid2(&self, num_entries: usize) -> Result { + if num_entries > KVM_MAX_CPUID_ENTRIES { + // Returns the same error the underlying `ioctl` would have sent. + return Err(errno::Error::new(libc::ENOMEM)); + } + let mut cpuid = CpuId::new(num_entries); let ret = unsafe { // Here we trust the kernel not to read past the end of the kvm_cpuid2 struct. @@ -1415,6 +1420,18 @@ mod tests { } } + #[cfg(target_arch = "x86_64")] + #[test] + fn test_get_cpuid_fail_num_entries_too_high() { + let kvm = Kvm::new().unwrap(); + if kvm.check_extension(Cap::ExtCpuid) { + let vm = kvm.create_vm().unwrap(); + let vcpu = vm.create_vcpu(0).unwrap(); + let err_cpuid = vcpu.get_cpuid2(KVM_MAX_CPUID_ENTRIES + 1 as usize); + assert!(err_cpuid.is_err()); + } + } + #[cfg(target_arch = "x86_64")] #[test] fn test_set_cpuid() { From 8c7b41749f8f010213f23f0220fdf0b2fd5f01e0 Mon Sep 17 00:00:00 2001 From: Abhijit Gadgil Date: Tue, 5 Jan 2021 19:12:54 +0530 Subject: [PATCH 133/332] documentation: Added returned Error value #119 Changed the documentation of `get_{supported|emulated}_cpuid` to describe returned error. Also, fixed a copy-paster error in he documentation for `get_supported_cpuid` (Example was calling `get_emulated_cpuid`) Signed-off-by: Abhijit Gadgil --- src/ioctls/system.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index 5fe3d97..13ce764 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -258,6 +258,9 @@ impl Kvm { /// * `num_entries` - Maximum number of CPUID entries. This function can return less than /// this when the hardware does not support so many CPUID entries. /// + /// Returns Error `errno::Error(libc::ENOMEM)` when the input `num_entries` is greater than + /// `KVM_MAX_CPUID_ENTRIES`. + /// /// # Example /// /// ``` @@ -285,6 +288,9 @@ impl Kvm { /// * `num_entries` - Maximum number of CPUID entries. This function can return less than /// this when the hardware does not support so many CPUID entries. /// + /// Returns Error `errno::Error(libc::ENOMEM)` when the input `num_entries` is greater than + /// `KVM_MAX_CPUID_ENTRIES`. + /// /// # Example /// /// ``` @@ -293,7 +299,7 @@ impl Kvm { /// use kvm_ioctls::Kvm; /// /// let kvm = Kvm::new().unwrap(); - /// let mut cpuid = kvm.get_emulated_cpuid(KVM_MAX_CPUID_ENTRIES).unwrap(); + /// let mut cpuid = kvm.get_supported_cpuid(KVM_MAX_CPUID_ENTRIES).unwrap(); /// let cpuid_entries = cpuid.as_mut_slice(); /// assert!(cpuid_entries.len() <= KVM_MAX_CPUID_ENTRIES); /// ``` From 6aa32c7c96f7fbc19964c8a93842f05010952168 Mon Sep 17 00:00:00 2001 From: Abhijit Gadgil Date: Tue, 5 Jan 2021 21:03:58 +0530 Subject: [PATCH 134/332] vcpu: Test Case for num_entries too small CpuId Kernel returns an error `E2BIG` if the number of entries passed to `get_cpuid2` is too small. Added a test case for that assertion. Also, added assertion to check `errno` to `ENOMEM`, when passed number of entries is greater than `KVM_MAX_CPUID_ENTRIES` (previously the assertion was there only for `is_err`). Signed-off-by: Abhijit Gadgil --- src/ioctls/vcpu.rs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 6517735..a15a3f8 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1427,8 +1427,27 @@ mod tests { if kvm.check_extension(Cap::ExtCpuid) { let vm = kvm.create_vm().unwrap(); let vcpu = vm.create_vcpu(0).unwrap(); - let err_cpuid = vcpu.get_cpuid2(KVM_MAX_CPUID_ENTRIES + 1 as usize); - assert!(err_cpuid.is_err()); + let err_cpuid = vcpu.get_cpuid2(KVM_MAX_CPUID_ENTRIES + 1 as usize).err(); + assert_eq!(err_cpuid.unwrap().errno(), libc::ENOMEM); + } + } + + #[cfg(target_arch = "x86_64")] + #[test] + fn test_get_cpuid_fail_num_entries_too_small() { + let kvm = Kvm::new().unwrap(); + if kvm.check_extension(Cap::ExtCpuid) { + let vm = kvm.create_vm().unwrap(); + let cpuid = kvm.get_supported_cpuid(KVM_MAX_CPUID_ENTRIES).unwrap(); + let ncpuids = cpuid.as_slice().len(); + assert!(ncpuids <= KVM_MAX_CPUID_ENTRIES); + let nr_vcpus = kvm.get_nr_vcpus(); + for cpu_idx in 0..nr_vcpus { + let vcpu = vm.create_vcpu(cpu_idx as u8).unwrap(); + vcpu.set_cpuid2(&cpuid).unwrap(); + let err = vcpu.get_cpuid2(ncpuids - 1 as usize).err(); + assert_eq!(err.unwrap().errno(), libc::E2BIG); + } } } From 4f61bba6236afff7beaece632e921a5428ff680d Mon Sep 17 00:00:00 2001 From: Abhijit Gadgil Date: Thu, 7 Jan 2021 17:53:37 +0530 Subject: [PATCH 135/332] vm: `create_vcpu` takes `u64` argument #123 Changed the passed parameter type from `u8` to `u64`, which the underlying `ioctl` uses. Added a missing cap `MaxVcpuId` (It is defined in kvm-bindings for both arm* and x86* but was missing inside cap.rs. This was required by a test case.) Added a test case to assert failure if we pass parameter higher than the `max_vcpu_id`. Signed-off-by: Abhijit Gadgil --- src/cap.rs | 1 + src/ioctls/vcpu.rs | 4 ++-- src/ioctls/vm.rs | 34 +++++++++++++++++++++++++++++++++- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/cap.rs b/src/cap.rs index bd0177b..4071425 100644 --- a/src/cap.rs +++ b/src/cap.rs @@ -97,6 +97,7 @@ pub enum Cap { PpcSmt = KVM_CAP_PPC_SMT, PpcRma = KVM_CAP_PPC_RMA, MaxVcpus = KVM_CAP_MAX_VCPUS, + MaxVcpuId = KVM_CAP_MAX_VCPU_ID, PpcHior = KVM_CAP_PPC_HIOR, PpcPapr = KVM_CAP_PPC_PAPR, SwTlb = KVM_CAP_SW_TLB, diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index a15a3f8..f3fd009 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1411,7 +1411,7 @@ mod tests { assert!(ncpuids <= KVM_MAX_CPUID_ENTRIES); let nr_vcpus = kvm.get_nr_vcpus(); for cpu_idx in 0..nr_vcpus { - let vcpu = vm.create_vcpu(cpu_idx as u8).unwrap(); + let vcpu = vm.create_vcpu(cpu_idx as u64).unwrap(); vcpu.set_cpuid2(&cpuid).unwrap(); let retrieved_cpuid = vcpu.get_cpuid2(ncpuids).unwrap(); // Only check the first few leafs as some (e.g. 13) are reserved. @@ -1443,7 +1443,7 @@ mod tests { assert!(ncpuids <= KVM_MAX_CPUID_ENTRIES); let nr_vcpus = kvm.get_nr_vcpus(); for cpu_idx in 0..nr_vcpus { - let vcpu = vm.create_vcpu(cpu_idx as u8).unwrap(); + let vcpu = vm.create_vcpu(cpu_idx as u64).unwrap(); vcpu.set_cpuid2(&cpuid).unwrap(); let err = vcpu.get_cpuid2(ncpuids - 1 as usize).err(); assert_eq!(err.unwrap().errno(), libc::E2BIG); diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 8d3c751..0689f17 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -1021,7 +1021,7 @@ impl VmFd { /// let vcpu = vm.create_vcpu(0); /// ``` /// - pub fn create_vcpu(&self, id: u8) -> Result { + pub fn create_vcpu(&self, id: u64) -> Result { // Safe because we know that vm is a VM fd and we verify the return result. #[allow(clippy::cast_lossless)] let vcpu_fd = unsafe { ioctl_with_val(&self.vm, KVM_CREATE_VCPU(), id as c_ulong) }; @@ -1799,6 +1799,38 @@ mod tests { assert!(vm.set_gsi_routing(&irq_routing).is_ok()); } + #[test] + fn create_vcpu_different_cpuids() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + + // Fails when an arbitrarily large value + let err = vm.create_vcpu(65537 as u64).err(); + assert_eq!(err.unwrap().errno(), libc::EINVAL); + + // Note: We can request up to KVM_MAX_VCPU_ID if it exists or up to KVM_MAX_VCPUS or + // NR_CPUS or 4. This is determined by the appropriate capability being present. + // We check near boundry conditions `max_vcpus - 1` should succeed but `max_vcpus` as + // determined by the appropriate capability should fail. + // + // Ref: https://www.kernel.org/doc/html/latest/virt/kvm/api.html#kvm-create-vcpu + // + let mut max_vcpus = vm.check_extension_int(Cap::MaxVcpuId); + if max_vcpus == 0 { + max_vcpus = vm.check_extension_int(Cap::MaxVcpus); + } + if max_vcpus == 0 { + max_vcpus = vm.check_extension_int(Cap::NrVcpus); + } + if max_vcpus == 0 { + max_vcpus = 4 + } + let vcpu = vm.create_vcpu((max_vcpus - 1) as u64); + assert!(vcpu.is_ok()); + let vcpu_err = vm.create_vcpu(max_vcpus as u64).err(); + assert_eq!(vcpu_err.unwrap().errno(), libc::EINVAL); + } + #[test] fn test_check_extension() { let kvm = Kvm::new().unwrap(); From 69d03d93741fb86c639674d4a4b5f5afc9688f9e Mon Sep 17 00:00:00 2001 From: Abhijit Gadgil Date: Tue, 12 Jan 2021 20:23:51 +0530 Subject: [PATCH 136/332] system: Added an API `get_max_vcpu_id` to `kvm` Added a new API `get_max_vcpu_id` that returns the `KVM_CAP_MAX_VCPU_ID` if the Extension is present or `get_max_vcpus`. Also updated the test case for `vm.create_cpu` to use `max_vcpu_id` obtained using the above API. Updated coverage score to 91.3 to fix build issue Signed-off-by: Abhijit Gadgil --- coverage_config_x86_64.json | 2 +- src/ioctls/system.rs | 21 +++++++++++++++++++++ src/ioctls/vm.rs | 25 +++++-------------------- 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index debc8fb..eba81b3 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 91.2, + "coverage_score": 91.3, "exclude_path": "", "crate_features": "" } diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index 13ce764..30e63db 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -227,6 +227,27 @@ impl Kvm { } } + /// Gets the Maximum VCPU ID per VM. + /// + /// See the documentation for `KVM_CAP_MAX_VCPU_ID` + /// Returns [get_max_vcpus()](struct.Kvm.html#method.get_max_vcpus) when + /// `KVM_CAP_MAX_VCPU_ID` is not implemented + /// + /// # Example + /// + /// ``` + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// assert!(kvm.get_max_vcpu_id() > 0); + /// ``` + /// + pub fn get_max_vcpu_id(&self) -> usize { + match self.check_extension_int(Cap::MaxVcpuId) { + 0 => self.get_max_vcpus(), + x => x as usize, + } + } + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn get_cpuid(&self, kind: u64, num_entries: usize) -> Result { if num_entries > KVM_MAX_CPUID_ENTRIES { diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 0689f17..6b17b44 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -1800,7 +1800,7 @@ mod tests { } #[test] - fn create_vcpu_different_cpuids() { + fn test_create_vcpu_different_ids() { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); @@ -1808,26 +1808,11 @@ mod tests { let err = vm.create_vcpu(65537 as u64).err(); assert_eq!(err.unwrap().errno(), libc::EINVAL); - // Note: We can request up to KVM_MAX_VCPU_ID if it exists or up to KVM_MAX_VCPUS or - // NR_CPUS or 4. This is determined by the appropriate capability being present. - // We check near boundry conditions `max_vcpus - 1` should succeed but `max_vcpus` as - // determined by the appropriate capability should fail. - // - // Ref: https://www.kernel.org/doc/html/latest/virt/kvm/api.html#kvm-create-vcpu - // - let mut max_vcpus = vm.check_extension_int(Cap::MaxVcpuId); - if max_vcpus == 0 { - max_vcpus = vm.check_extension_int(Cap::MaxVcpus); - } - if max_vcpus == 0 { - max_vcpus = vm.check_extension_int(Cap::NrVcpus); - } - if max_vcpus == 0 { - max_vcpus = 4 - } - let vcpu = vm.create_vcpu((max_vcpus - 1) as u64); + // Fails when input `id` = `max_vcpu_id` + let max_vcpu_id = kvm.get_max_vcpu_id(); + let vcpu = vm.create_vcpu((max_vcpu_id - 1) as u64); assert!(vcpu.is_ok()); - let vcpu_err = vm.create_vcpu(max_vcpus as u64).err(); + let vcpu_err = vm.create_vcpu(max_vcpu_id as u64).err(); assert_eq!(vcpu_err.unwrap().errno(), libc::EINVAL); } From e147023481102f9aee270df932b10f946f803f2b Mon Sep 17 00:00:00 2001 From: Laura Loghin Date: Mon, 18 Jan 2021 18:09:06 +0200 Subject: [PATCH 137/332] add myself to codeowners Signed-off-by: Laura Loghin --- CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index b2c60d3..3cbfdee 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,3 +1,3 @@ # These owners will be the default owners for everything in # the repo. -* @acatangiu @aghecenco @andreeaflorescu @sameo +* @acatangiu @aghecenco @andreeaflorescu @lauralt @sameo From 6c7cb082b74d3dd299833980f6146a3f7006c038 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Mon, 8 Feb 2021 12:33:31 +0000 Subject: [PATCH 138/332] Add ioctl number for KVM_MEMORY_ENCRYPT_OP This ioctl is used for handling platform encrypted VMs. e.g. for AMD SEV or for Intel TDX. As it is platform dependent the input parameter is an opaque platform dependent type. Signed-off-by: Rob Bradford --- coverage_config_x86_64.json | 2 +- src/kvm_ioctls.rs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index eba81b3..debc8fb 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 91.3, + "coverage_score": 91.2, "exclude_path": "", "crate_features": "" } diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index 08c7d41..31d8214 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -93,6 +93,9 @@ ioctl_ior_nr!(KVM_GET_PIT2, KVMIO, 0x9f, kvm_pit_state2); /* Available with KVM_CAP_PIT_STATE2 */ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_iow_nr!(KVM_SET_PIT2, KVMIO, 0xa0, kvm_pit_state2); +/* KVM_MEMORY_ENCRYPT_OP. Takes opaque platform dependent type: i.e. TDX or SEV */ +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_iowr_nr!(KVM_MEMORY_ENCRYPT_OP, KVMIO, 0xba, std::os::raw::c_ulong); // Ioctls for VCPU fds. From e27b55d39a6176a5d0bb89fdc87e646e7cc47fed Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 24 Feb 2021 07:08:30 +0000 Subject: [PATCH 139/332] Bump rust-vmm-ci from `3b7377c` to `ebc7016` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `3b7377c` to `ebc7016`. - [Release notes](https://github.com/rust-vmm/rust-vmm-ci/releases) - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/3b7377cb51b963c96cf6aba759d575b9ab9e8cc2...ebc701641fa57f78d03f3f5ecac617b7bf7470b4) Signed-off-by: dependabot-preview[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 3b7377c..ebc7016 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 3b7377cb51b963c96cf6aba759d575b9ab9e8cc2 +Subproject commit ebc701641fa57f78d03f3f5ecac617b7bf7470b4 From 1a2449527cd6417408c7c88e7c81f3b7e29a1903 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Wed, 24 Feb 2021 18:23:22 +0200 Subject: [PATCH 140/332] update usage of FAM struct FAM Struct now returns a result on new. Check the result before initializing the relevant structures. PartialEq is no longer implemented for FAM structs where the inner type does not implement PartialEq as well. As a consequence, we cannot test for equality between Cpuid values. Signed-off-by: Andreea Florescu --- src/ioctls/system.rs | 8 ++++---- src/ioctls/vcpu.rs | 24 ++++++++++++++---------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index 30e63db..5dddfc0 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -255,7 +255,7 @@ impl Kvm { return Err(errno::Error::new(libc::ENOMEM)); } - let mut cpuid = CpuId::new(num_entries); + let mut cpuid = CpuId::new(num_entries).map_err(|_| errno::Error::new(libc::ENOMEM))?; let ret = unsafe { // ioctl is unsafe. The kernel is trusted not to write beyond the bounds of the memory @@ -344,7 +344,8 @@ impl Kvm { /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_msr_index_list(&self) -> Result { - let mut msr_list = MsrList::new(KVM_MAX_MSR_ENTRIES); + let mut msr_list = + MsrList::new(KVM_MAX_MSR_ENTRIES).map_err(|_| errno::Error::new(libc::ENOMEM))?; let ret = unsafe { // ioctl is unsafe. The kernel is trusted not to write beyond the bounds of the memory @@ -656,8 +657,7 @@ mod tests { let kvm = unsafe { Kvm::from_raw_fd(rawfd) }; let cpuid_1 = kvm.get_supported_cpuid(KVM_MAX_CPUID_ENTRIES).unwrap(); - let cpuid_2 = CpuId::new(cpuid_1.as_fam_struct_ref().len()); - assert!(cpuid_1 != cpuid_2); + let _ = CpuId::new(cpuid_1.as_fam_struct_ref().len()).unwrap(); } #[test] diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index f3fd009..72fcfac 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -381,7 +381,7 @@ impl VcpuFd { return Err(errno::Error::new(libc::ENOMEM)); } - let mut cpuid = CpuId::new(num_entries); + let mut cpuid = CpuId::new(num_entries).map_err(|_| errno::Error::new(libc::ENOMEM))?; let ret = unsafe { // Here we trust the kernel not to read past the end of the kvm_cpuid2 struct. ioctl_with_mut_ptr(self, KVM_GET_CPUID2(), cpuid.as_mut_fam_struct_ptr()) @@ -548,7 +548,7 @@ impl VcpuFd { /// index: 0x0000_0175, /// ..Default::default() /// }, - /// ]); + /// ]).unwrap(); /// let read = vcpu.get_msrs(&mut msrs).unwrap(); /// assert_eq!(read, 2); /// ``` @@ -591,7 +591,7 @@ impl VcpuFd { /// index: 0x0000_0174, /// ..Default::default() /// }, - /// ]); + /// ]).unwrap(); /// let written = vcpu.set_msrs(&msrs).unwrap(); /// assert_eq!(written, 1); /// ``` @@ -1035,7 +1035,7 @@ impl VcpuFd { /// vm.get_preferred_target(&mut kvi).unwrap(); /// vcpu.vcpu_init(&kvi).expect("Cannot initialize vcpu"); /// - /// let mut reg_list = RegList::new(500); + /// let mut reg_list = RegList::new(500).unwrap(); /// vcpu.get_reg_list(&mut reg_list).unwrap(); /// assert!(reg_list.as_fam_struct_ref().n > 0); /// ``` @@ -1597,7 +1597,7 @@ mod tests { ..Default::default() }, ]; - let msrs_wrapper = Msrs::from_entries(&msrs_to_set); + let msrs_wrapper = Msrs::from_entries(&msrs_to_set).unwrap(); vcpu.set_msrs(&msrs_wrapper).unwrap(); // Now test that GET_MSRS returns the same. @@ -1611,7 +1611,8 @@ mod tests { index: 0x0000_0175, ..Default::default() }, - ]); + ]) + .unwrap(); let nmsrs = vcpu.get_msrs(&mut returned_kvm_msrs).unwrap(); // Verify the lengths match. @@ -1990,13 +1991,16 @@ mod tests { ); assert_eq!( faulty_vcpu_fd - .get_msrs(&mut Msrs::new(1)) + .get_msrs(&mut Msrs::new(1).unwrap()) .unwrap_err() .errno(), badf_errno ); assert_eq!( - faulty_vcpu_fd.set_msrs(&Msrs::new(1)).unwrap_err().errno(), + faulty_vcpu_fd + .set_msrs(&Msrs::new(1).unwrap()) + .unwrap_err() + .errno(), badf_errno ); assert_eq!( @@ -2136,7 +2140,7 @@ mod tests { let vm = kvm.create_vm().unwrap(); let vcpu = vm.create_vcpu(0).unwrap(); - let mut reg_list = RegList::new(1); + let mut reg_list = RegList::new(1).unwrap(); // KVM_GET_REG_LIST demands that the vcpus be initalized, so we expect this to fail. let err = vcpu.get_reg_list(&mut reg_list).unwrap_err(); assert!(err.errno() == libc::ENOEXEC); @@ -2154,7 +2158,7 @@ mod tests { // We make use of the number of registers returned to allocate memory and // try one more time. - let mut reg_list = RegList::new(reg_list.as_mut_fam_struct().n as usize); + let mut reg_list = RegList::new(reg_list.as_mut_fam_struct().n as usize).unwrap(); assert!(vcpu.get_reg_list(&mut reg_list).is_ok()); } From ec0d8526241663170b9433697bb069f413fe51c3 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Thu, 25 Feb 2021 10:47:31 +0200 Subject: [PATCH 141/332] update vmm-sys-util dependency version The crate now requires vmm-sys-util to be at least v0.8.0. Signed-off-by: Andreea Florescu --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 0b6bb41..53de34e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ license = "Apache-2.0 OR MIT" [dependencies] libc = ">=0.2.39" kvm-bindings = { version = ">=0.2.0", features = ["fam-wrappers"] } -vmm-sys-util = ">=0.2.1" +vmm-sys-util = ">=0.8.0" [dev-dependencies] byteorder = ">=1.2.1" From a78ddc2d47afb539c57f99d15a39cc8ec0ba3a48 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Thu, 25 Feb 2021 10:57:47 +0200 Subject: [PATCH 142/332] release v0.7.0 Signed-off-by: Andreea Florescu --- CHANGELOG.md | 16 ++++++++++++++++ Cargo.toml | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6044e8..a3bee4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +# v0.7.0 + +## Added +- Support for the system API that returns the maximum allowed vCPU ID + (`KVM_CAP_MAX_VCPU_ID`). +- Support for `KVM_MEMORY_ENCRYPT_OP`. + +## Fixed +- [[#119](https://github.com/rust-vmm/kvm-ioctls/issues/119)]: Disallow invalid + number of cpuid entries to be passed to `get_supported_cpuid` and + `get_emulated_cpuid`. + +## Changed +- [[#123](https://github.com/rust-vmm/kvm-ioctls/issues/123)]: Updated + `create_vcpu` to use `u64` as the parameter for the number of vCPUs. + # v0.6.0 ## Added diff --git a/Cargo.toml b/Cargo.toml index 53de34e..d51d384 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kvm-ioctls" -version = "0.6.0" +version = "0.7.0" authors = ["Amazon Firecracker Team "] description = "Safe wrappers over KVM ioctls" repository = "https://github.com/rust-vmm/kvm-ioctls" From 96c3d36beb6916ac34ea73711c02c5ecbe8baf8d Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Fri, 5 Mar 2021 10:05:12 +0000 Subject: [PATCH 143/332] Depend on kvm-bindings =0.3.0 The newer 0.3.1 release has a fixed dependency on a conflicting version of vmm-sys-utils so it is necessary to fix this crate to depend on the older version which has a more flexible vmm-sys-utils dependency. Signed-off-by: Rob Bradford --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d51d384..29f2ba4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ license = "Apache-2.0 OR MIT" [dependencies] libc = ">=0.2.39" -kvm-bindings = { version = ">=0.2.0", features = ["fam-wrappers"] } +kvm-bindings = { version = "=0.3.0", features = ["fam-wrappers"] } vmm-sys-util = ">=0.8.0" [dev-dependencies] From b1fadeadd5debc1a694685b7607318c920be6b41 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Tue, 23 Feb 2021 14:01:57 +0000 Subject: [PATCH 144/332] Expose supplying a type when creating the VM The KVM_CREATE_VM ioctl can take a parameter that is a platform and architecture specific VM type. This can be used for SEV or TDX VMs on x86_64 platforms of for conveying the IPA size on aarch64 platforms. Signed-off-by: Rob Bradford --- src/ioctls/system.rs | 55 +++++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index 5dddfc0..f6e4f18 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -382,17 +382,7 @@ impl Kvm { /// ``` /// pub fn create_vm(&self) -> Result { - // Safe because we know `self.kvm` is a real KVM fd as this module is the only one that - // create Kvm objects. - let ret = unsafe { ioctl(&self.kvm, KVM_CREATE_VM()) }; - if ret >= 0 { - // Safe because we verify the value of ret and we are the owners of the fd. - let vm_file = unsafe { File::from_raw_fd(ret) }; - let run_mmap_size = self.get_vcpu_mmap_size()?; - Ok(new_vmfd(vm_file, run_mmap_size)) - } else { - Err(errno::Error::last()) - } + self.create_vm_with_type(0) // Create using default VM type } /// AArch64 specific function to create a VM fd using the KVM fd with flexible IPA size. @@ -428,15 +418,31 @@ impl Kvm { /// #[cfg(any(target_arch = "aarch64"))] pub fn create_vm_with_ipa_size(&self, ipa_size: u32) -> Result { + self.create_vm_with_type((ipa_size & KVM_VM_TYPE_ARM_IPA_SIZE_MASK).into()) + } + + /// Creates a VM fd using the KVM fd of a specific type. + /// + /// See the documentation for `KVM_CREATE_VM`. + /// A call to this function will also initialize the size of the vcpu mmap area using the + /// `KVM_GET_VCPU_MMAP_SIZE` ioctl. + /// + /// * `vm_type` - Platform and architecture specific platform VM type. A value of 0 is the equivalent + /// to using the default VM type. + /// # Example + /// + /// ``` + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm_with_type(0).unwrap(); + /// // Check that the VM mmap size is the same reported by `KVM_GET_VCPU_MMAP_SIZE`. + /// assert!(vm.run_size() == kvm.get_vcpu_mmap_size().unwrap()); + /// ``` + /// + pub fn create_vm_with_type(&self, vm_type: u64) -> Result { // Safe because we know `self.kvm` is a real KVM fd as this module is the only one that // create Kvm objects. - let ret = unsafe { - ioctl_with_val( - &self.kvm, - KVM_CREATE_VM(), - (ipa_size & KVM_VM_TYPE_ARM_IPA_SIZE_MASK).into(), - ) - }; + let ret = unsafe { ioctl_with_val(&self.kvm, KVM_CREATE_VM(), vm_type) }; if ret >= 0 { // Safe because we verify the value of ret and we are the owners of the fd. let vm_file = unsafe { File::from_raw_fd(ret) }; @@ -596,6 +602,19 @@ mod tests { assert_eq!(vm.run_size(), kvm.get_vcpu_mmap_size().unwrap()); } + #[test] + fn test_create_vm_with_type() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm_with_type(0).unwrap(); + + // Test create_vmfd_from_rawfd() + let rawfd = unsafe { libc::dup(vm.as_raw_fd()) }; + assert!(rawfd >= 0); + let vm = unsafe { kvm.create_vmfd_from_rawfd(rawfd).unwrap() }; + + assert_eq!(vm.run_size(), kvm.get_vcpu_mmap_size().unwrap()); + } + #[test] #[cfg(any(target_arch = "aarch64"))] fn test_create_vm_with_ipa_size() { From e188905d330e57c334426c644549f0f8344f7532 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Mon, 8 Mar 2021 10:24:59 +0000 Subject: [PATCH 145/332] Release 0.8.0 Signed-off-by: Rob Bradford --- CHANGELOG.md | 11 +++++++++++ Cargo.toml | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3bee4f..1cf523e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +# v0.8.0 + +## Added +- Support for specifying VM type (an opaque platform and architecture specific + constant) when creating a VM (`KVM_CREATE_VM` ioctl) via the +`Kvm::create_vm_with_type` function. + +## Changed +- Now depends on kvm-bindings >=0.4.0 to support use of a newer vmm-sys-utils + dependency. + # v0.7.0 ## Added diff --git a/Cargo.toml b/Cargo.toml index 29f2ba4..11a7761 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kvm-ioctls" -version = "0.7.0" +version = "0.8.0" authors = ["Amazon Firecracker Team "] description = "Safe wrappers over KVM ioctls" repository = "https://github.com/rust-vmm/kvm-ioctls" @@ -10,7 +10,7 @@ license = "Apache-2.0 OR MIT" [dependencies] libc = ">=0.2.39" -kvm-bindings = { version = "=0.3.0", features = ["fam-wrappers"] } +kvm-bindings = { version = ">=0.4.0", features = ["fam-wrappers"] } vmm-sys-util = ">=0.8.0" [dev-dependencies] From 3b75cb1929d4eca0231a4b7fa54484fa039772d0 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 11 Mar 2021 15:56:16 +0100 Subject: [PATCH 146/332] aarch64: Fix create_vm_with_ipa_size test When KVM doesn't support Cap::ArmVmIPASize the VMM must use a KVM type of zero. Also fix a comment for get_host_ipa_limit. Signed-off-by: Andrew Jones --- src/ioctls/system.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index f6e4f18..b112c35 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -98,7 +98,7 @@ impl Kvm { /// AArch64 specific call to get the host Intermediate Physical Address space limit. /// - /// Returns 0 if the capability is not available and an integer larger than 32 otherwise. + /// Returns 0 if the capability is not available and an integer >= 32 otherwise. #[cfg(any(target_arch = "aarch64"))] pub fn get_host_ipa_limit(&self) -> i32 { self.check_extension_int(Cap::ArmVmIPASize) @@ -632,8 +632,8 @@ mod tests { .create_vm_with_ipa_size((host_ipa_limit + 1) as u32) .is_err()); } else { - // Unsupported, here we can test with the default value 40. - assert!(kvm.create_vm_with_ipa_size(40).is_err()); + // Unsupported, we can't provide an IPA size. Only KVM type=0 works. + assert!(kvm.create_vm_with_type(0).is_err()); } } From 363ac11884c7c3ba9964a61e8079b716b0ee31e7 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 11 Mar 2021 15:59:40 +0100 Subject: [PATCH 147/332] Create an AArch64 specific create_vm Not all AArch64 platforms support IPAs up to 40 bits. When KVM supports Cap::ArmVmIPASize it's better to get the IPA size from the host and use that when creating the VM, which may avoid unnecessary VM creation failures. Additionally, when 40 is selected with the value 0, which means use the default, some versions of KVM will skip a check resulting in a difficult to debug fail. We must still use 0 when KVM only supports the legacy fixed 40 bit IPA. Signed-off-by: Andrew Jones --- src/ioctls/system.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index b112c35..1875f12 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -381,10 +381,36 @@ impl Kvm { /// assert!(vm.run_size() == kvm.get_vcpu_mmap_size().unwrap()); /// ``` /// + #[cfg(not(any(target_arch = "aarch64")))] pub fn create_vm(&self) -> Result { self.create_vm_with_type(0) // Create using default VM type } + /// AArch64 specific create_vm to create a VM fd using the KVM fd using the host's maximum IPA size. + /// + /// See the arm64 section of KVM documentation for `KVM_CREATE_VM`. + /// A call to this function will also initialize the size of the vcpu mmap area using the + /// `KVM_GET_VCPU_MMAP_SIZE` ioctl. + /// + /// # Example + /// + /// ``` + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// // Check that the VM mmap size is the same reported by `KVM_GET_VCPU_MMAP_SIZE`. + /// assert!(vm.run_size() == kvm.get_vcpu_mmap_size().unwrap()); + /// ``` + /// + #[cfg(any(target_arch = "aarch64"))] + pub fn create_vm(&self) -> Result { + let mut ipa_size = 0; // Create using default VM type + if self.check_extension(Cap::ArmVmIPASize) { + ipa_size = self.get_host_ipa_limit(); + } + self.create_vm_with_type(ipa_size as u64) + } + /// AArch64 specific function to create a VM fd using the KVM fd with flexible IPA size. /// /// See the arm64 section of KVM documentation for `KVM_CREATE_VM`. From 6d846586c7c83f34ecf836422230b215a4dc00dd Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 16 Mar 2021 06:56:39 +0000 Subject: [PATCH 148/332] Bump rust-vmm-ci from `ebc7016` to `24d66cd` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `ebc7016` to `24d66cd`. Signed-off-by: dependabot-preview[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index ebc7016..24d66cd 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit ebc701641fa57f78d03f3f5ecac617b7bf7470b4 +Subproject commit 24d66cdae63d4aa7f8de01b616c015b97604a116 From 8eee8cd7ffea51c9463220f25e505b57b60cb2c7 Mon Sep 17 00:00:00 2001 From: Laura Loghin Date: Tue, 23 Mar 2021 11:10:35 +0200 Subject: [PATCH 149/332] fix clippy errors Signed-off-by: Laura Loghin --- src/ioctls/vcpu.rs | 50 +++++++++++++++------------------------------- 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 72fcfac..5f4611c 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1466,27 +1466,21 @@ mod tests { { let entries = cpuid.as_mut_slice(); for entry in entries.iter_mut() { - match entry.function { - 0 => { - // " KVMKVMKVM " - entry.ebx = 0x4b4d564b; - entry.ecx = 0x564b4d56; - entry.edx = 0x4d; - } - _ => (), + if entry.function == 0 { + // " KVMKVMKVM " + entry.ebx = 0x4b4d564b; + entry.ecx = 0x564b4d56; + entry.edx = 0x4d; } } } vcpu.set_cpuid2(&cpuid).unwrap(); let cpuid_0 = vcpu.get_cpuid2(ncpuids).unwrap(); for entry in cpuid_0.as_slice() { - match entry.function { - 0 => { - assert_eq!(entry.ebx, 0x4b4d564b); - assert_eq!(entry.ecx, 0x564b4d56); - assert_eq!(entry.edx, 0x4d); - } - _ => (), + if entry.function == 0 { + assert_eq!(entry.ebx, 0x4b4d564b); + assert_eq!(entry.ecx, 0x564b4d56); + assert_eq!(entry.edx, 0x4d); } } @@ -1496,27 +1490,17 @@ mod tests { { let entries = cpuid.as_mut_slice(); for entry in entries.iter_mut() { - match entry.function { - 7 => { - if entry.ecx == 0 { - entry.ebx &= !(1 << EBX_SHA_SHIFT); - ebx_sha_off = entry.ebx; - } - } - _ => (), + if entry.function == 7 && entry.ecx == 0 { + entry.ebx &= !(1 << EBX_SHA_SHIFT); + ebx_sha_off = entry.ebx; } } } vcpu.set_cpuid2(&cpuid).unwrap(); let cpuid_1 = vcpu.get_cpuid2(ncpuids).unwrap(); for entry in cpuid_1.as_slice() { - match entry.function { - 7 => { - if entry.ecx == 0 { - assert_eq!(entry.ebx, ebx_sha_off); - } - } - _ => (), + if entry.function == 7 && entry.ecx == 0 { + assert_eq!(entry.ebx, ebx_sha_off); } } } @@ -1898,10 +1882,8 @@ mod tests { // Disabling debugging/single-stepping debug_struct.control = 0; vcpu_fd.set_guest_debug(&debug_struct).unwrap(); - } else { - if instr_idx >= expected_rips.len() { - assert!(false); - } + } else if instr_idx >= expected_rips.len() { + unreachable!(); } let vcpu_regs = vcpu_fd.get_regs().unwrap(); assert_eq!(vcpu_regs.rip, expected_rips[instr_idx]); From 9d0fd1fbc22918fdd03c89ca888195a8ad297685 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 29 Apr 2021 16:08:41 +0000 Subject: [PATCH 150/332] Upgrade to GitHub-native Dependabot --- .github/dependabot.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..4fcd556 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: +- package-ecosystem: gitsubmodule + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 From 94cb91d0911a997ef630bdb9dc525a0997dc656f Mon Sep 17 00:00:00 2001 From: Andrei Sandu Date: Tue, 11 May 2021 13:01:57 +0300 Subject: [PATCH 151/332] Add KVM_SET_TSC_KHZ KVM_GET_TSC_KHZ for x86 The KVM_GET_TSC_KHZ and KVM_SET_TSC_KHZ allows users to access and control the frequency of the Time Stamp Counter. This is useful in cases such as snapshotting, where resuming from a snapshot on a different CPU than on the original machine will lead to inconsitencies in applications which make use of the Time Stamp Counter. This commit: - implements vCPU wrappers: set_tsc_khz(), get_tsc_khz() - adds unit tests - tests for expected failures if KVM_CAP_TSC_CONTROL or KVM_CAP_GET_TSC_KHZ are missing Signed-off-by: Andrei Sandu Signed-off-by: George Pisaltu --- coverage_config_x86_64.json | 2 +- src/ioctls/vcpu.rs | 90 ++++++++++++++++++++++++++++++++++++- src/kvm_ioctls.rs | 7 +++ 3 files changed, 97 insertions(+), 2 deletions(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index debc8fb..d0bb900 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 91.2, + "coverage_score": 91.4, "exclude_path": "", "crate_features": "" } diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 5f4611c..f9e1142 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -17,7 +17,7 @@ use kvm_ioctls::*; use vmm_sys_util::errno; use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ref}; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -use vmm_sys_util::ioctl::{ioctl_with_mut_ptr, ioctl_with_ptr}; +use vmm_sys_util::ioctl::{ioctl_with_mut_ptr, ioctl_with_ptr, ioctl_with_val}; /// Reasons for vCPU exits. /// @@ -1339,6 +1339,61 @@ impl VcpuFd { let kvm_run = self.kvm_run_ptr.as_mut_ref(); kvm_run.immediate_exit = val; } + + /// Returns the vCPU TSC frequency in KHz or an error if the host has unstable TSC. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// let tsc_khz = vcpu.get_tsc_khz().unwrap(); + /// ``` + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn get_tsc_khz(&self) -> Result { + // Safe because we know that our file is a KVM fd and that the request is one of the ones + // defined by kernel. + let ret = unsafe { ioctl(self, KVM_GET_TSC_KHZ()) }; + if ret >= 0 { + Ok(ret as u32) + } else { + Err(errno::Error::new(ret)) + } + } + + /// Sets the specified vCPU TSC frequency. + /// + /// # Arguments + /// + /// * `freq` - The frequency unit is KHz as per the the KVM API documentation + /// for `KVM_SET_TSC_KHZ`. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// vcpu.set_tsc_khz(1000).unwrap(); + /// ``` + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn set_tsc_khz(&self, freq: u32) -> Result<()> { + // Safe because we know that our file is a KVM fd and that the request is one of the ones + // defined by kernel. + let ret = unsafe { ioctl_with_val(self, KVM_SET_TSC_KHZ(), freq as u64) }; + if ret < 0 { + Err(errno::Error::last()) + } else { + Ok(()) + } + } } /// Helper function to create a new `VcpuFd`. @@ -2043,6 +2098,8 @@ mod tests { faulty_vcpu_fd.kvmclock_ctrl().unwrap_err().errno(), badf_errno ); + assert!(faulty_vcpu_fd.get_tsc_khz().is_err()); + assert!(faulty_vcpu_fd.set_tsc_khz(1000000).is_err()); } #[test] @@ -2172,4 +2229,35 @@ mod tests { vcpu.enable_cap(&cap).unwrap(); } } + #[cfg(target_arch = "x86_64")] + #[test] + fn test_get_tsc_khz() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let vcpu = vm.create_vcpu(0).unwrap(); + + if !kvm.check_extension(Cap::GetTscKhz) { + assert!(vcpu.get_tsc_khz().is_err()) + } else { + assert!(vcpu.get_tsc_khz().unwrap() > 0); + } + } + + #[cfg(target_arch = "x86_64")] + #[test] + fn test_set_tsc_khz() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let vcpu = vm.create_vcpu(0).unwrap(); + let freq = vcpu.get_tsc_khz().unwrap(); + + if !(kvm.check_extension(Cap::GetTscKhz) && kvm.check_extension(Cap::TscControl)) { + assert!(vcpu.set_tsc_khz(0).is_err()); + } else { + assert!(vcpu.set_tsc_khz(freq - 500000).is_ok()); + assert_eq!(vcpu.get_tsc_khz().unwrap(), freq - 500000); + assert!(vcpu.set_tsc_khz(freq + 500000).is_ok()); + assert_eq!(vcpu.get_tsc_khz().unwrap(), freq + 500000); + } + } } diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index 31d8214..760c91f 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -197,6 +197,13 @@ ioctl_iow_nr!(KVM_SET_XCRS, KVMIO, 0xa7, kvm_xcrs); #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_io_nr!(KVM_KVMCLOCK_CTRL, KVMIO, 0xad); +/* Available with KVM_CAP_TSC_CONTROL */ +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_io_nr!(KVM_SET_TSC_KHZ, KVMIO, 0xa2); +/* Available with KVM_CAP_GET_TSC_KHZ */ +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_io_nr!(KVM_GET_TSC_KHZ, KVMIO, 0xa3); + /* Available with KVM_CAP_ENABLE_CAP */ #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] ioctl_iow_nr!(KVM_ENABLE_CAP, KVMIO, 0xa3, kvm_enable_cap); From aa59b377675826393978444cc06057b6d48ccd50 Mon Sep 17 00:00:00 2001 From: George Pisaltu Date: Thu, 13 May 2021 17:28:47 +0300 Subject: [PATCH 152/332] Release 0.9.0 Signed-off-by: George Pisaltu --- CHANGELOG.md | 10 ++++++++++ Cargo.toml | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cf523e..12d3667 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +# v0.9.0 + +## Added +- Support for accessing and controlling the Time Stamp Counter on x86 platforms + through the `get_tsc_khz` and `set_tsc_khz` functions. + +## Changed +- Updated `create_vm` on `aarch64` to create a VM fd from the KVM fd using the + host's maximum IPA size. + # v0.8.0 ## Added diff --git a/Cargo.toml b/Cargo.toml index 11a7761..aec25a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kvm-ioctls" -version = "0.8.0" +version = "0.9.0" authors = ["Amazon Firecracker Team "] description = "Safe wrappers over KVM ioctls" repository = "https://github.com/rust-vmm/kvm-ioctls" From 032c1617242c5cef29f2014b9e983c3036c1eb97 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Wed, 23 Jun 2021 14:01:53 +0300 Subject: [PATCH 153/332] fix initialization of invalid fds With Rust 1.52.1 we can no longer initialize invalid fd 2 by using -1 as the value because there is an assert in the from_raw_fd function that checks for that value. We can use -2 instead. Signed-off-by: Andreea Florescu --- src/ioctls/system.rs | 2 +- src/ioctls/vcpu.rs | 2 +- src/ioctls/vm.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index 1875f12..b425f2f 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -718,7 +718,7 @@ mod tests { let badf_errno = libc::EBADF; let faulty_kvm = Kvm { - kvm: unsafe { File::from_raw_fd(-1) }, + kvm: unsafe { File::from_raw_fd(-2) }, }; assert_eq!( diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index f9e1142..8f755c1 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1969,7 +1969,7 @@ mod tests { let badf_errno = libc::EBADF; let faulty_vcpu_fd = VcpuFd { - vcpu: unsafe { File::from_raw_fd(-1) }, + vcpu: unsafe { File::from_raw_fd(-2) }, kvm_run_ptr: KvmRunWrapper { kvm_run_ptr: mmap_anonymous(10), mmap_size: 10, diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 6b17b44..810f71d 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -1633,7 +1633,7 @@ mod tests { let badf_errno = libc::EBADF; let faulty_vm_fd = VmFd { - vm: unsafe { File::from_raw_fd(-1) }, + vm: unsafe { File::from_raw_fd(-2) }, run_size: 0, }; From 16465aa58c51d350b08d7a1363a29705f72e0f95 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Wed, 23 Jun 2021 15:06:16 +0300 Subject: [PATCH 154/332] fix clippy warnings for Rust 1.52.1 Use Default::default() when initializing structures & use explicit type definition for constants. Signed-off-by: Andreea Florescu --- src/ioctls/system.rs | 4 ++-- src/ioctls/vcpu.rs | 20 ++++++++++++-------- src/ioctls/vm.rs | 30 +++++++++++++++++++----------- 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index b425f2f..f085971 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -673,7 +673,7 @@ mod tests { assert!(cpuid_entries.len() <= KVM_MAX_CPUID_ENTRIES); // Test case for more than MAX entries - let cpuid_err = kvm.get_emulated_cpuid(KVM_MAX_CPUID_ENTRIES + 1 as usize); + let cpuid_err = kvm.get_emulated_cpuid(KVM_MAX_CPUID_ENTRIES + 1_usize); assert!(cpuid_err.is_err()); } @@ -687,7 +687,7 @@ mod tests { assert!(cpuid_entries.len() <= KVM_MAX_CPUID_ENTRIES); // Test case for more than MAX entries - let cpuid_err = kvm.get_emulated_cpuid(KVM_MAX_CPUID_ENTRIES + 1 as usize); + let cpuid_err = kvm.get_emulated_cpuid(KVM_MAX_CPUID_ENTRIES + 1_usize); assert!(cpuid_err.is_err()); } diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 8f755c1..c03b773 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1482,7 +1482,7 @@ mod tests { if kvm.check_extension(Cap::ExtCpuid) { let vm = kvm.create_vm().unwrap(); let vcpu = vm.create_vcpu(0).unwrap(); - let err_cpuid = vcpu.get_cpuid2(KVM_MAX_CPUID_ENTRIES + 1 as usize).err(); + let err_cpuid = vcpu.get_cpuid2(KVM_MAX_CPUID_ENTRIES + 1_usize).err(); assert_eq!(err_cpuid.unwrap().errno(), libc::ENOMEM); } } @@ -1500,7 +1500,7 @@ mod tests { for cpu_idx in 0..nr_vcpus { let vcpu = vm.create_vcpu(cpu_idx as u64).unwrap(); vcpu.set_cpuid2(&cpuid).unwrap(); - let err = vcpu.get_cpuid2(ncpuids - 1 as usize).err(); + let err = vcpu.get_cpuid2(ncpuids - 1_usize).err(); assert_eq!(err.unwrap().errno(), libc::E2BIG); } } @@ -1602,7 +1602,7 @@ mod tests { let mut klapic: kvm_lapic_state = vcpu.get_lapic().unwrap(); let reg_offset = 0x300; - let value = 2 as u32; + let value = 2_u32; //try to write and read the APIC_ICR 0x300 let write_slice = unsafe { &mut *(&mut klapic.regs[reg_offset..] as *mut [i8] as *mut [u8]) }; @@ -2216,16 +2216,20 @@ mod tests { fn test_enable_cap() { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); - let mut cap: kvm_enable_cap = Default::default(); - // KVM_CAP_HYPERV_SYNIC needs KVM_CAP_SPLIT_IRQCHIP enabled - cap.cap = KVM_CAP_SPLIT_IRQCHIP; + let mut cap = kvm_enable_cap { + // KVM_CAP_HYPERV_SYNIC needs KVM_CAP_SPLIT_IRQCHIP enabled + cap: KVM_CAP_SPLIT_IRQCHIP, + ..Default::default() + }; cap.args[0] = 24; vm.enable_cap(&cap).unwrap(); let vcpu = vm.create_vcpu(0).unwrap(); if kvm.check_extension(Cap::HypervSynic) { - let mut cap: kvm_enable_cap = Default::default(); - cap.cap = KVM_CAP_HYPERV_SYNIC; + let cap = kvm_enable_cap { + cap: KVM_CAP_HYPERV_SYNIC, + ..Default::default() + }; vcpu.enable_cap(&cap).unwrap(); } } diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 810f71d..95e7030 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -42,8 +42,8 @@ pub enum IoEventAddress { /// [KVM API documentation](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). /// pub struct NoDatamatch; -impl Into for NoDatamatch { - fn into(self) -> u64 { +impl From for u64 { + fn from(_: NoDatamatch) -> u64 { 0 } } @@ -1378,16 +1378,20 @@ mod tests { let vm = kvm.create_vm().unwrap(); assert!(vm.create_irq_chip().is_ok()); - let mut irqchip = kvm_irqchip::default(); - irqchip.chip_id = KVM_IRQCHIP_PIC_MASTER; + let mut irqchip = kvm_irqchip { + chip_id: KVM_IRQCHIP_PIC_MASTER, + ..Default::default() + }; // Set the irq_base to a non-default value to check that set & get work. irqchip.chip.pic.irq_base = 10; assert!(vm.set_irqchip(&irqchip).is_ok()); // We initialize a dummy irq chip (`other_irqchip`) in which the // function `get_irqchip` returns its result. - let mut other_irqchip = kvm_irqchip::default(); - other_irqchip.chip_id = KVM_IRQCHIP_PIC_MASTER; + let mut other_irqchip = kvm_irqchip { + chip_id: KVM_IRQCHIP_PIC_MASTER, + ..Default::default() + }; assert!(vm.get_irqchip(&mut other_irqchip).is_ok()); // Safe because we know that the irqchip type is PIC. @@ -1443,8 +1447,10 @@ mod tests { let orig = vm.get_clock().unwrap(); // Reset time. - let mut fudged = kvm_clock_data::default(); - fudged.clock = 10; + let fudged = kvm_clock_data { + clock: 10, + ..Default::default() + }; vm.set_clock(&fudged).unwrap(); // Get new time. @@ -1768,8 +1774,10 @@ mod tests { fn test_enable_split_irqchip_cap() { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); - let mut cap: kvm_enable_cap = Default::default(); - cap.cap = KVM_CAP_SPLIT_IRQCHIP; + let mut cap = kvm_enable_cap { + cap: KVM_CAP_SPLIT_IRQCHIP, + ..Default::default() + }; // As per the KVM documentation, KVM_CAP_SPLIT_IRQCHIP only emulates // the local APIC in kernel, expecting that a userspace IOAPIC will // be implemented by the VMM. @@ -1805,7 +1813,7 @@ mod tests { let vm = kvm.create_vm().unwrap(); // Fails when an arbitrarily large value - let err = vm.create_vcpu(65537 as u64).err(); + let err = vm.create_vcpu(65537_u64).err(); assert_eq!(err.unwrap().errno(), libc::EINVAL); // Fails when input `id` = `max_vcpu_id` From b617581f87956e94552a359624edbf652c089e72 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 23 Jun 2021 10:16:53 +0000 Subject: [PATCH 155/332] Bump rust-vmm-ci from `24d66cd` to `d2ab3c0` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `24d66cd` to `d2ab3c0`. - [Release notes](https://github.com/rust-vmm/rust-vmm-ci/releases) - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/24d66cdae63d4aa7f8de01b616c015b97604a116...d2ab3c090833aec72eee7da1e3884032206b00e3) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 24d66cd..d2ab3c0 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 24d66cdae63d4aa7f8de01b616c015b97604a116 +Subproject commit d2ab3c090833aec72eee7da1e3884032206b00e3 From 46693b4dad7107b0634bf8d761e7ad08379201ef Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Mon, 5 Jul 2021 17:00:05 +0300 Subject: [PATCH 156/332] fix aarch64 clippy warnings The warnings are not showing up in the CI because we are not running clippy checks on aarch64. This will be fixed in a future commit in rust-vmm-ci. The PR that enables clippy on aarch64 (among other changes): https://github.com/rust-vmm/rust-vmm-ci/pull/70. Signed-off-by: Andreea Florescu --- src/ioctls/device.rs | 8 +++++--- src/ioctls/vcpu.rs | 6 +++--- src/ioctls/vm.rs | 5 ++--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/ioctls/device.rs b/src/ioctls/device.rs index 4f25727..ba27df3 100644 --- a/src/ioctls/device.rs +++ b/src/ioctls/device.rs @@ -297,9 +297,11 @@ mod tests { // This value should be saved in the address provided to the ioctl. let mut data: u32 = 0; - let mut gic_attr = kvm_bindings::kvm_device_attr::default(); - gic_attr.group = KVM_DEV_ARM_VGIC_GRP_NR_IRQS; - gic_attr.addr = data as u64; + let mut gic_attr = kvm_bindings::kvm_device_attr { + group: KVM_DEV_ARM_VGIC_GRP_NR_IRQS, + addr: data as u64, + ..Default::default() + }; // Without properly providing the address to where the // value will be stored, the ioctl fails with EFAULT. diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index c03b773..38ceaee 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1780,7 +1780,7 @@ mod tests { // Get a mutable slice of `mem_size` from `load_addr`. // This is safe because we mapped it before. let mut slice = std::slice::from_raw_parts_mut(load_addr, mem_size); - slice.write(&code).unwrap(); + slice.write_all(&code).unwrap(); } let vcpu_fd = vm.create_vcpu(0).unwrap(); @@ -1825,10 +1825,10 @@ mod tests { // The code snippet dirties one page at guest_addr + 0x10000. // The code page should not be dirty, as it's not written by the guest. let dirty_pages_bitmap = vm.get_dirty_log(slot, mem_size).unwrap(); - let dirty_pages = dirty_pages_bitmap + let dirty_pages: u32 = dirty_pages_bitmap .into_iter() .map(|page| page.count_ones()) - .fold(0, |dirty_page_count, i| dirty_page_count + i); + .sum(); assert_eq!(dirty_pages, 1); } VcpuExit::SystemEvent(type_, flags) => { diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 95e7030..7aa8a74 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -1291,15 +1291,14 @@ pub(crate) fn create_gic_device(vm: &VmFd, flags: u32) -> DeviceFd { fd: 0, flags, }; - let device_fd = match vm.create_device(&mut gic_device) { + match vm.create_device(&mut gic_device) { Ok(fd) => fd, Err(_) => { gic_device.type_ = kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2; vm.create_device(&mut gic_device) .expect("Cannot create KVM vGIC device") } - }; - device_fd + } } /// Set supported number of IRQs for vGIC. From abd7e89c416aa313a3cc719b1fd13a91b76c896a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Adamczak?= <14021+czak@users.noreply.github.com> Date: Mon, 19 Jul 2021 11:16:44 +0200 Subject: [PATCH 157/332] Link crates.io badge to crate page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Łukasz Adamczak <14021+czak@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 862117f..2a6effd 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ [![Build Status](https://badge.buildkite.com/9e0e6c88972a3248a0908506d6946624da84e4e18c0870c4d0.svg)](https://buildkite.com/rust-vmm/kvm-ioctls-ci) -![crates.io](https://img.shields.io/crates/v/kvm-ioctls.svg) +[![crates.io](https://img.shields.io/crates/v/kvm-ioctls.svg)](https://crates.io/crates/kvm-ioctls) # kvm-ioctls From 85de906f75b9f2f621703cebc30cc2904a0749cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Wed, 21 Jul 2021 16:12:50 +0200 Subject: [PATCH 158/332] Return kvm_debug_exit_arch with VcpuExit::Debug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Kröning --- coverage_config_x86_64.json | 2 +- src/ioctls/vcpu.rs | 20 +++++++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index d0bb900..debc8fb 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 91.4, + "coverage_score": 91.2, "exclude_path": "", "crate_features": "" } diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 38ceaee..16dcc74 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -47,7 +47,9 @@ pub enum VcpuExit<'a> { /// Corresponds to KVM_EXIT_HYPERCALL. Hypercall, /// Corresponds to KVM_EXIT_DEBUG. - Debug, + /// + /// Provides architecture specific information for the debug event. + Debug(kvm_debug_exit_arch), /// Corresponds to KVM_EXIT_HLT. Hlt, /// Corresponds to KVM_EXIT_IRQ_WINDOW_OPEN. @@ -1278,7 +1280,12 @@ impl VcpuFd { } } KVM_EXIT_HYPERCALL => Ok(VcpuExit::Hypercall), - KVM_EXIT_DEBUG => Ok(VcpuExit::Debug), + KVM_EXIT_DEBUG => { + // Safe because the exit_reason (which comes from the kernel) told us which + // union field to use. + let debug = unsafe { run.__bindgen_anon_1.debug }; + Ok(VcpuExit::Debug(debug.arch)) + } KVM_EXIT_HLT => Ok(VcpuExit::Hlt), KVM_EXIT_MMIO => { // Safe because the exit_reason (which comes from the kernel) told us which @@ -1932,7 +1939,7 @@ mod tests { assert_eq!(data.len(), 1); assert_eq!(data[0], 0); } - VcpuExit::Debug => { + VcpuExit::Debug(debug) => { if instr_idx == expected_rips.len() - 1 { // Disabling debugging/single-stepping debug_struct.control = 0; @@ -1942,6 +1949,13 @@ mod tests { } let vcpu_regs = vcpu_fd.get_regs().unwrap(); assert_eq!(vcpu_regs.rip, expected_rips[instr_idx]); + assert_eq!(debug.exception, 1); + assert_eq!(debug.pc, expected_rips[instr_idx]); + // Check first 15 bits of DR6 + let mask = (1 << 16) - 1; + assert_eq!(debug.dr6 & mask, 0b100111111110000); + // Bit 10 in DR7 is always 1 + assert_eq!(debug.dr7, 1 << 10); instr_idx += 1; } VcpuExit::Hlt => { From ca99f670d59bdeafcc3ff5844c47dc9f240010dd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Jul 2021 10:12:34 +0000 Subject: [PATCH 159/332] Bump rust-vmm-ci from `d2ab3c0` to `7693628` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `d2ab3c0` to `7693628`. - [Release notes](https://github.com/rust-vmm/rust-vmm-ci/releases) - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/d2ab3c090833aec72eee7da1e3884032206b00e3...769362803545f1d8abcffba77a9beca0bca8953e) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index d2ab3c0..7693628 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit d2ab3c090833aec72eee7da1e3884032206b00e3 +Subproject commit 769362803545f1d8abcffba77a9beca0bca8953e From 70317b6a9b1cc21b59f047a3d41fcf170675367f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Aug 2021 10:14:40 +0000 Subject: [PATCH 160/332] Bump rust-vmm-ci from `7693628` to `a00c7d2` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `7693628` to `a00c7d2`. - [Release notes](https://github.com/rust-vmm/rust-vmm-ci/releases) - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/769362803545f1d8abcffba77a9beca0bca8953e...a00c7d2f1be99c1519ef53ed310da49b4352973a) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 7693628..a00c7d2 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 769362803545f1d8abcffba77a9beca0bca8953e +Subproject commit a00c7d2f1be99c1519ef53ed310da49b4352973a From 495fdbed36717e45de8aa559355b1851c0c7aab3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Aug 2021 10:14:01 +0000 Subject: [PATCH 161/332] Bump rust-vmm-ci from `a00c7d2` to `877d1fb` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `a00c7d2` to `877d1fb`. - [Release notes](https://github.com/rust-vmm/rust-vmm-ci/releases) - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/a00c7d2f1be99c1519ef53ed310da49b4352973a...877d1fb680dcebb33124e6953969722b483fca8f) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index a00c7d2..877d1fb 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit a00c7d2f1be99c1519ef53ed310da49b4352973a +Subproject commit 877d1fb680dcebb33124e6953969722b483fca8f From 18eba53698ae208174ee0affa4cfa102b2417a95 Mon Sep 17 00:00:00 2001 From: Laura Loghin Date: Tue, 24 Aug 2021 15:12:08 +0300 Subject: [PATCH 162/332] fix doc test for set_tsc_khz The documentation test from set_tsc_khz() was failing if KVM_CAP_TSC_CONTROL or KVM_CAP_GET_TSC_KHZ was missing. Signed-off-by: Laura Loghin --- src/ioctls/vcpu.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 16dcc74..07bdaa6 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1376,18 +1376,20 @@ impl VcpuFd { /// /// # Arguments /// - /// * `freq` - The frequency unit is KHz as per the the KVM API documentation + /// * `freq` - The frequency unit is KHz as per the KVM API documentation /// for `KVM_SET_TSC_KHZ`. /// /// # Example /// /// ```rust /// # extern crate kvm_ioctls; - /// # use kvm_ioctls::Kvm; + /// # use kvm_ioctls::{Cap, Kvm}; /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// let vcpu = vm.create_vcpu(0).unwrap(); - /// vcpu.set_tsc_khz(1000).unwrap(); + /// if kvm.check_extension(Cap::GetTscKhz) && kvm.check_extension(Cap::TscControl) { + /// vcpu.set_tsc_khz(1000).unwrap(); + /// } /// ``` /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] From b41621911698bf9eade6c24ec320c5eee35e3ddb Mon Sep 17 00:00:00 2001 From: Laura Loghin Date: Tue, 24 Aug 2021 16:15:31 +0300 Subject: [PATCH 163/332] Release v0.10.0 This release updates the kvm-bindings to >=0.5.0. The kvm-bindings v0.5.0 release replaced the v4.20 bindings with the v5.13 ones. Signed-off-by: Laura Loghin --- CHANGELOG.md | 8 ++++++++ Cargo.toml | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12d3667..c9a4e60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# v0.10.0 + +## Changed +- Now depends on kvm-bindings >=0.5.0 which replaced the v4.20 KVM bindings + with the v5.13 ones. +- Updated `VcpuExit::Debug` to return architecture specific information for the + debug event. + # v0.9.0 ## Added diff --git a/Cargo.toml b/Cargo.toml index aec25a4..3b60085 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kvm-ioctls" -version = "0.9.0" +version = "0.10.0" authors = ["Amazon Firecracker Team "] description = "Safe wrappers over KVM ioctls" repository = "https://github.com/rust-vmm/kvm-ioctls" @@ -10,7 +10,7 @@ license = "Apache-2.0 OR MIT" [dependencies] libc = ">=0.2.39" -kvm-bindings = { version = ">=0.4.0", features = ["fam-wrappers"] } +kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } vmm-sys-util = ">=0.8.0" [dev-dependencies] From 2ea2cf45e962a5f789b4e684d217e2c81c239148 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Aug 2021 10:13:03 +0000 Subject: [PATCH 164/332] Bump rust-vmm-ci from `877d1fb` to `8901e77` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `877d1fb` to `8901e77`. - [Release notes](https://github.com/rust-vmm/rust-vmm-ci/releases) - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/877d1fb680dcebb33124e6953969722b483fca8f...8901e7752288ae1061e2ee888a104c083a451668) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 877d1fb..8901e77 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 877d1fb680dcebb33124e6953969722b483fca8f +Subproject commit 8901e7752288ae1061e2ee888a104c083a451668 From a5b3af4ebecc6ecff8f22384460b29e7e7a0f4d4 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Fri, 10 Sep 2021 12:54:09 +0300 Subject: [PATCH 165/332] update dependabot formatting Signed-off-by: Andreea Florescu --- .github/dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 4fcd556..2511b63 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,5 +3,5 @@ updates: - package-ecosystem: gitsubmodule directory: "/" schedule: - interval: daily + interval: weekly open-pull-requests-limit: 10 From 66529690273e1ee555356705daca39e2991f3f9c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Sep 2021 10:06:50 +0000 Subject: [PATCH 166/332] Bump rust-vmm-ci from `8901e77` to `f67ef4c` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `8901e77` to `f67ef4c`. - [Release notes](https://github.com/rust-vmm/rust-vmm-ci/releases) - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/8901e7752288ae1061e2ee888a104c083a451668...f67ef4c84d99facfdba70a97eb11a4364267eb2b) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 8901e77..f67ef4c 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 8901e7752288ae1061e2ee888a104c083a451668 +Subproject commit f67ef4c84d99facfdba70a97eb11a4364267eb2b From 47c79785804831466dcbdfd94882745c9dd5e038 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Sep 2021 10:05:02 +0000 Subject: [PATCH 167/332] Bump rust-vmm-ci from `f67ef4c` to `7f22582` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `f67ef4c` to `7f22582`. - [Release notes](https://github.com/rust-vmm/rust-vmm-ci/releases) - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/f67ef4c84d99facfdba70a97eb11a4364267eb2b...7f22582590b5816878e7f3f860766979cab297a0) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index f67ef4c..7f22582 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit f67ef4c84d99facfdba70a97eb11a4364267eb2b +Subproject commit 7f22582590b5816878e7f3f860766979cab297a0 From a6a4b1b4d31fa2a3f384256b2e5c40865d4d9c11 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 10:05:31 +0000 Subject: [PATCH 168/332] Bump rust-vmm-ci from `7f22582` to `68d4dbf` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `7f22582` to `68d4dbf`. - [Release notes](https://github.com/rust-vmm/rust-vmm-ci/releases) - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/7f22582590b5816878e7f3f860766979cab297a0...68d4dbf85c2ef54a440afa635d2f0db8635dcfb7) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 7f22582..68d4dbf 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 7f22582590b5816878e7f3f860766979cab297a0 +Subproject commit 68d4dbf85c2ef54a440afa635d2f0db8635dcfb7 From b80cbcf0ba027cc3a7919d2b54e865531d4226f2 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Thu, 14 Oct 2021 10:24:57 +0300 Subject: [PATCH 169/332] fix style in code examples Signed-off-by: Andreea Florescu --- src/cap.rs | 1 - src/ioctls/device.rs | 19 ++++---- src/ioctls/mod.rs | 1 - src/ioctls/system.rs | 17 ------- src/ioctls/vcpu.rs | 63 ++++++++----------------- src/ioctls/vm.rs | 109 +++++++++++++++++-------------------------- src/lib.rs | 64 ++++++++++++------------- 7 files changed, 98 insertions(+), 176 deletions(-) diff --git a/src/cap.rs b/src/cap.rs index 4071425..f44d41f 100644 --- a/src/cap.rs +++ b/src/cap.rs @@ -15,7 +15,6 @@ use kvm_bindings::*; /// /// The list of capabilities is based on the the KVM_CAP_* defines from the /// [Linux KVM header](https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/kvm.h). -/// #[derive(Clone, Copy, Debug)] #[repr(u32)] // We are allowing docs to be missing here because this enum is a wrapper diff --git a/src/ioctls/device.rs b/src/ioctls/device.rs index ba27df3..96f08bf 100644 --- a/src/ioctls/device.rs +++ b/src/ioctls/device.rs @@ -22,7 +22,6 @@ impl DeviceFd { /// # Arguments /// /// * `device_attr` - The device attribute to be tested. `addr` field is ignored. - /// pub fn has_device_attr(&self, device_attr: &kvm_device_attr) -> Result<()> { let ret = unsafe { ioctl_with_ref(self, KVM_HAS_DEVICE_ATTR(), device_attr) }; if ret != 0 { @@ -72,7 +71,6 @@ impl DeviceFd { /// device_fd.set_device_attr(&dist_attr).unwrap(); /// } /// ``` - /// pub fn set_device_attr(&self, device_attr: &kvm_device_attr) -> Result<()> { let ret = unsafe { ioctl_with_ref(self, KVM_SET_DEVICE_ATTR(), device_attr) }; if ret != 0 { @@ -115,8 +113,8 @@ impl DeviceFd { /// #[cfg(any(target_arch = "aarch64"))] /// { /// use kvm_bindings::{ - /// KVM_DEV_ARM_VGIC_GRP_NR_IRQS, kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2, - /// kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, + /// kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2, kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, + /// KVM_DEV_ARM_VGIC_GRP_NR_IRQS, /// }; /// /// // Create a GIC device. @@ -126,12 +124,12 @@ impl DeviceFd { /// flags: 0, /// }; /// let device_fd = match vm.create_device(&mut gic_device) { - /// Ok(fd) => fd, - /// Err(_) => { - /// gic_device.type_ = kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2; - /// vm.create_device(&mut gic_device) - /// .expect("Cannot create KVM vGIC device") - /// } + /// Ok(fd) => fd, + /// Err(_) => { + /// gic_device.type_ = kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2; + /// vm.create_device(&mut gic_device) + /// .expect("Cannot create KVM vGIC device") + /// } /// }; /// /// let mut data: u32 = 0; @@ -142,7 +140,6 @@ impl DeviceFd { /// device_fd.get_device_attr(&mut gic_attr).unwrap(); /// } /// ``` - /// pub fn get_device_attr(&self, device_attr: &mut kvm_device_attr) -> Result<()> { let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_DEVICE_ATTR(), device_attr) }; if ret != 0 { diff --git a/src/ioctls/mod.rs b/src/ioctls/mod.rs index e28fc0f..855aba1 100644 --- a/src/ioctls/mod.rs +++ b/src/ioctls/mod.rs @@ -73,7 +73,6 @@ impl KvmRunWrapper { } /// Returns a mutable reference to `kvm_run`. - /// #[allow(clippy::mut_from_ref)] pub fn as_mut_ref(&self) -> &mut kvm_run { // Safe because we know we mapped enough memory to hold the kvm_run struct because the diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index f085971..150b554 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -36,7 +36,6 @@ impl Kvm { /// use kvm_ioctls::Kvm; /// let kvm = Kvm::new().unwrap(); /// ``` - /// #[allow(clippy::new_ret_no_self)] pub fn new() -> Result { // Open `/dev/kvm` using `O_CLOEXEC` flag. @@ -66,7 +65,6 @@ impl Kvm { /// // `from_raw_fd` for creating a `Kvm` object: /// let kvm = unsafe { Kvm::from_raw_fd(kvm_fd) }; /// ``` - /// pub fn open_with_cloexec(close_on_exec: bool) -> Result { let open_flags = O_RDWR | if close_on_exec { O_CLOEXEC } else { 0 }; // Safe because we give a constant nul-terminated string and verify the result. @@ -89,7 +87,6 @@ impl Kvm { /// let kvm = Kvm::new().unwrap(); /// assert_eq!(kvm.get_api_version(), 12); /// ``` - /// pub fn get_api_version(&self) -> i32 { // Safe because we know that our file is a KVM fd and that the request is one of the ones // defined by kernel. @@ -132,7 +129,6 @@ impl Kvm { /// // Check if `KVM_CAP_USER_MEMORY` is supported. /// assert!(kvm.check_extension(Cap::UserMemory)); /// ``` - /// pub fn check_extension(&self, c: Cap) -> bool { self.check_extension_int(c) > 0 } @@ -148,7 +144,6 @@ impl Kvm { /// let kvm = Kvm::new().unwrap(); /// assert!(kvm.get_vcpu_mmap_size().unwrap() > 0); /// ``` - /// pub fn get_vcpu_mmap_size(&self) -> Result { // Safe because we know that our file is a KVM fd and we verify the return result. let res = unsafe { ioctl(self, KVM_GET_VCPU_MMAP_SIZE()) }; @@ -172,7 +167,6 @@ impl Kvm { /// // We expect the number of vCPUs to be > 0 as per KVM API documentation. /// assert!(kvm.get_nr_vcpus() > 0); /// ``` - /// pub fn get_nr_vcpus(&self) -> usize { let x = self.check_extension_int(Cap::NrVcpus); if x > 0 { @@ -196,7 +190,6 @@ impl Kvm { /// let kvm = Kvm::new().unwrap(); /// assert!(kvm.get_nr_memslots() > 0); /// ``` - /// pub fn get_nr_memslots(&self) -> usize { let x = self.check_extension_int(Cap::NrMemslots); if x > 0 { @@ -219,7 +212,6 @@ impl Kvm { /// let kvm = Kvm::new().unwrap(); /// assert!(kvm.get_max_vcpus() > 0); /// ``` - /// pub fn get_max_vcpus(&self) -> usize { match self.check_extension_int(Cap::MaxVcpus) { 0 => self.get_nr_vcpus(), @@ -240,7 +232,6 @@ impl Kvm { /// let kvm = Kvm::new().unwrap(); /// assert!(kvm.get_max_vcpu_id() > 0); /// ``` - /// pub fn get_max_vcpu_id(&self) -> usize { match self.check_extension_int(Cap::MaxVcpuId) { 0 => self.get_max_vcpus(), @@ -294,7 +285,6 @@ impl Kvm { /// let cpuid_entries = cpuid.as_mut_slice(); /// assert!(cpuid_entries.len() <= KVM_MAX_CPUID_ENTRIES); /// ``` - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_emulated_cpuid(&self, num_entries: usize) -> Result { self.get_cpuid(KVM_GET_EMULATED_CPUID(), num_entries) @@ -324,7 +314,6 @@ impl Kvm { /// let cpuid_entries = cpuid.as_mut_slice(); /// assert!(cpuid_entries.len() <= KVM_MAX_CPUID_ENTRIES); /// ``` - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_supported_cpuid(&self, num_entries: usize) -> Result { self.get_cpuid(KVM_GET_SUPPORTED_CPUID(), num_entries) @@ -380,7 +369,6 @@ impl Kvm { /// // Check that the VM mmap size is the same reported by `KVM_GET_VCPU_MMAP_SIZE`. /// assert!(vm.run_size() == kvm.get_vcpu_mmap_size().unwrap()); /// ``` - /// #[cfg(not(any(target_arch = "aarch64")))] pub fn create_vm(&self) -> Result { self.create_vm_with_type(0) // Create using default VM type @@ -401,7 +389,6 @@ impl Kvm { /// // Check that the VM mmap size is the same reported by `KVM_GET_VCPU_MMAP_SIZE`. /// assert!(vm.run_size() == kvm.get_vcpu_mmap_size().unwrap()); /// ``` - /// #[cfg(any(target_arch = "aarch64"))] pub fn create_vm(&self) -> Result { let mut ipa_size = 0; // Create using default VM type @@ -441,7 +428,6 @@ impl Kvm { /// assert!(vm.run_size() == kvm.get_vcpu_mmap_size().unwrap()); /// } /// ``` - /// #[cfg(any(target_arch = "aarch64"))] pub fn create_vm_with_ipa_size(&self, ipa_size: u32) -> Result { self.create_vm_with_type((ipa_size & KVM_VM_TYPE_ARM_IPA_SIZE_MASK).into()) @@ -464,7 +450,6 @@ impl Kvm { /// // Check that the VM mmap size is the same reported by `KVM_GET_VCPU_MMAP_SIZE`. /// assert!(vm.run_size() == kvm.get_vcpu_mmap_size().unwrap()); /// ``` - /// pub fn create_vm_with_type(&self, vm_type: u64) -> Result { // Safe because we know `self.kvm` is a real KVM fd as this module is the only one that // create Kvm objects. @@ -506,7 +491,6 @@ impl Kvm { /// assert!(rawfd >= 0); /// let vm = unsafe { kvm.create_vmfd_from_rawfd(rawfd).unwrap() }; /// ``` - /// pub unsafe fn create_vmfd_from_rawfd(&self, fd: RawFd) -> Result { let run_mmap_size = self.get_vcpu_mmap_size()?; Ok(new_vmfd(File::from_raw_fd(fd), run_mmap_size)) @@ -547,7 +531,6 @@ impl FromRawFd for Kvm { /// // Safe because we verify that the fd is valid in `open_with_cloexec` and we own the fd. /// let kvm = unsafe { Kvm::from_raw_fd(kvm_fd) }; /// ``` - /// unsafe fn from_raw_fd(fd: RawFd) -> Self { Kvm { kvm: File::from_raw_fd(fd), diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 07bdaa6..ea599f8 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -23,7 +23,6 @@ use vmm_sys_util::ioctl::{ioctl_with_mut_ptr, ioctl_with_ptr, ioctl_with_val}; /// /// The exit reasons are mapped to the `KVM_EXIT_*` defines in the /// [Linux KVM header](https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/kvm.h). -/// #[derive(Debug)] pub enum VcpuExit<'a> { /// An out port instruction was run on the given port with the given data. @@ -120,7 +119,6 @@ impl VcpuFd { /// #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] /// let regs = vcpu.get_regs().unwrap(); /// ``` - /// #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] pub fn get_regs(&self) -> Result { // Safe because we know that our file is a vCPU fd, we know the kernel will only read the @@ -149,7 +147,8 @@ impl VcpuFd { /// let vm = kvm.create_vm().unwrap(); /// let vcpu = vm.create_vcpu(0).unwrap(); /// - /// #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] { + /// #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] + /// { /// // Get the current vCPU registers. /// let mut regs = vcpu.get_regs().unwrap(); /// // Set a new value for the Instruction Pointer. @@ -157,7 +156,6 @@ impl VcpuFd { /// vcpu.set_regs(®s).unwrap(); /// } /// ``` - /// #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] pub fn set_regs(&self, regs: &kvm_regs) -> Result<()> { // Safe because we know that our file is a vCPU fd, we know the kernel will only read the @@ -186,7 +184,6 @@ impl VcpuFd { /// #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] /// let sregs = vcpu.get_sregs().unwrap(); /// ``` - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_sregs(&self) -> Result { // Safe because we know that our file is a vCPU fd, we know the kernel will only write the @@ -215,7 +212,8 @@ impl VcpuFd { /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// let vcpu = vm.create_vcpu(0).unwrap(); - /// #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] { + /// #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] + /// { /// let mut sregs = vcpu.get_sregs().unwrap(); /// // Update the code segment (cs). /// sregs.cs.base = 0; @@ -223,7 +221,6 @@ impl VcpuFd { /// vcpu.set_sregs(&sregs).unwrap(); /// } /// ``` - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_sregs(&self, sregs: &kvm_sregs) -> Result<()> { // Safe because we know that our file is a vCPU fd, we know the kernel will only read the @@ -252,7 +249,6 @@ impl VcpuFd { /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] /// let fpu = vcpu.get_fpu().unwrap(); /// ``` - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_fpu(&self) -> Result { let mut fpu = kvm_fpu::default(); @@ -284,7 +280,8 @@ impl VcpuFd { /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// let vcpu = vm.create_vcpu(0).unwrap(); - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// { /// let KVM_FPU_CWD: u16 = 0x37f; /// let fpu = kvm_fpu { /// fcw: KVM_FPU_CWD, @@ -293,7 +290,6 @@ impl VcpuFd { /// vcpu.set_fpu(&fpu).unwrap(); /// } /// ``` - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_fpu(&self, fpu: &kvm_fpu) -> Result<()> { let ret = unsafe { @@ -458,7 +454,6 @@ impl VcpuFd { /// let vcpu = vm.create_vcpu(0).unwrap(); /// let lapic = vcpu.get_lapic().unwrap(); /// ``` - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_lapic(&self) -> Result { let mut klapic = kvm_lapic_state::default(); @@ -504,9 +499,8 @@ impl VcpuFd { /// apic_icr_slice.write(write_value).unwrap(); /// /// // Update the value of LAPIC. - ///vcpu.set_lapic(&lapic).unwrap(); + /// vcpu.set_lapic(&lapic).unwrap(); /// ``` - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_lapic(&self, klapic: &kvm_lapic_state) -> Result<()> { let ret = unsafe { @@ -550,11 +544,11 @@ impl VcpuFd { /// index: 0x0000_0175, /// ..Default::default() /// }, - /// ]).unwrap(); + /// ]) + /// .unwrap(); /// let read = vcpu.get_msrs(&mut msrs).unwrap(); /// assert_eq!(read, 2); /// ``` - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_msrs(&self, msrs: &mut Msrs) -> Result { let ret = unsafe { @@ -588,16 +582,14 @@ impl VcpuFd { /// let vcpu = vm.create_vcpu(0).unwrap(); /// /// // Configure the entries we want to set. - /// let mut msrs = Msrs::from_entries(&[ - /// kvm_msr_entry { - /// index: 0x0000_0174, - /// ..Default::default() - /// }, - /// ]).unwrap(); + /// let mut msrs = Msrs::from_entries(&[kvm_msr_entry { + /// index: 0x0000_0174, + /// ..Default::default() + /// }]) + /// .unwrap(); /// let written = vcpu.set_msrs(&msrs).unwrap(); /// assert_eq!(written, 1); /// ``` - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_msrs(&self, msrs: &Msrs) -> Result { let ret = unsafe { @@ -630,7 +622,6 @@ impl VcpuFd { /// let vcpu = vm.create_vcpu(0).unwrap(); /// let mp_state = vcpu.get_mp_state().unwrap(); /// ``` - /// #[cfg(any( target_arch = "x86", target_arch = "x86_64", @@ -671,7 +662,6 @@ impl VcpuFd { /// // Your `mp_state` manipulation here. /// vcpu.set_mp_state(mp_state).unwrap(); /// ``` - /// #[cfg(any( target_arch = "x86", target_arch = "x86_64", @@ -709,7 +699,6 @@ impl VcpuFd { /// let vcpu = vm.create_vcpu(0).unwrap(); /// let xsave = vcpu.get_xsave().unwrap(); /// ``` - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_xsave(&self) -> Result { let mut xsave = Default::default(); @@ -744,7 +733,6 @@ impl VcpuFd { /// // Your `xsave` manipulation here. /// vcpu.set_xsave(&xsave).unwrap(); /// ``` - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_xsave(&self, xsave: &kvm_xsave) -> Result<()> { let ret = unsafe { @@ -776,7 +764,6 @@ impl VcpuFd { /// let vcpu = vm.create_vcpu(0).unwrap(); /// let xcrs = vcpu.get_xcrs().unwrap(); /// ``` - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_xcrs(&self) -> Result { let mut xcrs = Default::default(); @@ -811,7 +798,6 @@ impl VcpuFd { /// // Your `xcrs` manipulation here. /// vcpu.set_xcrs(&xcrs).unwrap(); /// ``` - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_xcrs(&self, xcrs: &kvm_xcrs) -> Result<()> { let ret = unsafe { @@ -843,7 +829,6 @@ impl VcpuFd { /// let vcpu = vm.create_vcpu(0).unwrap(); /// let debug_regs = vcpu.get_debug_regs().unwrap(); /// ``` - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_debug_regs(&self) -> Result { let mut debug_regs = Default::default(); @@ -878,7 +863,6 @@ impl VcpuFd { /// // Your `debug_regs` manipulation here. /// vcpu.set_debug_regs(&debug_regs).unwrap(); /// ``` - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_debug_regs(&self, debug_regs: &kvm_debugregs) -> Result<()> { let ret = unsafe { @@ -913,7 +897,6 @@ impl VcpuFd { /// let vcpu_events = vcpu.get_vcpu_events().unwrap(); /// } /// ``` - /// #[cfg(any( target_arch = "x86", target_arch = "x86_64", @@ -955,7 +938,6 @@ impl VcpuFd { /// vcpu.set_vcpu_events(&vcpu_events).unwrap(); /// } /// ``` - /// #[cfg(any( target_arch = "x86", target_arch = "x86_64", @@ -1000,7 +982,6 @@ impl VcpuFd { /// vm.get_preferred_target(&mut kvi).unwrap(); /// vcpu.vcpu_init(&kvi).unwrap(); /// ``` - /// #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] pub fn vcpu_init(&self, kvi: &kvm_vcpu_init) -> Result<()> { // This is safe because we allocated the struct and we know the kernel will read @@ -1041,7 +1022,6 @@ impl VcpuFd { /// vcpu.get_reg_list(&mut reg_list).unwrap(); /// assert!(reg_list.as_fam_struct_ref().n > 0); /// ``` - /// #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] pub fn get_reg_list(&self, reg_list: &mut RegList) -> Result<()> { let ret = @@ -1074,7 +1054,8 @@ impl VcpuFd { /// let vm = kvm.create_vm().unwrap(); /// let vcpu = vm.create_vcpu(0).unwrap(); /// - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// { /// let debug_struct = kvm_guest_debug { /// // Configure the vcpu so that a KVM_DEBUG_EXIT would be generated /// // when encountering a software breakpoint during execution @@ -1089,7 +1070,6 @@ impl VcpuFd { /// vcpu.set_guest_debug(&debug_struct).unwrap(); /// } /// ``` - /// #[cfg(any( target_arch = "x86", target_arch = "x86_64", @@ -1114,7 +1094,6 @@ impl VcpuFd { /// /// * `reg_id` - ID of the register for which we are setting the value. /// * `data` - value for the specified register. - /// #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] pub fn set_one_reg(&self, reg_id: u64, data: u64) -> Result<()> { let data_ref = &data as *const u64; @@ -1139,7 +1118,6 @@ impl VcpuFd { /// # Arguments /// /// * `reg_id` - ID of the register. - /// #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] pub fn get_one_reg(&self, reg_id: u64) -> Result { let mut reg_value = 0; @@ -1159,7 +1137,6 @@ impl VcpuFd { /// /// See the documentation for `KVM_KVMCLOCK_CTRL` in the /// [KVM API documentation](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn kvmclock_ctrl(&self) -> Result<()> { // Safe because we know that our file is a KVM fd and that the request @@ -1188,7 +1165,8 @@ impl VcpuFd { /// # let kvm = Kvm::new().unwrap(); /// # let vm = kvm.create_vm().unwrap(); /// // This is a dummy example for running on x86 based on https://lwn.net/Articles/658511/. - /// #[cfg(target_arch = "x86_64")] { + /// #[cfg(target_arch = "x86_64")] + /// { /// let mem_size = 0x4000; /// let guest_addr: u64 = 0x1000; /// let load_addr: *mut u8 = unsafe { @@ -1212,9 +1190,7 @@ impl VcpuFd { /// unsafe { vm.set_user_memory_region(mem_region).unwrap() }; /// /// // Dummy x86 code that just calls halt. - /// let x86_code = [ - /// 0xf4, /* hlt */ - /// ]; + /// let x86_code = [0xf4 /* hlt */]; /// /// // Write the code in the guest memory. This will generate a dirty page. /// unsafe { @@ -1247,7 +1223,6 @@ impl VcpuFd { /// } /// } /// ``` - /// pub fn run(&self) -> Result { // Safe because we know that our file is a vCPU fd and we verify the return result. let ret = unsafe { ioctl(self, KVM_RUN()) }; diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 7aa8a74..dc4d9e8 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -26,7 +26,6 @@ use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_ /// /// The `IoEventAddress` is used for specifying the type when registering an event /// in [register_ioevent](struct.VmFd.html#method.register_ioevent). -/// pub enum IoEventAddress { /// Representation of an programmable I/O address. Pio(u64), @@ -40,7 +39,6 @@ pub enum IoEventAddress { /// [`register_ioevent`](struct.VmFd.html#method.register_ioevent) /// to disable filtering of events based on the datamatch flag. For details check the /// [KVM API documentation](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). -/// pub struct NoDatamatch; impl From for u64 { fn from(_: NoDatamatch) -> u64 { @@ -80,23 +78,22 @@ impl VmFd { /// # extern crate kvm_ioctls; /// extern crate kvm_bindings; /// - /// use kvm_ioctls::Kvm; /// use kvm_bindings::kvm_userspace_memory_region; + /// use kvm_ioctls::Kvm; /// /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// let mem_region = kvm_userspace_memory_region { - /// slot: 0, - /// guest_phys_addr: 0x10000 as u64, - /// memory_size: 0x10000 as u64, - /// userspace_addr: 0x0 as u64, - /// flags: 0, - /// }; + /// slot: 0, + /// guest_phys_addr: 0x10000 as u64, + /// memory_size: 0x10000 as u64, + /// userspace_addr: 0x0 as u64, + /// flags: 0, + /// }; /// unsafe { /// vm.set_user_memory_region(mem_region).unwrap(); /// }; /// ``` - /// pub unsafe fn set_user_memory_region( &self, user_memory_region: kvm_userspace_memory_region, @@ -126,7 +123,6 @@ impl VmFd { /// let vm = kvm.create_vm().unwrap(); /// vm.set_tss_address(0xfffb_d000).unwrap(); /// ``` - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_tss_address(&self, offset: usize) -> Result<()> { // Safe because we know that our file is a VM fd and we verify the return result. @@ -153,9 +149,11 @@ impl VmFd { /// /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] /// vm.create_irq_chip().unwrap(); - /// #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] { - /// use kvm_bindings::{kvm_create_device, - /// kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2, KVM_CREATE_DEVICE_TEST}; + /// #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + /// { + /// use kvm_bindings::{ + /// kvm_create_device, kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2, KVM_CREATE_DEVICE_TEST, + /// }; /// let mut gic_device = kvm_bindings::kvm_create_device { /// type_: kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2, /// fd: 0, @@ -166,7 +164,6 @@ impl VmFd { /// } /// } /// ``` - /// #[cfg(any( target_arch = "x86", target_arch = "x86_64", @@ -207,7 +204,6 @@ impl VmFd { /// irqchip.chip_id = KVM_IRQCHIP_PIC_MASTER; /// vm.get_irqchip(&mut irqchip).unwrap(); /// ``` - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_irqchip(&self, irqchip: &mut kvm_irqchip) -> Result<()> { let ret = unsafe { @@ -246,7 +242,6 @@ impl VmFd { /// // Your `irqchip` manipulation here. /// vm.set_irqchip(&mut irqchip).unwrap(); /// ``` - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_irqchip(&self, irqchip: &kvm_irqchip) -> Result<()> { let ret = unsafe { @@ -279,7 +274,6 @@ impl VmFd { /// let pit_config = kvm_pit_config::default(); /// vm.create_pit2(pit_config).unwrap(); /// ``` - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn create_pit2(&self, pit_config: kvm_pit_config) -> Result<()> { // Safe because we know that our file is a VM fd, we know the kernel will only read the @@ -315,7 +309,6 @@ impl VmFd { /// vm.create_pit2(pit_config).unwrap(); /// let pitstate = vm.get_pit2().unwrap(); /// ``` - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_pit2(&self) -> Result { let mut pitstate = Default::default(); @@ -355,7 +348,6 @@ impl VmFd { /// // Your `pitstate` manipulation here. /// vm.set_pit2(&mut pitstate).unwrap(); /// ``` - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_pit2(&self, pitstate: &kvm_pit_state2) -> Result<()> { let ret = unsafe { @@ -387,7 +379,6 @@ impl VmFd { /// let vm = kvm.create_vm().unwrap(); /// let clock = vm.get_clock().unwrap(); /// ``` - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_clock(&self) -> Result { let mut clock = Default::default(); @@ -423,7 +414,6 @@ impl VmFd { /// let mut clock = kvm_clock_data::default(); /// vm.set_clock(&mut clock).unwrap(); /// ``` - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_clock(&self, clock: &kvm_clock_data) -> Result<()> { let ret = unsafe { @@ -471,7 +461,6 @@ impl VmFd { /// vm.create_irq_chip().unwrap(); /// //vm.signal_msi(msi).unwrap(); /// ``` - /// #[cfg(any( target_arch = "x86", target_arch = "x86_64", @@ -519,7 +508,6 @@ impl VmFd { /// let irq_routing = kvm_irq_routing::default(); /// vm.set_gsi_routing(&irq_routing).unwrap(); /// ``` - /// #[cfg(any( target_arch = "x86", target_arch = "x86_64", @@ -563,13 +551,12 @@ impl VmFd { /// let vm_fd = kvm.create_vm().unwrap(); /// let evtfd = EventFd::new(EFD_NONBLOCK).unwrap(); /// vm_fd - /// .register_ioevent(&evtfd, &IoEventAddress::Pio(0xf4), NoDatamatch) - /// .unwrap(); + /// .register_ioevent(&evtfd, &IoEventAddress::Pio(0xf4), NoDatamatch) + /// .unwrap(); /// vm_fd - /// .register_ioevent(&evtfd, &IoEventAddress::Mmio(0x1000), NoDatamatch) - /// .unwrap(); + /// .register_ioevent(&evtfd, &IoEventAddress::Mmio(0x1000), NoDatamatch) + /// .unwrap(); /// ``` - /// pub fn register_ioevent>( &self, fd: &EventFd, @@ -634,19 +621,18 @@ impl VmFd { /// let pio_addr = IoEventAddress::Pio(0xf4); /// let mmio_addr = IoEventAddress::Mmio(0x1000); /// vm_fd - /// .register_ioevent(&evtfd, &pio_addr, NoDatamatch) - /// .unwrap(); + /// .register_ioevent(&evtfd, &pio_addr, NoDatamatch) + /// .unwrap(); /// vm_fd - /// .register_ioevent(&evtfd, &mmio_addr, 0x1234u32) - /// .unwrap(); + /// .register_ioevent(&evtfd, &mmio_addr, 0x1234u32) + /// .unwrap(); /// vm_fd - /// .unregister_ioevent(&evtfd, &pio_addr, NoDatamatch) - /// .unwrap(); + /// .unregister_ioevent(&evtfd, &pio_addr, NoDatamatch) + /// .unwrap(); /// vm_fd - /// .unregister_ioevent(&evtfd, &mmio_addr, 0x1234u32) - /// .unwrap(); + /// .unregister_ioevent(&evtfd, &mmio_addr, 0x1234u32) + /// .unwrap(); /// ``` - /// pub fn unregister_ioevent>( &self, fd: &EventFd, @@ -733,15 +719,14 @@ impl VmFd { /// /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] /// // ASM code that just forces a MMIO Write. - /// let asm_code = [ - /// 0xc6, 0x06, 0x00, 0x80, 0x00, - /// ]; + /// let asm_code = [0xc6, 0x06, 0x00, 0x80, 0x00]; /// #[cfg(target_arch = "aarch64")] /// let asm_code = [ /// 0x01, 0x00, 0x00, 0x10, /* adr x1, */ /// 0x22, 0x10, 0x00, 0xb9, /* str w2, [x1, #16]; write to this page */ /// 0x02, 0x00, 0x00, 0xb9, /* str w2, [x0]; force MMIO exit */ - /// 0x00, 0x00, 0x00, 0x14, /* b ; shouldn't get here, but if so loop forever */ + /// 0x00, 0x00, 0x00, + /// 0x14, /* b ; shouldn't get here, but if so loop forever */ /// ]; /// /// // Write the code in the guest memory. This will generate a dirty page. @@ -789,9 +774,9 @@ impl VmFd { /// // while on aarch64 the dirty bit comes from writing to guest_addr (current PC). /// let dirty_pages_bitmap = vm.get_dirty_log(0, mem_size).unwrap(); /// let dirty_pages = dirty_pages_bitmap - /// .into_iter() - /// .map(|page| page.count_ones()) - /// .fold(0, |dirty_page_count, i| dirty_page_count + i); + /// .into_iter() + /// .map(|page| page.count_ones()) + /// .fold(0, |dirty_page_count, i| dirty_page_count + i); /// assert_eq!(dirty_pages, 1); /// break; /// } @@ -799,7 +784,6 @@ impl VmFd { /// } /// } /// ``` - /// pub fn get_dirty_log(&self, slot: u32, memory_size: usize) -> Result> { // Compute the length of the bitmap needed for all dirty pages in one memory slot. // One memory page is `page_size` bytes and `KVM_GET_DIRTY_LOG` returns one dirty bit for @@ -851,12 +835,12 @@ impl VmFd { /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// let evtfd = EventFd::new(EFD_NONBLOCK).unwrap(); - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// { /// vm.create_irq_chip().unwrap(); /// vm.register_irqfd(&evtfd, 0).unwrap(); /// } /// ``` - /// #[cfg(any( target_arch = "x86", target_arch = "x86_64", @@ -898,13 +882,13 @@ impl VmFd { /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// let evtfd = EventFd::new(EFD_NONBLOCK).unwrap(); - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// { /// vm.create_irq_chip().unwrap(); /// vm.register_irqfd(&evtfd, 0).unwrap(); /// vm.unregister_irqfd(&evtfd, 0).unwrap(); /// } /// ``` - /// #[cfg(any( target_arch = "x86", target_arch = "x86_64", @@ -965,16 +949,17 @@ impl VmFd { /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// arch_setup(&vm); - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// { /// vm.set_irq_line(4, true); /// // ... /// } - /// #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] { + /// #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + /// { /// vm.set_irq_line(0x01_00_0020, true); /// // .... /// } /// ``` - /// #[cfg(any( target_arch = "x86", target_arch = "x86_64", @@ -1020,7 +1005,6 @@ impl VmFd { /// // Create one vCPU with the ID=0. /// let vcpu = vm.create_vcpu(0); /// ``` - /// pub fn create_vcpu(&self, id: u64) -> Result { // Safe because we know that vm is a VM fd and we verify the return result. #[allow(clippy::cast_lossless)] @@ -1067,7 +1051,6 @@ impl VmFd { /// assert!(rawfd >= 0); /// let vcpu = unsafe { vm.create_vcpu_from_rawfd(rawfd).unwrap() }; /// ``` - /// pub unsafe fn create_vcpu_from_rawfd(&self, fd: RawFd) -> Result { let vcpu = File::from_raw_fd(fd); let kvm_run_ptr = KvmRunWrapper::mmap_from_fd(&vcpu, self.run_size)?; @@ -1090,10 +1073,8 @@ impl VmFd { /// # extern crate kvm_bindings; /// # use kvm_ioctls::Kvm; /// use kvm_bindings::{ - /// kvm_device_type_KVM_DEV_TYPE_VFIO, - /// kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2, - /// kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, - /// KVM_CREATE_DEVICE_TEST, + /// kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2, kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, + /// kvm_device_type_KVM_DEV_TYPE_VFIO, KVM_CREATE_DEVICE_TEST, /// }; /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); @@ -1117,11 +1098,11 @@ impl VmFd { /// #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] /// { /// device.type_ = kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2; - /// vm.create_device(&mut device).expect("Cannot create vGIC device") + /// vm.create_device(&mut device) + /// .expect("Cannot create vGIC device") /// } /// }); /// ``` - /// pub fn create_device(&self, device: &mut kvm_create_device) -> Result { let ret = unsafe { ioctl_with_ref(self, KVM_CREATE_DEVICE(), device) }; if ret == 0 { @@ -1153,7 +1134,6 @@ impl VmFd { /// let mut kvi = kvm_vcpu_init::default(); /// vm.get_preferred_target(&mut kvi).unwrap(); /// ``` - /// #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] pub fn get_preferred_target(&self, kvi: &mut kvm_vcpu_init) -> Result<()> { // The ioctl is safe because we allocated the struct and we know the @@ -1208,7 +1188,6 @@ impl VmFd { /// vm.enable_cap(&cap).unwrap(); /// } /// ``` - /// #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] pub fn enable_cap(&self, cap: &kvm_enable_cap) -> Result<()> { // The ioctl is safe because we allocated the struct and we know the @@ -1255,7 +1234,6 @@ impl VmFd { /// // Check if `KVM_CAP_MP_STATE` is supported. /// assert!(vm.check_extension(Cap::MpState)); /// ``` - /// pub fn check_extension(&self, c: Cap) -> bool { self.check_extension_int(c) > 0 } @@ -1282,7 +1260,6 @@ impl AsRawFd for VmFd { /// /// * `vm` - The vm file descriptor. /// * `flags` - Flags to be passed to `KVM_CREATE_DEVICE`. -/// #[cfg(test)] #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] pub(crate) fn create_gic_device(vm: &VmFd, flags: u32) -> DeviceFd { @@ -1307,7 +1284,6 @@ pub(crate) fn create_gic_device(vm: &VmFd, flags: u32) -> DeviceFd { /// /// * `vgic` - The vGIC file descriptor. /// * `nr_irqs` - Number of IRQs. -/// #[cfg(test)] #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] pub(crate) fn set_supported_nr_irqs(vgic: &DeviceFd, nr_irqs: u32) { @@ -1325,7 +1301,6 @@ pub(crate) fn set_supported_nr_irqs(vgic: &DeviceFd, nr_irqs: u32) { /// # Arguments /// /// * `vgic` - The vGIC file descriptor. -/// #[cfg(test)] #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] pub(crate) fn request_gic_init(vgic: &DeviceFd) { diff --git a/src/lib.rs b/src/lib.rs index 10c3cba..472eef8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,16 +50,16 @@ //! extern crate kvm_ioctls; //! extern crate kvm_bindings; //! -//! use kvm_ioctls::{Kvm, VmFd, VcpuFd}; //! use kvm_ioctls::VcpuExit; +//! use kvm_ioctls::{Kvm, VcpuFd, VmFd}; //! -//! fn main(){ +//! fn main() { //! use std::io::Write; -//! use std::slice; //! use std::ptr::null_mut; +//! use std::slice; //! -//! use kvm_bindings::KVM_MEM_LOG_DIRTY_PAGES; //! use kvm_bindings::kvm_userspace_memory_region; +//! use kvm_bindings::KVM_MEM_LOG_DIRTY_PAGES; //! //! let mem_size = 0x4000; //! let guest_addr = 0x1000; @@ -67,27 +67,29 @@ //! //! // Setting up architectural dependent values. //! #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -//! { -//! asm_code = &[ -//! 0xba, 0xf8, 0x03, /* mov $0x3f8, %dx */ -//! 0x00, 0xd8, /* add %bl, %al */ -//! 0x04, b'0', /* add $'0', %al */ -//! 0xee, /* out %al, %dx */ -//! 0xec, /* in %dx, %al */ -//! 0xc6, 0x06, 0x00, 0x80, 0x00, /* movl $0, (0x8000); This generates a MMIO Write.*/ -//! 0x8a, 0x16, 0x00, 0x80, /* movl (0x8000), %dl; This generates a MMIO Read.*/ -//! 0xf4, /* hlt */ -//! ]; -//! } -//! #[cfg(target_arch = "aarch64")] -//! { -//! asm_code = &[ +//! { +//! asm_code = &[ +//! 0xba, 0xf8, 0x03, /* mov $0x3f8, %dx */ +//! 0x00, 0xd8, /* add %bl, %al */ +//! 0x04, b'0', /* add $'0', %al */ +//! 0xee, /* out %al, %dx */ +//! 0xec, /* in %dx, %al */ +//! 0xc6, 0x06, 0x00, 0x80, +//! 0x00, /* movl $0, (0x8000); This generates a MMIO Write. */ +//! 0x8a, 0x16, 0x00, 0x80, /* movl (0x8000), %dl; This generates a MMIO Read. */ +//! 0xf4, /* hlt */ +//! ]; +//! } +//! #[cfg(target_arch = "aarch64")] +//! { +//! asm_code = &[ //! 0x01, 0x00, 0x00, 0x10, /* adr x1, */ //! 0x22, 0x10, 0x00, 0xb9, /* str w2, [x1, #16]; write to this page */ -//! 0x02, 0x00, 0x00, 0xb9, /* str w2, [x0]; This generates a MMIO Write.*/ -//! 0x00, 0x00, 0x00, 0x14, /* b ; shouldn't get here, but if so loop forever */ +//! 0x02, 0x00, 0x00, 0xb9, /* str w2, [x0]; This generates a MMIO Write. */ +//! 0x00, 0x00, 0x00, +//! 0x14, /* b ; shouldn't get here, but if so loop forever */ //! ]; -//! } +//! } //! //! // 1. Instantiate KVM. //! let kvm = Kvm::new().unwrap(); @@ -164,28 +166,20 @@ //! VcpuExit::IoIn(addr, data) => { //! println!( //! "Received an I/O in exit. Address: {:#x}. Data: {:#x}", -//! addr, -//! data[0], +//! addr, data[0], //! ); //! } //! VcpuExit::IoOut(addr, data) => { //! println!( //! "Received an I/O out exit. Address: {:#x}. Data: {:#x}", -//! addr, -//! data[0], +//! addr, data[0], //! ); //! } //! VcpuExit::MmioRead(addr, data) => { -//! println!( -//! "Received an MMIO Read Request for the address {:#x}.", -//! addr, -//! ); +//! println!("Received an MMIO Read Request for the address {:#x}.", addr,); //! } //! VcpuExit::MmioWrite(addr, data) => { -//! println!( -//! "Received an MMIO Write Request to the address {:#x}.", -//! addr, -//! ); +//! println!("Received an MMIO Write Request to the address {:#x}.", addr,); //! // The code snippet dirties 1 page when it is loaded in memory //! let dirty_pages_bitmap = vm.get_dirty_log(slot, mem_size).unwrap(); //! let dirty_pages = dirty_pages_bitmap @@ -229,7 +223,7 @@ pub use ioctls::vm::{IoEventAddress, NoDatamatch, VmFd}; /// /// ``` /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -/// use kvm_ioctls::{KvmRunWrapper, Error}; +/// use kvm_ioctls::{Error, KvmRunWrapper}; /// ``` pub use ioctls::KvmRunWrapper; pub use vmm_sys_util::errno::Error; From 2a6e22a9819be9af91b908480269ec246b9cd6d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Oct 2021 10:06:29 +0000 Subject: [PATCH 170/332] Bump rust-vmm-ci from `68d4dbf` to `7931077` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `68d4dbf` to `7931077`. - [Release notes](https://github.com/rust-vmm/rust-vmm-ci/releases) - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/68d4dbf85c2ef54a440afa635d2f0db8635dcfb7...7931077cdc577edc20af54bf4786de750886fb85) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 68d4dbf..7931077 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 68d4dbf85c2ef54a440afa635d2f0db8635dcfb7 +Subproject commit 7931077cdc577edc20af54bf4786de750886fb85 From de9b4197f2f0753a0ea1a16416d323cdda96e64e Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Fri, 15 Oct 2021 17:39:02 +0200 Subject: [PATCH 171/332] Introduce `has_sev` attribute Signed-off-by: Roman Volosatovs --- build.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 build.rs diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..60b655a --- /dev/null +++ b/build.rs @@ -0,0 +1,10 @@ +// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 OR MIT + +fn main() { + // Define a `has_sev` attribute, which is used for conditional + // execution of SEV-specific tests and examples. + if std::path::Path::new("/dev/sev").exists() { + println!("cargo:rustc-cfg=has_sev"); + } +} From 7ed4701da128ebf8be90a825093f334e83ff3be7 Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Wed, 13 Oct 2021 19:43:51 +0200 Subject: [PATCH 172/332] Add support for `KVM_MEMORY_ENCRYPT_OP` Signed-off-by: Roman Volosatovs --- src/ioctls/vm.rs | 98 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index dc4d9e8..25057d1 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -20,6 +20,8 @@ use ioctls::{KvmRunWrapper, Result}; use kvm_ioctls::*; use vmm_sys_util::errno; use vmm_sys_util::eventfd::EventFd; +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +use vmm_sys_util::ioctl::ioctl_with_mut_ptr; use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_val}; /// An address either in programmable I/O space or in memory mapped I/O space. @@ -1237,6 +1239,91 @@ impl VmFd { pub fn check_extension(&self, c: Cap) -> bool { self.check_extension_int(c) > 0 } + + /// Issues platform-specific memory encryption commands to manage encrypted VMs if + /// the platform supports creating those encrypted VMs. + /// + /// Currently, this ioctl is used for issuing Secure Encrypted Virtualization + /// (SEV) commands on AMD Processors. + /// + /// See the documentation for `KVM_MEMORY_ENCRYPT_OP` in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// For SEV-specific functionality, prefer safe wrapper: + /// - [`encrypt_op_sev`](Self::encrypt_op_sev) + /// + /// # Safety + /// + /// This function is unsafe because there is no guarantee `T` is valid in this context, how + /// much data kernel will read from memory and where it will write data on error. + /// + /// # Arguments + /// + /// * `op` - an opaque platform specific structure. + /// + /// # Example + #[cfg_attr(has_sev, doc = "```rust")] + #[cfg_attr(not(has_sev), doc = "```rust,no_run")] + /// # extern crate kvm_ioctls; + /// # extern crate kvm_bindings; + /// use kvm_bindings::bindings::kvm_sev_cmd; + /// # use kvm_ioctls::Kvm; + /// + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// + /// // Initialize the SEV platform context. + /// let mut init: kvm_sev_cmd = Default::default(); + /// unsafe { vm.encrypt_op(&mut init).unwrap() }; + /// ``` + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub unsafe fn encrypt_op(&self, op: *mut T) -> Result<()> { + let ret = ioctl_with_mut_ptr(self, KVM_MEMORY_ENCRYPT_OP(), op); + if ret == 0 { + Ok(()) + } else { + Err(errno::Error::last()) + } + } + + /// Issue common lifecycle events of SEV guests, such as launching, running, snapshotting, + /// migrating and decommissioning via `KVM_MEMORY_ENCRYPT_OP` ioctl. + /// + /// Kernel documentation states that this ioctl can be used for testing whether SEV is enabled + /// by sending `NULL`. To do that, pass [`std::ptr::null_mut`](std::ptr::null_mut) to [`encrypt_op`](Self::encrypt_op). + /// + /// See the documentation for Secure Encrypted Virtualization (SEV). + /// + /// # Arguments + /// + /// * `op` - SEV-specific structure. For details check the + /// [Secure Encrypted Virtualization (SEV) doc](https://www.kernel.org/doc/Documentation/virtual/kvm/amd-memory-encryption.rst). + /// + /// # Example + #[cfg_attr(has_sev, doc = "```rust")] + #[cfg_attr(not(has_sev), doc = "```rust,no_run")] + /// # extern crate kvm_ioctls; + /// # extern crate kvm_bindings; + /// # use std::{os::raw::c_void, ptr::null_mut}; + /// use kvm_bindings::bindings::kvm_sev_cmd; + /// # use kvm_ioctls::Kvm; + /// + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// + /// // Check whether SEV is enabled, optional. + /// assert!(unsafe { vm.encrypt_op(null_mut() as *mut c_void) }.is_ok()); + /// + /// // Initialize the SEV platform context. + /// let mut init: kvm_sev_cmd = Default::default(); + /// vm.encrypt_op_sev(&mut init).unwrap(); + /// ``` + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn encrypt_op_sev(&self, op: &mut kvm_sev_cmd) -> Result<()> { + // Safe because we know that kernel will only read the correct amount of memory from our pointer + // and we know where it will write it (op.error). + unsafe { self.encrypt_op(op) } + } } /// Helper function to create a new `VmFd`. @@ -1804,4 +1891,15 @@ mod tests { let vm = kvm.create_vm().unwrap(); assert!(vm.check_extension(Cap::MpState)); } + + #[test] + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg_attr(not(has_sev), ignore)] + fn test_encrypt_op_sev() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + + let mut init: kvm_sev_cmd = Default::default(); + assert!(vm.encrypt_op_sev(&mut init).is_ok()); + } } From 8b62c987e5d4ebfa2d62811aca8f531ba435e260 Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Thu, 7 Oct 2021 17:59:54 +0200 Subject: [PATCH 173/332] Add support for `KVM_MEMORY_ENCRYPT_REG_REGION` Co-authored-by: Harald Hoyer Signed-off-by: Roman Volosatovs --- src/ioctls/vm.rs | 75 +++++++++++++++++++++++++++++++++++++++++++++++ src/kvm_ioctls.rs | 3 ++ 2 files changed, 78 insertions(+) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 25057d1..83f6aff 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -1324,6 +1324,81 @@ impl VmFd { // and we know where it will write it (op.error). unsafe { self.encrypt_op(op) } } + + /// Register a guest memory region which may contain encrypted data. + /// + /// It is used in the SEV-enabled guest. + /// + /// See the documentation for `KVM_MEMORY_ENCRYPT_REG_REGION` in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Arguments + /// + /// * `memory_region` - Guest physical memory region. + /// + /// # Example + #[cfg_attr(has_sev, doc = "```rust")] + #[cfg_attr(not(has_sev), doc = "```rust,no_run")] + /// # extern crate kvm_bindings; + /// # extern crate kvm_ioctls; + /// # extern crate libc; + /// # use std::{fs::OpenOptions, ptr::null_mut}; + /// # use std::os::unix::io::AsRawFd; + /// use kvm_bindings::bindings::{kvm_enc_region, kvm_sev_cmd, kvm_sev_launch_start, sev_cmd_id_KVM_SEV_LAUNCH_START}; + /// # use kvm_ioctls::Kvm; + /// use libc; + /// + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let sev = OpenOptions::new() + /// .read(true) + /// .write(true) + /// .open("/dev/sev") + /// .unwrap(); + /// + /// // Initialize the SEV platform context. + /// let mut init: kvm_sev_cmd = Default::default(); + /// assert!(vm.encrypt_op_sev(&mut init).is_ok()); + /// + /// // Create the memory encryption context. + /// let start_data: kvm_sev_launch_start = Default::default(); + /// let mut start = kvm_sev_cmd { + /// id: sev_cmd_id_KVM_SEV_LAUNCH_START, + /// data: &start_data as *const kvm_sev_launch_start as _, + /// sev_fd: sev.as_raw_fd() as _, + /// ..Default::default() + /// }; + /// assert!(vm.encrypt_op_sev(&mut start).is_ok()); + /// + /// let addr = unsafe { + /// libc::mmap( + /// null_mut(), + /// 4096, + /// libc::PROT_READ | libc::PROT_WRITE, + /// libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + /// -1, + /// 0, + /// ) + /// }; + /// assert_ne!(addr, libc::MAP_FAILED); + /// + /// let memory_region = kvm_enc_region { + /// addr: addr as _, + /// size: 4096, + /// }; + /// vm.register_enc_memory_region(&memory_region).unwrap(); + /// ``` + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn register_enc_memory_region(&self, memory_region: &kvm_enc_region) -> Result<()> { + // Safe because we know that our file is a VM fd, we know the kernel will only read the + // correct amount of memory from our pointer, and we verify the return result. + let ret = unsafe { ioctl_with_ref(self, KVM_MEMORY_ENCRYPT_REG_REGION(), memory_region) }; + if ret == 0 { + Ok(()) + } else { + Err(errno::Error::last()) + } + } } /// Helper function to create a new `VmFd`. diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index 760c91f..799e9e9 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -96,6 +96,9 @@ ioctl_iow_nr!(KVM_SET_PIT2, KVMIO, 0xa0, kvm_pit_state2); /* KVM_MEMORY_ENCRYPT_OP. Takes opaque platform dependent type: i.e. TDX or SEV */ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_iowr_nr!(KVM_MEMORY_ENCRYPT_OP, KVMIO, 0xba, std::os::raw::c_ulong); +/* Available on SEV-enabled guests. */ +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_ior_nr!(KVM_MEMORY_ENCRYPT_REG_REGION, KVMIO, 0xbb, kvm_enc_region); // Ioctls for VCPU fds. From 7e3a551dac7c00863a14f2339ed533890a68cc1e Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Thu, 7 Oct 2021 17:59:40 +0200 Subject: [PATCH 174/332] Add support for `KVM_MEMORY_ENCRYPT_UNREG_REGION` Co-authored-by: Harald Hoyer Signed-off-by: Roman Volosatovs --- src/ioctls/vm.rs | 77 +++++++++++++++++++++++++++++++++++++++++++++++ src/kvm_ioctls.rs | 3 ++ 2 files changed, 80 insertions(+) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 83f6aff..a567e76 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -1399,6 +1399,83 @@ impl VmFd { Err(errno::Error::last()) } } + + /// Unregister a guest memory region registered with + /// [`register_enc_memory_region`](Self::register_enc_memory_region). + /// + /// It is used in the SEV-enabled guest. + /// + /// See the documentation for `KVM_MEMORY_ENCRYPT_UNREG_REGION` in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Arguments + /// + /// * `memory_region` - Guest physical memory region. + /// + /// # Example + #[cfg_attr(has_sev, doc = "```rust")] + #[cfg_attr(not(has_sev), doc = "```rust,no_run")] + /// # extern crate kvm_bindings; + /// # extern crate kvm_ioctls; + /// # extern crate libc; + /// # use std::{fs::OpenOptions, ptr::null_mut}; + /// # use std::os::unix::io::AsRawFd; + /// use kvm_bindings::bindings::{kvm_enc_region, kvm_sev_cmd, kvm_sev_launch_start, sev_cmd_id_KVM_SEV_LAUNCH_START}; + /// # use kvm_ioctls::Kvm; + /// use libc; + /// + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let sev = OpenOptions::new() + /// .read(true) + /// .write(true) + /// .open("/dev/sev") + /// .unwrap(); + /// + /// // Initialize the SEV platform context. + /// let mut init: kvm_sev_cmd = Default::default(); + /// assert!(vm.encrypt_op_sev(&mut init).is_ok()); + /// + /// // Create the memory encryption context. + /// let start_data: kvm_sev_launch_start = Default::default(); + /// let mut start = kvm_sev_cmd { + /// id: sev_cmd_id_KVM_SEV_LAUNCH_START, + /// data: &start_data as *const kvm_sev_launch_start as _, + /// sev_fd: sev.as_raw_fd() as _, + /// ..Default::default() + /// }; + /// assert!(vm.encrypt_op_sev(&mut start).is_ok()); + /// + /// let addr = unsafe { + /// libc::mmap( + /// null_mut(), + /// 4096, + /// libc::PROT_READ | libc::PROT_WRITE, + /// libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + /// -1, + /// 0, + /// ) + /// }; + /// assert_ne!(addr, libc::MAP_FAILED); + /// + /// let memory_region = kvm_enc_region { + /// addr: addr as _, + /// size: 4096, + /// }; + /// vm.register_enc_memory_region(&memory_region).unwrap(); + /// vm.unregister_enc_memory_region(&memory_region).unwrap(); + /// ``` + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn unregister_enc_memory_region(&self, memory_region: &kvm_enc_region) -> Result<()> { + // Safe because we know that our file is a VM fd, we know the kernel will only read the + // correct amount of memory from our pointer, and we verify the return result. + let ret = unsafe { ioctl_with_ref(self, KVM_MEMORY_ENCRYPT_UNREG_REGION(), memory_region) }; + if ret == 0 { + Ok(()) + } else { + Err(errno::Error::last()) + } + } } /// Helper function to create a new `VmFd`. diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index 799e9e9..cc9feb4 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -99,6 +99,9 @@ ioctl_iowr_nr!(KVM_MEMORY_ENCRYPT_OP, KVMIO, 0xba, std::os::raw::c_ulong); /* Available on SEV-enabled guests. */ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_ior_nr!(KVM_MEMORY_ENCRYPT_REG_REGION, KVMIO, 0xbb, kvm_enc_region); +/* Available on SEV-enabled guests. */ +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_ior_nr!(KVM_MEMORY_ENCRYPT_UNREG_REGION, KVMIO, 0xbc, kvm_enc_region); // Ioctls for VCPU fds. From f1541e4d45ee80ecbaf2d058d237915531561a49 Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Tue, 12 Oct 2021 19:39:52 +0200 Subject: [PATCH 175/332] Add `KVM_MEMORY_ENCRYPT_{UN,}REG_REGION` test Signed-off-by: Roman Volosatovs --- src/ioctls/vm.rs | 70 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index a567e76..1662ebd 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -1557,6 +1557,9 @@ mod tests { use super::*; use Kvm; + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + use std::{fs::OpenOptions, ptr::null_mut}; + use libc::EFD_NONBLOCK; #[test] @@ -2054,4 +2057,71 @@ mod tests { let mut init: kvm_sev_cmd = Default::default(); assert!(vm.encrypt_op_sev(&mut init).is_ok()); } + + #[test] + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg_attr(not(has_sev), ignore)] + fn test_register_unregister_enc_memory_region() { + let sev = OpenOptions::new() + .read(true) + .write(true) + .open("/dev/sev") + .unwrap(); + + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + + // Perform SEV launch sequence according to + // https://www.kernel.org/doc/Documentation/virtual/kvm/amd-memory-encryption.rst + + let mut init: kvm_sev_cmd = Default::default(); + assert!(vm.encrypt_op_sev(&mut init).is_ok()); + + let start_data: kvm_sev_launch_start = Default::default(); + let mut start = kvm_sev_cmd { + id: sev_cmd_id_KVM_SEV_LAUNCH_START, + data: &start_data as *const kvm_sev_launch_start as _, + sev_fd: sev.as_raw_fd() as _, + ..Default::default() + }; + assert!(vm.encrypt_op_sev(&mut start).is_ok()); + + let addr = unsafe { + libc::mmap( + null_mut(), + 4096, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + 0, + ) + }; + assert_ne!(addr, libc::MAP_FAILED); + + assert_eq!( + vm.register_enc_memory_region(&Default::default()) + .unwrap_err() + .errno(), + libc::EINVAL + ); + assert_eq!( + vm.unregister_enc_memory_region(&Default::default()) + .unwrap_err() + .errno(), + libc::EINVAL + ); + + let memory_region = kvm_enc_region { + addr: addr as _, + size: 4096, + }; + assert_eq!( + vm.unregister_enc_memory_region(&memory_region) + .unwrap_err() + .errno(), + libc::EINVAL + ); + assert!(vm.register_enc_memory_region(&memory_region).is_ok()); + assert!(vm.unregister_enc_memory_region(&memory_region).is_ok()); + } } From c9bad6c7f95c01f67200662c35cc90849b12ca1d Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Wed, 20 Oct 2021 12:09:16 +0200 Subject: [PATCH 176/332] Decrease x86_64 coverage score Signed-off-by: Roman Volosatovs --- coverage_config_x86_64.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index debc8fb..528c3bc 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 91.2, + "coverage_score": 88.0, "exclude_path": "", "crate_features": "" } From df2c1e8cf3375425606670a23c128878709a59e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 10:05:30 +0000 Subject: [PATCH 177/332] Bump rust-vmm-ci from `7931077` to `b037be3` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `7931077` to `b037be3`. - [Release notes](https://github.com/rust-vmm/rust-vmm-ci/releases) - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/7931077cdc577edc20af54bf4786de750886fb85...b037be339677c2f24b7ba676fc9ff893ad474305) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 7931077..b037be3 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 7931077cdc577edc20af54bf4786de750886fb85 +Subproject commit b037be339677c2f24b7ba676fc9ff893ad474305 From ed0bf1b4a6e183014ad3c35ccde38ac8f4bc2d3f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Nov 2021 10:05:44 +0000 Subject: [PATCH 178/332] Bump rust-vmm-ci from `b037be3` to `aee82cf` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `b037be3` to `aee82cf`. - [Release notes](https://github.com/rust-vmm/rust-vmm-ci/releases) - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/b037be339677c2f24b7ba676fc9ff893ad474305...aee82cf0a405f2983ec493fcd55fda5a1ad03f38) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index b037be3..aee82cf 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit b037be339677c2f24b7ba676fc9ff893ad474305 +Subproject commit aee82cf0a405f2983ec493fcd55fda5a1ad03f38 From d2e8ee1f80691b37c1dbbbd4d3fa403b8b215f83 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Wed, 17 Nov 2021 11:42:41 +0200 Subject: [PATCH 179/332] derive Debug for DeviceFd Without this derive it is hard to write negative tests because we cannot do unwrap_err. Signed-off-by: Andreea Florescu --- coverage_config_x86_64.json | 2 +- src/ioctls/device.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index 528c3bc..7abd6c6 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 88.0, + "coverage_score": 87.7, "exclude_path": "", "crate_features": "" } diff --git a/src/ioctls/device.rs b/src/ioctls/device.rs index 96f08bf..0562304 100644 --- a/src/ioctls/device.rs +++ b/src/ioctls/device.rs @@ -11,6 +11,7 @@ use vmm_sys_util::errno; use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref}; /// Wrapper over the file descriptor obtained when creating an emulated device in the kernel. +#[derive(Debug)] pub struct DeviceFd { fd: File, } From 3971fba0adfe08c9ed69fd353911983ea71f13fd Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Thu, 18 Nov 2021 09:50:46 +0200 Subject: [PATCH 180/332] update dependabot frequency Signed-off-by: Andreea Florescu --- .github/dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2511b63..97b2020 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,5 +3,5 @@ updates: - package-ecosystem: gitsubmodule directory: "/" schedule: - interval: weekly + interval: monthly open-pull-requests-limit: 10 From 0ba3f5d8cb6bbe17f1d7c8490dc6297a4987ca1f Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Wed, 17 Nov 2021 18:17:18 +0200 Subject: [PATCH 181/332] release v0.11.0 Details available in the CHANGELOG.md. Signed-off-by: Andreea Florescu --- CHANGELOG.md | 10 ++++++++++ Cargo.toml | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9a4e60..0161bca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +# v0.11.0 + +## Added +- [[#178](https://github.com/rust-vmm/kvm-ioctls/pull/178)] Support for the AMD + Security Encrypted Virtualization (SEV) through the following VM ioctls: + `encrypt_op`, `encrypt_op_sev`, `register_enc_memory_region` and + `unregister_enc_memory_region`. +- [[#184](https://github.com/rust-vmm/kvm-ioctls/pull/184)] `DeviceFd` now + derives `Debug`. + # v0.10.0 ## Changed diff --git a/Cargo.toml b/Cargo.toml index 3b60085..aa6d12b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kvm-ioctls" -version = "0.10.0" +version = "0.11.0" authors = ["Amazon Firecracker Team "] description = "Safe wrappers over KVM ioctls" repository = "https://github.com/rust-vmm/kvm-ioctls" From d22ef1f51852dfb055da38004e1a4fed81246f81 Mon Sep 17 00:00:00 2001 From: Sebastien Boeuf Date: Fri, 3 Dec 2021 16:38:31 +0100 Subject: [PATCH 182/332] Add support for KVM_SET_IDENTITY_MAP_ADDR Adding a missing ioctl to the list of supported ioctls. It is useful to define the address of a one-page region that is needed on Intel hardware because of a quirk in the virtualization implementation. Signed-off-by: Sebastien Boeuf --- coverage_config_x86_64.json | 2 +- src/ioctls/vm.rs | 41 +++++++++++++++++++++++++++++++++++++ src/kvm_ioctls.rs | 3 +++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index 7abd6c6..105c5c0 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 87.7, + "coverage_score": 87.9, "exclude_path": "", "crate_features": "" } diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 1662ebd..32087d9 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -136,6 +136,34 @@ impl VmFd { } } + /// Sets the address of the one-page region in the VM's address space. + /// + /// See the documentation for `KVM_SET_IDENTITY_MAP_ADDR`. + /// + /// # Arguments + /// + /// * `address` - Physical address of a one-page region in the guest's physical address space. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// vm.set_identity_map_address(0xfffb_c000).unwrap(); + /// ``` + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn set_identity_map_address(&self, address: u64) -> Result<()> { + // Safe because we know that our file is a VM fd and we verify the return result. + let ret = unsafe { ioctl_with_ref(self, KVM_SET_IDENTITY_MAP_ADDR(), &address) }; + if ret == 0 { + Ok(()) + } else { + Err(errno::Error::last()) + } + } + /// Creates an in-kernel interrupt controller. /// /// See the documentation for `KVM_CREATE_IRQCHIP`. @@ -1584,6 +1612,19 @@ mod tests { assert!(vm.set_tss_address(0xfffb_d000).is_ok()); } + #[test] + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn test_set_identity_map_address() { + let kvm = Kvm::new().unwrap(); + if kvm.check_extension(Cap::SetIdentityMapAddr) { + let vm = kvm.create_vm().unwrap(); + assert!(vm.set_identity_map_address(0xfffb_c000).is_ok()); + vm.create_vcpu(0).unwrap(); + // Setting the identity map after creating a vCPU must fail. + assert!(vm.set_identity_map_address(0xfffb_c000).is_err()); + } + } + #[test] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn test_irq_chip() { diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index cc9feb4..9138bef 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -36,6 +36,9 @@ ioctl_iow_nr!( /* Available with KVM_CAP_SET_TSS_ADDR */ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_io_nr!(KVM_SET_TSS_ADDR, KVMIO, 0x47); +/* Available with KVM_CAP_SET_IDENTITY_MAP_ADDR */ +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_iow_nr!(KVM_SET_IDENTITY_MAP_ADDR, KVMIO, 0x48, u64); /* Available with KVM_CAP_IRQCHIP */ #[cfg(any( target_arch = "x86", From d12f5776be0937a14da1ad8f9736653e8a2ad5ba Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Tue, 14 Dec 2021 11:17:42 +0200 Subject: [PATCH 183/332] derive debug for all exported structs & enums This is helping with debugging in the VMM. Because the exported structures from here are not deriving Debug, deriving it in the VMM is cumbersome. Signed-off-by: Andreea Florescu --- coverage_config_x86_64.json | 2 +- src/ioctls/mod.rs | 1 + src/ioctls/system.rs | 1 + src/ioctls/vcpu.rs | 1 + src/ioctls/vm.rs | 3 +++ 5 files changed, 7 insertions(+), 1 deletion(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index 105c5c0..8448b6d 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 87.9, + "coverage_score": 85.9, "exclude_path": "", "crate_features": "" } diff --git a/src/ioctls/mod.rs b/src/ioctls/mod.rs index 855aba1..b2b57b8 100644 --- a/src/ioctls/mod.rs +++ b/src/ioctls/mod.rs @@ -30,6 +30,7 @@ pub type Result = std::result::Result; /// /// The wrapper is needed for sending the pointer to `kvm_run` between /// threads as raw pointers do not implement `Send` and `Sync`. +#[derive(Debug)] pub struct KvmRunWrapper { kvm_run_ptr: *mut u8, // This field is need so we can `munmap` the memory mapped to hold `kvm_run`. diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index 150b554..3199a6d 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -23,6 +23,7 @@ use vmm_sys_util::ioctl::ioctl_with_mut_ptr; use vmm_sys_util::ioctl::{ioctl, ioctl_with_val}; /// Wrapper over KVM system ioctls. +#[derive(Debug)] pub struct Kvm { kvm: File, } diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index ea599f8..8b3db96 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -96,6 +96,7 @@ pub enum VcpuExit<'a> { } /// Wrapper over KVM vCPU ioctls. +#[derive(Debug)] pub struct VcpuFd { vcpu: File, kvm_run_ptr: KvmRunWrapper, diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 32087d9..2cee030 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -28,6 +28,7 @@ use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_ /// /// The `IoEventAddress` is used for specifying the type when registering an event /// in [register_ioevent](struct.VmFd.html#method.register_ioevent). +#[derive(Debug)] pub enum IoEventAddress { /// Representation of an programmable I/O address. Pio(u64), @@ -41,6 +42,7 @@ pub enum IoEventAddress { /// [`register_ioevent`](struct.VmFd.html#method.register_ioevent) /// to disable filtering of events based on the datamatch flag. For details check the /// [KVM API documentation](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). +#[derive(Debug)] pub struct NoDatamatch; impl From for u64 { fn from(_: NoDatamatch) -> u64 { @@ -49,6 +51,7 @@ impl From for u64 { } /// Wrapper over KVM VM ioctls. +#[derive(Debug)] pub struct VmFd { vm: File, run_size: usize, From 8ab2e374e262612020fb0b9f7fbdec839ee7a76f Mon Sep 17 00:00:00 2001 From: Jianyong Wu Date: Mon, 8 Mar 2021 11:12:47 +0800 Subject: [PATCH 184/332] Add SET and HAS attr interfaces for vcpu From [1], vcpu also has ioctl interfaces of KVM_SET_DEVICE_ATTR and KVM_HAS_DEVICE_ATTR like deivce does, thus should be added to vcpu struct. For now, I just see it is only used on arm64 in Cloud Hypervisor, so just add these APIs for arm64. Signed-off-by: Jianyong Wu [1] https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt --- src/ioctls/vcpu.rs | 105 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 8b3db96..804f4bc 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -132,6 +132,86 @@ impl VcpuFd { Ok(regs) } + /// Sets a specified piece of cpu configuration and/or state. + /// + /// See the documentation for `KVM_SET_DEVICE_ATTR` in + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt) + /// # Arguments + /// + /// * `device_attr` - The cpu attribute to be set. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # extern crate kvm_bindings; + /// # use kvm_ioctls::Kvm; + /// # use kvm_bindings::{ + /// KVM_ARM_VCPU_PMU_V3_CTRL, KVM_ARM_VCPU_PMU_V3_INIT + /// }; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// + /// let dist_attr = kvm_bindings::kvm_device_attr { + /// group: KVM_ARM_VCPU_PMU_V3_CTRL, + /// attr: u64::from(KVM_ARM_VCPU_PMU_V3_INIT), + /// addr: 0x0, + /// flags: 0, + /// }; + /// + /// if (vcpu.has_device_attr(&dist_attr).is_ok()) { + /// vcpu.set_device_attr(&dist_attr).unwrap(); + /// } + /// ``` + #[cfg(target_arch = "aarch64")] + pub fn set_device_attr(&self, device_attr: &kvm_device_attr) -> Result<()> { + let ret = unsafe { ioctl_with_ref(self, KVM_SET_DEVICE_ATTR(), device_attr) }; + if ret != 0 { + return Err(errno::Error::last()); + } + Ok(()) + } + + /// Tests whether a cpu supports a particular attribute. + /// + /// See the documentation for `KVM_HAS_DEVICE_ATTR` in + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt) + /// # Arguments + /// + /// * `device_attr` - The cpu attribute to be tested. `addr` field is ignored. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # extern crate kvm_bindings; + /// # use kvm_ioctls::Kvm; + /// # use kvm_bindings::{ + /// KVM_ARM_VCPU_PMU_V3_CTRL, KVM_ARM_VCPU_PMU_V3_INIT + /// }; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// + /// let dist_attr = kvm_bindings::kvm_device_attr { + /// group: KVM_ARM_VCPU_PMU_V3_CTRL, + /// attr: u64::from(KVM_ARM_VCPU_PMU_V3_INIT), + /// addr: 0x0, + /// flags: 0, + /// }; + /// + /// vcpu.has_device_attr(&dist_attr); + /// ``` + #[cfg(target_arch = "aarch64")] + pub fn has_device_attr(&self, device_attr: &kvm_device_attr) -> Result<()> { + let ret = unsafe { ioctl_with_ref(self, KVM_HAS_DEVICE_ATTR(), device_attr) }; + if ret != 0 { + return Err(errno::Error::last()); + } + Ok(()) + } + /// Sets the vCPU general purpose registers using the `KVM_SET_REGS` ioctl. /// /// # Arguments @@ -2256,4 +2336,29 @@ mod tests { assert_eq!(vcpu.get_tsc_khz().unwrap(), freq + 500000); } } + + #[test] + #[cfg(target_arch = "aarch64")] + fn test_vcpu_attr() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let vcpu = vm.create_vcpu(0).unwrap(); + + let dist_attr = kvm_bindings::kvm_device_attr { + group: KVM_ARM_VCPU_PMU_V3_CTRL, + attr: u64::from(KVM_ARM_VCPU_PMU_V3_INIT), + addr: 0x0, + flags: 0, + }; + + assert!(vcpu.has_device_attr(&dist_attr).is_err()); + assert!(vcpu.set_device_attr(&dist_attr).is_err()); + let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default(); + vm.get_preferred_target(&mut kvi) + .expect("Cannot get preferred target"); + kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PSCI_0_2 | 1 << KVM_ARM_VCPU_PMU_V3; + assert!(vcpu.vcpu_init(&kvi).is_ok()); + assert!(vcpu.has_device_attr(&dist_attr).is_ok()); + assert!(vcpu.set_device_attr(&dist_attr).is_ok()); + } } From 97ff779b6ea96ae451308149b0ae38f13b271c19 Mon Sep 17 00:00:00 2001 From: Akira Moroo Date: Sat, 22 Jan 2022 16:52:26 +0900 Subject: [PATCH 185/332] Add KVM_TRANSLATE and translate_gva This commit adds support for KVM_TRANSLATE and function translate_gva, translates guest virtual address to the physical address by calling ioctl as described in [1]. [1] https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt Signed-off-by: Akira Moroo Co-authored-by: Adrian Catangiu --- coverage_config_x86_64.json | 2 +- src/ioctls/vcpu.rs | 54 +++++++++++++++++++++++++++++++++++++ src/kvm_ioctls.rs | 2 ++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index 8448b6d..2e04917 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 85.9, + "coverage_score": 86.1, "exclude_path": "", "crate_features": "" } diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 804f4bc..c5e57c6 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1459,6 +1459,43 @@ impl VcpuFd { Ok(()) } } + + /// Translates a virtual address according to the vCPU's current address translation mode. + /// + /// The physical address is returned in a `kvm_translation` structure as defined in the + /// [KVM API documentation](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// See documentation for `KVM_TRANSLATE`. + /// + /// # Arguments + /// + /// * `gva` - The virtual address to translate. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::Kvm; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// let tr = vcpu.translate_gva(0x10000).unwrap(); + /// ``` + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn translate_gva(&self, gva: u64) -> Result { + let mut tr = kvm_translation { + linear_address: gva, + ..Default::default() + }; + + // Safe because we know that our file is a vCPU fd, we know the kernel will only write the + // correct amount of memory to our pointer, and we verify the return result. + let ret = unsafe { ioctl_with_mut_ref(self, KVM_TRANSLATE(), &mut tr) }; + if ret != 0 { + return Err(errno::Error::last()); + } + Ok(tr) + } } /// Helper function to create a new `VcpuFd`. @@ -2172,6 +2209,7 @@ mod tests { ); assert!(faulty_vcpu_fd.get_tsc_khz().is_err()); assert!(faulty_vcpu_fd.set_tsc_khz(1000000).is_err()); + assert!(faulty_vcpu_fd.translate_gva(u64::MAX).is_err()); } #[test] @@ -2337,6 +2375,22 @@ mod tests { } } + #[test] + #[cfg(target_arch = "x86_64")] + fn test_translate_gva() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let vcpu = vm.create_vcpu(0).unwrap(); + assert!(vcpu.translate_gva(0x10000).is_ok()); + assert_eq!(vcpu.translate_gva(0x10000).unwrap().valid, 1); + assert_eq!( + vcpu.translate_gva(0x10000).unwrap().physical_address, + 0x10000 + ); + assert!(vcpu.translate_gva(u64::MAX).is_ok()); + assert_eq!(vcpu.translate_gva(u64::MAX).unwrap().valid, 0); + } + #[test] #[cfg(target_arch = "aarch64")] fn test_vcpu_attr() { diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index 9138bef..25aacb0 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -127,6 +127,8 @@ ioctl_ior_nr!(KVM_GET_SREGS, KVMIO, 0x83, kvm_sregs); target_arch = "powerpc64" ))] ioctl_iow_nr!(KVM_SET_SREGS, KVMIO, 0x84, kvm_sregs); +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_iowr_nr!(KVM_TRANSLATE, KVMIO, 0x85, kvm_translation); /* Available with KVM_CAP_GET_MSR_FEATURES */ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_iowr_nr!(KVM_GET_MSR_INDEX_LIST, KVMIO, 0x02, kvm_msr_list); From 4f12dab636f93cf05e2f0c4c1684e234b3514e66 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Feb 2022 10:01:50 +0000 Subject: [PATCH 186/332] Bump rust-vmm-ci from `aee82cf` to `d216a46` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `aee82cf` to `d216a46`. - [Release notes](https://github.com/rust-vmm/rust-vmm-ci/releases) - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/aee82cf0a405f2983ec493fcd55fda5a1ad03f38...d216a46879096a4f1e83af3a7b6c90c26e1a688d) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index aee82cf..d216a46 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit aee82cf0a405f2983ec493fcd55fda5a1ad03f38 +Subproject commit d216a46879096a4f1e83af3a7b6c90c26e1a688d From dd05e046b7c879a639a2d8fcf3047f25e17f865d Mon Sep 17 00:00:00 2001 From: Sergii Glushchenko Date: Wed, 2 Feb 2022 12:26:26 +0100 Subject: [PATCH 187/332] Adjust test coverage Signed-off-by: Sergii Glushchenko --- coverage_config_x86_64.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index 2e04917..38e6dec 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 86.1, + "coverage_score": 85.0, "exclude_path": "", "crate_features": "" } From b5b9b753bd5ec81e661e63d3ab55c97c9811ecc7 Mon Sep 17 00:00:00 2001 From: ctfhacker Date: Wed, 26 Jan 2022 13:07:31 -0600 Subject: [PATCH 188/332] Add sync_regs and sync_regs_mut KVM provides the ability to synchronize general purpose registers, system registers, and vcpu events without the need of an ioctl. This is worthwhile for VMs that exit frequently to avoid calling `get_regs`, `get_sregs`, or `get_vcpu_events` each exit or `set_regs`, `set_sregs`, or `set_vcpu_events` on each entry. Signed-off-by: Cory Duplantis --- coverage_config_x86_64.json | 2 +- src/ioctls/vcpu.rs | 285 ++++++++++++++++++++++++++++++++++++ src/lib.rs | 4 + 3 files changed, 290 insertions(+), 1 deletion(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index 38e6dec..6d4f884 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 85.0, + "coverage_score": 85.4, "exclude_path": "", "crate_features": "" } diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index c5e57c6..31eba60 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -102,6 +102,21 @@ pub struct VcpuFd { kvm_run_ptr: KvmRunWrapper, } +/// KVM Sync Registers used to tell KVM which registers to sync +#[repr(u32)] +#[derive(Debug, Copy, Clone)] +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +pub enum SyncReg { + /// General purpose registers, + Register = KVM_SYNC_X86_REGS, + + /// System registers + SystemRegister = KVM_SYNC_X86_SREGS, + + /// CPU events + VcpuEvents = KVM_SYNC_X86_EVENTS, +} + impl VcpuFd { /// Returns the vCPU general purpose registers. /// @@ -1496,6 +1511,151 @@ impl VcpuFd { } Ok(tr) } + + /// Enable the given [`SyncReg`] to be copied to userspace on the next exit + /// + /// # Arguments + /// + /// * `reg` - The [`SyncReg`] to copy out of the guest + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::{Kvm, SyncReg, Cap}; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let mut vcpu = vm.create_vcpu(0).unwrap(); + /// vcpu.set_sync_valid_reg(SyncReg::Register); + /// vcpu.set_sync_valid_reg(SyncReg::SystemRegister); + /// vcpu.set_sync_valid_reg(SyncReg::VcpuEvents); + /// ``` + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn set_sync_valid_reg(&mut self, reg: SyncReg) { + let mut kvm_run: &mut kvm_run = self.kvm_run_ptr.as_mut_ref(); + kvm_run.kvm_valid_regs |= reg as u64; + } + + /// Tell KVM to copy the given [`SyncReg`] into the guest on the next entry + /// + /// # Arguments + /// + /// * `reg` - The [`SyncReg`] to copy into the guest + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::{Kvm, SyncReg, Cap}; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let mut vcpu = vm.create_vcpu(0).unwrap(); + /// vcpu.set_sync_dirty_reg(SyncReg::Register); + /// ``` + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn set_sync_dirty_reg(&mut self, reg: SyncReg) { + let mut kvm_run: &mut kvm_run = self.kvm_run_ptr.as_mut_ref(); + kvm_run.kvm_dirty_regs |= reg as u64; + } + + /// Disable the given [`SyncReg`] to be copied to userspace on the next exit + /// + /// # Arguments + /// + /// * `reg` - The [`SyncReg`] to not copy out of the guest + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::{Kvm, SyncReg, Cap}; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let mut vcpu = vm.create_vcpu(0).unwrap(); + /// vcpu.clear_sync_valid_reg(SyncReg::Register); + /// ``` + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn clear_sync_valid_reg(&mut self, reg: SyncReg) { + let mut kvm_run: &mut kvm_run = self.kvm_run_ptr.as_mut_ref(); + kvm_run.kvm_valid_regs &= !(reg as u64); + } + + /// Tell KVM to not copy the given [`SyncReg`] into the guest on the next entry + /// + /// # Arguments + /// + /// * `reg` - The [`SyncReg`] to not copy out into the guest + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::{Kvm, SyncReg, Cap}; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let mut vcpu = vm.create_vcpu(0).unwrap(); + /// vcpu.clear_sync_dirty_reg(SyncReg::Register); + /// ``` + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn clear_sync_dirty_reg(&mut self, reg: SyncReg) { + let mut kvm_run: &mut kvm_run = self.kvm_run_ptr.as_mut_ref(); + kvm_run.kvm_dirty_regs &= !(reg as u64); + } + + /// Get the [`kvm_sync_regs`] from the VM + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::{Kvm, SyncReg, Cap}; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let mut vcpu = vm.create_vcpu(0).unwrap(); + /// if kvm.check_extension(Cap::SyncRegs) { + /// vcpu.set_sync_valid_reg(SyncReg::Register); + /// vcpu.run(); + /// let guest_rax = vcpu.sync_regs().regs.rax; + /// } + /// ``` + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn sync_regs(&self) -> kvm_sync_regs { + let kvm_run: &mut kvm_run = self.kvm_run_ptr.as_mut_ref(); + + // SAFETY: Accessing this union field could be out of bounds if the `kvm_run` + // allocation isn't large enough. The `kvm_run` region is set using + // `get_vcpu_map_size`, so this region is in bounds + unsafe { kvm_run.s.regs } + } + + /// Get a mutable reference to the [`kvm_sync_regs`] from the VM + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # use kvm_ioctls::{Kvm, SyncReg, Cap}; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let mut vcpu = vm.create_vcpu(0).unwrap(); + /// if kvm.check_extension(Cap::SyncRegs) { + /// vcpu.set_sync_valid_reg(SyncReg::Register); + /// vcpu.run(); + /// // Set the guest RAX to 0xdeadbeef + /// vcpu.sync_regs_mut().regs.rax = 0xdeadbeef; + /// vcpu.set_sync_dirty_reg(SyncReg::Register); + /// vcpu.run(); + /// } + /// ``` + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn sync_regs_mut(&mut self) -> &mut kvm_sync_regs { + let kvm_run: &mut kvm_run = self.kvm_run_ptr.as_mut_ref(); + + // SAFETY: Accessing this union field could be out of bounds if the `kvm_run` + // allocation isn't large enough. The `kvm_run` region is set using + // `get_vcpu_map_size`, so this region is in bounds + unsafe { &mut kvm_run.s.regs } + } } /// Helper function to create a new `VcpuFd`. @@ -2375,6 +2535,131 @@ mod tests { } } + #[cfg(target_arch = "x86_64")] + #[test] + fn test_sync_regs() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let mut vcpu = vm.create_vcpu(0).unwrap(); + + // Test setting each valid register + let sync_regs = [ + SyncReg::Register, + SyncReg::SystemRegister, + SyncReg::VcpuEvents, + ]; + for reg in &sync_regs { + vcpu.set_sync_valid_reg(*reg); + assert_eq!(vcpu.kvm_run_ptr.as_mut_ref().kvm_valid_regs, *reg as u64); + vcpu.clear_sync_valid_reg(*reg); + assert_eq!(vcpu.kvm_run_ptr.as_mut_ref().kvm_valid_regs, 0); + } + + // Test that multiple valid SyncRegs can be set at the same time + vcpu.set_sync_valid_reg(SyncReg::Register); + vcpu.set_sync_valid_reg(SyncReg::SystemRegister); + vcpu.set_sync_valid_reg(SyncReg::VcpuEvents); + assert_eq!( + vcpu.kvm_run_ptr.as_mut_ref().kvm_valid_regs, + SyncReg::Register as u64 | SyncReg::SystemRegister as u64 | SyncReg::VcpuEvents as u64 + ); + + // Test setting each dirty register + let sync_regs = [ + SyncReg::Register, + SyncReg::SystemRegister, + SyncReg::VcpuEvents, + ]; + + for reg in &sync_regs { + vcpu.set_sync_dirty_reg(*reg); + assert_eq!(vcpu.kvm_run_ptr.as_mut_ref().kvm_dirty_regs, *reg as u64); + vcpu.clear_sync_dirty_reg(*reg); + assert_eq!(vcpu.kvm_run_ptr.as_mut_ref().kvm_dirty_regs, 0); + } + + // Test that multiple dirty SyncRegs can be set at the same time + vcpu.set_sync_dirty_reg(SyncReg::Register); + vcpu.set_sync_dirty_reg(SyncReg::SystemRegister); + vcpu.set_sync_dirty_reg(SyncReg::VcpuEvents); + assert_eq!( + vcpu.kvm_run_ptr.as_mut_ref().kvm_dirty_regs, + SyncReg::Register as u64 | SyncReg::SystemRegister as u64 | SyncReg::VcpuEvents as u64 + ); + } + + #[cfg(target_arch = "x86_64")] + #[test] + fn test_sync_regs_with_run() { + use std::io::Write; + + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + if kvm.check_extension(Cap::SyncRegs) { + // This example is based on https://lwn.net/Articles/658511/ + #[rustfmt::skip] + let code = [ + 0xff, 0xc0, /* inc eax */ + 0xf4, /* hlt */ + ]; + + let mem_size = 0x4000; + let load_addr = mmap_anonymous(mem_size); + let guest_addr: u64 = 0x1000; + let slot: u32 = 0; + let mem_region = kvm_userspace_memory_region { + slot, + guest_phys_addr: guest_addr, + memory_size: mem_size as u64, + userspace_addr: load_addr as u64, + flags: KVM_MEM_LOG_DIRTY_PAGES, + }; + unsafe { + vm.set_user_memory_region(mem_region).unwrap(); + } + + unsafe { + // Get a mutable slice of `mem_size` from `load_addr`. + // This is safe because we mapped it before. + let mut slice = std::slice::from_raw_parts_mut(load_addr, mem_size); + slice.write_all(&code).unwrap(); + } + + let mut vcpu = vm.create_vcpu(0).unwrap(); + + let orig_sregs = vcpu.get_sregs().unwrap(); + + let mut sync_regs = vcpu.sync_regs_mut(); + + // Initialize the sregs in sync_regs to be the original sregs + sync_regs.sregs = orig_sregs; + sync_regs.sregs.cs.base = 0; + sync_regs.sregs.cs.selector = 0; + + // Set up the guest to attempt to `inc rax` + sync_regs.regs.rip = guest_addr; + sync_regs.regs.rax = 0x8000; + sync_regs.regs.rflags = 2; + + // Initialize the sync_reg flags + vcpu.set_sync_valid_reg(SyncReg::Register); + vcpu.set_sync_valid_reg(SyncReg::SystemRegister); + vcpu.set_sync_valid_reg(SyncReg::VcpuEvents); + vcpu.set_sync_dirty_reg(SyncReg::Register); + vcpu.set_sync_dirty_reg(SyncReg::SystemRegister); + vcpu.set_sync_dirty_reg(SyncReg::VcpuEvents); + + // hlt is the only expected return from guest execution + assert!(matches!(vcpu.run().expect("run failed"), VcpuExit::Hlt)); + + let regs = vcpu.get_regs().unwrap(); + + let sync_regs = vcpu.sync_regs(); + assert_eq!(regs, sync_regs.regs); + assert_eq!(sync_regs.regs.rax, 0x8001); + } + } + #[test] #[cfg(target_arch = "x86_64")] fn test_translate_gva() { diff --git a/src/lib.rs b/src/lib.rs index 472eef8..6969c7a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -216,6 +216,10 @@ pub use cap::Cap; pub use ioctls::device::DeviceFd; pub use ioctls::system::Kvm; pub use ioctls::vcpu::{VcpuExit, VcpuFd}; + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +pub use ioctls::vcpu::SyncReg; + pub use ioctls::vm::{IoEventAddress, NoDatamatch, VmFd}; // The following example is used to verify that our public // structures are exported properly. From f499ce92a60de387f88c0f28d7c50c34623f1489 Mon Sep 17 00:00:00 2001 From: Sebastien Boeuf Date: Mon, 14 Feb 2022 17:05:35 +0100 Subject: [PATCH 189/332] ioctls: Don't panic on unsupported exit reason In case the underlying KVM version is more recent than what is known and supported by the kvm-ioctls crate, and in case KVM reports an unknown reason for a VM exit, we don't want the vCPU run() to panic. A more elegant way of handling such situation is by propagating the exit reason up to the consumer's crate as it might know what to do with it. Signed-off-by: Sebastien Boeuf --- coverage_config_x86_64.json | 2 +- src/ioctls/vcpu.rs | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index 6d4f884..b1ed33d 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 85.4, + "coverage_score": 85.3, "exclude_path": "", "crate_features": "" } diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 31eba60..f910b1b 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -93,6 +93,10 @@ pub enum VcpuExit<'a> { IoapicEoi(u8 /* vector */), /// Corresponds to KVM_EXIT_HYPERV. Hyperv, + /// Corresponds to an exit reason that is unknown from the current version + /// of the kvm-ioctls crate. Let the consumer decide about what to do with + /// it. + Unsupported(u32), } /// Wrapper over KVM vCPU ioctls. @@ -1405,7 +1409,7 @@ impl VcpuFd { Ok(VcpuExit::IoapicEoi(eoi.vector)) } KVM_EXIT_HYPERV => Ok(VcpuExit::Hyperv), - r => panic!("unknown kvm exit reason: {}", r), + r => Ok(VcpuExit::Unsupported(r)), } } else { Err(errno::Error::last()) From 1e03e29cdfbb0cb108a98de7a78045a5a517f18e Mon Sep 17 00:00:00 2001 From: Sebastien Boeuf Date: Wed, 16 Feb 2022 15:33:45 +0100 Subject: [PATCH 190/332] ioctls: vcpu: Expose kvm_run structure Returning a mutable reference to the kvm_run structure is needed for unsupported VM exit reasons as it allows the consumer of the kvm-ioctls crate to handle them correctly. Signed-off-by: Sebastien Boeuf --- src/ioctls/vcpu.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index f910b1b..b0a9b03 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1416,6 +1416,11 @@ impl VcpuFd { } } + /// Returns a mutable reference to the kvm_run structure + pub fn get_kvm_run(&mut self) -> &mut kvm_run { + self.kvm_run_ptr.as_mut_ref() + } + /// Sets the `immediate_exit` flag on the `kvm_run` struct associated with this vCPU to `val`. pub fn set_kvm_immediate_exit(&self, val: u8) { let kvm_run = self.kvm_run_ptr.as_mut_ref(); @@ -2476,7 +2481,16 @@ mod tests { } #[test] - fn set_kvm_immediate_exit() { + fn test_get_kvm_run() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let mut vcpu = vm.create_vcpu(0).unwrap(); + vcpu.kvm_run_ptr.as_mut_ref().immediate_exit = 1; + assert_eq!(vcpu.get_kvm_run().immediate_exit, 1); + } + + #[test] + fn test_set_kvm_immediate_exit() { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); let vcpu = vm.create_vcpu(0).unwrap(); From 8ae794affdc1e5538fce1f622d5719a463f60ae4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Mar 2022 10:01:31 +0000 Subject: [PATCH 191/332] Bump rust-vmm-ci from `d216a46` to `99fe2eb` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `d216a46` to `99fe2eb`. - [Release notes](https://github.com/rust-vmm/rust-vmm-ci/releases) - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/d216a46879096a4f1e83af3a7b6c90c26e1a688d...99fe2eb2e05d1b2cbeed6fb00b754e8f1c5b2f81) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index d216a46..99fe2eb 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit d216a46879096a4f1e83af3a7b6c90c26e1a688d +Subproject commit 99fe2eb2e05d1b2cbeed6fb00b754e8f1c5b2f81 From ccf0bda07485b2433616de998c000aa5f04ce806 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Mon, 23 May 2022 17:45:33 +0100 Subject: [PATCH 192/332] ioctls: vcpu: Expose details from KVM_EXIT_FAIL_ENTRY exit Signed-off-by: Rob Bradford --- coverage_config_x86_64.json | 2 +- src/ioctls/vcpu.rs | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index b1ed33d..2ab10dd 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 85.3, + "coverage_score": 84.6, "exclude_path": "", "crate_features": "" } diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index b0a9b03..62486bc 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -56,7 +56,10 @@ pub enum VcpuExit<'a> { /// Corresponds to KVM_EXIT_SHUTDOWN. Shutdown, /// Corresponds to KVM_EXIT_FAIL_ENTRY. - FailEntry, + FailEntry( + u64, /* hardware_entry_failure_reason */ + u32, /* cpu */ + ), /// Corresponds to KVM_EXIT_INTR. Intr, /// Corresponds to KVM_EXIT_SET_TPR. @@ -1377,7 +1380,15 @@ impl VcpuFd { } KVM_EXIT_IRQ_WINDOW_OPEN => Ok(VcpuExit::IrqWindowOpen), KVM_EXIT_SHUTDOWN => Ok(VcpuExit::Shutdown), - KVM_EXIT_FAIL_ENTRY => Ok(VcpuExit::FailEntry), + KVM_EXIT_FAIL_ENTRY => { + // Safe because the exit_reason (which comes from the kernel) told us which + // union field to use. + let fail_entry = unsafe { &mut run.__bindgen_anon_1.fail_entry }; + Ok(VcpuExit::FailEntry( + fail_entry.hardware_entry_failure_reason, + fail_entry.cpu, + )) + } KVM_EXIT_INTR => Ok(VcpuExit::Intr), KVM_EXIT_SET_TPR => Ok(VcpuExit::SetTpr), KVM_EXIT_TPR_ACCESS => Ok(VcpuExit::TprAccess), From 9b64903c86b1fbae0b9cd90bee58ee51241c79d7 Mon Sep 17 00:00:00 2001 From: Nuno Das Neves Date: Thu, 23 Jun 2022 20:13:50 +0000 Subject: [PATCH 193/332] ioctls: vm: Add register_irqfd_with_resample Signed-off-by: Nuno Das Neves --- src/ioctls/vm.rs | 89 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 2cee030..62c30bd 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -896,6 +896,65 @@ impl VmFd { } } + /// Registers an event that will, when signaled, assert the `gsi` IRQ. + /// If the irqchip is resampled by the guest, the IRQ is de-asserted, + /// and `resamplefd` is notified. + /// + /// # Arguments + /// + /// * `fd` - `EventFd` to be signaled. + /// * `resamplefd` - `EventFd`to be notified on resample. + /// * `gsi` - IRQ to be triggered. + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// # extern crate libc; + /// # extern crate vmm_sys_util; + /// # use kvm_ioctls::Kvm; + /// # use libc::EFD_NONBLOCK; + /// # use vmm_sys_util::eventfd::EventFd; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let evtfd = EventFd::new(EFD_NONBLOCK).unwrap(); + /// let resamplefd = EventFd::new(EFD_NONBLOCK).unwrap(); + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// { + /// vm.create_irq_chip().unwrap(); + /// vm.register_irqfd_with_resample(&evtfd, &resamplefd, 0) + /// .unwrap(); + /// } + /// ``` + #[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" + ))] + pub fn register_irqfd_with_resample( + &self, + fd: &EventFd, + resamplefd: &EventFd, + gsi: u32, + ) -> Result<()> { + let irqfd = kvm_irqfd { + fd: fd.as_raw_fd() as u32, + resamplefd: resamplefd.as_raw_fd() as u32, + gsi, + flags: KVM_IRQFD_FLAG_RESAMPLE, + ..Default::default() + }; + // Safe because we know that our file is a VM fd, we know the kernel will only read the + // correct amount of memory from our pointer, and we verify the return result. + let ret = unsafe { ioctl_with_ref(self, KVM_IRQFD(), &irqfd) }; + if ret == 0 { + Ok(()) + } else { + Err(errno::Error::last()) + } + } + /// Unregisters an event that will, when signaled, trigger the `gsi` IRQ. /// /// # Arguments @@ -915,11 +974,15 @@ impl VmFd { /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// let evtfd = EventFd::new(EFD_NONBLOCK).unwrap(); + /// let resamplefd = EventFd::new(EFD_NONBLOCK).unwrap(); /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] /// { /// vm.create_irq_chip().unwrap(); /// vm.register_irqfd(&evtfd, 0).unwrap(); /// vm.unregister_irqfd(&evtfd, 0).unwrap(); + /// vm.register_irqfd_with_resample(&evtfd, &resamplefd, 0) + /// .unwrap(); + /// vm.unregister_irqfd(&evtfd, 0).unwrap(); /// } /// ``` #[cfg(any( @@ -1792,6 +1855,8 @@ mod tests { let evtfd1 = EventFd::new(EFD_NONBLOCK).unwrap(); let evtfd2 = EventFd::new(EFD_NONBLOCK).unwrap(); let evtfd3 = EventFd::new(EFD_NONBLOCK).unwrap(); + let evtfd4 = EventFd::new(EFD_NONBLOCK).unwrap(); + let resamplefd = EventFd::new(EFD_NONBLOCK).unwrap(); assert!(vm_fd.create_irq_chip().is_ok()); @@ -1808,6 +1873,17 @@ mod tests { assert!(vm_fd.register_irqfd(&evtfd3, 5).is_err()); // KVM irqfd doesn't report failure on this case:( assert!(vm_fd.unregister_irqfd(&evtfd3, 5).is_ok()); + + if vm_fd.check_extension(Cap::IrqfdResample) { + assert!(vm_fd + .register_irqfd_with_resample(&evtfd4, &resamplefd, 6) + .is_ok()); + assert!(vm_fd.unregister_irqfd(&evtfd4, 6).is_ok()); + } else { + assert!(vm_fd + .register_irqfd_with_resample(&evtfd4, &resamplefd, 6) + .is_err()); + } } #[test] @@ -1818,6 +1894,8 @@ mod tests { let evtfd1 = EventFd::new(EFD_NONBLOCK).unwrap(); let evtfd2 = EventFd::new(EFD_NONBLOCK).unwrap(); let evtfd3 = EventFd::new(EFD_NONBLOCK).unwrap(); + let evtfd4 = EventFd::new(EFD_NONBLOCK).unwrap(); + let resamplefd = EventFd::new(EFD_NONBLOCK).unwrap(); // Create the vGIC device. let vgic_fd = create_gic_device(&vm_fd, 0); @@ -1845,6 +1923,17 @@ mod tests { assert!(vm_fd.register_irqfd(&evtfd3, 5).is_err()); // KVM irqfd doesn't report failure on this case:( assert!(vm_fd.unregister_irqfd(&evtfd3, 5).is_ok()); + + if vm_fd.check_extension(Cap::IrqfdResample) { + assert!(vm_fd + .register_irqfd_with_resample(&evtfd4, &resamplefd, 6) + .is_ok()); + assert!(vm_fd.unregister_irqfd(&evtfd4, 6).is_ok()); + } else { + assert!(vm_fd + .register_irqfd_with_resample(&evtfd4, &resamplefd, 6) + .is_err()); + } } #[test] From 63f2f1231d49478197e15b8e9ff5d4e827aa288f Mon Sep 17 00:00:00 2001 From: Henry Wang Date: Mon, 27 Jun 2022 22:25:47 -0400 Subject: [PATCH 194/332] vcpu: Fix Arm target_arch gate for set_guest_debug KVM_SET_GUEST_DEBUG supports arm64 architecture, but the correct target_arch gate name in Rust should be aarch64 instead of arm64. This commit does the fix for set_guest_debug() so that other crates that depend on the kvm-ioctls crate can be benefited. Signed-off-by: Henry Wang --- src/ioctls/vcpu.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 62486bc..c75a40c 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1157,17 +1157,15 @@ impl VcpuFd { /// let vm = kvm.create_vm().unwrap(); /// let vcpu = vm.create_vcpu(0).unwrap(); /// - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// #[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))] /// { /// let debug_struct = kvm_guest_debug { /// // Configure the vcpu so that a KVM_DEBUG_EXIT would be generated /// // when encountering a software breakpoint during execution /// control: KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP, /// pad: 0, - /// // Reset all x86-specific debug registers - /// arch: kvm_guest_debug_arch { - /// debugreg: [0, 0, 0, 0, 0, 0, 0, 0], - /// }, + /// // Reset all arch-specific debug registers + /// arch: Default::default(), /// }; /// /// vcpu.set_guest_debug(&debug_struct).unwrap(); @@ -1176,7 +1174,7 @@ impl VcpuFd { #[cfg(any( target_arch = "x86", target_arch = "x86_64", - target_arch = "arm64", + target_arch = "aarch64", target_arch = "s390", target_arch = "ppc" ))] From 202c2d448c70f25bc0b8a60c61b433c233645256 Mon Sep 17 00:00:00 2001 From: Michael Zhao Date: Thu, 18 Aug 2022 22:52:15 +0800 Subject: [PATCH 195/332] Add `KVM_CAP_GUEST_DEBUG_HW_BPS/WPS` The API `KVM_CAP_GUEST_DEBUG_HW_BPS` and `KVM_CAP_GUEST_DEBUG_HW_WPS` are required to determine the number of supported guest debug registers. Signed-off-by: Michael Zhao --- src/cap.rs | 2 ++ src/ioctls/system.rs | 31 +++++++++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/cap.rs b/src/cap.rs index f44d41f..fbeb8b7 100644 --- a/src/cap.rs +++ b/src/cap.rs @@ -144,4 +144,6 @@ pub enum Cap { MsiDevid = KVM_CAP_MSI_DEVID, HypervSynic = KVM_CAP_HYPERV_SYNIC, HypervSynic2 = KVM_CAP_HYPERV_SYNIC2, + DebugHwBps = KVM_CAP_GUEST_DEBUG_HW_BPS, + DebugHwWps = KVM_CAP_GUEST_DEBUG_HW_WPS, } diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index 3199a6d..1c8be93 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -97,11 +97,27 @@ impl Kvm { /// AArch64 specific call to get the host Intermediate Physical Address space limit. /// /// Returns 0 if the capability is not available and an integer >= 32 otherwise. - #[cfg(any(target_arch = "aarch64"))] + #[cfg(target_arch = "aarch64")] pub fn get_host_ipa_limit(&self) -> i32 { self.check_extension_int(Cap::ArmVmIPASize) } + /// AArch64 specific call to get the number of supported hardware breakpoints. + /// + /// Returns 0 if the capability is not available and a positive integer otherwise. + #[cfg(target_arch = "aarch64")] + pub fn get_guest_debug_hw_bps(&self) -> i32 { + self.check_extension_int(Cap::DebugHwBps) + } + + /// AArch64 specific call to get the number of supported hardware watchpoints. + /// + /// Returns 0 if the capability is not available and a positive integer otherwise. + #[cfg(target_arch = "aarch64")] + pub fn get_guest_debug_hw_wps(&self) -> i32 { + self.check_extension_int(Cap::DebugHwWps) + } + /// Wrapper over `KVM_CHECK_EXTENSION`. /// /// Returns 0 if the capability is not available and a positive integer otherwise. @@ -571,7 +587,7 @@ mod tests { } #[test] - #[cfg(any(target_arch = "aarch64"))] + #[cfg(target_arch = "aarch64")] fn test_get_host_ipa_limit() { let kvm = Kvm::new().unwrap(); let host_ipa_limit = kvm.get_host_ipa_limit(); @@ -584,6 +600,17 @@ mod tests { } } + #[test] + #[cfg(target_arch = "aarch64")] + fn test_guest_debug_hw_capacity() { + let kvm = Kvm::new().unwrap(); + // The number of supported breakpoints and watchpoints may vary on + // different platforms. + // It could be 0 if no supported, or any positive integer otherwise. + assert!(kvm.get_guest_debug_hw_bps() >= 0); + assert!(kvm.get_guest_debug_hw_wps() >= 0); + } + #[test] fn test_kvm_getters() { let kvm = Kvm::new().unwrap(); From 6705a619970fb0b2be47f1781f2ccf856cd5ce6a Mon Sep 17 00:00:00 2001 From: Michael Zhao Date: Sun, 21 Aug 2022 14:09:05 +0800 Subject: [PATCH 196/332] Expose `Kvm::check_extension_int()` The function is useful for checking the capabilities that return numbers rather than booleans. Signed-off-by: Michael Zhao --- src/ioctls/system.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index 1c8be93..7659fac 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -121,7 +121,22 @@ impl Kvm { /// Wrapper over `KVM_CHECK_EXTENSION`. /// /// Returns 0 if the capability is not available and a positive integer otherwise. - fn check_extension_int(&self, c: Cap) -> i32 { + /// See the documentation for `KVM_CHECK_EXTENSION`. + /// + /// # Arguments + /// + /// * `c` - KVM capability to check. + /// + /// # Example + /// + /// ``` + /// # use kvm_ioctls::Kvm; + /// use kvm_ioctls::Cap; + /// + /// let kvm = Kvm::new().unwrap(); + /// assert!(kvm.check_extension_int(Cap::MaxVcpuId) > 0); + /// ``` + pub fn check_extension_int(&self, c: Cap) -> i32 { // Safe because we know that our file is a KVM fd and that the extension is one of the ones // defined by kernel. unsafe { ioctl_with_val(self, KVM_CHECK_EXTENSION(), c as c_ulong) } From 87560c653bb481be82e31b7fd469c1fb29876742 Mon Sep 17 00:00:00 2001 From: wanglei01 Date: Wed, 24 Aug 2022 17:22:13 +0800 Subject: [PATCH 197/332] update dependency requirements Use caret requirements for dependencies. Fixes: community/issues/131 Signed-off-by: wanglei01 --- Cargo.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index aa6d12b..663d676 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,9 +9,9 @@ keywords = ["kvm"] license = "Apache-2.0 OR MIT" [dependencies] -libc = ">=0.2.39" -kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } -vmm-sys-util = ">=0.8.0" +libc = "0.2.39" +kvm-bindings = { version = "0.5.0", features = ["fam-wrappers"] } +vmm-sys-util = "0.10.0" [dev-dependencies] -byteorder = ">=1.2.1" +byteorder = "1.2.1" From d7b52c125ce099cf6c42bfceb83e1114d6fe2dfd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 10:04:12 +0000 Subject: [PATCH 198/332] Bump rust-vmm-ci from `99fe2eb` to `258161e` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `99fe2eb` to `258161e`. - [Release notes](https://github.com/rust-vmm/rust-vmm-ci/releases) - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/99fe2eb2e05d1b2cbeed6fb00b754e8f1c5b2f81...258161e88af86e1fb1fb188de25fd0e1f9d26d0a) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 99fe2eb..258161e 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 99fe2eb2e05d1b2cbeed6fb00b754e8f1c5b2f81 +Subproject commit 258161e88af86e1fb1fb188de25fd0e1f9d26d0a From 9e98f95b425fb793153cbe444044d86cf4e872b9 Mon Sep 17 00:00:00 2001 From: Nguyen Dinh Phi Date: Sun, 11 Sep 2022 18:54:34 +0800 Subject: [PATCH 199/332] Upgrade to version 2021 Signed-off-by: Nguyen Dinh Phi --- Cargo.toml | 1 + src/ioctls/device.rs | 8 ++++---- src/ioctls/system.rs | 8 ++++---- src/ioctls/vcpu.rs | 8 ++++---- src/ioctls/vm.rs | 16 ++++++++-------- 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 663d676..fd1cc5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ repository = "https://github.com/rust-vmm/kvm-ioctls" readme = "README.md" keywords = ["kvm"] license = "Apache-2.0 OR MIT" +edition = "2021" [dependencies] libc = "0.2.39" diff --git a/src/ioctls/device.rs b/src/ioctls/device.rs index 0562304..6ed95d0 100644 --- a/src/ioctls/device.rs +++ b/src/ioctls/device.rs @@ -4,9 +4,9 @@ use std::fs::File; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; -use ioctls::Result; +use crate::ioctls::Result; +use crate::kvm_ioctls::{KVM_GET_DEVICE_ATTR, KVM_HAS_DEVICE_ATTR, KVM_SET_DEVICE_ATTR}; use kvm_bindings::kvm_device_attr; -use kvm_ioctls::{KVM_GET_DEVICE_ATTR, KVM_HAS_DEVICE_ATTR, KVM_SET_DEVICE_ATTR}; use vmm_sys_util::errno; use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref}; @@ -178,7 +178,7 @@ impl FromRawFd for DeviceFd { #[cfg(test)] mod tests { use super::*; - use ioctls::system::Kvm; + use crate::ioctls::system::Kvm; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use kvm_bindings::{ kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, kvm_device_type_KVM_DEV_TYPE_VFIO, @@ -238,7 +238,7 @@ mod tests { #[test] #[cfg(target_arch = "aarch64")] fn test_create_device() { - use ioctls::vm::{create_gic_device, set_supported_nr_irqs}; + use crate::ioctls::vm::{create_gic_device, set_supported_nr_irqs}; use kvm_bindings::{ kvm_device_type_KVM_DEV_TYPE_FSL_MPIC_20, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, }; diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index 7659fac..fcea7e3 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -9,14 +9,14 @@ use std::fs::File; use std::os::raw::{c_char, c_ulong}; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; -use cap::Cap; -use ioctls::vm::{new_vmfd, VmFd}; -use ioctls::Result; +use crate::cap::Cap; +use crate::ioctls::vm::{new_vmfd, VmFd}; +use crate::ioctls::Result; +use crate::kvm_ioctls::*; #[cfg(any(target_arch = "aarch64"))] use kvm_bindings::KVM_VM_TYPE_ARM_IPA_SIZE_MASK; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use kvm_bindings::{CpuId, MsrList, KVM_MAX_CPUID_ENTRIES, KVM_MAX_MSR_ENTRIES}; -use kvm_ioctls::*; use vmm_sys_util::errno; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use vmm_sys_util::ioctl::ioctl_with_mut_ptr; diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index c75a40c..8b2d766 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -10,10 +10,10 @@ use libc::EINVAL; use std::fs::File; use std::os::unix::io::{AsRawFd, RawFd}; -use ioctls::{KvmRunWrapper, Result}; +use crate::ioctls::{KvmRunWrapper, Result}; +use crate::kvm_ioctls::*; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use kvm_bindings::{CpuId, Msrs, KVM_MAX_CPUID_ENTRIES}; -use kvm_ioctls::*; use vmm_sys_util::errno; use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ref}; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] @@ -1696,14 +1696,14 @@ mod tests { extern crate byteorder; use super::*; - use ioctls::system::Kvm; #[cfg(any( target_arch = "x86", target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64" ))] - use Cap; + use crate::cap::Cap; + use crate::ioctls::system::Kvm; // Helper function for memory mapping `size` bytes of anonymous memory. // Panics if the mmap fails. diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 62c30bd..6bba2b7 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -11,13 +11,13 @@ use std::os::raw::c_void; use std::os::raw::{c_int, c_ulong}; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; -use cap::Cap; -use ioctls::device::new_device; -use ioctls::device::DeviceFd; -use ioctls::vcpu::new_vcpu; -use ioctls::vcpu::VcpuFd; -use ioctls::{KvmRunWrapper, Result}; -use kvm_ioctls::*; +use crate::cap::Cap; +use crate::ioctls::device::new_device; +use crate::ioctls::device::DeviceFd; +use crate::ioctls::vcpu::new_vcpu; +use crate::ioctls::vcpu::VcpuFd; +use crate::ioctls::{KvmRunWrapper, Result}; +use crate::kvm_ioctls::*; use vmm_sys_util::errno; use vmm_sys_util::eventfd::EventFd; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] @@ -1649,7 +1649,7 @@ pub(crate) fn request_gic_init(vgic: &DeviceFd) { #[cfg(test)] mod tests { use super::*; - use Kvm; + use crate::Kvm; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use std::{fs::OpenOptions, ptr::null_mut}; From e6739aeebcc56dfea62216b2cc4cddd13d0a1182 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Fri, 12 Aug 2022 12:14:42 +0000 Subject: [PATCH 200/332] ioctls: use u128 in get/set_one_reg Arm's register size can go up to 128-bit. Signed-off-by: Wei Liu --- src/ioctls/vcpu.rs | 22 +++++++++++----------- src/ioctls/vm.rs | 4 ++-- src/lib.rs | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 8b2d766..16edefc 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1196,11 +1196,11 @@ impl VcpuFd { /// * `reg_id` - ID of the register for which we are setting the value. /// * `data` - value for the specified register. #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] - pub fn set_one_reg(&self, reg_id: u64, data: u64) -> Result<()> { - let data_ref = &data as *const u64; + pub fn set_one_reg(&self, reg_id: u64, data: u128) -> Result<()> { + let data_ptr = &data as *const _; let onereg = kvm_one_reg { id: reg_id, - addr: data_ref as u64, + addr: data_ptr as u64, }; // This is safe because we allocated the struct and we know the kernel will read // exactly the size of the struct. @@ -1220,11 +1220,11 @@ impl VcpuFd { /// /// * `reg_id` - ID of the register. #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] - pub fn get_one_reg(&self, reg_id: u64) -> Result { + pub fn get_one_reg(&self, reg_id: u64) -> Result { let mut reg_value = 0; let mut onereg = kvm_one_reg { id: reg_id, - addr: &mut reg_value as *mut u64 as u64, + addr: &mut reg_value as *mut _ as u64, }; let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_ONE_REG(), &mut onereg) }; @@ -2074,15 +2074,15 @@ mod tests { // Set the PC to the guest address where we loaded the code. vcpu_fd - .set_one_reg(core_reg_base + 2 * 32, guest_addr) + .set_one_reg(core_reg_base + 2 * 32, guest_addr as u128) .unwrap(); // Set x8 and x9 to the addresses the guest test code needs vcpu_fd - .set_one_reg(core_reg_base + 2 * 8, guest_addr + 0x10000) + .set_one_reg(core_reg_base + 2 * 8, guest_addr as u128 + 0x10000) .unwrap(); vcpu_fd - .set_one_reg(core_reg_base + 2 * 9, mmio_addr) + .set_one_reg(core_reg_base + 2 * 9, mmio_addr as u128) .unwrap(); loop { @@ -2416,7 +2416,7 @@ mod tests { vm.get_preferred_target(&mut kvi) .expect("Cannot get preferred target"); vcpu.vcpu_init(&kvi).expect("Cannot initialize vcpu"); - let data: u64 = 0; + let data: u128 = 0; let reg_id: u64 = 0; assert!(vcpu.set_one_reg(reg_id, data).is_err()); @@ -2448,7 +2448,7 @@ mod tests { const PSR_D_BIT: u64 = 0x0000_0200; const PSTATE_FAULT_BITS_64: u64 = PSR_MODE_EL1H | PSR_A_BIT | PSR_F_BIT | PSR_I_BIT | PSR_D_BIT; - let data: u64 = PSTATE_FAULT_BITS_64; + let data: u128 = PSTATE_FAULT_BITS_64 as u128; const PSTATE_REG_ID: u64 = 0x6030_0000_0010_0042; vcpu.set_one_reg(PSTATE_REG_ID, data) .expect("Failed to set pstate register"); @@ -2456,7 +2456,7 @@ mod tests { assert_eq!( vcpu.get_one_reg(PSTATE_REG_ID) .expect("Failed to get pstate register"), - PSTATE_FAULT_BITS_64 + PSTATE_FAULT_BITS_64 as u128 ); } diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 6bba2b7..df4b22e 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -796,8 +796,8 @@ impl VmFd { /// /// let core_reg_base: u64 = 0x6030_0000_0010_0000; /// let mmio_addr: u64 = guest_addr + mem_size as u64; - /// vcpu_fd.set_one_reg(core_reg_base + 2 * 32, guest_addr); // set PC - /// vcpu_fd.set_one_reg(core_reg_base + 2 * 0, mmio_addr); // set X0 + /// vcpu_fd.set_one_reg(core_reg_base + 2 * 32, guest_addr as u128); // set PC + /// vcpu_fd.set_one_reg(core_reg_base + 2 * 0, mmio_addr as u128); // set X0 /// } /// /// loop { diff --git a/src/lib.rs b/src/lib.rs index 6969c7a..332200f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -156,8 +156,8 @@ //! //! let core_reg_base: u64 = 0x6030_0000_0010_0000; //! let mmio_addr: u64 = guest_addr + mem_size as u64; -//! vcpu_fd.set_one_reg(core_reg_base + 2 * 32, guest_addr); // set PC -//! vcpu_fd.set_one_reg(core_reg_base + 2 * 0, mmio_addr); // set X0 +//! vcpu_fd.set_one_reg(core_reg_base + 2 * 32, guest_addr as u128); // set PC +//! vcpu_fd.set_one_reg(core_reg_base + 2 * 0, mmio_addr as u128); // set X0 //! } //! //! // 6. Run code on the vCPU. From e40595b57d87b71e568326452529720b8cd3e83a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 10:01:36 +0000 Subject: [PATCH 201/332] Bump rust-vmm-ci from `258161e` to `5f36cc9` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `258161e` to `5f36cc9`. - [Release notes](https://github.com/rust-vmm/rust-vmm-ci/releases) - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/258161e88af86e1fb1fb188de25fd0e1f9d26d0a...5f36cc9604fbe8beb2dc9cc27d2a551300b741a5) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 258161e..5f36cc9 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 258161e88af86e1fb1fb188de25fd0e1f9d26d0a +Subproject commit 5f36cc9604fbe8beb2dc9cc27d2a551300b741a5 From 6d3ffc829e0f3f9066a9988174c77033be94662f Mon Sep 17 00:00:00 2001 From: khenidak Date: Tue, 1 Nov 2022 18:03:35 +0000 Subject: [PATCH 202/332] rev container image to match toolchain Signed-off-by: khenidak --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2a6effd..faedf93 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ docker run --device=/dev/kvm \ -it \ --security-opt seccomp=unconfined \ --volume $(pwd)/kvm-ioctls:/kvm-ioctls \ - rustvmm/dev:v5 + rustvmm/dev:v16 cd kvm-ioctls/ cargo test ``` From ad3976ab012f79f1462f1ba952459ac98b7e0a85 Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Thu, 10 Nov 2022 12:49:01 +0000 Subject: [PATCH 203/332] Update vmm-sys-util and kvm-bindings vmm-sys-util 0.10.0 -> 0.11.0 kvm-bindings 0.5.0 -> 0.6.0 See https://github.com/rust-vmm/community/issues/136 Signed-off-by: Patrick Roy --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fd1cc5f..1dc87db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,8 +11,8 @@ edition = "2021" [dependencies] libc = "0.2.39" -kvm-bindings = { version = "0.5.0", features = ["fam-wrappers"] } -vmm-sys-util = "0.10.0" +kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } +vmm-sys-util = "0.11.0" [dev-dependencies] byteorder = "1.2.1" From b2c37b6f46b641b4cf862e63eef4a4a18f480ae0 Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Fri, 11 Nov 2022 10:31:23 +0000 Subject: [PATCH 204/332] Release kvm-ioctl v0.12.0 See https://github.com/rust-vmm/community/issues/136 Signed-off-by: Patrick Roy --- CHANGELOG.md | 37 +++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 +- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0161bca..c62833b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,40 @@ +# v0.12.0 + +## Added + +- [[#187](https://github.com/rust-vmm/kvm-ioctls/pull/187)] Support for + `KVM_SET_IDENTITY_MAP_ADDR` +- Derive Debug for all exported structs and enums +- [[#189](https://github.com/rust-vmm/kvm-ioctls/pull/189)] Expose `KVM_SET_` and + `KVM_HAS_DEVICE_ATTR` for vcpu +- [[#191](https://github.com/rust-vmm/kvm-ioctls/pull/191)] Add `KVM_TRANSLATE` support and + the `translate_gva` function that translates guest virtual address to the physical address +- [[#190](https://github.com/rust-vmm/kvm-ioctls/pull/190)] Enable usage of `sync_regs` + to allow bulk getting and setting of general purpose registers, reducing the number of + ioctls needed. +- [[#198](https://github.com/rust-vmm/kvm-ioctls/pull/198)] Return details about + `KVM_EXIT_FAIL_ENTRY` in vCPU run +- [[#199](https://github.com/rust-vmm/kvm-ioctls/pull/199)] Add `register_irqfd_with_resample` + so that `irqfd` + `resaplefd` can be registered through `KVM_IRQFD` +- [[#202](https://github.com/rust-vmm/kvm-ioctls/pull/202)] Add `KVM_CAP_GUEST_DEBUG_HVM_DPS/WPS` +- [[#202](https://github.com/rust-vmm/kvm-ioctls/pull/202)] Added `check_extension_int` + which allows checking the capabilities that return numbers instead of booleans + +## Changed + +- Updated vmm-sys-util to 0.11.0 +- Updated kvm-bindings to 0.6.0 +- Upgraded to rust 2021 edition +- Switched to specifying dependencies using caret requirements + instead of comparision requirements +- [[#195](https://github.com/rust-vmm/kvm-ioctls/pull/195)] Do not panic on unsupported + `KVM_EXIT` reason +- [[#196](https://github.com/rust-vmm/kvm-ioctls/pull/196)] Expose a mutable reference + to the `kvm_run` structure to allow proper handling of unsupported exit reasons +- [[#200](https://github.com/rust-vmm/kvm-ioctls/pull/200)] Fix wrong `target_arch` gate + preventing `set_guest_debug` from being exported on ARM +- [[#206](https://github.com/rust-vmm/kvm-ioctls/pull/206)] use `u128` in `get/set_on_reg` + # v0.11.0 ## Added diff --git a/Cargo.toml b/Cargo.toml index 1dc87db..c475ca0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kvm-ioctls" -version = "0.11.0" +version = "0.12.0" authors = ["Amazon Firecracker Team "] description = "Safe wrappers over KVM ioctls" repository = "https://github.com/rust-vmm/kvm-ioctls" From b609232f5f8b03692346139e148bc5713371b9e2 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Fri, 13 Jan 2023 11:02:24 +0100 Subject: [PATCH 205/332] update rust-vmm-ci 607c775 update container to v17 aa33f19 temporarily increase the timeout to 15 mins 45443cc add hypervisor tag for agents e8c8fc3 update docker plugin 4cb208e .buildkite: add hypervisor to special_keys 3e1f6fc autogenerate_pipeline: Add hypervisor tag 7af2e16 add support for specifying the agent queue e2e2918 generate pipeline: support for FW unknown keys b0edd17 autogenerate_pipeline: use dictionary instead of self edd221a don't allow undocumented unsafe blocks Signed-off-by: Andreea Florescu --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 5f36cc9..607c775 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 5f36cc9604fbe8beb2dc9cc27d2a551300b741a5 +Subproject commit 607c775ddec0d77a2865520d3f9f7c211d9fb7c7 From 21b9ed16065c0cac0e06150308eda497dd7e822b Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Fri, 13 Jan 2023 11:52:52 +0100 Subject: [PATCH 206/332] add safety suffix to safety comments Also added safety comments where none were already added. For tests, we ignore this requirement as in tests we might be doing unsafe things on purpose and it just adds significant overhead to comment all the blocks. Signed-off-by: Andreea Florescu --- src/ioctls/device.rs | 4 ++ src/ioctls/mod.rs | 13 ++--- src/ioctls/system.rs | 35 ++++++------- src/ioctls/vcpu.rs | 118 +++++++++++++++++++++++-------------------- src/ioctls/vm.rs | 85 ++++++++++++++++--------------- src/kvm_ioctls.rs | 1 + 6 files changed, 137 insertions(+), 119 deletions(-) diff --git a/src/ioctls/device.rs b/src/ioctls/device.rs index 6ed95d0..6669570 100644 --- a/src/ioctls/device.rs +++ b/src/ioctls/device.rs @@ -24,6 +24,7 @@ impl DeviceFd { /// /// * `device_attr` - The device attribute to be tested. `addr` field is ignored. pub fn has_device_attr(&self, device_attr: &kvm_device_attr) -> Result<()> { + // SAFETY: We are calling this function with a Device fd, and we trust the kernel. let ret = unsafe { ioctl_with_ref(self, KVM_HAS_DEVICE_ATTR(), device_attr) }; if ret != 0 { return Err(errno::Error::last()); @@ -73,6 +74,7 @@ impl DeviceFd { /// } /// ``` pub fn set_device_attr(&self, device_attr: &kvm_device_attr) -> Result<()> { + // SAFETY: We are calling this function with a Device fd, and we trust the kernel. let ret = unsafe { ioctl_with_ref(self, KVM_SET_DEVICE_ATTR(), device_attr) }; if ret != 0 { return Err(errno::Error::last()); @@ -142,6 +144,7 @@ impl DeviceFd { /// } /// ``` pub fn get_device_attr(&self, device_attr: &mut kvm_device_attr) -> Result<()> { + // SAFETY: We are calling this function with a Device fd, and we trust the kernel. let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_DEVICE_ATTR(), device_attr) }; if ret != 0 { return Err(errno::Error::last()); @@ -177,6 +180,7 @@ impl FromRawFd for DeviceFd { #[cfg(test)] mod tests { + #![allow(clippy::undocumented_unsafe_blocks)] use super::*; use crate::ioctls::system::Kvm; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] diff --git a/src/ioctls/mod.rs b/src/ioctls/mod.rs index b2b57b8..9079acc 100644 --- a/src/ioctls/mod.rs +++ b/src/ioctls/mod.rs @@ -37,11 +37,12 @@ pub struct KvmRunWrapper { mmap_size: usize, } -// Send and Sync aren't automatically inherited for the raw address pointer. +// SAFETY: Send and Sync aren't automatically inherited for the raw address pointer. // Accessing that pointer is only done through the stateless interface which // allows the object to be shared by multiple threads without a decrease in // safety. unsafe impl Send for KvmRunWrapper {} +// SAFETY: See above. unsafe impl Sync for KvmRunWrapper {} impl KvmRunWrapper { @@ -51,8 +52,8 @@ impl KvmRunWrapper { /// * `fd` - File descriptor to mmap from. /// * `size` - Size of memory region in bytes. pub fn mmap_from_fd(fd: &dyn AsRawFd, size: usize) -> Result { - // This is safe because we are creating a mapping in a place not already used by any other - // area in this process. + // SAFETY: This is safe because we are creating a mapping in a place not already used by + // any other area in this process. let addr = unsafe { libc::mmap( null_mut(), @@ -76,9 +77,9 @@ impl KvmRunWrapper { /// Returns a mutable reference to `kvm_run`. #[allow(clippy::mut_from_ref)] pub fn as_mut_ref(&self) -> &mut kvm_run { - // Safe because we know we mapped enough memory to hold the kvm_run struct because the - // kernel told us how large it was. #[allow(clippy::cast_ptr_alignment)] + // SAFETY: Safe because we know we mapped enough memory to hold the kvm_run struct because + // the kernel told us how large it was. unsafe { &mut *(self.kvm_run_ptr as *mut kvm_run) } @@ -87,7 +88,7 @@ impl KvmRunWrapper { impl Drop for KvmRunWrapper { fn drop(&mut self) { - // This is safe because we mmap the area at kvm_run_ptr ourselves, + // SAFETY: This is safe because we mmap the area at kvm_run_ptr ourselves, // and nobody else is holding a reference to it. unsafe { libc::munmap(self.kvm_run_ptr as *mut libc::c_void, self.mmap_size); diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index fcea7e3..f8b6ef2 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -41,7 +41,8 @@ impl Kvm { pub fn new() -> Result { // Open `/dev/kvm` using `O_CLOEXEC` flag. let fd = Self::open_with_cloexec(true)?; - // Safe because we verify that the fd is valid in `open_with_cloexec` and we own the fd. + // SAFETY: Safe because we verify that the fd is valid in `open_with_cloexec` and we own + // the fd. Ok(unsafe { Self::from_raw_fd(fd) }) } @@ -68,7 +69,7 @@ impl Kvm { /// ``` pub fn open_with_cloexec(close_on_exec: bool) -> Result { let open_flags = O_RDWR | if close_on_exec { O_CLOEXEC } else { 0 }; - // Safe because we give a constant nul-terminated string and verify the result. + // SAFETY: Safe because we give a constant nul-terminated string and verify the result. let ret = unsafe { open("/dev/kvm\0".as_ptr() as *const c_char, open_flags) }; if ret < 0 { Err(errno::Error::last()) @@ -89,8 +90,8 @@ impl Kvm { /// assert_eq!(kvm.get_api_version(), 12); /// ``` pub fn get_api_version(&self) -> i32 { - // Safe because we know that our file is a KVM fd and that the request is one of the ones - // defined by kernel. + // SAFETY: Safe because we know that our file is a KVM fd and that the request is one of + // the ones defined by kernel. unsafe { ioctl(self, KVM_GET_API_VERSION()) } } @@ -137,8 +138,8 @@ impl Kvm { /// assert!(kvm.check_extension_int(Cap::MaxVcpuId) > 0); /// ``` pub fn check_extension_int(&self, c: Cap) -> i32 { - // Safe because we know that our file is a KVM fd and that the extension is one of the ones - // defined by kernel. + // SAFETY: Safe because we know that our file is a KVM fd and that the extension is one of + // the ones defined by kernel. unsafe { ioctl_with_val(self, KVM_CHECK_EXTENSION(), c as c_ulong) } } @@ -177,7 +178,7 @@ impl Kvm { /// assert!(kvm.get_vcpu_mmap_size().unwrap() > 0); /// ``` pub fn get_vcpu_mmap_size(&self) -> Result { - // Safe because we know that our file is a KVM fd and we verify the return result. + // SAFETY: Safe because we know that our file is a KVM fd and we verify the return result. let res = unsafe { ioctl(self, KVM_GET_VCPU_MMAP_SIZE()) }; if res > 0 { Ok(res as usize) @@ -279,11 +280,10 @@ impl Kvm { } let mut cpuid = CpuId::new(num_entries).map_err(|_| errno::Error::new(libc::ENOMEM))?; - + // SAFETY: The kernel is trusted not to write beyond the bounds of the memory + // allocated for the struct. The limit is read from nent, which is set to the allocated + // size(num_entries) above. let ret = unsafe { - // ioctl is unsafe. The kernel is trusted not to write beyond the bounds of the memory - // allocated for the struct. The limit is read from nent, which is set to the allocated - // size(num_entries) above. ioctl_with_mut_ptr(self, kind, cpuid.as_mut_fam_struct_ptr()) }; if ret < 0 { @@ -368,10 +368,10 @@ impl Kvm { let mut msr_list = MsrList::new(KVM_MAX_MSR_ENTRIES).map_err(|_| errno::Error::new(libc::ENOMEM))?; + // SAFETY: The kernel is trusted not to write beyond the bounds of the memory + // allocated for the struct. The limit is read from nmsrs, which is set to the allocated + // size (MAX_KVM_MSR_ENTRIES) above. let ret = unsafe { - // ioctl is unsafe. The kernel is trusted not to write beyond the bounds of the memory - // allocated for the struct. The limit is read from nmsrs, which is set to the allocated - // size (MAX_KVM_MSR_ENTRIES) above. ioctl_with_mut_ptr( self, KVM_GET_MSR_INDEX_LIST(), @@ -483,11 +483,11 @@ impl Kvm { /// assert!(vm.run_size() == kvm.get_vcpu_mmap_size().unwrap()); /// ``` pub fn create_vm_with_type(&self, vm_type: u64) -> Result { - // Safe because we know `self.kvm` is a real KVM fd as this module is the only one that - // create Kvm objects. + // SAFETY: Safe because we know `self.kvm` is a real KVM fd as this module is the only one + // that create Kvm objects. let ret = unsafe { ioctl_with_val(&self.kvm, KVM_CREATE_VM(), vm_type) }; if ret >= 0 { - // Safe because we verify the value of ret and we are the owners of the fd. + // SAFETY: Safe because we verify the value of ret and we are the owners of the fd. let vm_file = unsafe { File::from_raw_fd(ret) }; let run_mmap_size = self.get_vcpu_mmap_size()?; Ok(new_vmfd(vm_file, run_mmap_size)) @@ -572,6 +572,7 @@ impl FromRawFd for Kvm { #[cfg(test)] mod tests { + #![allow(clippy::undocumented_unsafe_blocks)] use super::*; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use kvm_bindings::KVM_MAX_CPUID_ENTRIES; diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 16edefc..25689ea 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -144,9 +144,10 @@ impl VcpuFd { /// ``` #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] pub fn get_regs(&self) -> Result { - // Safe because we know that our file is a vCPU fd, we know the kernel will only read the - // correct amount of memory from our pointer, and we verify the return result. + // SAFETY: kvm_regs has only POD fields which are safe to be initialized with 0s. let mut regs = unsafe { std::mem::zeroed() }; + // SAFETY: Safe because we know that our file is a vCPU fd, we know the kernel will only + // read the correct amount of memory from our pointer, and we verify the return result. let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_REGS(), &mut regs) }; if ret != 0 { return Err(errno::Error::last()); @@ -188,6 +189,7 @@ impl VcpuFd { /// ``` #[cfg(target_arch = "aarch64")] pub fn set_device_attr(&self, device_attr: &kvm_device_attr) -> Result<()> { + // SAFETY: Safe because we call this with a Vcpu fd and we trust the kernel. let ret = unsafe { ioctl_with_ref(self, KVM_SET_DEVICE_ATTR(), device_attr) }; if ret != 0 { return Err(errno::Error::last()); @@ -227,6 +229,7 @@ impl VcpuFd { /// ``` #[cfg(target_arch = "aarch64")] pub fn has_device_attr(&self, device_attr: &kvm_device_attr) -> Result<()> { + // SAFETY: Safe because we call this with a Vcpu fd and we trust the kernel. let ret = unsafe { ioctl_with_ref(self, KVM_HAS_DEVICE_ATTR(), device_attr) }; if ret != 0 { return Err(errno::Error::last()); @@ -261,8 +264,8 @@ impl VcpuFd { /// ``` #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] pub fn set_regs(&self, regs: &kvm_regs) -> Result<()> { - // Safe because we know that our file is a vCPU fd, we know the kernel will only read the - // correct amount of memory from our pointer, and we verify the return result. + // SAFETY: Safe because we know that our file is a vCPU fd, we know the kernel will only + // read the correct amount of memory from our pointer, and we verify the return result. let ret = unsafe { ioctl_with_ref(self, KVM_SET_REGS(), regs) }; if ret != 0 { return Err(errno::Error::last()); @@ -289,10 +292,9 @@ impl VcpuFd { /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_sregs(&self) -> Result { - // Safe because we know that our file is a vCPU fd, we know the kernel will only write the - // correct amount of memory to our pointer, and we verify the return result. let mut regs = kvm_sregs::default(); - + // SAFETY: Safe because we know that our file is a vCPU fd, we know the kernel will only + // write the correct amount of memory to our pointer, and we verify the return result. let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_SREGS(), &mut regs) }; if ret != 0 { return Err(errno::Error::last()); @@ -326,8 +328,8 @@ impl VcpuFd { /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_sregs(&self, sregs: &kvm_sregs) -> Result<()> { - // Safe because we know that our file is a vCPU fd, we know the kernel will only read the - // correct amount of memory from our pointer, and we verify the return result. + // SAFETY: Safe because we know that our file is a vCPU fd, we know the kernel will only + // read the correct amount of memory from our pointer, and we verify the return result. let ret = unsafe { ioctl_with_ref(self, KVM_SET_SREGS(), sregs) }; if ret != 0 { return Err(errno::Error::last()); @@ -355,9 +357,8 @@ impl VcpuFd { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_fpu(&self) -> Result { let mut fpu = kvm_fpu::default(); - + // SAFETY: Here we trust the kernel not to read past the end of the kvm_fpu struct. let ret = unsafe { - // Here we trust the kernel not to read past the end of the kvm_fpu struct. ioctl_with_mut_ref(self, KVM_GET_FPU(), &mut fpu) }; if ret != 0 { @@ -395,8 +396,8 @@ impl VcpuFd { /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_fpu(&self, fpu: &kvm_fpu) -> Result<()> { + // SAFETY: Here we trust the kernel not to read past the end of the kvm_fpu struct. let ret = unsafe { - // Here we trust the kernel not to read past the end of the kvm_fpu struct. ioctl_with_ref(self, KVM_SET_FPU(), fpu) }; if ret < 0 { @@ -442,8 +443,8 @@ impl VcpuFd { /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_cpuid2(&self, cpuid: &CpuId) -> Result<()> { + // SAFETY: Here we trust the kernel not to read past the end of the kvm_cpuid2 struct. let ret = unsafe { - // Here we trust the kernel not to read past the end of the kvm_cpuid2 struct. ioctl_with_ptr(self, KVM_SET_CPUID2(), cpuid.as_fam_struct_ptr()) }; if ret < 0 { @@ -483,8 +484,8 @@ impl VcpuFd { } let mut cpuid = CpuId::new(num_entries).map_err(|_| errno::Error::new(libc::ENOMEM))?; + // SAFETY: Here we trust the kernel not to read past the end of the kvm_cpuid2 struct. let ret = unsafe { - // Here we trust the kernel not to read past the end of the kvm_cpuid2 struct. ioctl_with_mut_ptr(self, KVM_GET_CPUID2(), cpuid.as_mut_fam_struct_ptr()) }; if ret != 0 { @@ -529,7 +530,7 @@ impl VcpuFd { /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn enable_cap(&self, cap: &kvm_enable_cap) -> Result<()> { - // The ioctl is safe because we allocated the struct and we know the + // SAFETY: The ioctl is safe because we allocated the struct and we know the // kernel will write exactly the size of the struct. let ret = unsafe { ioctl_with_ref(self, KVM_ENABLE_CAP(), cap) }; if ret == 0 { @@ -561,9 +562,9 @@ impl VcpuFd { pub fn get_lapic(&self) -> Result { let mut klapic = kvm_lapic_state::default(); + // SAFETY: The ioctl is unsafe unless you trust the kernel not to write past the end of the + // local_apic struct. let ret = unsafe { - // The ioctl is unsafe unless you trust the kernel not to write past the end of the - // local_apic struct. ioctl_with_mut_ref(self, KVM_GET_LAPIC(), &mut klapic) }; if ret < 0 { @@ -606,8 +607,8 @@ impl VcpuFd { /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_lapic(&self, klapic: &kvm_lapic_state) -> Result<()> { + // SAFETY: The ioctl is safe because the kernel will only read from the klapic struct. let ret = unsafe { - // The ioctl is safe because the kernel will only read from the klapic struct. ioctl_with_ref(self, KVM_SET_LAPIC(), klapic) }; if ret < 0 { @@ -654,8 +655,8 @@ impl VcpuFd { /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_msrs(&self, msrs: &mut Msrs) -> Result { + // SAFETY: Here we trust the kernel not to read past the end of the kvm_msrs struct. let ret = unsafe { - // Here we trust the kernel not to read past the end of the kvm_msrs struct. ioctl_with_mut_ptr(self, KVM_GET_MSRS(), msrs.as_mut_fam_struct_ptr()) }; if ret < 0 { @@ -695,8 +696,8 @@ impl VcpuFd { /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_msrs(&self, msrs: &Msrs) -> Result { + // SAFETY: Here we trust the kernel not to read past the end of the kvm_msrs struct. let ret = unsafe { - // Here we trust the kernel not to read past the end of the kvm_msrs struct. ioctl_with_ptr(self, KVM_SET_MSRS(), msrs.as_fam_struct_ptr()) }; // KVM_SET_MSRS actually returns the number of msr entries written. @@ -734,8 +735,8 @@ impl VcpuFd { ))] pub fn get_mp_state(&self) -> Result { let mut mp_state = Default::default(); + // SAFETY: Here we trust the kernel not to read past the end of the kvm_mp_state struct. let ret = unsafe { - // Here we trust the kernel not to read past the end of the kvm_mp_state struct. ioctl_with_mut_ref(self, KVM_GET_MP_STATE(), &mut mp_state) }; if ret != 0 { @@ -773,8 +774,8 @@ impl VcpuFd { target_arch = "s390" ))] pub fn set_mp_state(&self, mp_state: kvm_mp_state) -> Result<()> { + // SAFETY: Here we trust the kernel not to read past the end of the kvm_mp_state struct. let ret = unsafe { - // Here we trust the kernel not to read past the end of the kvm_mp_state struct. ioctl_with_ref(self, KVM_SET_MP_STATE(), &mp_state) }; if ret != 0 { @@ -805,8 +806,8 @@ impl VcpuFd { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_xsave(&self) -> Result { let mut xsave = Default::default(); + // SAFETY: Here we trust the kernel not to read past the end of the kvm_xsave struct. let ret = unsafe { - // Here we trust the kernel not to read past the end of the kvm_xsave struct. ioctl_with_mut_ref(self, KVM_GET_XSAVE(), &mut xsave) }; if ret != 0 { @@ -838,8 +839,8 @@ impl VcpuFd { /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_xsave(&self, xsave: &kvm_xsave) -> Result<()> { + // SAFETY: Here we trust the kernel not to read past the end of the kvm_xsave struct. let ret = unsafe { - // Here we trust the kernel not to read past the end of the kvm_xsave struct. ioctl_with_ref(self, KVM_SET_XSAVE(), xsave) }; if ret != 0 { @@ -870,8 +871,8 @@ impl VcpuFd { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_xcrs(&self) -> Result { let mut xcrs = Default::default(); + // SAFETY: Here we trust the kernel not to read past the end of the kvm_xcrs struct. let ret = unsafe { - // Here we trust the kernel not to read past the end of the kvm_xcrs struct. ioctl_with_mut_ref(self, KVM_GET_XCRS(), &mut xcrs) }; if ret != 0 { @@ -903,8 +904,8 @@ impl VcpuFd { /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_xcrs(&self, xcrs: &kvm_xcrs) -> Result<()> { + // SAFETY: Here we trust the kernel not to read past the end of the kvm_xcrs struct. let ret = unsafe { - // Here we trust the kernel not to read past the end of the kvm_xcrs struct. ioctl_with_ref(self, KVM_SET_XCRS(), xcrs) }; if ret != 0 { @@ -935,8 +936,8 @@ impl VcpuFd { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_debug_regs(&self) -> Result { let mut debug_regs = Default::default(); + // SAFETY: Here we trust the kernel not to read past the end of the kvm_debugregs struct. let ret = unsafe { - // Here we trust the kernel not to read past the end of the kvm_debugregs struct. ioctl_with_mut_ref(self, KVM_GET_DEBUGREGS(), &mut debug_regs) }; if ret != 0 { @@ -968,8 +969,8 @@ impl VcpuFd { /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_debug_regs(&self, debug_regs: &kvm_debugregs) -> Result<()> { + // SAFETY: Here we trust the kernel not to read past the end of the kvm_debugregs struct. let ret = unsafe { - // Here we trust the kernel not to read past the end of the kvm_debugregs struct. ioctl_with_ref(self, KVM_SET_DEBUGREGS(), debug_regs) }; if ret != 0 { @@ -1008,8 +1009,8 @@ impl VcpuFd { ))] pub fn get_vcpu_events(&self) -> Result { let mut vcpu_events = Default::default(); + // SAFETY: Here we trust the kernel not to read past the end of the kvm_vcpu_events struct. let ret = unsafe { - // Here we trust the kernel not to read past the end of the kvm_vcpu_events struct. ioctl_with_mut_ref(self, KVM_GET_VCPU_EVENTS(), &mut vcpu_events) }; if ret != 0 { @@ -1049,8 +1050,8 @@ impl VcpuFd { ))] pub fn set_vcpu_events(&self, vcpu_events: &kvm_vcpu_events) -> Result<()> { + // SAFETY: Here we trust the kernel not to read past the end of the kvm_vcpu_events struct. let ret = unsafe { - // Here we trust the kernel not to read past the end of the kvm_vcpu_events struct. ioctl_with_ref(self, KVM_SET_VCPU_EVENTS(), vcpu_events) }; if ret != 0 { @@ -1087,7 +1088,7 @@ impl VcpuFd { /// ``` #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] pub fn vcpu_init(&self, kvi: &kvm_vcpu_init) -> Result<()> { - // This is safe because we allocated the struct and we know the kernel will read + // SAFETY: This is safe because we allocated the struct and we know the kernel will read // exactly the size of the struct. let ret = unsafe { ioctl_with_ref(self, KVM_ARM_VCPU_INIT(), kvi) }; if ret < 0 { @@ -1127,6 +1128,8 @@ impl VcpuFd { /// ``` #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] pub fn get_reg_list(&self, reg_list: &mut RegList) -> Result<()> { + // SAFETY: This is safe because we allocated the struct and we trust the kernel will read + // exactly the size of the struct. let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_REG_LIST(), reg_list.as_mut_fam_struct()) }; if ret < 0 { @@ -1179,6 +1182,7 @@ impl VcpuFd { target_arch = "ppc" ))] pub fn set_guest_debug(&self, debug_struct: &kvm_guest_debug) -> Result<()> { + // SAFETY: Safe because we allocated the structure and we trust the kernel. let ret = unsafe { ioctl_with_ref(self, KVM_SET_GUEST_DEBUG(), debug_struct) }; if ret < 0 { return Err(errno::Error::last()); @@ -1202,7 +1206,7 @@ impl VcpuFd { id: reg_id, addr: data_ptr as u64, }; - // This is safe because we allocated the struct and we know the kernel will read + // SAFETY: This is safe because we allocated the struct and we know the kernel will read // exactly the size of the struct. let ret = unsafe { ioctl_with_ref(self, KVM_SET_ONE_REG(), &onereg) }; if ret < 0 { @@ -1226,7 +1230,8 @@ impl VcpuFd { id: reg_id, addr: &mut reg_value as *mut _ as u64, }; - + // SAFETY: This is safe because we allocated the struct and we know the kernel will read + // exactly the size of the struct. let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_ONE_REG(), &mut onereg) }; if ret < 0 { return Err(errno::Error::last()); @@ -1240,7 +1245,7 @@ impl VcpuFd { /// [KVM API documentation](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn kvmclock_ctrl(&self) -> Result<()> { - // Safe because we know that our file is a KVM fd and that the request + // SAFETY: Safe because we know that our file is a KVM fd and that the request // is one of the ones defined by kernel. let ret = unsafe { ioctl(self, KVM_KVMCLOCK_CTRL()) }; if ret != 0 { @@ -1325,7 +1330,7 @@ impl VcpuFd { /// } /// ``` pub fn run(&self) -> Result { - // Safe because we know that our file is a vCPU fd and we verify the return result. + // SAFETY: Safe because we know that our file is a vCPU fd and we verify the return result. let ret = unsafe { ioctl(self, KVM_RUN()) }; if ret == 0 { let run = self.kvm_run_ptr.as_mut_ref(); @@ -1336,15 +1341,15 @@ impl VcpuFd { KVM_EXIT_EXCEPTION => Ok(VcpuExit::Exception), KVM_EXIT_IO => { let run_start = run as *mut kvm_run as *mut u8; - // Safe because the exit_reason (which comes from the kernel) told us which - // union field to use. + // SAFETY: Safe because the exit_reason (which comes from the kernel) told us + // which union field to use. let io = unsafe { run.__bindgen_anon_1.io }; let port = io.port; let data_size = io.count as usize * io.size as usize; - // The data_offset is defined by the kernel to be some number of bytes into the - // kvm_run stucture, which we have fully mmap'd. + // SAFETY: The data_offset is defined by the kernel to be some number of bytes + // into the kvm_run stucture, which we have fully mmap'd. let data_ptr = unsafe { run_start.offset(io.data_offset as isize) }; - // The slice's lifetime is limited to the lifetime of this vCPU, which is equal + // SAFETY: The slice's lifetime is limited to the lifetime of this vCPU, which is equal // to the mmap of the `kvm_run` struct that this is slicing from. let data_slice = unsafe { std::slice::from_raw_parts_mut::(data_ptr as *mut u8, data_size) @@ -1357,15 +1362,15 @@ impl VcpuFd { } KVM_EXIT_HYPERCALL => Ok(VcpuExit::Hypercall), KVM_EXIT_DEBUG => { - // Safe because the exit_reason (which comes from the kernel) told us which - // union field to use. + // SAFETY: Safe because the exit_reason (which comes from the kernel) told us + // which union field to use. let debug = unsafe { run.__bindgen_anon_1.debug }; Ok(VcpuExit::Debug(debug.arch)) } KVM_EXIT_HLT => Ok(VcpuExit::Hlt), KVM_EXIT_MMIO => { - // Safe because the exit_reason (which comes from the kernel) told us which - // union field to use. + // SAFETY: Safe because the exit_reason (which comes from the kernel) told us + // which union field to use. let mmio = unsafe { &mut run.__bindgen_anon_1.mmio }; let addr = mmio.phys_addr; let len = mmio.len as usize; @@ -1379,8 +1384,8 @@ impl VcpuFd { KVM_EXIT_IRQ_WINDOW_OPEN => Ok(VcpuExit::IrqWindowOpen), KVM_EXIT_SHUTDOWN => Ok(VcpuExit::Shutdown), KVM_EXIT_FAIL_ENTRY => { - // Safe because the exit_reason (which comes from the kernel) told us which - // union field to use. + // SAFETY: Safe because the exit_reason (which comes from the kernel) told us + // which union field to use. let fail_entry = unsafe { &mut run.__bindgen_anon_1.fail_entry }; Ok(VcpuExit::FailEntry( fail_entry.hardware_entry_failure_reason, @@ -1402,8 +1407,8 @@ impl VcpuFd { KVM_EXIT_S390_TSCH => Ok(VcpuExit::S390Tsch), KVM_EXIT_EPR => Ok(VcpuExit::Epr), KVM_EXIT_SYSTEM_EVENT => { - // Safe because the exit_reason (which comes from the kernel) told us which - // union field to use. + // SAFETY: Safe because the exit_reason (which comes from the kernel) told us + // which union field to use. let system_event = unsafe { &mut run.__bindgen_anon_1.system_event }; Ok(VcpuExit::SystemEvent( system_event.type_, @@ -1412,8 +1417,8 @@ impl VcpuFd { } KVM_EXIT_S390_STSI => Ok(VcpuExit::S390Stsi), KVM_EXIT_IOAPIC_EOI => { - // Safe because the exit_reason (which comes from the kernel) told us which - // union field to use. + // SAFETY: Safe because the exit_reason (which comes from the kernel) told us + // which union field to use. let eoi = unsafe { &mut run.__bindgen_anon_1.eoi }; Ok(VcpuExit::IoapicEoi(eoi.vector)) } @@ -1451,8 +1456,8 @@ impl VcpuFd { /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_tsc_khz(&self) -> Result { - // Safe because we know that our file is a KVM fd and that the request is one of the ones - // defined by kernel. + // SAFETY: Safe because we know that our file is a KVM fd and that the request is one of + // the ones defined by kernel. let ret = unsafe { ioctl(self, KVM_GET_TSC_KHZ()) }; if ret >= 0 { Ok(ret as u32) @@ -1483,8 +1488,8 @@ impl VcpuFd { /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_tsc_khz(&self, freq: u32) -> Result<()> { - // Safe because we know that our file is a KVM fd and that the request is one of the ones - // defined by kernel. + // SAFETY: Safe because we know that our file is a KVM fd and that the request is one of + // the ones defined by kernel. let ret = unsafe { ioctl_with_val(self, KVM_SET_TSC_KHZ(), freq as u64) }; if ret < 0 { Err(errno::Error::last()) @@ -1521,8 +1526,8 @@ impl VcpuFd { ..Default::default() }; - // Safe because we know that our file is a vCPU fd, we know the kernel will only write the - // correct amount of memory to our pointer, and we verify the return result. + // SAFETY: Safe because we know that our file is a vCPU fd, we know the kernel will only + // write the correct amount of memory to our pointer, and we verify the return result. let ret = unsafe { ioctl_with_mut_ref(self, KVM_TRANSLATE(), &mut tr) }; if ret != 0 { return Err(errno::Error::last()); @@ -1693,6 +1698,7 @@ impl AsRawFd for VcpuFd { #[cfg(test)] mod tests { + #![allow(clippy::undocumented_unsafe_blocks)] extern crate byteorder; use super::*; diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index df4b22e..d9c9a2d 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -130,7 +130,7 @@ impl VmFd { /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_tss_address(&self, offset: usize) -> Result<()> { - // Safe because we know that our file is a VM fd and we verify the return result. + // SAFETY: Safe because we know that our file is a VM fd and we verify the return result. let ret = unsafe { ioctl_with_val(self, KVM_SET_TSS_ADDR(), offset as c_ulong) }; if ret == 0 { Ok(()) @@ -158,7 +158,7 @@ impl VmFd { /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_identity_map_address(&self, address: u64) -> Result<()> { - // Safe because we know that our file is a VM fd and we verify the return result. + // SAFETY: Safe because we know that our file is a VM fd and we verify the return result. let ret = unsafe { ioctl_with_ref(self, KVM_SET_IDENTITY_MAP_ADDR(), &address) }; if ret == 0 { Ok(()) @@ -204,7 +204,7 @@ impl VmFd { target_arch = "aarch64" ))] pub fn create_irq_chip(&self) -> Result<()> { - // Safe because we know that our file is a VM fd and we verify the return result. + // SAFETY: Safe because we know that our file is a VM fd and we verify the return result. let ret = unsafe { ioctl(self, KVM_CREATE_IRQCHIP()) }; if ret == 0 { Ok(()) @@ -239,8 +239,8 @@ impl VmFd { /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_irqchip(&self, irqchip: &mut kvm_irqchip) -> Result<()> { + // SAFETY: Here we trust the kernel not to read past the end of the kvm_irqchip struct. let ret = unsafe { - // Here we trust the kernel not to read past the end of the kvm_irqchip struct. ioctl_with_mut_ref(self, KVM_GET_IRQCHIP(), irqchip) }; if ret == 0 { @@ -277,8 +277,8 @@ impl VmFd { /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_irqchip(&self, irqchip: &kvm_irqchip) -> Result<()> { + // SAFETY: Here we trust the kernel not to read past the end of the kvm_irqchip struct. let ret = unsafe { - // Here we trust the kernel not to read past the end of the kvm_irqchip struct. ioctl_with_ref(self, KVM_SET_IRQCHIP(), irqchip) }; if ret == 0 { @@ -309,8 +309,8 @@ impl VmFd { /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn create_pit2(&self, pit_config: kvm_pit_config) -> Result<()> { - // Safe because we know that our file is a VM fd, we know the kernel will only read the - // correct amount of memory from our pointer, and we verify the return result. + // SAFETY: Safe because we know that our file is a VM fd, we know the kernel will only read + // the correct amount of memory from our pointer, and we verify the return result. let ret = unsafe { ioctl_with_ref(self, KVM_CREATE_PIT2(), &pit_config) }; if ret == 0 { Ok(()) @@ -345,8 +345,8 @@ impl VmFd { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_pit2(&self) -> Result { let mut pitstate = Default::default(); + // SAFETY: Here we trust the kernel not to read past the end of the kvm_pit_state2 struct. let ret = unsafe { - // Here we trust the kernel not to read past the end of the kvm_pit_state2 struct. ioctl_with_mut_ref(self, KVM_GET_PIT2(), &mut pitstate) }; if ret == 0 { @@ -383,8 +383,8 @@ impl VmFd { /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_pit2(&self, pitstate: &kvm_pit_state2) -> Result<()> { + // SAFETY: Here we trust the kernel not to read past the end of the kvm_pit_state2 struct. let ret = unsafe { - // Here we trust the kernel not to read past the end of the kvm_pit_state2 struct. ioctl_with_ref(self, KVM_SET_PIT2(), pitstate) }; if ret == 0 { @@ -415,8 +415,8 @@ impl VmFd { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_clock(&self) -> Result { let mut clock = Default::default(); + // SAFETY: Here we trust the kernel not to read past the end of the kvm_clock_data struct. let ret = unsafe { - // Here we trust the kernel not to read past the end of the kvm_clock_data struct. ioctl_with_mut_ref(self, KVM_GET_CLOCK(), &mut clock) }; if ret == 0 { @@ -449,8 +449,8 @@ impl VmFd { /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_clock(&self, clock: &kvm_clock_data) -> Result<()> { + // SAFETY: Here we trust the kernel not to read past the end of the kvm_clock_data struct. let ret = unsafe { - // Here we trust the kernel not to read past the end of the kvm_clock_data struct. ioctl_with_ref(self, KVM_SET_CLOCK(), clock) }; if ret == 0 { @@ -501,7 +501,7 @@ impl VmFd { target_arch = "aarch64" ))] pub fn signal_msi(&self, msi: kvm_msi) -> Result { - // Safe because we allocated the structure and we know the kernel + // SAFETY: Safe because we allocated the structure and we know the kernel // will read exactly the size of the structure. let ret = unsafe { ioctl_with_ref(self, KVM_SIGNAL_MSI(), &msi) }; if ret >= 0 { @@ -548,7 +548,7 @@ impl VmFd { target_arch = "aarch64" ))] pub fn set_gsi_routing(&self, irq_routing: &kvm_irq_routing) -> Result<()> { - // Safe because we allocated the structure and we know the kernel + // SAFETY: Safe because we allocated the structure and we know the kernel // will read exactly the size of the structure. let ret = unsafe { ioctl_with_ref(self, KVM_SET_GSI_ROUTING(), irq_routing) }; if ret == 0 { @@ -615,8 +615,8 @@ impl VmFd { flags, ..Default::default() }; - // Safe because we know that our file is a VM fd, we know the kernel will only read the - // correct amount of memory from our pointer, and we verify the return result. + // SAFETY: Safe because we know that our file is a VM fd, we know the kernel will only read + // the correct amount of memory from our pointer, and we verify the return result. let ret = unsafe { ioctl_with_ref(self, KVM_IOEVENTFD(), &ioeventfd) }; if ret == 0 { Ok(()) @@ -691,8 +691,8 @@ impl VmFd { flags, ..Default::default() }; - // Safe because we know that our file is a VM fd, we know the kernel will only read the - // correct amount of memory from our pointer, and we verify the return result. + // SAFETY: Safe because we know that our file is a VM fd, we know the kernel will only read + // the correct amount of memory from our pointer, and we verify the return result. let ret = unsafe { ioctl_with_ref(self, KVM_IOEVENTFD(), &ioeventfd) }; if ret == 0 { Ok(()) @@ -821,6 +821,7 @@ impl VmFd { // Compute the length of the bitmap needed for all dirty pages in one memory slot. // One memory page is `page_size` bytes and `KVM_GET_DIRTY_LOG` returns one dirty bit for // each page. + // SAFETY: We trust the sysconf libc function and we're calling it with a correct parameter. let page_size = match unsafe { libc::sysconf(libc::_SC_PAGESIZE) } { -1 => return Err(errno::Error::last()), ps => ps as usize, @@ -839,8 +840,8 @@ impl VmFd { dirty_bitmap: bitmap.as_mut_ptr() as *mut c_void, }, }; - // Safe because we know that our file is a VM fd, and we know that the amount of memory - // we allocated for the bitmap is at least one bit per page. + // SAFETY: Safe because we know that our file is a VM fd, and we know that the amount of + // memory we allocated for the bitmap is at least one bit per page. let ret = unsafe { ioctl_with_ref(self, KVM_GET_DIRTY_LOG(), &dirtylog) }; if ret == 0 { Ok(bitmap) @@ -886,8 +887,8 @@ impl VmFd { gsi, ..Default::default() }; - // Safe because we know that our file is a VM fd, we know the kernel will only read the - // correct amount of memory from our pointer, and we verify the return result. + // SAFETY: Safe because we know that our file is a VM fd, we know the kernel will only read + // the correct amount of memory from our pointer, and we verify the return result. let ret = unsafe { ioctl_with_ref(self, KVM_IRQFD(), &irqfd) }; if ret == 0 { Ok(()) @@ -945,8 +946,8 @@ impl VmFd { flags: KVM_IRQFD_FLAG_RESAMPLE, ..Default::default() }; - // Safe because we know that our file is a VM fd, we know the kernel will only read the - // correct amount of memory from our pointer, and we verify the return result. + // SAFETY: Safe because we know that our file is a VM fd, we know the kernel will only read + // the correct amount of memory from our pointer, and we verify the return result. let ret = unsafe { ioctl_with_ref(self, KVM_IRQFD(), &irqfd) }; if ret == 0 { Ok(()) @@ -998,8 +999,8 @@ impl VmFd { flags: KVM_IRQFD_FLAG_DEASSIGN, ..Default::default() }; - // Safe because we know that our file is a VM fd, we know the kernel will only read the - // correct amount of memory from our pointer, and we verify the return result. + // SAFETY: Safe because we know that our file is a VM fd, we know the kernel will only read + // the correct amount of memory from our pointer, and we verify the return result. let ret = unsafe { ioctl_with_ref(self, KVM_IRQFD(), &irqfd) }; if ret == 0 { Ok(()) @@ -1067,8 +1068,8 @@ impl VmFd { irq_level.__bindgen_anon_1.irq = irq; irq_level.level = if active { 1 } else { 0 }; - // Safe because we know that our file is a VM fd, we know the kernel will only read the - // correct amount of memory from our pointer, and we verify the return result. + // SAFETY: Safe because we know that our file is a VM fd, we know the kernel will only read + // the correct amount of memory from our pointer, and we verify the return result. let ret = unsafe { ioctl_with_ref(self, KVM_IRQ_LINE(), &irq_level) }; if ret == 0 { Ok(()) @@ -1102,15 +1103,15 @@ impl VmFd { /// let vcpu = vm.create_vcpu(0); /// ``` pub fn create_vcpu(&self, id: u64) -> Result { - // Safe because we know that vm is a VM fd and we verify the return result. #[allow(clippy::cast_lossless)] + // SAFETY: Safe because we know that vm is a VM fd and we verify the return result. let vcpu_fd = unsafe { ioctl_with_val(&self.vm, KVM_CREATE_VCPU(), id as c_ulong) }; if vcpu_fd < 0 { return Err(errno::Error::last()); } - // Wrap the vCPU now in case the following ? returns early. This is safe because we verified - // the value of the fd and we own the fd. + // Wrap the vCPU now in case the following ? returns early. + // SAFETY: This is safe because we verified the value of the fd and we own the fd. let vcpu = unsafe { File::from_raw_fd(vcpu_fd) }; let kvm_run_ptr = KvmRunWrapper::mmap_from_fd(&vcpu, self.run_size)?; @@ -1200,8 +1201,11 @@ impl VmFd { /// }); /// ``` pub fn create_device(&self, device: &mut kvm_create_device) -> Result { + // SAFETY: Safe because we are calling this with the VM fd and we trust the kernel. let ret = unsafe { ioctl_with_ref(self, KVM_CREATE_DEVICE(), device) }; if ret == 0 { + // SAFETY: We validated the return of the function creating the fd and we trust the + // kernel. Ok(new_device(unsafe { File::from_raw_fd(device.fd as i32) })) } else { Err(errno::Error::last()) @@ -1232,7 +1236,7 @@ impl VmFd { /// ``` #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] pub fn get_preferred_target(&self, kvi: &mut kvm_vcpu_init) -> Result<()> { - // The ioctl is safe because we allocated the struct and we know the + // SAFETY: The ioctl is safe because we allocated the struct and we know the // kernel will write exactly the size of the struct. let ret = unsafe { ioctl_with_mut_ref(self, KVM_ARM_PREFERRED_TARGET(), kvi) }; if ret != 0 { @@ -1286,7 +1290,7 @@ impl VmFd { /// ``` #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] pub fn enable_cap(&self, cap: &kvm_enable_cap) -> Result<()> { - // The ioctl is safe because we allocated the struct and we know the + // SAFETY: The ioctl is safe because we allocated the struct and we know the // kernel will write exactly the size of the struct. let ret = unsafe { ioctl_with_ref(self, KVM_ENABLE_CAP(), cap) }; if ret == 0 { @@ -1305,8 +1309,8 @@ impl VmFd { /// /// Returns 0 if the capability is not available and a positive integer otherwise. fn check_extension_int(&self, c: Cap) -> i32 { - // Safe because we know that our file is a VM fd and that the extension is one of the ones - // defined by kernel. + // SAFETY: Safe because we know that our file is a VM fd and that the extension is one of + // the ones defined by kernel. unsafe { ioctl_with_val(self, KVM_CHECK_EXTENSION(), c as c_ulong) } } @@ -1414,8 +1418,8 @@ impl VmFd { /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn encrypt_op_sev(&self, op: &mut kvm_sev_cmd) -> Result<()> { - // Safe because we know that kernel will only read the correct amount of memory from our pointer - // and we know where it will write it (op.error). + // SAFETY: Safe because we know that kernel will only read the correct amount of memory + // from our pointer and we know where it will write it (op.error). unsafe { self.encrypt_op(op) } } @@ -1484,8 +1488,8 @@ impl VmFd { /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn register_enc_memory_region(&self, memory_region: &kvm_enc_region) -> Result<()> { - // Safe because we know that our file is a VM fd, we know the kernel will only read the - // correct amount of memory from our pointer, and we verify the return result. + // SAFETY: Safe because we know that our file is a VM fd, we know the kernel will only read + // the correct amount of memory from our pointer, and we verify the return result. let ret = unsafe { ioctl_with_ref(self, KVM_MEMORY_ENCRYPT_REG_REGION(), memory_region) }; if ret == 0 { Ok(()) @@ -1561,8 +1565,8 @@ impl VmFd { /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn unregister_enc_memory_region(&self, memory_region: &kvm_enc_region) -> Result<()> { - // Safe because we know that our file is a VM fd, we know the kernel will only read the - // correct amount of memory from our pointer, and we verify the return result. + // SAFETY: Safe because we know that our file is a VM fd, we know the kernel will only read + // the correct amount of memory from our pointer, and we verify the return result. let ret = unsafe { ioctl_with_ref(self, KVM_MEMORY_ENCRYPT_UNREG_REGION(), memory_region) }; if ret == 0 { Ok(()) @@ -1648,6 +1652,7 @@ pub(crate) fn request_gic_init(vgic: &DeviceFd) { #[cfg(test)] mod tests { + #![allow(clippy::undocumented_unsafe_blocks)] use super::*; use crate::Kvm; diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index 25aacb0..559611a 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -254,6 +254,7 @@ ioctl_iow_nr!(KVM_HAS_DEVICE_ATTR, KVMIO, 0xe3, kvm_device_attr); #[cfg(test)] mod tests { + #![allow(clippy::undocumented_unsafe_blocks)] use std::fs::File; use std::os::unix::io::FromRawFd; From a4e0e3e820ae6fb0505188cba08b848998b0fe24 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Fri, 13 Jan 2023 11:55:48 +0100 Subject: [PATCH 207/332] use default implementation instead of using unsafe zeroed() There is no need to use unsafe code to initialize the kvm_regs structure because this one has a default implementation. Signed-off-by: Andreea Florescu --- src/ioctls/vcpu.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 25689ea..c6ad12c 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -144,8 +144,7 @@ impl VcpuFd { /// ``` #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] pub fn get_regs(&self) -> Result { - // SAFETY: kvm_regs has only POD fields which are safe to be initialized with 0s. - let mut regs = unsafe { std::mem::zeroed() }; + let mut regs = kvm_regs::default(); // SAFETY: Safe because we know that our file is a vCPU fd, we know the kernel will only // read the correct amount of memory from our pointer, and we verify the return result. let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_REGS(), &mut regs) }; From 50d03b831829892024907be666e725b035f90a38 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Fri, 13 Jan 2023 11:57:43 +0100 Subject: [PATCH 208/332] clippy fix: remove useless cast Signed-off-by: Andreea Florescu --- src/ioctls/vm.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index d9c9a2d..b9dc6fd 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -608,7 +608,7 @@ impl VmFd { datamatch: datamatch.into(), len: std::mem::size_of::() as u32, addr: match addr { - IoEventAddress::Pio(ref p) => *p as u64, + IoEventAddress::Pio(ref p) => *p, IoEventAddress::Mmio(ref m) => *m, }, fd: fd.as_raw_fd(), @@ -684,7 +684,7 @@ impl VmFd { datamatch: datamatch.into(), len: std::mem::size_of::() as u32, addr: match addr { - IoEventAddress::Pio(ref p) => *p as u64, + IoEventAddress::Pio(ref p) => *p, IoEventAddress::Mmio(ref m) => *m, }, fd: fd.as_raw_fd(), From 83f7b0ba573b7079a1c5c0ea95662faabfa047a8 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Fri, 13 Jan 2023 11:59:29 +0100 Subject: [PATCH 209/332] create irq level by using u32::from instead of if This fixes a clippy warning. Signed-off-by: Andreea Florescu --- src/ioctls/vm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index b9dc6fd..de6966f 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -1066,7 +1066,7 @@ impl VmFd { pub fn set_irq_line(&self, irq: u32, active: bool) -> Result<()> { let mut irq_level = kvm_irq_level::default(); irq_level.__bindgen_anon_1.irq = irq; - irq_level.level = if active { 1 } else { 0 }; + irq_level.level = u32::from(active); // SAFETY: Safe because we know that our file is a VM fd, we know the kernel will only read // the correct amount of memory from our pointer, and we verify the return result. From 346d472dbc327b8c78bfed2a78740ffe57a4b308 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Fri, 13 Jan 2023 12:00:36 +0100 Subject: [PATCH 210/332] remove unneeded casts in vcpu test Signed-off-by: Andreea Florescu --- src/ioctls/vcpu.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index c6ad12c..f4a2f6d 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1941,7 +1941,7 @@ mod tests { // Verify the lengths match. assert_eq!(nmsrs, msrs_to_set.len()); - assert_eq!(nmsrs, returned_kvm_msrs.as_fam_struct_ref().len() as usize); + assert_eq!(nmsrs, returned_kvm_msrs.as_fam_struct_ref().len()); // Verify the contents match. let returned_kvm_msr_entries = returned_kvm_msrs.as_slice(); From 2deee0360cdc5f1633a7b6242bb157a4b520e877 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Fri, 13 Jan 2023 14:12:32 +0100 Subject: [PATCH 211/332] run cargo fmt with new rust version Signed-off-by: Andreea Florescu --- src/ioctls/system.rs | 4 +-- src/ioctls/vcpu.rs | 73 ++++++++++++-------------------------------- src/ioctls/vm.rs | 24 ++++----------- 3 files changed, 26 insertions(+), 75 deletions(-) diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index f8b6ef2..ee1d68c 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -283,9 +283,7 @@ impl Kvm { // SAFETY: The kernel is trusted not to write beyond the bounds of the memory // allocated for the struct. The limit is read from nent, which is set to the allocated // size(num_entries) above. - let ret = unsafe { - ioctl_with_mut_ptr(self, kind, cpuid.as_mut_fam_struct_ptr()) - }; + let ret = unsafe { ioctl_with_mut_ptr(self, kind, cpuid.as_mut_fam_struct_ptr()) }; if ret < 0 { return Err(errno::Error::last()); } diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index f4a2f6d..69fa30c 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -357,9 +357,7 @@ impl VcpuFd { pub fn get_fpu(&self) -> Result { let mut fpu = kvm_fpu::default(); // SAFETY: Here we trust the kernel not to read past the end of the kvm_fpu struct. - let ret = unsafe { - ioctl_with_mut_ref(self, KVM_GET_FPU(), &mut fpu) - }; + let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_FPU(), &mut fpu) }; if ret != 0 { return Err(errno::Error::last()); } @@ -396,9 +394,7 @@ impl VcpuFd { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_fpu(&self, fpu: &kvm_fpu) -> Result<()> { // SAFETY: Here we trust the kernel not to read past the end of the kvm_fpu struct. - let ret = unsafe { - ioctl_with_ref(self, KVM_SET_FPU(), fpu) - }; + let ret = unsafe { ioctl_with_ref(self, KVM_SET_FPU(), fpu) }; if ret < 0 { return Err(errno::Error::last()); } @@ -443,9 +439,7 @@ impl VcpuFd { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_cpuid2(&self, cpuid: &CpuId) -> Result<()> { // SAFETY: Here we trust the kernel not to read past the end of the kvm_cpuid2 struct. - let ret = unsafe { - ioctl_with_ptr(self, KVM_SET_CPUID2(), cpuid.as_fam_struct_ptr()) - }; + let ret = unsafe { ioctl_with_ptr(self, KVM_SET_CPUID2(), cpuid.as_fam_struct_ptr()) }; if ret < 0 { return Err(errno::Error::last()); } @@ -484,9 +478,8 @@ impl VcpuFd { let mut cpuid = CpuId::new(num_entries).map_err(|_| errno::Error::new(libc::ENOMEM))?; // SAFETY: Here we trust the kernel not to read past the end of the kvm_cpuid2 struct. - let ret = unsafe { - ioctl_with_mut_ptr(self, KVM_GET_CPUID2(), cpuid.as_mut_fam_struct_ptr()) - }; + let ret = + unsafe { ioctl_with_mut_ptr(self, KVM_GET_CPUID2(), cpuid.as_mut_fam_struct_ptr()) }; if ret != 0 { return Err(errno::Error::last()); } @@ -563,9 +556,7 @@ impl VcpuFd { // SAFETY: The ioctl is unsafe unless you trust the kernel not to write past the end of the // local_apic struct. - let ret = unsafe { - ioctl_with_mut_ref(self, KVM_GET_LAPIC(), &mut klapic) - }; + let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_LAPIC(), &mut klapic) }; if ret < 0 { return Err(errno::Error::last()); } @@ -607,9 +598,7 @@ impl VcpuFd { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_lapic(&self, klapic: &kvm_lapic_state) -> Result<()> { // SAFETY: The ioctl is safe because the kernel will only read from the klapic struct. - let ret = unsafe { - ioctl_with_ref(self, KVM_SET_LAPIC(), klapic) - }; + let ret = unsafe { ioctl_with_ref(self, KVM_SET_LAPIC(), klapic) }; if ret < 0 { return Err(errno::Error::last()); } @@ -655,9 +644,7 @@ impl VcpuFd { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_msrs(&self, msrs: &mut Msrs) -> Result { // SAFETY: Here we trust the kernel not to read past the end of the kvm_msrs struct. - let ret = unsafe { - ioctl_with_mut_ptr(self, KVM_GET_MSRS(), msrs.as_mut_fam_struct_ptr()) - }; + let ret = unsafe { ioctl_with_mut_ptr(self, KVM_GET_MSRS(), msrs.as_mut_fam_struct_ptr()) }; if ret < 0 { return Err(errno::Error::last()); } @@ -696,9 +683,7 @@ impl VcpuFd { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_msrs(&self, msrs: &Msrs) -> Result { // SAFETY: Here we trust the kernel not to read past the end of the kvm_msrs struct. - let ret = unsafe { - ioctl_with_ptr(self, KVM_SET_MSRS(), msrs.as_fam_struct_ptr()) - }; + let ret = unsafe { ioctl_with_ptr(self, KVM_SET_MSRS(), msrs.as_fam_struct_ptr()) }; // KVM_SET_MSRS actually returns the number of msr entries written. if ret < 0 { return Err(errno::Error::last()); @@ -735,9 +720,7 @@ impl VcpuFd { pub fn get_mp_state(&self) -> Result { let mut mp_state = Default::default(); // SAFETY: Here we trust the kernel not to read past the end of the kvm_mp_state struct. - let ret = unsafe { - ioctl_with_mut_ref(self, KVM_GET_MP_STATE(), &mut mp_state) - }; + let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_MP_STATE(), &mut mp_state) }; if ret != 0 { return Err(errno::Error::last()); } @@ -774,9 +757,7 @@ impl VcpuFd { ))] pub fn set_mp_state(&self, mp_state: kvm_mp_state) -> Result<()> { // SAFETY: Here we trust the kernel not to read past the end of the kvm_mp_state struct. - let ret = unsafe { - ioctl_with_ref(self, KVM_SET_MP_STATE(), &mp_state) - }; + let ret = unsafe { ioctl_with_ref(self, KVM_SET_MP_STATE(), &mp_state) }; if ret != 0 { return Err(errno::Error::last()); } @@ -806,9 +787,7 @@ impl VcpuFd { pub fn get_xsave(&self) -> Result { let mut xsave = Default::default(); // SAFETY: Here we trust the kernel not to read past the end of the kvm_xsave struct. - let ret = unsafe { - ioctl_with_mut_ref(self, KVM_GET_XSAVE(), &mut xsave) - }; + let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_XSAVE(), &mut xsave) }; if ret != 0 { return Err(errno::Error::last()); } @@ -839,9 +818,7 @@ impl VcpuFd { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_xsave(&self, xsave: &kvm_xsave) -> Result<()> { // SAFETY: Here we trust the kernel not to read past the end of the kvm_xsave struct. - let ret = unsafe { - ioctl_with_ref(self, KVM_SET_XSAVE(), xsave) - }; + let ret = unsafe { ioctl_with_ref(self, KVM_SET_XSAVE(), xsave) }; if ret != 0 { return Err(errno::Error::last()); } @@ -871,9 +848,7 @@ impl VcpuFd { pub fn get_xcrs(&self) -> Result { let mut xcrs = Default::default(); // SAFETY: Here we trust the kernel not to read past the end of the kvm_xcrs struct. - let ret = unsafe { - ioctl_with_mut_ref(self, KVM_GET_XCRS(), &mut xcrs) - }; + let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_XCRS(), &mut xcrs) }; if ret != 0 { return Err(errno::Error::last()); } @@ -904,9 +879,7 @@ impl VcpuFd { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_xcrs(&self, xcrs: &kvm_xcrs) -> Result<()> { // SAFETY: Here we trust the kernel not to read past the end of the kvm_xcrs struct. - let ret = unsafe { - ioctl_with_ref(self, KVM_SET_XCRS(), xcrs) - }; + let ret = unsafe { ioctl_with_ref(self, KVM_SET_XCRS(), xcrs) }; if ret != 0 { return Err(errno::Error::last()); } @@ -936,9 +909,7 @@ impl VcpuFd { pub fn get_debug_regs(&self) -> Result { let mut debug_regs = Default::default(); // SAFETY: Here we trust the kernel not to read past the end of the kvm_debugregs struct. - let ret = unsafe { - ioctl_with_mut_ref(self, KVM_GET_DEBUGREGS(), &mut debug_regs) - }; + let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_DEBUGREGS(), &mut debug_regs) }; if ret != 0 { return Err(errno::Error::last()); } @@ -969,9 +940,7 @@ impl VcpuFd { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_debug_regs(&self, debug_regs: &kvm_debugregs) -> Result<()> { // SAFETY: Here we trust the kernel not to read past the end of the kvm_debugregs struct. - let ret = unsafe { - ioctl_with_ref(self, KVM_SET_DEBUGREGS(), debug_regs) - }; + let ret = unsafe { ioctl_with_ref(self, KVM_SET_DEBUGREGS(), debug_regs) }; if ret != 0 { return Err(errno::Error::last()); } @@ -1009,9 +978,7 @@ impl VcpuFd { pub fn get_vcpu_events(&self) -> Result { let mut vcpu_events = Default::default(); // SAFETY: Here we trust the kernel not to read past the end of the kvm_vcpu_events struct. - let ret = unsafe { - ioctl_with_mut_ref(self, KVM_GET_VCPU_EVENTS(), &mut vcpu_events) - }; + let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_VCPU_EVENTS(), &mut vcpu_events) }; if ret != 0 { return Err(errno::Error::last()); } @@ -1050,9 +1017,7 @@ impl VcpuFd { pub fn set_vcpu_events(&self, vcpu_events: &kvm_vcpu_events) -> Result<()> { // SAFETY: Here we trust the kernel not to read past the end of the kvm_vcpu_events struct. - let ret = unsafe { - ioctl_with_ref(self, KVM_SET_VCPU_EVENTS(), vcpu_events) - }; + let ret = unsafe { ioctl_with_ref(self, KVM_SET_VCPU_EVENTS(), vcpu_events) }; if ret != 0 { return Err(errno::Error::last()); } diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index de6966f..876ced3 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -240,9 +240,7 @@ impl VmFd { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_irqchip(&self, irqchip: &mut kvm_irqchip) -> Result<()> { // SAFETY: Here we trust the kernel not to read past the end of the kvm_irqchip struct. - let ret = unsafe { - ioctl_with_mut_ref(self, KVM_GET_IRQCHIP(), irqchip) - }; + let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_IRQCHIP(), irqchip) }; if ret == 0 { Ok(()) } else { @@ -278,9 +276,7 @@ impl VmFd { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_irqchip(&self, irqchip: &kvm_irqchip) -> Result<()> { // SAFETY: Here we trust the kernel not to read past the end of the kvm_irqchip struct. - let ret = unsafe { - ioctl_with_ref(self, KVM_SET_IRQCHIP(), irqchip) - }; + let ret = unsafe { ioctl_with_ref(self, KVM_SET_IRQCHIP(), irqchip) }; if ret == 0 { Ok(()) } else { @@ -346,9 +342,7 @@ impl VmFd { pub fn get_pit2(&self) -> Result { let mut pitstate = Default::default(); // SAFETY: Here we trust the kernel not to read past the end of the kvm_pit_state2 struct. - let ret = unsafe { - ioctl_with_mut_ref(self, KVM_GET_PIT2(), &mut pitstate) - }; + let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_PIT2(), &mut pitstate) }; if ret == 0 { Ok(pitstate) } else { @@ -384,9 +378,7 @@ impl VmFd { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_pit2(&self, pitstate: &kvm_pit_state2) -> Result<()> { // SAFETY: Here we trust the kernel not to read past the end of the kvm_pit_state2 struct. - let ret = unsafe { - ioctl_with_ref(self, KVM_SET_PIT2(), pitstate) - }; + let ret = unsafe { ioctl_with_ref(self, KVM_SET_PIT2(), pitstate) }; if ret == 0 { Ok(()) } else { @@ -416,9 +408,7 @@ impl VmFd { pub fn get_clock(&self) -> Result { let mut clock = Default::default(); // SAFETY: Here we trust the kernel not to read past the end of the kvm_clock_data struct. - let ret = unsafe { - ioctl_with_mut_ref(self, KVM_GET_CLOCK(), &mut clock) - }; + let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_CLOCK(), &mut clock) }; if ret == 0 { Ok(clock) } else { @@ -450,9 +440,7 @@ impl VmFd { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_clock(&self, clock: &kvm_clock_data) -> Result<()> { // SAFETY: Here we trust the kernel not to read past the end of the kvm_clock_data struct. - let ret = unsafe { - ioctl_with_ref(self, KVM_SET_CLOCK(), clock) - }; + let ret = unsafe { ioctl_with_ref(self, KVM_SET_CLOCK(), clock) }; if ret == 0 { Ok(()) } else { From 071626b0147bb47b3d1300942dcee36aea162030 Mon Sep 17 00:00:00 2001 From: Andreea Florescu Date: Fri, 13 Jan 2023 14:27:21 +0100 Subject: [PATCH 212/332] move unsafe comment to line before unsafe Because of the cargo fmt some lines got shifted and the comments need to be moved as well. Signed-off-by: Andreea Florescu --- src/ioctls/vcpu.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 69fa30c..0b5d45a 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -477,8 +477,8 @@ impl VcpuFd { } let mut cpuid = CpuId::new(num_entries).map_err(|_| errno::Error::new(libc::ENOMEM))?; - // SAFETY: Here we trust the kernel not to read past the end of the kvm_cpuid2 struct. let ret = + // SAFETY: Here we trust the kernel not to read past the end of the kvm_cpuid2 struct. unsafe { ioctl_with_mut_ptr(self, KVM_GET_CPUID2(), cpuid.as_mut_fam_struct_ptr()) }; if ret != 0 { return Err(errno::Error::last()); @@ -1092,9 +1092,9 @@ impl VcpuFd { /// ``` #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] pub fn get_reg_list(&self, reg_list: &mut RegList) -> Result<()> { - // SAFETY: This is safe because we allocated the struct and we trust the kernel will read - // exactly the size of the struct. let ret = + // SAFETY: This is safe because we allocated the struct and we trust the kernel will read + // exactly the size of the struct. unsafe { ioctl_with_mut_ref(self, KVM_GET_REG_LIST(), reg_list.as_mut_fam_struct()) }; if ret < 0 { return Err(errno::Error::last()); From 3de4150068183b640ac307ac47674bbc8e7c8695 Mon Sep 17 00:00:00 2001 From: Changyuan Lyu Date: Thu, 15 Dec 2022 14:10:33 -0800 Subject: [PATCH 213/332] ioctls: Add open_with_cloexec_at and new_with_path These two functions are the same as `open_with_cloexec` and `new` except they allow users to specify the path to the KVM device file. Signed-off-by: Changyuan Lyu --- CHANGELOG.md | 7 ++++ src/ioctls/system.rs | 78 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c62833b..a5d1858 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# Unreleased + +## Added +- [[#213](https://github.com/rust-vmm/kvm-ioctls/pull/213)] Add `Kvm::new_with_path()` + and `Kvm::open_with_cloexec_at()` to allowing using kvm device file other than + `/dev/kvm`. + # v0.12.0 ## Added diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index ee1d68c..5dacb03 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -5,6 +5,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the THIRD-PARTY file. use libc::{open, O_CLOEXEC, O_RDWR}; +use std::ffi::CStr; use std::fs::File; use std::os::raw::{c_char, c_ulong}; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; @@ -46,6 +47,32 @@ impl Kvm { Ok(unsafe { Self::from_raw_fd(fd) }) } + /// Opens the KVM device at `kvm_path` and returns a `Kvm` object on success. + /// + /// # Arguments + /// + /// * `kvm_path`: path to the KVM device. Usually it is `/dev/kvm`. + /// + /// # Example + /// + /// ``` + /// use kvm_ioctls::Kvm; + /// use std::ffi::CString; + /// let kvm_path = CString::new("/dev/kvm").unwrap(); + /// let kvm = Kvm::new_with_path(&kvm_path).unwrap(); + /// ``` + #[allow(clippy::new_ret_no_self)] + pub fn new_with_path

(kvm_path: P) -> Result + where + P: AsRef, + { + // Open `kvm_path` using `O_CLOEXEC` flag. + let fd = Self::open_with_cloexec_at(kvm_path, true)?; + // SAFETY: Safe because we verify that the fd is valid in `open_with_cloexec_at` + // and we own the fd. + Ok(unsafe { Self::from_raw_fd(fd) }) + } + /// Opens `/dev/kvm` and returns the fd number on success. /// /// One usecase for this method is opening `/dev/kvm` before exec-ing into a @@ -68,9 +95,39 @@ impl Kvm { /// let kvm = unsafe { Kvm::from_raw_fd(kvm_fd) }; /// ``` pub fn open_with_cloexec(close_on_exec: bool) -> Result { + // SAFETY: Safe because we give a constant nul-terminated string. + let kvm_path = unsafe { CStr::from_bytes_with_nul_unchecked(b"/dev/kvm\0") }; + Self::open_with_cloexec_at(kvm_path, close_on_exec) + } + + /// Opens the KVM device at `kvm_path` and returns the fd number on success. + /// Same as [open_with_cloexec()](struct.Kvm.html#method.open_with_cloexec) + /// except this method opens `kvm_path` instead of `/dev/kvm`. + /// + /// # Arguments + /// + /// * `kvm_path`: path to the KVM device. Usually it is `/dev/kvm`. + /// * `close_on_exec`: If true opens `kvm_path` using the `O_CLOEXEC` flag. + /// + /// # Example + /// + /// ``` + /// # use kvm_ioctls::Kvm; + /// # use std::ffi::CString; + /// # use std::os::unix::io::FromRawFd; + /// let kvm_path = CString::new("/dev/kvm").unwrap(); + /// let kvm_fd = Kvm::open_with_cloexec_at(kvm_path, false).unwrap(); + /// // The `kvm_fd` can now be passed to another process where we can use + /// // `from_raw_fd` for creating a `Kvm` object: + /// let kvm = unsafe { Kvm::from_raw_fd(kvm_fd) }; + /// ``` + pub fn open_with_cloexec_at

(path: P, close_on_exec: bool) -> Result + where + P: AsRef, + { let open_flags = O_RDWR | if close_on_exec { O_CLOEXEC } else { 0 }; - // SAFETY: Safe because we give a constant nul-terminated string and verify the result. - let ret = unsafe { open("/dev/kvm\0".as_ptr() as *const c_char, open_flags) }; + // SAFETY: Safe because we verify the result. + let ret = unsafe { open(path.as_ref().as_ptr() as *const c_char, open_flags) }; if ret < 0 { Err(errno::Error::last()) } else { @@ -583,6 +640,12 @@ mod tests { Kvm::new().unwrap(); } + #[test] + fn test_kvm_new_with_path() { + let kvm_path = unsafe { CStr::from_bytes_with_nul_unchecked(b"/dev/kvm\0") }; + Kvm::new_with_path(kvm_path).unwrap(); + } + #[test] fn test_open_with_cloexec() { let fd = Kvm::open_with_cloexec(false).unwrap(); @@ -593,6 +656,17 @@ mod tests { assert_eq!(flags & FD_CLOEXEC, FD_CLOEXEC); } + #[test] + fn test_open_with_cloexec_at() { + let kvm_path = std::ffi::CString::new("/dev/kvm").unwrap(); + let fd = Kvm::open_with_cloexec_at(&kvm_path, false).unwrap(); + let flags = unsafe { fcntl(fd, F_GETFD, 0) }; + assert_eq!(flags & FD_CLOEXEC, 0); + let fd = Kvm::open_with_cloexec_at(&kvm_path, true).unwrap(); + let flags = unsafe { fcntl(fd, F_GETFD, 0) }; + assert_eq!(flags & FD_CLOEXEC, FD_CLOEXEC); + } + #[test] fn test_kvm_api_version() { let kvm = Kvm::new().unwrap(); From 088d4d88f2f84e397d32260ac7b39981e9d4bdcf Mon Sep 17 00:00:00 2001 From: Changyuan Lyu Date: Fri, 27 Jan 2023 13:13:54 -0800 Subject: [PATCH 214/332] release v0.13.0 Details available in the CHANGELOG.md. Signed-off-by: Changyuan Lyu --- CHANGELOG.md | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5d1858..8350237 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ -# Unreleased +# v0.13.0 ## Added - [[#213](https://github.com/rust-vmm/kvm-ioctls/pull/213)] Add `Kvm::new_with_path()` - and `Kvm::open_with_cloexec_at()` to allowing using kvm device file other than + and `Kvm::open_with_cloexec_at()` to allow using kvm device files other than `/dev/kvm`. # v0.12.0 diff --git a/Cargo.toml b/Cargo.toml index c475ca0..59d1f7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kvm-ioctls" -version = "0.12.0" +version = "0.13.0" authors = ["Amazon Firecracker Team "] description = "Safe wrappers over KVM ioctls" repository = "https://github.com/rust-vmm/kvm-ioctls" From 534d0406040d1aa63b878cfa3fbe53cbe84d2622 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Mar 2023 10:57:41 +0000 Subject: [PATCH 215/332] Bump rust-vmm-ci from `607c775` to `c2f8c93` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `607c775` to `c2f8c93`. - [Release notes](https://github.com/rust-vmm/rust-vmm-ci/releases) - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/607c775ddec0d77a2865520d3f9f7c211d9fb7c7...c2f8c93e3796d8b3ea7dc339fad211457be9c238) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 607c775..c2f8c93 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 607c775ddec0d77a2865520d3f9f7c211d9fb7c7 +Subproject commit c2f8c93e3796d8b3ea7dc339fad211457be9c238 From 471169d4b9da176968042118e2ab8d8249e1e451 Mon Sep 17 00:00:00 2001 From: Takahiro Itazuri Date: Wed, 22 Mar 2023 10:41:37 +0000 Subject: [PATCH 216/332] feat: add MSR-related system ioctls This commit adds two system ioctls for MSR: `KVM_GET_MSR_FEATURE_INDEX_LIST` and `KVM_GET_MSRS`. Signed-off-by: Takahiro Itazuri --- CHANGELOG.md | 6 +++ src/cap.rs | 1 + src/ioctls/system.rs | 111 ++++++++++++++++++++++++++++++++++++++++++- src/kvm_ioctls.rs | 8 ++-- 4 files changed, 121 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8350237..2e6a9fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# Upcoming Release + +## Added +- [[#219](https://github.com/rust-vmm/kvm-ioctls/pull/219)] Support for + `KVM_GET_MSR_FEATURE_INDEX_LIST` and `KVM_GET_MSRS` system ioctls. + # v0.13.0 ## Added diff --git a/src/cap.rs b/src/cap.rs index fbeb8b7..9480d45 100644 --- a/src/cap.rs +++ b/src/cap.rs @@ -146,4 +146,5 @@ pub enum Cap { HypervSynic2 = KVM_CAP_HYPERV_SYNIC2, DebugHwBps = KVM_CAP_GUEST_DEBUG_HW_BPS, DebugHwWps = KVM_CAP_GUEST_DEBUG_HW_WPS, + GetMsrFeatures = KVM_CAP_GET_MSR_FEATURES, } diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index 5dacb03..c79d079 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -17,7 +17,7 @@ use crate::kvm_ioctls::*; #[cfg(any(target_arch = "aarch64"))] use kvm_bindings::KVM_VM_TYPE_ARM_IPA_SIZE_MASK; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -use kvm_bindings::{CpuId, MsrList, KVM_MAX_CPUID_ENTRIES, KVM_MAX_MSR_ENTRIES}; +use kvm_bindings::{CpuId, MsrList, Msrs, KVM_MAX_CPUID_ENTRIES, KVM_MAX_MSR_ENTRIES}; use vmm_sys_util::errno; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use vmm_sys_util::ioctl::ioctl_with_mut_ptr; @@ -425,7 +425,7 @@ impl Kvm { // SAFETY: The kernel is trusted not to write beyond the bounds of the memory // allocated for the struct. The limit is read from nmsrs, which is set to the allocated - // size (MAX_KVM_MSR_ENTRIES) above. + // size (KVM_MAX_MSR_ENTRIES) above. let ret = unsafe { ioctl_with_mut_ptr( self, @@ -441,6 +441,83 @@ impl Kvm { Ok(msr_list) } + /// X86 specific call to get a list of MSRs that can be passed to the KVM_GET_MSRS system ioctl. + /// + /// See the documentation for `KVM_GET_MSR_FEATURE_INDEX_LIST`. + /// + /// # Example + /// + /// ``` + /// use kvm_bindings::{kvm_msr_entry, Msrs}; + /// use kvm_ioctls::Kvm; + /// + /// let kvm = Kvm::new().unwrap(); + /// let msr_feature_index_list = kvm.get_msr_feature_index_list().unwrap(); + /// ``` + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn get_msr_feature_index_list(&self) -> Result { + let mut msr_list = + MsrList::new(KVM_MAX_MSR_ENTRIES).map_err(|_| errno::Error::new(libc::ENOMEM))?; + + // SAFETY: The kernel is trusted not to write beyond the bounds of the memory + // allocated for the struct. The limit is read from nmsrs, which is set to the allocated + // size (KVM_MAX_MSR_ENTRIES) above. + let ret = unsafe { + ioctl_with_mut_ptr( + self, + KVM_GET_MSR_FEATURE_INDEX_LIST(), + msr_list.as_mut_fam_struct_ptr(), + ) + }; + if ret < 0 { + return Err(errno::Error::last()); + } + + Ok(msr_list) + } + + /// X86 specific call to read the values of MSR-based features that are available for the VM. + /// As opposed to `VcpuFd::get_msrs()`, this call returns all the MSRs supported by the + /// system, similar to `get_supported_cpuid()` for CPUID. + /// + /// See the documentation for `KVM_GET_MSRS`. + /// + /// # Arguments + /// + /// * `msrs` - MSRs (input/output). For details check the `kvm_msrs` structure in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Example + /// + /// ``` + /// use kvm_bindings::{kvm_msr_entry, Msrs}; + /// use kvm_ioctls::Kvm; + /// + /// let kvm = Kvm::new().unwrap(); + /// let msr_feature_index_list = kvm.get_msr_feature_index_list().unwrap(); + /// let mut msrs = Msrs::from_entries( + /// &msr_feature_index_list + /// .as_slice() + /// .iter() + /// .map(|&idx| kvm_msr_entry { + /// index: idx, + /// ..Default::default() + /// }) + /// .collect::>(), + /// ) + /// .unwrap(); + /// let ret = kvm.get_msrs(&mut msrs).unwrap(); + /// ``` + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn get_msrs(&self, msrs: &mut Msrs) -> Result { + // SAFETY: Here we trust the kernel not to read past the end of the kvm_msrs struct. + let ret = unsafe { ioctl_with_mut_ptr(self, KVM_GET_MSRS(), msrs.as_mut_fam_struct_ptr()) }; + if ret < 0 { + return Err(errno::Error::last()); + } + Ok(ret as usize) + } + /// Creates a VM fd using the KVM fd. /// /// See the documentation for `KVM_CREATE_VM`. @@ -812,6 +889,36 @@ mod tests { assert!(msr_list.as_slice().len() >= 2); } + #[test] + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn get_msr_feature_index_list() { + let kvm = Kvm::new().unwrap(); + let msr_feature_index_list = kvm.get_msr_feature_index_list().unwrap(); + assert!(!msr_feature_index_list.as_slice().is_empty()); + } + + #[test] + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn get_msrs() { + use kvm_bindings::kvm_msr_entry; + + let kvm = Kvm::new().unwrap(); + let mut msrs = Msrs::from_entries(&[ + kvm_msr_entry { + index: 0x0000010a, // MSR_IA32_ARCH_CAPABILITIES + ..Default::default() + }, + kvm_msr_entry { + index: 0x00000345, // MSR_IA32_PERF_CAPABILITIES + ..Default::default() + }, + ]) + .unwrap(); + let nmsrs = kvm.get_msrs(&mut msrs).unwrap(); + + assert_eq!(nmsrs, 2); + } + #[test] fn test_bad_kvm_fd() { let badf_errno = libc::EBADF; diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index 559611a..0133c66 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -13,6 +13,8 @@ use kvm_bindings::*; ioctl_io_nr!(KVM_GET_API_VERSION, KVMIO, 0x00); ioctl_io_nr!(KVM_CREATE_VM, KVMIO, 0x01); +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_iowr_nr!(KVM_GET_MSR_INDEX_LIST, KVMIO, 0x02, kvm_msr_list); ioctl_io_nr!(KVM_CHECK_EXTENSION, KVMIO, 0x03); ioctl_io_nr!(KVM_GET_VCPU_MMAP_SIZE, KVMIO, 0x04); /* Available with KVM_CAP_EXT_CPUID */ @@ -21,6 +23,9 @@ ioctl_iowr_nr!(KVM_GET_SUPPORTED_CPUID, KVMIO, 0x05, kvm_cpuid2); /* Available with KVM_CAP_EXT_EMUL_CPUID */ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_iowr_nr!(KVM_GET_EMULATED_CPUID, KVMIO, 0x09, kvm_cpuid2); +/* Available with KVM_CAP_GET_MSR_FEATURES */ +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_iowr_nr!(KVM_GET_MSR_FEATURE_INDEX_LIST, KVMIO, 0x0a, kvm_msr_list); // Ioctls for VM fds. @@ -129,9 +134,6 @@ ioctl_ior_nr!(KVM_GET_SREGS, KVMIO, 0x83, kvm_sregs); ioctl_iow_nr!(KVM_SET_SREGS, KVMIO, 0x84, kvm_sregs); #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_iowr_nr!(KVM_TRANSLATE, KVMIO, 0x85, kvm_translation); -/* Available with KVM_CAP_GET_MSR_FEATURES */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -ioctl_iowr_nr!(KVM_GET_MSR_INDEX_LIST, KVMIO, 0x02, kvm_msr_list); #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_iowr_nr!(KVM_GET_MSRS, KVMIO, 0x88, kvm_msrs); #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] From 58b9a4ac85c974922888c6c7e478286735a1896f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Apr 2023 10:56:38 +0000 Subject: [PATCH 217/332] Bump rust-vmm-ci from `c2f8c93` to `3f9869f` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `c2f8c93` to `3f9869f`. - [Release notes](https://github.com/rust-vmm/rust-vmm-ci/releases) - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/c2f8c93e3796d8b3ea7dc339fad211457be9c238...3f9869f285323bf51d7b26acde4c0b7777fd0805) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index c2f8c93..3f9869f 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit c2f8c93e3796d8b3ea7dc339fad211457be9c238 +Subproject commit 3f9869f285323bf51d7b26acde4c0b7777fd0805 From 23a3bb045a467e60bb00328a0b13cea13b5815d0 Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Mon, 10 Apr 2023 09:43:35 +0000 Subject: [PATCH 218/332] Add Cap::ArmPmuV3 Signed-off-by: Alyssa Ross --- CHANGELOG.md | 2 ++ src/cap.rs | 1 + 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e6a9fd..04a7bf4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ## Added - [[#219](https://github.com/rust-vmm/kvm-ioctls/pull/219)] Support for `KVM_GET_MSR_FEATURE_INDEX_LIST` and `KVM_GET_MSRS` system ioctls. +- [[#221](https://github.com/rust-vmm/kvm-ioctls/pull/221)] Add + `Cap::ArmPmuV3`. # v0.13.0 diff --git a/src/cap.rs b/src/cap.rs index 9480d45..f8a2274 100644 --- a/src/cap.rs +++ b/src/cap.rs @@ -139,6 +139,7 @@ pub enum Cap { S390UserSigp = KVM_CAP_S390_USER_SIGP, #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] SplitIrqchip = KVM_CAP_SPLIT_IRQCHIP, + ArmPmuV3 = KVM_CAP_ARM_PMU_V3, ImmediateExit = KVM_CAP_IMMEDIATE_EXIT, ArmVmIPASize = KVM_CAP_ARM_VM_IPA_SIZE, MsiDevid = KVM_CAP_MSI_DEVID, From ca88c67dffcc65591d49ce6484aaca4a92d93005 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 May 2023 10:56:37 +0000 Subject: [PATCH 219/332] Bump rust-vmm-ci from `3f9869f` to `8627b37` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `3f9869f` to `8627b37`. - [Release notes](https://github.com/rust-vmm/rust-vmm-ci/releases) - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/3f9869f285323bf51d7b26acde4c0b7777fd0805...8627b3766b2bedde4657c7e9ddfc6f95a20e6942) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 3f9869f..8627b37 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 3f9869f285323bf51d7b26acde4c0b7777fd0805 +Subproject commit 8627b3766b2bedde4657c7e9ddfc6f95a20e6942 From 245f51dfba0029a2cff2f47bf58d265b13c9c829 Mon Sep 17 00:00:00 2001 From: Egor Lazarchuk Date: Mon, 15 May 2023 18:18:11 +0100 Subject: [PATCH 220/332] feat(aarch64): generic get/set_one_reg Linux kernel defines arm registers up to 2048 bits wide. To support all different sized registers we change get/set_one_reg methods to accept a byte slice. For `set_one_reg` slice contains register data to be written to vcpu. For `get_one_reg` slice will have register data read from vcpu. Signed-off-by: Egor Lazarchuk --- src/ioctls/vcpu.rs | 74 +++++++++++++++++++++++++++++++++------------- src/ioctls/vm.rs | 4 +-- src/lib.rs | 6 ++-- 3 files changed, 59 insertions(+), 25 deletions(-) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 0b5d45a..2637762 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -19,6 +19,12 @@ use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ref}; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use vmm_sys_util::ioctl::{ioctl_with_mut_ptr, ioctl_with_ptr, ioctl_with_val}; +/// Helper method to obtain the size of the register through its id +#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] +fn reg_size(reg_id: u64) -> usize { + 2_usize.pow(((reg_id & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT) as u32) +} + /// Reasons for vCPU exits. /// /// The exit reasons are mapped to the `KVM_EXIT_*` defines in the @@ -1162,13 +1168,21 @@ impl VcpuFd { /// # Arguments /// /// * `reg_id` - ID of the register for which we are setting the value. - /// * `data` - value for the specified register. + /// * `data` - byte slice where the register value will be written to. + /// + /// # Note + /// + /// `data` should be equal or bigger then the register size + /// oterwise function will return EINVAL error #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] - pub fn set_one_reg(&self, reg_id: u64, data: u128) -> Result<()> { - let data_ptr = &data as *const _; + pub fn set_one_reg(&self, reg_id: u64, data: &[u8]) -> Result { + let reg_size = reg_size(reg_id); + if data.len() < reg_size { + return Err(errno::Error::new(libc::EINVAL)); + } let onereg = kvm_one_reg { id: reg_id, - addr: data_ptr as u64, + addr: data.as_ptr() as u64, }; // SAFETY: This is safe because we allocated the struct and we know the kernel will read // exactly the size of the struct. @@ -1176,10 +1190,10 @@ impl VcpuFd { if ret < 0 { return Err(errno::Error::last()); } - Ok(()) + Ok(reg_size) } - /// Returns the value of the specified vCPU register. + /// Writes the value of the specified vCPU register into provided buffer. /// /// The id of the register is encoded as specified in the kernel documentation /// for `KVM_GET_ONE_REG`. @@ -1187,12 +1201,20 @@ impl VcpuFd { /// # Arguments /// /// * `reg_id` - ID of the register. + /// * `data` - byte slice where the register value will be written to. + /// # Note + /// + /// `data` should be equal or bigger then the register size + /// oterwise function will return EINVAL error #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] - pub fn get_one_reg(&self, reg_id: u64) -> Result { - let mut reg_value = 0; + pub fn get_one_reg(&self, reg_id: u64, data: &mut [u8]) -> Result { + let reg_size = reg_size(reg_id); + if data.len() < reg_size { + return Err(errno::Error::new(libc::EINVAL)); + } let mut onereg = kvm_one_reg { id: reg_id, - addr: &mut reg_value as *mut _ as u64, + addr: data.as_ptr() as u64, }; // SAFETY: This is safe because we allocated the struct and we know the kernel will read // exactly the size of the struct. @@ -1200,7 +1222,7 @@ impl VcpuFd { if ret < 0 { return Err(errno::Error::last()); } - Ok(reg_value) + Ok(reg_size) } /// Notify the guest about the vCPU being paused. @@ -2044,15 +2066,18 @@ mod tests { // Set the PC to the guest address where we loaded the code. vcpu_fd - .set_one_reg(core_reg_base + 2 * 32, guest_addr as u128) + .set_one_reg(core_reg_base + 2 * 32, &(guest_addr as u128).to_le_bytes()) .unwrap(); // Set x8 and x9 to the addresses the guest test code needs vcpu_fd - .set_one_reg(core_reg_base + 2 * 8, guest_addr as u128 + 0x10000) + .set_one_reg( + core_reg_base + 2 * 8, + &(guest_addr as u128 + 0x10000).to_le_bytes(), + ) .unwrap(); vcpu_fd - .set_one_reg(core_reg_base + 2 * 9, mmio_addr as u128) + .set_one_reg(core_reg_base + 2 * 9, &(mmio_addr as u128).to_le_bytes()) .unwrap(); loop { @@ -2389,12 +2414,16 @@ mod tests { let data: u128 = 0; let reg_id: u64 = 0; - assert!(vcpu.set_one_reg(reg_id, data).is_err()); + assert!(vcpu.set_one_reg(reg_id, &data.to_le_bytes()).is_err()); // Exercising KVM_SET_ONE_REG by trying to alter the data inside the PSTATE register (which is a // specific aarch64 register). + // This regiseter is 64 bit wide (8 bytes). const PSTATE_REG_ID: u64 = 0x6030_0000_0010_0042; - vcpu.set_one_reg(PSTATE_REG_ID, data) + vcpu.set_one_reg(PSTATE_REG_ID, &data.to_le_bytes()) .expect("Failed to set pstate register"); + + // Trying to set 8 byte register with 7 bytes must fail. + assert!(vcpu.set_one_reg(PSTATE_REG_ID, &[0_u8; 7]).is_err()); } #[test] @@ -2420,14 +2449,17 @@ mod tests { PSR_MODE_EL1H | PSR_A_BIT | PSR_F_BIT | PSR_I_BIT | PSR_D_BIT; let data: u128 = PSTATE_FAULT_BITS_64 as u128; const PSTATE_REG_ID: u64 = 0x6030_0000_0010_0042; - vcpu.set_one_reg(PSTATE_REG_ID, data) + vcpu.set_one_reg(PSTATE_REG_ID, &data.to_le_bytes()) .expect("Failed to set pstate register"); - assert_eq!( - vcpu.get_one_reg(PSTATE_REG_ID) - .expect("Failed to get pstate register"), - PSTATE_FAULT_BITS_64 as u128 - ); + let mut bytes = [0_u8; 16]; + vcpu.get_one_reg(PSTATE_REG_ID, &mut bytes) + .expect("Failed to get pstate register"); + let data = u128::from_le_bytes(bytes); + assert_eq!(data, PSTATE_FAULT_BITS_64 as u128); + + // Trying to get 8 byte register with 7 bytes must fail. + assert!(vcpu.get_one_reg(PSTATE_REG_ID, &mut [0_u8; 7]).is_err()); } #[test] diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 876ced3..e26b1cd 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -784,8 +784,8 @@ impl VmFd { /// /// let core_reg_base: u64 = 0x6030_0000_0010_0000; /// let mmio_addr: u64 = guest_addr + mem_size as u64; - /// vcpu_fd.set_one_reg(core_reg_base + 2 * 32, guest_addr as u128); // set PC - /// vcpu_fd.set_one_reg(core_reg_base + 2 * 0, mmio_addr as u128); // set X0 + /// vcpu_fd.set_one_reg(core_reg_base + 2 * 32, &guest_addr.to_le_bytes()); // set PC + /// vcpu_fd.set_one_reg(core_reg_base + 2 * 0, &mmio_addr.to_le_bytes()); // set X0 /// } /// /// loop { diff --git a/src/lib.rs b/src/lib.rs index 332200f..a00f203 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -156,8 +156,10 @@ //! //! let core_reg_base: u64 = 0x6030_0000_0010_0000; //! let mmio_addr: u64 = guest_addr + mem_size as u64; -//! vcpu_fd.set_one_reg(core_reg_base + 2 * 32, guest_addr as u128); // set PC -//! vcpu_fd.set_one_reg(core_reg_base + 2 * 0, mmio_addr as u128); // set X0 +//! // set PC +//! vcpu_fd.set_one_reg(core_reg_base + 2 * 32, &guest_addr.to_le_bytes()); +//! // set X0 +//! vcpu_fd.set_one_reg(core_reg_base + 2 * 0, &mmio_addr.to_le_bytes()); //! } //! //! // 6. Run code on the vCPU. From b0a258655e84c7ab2c50cbdae5324216fa530adb Mon Sep 17 00:00:00 2001 From: Egor Lazarchuk Date: Fri, 19 May 2023 13:41:50 +0100 Subject: [PATCH 221/332] chore: updated CHANGELOG Signed-off-by: Egor Lazarchuk --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04a7bf4..a39b0a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ `KVM_GET_MSR_FEATURE_INDEX_LIST` and `KVM_GET_MSRS` system ioctls. - [[#221](https://github.com/rust-vmm/kvm-ioctls/pull/221)] Add `Cap::ArmPmuV3`. +- [[#223](https://github.com/rust-vmm/kvm-ioctls/pull/223)] aarch64: + Updated `get/set_one_reg` to support different registers sizes through + byte slices. # v0.13.0 From 0b102e70c5d0c81126c4ca9f07aec62cdd5cd4c6 Mon Sep 17 00:00:00 2001 From: Egor Lazarchuk Date: Fri, 26 May 2023 11:30:02 +0100 Subject: [PATCH 222/332] release v0.14.0 Signed-off-by: Egor Lazarchuk --- CHANGELOG.md | 6 ++++++ Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a39b0a8..8d1db42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,16 @@ # Upcoming Release +# v0.14.0 + ## Added + - [[#219](https://github.com/rust-vmm/kvm-ioctls/pull/219)] Support for `KVM_GET_MSR_FEATURE_INDEX_LIST` and `KVM_GET_MSRS` system ioctls. - [[#221](https://github.com/rust-vmm/kvm-ioctls/pull/221)] Add `Cap::ArmPmuV3`. + +## Changed + - [[#223](https://github.com/rust-vmm/kvm-ioctls/pull/223)] aarch64: Updated `get/set_one_reg` to support different registers sizes through byte slices. diff --git a/Cargo.toml b/Cargo.toml index 59d1f7c..178067b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kvm-ioctls" -version = "0.13.0" +version = "0.14.0" authors = ["Amazon Firecracker Team "] description = "Safe wrappers over KVM ioctls" repository = "https://github.com/rust-vmm/kvm-ioctls" From ab98dd7f7cbeffc4524bd3314c33320851369d67 Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Wed, 21 Jun 2023 10:04:38 +0100 Subject: [PATCH 223/332] Update CODEOWNERS Adding @roypat and @JonathanWoollett-Light from the firecracker team. Signed-off-by: Patrick Roy --- CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index 3cbfdee..9649f2d 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,3 +1,3 @@ # These owners will be the default owners for everything in # the repo. -* @acatangiu @aghecenco @andreeaflorescu @lauralt @sameo +* @acatangiu @aghecenco @andreeaflorescu @lauralt @sameo @roypat @JonathanWoollett-Light From 6da99cc2d68ae188ea1c09307869c695a2a8d3cf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jul 2023 10:47:21 +0000 Subject: [PATCH 224/332] Bump rust-vmm-ci from `8627b37` to `9dfe5b2` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `8627b37` to `9dfe5b2`. - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/8627b3766b2bedde4657c7e9ddfc6f95a20e6942...9dfe5b267c4009150f0cdf49597b683f15848d1a) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 8627b37..9dfe5b2 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 8627b3766b2bedde4657c7e9ddfc6f95a20e6942 +Subproject commit 9dfe5b267c4009150f0cdf49597b683f15848d1a From fadc154c5945090cff35517939cb6a2c7fde07d2 Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Mon, 24 Jul 2023 11:05:44 +0100 Subject: [PATCH 225/332] Appease Clippy With new rust toolchain version, it was complaining about hexadecimals not being grouped into sections of equal size using '_'. Since this was done for readability, suppress the lint on the test in question. Signed-off-by: Patrick Roy --- src/ioctls/vm.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index e26b1cd..6b9dc9c 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -1944,6 +1944,7 @@ mod tests { #[test] #[cfg(target_arch = "aarch64")] + #[allow(clippy::unusual_byte_groupings)] fn test_set_irq_line() { let kvm = Kvm::new().unwrap(); let vm_fd = kvm.create_vm().unwrap(); From 6b4f27221fd141d40765742fcf392ecb6ee47666 Mon Sep 17 00:00:00 2001 From: Egor Lazarchuk Date: Thu, 13 Jul 2023 17:51:40 +0100 Subject: [PATCH 226/332] feat: additional method to query kvm extension Added method to query kvm extension using raw integer instead of an enum value. Signed-off-by: Egor Lazarchuk --- CHANGELOG.md | 5 +++++ src/ioctls/system.rs | 36 +++++++++++++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d1db42..5a78f35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Upcoming Release +## Added +- [[#230](https://github.com/rust-vmm/kvm-ioctls/pull/230)] Added + `check_extension_raw` method to use raw integer values instead + of `Cap` enum. + # v0.14.0 ## Added diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index c79d079..69f48d7 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -176,6 +176,31 @@ impl Kvm { self.check_extension_int(Cap::DebugHwWps) } + /// Wrapper over `KVM_CHECK_EXTENSION`. + /// + /// Returns 0 if the capability is not available and a positive integer otherwise. + /// See the documentation for `KVM_CHECK_EXTENSION`. + /// + /// # Arguments + /// + /// * `c` - KVM capability to check in a form of a raw integer. + /// + /// # Example + /// + /// ``` + /// # use kvm_ioctls::Kvm; + /// # use std::os::raw::c_ulong; + /// use kvm_ioctls::Cap; + /// + /// let kvm = Kvm::new().unwrap(); + /// assert!(kvm.check_extension_raw(Cap::MaxVcpuId as c_ulong) > 0); + /// ``` + pub fn check_extension_raw(&self, c: c_ulong) -> i32 { + // SAFETY: Safe because we know that our file is a KVM fd. + // If `c` is not a known kernel extension, kernel will return 0. + unsafe { ioctl_with_val(self, KVM_CHECK_EXTENSION(), c) } + } + /// Wrapper over `KVM_CHECK_EXTENSION`. /// /// Returns 0 if the capability is not available and a positive integer otherwise. @@ -195,9 +220,7 @@ impl Kvm { /// assert!(kvm.check_extension_int(Cap::MaxVcpuId) > 0); /// ``` pub fn check_extension_int(&self, c: Cap) -> i32 { - // SAFETY: Safe because we know that our file is a KVM fd and that the extension is one of - // the ones defined by kernel. - unsafe { ioctl_with_val(self, KVM_CHECK_EXTENSION(), c as c_ulong) } + self.check_extension_raw(c as c_ulong) } /// Checks if a particular `Cap` is available. @@ -751,6 +774,13 @@ mod tests { assert!(kvm.check_extension(Cap::UserMemory)); } + #[test] + fn test_kvm_check_extension() { + let kvm = Kvm::new().unwrap(); + // unsupported extension will return 0 + assert_eq!(kvm.check_extension_raw(696969), 0); + } + #[test] #[cfg(target_arch = "aarch64")] fn test_get_host_ipa_limit() { From 49782d05e85975ef24ae980f78f895109f1416bf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Aug 2023 10:16:43 +0000 Subject: [PATCH 227/332] Bump rust-vmm-ci from `9dfe5b2` to `7c1057e` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `9dfe5b2` to `7c1057e`. - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/9dfe5b267c4009150f0cdf49597b683f15848d1a...7c1057e9bcba7ed5090fd62b9fef0570fadb06e6) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 9dfe5b2..7c1057e 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 9dfe5b267c4009150f0cdf49597b683f15848d1a +Subproject commit 7c1057e9bcba7ed5090fd62b9fef0570fadb06e6 From 735dd2097685f1ec3cf0a70ae364a0725ed069e3 Mon Sep 17 00:00:00 2001 From: xuejun-xj Date: Sat, 24 Jun 2023 09:51:42 +0800 Subject: [PATCH 228/332] arm64: add support for vCPU SVE feature This commit add support for initialization of vCPU SVE feature on aarch64: 1. Add ArmSve in Cap struct. 2. Add KVM_ARM_VCPU_FINALIZE ioctl in VcpuFd struct, which is used to initialize the finalization of SVE feature for now. Signed-off-by: xuejun-xj --- src/cap.rs | 2 ++ src/ioctls/vcpu.rs | 56 ++++++++++++++++++++++++++++++++++++++++++++++ src/kvm_ioctls.rs | 4 ++++ 3 files changed, 62 insertions(+) diff --git a/src/cap.rs b/src/cap.rs index f8a2274..ebea244 100644 --- a/src/cap.rs +++ b/src/cap.rs @@ -148,4 +148,6 @@ pub enum Cap { DebugHwBps = KVM_CAP_GUEST_DEBUG_HW_BPS, DebugHwWps = KVM_CAP_GUEST_DEBUG_HW_WPS, GetMsrFeatures = KVM_CAP_GET_MSR_FEATURES, + #[cfg(target_arch = "aarch64")] + ArmSve = KVM_CAP_ARM_SVE, } diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 2637762..681d402 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1067,6 +1067,62 @@ impl VcpuFd { Ok(()) } + /// Finalizes the configuration of the specified vcpu feature. + /// + /// The vcpu must already have been initialised, enabling the affected feature, + /// by means of a successful KVM_ARM_VCPU_INIT call with the appropriate flag set + /// in features[]. + /// + /// For affected vcpu features, this is a mandatory step that must be performed before + /// the vcpu is fully usable. + /// + /// Between KVM_ARM_VCPU_INIT and KVM_ARM_VCPU_FINALIZE, the feature may be configured + /// by use of ioctls such as KVM_SET_ONE_REG. The exact configuration that should be + /// performaned and how to do it are feature-dependent. + /// + /// Other calls that depend on a particular feature being finalized, such as KVM_RUN, + /// KVM_GET_REG_LIST, KVM_GET_ONE_REG and KVM_SET_ONE_REG, will fail with -EPERM unless + /// the feature has already been finalized by means of a KVM_ARM_VCPU_FINALIZE call. + /// + /// See KVM_ARM_VCPU_INIT for details of vcpu features that require finalization using this ioctl. + /// [KVM_ARM_VCPU_FINALIZE](https://www.kernel.org/doc/html/latest/virt/kvm/api.html#kvm-arm-vcpu-finalize). + /// + /// # Arguments + /// + /// * `feature` - vCPU features that needs to be finalized. + /// + /// # Example + /// ```rust + /// # extern crate kvm_ioctls; + /// # extern crate kvm_bindings; + /// # use kvm_ioctls::Kvm; + /// use std::arch::is_aarch64_feature_detected; + /// + /// use kvm_bindings::{kvm_vcpu_init, KVM_ARM_VCPU_SVE}; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// + /// let mut kvi = kvm_vcpu_init::default(); + /// vm.get_preferred_target(&mut kvi).unwrap(); + /// kvi.features[0] |= 1 << KVM_ARM_VCPU_SVE; + /// if is_aarch64_feature_detected!("sve2") || is_aarch64_feature_detected!("sve") { + /// vcpu.vcpu_init(&kvi).unwrap(); + /// let feature = KVM_ARM_VCPU_SVE as i32; + /// vcpu.vcpu_finalize(&feature).unwrap(); + /// } + /// ``` + #[cfg(target_arch = "aarch64")] + pub fn vcpu_finalize(&self, feature: &std::os::raw::c_int) -> Result<()> { + // SAFETY: This is safe because we know the kernel will only read this + // parameter to select the correct finalization case in KVM. + let ret = unsafe { ioctl_with_ref(self, KVM_ARM_VCPU_FINALIZE(), feature) }; + if ret < 0 { + return Err(errno::Error::last()); + } + Ok(()) + } + /// Returns the guest registers that are supported for the /// KVM_GET_ONE_REG/KVM_SET_ONE_REG calls. /// diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index 0133c66..5b9360c 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -240,6 +240,10 @@ ioctl_ior_nr!(KVM_ARM_PREFERRED_TARGET, KVMIO, 0xaf, kvm_vcpu_init); #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] ioctl_iowr_nr!(KVM_GET_REG_LIST, KVMIO, 0xb0, kvm_reg_list); +/* Available with KVM_CAP_ARM_SVE */ +#[cfg(target_arch = "aarch64")] +ioctl_iow_nr!(KVM_ARM_VCPU_FINALIZE, KVMIO, 0xc2, std::os::raw::c_int); + /* Available with KVM_CAP_SET_GUEST_DEBUG */ ioctl_iow_nr!(KVM_SET_GUEST_DEBUG, KVMIO, 0x9b, kvm_guest_debug); From 4bb7199d2de0de04bd75e99432c36bdfa448e86d Mon Sep 17 00:00:00 2001 From: xuejun-xj Date: Wed, 2 Aug 2023 18:45:01 +0800 Subject: [PATCH 229/332] CHANGELOG: update for PR #228 Signed-off-by: xuejun-xj --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a78f35..9fe43f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ - [[#230](https://github.com/rust-vmm/kvm-ioctls/pull/230)] Added `check_extension_raw` method to use raw integer values instead of `Cap` enum. +- [[#228](https://github.com/rust-vmm/kvm-ioctls/pull/228)] arm64: add +support for vCPU SVE feature. # v0.14.0 From a27d964a31278bbb4e6c4b586cd262b678eb11d7 Mon Sep 17 00:00:00 2001 From: Babis Chalios Date: Thu, 15 Jun 2023 10:17:00 +0000 Subject: [PATCH 230/332] Add capabilities for pointer authentication Add the capabilities for pointer authentication in Aarch64, KVM_CAP_ARM_PTRAUTH_GENERIC and KVM_CAP_ARM_PTRAUTH_ADDRESS. Also, add a unit test for checking the existence of capabilities and try to enable the feature in the vcpu initialization if present. Signed-off-by: Babis Chalios --- CHANGELOG.md | 2 ++ src/cap.rs | 4 ++++ src/ioctls/vcpu.rs | 19 +++++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fe43f3..e0b8453 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ of `Cap` enum. - [[#228](https://github.com/rust-vmm/kvm-ioctls/pull/228)] arm64: add support for vCPU SVE feature. +- [[#219](https://github.com/rust-vmm/kvm-ioctls/pull/226)] Add `Cap::ArmPtrAuthAddress` + and `Cap::ArmPtrAuthGeneric` capabilities. # v0.14.0 diff --git a/src/cap.rs b/src/cap.rs index ebea244..91e294b 100644 --- a/src/cap.rs +++ b/src/cap.rs @@ -150,4 +150,8 @@ pub enum Cap { GetMsrFeatures = KVM_CAP_GET_MSR_FEATURES, #[cfg(target_arch = "aarch64")] ArmSve = KVM_CAP_ARM_SVE, + #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + ArmPtrAuthAddress = KVM_CAP_ARM_PTRAUTH_ADDRESS, + #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + ArmPtrAuthGeneric = KVM_CAP_ARM_PTRAUTH_GENERIC, } diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 681d402..0da5f12 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -2785,4 +2785,23 @@ mod tests { assert!(vcpu.has_device_attr(&dist_attr).is_ok()); assert!(vcpu.set_device_attr(&dist_attr).is_ok()); } + + #[test] + #[cfg(target_arch = "aarch64")] + fn test_pointer_authentication() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let vcpu = vm.create_vcpu(0).unwrap(); + + let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default(); + vm.get_preferred_target(&mut kvi) + .expect("Cannot get preferred target"); + if kvm.check_extension(Cap::ArmPtrAuthAddress) { + kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PTRAUTH_ADDRESS; + } + if kvm.check_extension(Cap::ArmPtrAuthGeneric) { + kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PTRAUTH_GENERIC; + } + assert!(vcpu.vcpu_init(&kvi).is_ok()); + } } From 683df49b2a8e6d4b7e0b893821ef33c6c738202f Mon Sep 17 00:00:00 2001 From: Babis Chalios Date: Thu, 15 Jun 2023 12:42:15 +0000 Subject: [PATCH 231/332] Derive PartialEq & Eq in capabilities enum This helps out when we use the Enum inside error types like `thiserror::Error`. Signed-off-by: Babis Chalios --- src/cap.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cap.rs b/src/cap.rs index 91e294b..99c5c16 100644 --- a/src/cap.rs +++ b/src/cap.rs @@ -20,6 +20,7 @@ use kvm_bindings::*; // We are allowing docs to be missing here because this enum is a wrapper // over auto-generated code. #[allow(missing_docs)] +#[derive(PartialEq, Eq)] pub enum Cap { Irqchip = KVM_CAP_IRQCHIP, Hlt = KVM_CAP_HLT, From 1bae7cd10ed1d946e3cc7f39379b4f5fb16a7880 Mon Sep 17 00:00:00 2001 From: Egor Lazarchuk Date: Wed, 2 Aug 2023 09:54:33 +0000 Subject: [PATCH 232/332] release 0.15.0 Signed-off-by: Egor Lazarchuk --- CHANGELOG.md | 6 ++++++ Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0b8453..752408d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Upcoming Release +## Added + +## Changed + +# v0.15.0 + ## Added - [[#230](https://github.com/rust-vmm/kvm-ioctls/pull/230)] Added `check_extension_raw` method to use raw integer values instead diff --git a/Cargo.toml b/Cargo.toml index 178067b..ddeaf95 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kvm-ioctls" -version = "0.14.0" +version = "0.15.0" authors = ["Amazon Firecracker Team "] description = "Safe wrappers over KVM ioctls" repository = "https://github.com/rust-vmm/kvm-ioctls" From e4ee241cc188f253c184ed48b3d5c4fb58a86da9 Mon Sep 17 00:00:00 2001 From: xuejun-xj Date: Tue, 5 Sep 2023 17:20:48 +0800 Subject: [PATCH 233/332] vcpu: export reg_size as a public method For better use of set_one_reg/get_one_reg on arm, it's useful to export the helper method, reg_size, to be used outside of the crate. fixes #234 Signed-off-by: xuejun-xj --- CHANGELOG.md | 2 ++ src/ioctls/vcpu.rs | 2 +- src/lib.rs | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 752408d..5bc8445 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ## Added ## Changed +- [[#234](https://github.com/rust-vmm/kvm-ioctls/issues/234)] vcpu: export +reg_size as a public method. # v0.15.0 diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 0da5f12..f7bcdc3 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -21,7 +21,7 @@ use vmm_sys_util::ioctl::{ioctl_with_mut_ptr, ioctl_with_ptr, ioctl_with_val}; /// Helper method to obtain the size of the register through its id #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] -fn reg_size(reg_id: u64) -> usize { +pub fn reg_size(reg_id: u64) -> usize { 2_usize.pow(((reg_id & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT) as u32) } diff --git a/src/lib.rs b/src/lib.rs index a00f203..4b9a9d0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -217,6 +217,8 @@ mod ioctls; pub use cap::Cap; pub use ioctls::device::DeviceFd; pub use ioctls::system::Kvm; +#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] +pub use ioctls::vcpu::reg_size; pub use ioctls::vcpu::{VcpuExit, VcpuFd}; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] From 5a99554744f86dd6c864c25679da33a5f68d14e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Oct 2023 10:18:55 +0000 Subject: [PATCH 234/332] Bump rust-vmm-ci from `7c1057e` to `9751aaa` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `7c1057e` to `9751aaa`. - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/7c1057e9bcba7ed5090fd62b9fef0570fadb06e6...9751aaa0d0706964b1d4a228509a86bc25ffc0e7) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 7c1057e..9751aaa 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 7c1057e9bcba7ed5090fd62b9fef0570fadb06e6 +Subproject commit 9751aaa0d0706964b1d4a228509a86bc25ffc0e7 From 046074405f760b526fcf24ebbd12eb4a9d8f8273 Mon Sep 17 00:00:00 2001 From: Jonathan Woollett-Light Date: Mon, 2 Oct 2023 10:47:11 +0100 Subject: [PATCH 235/332] Update clippy Signed-off-by: Jonathan Woollett-Light --- src/ioctls/system.rs | 8 ++++---- src/ioctls/vcpu.rs | 19 +++++++++---------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index 69f48d7..c59e164 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -14,7 +14,7 @@ use crate::cap::Cap; use crate::ioctls::vm::{new_vmfd, VmFd}; use crate::ioctls::Result; use crate::kvm_ioctls::*; -#[cfg(any(target_arch = "aarch64"))] +#[cfg(target_arch = "aarch64")] use kvm_bindings::KVM_VM_TYPE_ARM_IPA_SIZE_MASK; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use kvm_bindings::{CpuId, MsrList, Msrs, KVM_MAX_CPUID_ENTRIES, KVM_MAX_MSR_ENTRIES}; @@ -576,7 +576,7 @@ impl Kvm { /// // Check that the VM mmap size is the same reported by `KVM_GET_VCPU_MMAP_SIZE`. /// assert!(vm.run_size() == kvm.get_vcpu_mmap_size().unwrap()); /// ``` - #[cfg(any(target_arch = "aarch64"))] + #[cfg(target_arch = "aarch64")] pub fn create_vm(&self) -> Result { let mut ipa_size = 0; // Create using default VM type if self.check_extension(Cap::ArmVmIPASize) { @@ -615,7 +615,7 @@ impl Kvm { /// assert!(vm.run_size() == kvm.get_vcpu_mmap_size().unwrap()); /// } /// ``` - #[cfg(any(target_arch = "aarch64"))] + #[cfg(target_arch = "aarch64")] pub fn create_vm_with_ipa_size(&self, ipa_size: u32) -> Result { self.create_vm_with_type((ipa_size & KVM_VM_TYPE_ARM_IPA_SIZE_MASK).into()) } @@ -848,7 +848,7 @@ mod tests { } #[test] - #[cfg(any(target_arch = "aarch64"))] + #[cfg(target_arch = "aarch64")] fn test_create_vm_with_ipa_size() { let kvm = Kvm::new().unwrap(); if kvm.check_extension(Cap::ArmVmIPASize) { diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index f7bcdc3..a6f940d 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1391,11 +1391,10 @@ impl VcpuFd { // SAFETY: The data_offset is defined by the kernel to be some number of bytes // into the kvm_run stucture, which we have fully mmap'd. let data_ptr = unsafe { run_start.offset(io.data_offset as isize) }; - // SAFETY: The slice's lifetime is limited to the lifetime of this vCPU, which is equal - // to the mmap of the `kvm_run` struct that this is slicing from. - let data_slice = unsafe { - std::slice::from_raw_parts_mut::(data_ptr as *mut u8, data_size) - }; + let data_slice = + // SAFETY: The slice's lifetime is limited to the lifetime of this vCPU, which is equal + // to the mmap of the `kvm_run` struct that this is slicing from. + unsafe { std::slice::from_raw_parts_mut::(data_ptr, data_size) }; match u32::from(io.direction) { KVM_EXIT_IO_IN => Ok(VcpuExit::IoIn(port, data_slice)), KVM_EXIT_IO_OUT => Ok(VcpuExit::IoOut(port, data_slice)), @@ -1597,7 +1596,7 @@ impl VcpuFd { /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_sync_valid_reg(&mut self, reg: SyncReg) { - let mut kvm_run: &mut kvm_run = self.kvm_run_ptr.as_mut_ref(); + let kvm_run: &mut kvm_run = self.kvm_run_ptr.as_mut_ref(); kvm_run.kvm_valid_regs |= reg as u64; } @@ -1619,7 +1618,7 @@ impl VcpuFd { /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_sync_dirty_reg(&mut self, reg: SyncReg) { - let mut kvm_run: &mut kvm_run = self.kvm_run_ptr.as_mut_ref(); + let kvm_run: &mut kvm_run = self.kvm_run_ptr.as_mut_ref(); kvm_run.kvm_dirty_regs |= reg as u64; } @@ -1641,7 +1640,7 @@ impl VcpuFd { /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn clear_sync_valid_reg(&mut self, reg: SyncReg) { - let mut kvm_run: &mut kvm_run = self.kvm_run_ptr.as_mut_ref(); + let kvm_run: &mut kvm_run = self.kvm_run_ptr.as_mut_ref(); kvm_run.kvm_valid_regs &= !(reg as u64); } @@ -1663,7 +1662,7 @@ impl VcpuFd { /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn clear_sync_dirty_reg(&mut self, reg: SyncReg) { - let mut kvm_run: &mut kvm_run = self.kvm_run_ptr.as_mut_ref(); + let kvm_run: &mut kvm_run = self.kvm_run_ptr.as_mut_ref(); kvm_run.kvm_dirty_regs &= !(reg as u64); } @@ -2714,7 +2713,7 @@ mod tests { let orig_sregs = vcpu.get_sregs().unwrap(); - let mut sync_regs = vcpu.sync_regs_mut(); + let sync_regs = vcpu.sync_regs_mut(); // Initialize the sregs in sync_regs to be the original sregs sync_regs.sregs = orig_sregs; From 98362b11732b815fcd1e4bba15db212c997e62f6 Mon Sep 17 00:00:00 2001 From: Jonathan Woollett-Light Date: Mon, 2 Oct 2023 10:44:42 +0100 Subject: [PATCH 236/332] Update coverage Signed-off-by: Jonathan Woollett-Light --- coverage_config_x86_64.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index 2ab10dd..35ba591 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 84.6, + "coverage_score": 89.60, "exclude_path": "", "crate_features": "" } From 22559d2b565f9d1b3aea5e19f2ffbf061d45cbcd Mon Sep 17 00:00:00 2001 From: Tan En De Date: Wed, 27 Sep 2023 16:30:26 +0800 Subject: [PATCH 237/332] cap: Skip compiling SetGuestDebug for RISC-V at the moment RISC-V currently doesn't support KVM_CAP_SET_GUEST_DEBUG. Signed-off-by: Tan En De --- src/cap.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/cap.rs b/src/cap.rs index 99c5c16..245ae74 100644 --- a/src/cap.rs +++ b/src/cap.rs @@ -41,6 +41,13 @@ pub enum Cap { Iommu = KVM_CAP_IOMMU, DestroyMemoryRegionWorks = KVM_CAP_DESTROY_MEMORY_REGION_WORKS, UserNmi = KVM_CAP_USER_NMI, + #[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64", + target_arch = "s390" + ))] SetGuestDebug = KVM_CAP_SET_GUEST_DEBUG, #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ReinjectControl = KVM_CAP_REINJECT_CONTROL, From 998887ee98ad0d031ee42b41ef8e26b0b66c49c8 Mon Sep 17 00:00:00 2001 From: Tan En De Date: Thu, 28 Sep 2023 13:02:14 +0800 Subject: [PATCH 238/332] .cargo: config: Enable riscv64 cross-compilation Add linker for `--target=riscv64gc-unknown-linux-gnu`. Signed-off-by: Tan En De --- .cargo/config | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.cargo/config b/.cargo/config index ba63e46..d23b917 100644 --- a/.cargo/config +++ b/.cargo/config @@ -1,3 +1,5 @@ [target.aarch64-unknown-linux-musl] rustflags = [ "-C", "target-feature=+crt-static", "-C", "link-arg=-lgcc" ] +[target.riscv64gc-unknown-linux-gnu] +linker = "riscv64-unknown-linux-gnu-gcc" From 8bddeff5c7502ffed58e84a23e7ba585c9da9c26 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Nov 2023 10:07:38 +0000 Subject: [PATCH 239/332] Bump rust-vmm-ci from `9751aaa` to `be28ad8` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `9751aaa` to `be28ad8`. - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/9751aaa0d0706964b1d4a228509a86bc25ffc0e7...be28ad8e2c248875637d791c40282ec3d6e9c639) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 9751aaa..be28ad8 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 9751aaa0d0706964b1d4a228509a86bc25ffc0e7 +Subproject commit be28ad8e2c248875637d791c40282ec3d6e9c639 From 7dda0d053afc2ef5b77e736dad36042abc1bc975 Mon Sep 17 00:00:00 2001 From: Tan En De Date: Thu, 28 Sep 2023 13:16:40 +0800 Subject: [PATCH 240/332] vm: Conditional compilation for test_set_gsi_routing set_gsi_routing is guarded with conditional compilation, so guard test_set_gsi_routing the same way. Signed-off-by: Tan En De --- src/ioctls/vm.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 6b9dc9c..39b305f 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -2137,6 +2137,12 @@ mod tests { } #[test] + #[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" + ))] fn test_set_gsi_routing() { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); From baa70045e9f2ae00191822608f20cc5fdda31ca4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Fri, 17 Nov 2023 10:38:36 +0100 Subject: [PATCH 241/332] Add missing Copy derives MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add free Copy derives where possible. Add a lint to prevent future additions of types that do not derive Copy where possible. Signed-off-by: Carlos López --- CHANGELOG.md | 2 ++ src/ioctls/vm.rs | 4 ++-- src/lib.rs | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bc8445..df2a949 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ ## Changed - [[#234](https://github.com/rust-vmm/kvm-ioctls/issues/234)] vcpu: export reg_size as a public method. +-[[#243](https://github.com/rust-vmm/kvm-ioctls/pull/243)] derived the `Copy` + trait for `IoEventAddress` and `NoDatamatch`. # v0.15.0 diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 39b305f..2663cb4 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -28,7 +28,7 @@ use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_ /// /// The `IoEventAddress` is used for specifying the type when registering an event /// in [register_ioevent](struct.VmFd.html#method.register_ioevent). -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub enum IoEventAddress { /// Representation of an programmable I/O address. Pio(u64), @@ -42,7 +42,7 @@ pub enum IoEventAddress { /// [`register_ioevent`](struct.VmFd.html#method.register_ioevent) /// to disable filtering of events based on the datamatch flag. For details check the /// [KVM API documentation](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub struct NoDatamatch; impl From for u64 { fn from(_: NoDatamatch) -> u64 { diff --git a/src/lib.rs b/src/lib.rs index 4b9a9d0..2c3964d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the THIRD-PARTY file. #![deny(missing_docs)] +#![deny(missing_copy_implementations)] //! A safe wrapper around the kernel's KVM interface. //! From 72fc763d41dc60543b548d2a665cefb3b241bc2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Fri, 17 Nov 2023 10:40:14 +0100 Subject: [PATCH 242/332] Add deny(missing_debug_implementations) lint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prevent future additions of types that do not derive Debug where possible. Signed-off-by: Carlos López --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 2c3964d..9985d6e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,7 @@ // found in the THIRD-PARTY file. #![deny(missing_docs)] #![deny(missing_copy_implementations)] +#![deny(missing_debug_implementations)] //! A safe wrapper around the kernel's KVM interface. //! From 14a08dd39627bc0972ab01cbc9e0f0853478bd9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Fri, 17 Nov 2023 10:49:31 +0100 Subject: [PATCH 243/332] Update coverage_config_x86_64.json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Carlos López --- coverage_config_x86_64.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index 35ba591..7ba1793 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 89.60, + "coverage_score": 88.89, "exclude_path": "", "crate_features": "" } From b07d90690ab8be439326d959d402e0a3345f0764 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Wed, 15 Nov 2023 20:24:27 +0100 Subject: [PATCH 244/332] Add support for KVM_CAP_X86_SMM / KVM_SMI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow VMMs to inject System Management Interrupts (SMIs) into the guest via the KVM_SMI vcpu ioctl, which is available if the KVM_CAP_X86_SMM capability is present. Signed-off-by: Carlos López --- CHANGELOG.md | 6 ++++-- src/cap.rs | 2 ++ src/ioctls/vcpu.rs | 23 +++++++++++++++++++++++ src/kvm_ioctls.rs | 4 ++++ 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df2a949..bd5b2d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,10 @@ ## Changed - [[#234](https://github.com/rust-vmm/kvm-ioctls/issues/234)] vcpu: export reg_size as a public method. --[[#243](https://github.com/rust-vmm/kvm-ioctls/pull/243)] derived the `Copy` - trait for `IoEventAddress` and `NoDatamatch`. +- [[#243](https://github.com/rust-vmm/kvm-ioctls/pull/243)] derived the `Copy` + trait for `IoEventAddress` and `NoDatamatch`. +- [[#242](https://github.com/rust-vmm/kvm-ioctls/pull/242)] x86: add support + for SMI injection via `Vcpu::smi()` (`KVM_SMI` ioctl). # v0.15.0 diff --git a/src/cap.rs b/src/cap.rs index 245ae74..bcd4d12 100644 --- a/src/cap.rs +++ b/src/cap.rs @@ -146,6 +146,8 @@ pub enum Cap { CheckExtensionVm = KVM_CAP_CHECK_EXTENSION_VM, S390UserSigp = KVM_CAP_S390_USER_SIGP, #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + X86Smm = KVM_CAP_X86_SMM, + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] SplitIrqchip = KVM_CAP_SPLIT_IRQCHIP, ArmPmuV3 = KVM_CAP_ARM_PMU_V3, ImmediateExit = KVM_CAP_IMMEDIATE_EXIT, diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index a6f940d..0f76135 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1720,6 +1720,29 @@ impl VcpuFd { // `get_vcpu_map_size`, so this region is in bounds unsafe { &mut kvm_run.s.regs } } + + /// Triggers an SMI on the virtual CPU. + /// + /// See documentation for `KVM_SMI`. + /// + /// ```rust + /// # use kvm_ioctls::{Kvm, Cap}; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// if kvm.check_extension(Cap::X86Smm) { + /// vcpu.smi().unwrap(); + /// } + /// ``` + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn smi(&self) -> Result<()> { + // SAFETY: Safe because we call this with a Vcpu fd and we trust the kernel. + let ret = unsafe { ioctl(self, KVM_SMI()) }; + match ret { + 0 => Ok(()), + _ => Err(errno::Error::last()), + } + } } /// Helper function to create a new `VcpuFd`. diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index 5b9360c..f3f58a9 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -240,6 +240,10 @@ ioctl_ior_nr!(KVM_ARM_PREFERRED_TARGET, KVMIO, 0xaf, kvm_vcpu_init); #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] ioctl_iowr_nr!(KVM_GET_REG_LIST, KVMIO, 0xb0, kvm_reg_list); +/* Available with KVM_CAP_X86_SMM */ +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_io_nr!(KVM_SMI, KVMIO, 0xb7); + /* Available with KVM_CAP_ARM_SVE */ #[cfg(target_arch = "aarch64")] ioctl_iow_nr!(KVM_ARM_VCPU_FINALIZE, KVMIO, 0xc2, std::os::raw::c_int); From 209f415c9de13a2d11bb63a14be64ea7e1b1cbb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Wed, 15 Nov 2023 10:52:01 +0100 Subject: [PATCH 245/332] Add support for userspace MSR handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the appropriate types to allow for a VMM to intercept RDMSR and WRMSR instructions if access to an MSR is denied. By default, KVM injects #GP on denied accesses. To do this we need to add a new Cap variant which corresponds to KVM_CAP_X86_USER_SPACE_MSR, and two new VcpuExit variants that correspond to KVM_EXIT_X86_RDMSR and KVM_EXIT_X86_WRMSR respectively. We also need helper types to pass the relevant information about the exit to the VMM. Signed-off-by: Carlos López --- CHANGELOG.md | 2 ++ Cargo.toml | 1 + src/cap.rs | 2 ++ src/ioctls/vcpu.rs | 82 +++++++++++++++++++++++++++++++++++++++++++++- src/lib.rs | 2 +- 5 files changed, 87 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd5b2d0..8047314 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ reg_size as a public method. trait for `IoEventAddress` and `NoDatamatch`. - [[#242](https://github.com/rust-vmm/kvm-ioctls/pull/242)] x86: add support for SMI injection via `Vcpu::smi()` (`KVM_SMI` ioctl). +- [[#241](https://github.com/rust-vmm/kvm-ioctls/pull/241)] Add support for + userspace MSR handling. # v0.15.0 diff --git a/Cargo.toml b/Cargo.toml index ddeaf95..dc5729f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ edition = "2021" libc = "0.2.39" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } vmm-sys-util = "0.11.0" +bitflags = "2.4.1" [dev-dependencies] byteorder = "1.2.1" diff --git a/src/cap.rs b/src/cap.rs index bcd4d12..b8bfd15 100644 --- a/src/cap.rs +++ b/src/cap.rs @@ -164,4 +164,6 @@ pub enum Cap { ArmPtrAuthAddress = KVM_CAP_ARM_PTRAUTH_ADDRESS, #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] ArmPtrAuthGeneric = KVM_CAP_ARM_PTRAUTH_GENERIC, + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + X86UserSpaceMsr = KVM_CAP_X86_USER_SPACE_MSR, } diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 0f76135..193e474 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -13,7 +13,10 @@ use std::os::unix::io::{AsRawFd, RawFd}; use crate::ioctls::{KvmRunWrapper, Result}; use crate::kvm_ioctls::*; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -use kvm_bindings::{CpuId, Msrs, KVM_MAX_CPUID_ENTRIES}; +use kvm_bindings::{ + CpuId, Msrs, KVM_MAX_CPUID_ENTRIES, KVM_MSR_EXIT_REASON_FILTER, KVM_MSR_EXIT_REASON_INVAL, + KVM_MSR_EXIT_REASON_UNKNOWN, +}; use vmm_sys_util::errno; use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ref}; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] @@ -25,6 +28,55 @@ pub fn reg_size(reg_id: u64) -> usize { 2_usize.pow(((reg_id & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT) as u32) } +/// Information about a [`VcpuExit`] triggered by an MSR read (`KVM_EXIT_X86_RDMSR`). +#[derive(Debug)] +pub struct ReadMsrExit<'a> { + /// Must be set to 1 by the the user if the read access should fail. This + /// will inject a #GP fault into the guest when the VCPU is executed + /// again. + pub error: &'a mut u8, + /// The reason for this exit. + pub reason: MsrExitReason, + /// The MSR the guest wants to read. + pub index: u32, + /// The data to be supplied by the user as the MSR Contents to the guest. + pub data: &'a mut u64, +} + +/// Information about a [`VcpuExit`] triggered by an MSR write (`KVM_EXIT_X86_WRMSR`). +#[derive(Debug)] +pub struct WriteMsrExit<'a> { + /// Must be set to 1 by the the user if the write access should fail. This + /// will inject a #GP fault into the guest when the VCPU is executed + /// again. + pub error: &'a mut u8, + /// The reason for this exit. + pub reason: MsrExitReason, + /// The MSR the guest wants to write. + pub index: u32, + /// The data the guest wants to write into the MSR. + pub data: u64, +} + +bitflags::bitflags! { + /// The reason for a [`VcpuExit::X86Rdmsr`] or[`VcpuExit::X86Wrmsr`]. This + /// is also used when enabling + /// [`Cap::X86UserSpaceMsr`](crate::Cap::X86UserSpaceMsr) to specify which + /// reasons should be forwarded to the user via those exits. + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub struct MsrExitReason: u32 { + /// Corresponds to [`KVM_MSR_EXIT_REASON_UNKNOWN`]. The exit was + /// triggered by an access to an MSR that is unknown to KVM. + const Unknown = KVM_MSR_EXIT_REASON_UNKNOWN; + /// Corresponds to [`KVM_MSR_EXIT_REASON_INVAL`]. The exit was + /// triggered by an access to an invalid MSR or to reserved bits. + const Inval = KVM_MSR_EXIT_REASON_INVAL; + /// Corresponds to [`KVM_MSR_EXIT_REASON_FILTER`]. The exit was + /// triggered by an access to a filtered MSR. + const Filter = KVM_MSR_EXIT_REASON_FILTER; + } +} + /// Reasons for vCPU exits. /// /// The exit reasons are mapped to the `KVM_EXIT_*` defines in the @@ -102,6 +154,10 @@ pub enum VcpuExit<'a> { IoapicEoi(u8 /* vector */), /// Corresponds to KVM_EXIT_HYPERV. Hyperv, + /// Corresponds to KVM_EXIT_X86_RDMSR. + X86Rdmsr(ReadMsrExit<'a>), + /// Corresponds to KVM_EXIT_X86_WRMSR. + X86Wrmsr(WriteMsrExit<'a>), /// Corresponds to an exit reason that is unknown from the current version /// of the kvm-ioctls crate. Let the consumer decide about what to do with /// it. @@ -1422,6 +1478,30 @@ impl VcpuFd { Ok(VcpuExit::MmioRead(addr, data_slice)) } } + KVM_EXIT_X86_RDMSR => { + // SAFETY: Safe because the exit_reason (which comes from the kernel) told us + // which union field to use. + let msr = unsafe { &mut run.__bindgen_anon_1.msr }; + let exit = ReadMsrExit { + error: &mut msr.error, + reason: MsrExitReason::from_bits_truncate(msr.reason), + index: msr.index, + data: &mut msr.data, + }; + Ok(VcpuExit::X86Rdmsr(exit)) + } + KVM_EXIT_X86_WRMSR => { + // SAFETY: Safe because the exit_reason (which comes from the kernel) told us + // which union field to use. + let msr = unsafe { &mut run.__bindgen_anon_1.msr }; + let exit = WriteMsrExit { + error: &mut msr.error, + reason: MsrExitReason::from_bits_truncate(msr.reason), + index: msr.index, + data: msr.data, + }; + Ok(VcpuExit::X86Wrmsr(exit)) + } KVM_EXIT_IRQ_WINDOW_OPEN => Ok(VcpuExit::IrqWindowOpen), KVM_EXIT_SHUTDOWN => Ok(VcpuExit::Shutdown), KVM_EXIT_FAIL_ENTRY => { diff --git a/src/lib.rs b/src/lib.rs index 9985d6e..d999d74 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -224,7 +224,7 @@ pub use ioctls::vcpu::reg_size; pub use ioctls::vcpu::{VcpuExit, VcpuFd}; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -pub use ioctls::vcpu::SyncReg; +pub use ioctls::vcpu::{MsrExitReason, ReadMsrExit, SyncReg, WriteMsrExit}; pub use ioctls::vm::{IoEventAddress, NoDatamatch, VmFd}; // The following example is used to verify that our public From 8bc6129b6076667ba82f4edb967e79212fb15efa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Wed, 15 Nov 2023 12:00:23 +0100 Subject: [PATCH 246/332] Add tests for userspace MSR handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add two tests to verify respectively that reading and writing to an invalid MSR trigger a VcpuExit. Signed-off-by: Carlos López --- src/ioctls/vcpu.rs | 143 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 193e474..c870b5d 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -2906,4 +2906,147 @@ mod tests { } assert!(vcpu.vcpu_init(&kvi).is_ok()); } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[test] + fn test_userspace_rdmsr_exit() { + use std::io::Write; + + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + #[rustfmt::skip] + let code = [ + 0x0F, 0x32, /* rdmsr */ + 0xF4 /* hlt */ + ]; + + if !vm.check_extension(Cap::X86UserSpaceMsr) { + return; + } + let cap = kvm_enable_cap { + cap: Cap::X86UserSpaceMsr as u32, + args: [MsrExitReason::Unknown.bits() as u64, 0, 0, 0], + ..Default::default() + }; + vm.enable_cap(&cap).unwrap(); + + let mem_size = 0x4000; + let load_addr = mmap_anonymous(mem_size); + let guest_addr: u64 = 0x1000; + let slot: u32 = 0; + let mem_region = kvm_userspace_memory_region { + slot, + guest_phys_addr: guest_addr, + memory_size: mem_size as u64, + userspace_addr: load_addr as u64, + flags: 0, + }; + unsafe { + vm.set_user_memory_region(mem_region).unwrap(); + + // Get a mutable slice of `mem_size` from `load_addr`. + // This is safe because we mapped it before. + let mut slice = std::slice::from_raw_parts_mut(load_addr, mem_size); + slice.write_all(&code).unwrap(); + } + + let vcpu = vm.create_vcpu(0).unwrap(); + + // Set up special registers + let mut vcpu_sregs = vcpu.get_sregs().unwrap(); + assert_ne!(vcpu_sregs.cs.base, 0); + assert_ne!(vcpu_sregs.cs.selector, 0); + vcpu_sregs.cs.base = 0; + vcpu_sregs.cs.selector = 0; + vcpu.set_sregs(&vcpu_sregs).unwrap(); + + // Set the Instruction Pointer to the guest address where we loaded + // the code, and RCX to the MSR to be read. + let mut vcpu_regs = vcpu.get_regs().unwrap(); + vcpu_regs.rip = guest_addr; + vcpu_regs.rcx = 0x474f4f00; + vcpu.set_regs(&vcpu_regs).unwrap(); + + match vcpu.run().unwrap() { + VcpuExit::X86Rdmsr(exit) => { + assert_eq!(exit.reason, MsrExitReason::Unknown); + assert_eq!(exit.index, 0x474f4f00); + } + e => panic!("Unexpected exit: {:?}", e), + } + } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[test] + fn test_userspace_wrmsr_exit() { + use std::io::Write; + + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + #[rustfmt::skip] + let code = [ + 0x0F, 0x30, /* wrmsr */ + 0xF4 /* hlt */ + ]; + + if !vm.check_extension(Cap::X86UserSpaceMsr) { + return; + } + let cap = kvm_enable_cap { + cap: Cap::X86UserSpaceMsr as u32, + args: [MsrExitReason::Unknown.bits() as u64, 0, 0, 0], + ..Default::default() + }; + vm.enable_cap(&cap).unwrap(); + + let mem_size = 0x4000; + let load_addr = mmap_anonymous(mem_size); + let guest_addr: u64 = 0x1000; + let slot: u32 = 0; + let mem_region = kvm_userspace_memory_region { + slot, + guest_phys_addr: guest_addr, + memory_size: mem_size as u64, + userspace_addr: load_addr as u64, + flags: 0, + }; + unsafe { + vm.set_user_memory_region(mem_region).unwrap(); + + // Get a mutable slice of `mem_size` from `load_addr`. + // This is safe because we mapped it before. + let mut slice = std::slice::from_raw_parts_mut(load_addr, mem_size); + slice.write_all(&code).unwrap(); + } + + let vcpu = vm.create_vcpu(0).unwrap(); + + // Set up special registers + let mut vcpu_sregs = vcpu.get_sregs().unwrap(); + assert_ne!(vcpu_sregs.cs.base, 0); + assert_ne!(vcpu_sregs.cs.selector, 0); + vcpu_sregs.cs.base = 0; + vcpu_sregs.cs.selector = 0; + vcpu.set_sregs(&vcpu_sregs).unwrap(); + + // Set the Instruction Pointer to the guest address where we loaded + // the code, RCX to the MSR to be written, and EDX:EAX to the data to + // be written. + let mut vcpu_regs = vcpu.get_regs().unwrap(); + vcpu_regs.rip = guest_addr; + vcpu_regs.rcx = 0x474f4f00; + vcpu_regs.rax = 0xdeadbeef; + vcpu_regs.rdx = 0xd0c0ffee; + vcpu.set_regs(&vcpu_regs).unwrap(); + + match vcpu.run().unwrap() { + VcpuExit::X86Wrmsr(exit) => { + assert_eq!(exit.reason, MsrExitReason::Unknown); + assert_eq!(exit.index, 0x474f4f00); + assert_eq!(exit.data & 0xffffffff, 0xdeadbeef); + assert_eq!((exit.data >> 32) & 0xffffffff, 0xd0c0ffee); + } + e => panic!("Unexpected exit: {:?}", e), + } + } } From a569412237a1e078cca91428da0626504c554962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Fri, 17 Nov 2023 12:43:55 +0100 Subject: [PATCH 247/332] Update coverage_config_x86_64.json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Carlos López --- coverage_config_x86_64.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index 7ba1793..f93dc95 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 88.89, + "coverage_score": 87.40, "exclude_path": "", "crate_features": "" } From c61285025ab6f388fa8f23551426c8f0778e7cd9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 10:09:20 +0000 Subject: [PATCH 248/332] Bump rust-vmm-ci from `be28ad8` to `0100de0` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `be28ad8` to `0100de0`. - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/be28ad8e2c248875637d791c40282ec3d6e9c639...0100de0f57ec8d9f55141b43b7332a5d7f357496) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index be28ad8..0100de0 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit be28ad8e2c248875637d791c40282ec3d6e9c639 +Subproject commit 0100de0f57ec8d9f55141b43b7332a5d7f357496 From 111f11a43984c45f3b4dbd1e9be3ed9aa1407963 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Mon, 27 Nov 2023 09:15:19 +0100 Subject: [PATCH 249/332] Add support for userspace NMI injection (KVM_NMI) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the KVM_NMI ioctl, which allows a VMM to inject a non maskable interrupt into the guest. Signed-off-by: Carlos López --- CHANGELOG.md | 2 ++ src/ioctls/vcpu.rs | 26 ++++++++++++++++++++++++++ src/kvm_ioctls.rs | 3 +++ 3 files changed, 31 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8047314..d558de4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ reg_size as a public method. for SMI injection via `Vcpu::smi()` (`KVM_SMI` ioctl). - [[#241](https://github.com/rust-vmm/kvm-ioctls/pull/241)] Add support for userspace MSR handling. +- [[#246](https://github.com/rust-vmm/kvm-ioctls/pull/246)] Add support for + userspace NMI injection (`KVM_NMI` ioctl). # v0.15.0 diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index c870b5d..1154dfc 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1823,6 +1823,32 @@ impl VcpuFd { _ => Err(errno::Error::last()), } } + + /// Queues an NMI on the thread's vcpu. Only usable if `KVM_CAP_USER_NMI` + /// is available. + /// + /// See the documentation for `KVM_NMI`. + /// + /// # Example + /// + /// ```rust + /// # use kvm_ioctls::{Kvm, Cap}; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let vcpu = vm.create_vcpu(0).unwrap(); + /// if kvm.check_extension(Cap::UserNmi) { + /// vcpu.nmi().unwrap(); + /// } + /// ``` + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn nmi(&self) -> Result<()> { + // SAFETY: Safe because we call this with a Vcpu fd and we trust the kernel. + let ret = unsafe { ioctl(self, KVM_NMI()) }; + match ret { + 0 => Ok(()), + _ => Err(errno::Error::last()), + } + } } /// Helper function to create a new `VcpuFd`. diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index f3f58a9..a173778 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -172,6 +172,9 @@ ioctl_ior_nr!(KVM_GET_MP_STATE, KVMIO, 0x98, kvm_mp_state); target_arch = "s390" ))] ioctl_iow_nr!(KVM_SET_MP_STATE, KVMIO, 0x99, kvm_mp_state); +/* Available with KVM_CAP_USER_NMI */ +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +ioctl_io_nr!(KVM_NMI, KVMIO, 0x9a); /* Available with KVM_CAP_VCPU_EVENTS */ #[cfg(any( target_arch = "x86", From 2a102e7e2240e3707796261d57f61ed347bf37e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Mon, 27 Nov 2023 09:27:16 +0100 Subject: [PATCH 250/332] Update coverage_config_x86_64.json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Carlos López --- coverage_config_x86_64.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index f93dc95..f3a954e 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 87.40, + "coverage_score": 86.74, "exclude_path": "", "crate_features": "" } From b6604a089d73d8bbd89edcb60e2659b833371bd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Tue, 21 Nov 2023 11:33:07 +0100 Subject: [PATCH 251/332] Add support for coalesced MMIO (KVM_CAP_COALESCED_MMIO) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for coalesced MMIO. This performance feature allows guest writes to port and memory space to not trigger VM exits. Instead, the kernel will write an entry into a shared ring buffer for each access, which userspace must consume. The ring buffer is mapped at a certain offset in the vcpu's file descriptor. In order to enable this capability, introduce the KvmCoalescedIoRing struct, which will act as a safe wrapper around the raw mapping of the ring buffer. Since users may not use coalesced MMIO, or it might not be available, store it as an Option in the VcpuFd struct. Signed-off-by: Carlos López --- CHANGELOG.md | 2 + src/cap.rs | 1 + src/ioctls/mod.rs | 99 +++++++++++++++++++++++++++++++++++++++++++++- src/ioctls/vcpu.rs | 60 +++++++++++++++++++++++++++- src/ioctls/vm.rs | 62 +++++++++++++++++++++++++++++ src/kvm_ioctls.rs | 14 +++++++ 6 files changed, 235 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d558de4..96c78ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ reg_size as a public method. userspace MSR handling. - [[#246](https://github.com/rust-vmm/kvm-ioctls/pull/246)] Add support for userspace NMI injection (`KVM_NMI` ioctl). +- [[#244](https://github.com/rust-vmm/kvm-ioctls/pull/244)] add support for + coalesced MMIO (`KVM_CAP_COALESCED_MMIO` / `KVM_CAP_COALESCED_PIO`) # v0.15.0 diff --git a/src/cap.rs b/src/cap.rs index b8bfd15..71b7181 100644 --- a/src/cap.rs +++ b/src/cap.rs @@ -158,6 +158,7 @@ pub enum Cap { DebugHwBps = KVM_CAP_GUEST_DEBUG_HW_BPS, DebugHwWps = KVM_CAP_GUEST_DEBUG_HW_WPS, GetMsrFeatures = KVM_CAP_GET_MSR_FEATURES, + CoalescedPio = KVM_CAP_COALESCED_PIO, #[cfg(target_arch = "aarch64")] ArmSve = KVM_CAP_ARM_SVE, #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] diff --git a/src/ioctls/mod.rs b/src/ioctls/mod.rs index 9079acc..e3ed2f7 100644 --- a/src/ioctls/mod.rs +++ b/src/ioctls/mod.rs @@ -5,10 +5,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the THIRD-PARTY file. +use std::mem::size_of; use std::os::unix::io::AsRawFd; use std::ptr::null_mut; -use kvm_bindings::kvm_run; +use kvm_bindings::{ + kvm_coalesced_mmio, kvm_coalesced_mmio_ring, kvm_run, KVM_COALESCED_MMIO_PAGE_OFFSET, +}; use vmm_sys_util::errno; /// Wrappers over KVM device ioctls. @@ -26,6 +29,100 @@ pub mod vm; /// is otherwise a direct mapping to Result. pub type Result = std::result::Result; +/// A wrapper around the coalesced MMIO ring page. +#[derive(Debug)] +pub(crate) struct KvmCoalescedIoRing { + addr: *mut kvm_coalesced_mmio_ring, + page_size: usize, +} + +impl KvmCoalescedIoRing { + /// Maps the coalesced MMIO ring from the vCPU file descriptor. + pub(crate) fn mmap_from_fd(fd: &F) -> Result { + // SAFETY: We trust the sysconf libc function and we're calling it + // with a correct parameter. + let page_size = match unsafe { libc::sysconf(libc::_SC_PAGESIZE) } { + -1 => return Err(errno::Error::last()), + ps => ps as usize, + }; + + let offset = KVM_COALESCED_MMIO_PAGE_OFFSET * page_size as u32; + // SAFETY: KVM guarantees that there is a page at offset + // KVM_COALESCED_MMIO_PAGE_OFFSET * PAGE_SIZE if the appropriate + // capability is available. If it is not, the call will simply + // fail. + let addr = unsafe { + libc::mmap( + null_mut(), + page_size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_SHARED, + fd.as_raw_fd(), + offset.into(), + ) + }; + if addr == libc::MAP_FAILED { + return Err(errno::Error::last()); + } + Ok(Self { + addr: addr.cast(), + page_size, + }) + } + + /// Compute the size of the MMIO ring. + /// Taken from [include/uapi/linux/kvm.h](https://elixir.bootlin.com/linux/v6.6/source/include/uapi/linux/kvm.h#L562) + const fn ring_max(&self) -> usize { + (self.page_size - size_of::()) / size_of::() + } + + /// Gets a mutable reference to the ring + fn ring_mut(&mut self) -> &mut kvm_coalesced_mmio_ring { + // SAFETY: We have a `&mut self` and the pointer is private, so this + // access is exclusive. + unsafe { &mut *self.addr } + } + + /// Reads a single entry from the MMIO ring. + /// + /// # Returns + /// + /// An entry from the MMIO ring buffer, or [`None`] if the ring is empty. + pub(crate) fn read_entry(&mut self) -> Option { + let ring_max = self.ring_max(); + + let ring = self.ring_mut(); + if ring.first == ring.last { + return None; + } + + let entries = ring.coalesced_mmio.as_ptr(); + // SAFETY: `ring.first` is an `u32` coming from mapped memory filled + // by the kernel, so we trust it. `entries` is a pointer coming from + // mmap(), so pointer arithmetic cannot overflow. We have a `&mut self`, + // so nobody else has access to the contents of the pointer. + let elem = unsafe { entries.add(ring.first as usize).read() }; + ring.first = (ring.first + 1) % ring_max as u32; + + Some(elem) + } +} + +impl Drop for KvmCoalescedIoRing { + fn drop(&mut self) { + // SAFETY: This is safe because we mmap the page ourselves, and nobody + // else is holding a reference to it. + unsafe { + libc::munmap(self.addr.cast(), self.page_size); + } + } +} + +// SAFETY: See safety comments about [`KvmRunWrapper`]. +unsafe impl Send for KvmCoalescedIoRing {} +// SAFETY: See safety comments about [`KvmRunWrapper`]. +unsafe impl Sync for KvmCoalescedIoRing {} + /// Safe wrapper over the `kvm_run` struct. /// /// The wrapper is needed for sending the pointer to `kvm_run` between diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 1154dfc..c309742 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -10,7 +10,7 @@ use libc::EINVAL; use std::fs::File; use std::os::unix::io::{AsRawFd, RawFd}; -use crate::ioctls::{KvmRunWrapper, Result}; +use crate::ioctls::{KvmCoalescedIoRing, KvmRunWrapper, Result}; use crate::kvm_ioctls::*; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use kvm_bindings::{ @@ -169,6 +169,8 @@ pub enum VcpuExit<'a> { pub struct VcpuFd { vcpu: File, kvm_run_ptr: KvmRunWrapper, + /// A pointer to the coalesced MMIO page + coalesced_mmio_ring: Option, } /// KVM Sync Registers used to tell KVM which registers to sync @@ -1849,6 +1851,55 @@ impl VcpuFd { _ => Err(errno::Error::last()), } } + + /// Maps the coalesced MMIO ring page. This allows reading entries from + /// the ring via [`coalesced_mmio_read()`](VcpuFd::coalesced_mmio_read). + /// + /// # Returns + /// + /// Returns an error if the buffer could not be mapped, usually because + /// `KVM_CAP_COALESCED_MMIO` ([`Cap::CoalescedMmio`](crate::Cap::CoalescedMmio)) + /// is not available. + /// + /// # Examples + /// + /// ```rust + /// # use kvm_ioctls::{Kvm, Cap}; + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let mut vcpu = vm.create_vcpu(0).unwrap(); + /// if kvm.check_extension(Cap::CoalescedMmio) { + /// vcpu.map_coalesced_mmio_ring().unwrap(); + /// } + /// ``` + pub fn map_coalesced_mmio_ring(&mut self) -> Result<()> { + if self.coalesced_mmio_ring.is_none() { + let ring = KvmCoalescedIoRing::mmap_from_fd(&self.vcpu)?; + self.coalesced_mmio_ring = Some(ring); + } + Ok(()) + } + + /// Read a single entry from the coalesced MMIO ring. + /// For entries to be appended to the ring by the kernel, addresses must be registered + /// via [`VmFd::register_coalesced_mmio()`](crate::VmFd::register_coalesced_mmio()). + /// + /// [`map_coalesced_mmio_ring()`](VcpuFd::map_coalesced_mmio_ring) must have been called beforehand. + /// + /// See the documentation for `KVM_(UN)REGISTER_COALESCED_MMIO`. + /// + /// # Returns + /// + /// * An error if [`map_coalesced_mmio_ring()`](VcpuFd::map_coalesced_mmio_ring) + /// was not called beforehand. + /// * [`Ok`] if the ring is empty. + /// * [`Ok>`] if an entry was successfully read. + pub fn coalesced_mmio_read(&mut self) -> Result> { + self.coalesced_mmio_ring + .as_mut() + .ok_or(errno::Error::new(libc::EIO)) + .map(|ring| ring.read_entry()) + } } /// Helper function to create a new `VcpuFd`. @@ -1857,7 +1908,11 @@ impl VcpuFd { /// `create_vcpu` from `VmFd`. The function cannot be part of the `VcpuFd` implementation because /// then it would be exported with the public `VcpuFd` interface. pub fn new_vcpu(vcpu: File, kvm_run_ptr: KvmRunWrapper) -> VcpuFd { - VcpuFd { vcpu, kvm_run_ptr } + VcpuFd { + vcpu, + kvm_run_ptr, + coalesced_mmio_ring: None, + } } impl AsRawFd for VcpuFd { @@ -2440,6 +2495,7 @@ mod tests { kvm_run_ptr: mmap_anonymous(10), mmap_size: 10, }, + coalesced_mmio_ring: None, }; assert_eq!(faulty_vcpu_fd.get_regs().unwrap_err().errno(), badf_errno); diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 2663cb4..813c490 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -1562,6 +1562,68 @@ impl VmFd { Err(errno::Error::last()) } } + + /// Registers an address for coalesced MMIO. Write accesses to the address + /// will not cause a corresponding [`VcpuExit`](crate::VcpuExit), but + /// instead will be appended to the MMIO ring buffer. The [`VcpuFd`] can + /// read entries in the ring buffer via [`VcpuFd::coalesced_mmio_read()`]. + /// If entries are not read the buffer will eventually be full, + /// preventing further elements from being appended by the kernel. + /// + /// Needs `KVM_CAP_COALESCED_MMIO` ([`Cap::CoalescedMmio`](crate::Cap::CoalescedMmio)) + /// and/or `KVM_CAP_COALESCED_PIO` ([`Cap::CoalescedMmio`](crate::Cap::CoalescedPio)). + /// + /// See the documentation for `KVM_REGISTER_COALESCED_MMIO`. + /// + /// # Arguments + /// + /// * `addr` - Address being written to. + /// * `size` - The size of the write for the mechanism to trigger. + pub fn register_coalesced_mmio(&self, addr: IoEventAddress, size: u32) -> Result<()> { + let (addr, pio) = match addr { + IoEventAddress::Pio(addr) => (addr, 1), + IoEventAddress::Mmio(addr) => (addr, 0), + }; + let mut zone = kvm_coalesced_mmio_zone { + addr, + size, + ..Default::default() + }; + zone.__bindgen_anon_1.pio = pio; + + // SAFETY: Safe because we know that our file is a VM fd, we know the kernel will only read + // the correct amount of memory from our pointer, and we verify the return result. + let ret = unsafe { ioctl_with_ref(self, KVM_REGISTER_COALESCED_MMIO(), &zone) }; + if ret != 0 { + return Err(errno::Error::last()); + } + Ok(()) + } + + /// Unregister an address that was previously registered via + /// [`register_coalesced_mmio()`](VmFd::register_coalesced_mmio). + /// + /// See the documentation for `KVM_UNREGISTER_COALESCED_MMIO`. + pub fn unregister_coalesced_mmio(&self, addr: IoEventAddress, size: u32) -> Result<()> { + let (addr, pio) = match addr { + IoEventAddress::Pio(addr) => (addr, 1), + IoEventAddress::Mmio(addr) => (addr, 0), + }; + let mut zone = kvm_coalesced_mmio_zone { + addr, + size, + ..Default::default() + }; + zone.__bindgen_anon_1.pio = pio; + + // SAFETY: Safe because we know that our file is a VM fd, we know the kernel will only read + // the correct amount of memory from our pointer, and we verify the return result. + let ret = unsafe { ioctl_with_ref(self, KVM_UNREGISTER_COALESCED_MMIO(), &zone) }; + if ret != 0 { + return Err(errno::Error::last()); + } + Ok(()) + } } /// Helper function to create a new `VmFd`. diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index a173778..397bb14 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -61,6 +61,20 @@ ioctl_io_nr!(KVM_CREATE_IRQCHIP, KVMIO, 0x60); target_arch = "aarch64" ))] ioctl_iow_nr!(KVM_IRQ_LINE, KVMIO, 0x61, kvm_irq_level); +/* Available with KVM_CAP_COALESCED_MMIO / KVM_CAP_COALESCED_PIO */ +ioctl_iow_nr!( + KVM_REGISTER_COALESCED_MMIO, + KVMIO, + 0x67, + kvm_coalesced_mmio_zone +); +/* Available with KVM_CAP_COALESCED_MMIO / KVM_CAP_COALESCED_PIO */ +ioctl_iow_nr!( + KVM_UNREGISTER_COALESCED_MMIO, + KVMIO, + 0x68, + kvm_coalesced_mmio_zone +); /* Available with KVM_CAP_IRQ_ROUTING */ #[cfg(any( target_arch = "x86", From 8f68bf6f6d974346c1fa725282deeafadace457b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Tue, 21 Nov 2023 12:40:36 +0100 Subject: [PATCH 252/332] Add tests for coalesced MMIO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add tests for coalesced MMIO and PIO respectively. Both tests are similar. They: 1. Set up a guest 2. Register a coalesced MMIO/PIO address 3. Map the ring buffer 4. Trigger a MMIO/PIO write 5. Check that the access did not trigger a VM exit 6. Check that there is a new matching entry in the ring buffer. 7. Unregister the MMIO/PIO address 8. Trigger a MMIO/PIO write 9. Check that the access triggered a VM exit Signed-off-by: Carlos López --- src/ioctls/vcpu.rs | 191 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index c309742..a6fc0e8 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -3131,4 +3131,195 @@ mod tests { e => panic!("Unexpected exit: {:?}", e), } } + + #[test] + #[cfg(target_arch = "x86_64")] + fn test_coalesced_pio() { + use crate::IoEventAddress; + use std::io::Write; + + const PORT: u64 = 0x2c; + const DATA: u64 = 0x39; + const SIZE: u32 = 1; + + #[rustfmt::skip] + let code = [ + 0xe6, 0x2c, // out 0x2c, al + 0xf4, // hlt + 0xe6, 0x2c, // out 0x2c, al + 0xf4, // hlt + ]; + + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + assert!(vm.check_extension(Cap::CoalescedPio)); + + // Prepare guest memory + let mem_size = 0x4000; + let load_addr = mmap_anonymous(mem_size); + let guest_addr: u64 = 0x1000; + let slot = 0; + let mem_region = kvm_userspace_memory_region { + slot, + guest_phys_addr: guest_addr, + memory_size: mem_size as u64, + userspace_addr: load_addr as u64, + flags: 0, + }; + + unsafe { + vm.set_user_memory_region(mem_region).unwrap(); + + // Get a mutable slice of `mem_size` from `load_addr`. + // This is safe because we mapped it before. + let mut slice = std::slice::from_raw_parts_mut(load_addr, mem_size); + slice.write_all(&code).unwrap(); + } + + let addr = IoEventAddress::Pio(PORT); + vm.register_coalesced_mmio(addr, SIZE).unwrap(); + + let mut vcpu = vm.create_vcpu(0).unwrap(); + + // Map the MMIO ring + vcpu.map_coalesced_mmio_ring().unwrap(); + + // Set regs + let mut regs = vcpu.get_regs().unwrap(); + regs.rip = guest_addr; + regs.rax = DATA; + regs.rflags = 2; + vcpu.set_regs(®s).unwrap(); + + // Set sregs + let mut sregs = vcpu.get_sregs().unwrap(); + sregs.cs.base = 0; + sregs.cs.selector = 0; + vcpu.set_sregs(&sregs).unwrap(); + + // Run and check that the exit was caused by the hlt and not the port + // I/O + let exit = vcpu.run().unwrap(); + assert!(matches!(exit, VcpuExit::Hlt)); + + // Check that the ring buffer entry is what we expect + let entry = vcpu.coalesced_mmio_read().unwrap().unwrap(); + assert_eq!(entry.phys_addr, PORT); + assert_eq!(entry.len, 1); + assert_eq!(entry.data[0] as u64, DATA); + // SAFETY: this field is a u32 in all variants of the union, + // so access is always safe. + let pio = unsafe { entry.__bindgen_anon_1.pio }; + assert_eq!(pio, 1); + + // The ring buffer should be empty now + assert!(vcpu.coalesced_mmio_read().unwrap().is_none()); + + // Unregister and check that the next PIO write triggers an exit + vm.unregister_coalesced_mmio(addr, SIZE).unwrap(); + let exit = vcpu.run().unwrap(); + let VcpuExit::IoOut(port, data) = exit else { + panic!("Unexpected VM exit: {:?}", exit); + }; + assert_eq!(port, PORT as u16); + assert_eq!(data, (DATA as u8).to_le_bytes()); + } + + #[test] + #[cfg(target_arch = "x86_64")] + fn test_coalesced_mmio() { + use crate::IoEventAddress; + use std::io::Write; + + const ADDR: u64 = 0x124; + const DATA: u64 = 0x39; + const SIZE: u32 = 2; + + #[rustfmt::skip] + let code = [ + 0x66, 0x31, 0xFF, // xor di,di + 0x66, 0xBF, 0x24, 0x01, // mov di, 0x124 + 0x67, 0x66, 0x89, 0x05, // mov WORD PTR [di], ax + 0xF4, // hlt + 0x66, 0x31, 0xFF, // xor di,di + 0x66, 0xBF, 0x24, 0x01, // mov di, 0x124 + 0x67, 0x66, 0x89, 0x05, // mov WORD PTR [di], ax + 0xF4, // hlt + ]; + + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + assert!(vm.check_extension(Cap::CoalescedMmio)); + + // Prepare guest memory + let mem_size = 0x4000; + let load_addr = mmap_anonymous(mem_size); + let guest_addr: u64 = 0x1000; + let slot: u32 = 0; + let mem_region = kvm_userspace_memory_region { + slot, + guest_phys_addr: guest_addr, + memory_size: mem_size as u64, + userspace_addr: load_addr as u64, + flags: 0, + }; + + unsafe { + vm.set_user_memory_region(mem_region).unwrap(); + + // Get a mutable slice of `mem_size` from `load_addr`. + // This is safe because we mapped it before. + let mut slice = std::slice::from_raw_parts_mut(load_addr, mem_size); + slice.write_all(&code).unwrap(); + } + + let addr = IoEventAddress::Mmio(ADDR); + vm.register_coalesced_mmio(addr, SIZE).unwrap(); + + let mut vcpu = vm.create_vcpu(0).unwrap(); + + // Map the MMIO ring + vcpu.map_coalesced_mmio_ring().unwrap(); + + // Set regs + let mut regs = vcpu.get_regs().unwrap(); + regs.rip = guest_addr; + regs.rax = DATA; + regs.rdx = ADDR; + regs.rflags = 2; + vcpu.set_regs(®s).unwrap(); + + // Set sregs + let mut sregs = vcpu.get_sregs().unwrap(); + sregs.cs.base = 0; + sregs.cs.selector = 0; + vcpu.set_sregs(&sregs).unwrap(); + + // Run and check that the exit was caused by the hlt and not the MMIO + // access + let exit = vcpu.run().unwrap(); + assert!(matches!(exit, VcpuExit::Hlt)); + + // Check that the ring buffer entry is what we expect + let entry = vcpu.coalesced_mmio_read().unwrap().unwrap(); + assert_eq!(entry.phys_addr, ADDR); + assert_eq!(entry.len, SIZE); + assert_eq!(entry.data[0] as u64, DATA); + // SAFETY: this field is a u32 in all variants of the union, + // so access is always safe. + let pio = unsafe { entry.__bindgen_anon_1.pio }; + assert_eq!(pio, 0); + + // The ring buffer should be empty now + assert!(vcpu.coalesced_mmio_read().unwrap().is_none()); + + // Unregister and check that the next MMIO write triggers an exit + vm.unregister_coalesced_mmio(addr, SIZE).unwrap(); + let exit = vcpu.run().unwrap(); + let VcpuExit::MmioWrite(addr, data) = exit else { + panic!("Unexpected VM exit: {:?}", exit); + }; + assert_eq!(addr, ADDR); + assert_eq!(data, (DATA as u16).to_le_bytes()); + } } From 54e5adeb76e2c665b58a1d3d87c1118557cd0f08 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Wed, 3 Jan 2024 13:10:15 +0000 Subject: [PATCH 253/332] Bump for kvm-bindings and vmm-sys-util releases Alongside the Cargo.toml update there are also minor code changes required for the adjusted bindings. Signed-off-by: Rob Bradford --- Cargo.toml | 4 ++-- src/ioctls/vcpu.rs | 21 ++++++++++++--------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dc5729f..45cbca3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,8 +11,8 @@ edition = "2021" [dependencies] libc = "0.2.39" -kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } -vmm-sys-util = "0.11.0" +kvm-bindings = { version = "0.7.0", features = ["fam-wrappers"] } +vmm-sys-util = "0.12.1" bitflags = "2.4.1" [dev-dependencies] diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index a6fc0e8..a8b6dba 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -147,7 +147,7 @@ pub enum VcpuExit<'a> { /// Corresponds to KVM_EXIT_EPR. Epr, /// Corresponds to KVM_EXIT_SYSTEM_EVENT. - SystemEvent(u32 /* type */, u64 /* flags */), + SystemEvent(u32 /* type */, &'a [u64] /* data */), /// Corresponds to KVM_EXIT_S390_STSI. S390Stsi, /// Corresponds to KVM_EXIT_IOAPIC_EOI. @@ -1533,10 +1533,10 @@ impl VcpuFd { // SAFETY: Safe because the exit_reason (which comes from the kernel) told us // which union field to use. let system_event = unsafe { &mut run.__bindgen_anon_1.system_event }; - Ok(VcpuExit::SystemEvent( - system_event.type_, - system_event.flags, - )) + let ndata = system_event.ndata; + // SAFETY: Safe because we only populate with valid data (based on ndata) + let data = unsafe { &system_event.__bindgen_anon_1.data[0..ndata as usize] }; + Ok(VcpuExit::SystemEvent(system_event.type_, data)) } KVM_EXIT_S390_STSI => Ok(VcpuExit::S390Stsi), KVM_EXIT_IOAPIC_EOI => { @@ -2345,9 +2345,9 @@ mod tests { .sum(); assert_eq!(dirty_pages, 1); } - VcpuExit::SystemEvent(type_, flags) => { + VcpuExit::SystemEvent(type_, data) => { assert_eq!(type_, KVM_SYSTEM_EVENT_SHUTDOWN); - assert_eq!(flags, 0); + assert_eq!(data[0], 0); break; } r => panic!("unexpected exit reason: {:?}", r), @@ -2723,11 +2723,14 @@ mod tests { // not allocated memory, so the first time it fails. let err = vcpu.get_reg_list(&mut reg_list).unwrap_err(); assert!(err.errno() == libc::E2BIG); - assert!(reg_list.as_mut_fam_struct().n > 0); + // SAFETY: This structure is a result from a specific vCPU ioctl + assert!(unsafe { reg_list.as_mut_fam_struct() }.n > 0); // We make use of the number of registers returned to allocate memory and // try one more time. - let mut reg_list = RegList::new(reg_list.as_mut_fam_struct().n as usize).unwrap(); + // SAFETY: This structure is a result from a specific vCPU ioctl + let mut reg_list = + RegList::new(unsafe { reg_list.as_mut_fam_struct() }.n as usize).unwrap(); assert!(vcpu.get_reg_list(&mut reg_list).is_ok()); } From a640b5e8a64de2bf22b7e160f56327ec75feac27 Mon Sep 17 00:00:00 2001 From: Jinank Jain Date: Thu, 21 Dec 2023 14:30:37 +0000 Subject: [PATCH 254/332] release 0.16.0 Signed-off-by: Jinank Jain --- CHANGELOG.md | 14 ++++++++++---- Cargo.toml | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96c78ab..c6f89f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,10 +3,10 @@ ## Added ## Changed -- [[#234](https://github.com/rust-vmm/kvm-ioctls/issues/234)] vcpu: export -reg_size as a public method. -- [[#243](https://github.com/rust-vmm/kvm-ioctls/pull/243)] derived the `Copy` - trait for `IoEventAddress` and `NoDatamatch`. + +# v0.16.0 + +## Added - [[#242](https://github.com/rust-vmm/kvm-ioctls/pull/242)] x86: add support for SMI injection via `Vcpu::smi()` (`KVM_SMI` ioctl). - [[#241](https://github.com/rust-vmm/kvm-ioctls/pull/241)] Add support for @@ -16,6 +16,12 @@ reg_size as a public method. - [[#244](https://github.com/rust-vmm/kvm-ioctls/pull/244)] add support for coalesced MMIO (`KVM_CAP_COALESCED_MMIO` / `KVM_CAP_COALESCED_PIO`) +## Changed +- [[#234](https://github.com/rust-vmm/kvm-ioctls/issues/234)] vcpu: export +reg_size as a public method. +- [[#243](https://github.com/rust-vmm/kvm-ioctls/pull/243)] derived the `Copy` + trait for `IoEventAddress` and `NoDatamatch`. + # v0.15.0 ## Added diff --git a/Cargo.toml b/Cargo.toml index 45cbca3..799e852 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kvm-ioctls" -version = "0.15.0" +version = "0.16.0" authors = ["Amazon Firecracker Team "] description = "Safe wrappers over KVM ioctls" repository = "https://github.com/rust-vmm/kvm-ioctls" From aa32a8c0a8527125122b5084b50ef47601a2cea0 Mon Sep 17 00:00:00 2001 From: Popa Ioan Alexandru Date: Sat, 6 Jan 2024 06:48:30 +0200 Subject: [PATCH 255/332] Add negative tests for vcpu ioctls on aarch64 Signed-off-by: Popa Ioan Alexandru --- src/ioctls/vcpu.rs | 136 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 118 insertions(+), 18 deletions(-) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index a8b6dba..38c91e1 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -2483,7 +2483,12 @@ mod tests { } #[test] - #[cfg(target_arch = "x86_64")] + #[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" + ))] fn test_faulty_vcpu_fd() { use std::os::unix::io::FromRawFd; @@ -2498,6 +2503,47 @@ mod tests { coalesced_mmio_ring: None, }; + assert_eq!( + faulty_vcpu_fd.get_mp_state().unwrap_err().errno(), + badf_errno + ); + assert_eq!( + faulty_vcpu_fd + .set_mp_state(kvm_mp_state::default()) + .unwrap_err() + .errno(), + badf_errno + ); + assert_eq!( + faulty_vcpu_fd.get_vcpu_events().unwrap_err().errno(), + badf_errno + ); + assert_eq!( + faulty_vcpu_fd + .set_vcpu_events(&kvm_vcpu_events::default()) + .unwrap_err() + .errno(), + badf_errno + ); + assert_eq!(faulty_vcpu_fd.run().unwrap_err().errno(), badf_errno); + } + + #[test] + #[cfg(target_arch = "x86_64")] + fn test_faulty_vcpu_fd_x86_64() { + use std::os::unix::io::FromRawFd; + + let badf_errno = libc::EBADF; + + let faulty_vcpu_fd = VcpuFd { + vcpu: unsafe { File::from_raw_fd(-2) }, + kvm_run_ptr: KvmRunWrapper { + kvm_run_ptr: mmap_anonymous(10), + mmap_size: 10, + }, + coalesced_mmio_ring: None, + }; + assert_eq!(faulty_vcpu_fd.get_regs().unwrap_err().errno(), badf_errno); assert_eq!( faulty_vcpu_fd @@ -2563,66 +2609,120 @@ mod tests { badf_errno ); assert_eq!( - faulty_vcpu_fd.get_mp_state().unwrap_err().errno(), + faulty_vcpu_fd.get_xsave().err().unwrap().errno(), badf_errno ); assert_eq!( faulty_vcpu_fd - .set_mp_state(kvm_mp_state::default()) + .set_xsave(&kvm_xsave::default()) .unwrap_err() .errno(), badf_errno ); + assert_eq!(faulty_vcpu_fd.get_xcrs().unwrap_err().errno(), badf_errno); assert_eq!( - faulty_vcpu_fd.get_xsave().err().unwrap().errno(), + faulty_vcpu_fd + .set_xcrs(&kvm_xcrs::default()) + .err() + .unwrap() + .errno(), + badf_errno + ); + assert_eq!( + faulty_vcpu_fd.get_debug_regs().unwrap_err().errno(), badf_errno ); assert_eq!( faulty_vcpu_fd - .set_xsave(&kvm_xsave::default()) + .set_debug_regs(&kvm_debugregs::default()) .unwrap_err() .errno(), badf_errno ); - assert_eq!(faulty_vcpu_fd.get_xcrs().unwrap_err().errno(), badf_errno); + assert_eq!( + faulty_vcpu_fd.kvmclock_ctrl().unwrap_err().errno(), + badf_errno + ); + assert!(faulty_vcpu_fd.get_tsc_khz().is_err()); + assert!(faulty_vcpu_fd.set_tsc_khz(1000000).is_err()); + assert!(faulty_vcpu_fd.translate_gva(u64::MAX).is_err()); + } + + #[test] + #[cfg(target_arch = "aarch64")] + fn test_faulty_vcpu_fd_aarch64() { + use std::os::unix::io::FromRawFd; + + let badf_errno = libc::EBADF; + + let faulty_vcpu_fd = VcpuFd { + vcpu: unsafe { File::from_raw_fd(-2) }, + kvm_run_ptr: KvmRunWrapper { + kvm_run_ptr: mmap_anonymous(10), + mmap_size: 10, + }, + coalesced_mmio_ring: None, + }; + + let device_attr = kvm_bindings::kvm_device_attr { + group: KVM_ARM_VCPU_PMU_V3_CTRL, + attr: u64::from(KVM_ARM_VCPU_PMU_V3_INIT), + addr: 0x0, + flags: 0, + }; + + let reg_id = 0x6030_0000_0010_0042; + let mut reg_data = 0u128.to_le_bytes(); + assert_eq!( faulty_vcpu_fd - .set_xcrs(&kvm_xcrs::default()) - .err() - .unwrap() + .set_device_attr(&device_attr) + .unwrap_err() .errno(), badf_errno ); assert_eq!( - faulty_vcpu_fd.get_debug_regs().unwrap_err().errno(), + faulty_vcpu_fd + .has_device_attr(&device_attr) + .unwrap_err() + .errno(), badf_errno ); assert_eq!( faulty_vcpu_fd - .set_debug_regs(&kvm_debugregs::default()) + .vcpu_init(&kvm_vcpu_init::default()) .unwrap_err() .errno(), badf_errno ); assert_eq!( - faulty_vcpu_fd.get_vcpu_events().unwrap_err().errno(), + faulty_vcpu_fd + .vcpu_finalize(&(KVM_ARM_VCPU_SVE as i32)) + .unwrap_err() + .errno(), badf_errno ); assert_eq!( faulty_vcpu_fd - .set_vcpu_events(&kvm_vcpu_events::default()) + .get_reg_list(&mut RegList::new(500).unwrap()) .unwrap_err() .errno(), badf_errno ); - assert_eq!(faulty_vcpu_fd.run().unwrap_err().errno(), badf_errno); assert_eq!( - faulty_vcpu_fd.kvmclock_ctrl().unwrap_err().errno(), + faulty_vcpu_fd + .set_one_reg(reg_id, ®_data) + .unwrap_err() + .errno(), + badf_errno + ); + assert_eq!( + faulty_vcpu_fd + .get_one_reg(reg_id, &mut reg_data) + .unwrap_err() + .errno(), badf_errno ); - assert!(faulty_vcpu_fd.get_tsc_khz().is_err()); - assert!(faulty_vcpu_fd.set_tsc_khz(1000000).is_err()); - assert!(faulty_vcpu_fd.translate_gva(u64::MAX).is_err()); } #[test] From 1bd9b82c5ea657e75bc1ea5604d9251baac1f3f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 10:47:41 +0000 Subject: [PATCH 256/332] Bump rust-vmm-ci from `0100de0` to `72ebeb3` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `0100de0` to `72ebeb3`. - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/0100de0f57ec8d9f55141b43b7332a5d7f357496...72ebeb393cf0843fef7b2b543058883095780797) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 0100de0..72ebeb3 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 0100de0f57ec8d9f55141b43b7332a5d7f357496 +Subproject commit 72ebeb393cf0843fef7b2b543058883095780797 From d4a249a8a94d083c0ea9cfda70f9b8eb9ce0edb8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Mar 2024 10:18:35 +0000 Subject: [PATCH 257/332] Bump rust-vmm-ci from `72ebeb3` to `7606478` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `72ebeb3` to `7606478`. - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/72ebeb393cf0843fef7b2b543058883095780797...7606478202667234d9378d6850bfda9c219eee7b) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 72ebeb3..7606478 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 72ebeb393cf0843fef7b2b543058883095780797 +Subproject commit 7606478202667234d9378d6850bfda9c219eee7b From 505a4549d49a14800c7cdb9868830ce8e93396a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Tue, 13 Feb 2024 10:54:35 +0100 Subject: [PATCH 258/332] Introduce KvmRunWrapper::as_ref() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new method to get an immutable reference to the kvm_run struct. Replace uses of `as_mut_ref()` with `as_ref()` where possible Signed-off-by: Carlos López --- src/ioctls/mod.rs | 8 ++++++++ src/ioctls/vcpu.rs | 18 +++++++++--------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/ioctls/mod.rs b/src/ioctls/mod.rs index e3ed2f7..316d0da 100644 --- a/src/ioctls/mod.rs +++ b/src/ioctls/mod.rs @@ -183,6 +183,14 @@ impl KvmRunWrapper { } } +impl AsRef for KvmRunWrapper { + fn as_ref(&self) -> &kvm_run { + // SAFETY: Safe because we know we mapped enough memory to hold the kvm_run struct because + // the kernel told us how large it was. + unsafe { &*(self.kvm_run_ptr as *const kvm_run) } + } +} + impl Drop for KvmRunWrapper { fn drop(&mut self) { // SAFETY: This is safe because we mmap the area at kvm_run_ptr ourselves, diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 38c91e1..391bdc7 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1766,7 +1766,7 @@ impl VcpuFd { /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn sync_regs(&self) -> kvm_sync_regs { - let kvm_run: &mut kvm_run = self.kvm_run_ptr.as_mut_ref(); + let kvm_run = self.kvm_run_ptr.as_ref(); // SAFETY: Accessing this union field could be out of bounds if the `kvm_run` // allocation isn't large enough. The `kvm_run` region is set using @@ -2848,9 +2848,9 @@ mod tests { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); let vcpu = vm.create_vcpu(0).unwrap(); - assert_eq!(vcpu.kvm_run_ptr.as_mut_ref().immediate_exit, 0); + assert_eq!(vcpu.kvm_run_ptr.as_ref().immediate_exit, 0); vcpu.set_kvm_immediate_exit(1); - assert_eq!(vcpu.kvm_run_ptr.as_mut_ref().immediate_exit, 1); + assert_eq!(vcpu.kvm_run_ptr.as_ref().immediate_exit, 1); } #[test] @@ -2922,9 +2922,9 @@ mod tests { ]; for reg in &sync_regs { vcpu.set_sync_valid_reg(*reg); - assert_eq!(vcpu.kvm_run_ptr.as_mut_ref().kvm_valid_regs, *reg as u64); + assert_eq!(vcpu.kvm_run_ptr.as_ref().kvm_valid_regs, *reg as u64); vcpu.clear_sync_valid_reg(*reg); - assert_eq!(vcpu.kvm_run_ptr.as_mut_ref().kvm_valid_regs, 0); + assert_eq!(vcpu.kvm_run_ptr.as_ref().kvm_valid_regs, 0); } // Test that multiple valid SyncRegs can be set at the same time @@ -2932,7 +2932,7 @@ mod tests { vcpu.set_sync_valid_reg(SyncReg::SystemRegister); vcpu.set_sync_valid_reg(SyncReg::VcpuEvents); assert_eq!( - vcpu.kvm_run_ptr.as_mut_ref().kvm_valid_regs, + vcpu.kvm_run_ptr.as_ref().kvm_valid_regs, SyncReg::Register as u64 | SyncReg::SystemRegister as u64 | SyncReg::VcpuEvents as u64 ); @@ -2945,9 +2945,9 @@ mod tests { for reg in &sync_regs { vcpu.set_sync_dirty_reg(*reg); - assert_eq!(vcpu.kvm_run_ptr.as_mut_ref().kvm_dirty_regs, *reg as u64); + assert_eq!(vcpu.kvm_run_ptr.as_ref().kvm_dirty_regs, *reg as u64); vcpu.clear_sync_dirty_reg(*reg); - assert_eq!(vcpu.kvm_run_ptr.as_mut_ref().kvm_dirty_regs, 0); + assert_eq!(vcpu.kvm_run_ptr.as_ref().kvm_dirty_regs, 0); } // Test that multiple dirty SyncRegs can be set at the same time @@ -2955,7 +2955,7 @@ mod tests { vcpu.set_sync_dirty_reg(SyncReg::SystemRegister); vcpu.set_sync_dirty_reg(SyncReg::VcpuEvents); assert_eq!( - vcpu.kvm_run_ptr.as_mut_ref().kvm_dirty_regs, + vcpu.kvm_run_ptr.as_ref().kvm_dirty_regs, SyncReg::Register as u64 | SyncReg::SystemRegister as u64 | SyncReg::VcpuEvents as u64 ); } From 8be04f62fedbf02ceb3c127c64a2fddc60767fb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Tue, 13 Feb 2024 11:01:59 +0100 Subject: [PATCH 259/332] Take &mut self in KvmRunWrapper::as_mut_ref() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since KvmRunWrapper implements Send and Sync, this method can cause undefined behavior, as two threads can acquire a mutable reference to the kvm_run struct via an immutable reference to KvmRunWrapper. Fix this by making KvmRunWrapper::as_mut_ref() take &mut self, which also gets rid of a clippy warning suppression, and update the callers. This results in potentially breaking changes in the public interface, as several VcpuFd methods now take &mut self as well. Fixes: #248 Signed-off-by: Carlos López --- CHANGELOG.md | 4 ++++ src/ioctls/mod.rs | 3 +-- src/ioctls/vcpu.rs | 18 +++++++++--------- src/ioctls/vm.rs | 2 +- src/lib.rs | 2 +- 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c6f89f6..1eb4306 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ ## Changed +- [[#255](https://github.com/rust-vmm/kvm-ioctls/issues/255)]: Fixed a + soundness issue when accessing the `kvm_run` struct. `VcpuFd::run()` and + `VcpuFd::set_kvm_immediate_exit()` now take `&mut self` as a consequence. + # v0.16.0 ## Added diff --git a/src/ioctls/mod.rs b/src/ioctls/mod.rs index 316d0da..80ddece 100644 --- a/src/ioctls/mod.rs +++ b/src/ioctls/mod.rs @@ -172,8 +172,7 @@ impl KvmRunWrapper { } /// Returns a mutable reference to `kvm_run`. - #[allow(clippy::mut_from_ref)] - pub fn as_mut_ref(&self) -> &mut kvm_run { + pub fn as_mut_ref(&mut self) -> &mut kvm_run { #[allow(clippy::cast_ptr_alignment)] // SAFETY: Safe because we know we mapped enough memory to hold the kvm_run struct because // the kernel told us how large it was. diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 391bdc7..1ac0ae3 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1404,7 +1404,7 @@ impl VcpuFd { /// slice.write(&x86_code).unwrap(); /// } /// - /// let vcpu_fd = vm.create_vcpu(0).unwrap(); + /// let mut vcpu_fd = vm.create_vcpu(0).unwrap(); /// /// let mut vcpu_sregs = vcpu_fd.get_sregs().unwrap(); /// vcpu_sregs.cs.base = 0; @@ -1429,7 +1429,7 @@ impl VcpuFd { /// } /// } /// ``` - pub fn run(&self) -> Result { + pub fn run(&mut self) -> Result { // SAFETY: Safe because we know that our file is a vCPU fd and we verify the return result. let ret = unsafe { ioctl(self, KVM_RUN()) }; if ret == 0 { @@ -1559,7 +1559,7 @@ impl VcpuFd { } /// Sets the `immediate_exit` flag on the `kvm_run` struct associated with this vCPU to `val`. - pub fn set_kvm_immediate_exit(&self, val: u8) { + pub fn set_kvm_immediate_exit(&mut self, val: u8) { let kvm_run = self.kvm_run_ptr.as_mut_ref(); kvm_run.immediate_exit = val; } @@ -2294,7 +2294,7 @@ mod tests { slice.write_all(&code).unwrap(); } - let vcpu_fd = vm.create_vcpu(0).unwrap(); + let mut vcpu_fd = vm.create_vcpu(0).unwrap(); let mut kvi = kvm_bindings::kvm_vcpu_init::default(); vm.get_preferred_target(&mut kvi).unwrap(); kvi.features[0] |= 1 << KVM_ARM_VCPU_PSCI_0_2; @@ -2399,7 +2399,7 @@ mod tests { slice.write_all(&code).unwrap(); } - let vcpu_fd = vm.create_vcpu(0).unwrap(); + let mut vcpu_fd = vm.create_vcpu(0).unwrap(); let mut vcpu_sregs = vcpu_fd.get_sregs().unwrap(); assert_ne!(vcpu_sregs.cs.base, 0); @@ -2494,7 +2494,7 @@ mod tests { let badf_errno = libc::EBADF; - let faulty_vcpu_fd = VcpuFd { + let mut faulty_vcpu_fd = VcpuFd { vcpu: unsafe { File::from_raw_fd(-2) }, kvm_run_ptr: KvmRunWrapper { kvm_run_ptr: mmap_anonymous(10), @@ -2847,7 +2847,7 @@ mod tests { fn test_set_kvm_immediate_exit() { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); - let vcpu = vm.create_vcpu(0).unwrap(); + let mut vcpu = vm.create_vcpu(0).unwrap(); assert_eq!(vcpu.kvm_run_ptr.as_ref().immediate_exit, 0); vcpu.set_kvm_immediate_exit(1); assert_eq!(vcpu.kvm_run_ptr.as_ref().immediate_exit, 1); @@ -3135,7 +3135,7 @@ mod tests { slice.write_all(&code).unwrap(); } - let vcpu = vm.create_vcpu(0).unwrap(); + let mut vcpu = vm.create_vcpu(0).unwrap(); // Set up special registers let mut vcpu_sregs = vcpu.get_sregs().unwrap(); @@ -3204,7 +3204,7 @@ mod tests { slice.write_all(&code).unwrap(); } - let vcpu = vm.create_vcpu(0).unwrap(); + let mut vcpu = vm.create_vcpu(0).unwrap(); // Set up special registers let mut vcpu_sregs = vcpu.get_sregs().unwrap(); diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 813c490..c860710 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -756,7 +756,7 @@ impl VmFd { /// slice.write(&asm_code).unwrap(); /// } /// - /// let vcpu_fd = vm.create_vcpu(0).unwrap(); + /// let mut vcpu_fd = vm.create_vcpu(0).unwrap(); /// /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] /// { diff --git a/src/lib.rs b/src/lib.rs index d999d74..8229f4f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -130,7 +130,7 @@ //! } //! //! // 4. Create one vCPU. -//! let vcpu_fd = vm.create_vcpu(0).unwrap(); +//! let mut vcpu_fd = vm.create_vcpu(0).unwrap(); //! //! // 5. Initialize general purpose and special registers. //! #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] From aa7fb8d953cf73c46d9b03f0be226bc2e0ef1cd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Mon, 4 Mar 2024 20:02:39 +0100 Subject: [PATCH 260/332] Remove redundant imports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The nightly toolchain reports some redundant imports, so remove them. Signed-off-by: Carlos López --- src/ioctls/system.rs | 2 -- src/ioctls/vcpu.rs | 5 ----- 2 files changed, 7 deletions(-) diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index c59e164..fc8b19a 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -729,8 +729,6 @@ impl FromRawFd for Kvm { mod tests { #![allow(clippy::undocumented_unsafe_blocks)] use super::*; - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - use kvm_bindings::KVM_MAX_CPUID_ENTRIES; use libc::{fcntl, FD_CLOEXEC, F_GETFD}; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use vmm_sys_util::fam::FamStruct; diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 1ac0ae3..46ec442 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -12,11 +12,6 @@ use std::os::unix::io::{AsRawFd, RawFd}; use crate::ioctls::{KvmCoalescedIoRing, KvmRunWrapper, Result}; use crate::kvm_ioctls::*; -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -use kvm_bindings::{ - CpuId, Msrs, KVM_MAX_CPUID_ENTRIES, KVM_MSR_EXIT_REASON_FILTER, KVM_MSR_EXIT_REASON_INVAL, - KVM_MSR_EXIT_REASON_UNKNOWN, -}; use vmm_sys_util::errno; use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ref}; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] From f364358481bc7a118baf4852e85de3b08d44784f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Tue, 13 Feb 2024 16:50:08 +0100 Subject: [PATCH 261/332] Do not needlessly use dynamic dispatch in KvmRunWrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't use any of the advantages of dynamic dispatch even though it has a runtime overhead, so simply use static dispatch. Signed-off-by: Carlos López --- src/ioctls/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ioctls/mod.rs b/src/ioctls/mod.rs index 80ddece..57ff84f 100644 --- a/src/ioctls/mod.rs +++ b/src/ioctls/mod.rs @@ -148,7 +148,7 @@ impl KvmRunWrapper { /// # Arguments /// * `fd` - File descriptor to mmap from. /// * `size` - Size of memory region in bytes. - pub fn mmap_from_fd(fd: &dyn AsRawFd, size: usize) -> Result { + pub fn mmap_from_fd(fd: &F, size: usize) -> Result { // SAFETY: This is safe because we are creating a mapping in a place not already used by // any other area in this process. let addr = unsafe { From 913c4dce0e00700ec8404e4667b71eb8e511fd5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Tue, 13 Feb 2024 16:47:52 +0100 Subject: [PATCH 262/332] Use NonNull in KvmRunWrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pointer to the kvm_run struct in KvmRunWrapper should never be NULL, so store it as a NonNull. It always points to a kvm_run struct, so type is as such instead of a pointer to a u8. Signed-off-by: Carlos López --- src/ioctls/mod.rs | 24 +++++++++++------------- src/ioctls/vcpu.rs | 25 +++++++++++++------------ 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/ioctls/mod.rs b/src/ioctls/mod.rs index 57ff84f..6277056 100644 --- a/src/ioctls/mod.rs +++ b/src/ioctls/mod.rs @@ -7,7 +7,7 @@ use std::mem::size_of; use std::os::unix::io::AsRawFd; -use std::ptr::null_mut; +use std::ptr::{null_mut, NonNull}; use kvm_bindings::{ kvm_coalesced_mmio, kvm_coalesced_mmio_ring, kvm_run, KVM_COALESCED_MMIO_PAGE_OFFSET, @@ -129,7 +129,7 @@ unsafe impl Sync for KvmCoalescedIoRing {} /// threads as raw pointers do not implement `Send` and `Sync`. #[derive(Debug)] pub struct KvmRunWrapper { - kvm_run_ptr: *mut u8, + kvm_run_ptr: NonNull, // This field is need so we can `munmap` the memory mapped to hold `kvm_run`. mmap_size: usize, } @@ -161,24 +161,22 @@ impl KvmRunWrapper { 0, ) }; - if addr == libc::MAP_FAILED { - return Err(errno::Error::last()); - } + let addr = NonNull::new(addr) + .filter(|addr| addr.as_ptr() != libc::MAP_FAILED) + .ok_or_else(errno::Error::last)?; Ok(KvmRunWrapper { - kvm_run_ptr: addr as *mut u8, + kvm_run_ptr: addr.cast(), mmap_size: size, }) } /// Returns a mutable reference to `kvm_run`. pub fn as_mut_ref(&mut self) -> &mut kvm_run { - #[allow(clippy::cast_ptr_alignment)] // SAFETY: Safe because we know we mapped enough memory to hold the kvm_run struct because - // the kernel told us how large it was. - unsafe { - &mut *(self.kvm_run_ptr as *mut kvm_run) - } + // the kernel told us how large it was. Nobody else has access to this pointer so it cannot + // be aliased. + unsafe { self.kvm_run_ptr.as_mut() } } } @@ -186,7 +184,7 @@ impl AsRef for KvmRunWrapper { fn as_ref(&self) -> &kvm_run { // SAFETY: Safe because we know we mapped enough memory to hold the kvm_run struct because // the kernel told us how large it was. - unsafe { &*(self.kvm_run_ptr as *const kvm_run) } + unsafe { self.kvm_run_ptr.as_ref() } } } @@ -195,7 +193,7 @@ impl Drop for KvmRunWrapper { // SAFETY: This is safe because we mmap the area at kvm_run_ptr ourselves, // and nobody else is holding a reference to it. unsafe { - libc::munmap(self.kvm_run_ptr as *mut libc::c_void, self.mmap_size); + libc::munmap(self.kvm_run_ptr.as_ptr().cast(), self.mmap_size); } } } diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 46ec442..b4519f7 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1930,10 +1930,11 @@ mod tests { ))] use crate::cap::Cap; use crate::ioctls::system::Kvm; + use std::ptr::NonNull; // Helper function for memory mapping `size` bytes of anonymous memory. // Panics if the mmap fails. - fn mmap_anonymous(size: usize) -> *mut u8 { + fn mmap_anonymous(size: usize) -> NonNull { use std::ptr::null_mut; let addr = unsafe { @@ -1950,7 +1951,7 @@ mod tests { panic!("mmap failed."); } - addr as *mut u8 + NonNull::new(addr).unwrap().cast() } #[test] @@ -2268,7 +2269,7 @@ mod tests { ]; let mem_size = 0x20000; - let load_addr = mmap_anonymous(mem_size); + let load_addr = mmap_anonymous(mem_size).as_ptr(); let guest_addr: u64 = 0x10000; let slot: u32 = 0; let mem_region = kvm_userspace_memory_region { @@ -2373,7 +2374,7 @@ mod tests { let expected_rips: [u64; 3] = [0x1003, 0x1005, 0x1007]; let mem_size = 0x4000; - let load_addr = mmap_anonymous(mem_size); + let load_addr = mmap_anonymous(mem_size).as_ptr(); let guest_addr: u64 = 0x1000; let slot: u32 = 0; let mem_region = kvm_userspace_memory_region { @@ -2492,7 +2493,7 @@ mod tests { let mut faulty_vcpu_fd = VcpuFd { vcpu: unsafe { File::from_raw_fd(-2) }, kvm_run_ptr: KvmRunWrapper { - kvm_run_ptr: mmap_anonymous(10), + kvm_run_ptr: mmap_anonymous(10).cast(), mmap_size: 10, }, coalesced_mmio_ring: None, @@ -2533,7 +2534,7 @@ mod tests { let faulty_vcpu_fd = VcpuFd { vcpu: unsafe { File::from_raw_fd(-2) }, kvm_run_ptr: KvmRunWrapper { - kvm_run_ptr: mmap_anonymous(10), + kvm_run_ptr: mmap_anonymous(10).cast(), mmap_size: 10, }, coalesced_mmio_ring: None, @@ -2653,7 +2654,7 @@ mod tests { let faulty_vcpu_fd = VcpuFd { vcpu: unsafe { File::from_raw_fd(-2) }, kvm_run_ptr: KvmRunWrapper { - kvm_run_ptr: mmap_anonymous(10), + kvm_run_ptr: mmap_anonymous(10).cast(), mmap_size: 10, }, coalesced_mmio_ring: None, @@ -2971,7 +2972,7 @@ mod tests { ]; let mem_size = 0x4000; - let load_addr = mmap_anonymous(mem_size); + let load_addr = mmap_anonymous(mem_size).as_ptr(); let guest_addr: u64 = 0x1000; let slot: u32 = 0; let mem_region = kvm_userspace_memory_region { @@ -3111,7 +3112,7 @@ mod tests { vm.enable_cap(&cap).unwrap(); let mem_size = 0x4000; - let load_addr = mmap_anonymous(mem_size); + let load_addr = mmap_anonymous(mem_size).as_ptr(); let guest_addr: u64 = 0x1000; let slot: u32 = 0; let mem_region = kvm_userspace_memory_region { @@ -3180,7 +3181,7 @@ mod tests { vm.enable_cap(&cap).unwrap(); let mem_size = 0x4000; - let load_addr = mmap_anonymous(mem_size); + let load_addr = mmap_anonymous(mem_size).as_ptr(); let guest_addr: u64 = 0x1000; let slot: u32 = 0; let mem_region = kvm_userspace_memory_region { @@ -3254,7 +3255,7 @@ mod tests { // Prepare guest memory let mem_size = 0x4000; - let load_addr = mmap_anonymous(mem_size); + let load_addr = mmap_anonymous(mem_size).as_ptr(); let guest_addr: u64 = 0x1000; let slot = 0; let mem_region = kvm_userspace_memory_region { @@ -3351,7 +3352,7 @@ mod tests { // Prepare guest memory let mem_size = 0x4000; - let load_addr = mmap_anonymous(mem_size); + let load_addr = mmap_anonymous(mem_size).as_ptr(); let guest_addr: u64 = 0x1000; let slot: u32 = 0; let mem_region = kvm_userspace_memory_region { From 4b24fa05b640ff4e5690a7f91661080adaeb82c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Tue, 13 Feb 2024 16:56:17 +0100 Subject: [PATCH 263/332] Use NonNull in KvmCoalescedIoRing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pointer to the kvm_coalesced_mmio_ring struct in KvmRunWrapper should never be NULL, so store it as such. Signed-off-by: Carlos López --- src/ioctls/mod.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/ioctls/mod.rs b/src/ioctls/mod.rs index 6277056..a5f96c1 100644 --- a/src/ioctls/mod.rs +++ b/src/ioctls/mod.rs @@ -32,7 +32,7 @@ pub type Result = std::result::Result; /// A wrapper around the coalesced MMIO ring page. #[derive(Debug)] pub(crate) struct KvmCoalescedIoRing { - addr: *mut kvm_coalesced_mmio_ring, + addr: NonNull, page_size: usize, } @@ -61,9 +61,10 @@ impl KvmCoalescedIoRing { offset.into(), ) }; - if addr == libc::MAP_FAILED { - return Err(errno::Error::last()); - } + let addr = NonNull::new(addr) + .filter(|addr| addr.as_ptr() != libc::MAP_FAILED) + .ok_or_else(errno::Error::last)?; + Ok(Self { addr: addr.cast(), page_size, @@ -80,7 +81,7 @@ impl KvmCoalescedIoRing { fn ring_mut(&mut self) -> &mut kvm_coalesced_mmio_ring { // SAFETY: We have a `&mut self` and the pointer is private, so this // access is exclusive. - unsafe { &mut *self.addr } + unsafe { self.addr.as_mut() } } /// Reads a single entry from the MMIO ring. @@ -113,7 +114,7 @@ impl Drop for KvmCoalescedIoRing { // SAFETY: This is safe because we mmap the page ourselves, and nobody // else is holding a reference to it. unsafe { - libc::munmap(self.addr.cast(), self.page_size); + libc::munmap(self.addr.as_ptr().cast(), self.page_size); } } } From d80807f5941ef4e7b5153881f392dd094a0cfb1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Mon, 4 Mar 2024 20:26:48 +0100 Subject: [PATCH 264/332] Update coverage_config_x86_64.json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Carlos López --- coverage_config_x86_64.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index f3a954e..903a58b 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 86.74, + "coverage_score": 87.32, "exclude_path": "", "crate_features": "" } From 913a291c18614ac8f8ee43567a4da738f2a1f15a Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Tue, 23 Apr 2024 15:32:13 +0100 Subject: [PATCH 265/332] Format changelog as other rust-vmm crates do Other rust-vmm crates use a level-1 heading "Changelog", so use the same here. Signed-off-by: Patrick Roy --- CHANGELOG.md | 96 +++++++++++++++++++++++++++------------------------- 1 file changed, 49 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1eb4306..70963ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,18 @@ -# Upcoming Release +# Changelog -## Added +## Upcoming Release -## Changed +### Added + +### Changed - [[#255](https://github.com/rust-vmm/kvm-ioctls/issues/255)]: Fixed a soundness issue when accessing the `kvm_run` struct. `VcpuFd::run()` and `VcpuFd::set_kvm_immediate_exit()` now take `&mut self` as a consequence. -# v0.16.0 +## v0.16.0 -## Added +### Added - [[#242](https://github.com/rust-vmm/kvm-ioctls/pull/242)] x86: add support for SMI injection via `Vcpu::smi()` (`KVM_SMI` ioctl). - [[#241](https://github.com/rust-vmm/kvm-ioctls/pull/241)] Add support for @@ -20,15 +22,15 @@ - [[#244](https://github.com/rust-vmm/kvm-ioctls/pull/244)] add support for coalesced MMIO (`KVM_CAP_COALESCED_MMIO` / `KVM_CAP_COALESCED_PIO`) -## Changed +### Changed - [[#234](https://github.com/rust-vmm/kvm-ioctls/issues/234)] vcpu: export reg_size as a public method. - [[#243](https://github.com/rust-vmm/kvm-ioctls/pull/243)] derived the `Copy` trait for `IoEventAddress` and `NoDatamatch`. -# v0.15.0 +## v0.15.0 -## Added +### Added - [[#230](https://github.com/rust-vmm/kvm-ioctls/pull/230)] Added `check_extension_raw` method to use raw integer values instead of `Cap` enum. @@ -37,31 +39,31 @@ support for vCPU SVE feature. - [[#219](https://github.com/rust-vmm/kvm-ioctls/pull/226)] Add `Cap::ArmPtrAuthAddress` and `Cap::ArmPtrAuthGeneric` capabilities. -# v0.14.0 +## v0.14.0 -## Added +### Added - [[#219](https://github.com/rust-vmm/kvm-ioctls/pull/219)] Support for `KVM_GET_MSR_FEATURE_INDEX_LIST` and `KVM_GET_MSRS` system ioctls. - [[#221](https://github.com/rust-vmm/kvm-ioctls/pull/221)] Add `Cap::ArmPmuV3`. -## Changed +### Changed - [[#223](https://github.com/rust-vmm/kvm-ioctls/pull/223)] aarch64: Updated `get/set_one_reg` to support different registers sizes through byte slices. -# v0.13.0 +## v0.13.0 -## Added +### Added - [[#213](https://github.com/rust-vmm/kvm-ioctls/pull/213)] Add `Kvm::new_with_path()` and `Kvm::open_with_cloexec_at()` to allow using kvm device files other than `/dev/kvm`. -# v0.12.0 +## v0.12.0 -## Added +### Added - [[#187](https://github.com/rust-vmm/kvm-ioctls/pull/187)] Support for `KVM_SET_IDENTITY_MAP_ADDR` @@ -81,7 +83,7 @@ support for vCPU SVE feature. - [[#202](https://github.com/rust-vmm/kvm-ioctls/pull/202)] Added `check_extension_int` which allows checking the capabilities that return numbers instead of booleans -## Changed +### Changed - Updated vmm-sys-util to 0.11.0 - Updated kvm-bindings to 0.6.0 @@ -96,9 +98,9 @@ support for vCPU SVE feature. preventing `set_guest_debug` from being exported on ARM - [[#206](https://github.com/rust-vmm/kvm-ioctls/pull/206)] use `u128` in `get/set_on_reg` -# v0.11.0 +## v0.11.0 -## Added +### Added - [[#178](https://github.com/rust-vmm/kvm-ioctls/pull/178)] Support for the AMD Security Encrypted Virtualization (SEV) through the following VM ioctls: `encrypt_op`, `encrypt_op_sev`, `register_enc_memory_region` and @@ -106,54 +108,54 @@ support for vCPU SVE feature. - [[#184](https://github.com/rust-vmm/kvm-ioctls/pull/184)] `DeviceFd` now derives `Debug`. -# v0.10.0 +## v0.10.0 -## Changed +### Changed - Now depends on kvm-bindings >=0.5.0 which replaced the v4.20 KVM bindings with the v5.13 ones. - Updated `VcpuExit::Debug` to return architecture specific information for the debug event. -# v0.9.0 +## v0.9.0 -## Added +### Added - Support for accessing and controlling the Time Stamp Counter on x86 platforms through the `get_tsc_khz` and `set_tsc_khz` functions. -## Changed +### Changed - Updated `create_vm` on `aarch64` to create a VM fd from the KVM fd using the host's maximum IPA size. -# v0.8.0 +## v0.8.0 -## Added +### Added - Support for specifying VM type (an opaque platform and architecture specific constant) when creating a VM (`KVM_CREATE_VM` ioctl) via the `Kvm::create_vm_with_type` function. -## Changed +### Changed - Now depends on kvm-bindings >=0.4.0 to support use of a newer vmm-sys-utils dependency. -# v0.7.0 +## v0.7.0 -## Added +### Added - Support for the system API that returns the maximum allowed vCPU ID (`KVM_CAP_MAX_VCPU_ID`). - Support for `KVM_MEMORY_ENCRYPT_OP`. -## Fixed +### Fixed - [[#119](https://github.com/rust-vmm/kvm-ioctls/issues/119)]: Disallow invalid number of cpuid entries to be passed to `get_supported_cpuid` and `get_emulated_cpuid`. -## Changed +### Changed - [[#123](https://github.com/rust-vmm/kvm-ioctls/issues/123)]: Updated `create_vcpu` to use `u64` as the parameter for the number of vCPUs. -# v0.6.0 +## v0.6.0 -## Added +### Added - Support for the vcpu ioctls: `KVM_SET_GUEST_DEBUG`, `KVM_KVMCLOCK_CTRL`, and `KVM_GET_REG_LIST`. - Support for the vm ioctl `KVM_GET_DEVICE_ATTR`. @@ -166,31 +168,31 @@ support for vCPU SVE feature. - Support for checking the VM capabilities via `Vm::check_extension`. - Create a VM with flexible IPA size using `Kvm::create_vm_with_ipa_size`. -## Removed +### Removed - Removed `Kvm::new_with_fd_number`. The same functionality is offered by the `Kvm` [FromRawFd](https://doc.rust-lang.org/std/os/unix/io/trait.FromRawFd.html) trait implementation. -## Changed +### Changed - The VM ioctl `unregister_ioevent` now correctly unregisters the events that correspond to the data match passed as a parameter. - The `SystemEvent` Vcpu Exit now also contains the relevant type and flags. - Updated `get_dirty_log` such that it does not assume the page size is 4K, but instead reads it using `libc::sysconf`. -# v0.5.0 +## v0.5.0 -## Added +### Added - Support for the vcpu ioctls `KVM_GET/SET_VCPU_EVENTS` and `KVM_GET_DIRTY_LOG` on `aarch64`. - Support for the vcpu ioctl `KVM_IRQ_LINE`. -# v0.4.0 +## v0.4.0 -## Added +### Added - Support for unregistering ioeventfds through `KVM_IOEVENTFD`. -## Changed +### Changed - Functions working with event FDs now require vmm_sys_util::eventfd::EventFd in their interface instead of RawFd. @@ -204,15 +206,15 @@ support for vCPU SVE feature. not have to use kvm_ioctls::Result outside the crate. - kvm_ioctls::Error now works with errno::Error instead of io::Error. -## Removed +### Removed - CpuId safe wrapper over FAM struct kvm_cpuid2. The safe wrapper is now provided by the kvm_bindings crate starting with v0.2.0. - KVM_MAX_MSR_ENTRIES and MAX_KVM_CPUID_ENTRIES. Equivalent constants are provided by the kvm_bindings crate starting with v0.2.0. -# v0.3.0 +## v0.3.0 -## Added +### Added - Support for setting vcpu `kvm_immediate_exit` flag - Support for the vcpu ioctl `KVM_GET_CPUID2` - Support for the vcpu ioctl `KVM_GET_MP_STATE` @@ -233,23 +235,23 @@ support for vCPU SVE feature. - Support for the vm ioctl `KVM_SET_PIT2` - Support for the vcpu ioctl `KVM_GET_ONE_REG` -## Changed +### Changed - Function offering support for `KVM_SET_MSRS` also returns the number of MSR entries successfully written. -# v0.2.0 +## v0.2.0 -## Added +### Added - Add support for `KVM_ENABLE_CAP`. - Add support for `KVM_SIGNAL_MSI`. -## Fixed +### Fixed - Fix bug in KvmRunWrapper. The memory for kvm_run struct was not unmapped after the KvmRunWrapper object got out of scope. - Return proper value when receiving the EOI KVM exit. - Mark set_user_memory_region as unsafe. -# v0.1.0 +## v0.1.0 First release of the kvm-ioctls crate. From e2d35ee64f6db2615f2329d4c51f7f7cf17330bf Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Tue, 23 Apr 2024 15:34:30 +0100 Subject: [PATCH 266/332] Update kvm-bindings to 0.8.0 Signed-off-by: Patrick Roy --- CHANGELOG.md | 1 + Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70963ad..fe12cbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - [[#255](https://github.com/rust-vmm/kvm-ioctls/issues/255)]: Fixed a soundness issue when accessing the `kvm_run` struct. `VcpuFd::run()` and `VcpuFd::set_kvm_immediate_exit()` now take `&mut self` as a consequence. +- [[#260](https://github.com/rust-vmm/kvm-ioctls/pull/260)]: Updated kvm-bindings to 0.8.0. ## v0.16.0 diff --git a/Cargo.toml b/Cargo.toml index 799e852..0a2ff31 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ edition = "2021" [dependencies] libc = "0.2.39" -kvm-bindings = { version = "0.7.0", features = ["fam-wrappers"] } +kvm-bindings = { version = "0.8.0", features = ["fam-wrappers"] } vmm-sys-util = "0.12.1" bitflags = "2.4.1" From 0ddf7ef6f6079bf217cb297278b7ba35e3e70116 Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Tue, 23 Apr 2024 15:34:54 +0100 Subject: [PATCH 267/332] Prepare 0.17.0 release Signed-off-by: Patrick Roy --- CHANGELOG.md | 4 ++++ Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe12cbc..b1244d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ ### Changed +## v0.17.0 + +### Changed + - [[#255](https://github.com/rust-vmm/kvm-ioctls/issues/255)]: Fixed a soundness issue when accessing the `kvm_run` struct. `VcpuFd::run()` and `VcpuFd::set_kvm_immediate_exit()` now take `&mut self` as a consequence. diff --git a/Cargo.toml b/Cargo.toml index 0a2ff31..fd985e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kvm-ioctls" -version = "0.16.0" +version = "0.17.0" authors = ["Amazon Firecracker Team "] description = "Safe wrappers over KVM ioctls" repository = "https://github.com/rust-vmm/kvm-ioctls" From 9567fdee104ae917cf2740e3518441b43d24b8ad Mon Sep 17 00:00:00 2001 From: Egor Lazarchuk Date: Wed, 29 May 2024 11:25:32 +0000 Subject: [PATCH 268/332] chore: update codeowners Remove JonathanWoollett-Light. Add ShadowCurse. Signed-off-by: Egor Lazarchuk --- CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index 9649f2d..064bfa2 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,3 +1,3 @@ # These owners will be the default owners for everything in # the repo. -* @acatangiu @aghecenco @andreeaflorescu @lauralt @sameo @roypat @JonathanWoollett-Light +* @acatangiu @aghecenco @andreeaflorescu @lauralt @sameo @roypat @ShadowCurse From b8dd6f07cce0d332fe1714d3bc9af13cdbf5dc1e Mon Sep 17 00:00:00 2001 From: Matias Ezequiel Vara Larsen Date: Tue, 4 Jun 2024 15:27:34 +0200 Subject: [PATCH 269/332] Add test for vcpu_init with an invalid target To test vcpu_init, replace the zeroed value with an invalid value. Put the test in a separate test from the test_get_preferred_target test. Signed-off-by: Matias Ezequiel Vara Larsen --- src/ioctls/vcpu.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index b4519f7..d7a8bb2 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -2644,6 +2644,23 @@ mod tests { assert!(faulty_vcpu_fd.translate_gva(u64::MAX).is_err()); } + #[test] + #[cfg(target_arch = "aarch64")] + fn test_faulty_vcpu_target_aarch64() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let vcpu = vm.create_vcpu(0).unwrap(); + + // KVM defines valid targets as 0 to KVM_ARM_NUM_TARGETS-1, so pick a big raw number + // greater than that as target to be invalid + let kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init { + target: 300, + ..Default::default() + }; + + assert!(vcpu.vcpu_init(&kvi).is_err()); + } + #[test] #[cfg(target_arch = "aarch64")] fn test_faulty_vcpu_fd_aarch64() { @@ -2729,7 +2746,6 @@ mod tests { let vcpu = vm.create_vcpu(0).unwrap(); let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default(); - assert!(vcpu.vcpu_init(&kvi).is_err()); vm.get_preferred_target(&mut kvi) .expect("Cannot get preferred target"); From 20aff9f6c6585f172115061666d0f833a4ccd162 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Wed, 26 Jun 2024 10:06:17 +0000 Subject: [PATCH 270/332] expose fields for KVM_EXIT_HYPERCALL This is needed for userspace to handle the `KVM_HC_MAP_GPA_RANGE` hypercall. Signed-off-by: Tom Dohrmann --- src/ioctls/vcpu.rs | 27 +++++++++++++++++++++++++-- src/lib.rs | 2 +- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index d7a8bb2..fd5cd82 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -23,6 +23,19 @@ pub fn reg_size(reg_id: u64) -> usize { 2_usize.pow(((reg_id & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT) as u32) } +/// Information about a [`VcpuExit`] triggered by an Hypercall (`KVM_EXIT_HYPERCALL`). +#[derive(Debug)] +pub struct HypercallExit<'a> { + /// The hypercall number. + pub nr: u64, + /// The arguments for the hypercall. + pub args: [u64; 6], + /// The return code to be indicated to the guest. + pub ret: &'a mut u64, + /// Whether the hypercall was executed in long mode. + pub longmode: u32, +} + /// Information about a [`VcpuExit`] triggered by an MSR read (`KVM_EXIT_X86_RDMSR`). #[derive(Debug)] pub struct ReadMsrExit<'a> { @@ -97,7 +110,7 @@ pub enum VcpuExit<'a> { /// Corresponds to KVM_EXIT_EXCEPTION. Exception, /// Corresponds to KVM_EXIT_HYPERCALL. - Hypercall, + Hypercall(HypercallExit<'a>), /// Corresponds to KVM_EXIT_DEBUG. /// /// Provides architecture specific information for the debug event. @@ -1454,7 +1467,17 @@ impl VcpuFd { _ => Err(errno::Error::new(EINVAL)), } } - KVM_EXIT_HYPERCALL => Ok(VcpuExit::Hypercall), + KVM_EXIT_HYPERCALL => { + // SAFETY: Safe because the exit_reason (which comes from the kernel) told us + // which union field to use. + let hypercall = unsafe { &mut run.__bindgen_anon_1.hypercall }; + Ok(VcpuExit::Hypercall(HypercallExit { + nr: hypercall.nr, + args: hypercall.args, + ret: &mut hypercall.ret, + longmode: hypercall.longmode, + })) + } KVM_EXIT_DEBUG => { // SAFETY: Safe because the exit_reason (which comes from the kernel) told us // which union field to use. diff --git a/src/lib.rs b/src/lib.rs index 8229f4f..c1d1ec5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -221,7 +221,7 @@ pub use ioctls::device::DeviceFd; pub use ioctls::system::Kvm; #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] pub use ioctls::vcpu::reg_size; -pub use ioctls::vcpu::{VcpuExit, VcpuFd}; +pub use ioctls::vcpu::{HypercallExit, VcpuExit, VcpuFd}; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub use ioctls::vcpu::{MsrExitReason, ReadMsrExit, SyncReg, WriteMsrExit}; From eb1260d5d5bdd793e866306e405d32e3b4537c6f Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Wed, 26 Jun 2024 10:58:04 +0000 Subject: [PATCH 271/332] add ExitHypercall variant to Cap Signed-off-by: Tom Dohrmann --- src/cap.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cap.rs b/src/cap.rs index 71b7181..43fd625 100644 --- a/src/cap.rs +++ b/src/cap.rs @@ -167,4 +167,5 @@ pub enum Cap { ArmPtrAuthGeneric = KVM_CAP_ARM_PTRAUTH_GENERIC, #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] X86UserSpaceMsr = KVM_CAP_X86_USER_SPACE_MSR, + ExitHypercall = KVM_CAP_EXIT_HYPERCALL, } From 6a3dd5f6979438bf914d3e3dd5ef6c1194269946 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Wed, 26 Jun 2024 10:58:40 +0000 Subject: [PATCH 272/332] add unit test for HypercallExit Signed-off-by: Tom Dohrmann --- src/ioctls/vcpu.rs | 97 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index fd5cd82..92ac796 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -3196,6 +3196,103 @@ mod tests { } } + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[test] + fn test_userspace_hypercall_exit() { + use std::io::Write; + + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + + // Use `vmcall` or `vmmcall` depending on what's supported. + let cpuid = kvm.get_supported_cpuid(KVM_MAX_CPUID_ENTRIES).unwrap(); + let supports_vmcall = cpuid + .as_slice() + .iter() + .find(|entry| entry.function == 1) + .map_or(false, |entry| entry.ecx & (1 << 5) != 0); + let supports_vmmcall = cpuid + .as_slice() + .iter() + .find(|entry| entry.function == 0x8000_0001) + .map_or(false, |entry| entry.ecx & (1 << 2) != 0); + #[rustfmt::skip] + let code = if supports_vmcall { + [ + 0x0F, 0x01, 0xC1, /* vmcall */ + 0xF4 /* hlt */ + ] + } else if supports_vmmcall { + [ + 0x0F, 0x01, 0xD9, /* vmmcall */ + 0xF4 /* hlt */ + ] + } else { + return; + }; + + if !vm.check_extension(Cap::ExitHypercall) { + return; + } + const KVM_HC_MAP_GPA_RANGE: u64 = 12; + let cap = kvm_enable_cap { + cap: Cap::ExitHypercall as u32, + args: [1 << KVM_HC_MAP_GPA_RANGE, 0, 0, 0], + ..Default::default() + }; + vm.enable_cap(&cap).unwrap(); + + let mem_size = 0x4000; + let load_addr = mmap_anonymous(mem_size).as_ptr(); + let guest_addr: u64 = 0x1000; + let slot: u32 = 0; + let mem_region = kvm_userspace_memory_region { + slot, + guest_phys_addr: guest_addr, + memory_size: mem_size as u64, + userspace_addr: load_addr as u64, + flags: 0, + }; + unsafe { + vm.set_user_memory_region(mem_region).unwrap(); + + // Get a mutable slice of `mem_size` from `load_addr`. + // This is safe because we mapped it before. + let mut slice = std::slice::from_raw_parts_mut(load_addr, mem_size); + slice.write_all(&code).unwrap(); + } + + let mut vcpu = vm.create_vcpu(0).unwrap(); + + // Set up special registers + let mut vcpu_sregs = vcpu.get_sregs().unwrap(); + assert_ne!(vcpu_sregs.cs.base, 0); + assert_ne!(vcpu_sregs.cs.selector, 0); + vcpu_sregs.cs.base = 0; + vcpu_sregs.cs.selector = 0; + vcpu.set_sregs(&vcpu_sregs).unwrap(); + + // Set the Instruction Pointer to the guest address where we loaded + // the code, and RCX to the MSR to be read. + let mut vcpu_regs = vcpu.get_regs().unwrap(); + vcpu_regs.rip = guest_addr; + vcpu_regs.rax = KVM_HC_MAP_GPA_RANGE; + vcpu_regs.rbx = 0x1234000; + vcpu_regs.rcx = 1; + vcpu_regs.rdx = 0; + vcpu.set_regs(&vcpu_regs).unwrap(); + + match vcpu.run().unwrap() { + VcpuExit::Hypercall(exit) => { + assert_eq!(exit.nr, KVM_HC_MAP_GPA_RANGE); + assert_eq!(exit.args[0], 0x1234000); + assert_eq!(exit.args[1], 1); + assert_eq!(exit.args[2], 0); + } + e => panic!("Unexpected exit: {:?}", e), + } + } + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] #[test] fn test_userspace_wrmsr_exit() { From d8715c9239a95fa753c75f61bc8e622b4e6cd259 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Wed, 26 Jun 2024 11:04:00 +0000 Subject: [PATCH 273/332] update changelog Signed-off-by: Tom Dohrmann --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1244d9..5977697 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### Added +- [[#267](https://github.com/rust-vmm/kvm-ioctls/pull/267)]: Added `HypercallExit` field to `VcpuExit::Hypercall` and added `ExitHypercall` to `Cap`. + ### Changed ## v0.17.0 From 9b85aefd2bd1c7e96aa7362fa2d11a84dd40de1a Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Wed, 26 Jun 2024 11:22:19 +0000 Subject: [PATCH 274/332] restrict ExitHypercall to x86_64 According to the official docs, this capability is only supported on x86. Signed-off-by: Tom Dohrmann --- src/cap.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cap.rs b/src/cap.rs index 43fd625..9d31f91 100644 --- a/src/cap.rs +++ b/src/cap.rs @@ -167,5 +167,6 @@ pub enum Cap { ArmPtrAuthGeneric = KVM_CAP_ARM_PTRAUTH_GENERIC, #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] X86UserSpaceMsr = KVM_CAP_X86_USER_SPACE_MSR, + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ExitHypercall = KVM_CAP_EXIT_HYPERCALL, } From f3ea531ee657612b3226388a03be25a25a54430e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 10:26:06 +0000 Subject: [PATCH 275/332] Bump rust-vmm-ci from `7606478` to `0503867` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `7606478` to `0503867`. - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/7606478202667234d9378d6850bfda9c219eee7b...05038671bbc09476d9051c884f754689ae1774f9) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 7606478..0503867 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 7606478202667234d9378d6850bfda9c219eee7b +Subproject commit 05038671bbc09476d9051c884f754689ae1774f9 From 9abac22269231d01b88607070a1a5fde02740f93 Mon Sep 17 00:00:00 2001 From: Matias Ezequiel Vara Larsen Date: Wed, 15 May 2024 15:49:53 +0200 Subject: [PATCH 276/332] Add support for KVM_SET_USER_MEMORY_REGION2 Add support for the KVM_SET_USER_MEMORY_REGION2 ioctl, which allows mapping guest_memfd memory into a guest. Signed-off-by: Matias Ezequiel Vara Larsen --- src/ioctls/vm.rs | 110 ++++++++++++++++++++++++++++++++++++++++++++++ src/kvm_ioctls.rs | 6 +++ 2 files changed, 116 insertions(+) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index c860710..a9ae072 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -111,6 +111,98 @@ impl VmFd { } } + /// Creates/modifies a guest physical memory slot. + /// + /// See the documentation for `KVM_SET_USER_MEMORY_REGION2`. + /// + /// # Arguments + /// + /// * `user_memory_region2` - Guest physical memory slot. For details check the + /// `kvm_userspace_memory_region2` structure in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Safety + /// + /// This function is unsafe because there is no guarantee `userspace_addr` points to a valid + /// memory region, nor the memory region lives as long as the kernel needs it to. + /// + /// The caller of this method must make sure that: + /// - the raw pointer (`userspace_addr`) points to valid memory + /// - the regions provided to KVM are not overlapping other memory regions. + /// - the guest_memfd points at a file created via KVM_CREATE_GUEST_MEMFD on + /// the current VM, and the target range must not be bound to any other memory region + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// extern crate kvm_bindings; + /// + /// use kvm_bindings::{ + /// kvm_create_guest_memfd, kvm_enable_cap, kvm_userspace_memory_region2, KVM_CAP_GUEST_MEMFD, + /// KVM_CAP_USER_MEMORY2, KVM_MEM_GUEST_MEMFD, + /// }; + /// use kvm_ioctls::Kvm; + /// use std::os::fd::RawFd; + /// + /// # #[cfg(target_arch = "x86_64")] + /// { + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// + /// let address_space = unsafe { libc::mmap(0 as _, 10000, 3, 34, -1, 0) }; + /// let userspace_addr = address_space as *const u8 as u64; + /// + /// let mut config = kvm_enable_cap { + /// cap: KVM_CAP_GUEST_MEMFD, + /// ..Default::default() + /// }; + /// + /// if vm.enable_cap(&config).is_err() { + /// return; + /// } + /// let gmem = kvm_create_guest_memfd { + /// size: 0x10000, + /// flags: 0, + /// reserved: [0; 6], + /// }; + /// + /// let fd: RawFd = unsafe { vm.create_guest_memfd(gmem).unwrap() }; + /// + /// config.cap = KVM_CAP_USER_MEMORY2; + /// + /// if vm.enable_cap(&config).is_err() { + /// return; + /// } + /// + /// let mem_region = kvm_userspace_memory_region2 { + /// slot: 0, + /// flags: KVM_MEM_GUEST_MEMFD, + /// guest_phys_addr: 0x10000 as u64, + /// memory_size: 0x10000 as u64, + /// userspace_addr, + /// guest_memfd_offset: 0, + /// guest_memfd: fd as u32, + /// pad1: 0, + /// pad2: [0; 14], + /// }; + /// unsafe { + /// vm.set_user_memory_region2(mem_region).unwrap(); + /// }; + /// } + /// ``` + pub unsafe fn set_user_memory_region2( + &self, + user_memory_region2: kvm_userspace_memory_region2, + ) -> Result<()> { + let ret = ioctl_with_ref(self, KVM_SET_USER_MEMORY_REGION2(), &user_memory_region2); + if ret == 0 { + Ok(()) + } else { + Err(errno::Error::last()) + } + } + /// Sets the address of the three-page region in the VM's address space. /// /// See the documentation for `KVM_SET_TSS_ADDR`. @@ -1725,6 +1817,24 @@ mod tests { assert!(unsafe { vm.set_user_memory_region(invalid_mem_region) }.is_err()); } + #[test] + fn test_set_invalid_memory2() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let invalid_mem_region = kvm_userspace_memory_region2 { + slot: 0, + flags: 0, + guest_phys_addr: 0, + memory_size: 0, + userspace_addr: 0, + guest_memfd_offset: 0, + guest_memfd: 0, + pad1: 0, + pad2: [0; 14], + }; + assert!(unsafe { vm.set_user_memory_region2(invalid_mem_region) }.is_err()); + } + #[test] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn test_set_tss_address() { diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index 397bb14..14fe21f 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -38,6 +38,12 @@ ioctl_iow_nr!( 0x46, kvm_userspace_memory_region ); +ioctl_iow_nr!( + KVM_SET_USER_MEMORY_REGION2, + KVMIO, + 0x49, + kvm_userspace_memory_region2 +); /* Available with KVM_CAP_SET_TSS_ADDR */ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_io_nr!(KVM_SET_TSS_ADDR, KVMIO, 0x47); From 6482786214dcc458eb9e39814db72ccf1b9db428 Mon Sep 17 00:00:00 2001 From: Matias Ezequiel Vara Larsen Date: Wed, 22 May 2024 16:23:58 +0200 Subject: [PATCH 277/332] Add support for KVM_CREATE_GUEST_MEMFD Add KVM_CREATE_GUEST_MEMFD ioctl, which allows creating an anonymous file and returns a file descriptor that refers to it. Signed-off-by: Matias Ezequiel Vara Larsen --- src/ioctls/vm.rs | 56 +++++++++++++++++++++++++++++++++++++++++++++++ src/kvm_ioctls.rs | 2 ++ 2 files changed, 58 insertions(+) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index a9ae072..eb966e3 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -1418,6 +1418,62 @@ impl VmFd { self.check_extension_int(c) > 0 } + /// Creates an anonymous file and returns a file descriptor that refers to it. + /// + /// See the documentation for `KVM_CREATE_GUEST_MEMFD`. + /// + /// Returns an io::Error when the file could not be created. + /// + /// # Arguments + /// + /// * kvm_create_guest_memfd - KVM create guest memfd structure. For details check the + /// `kvm_create_guest_memfd` structure in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// extern crate kvm_bindings; + /// + /// # use kvm_ioctls::Kvm; + /// use kvm_bindings::{kvm_create_guest_memfd, kvm_enable_cap, KVM_CAP_GUEST_MEMFD}; + /// use std::os::fd::RawFd; + /// + /// # #[cfg(target_arch = "x86_64")] + /// { + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// + /// let config = kvm_enable_cap { + /// cap: KVM_CAP_GUEST_MEMFD, + /// ..Default::default() + /// }; + /// + /// if vm.enable_cap(&config).is_err() { + /// return; + /// } + /// + /// let gmem = kvm_create_guest_memfd { + /// size: 0x1000, + /// flags: 0, + /// reserved: [0; 6], + /// }; + /// + /// let id: RawFd = vm.create_guest_memfd(gmem).unwrap(); + /// } + /// ``` + #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] + pub fn create_guest_memfd(&self, gmem: kvm_create_guest_memfd) -> Result { + // SAFETY: Safe because we know that our file is a VM fd, we know the kernel will only + // read the correct amount of memory from our pointer, and we verify the return result. + let ret = unsafe { ioctl_with_ref(self, KVM_CREATE_GUEST_MEMFD(), &gmem) }; + if ret < 0 { + return Err(errno::Error::last()); + } + Ok(ret) + } + /// Issues platform-specific memory encryption commands to manage encrypted VMs if /// the platform supports creating those encrypted VMs. /// diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index 14fe21f..1d07e3e 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -50,6 +50,8 @@ ioctl_io_nr!(KVM_SET_TSS_ADDR, KVMIO, 0x47); /* Available with KVM_CAP_SET_IDENTITY_MAP_ADDR */ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ioctl_iow_nr!(KVM_SET_IDENTITY_MAP_ADDR, KVMIO, 0x48, u64); +#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] +ioctl_iowr_nr!(KVM_CREATE_GUEST_MEMFD, KVMIO, 0xd4, kvm_create_guest_memfd); /* Available with KVM_CAP_IRQCHIP */ #[cfg(any( target_arch = "x86", From ba56464a8b958820305498d10f3a9a3682dbf17e Mon Sep 17 00:00:00 2001 From: Matias Ezequiel Vara Larsen Date: Tue, 4 Jun 2024 11:37:44 +0200 Subject: [PATCH 278/332] Add support for KVM_SET_MEMORY_ATTRIBUTES Add KVM_SET_MEMORY_ATTRIBUTES ioctl, which allows userspace to set memory attributes for a range of guest physical memory. Signed-off-by: Matias Ezequiel Vara Larsen --- src/ioctls/vm.rs | 94 +++++++++++++++++++++++++++++++++++++++++++++++ src/kvm_ioctls.rs | 8 ++++ 2 files changed, 102 insertions(+) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index eb966e3..d6ad5c5 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -1474,6 +1474,100 @@ impl VmFd { Ok(ret) } + /// Allows userspace to set memory attributes for a range of guest physical memory. + /// + /// See the documentation for `KVM_SET_MEMORY_ATTRIBUTES`. + /// + /// Returns an io::Error when the attributes could not be set. + /// + /// # Arguments + /// + /// * kvm_memory_attributes - KVM set memory attributes structure. For details check the + /// `kvm_memory_attributes` structure in the + /// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). + /// + /// # Example + /// + /// ```rust + /// # extern crate kvm_ioctls; + /// extern crate kvm_bindings; + /// + /// # use kvm_ioctls::Kvm; + /// use kvm_bindings::{ + /// kvm_create_guest_memfd, kvm_enable_cap, kvm_memory_attributes, + /// kvm_userspace_memory_region2, KVM_CAP_GUEST_MEMFD, KVM_CAP_MEMORY_ATTRIBUTES, + /// KVM_CAP_USER_MEMORY2, KVM_MEMORY_ATTRIBUTE_PRIVATE, KVM_MEM_GUEST_MEMFD, + /// }; + /// use std::os::fd::RawFd; + /// + /// # #[cfg(target_arch = "x86_64")] + /// { + /// let kvm = Kvm::new().unwrap(); + /// let vm = kvm.create_vm().unwrap(); + /// let gmem = kvm_create_guest_memfd { + /// size: 0x10000, + /// flags: 0, + /// reserved: [0; 6], + /// }; + /// + /// let address_space = unsafe { libc::mmap(0 as _, 10000, 3, 34, -1, 0) }; + /// let userspace_addr = address_space as *const u8 as u64; + /// let mut config = kvm_enable_cap { + /// cap: KVM_CAP_GUEST_MEMFD, + /// ..Default::default() + /// }; + /// + /// if vm.enable_cap(&config).is_err() { + /// return; + /// } + /// + /// config.cap = KVM_CAP_USER_MEMORY2; + /// + /// if vm.enable_cap(&config).is_err() { + /// return; + /// } + /// config.cap = KVM_CAP_MEMORY_ATTRIBUTES; + /// + /// if vm.enable_cap(&config).is_err() { + /// return; + /// } + /// let fd: RawFd = unsafe { vm.create_guest_memfd(gmem).unwrap() }; + /// let mem_region = kvm_userspace_memory_region2 { + /// slot: 0, + /// flags: KVM_MEM_GUEST_MEMFD, + /// guest_phys_addr: 0x10000 as u64, + /// memory_size: 0x10000 as u64, + /// userspace_addr, + /// guest_memfd_offset: 0, + /// guest_memfd: fd as u32, + /// pad1: 0, + /// pad2: [0; 14], + /// }; + /// unsafe { + /// vm.set_user_memory_region2(mem_region).unwrap(); + /// }; + /// + /// let attr = kvm_memory_attributes { + /// address: 0x10000, + /// size: 0x10000, + /// attributes: KVM_MEMORY_ATTRIBUTE_PRIVATE as u64, + /// flags: 0, + /// }; + /// vm.set_memory_attributes(attr).unwrap(); + /// } + /// ``` + #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] + pub fn set_memory_attributes(&self, attr: kvm_memory_attributes) -> Result<()> { + // SAFETY: Safe because we know that our file is a VM fd, we know the kernel will only read + // the correct amount of memory from our pointer, and we verify the return result. + let ret = unsafe { ioctl_with_ref(self, KVM_SET_MEMORY_ATTRIBUTES(), &attr) }; + if ret == 0 { + Ok(()) + } else { + Err(errno::Error::last()) + } + } + /// Issues platform-specific memory encryption commands to manage encrypted VMs if /// the platform supports creating those encrypted VMs. /// diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index 1d07e3e..ed5d87e 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -276,6 +276,14 @@ ioctl_iow_nr!(KVM_ARM_VCPU_FINALIZE, KVMIO, 0xc2, std::os::raw::c_int); /* Available with KVM_CAP_SET_GUEST_DEBUG */ ioctl_iow_nr!(KVM_SET_GUEST_DEBUG, KVMIO, 0x9b, kvm_guest_debug); +#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] +ioctl_iow_nr!( + KVM_SET_MEMORY_ATTRIBUTES, + KVMIO, + 0xd2, + kvm_memory_attributes +); + // Device ioctls. /* Available with KVM_CAP_DEVICE_CTRL */ From 0fb6fb7420cf76b6532588392b9a548f377f8d5b Mon Sep 17 00:00:00 2001 From: Matias Ezequiel Vara Larsen Date: Thu, 25 Jul 2024 11:44:55 +0200 Subject: [PATCH 279/332] Update kvm-bindings to 0.9.0 Signed-off-by: Matias Ezequiel Vara Larsen --- Cargo.toml | 2 +- src/ioctls/vcpu.rs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fd985e8..574f80c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ edition = "2021" [dependencies] libc = "0.2.39" -kvm-bindings = { version = "0.8.0", features = ["fam-wrappers"] } +kvm-bindings = { version = "0.9.0", features = ["fam-wrappers"] } vmm-sys-util = "0.12.1" bitflags = "2.4.1" diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 92ac796..afa75ad 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1475,7 +1475,9 @@ impl VcpuFd { nr: hypercall.nr, args: hypercall.args, ret: &mut hypercall.ret, - longmode: hypercall.longmode, + // SAFETY: Safe because the exit_reason (which comes from the kernel) told us + // which union field to use. + longmode: unsafe { hypercall.__bindgen_anon_1.longmode }, })) } KVM_EXIT_DEBUG => { From 9890580bda29802513b962adb5304bddc46b809a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 10:54:45 +0000 Subject: [PATCH 280/332] Bump rust-vmm-ci from `0503867` to `9f641f2` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `0503867` to `9f641f2`. - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/05038671bbc09476d9051c884f754689ae1774f9...9f641f269b987aeec4a771f459f6d2af28cf7288) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 0503867..9f641f2 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 05038671bbc09476d9051c884f754689ae1774f9 +Subproject commit 9f641f269b987aeec4a771f459f6d2af28cf7288 From 295e83146938d00add1c0cea7b2a08c273323154 Mon Sep 17 00:00:00 2001 From: Nikita Kalyazin Date: Thu, 1 Aug 2024 13:23:35 +0000 Subject: [PATCH 281/332] Add MemoryFaultInfo variant to Cap Signed-off-by: Nikita Kalyazin --- src/cap.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cap.rs b/src/cap.rs index 9d31f91..08265d8 100644 --- a/src/cap.rs +++ b/src/cap.rs @@ -169,4 +169,6 @@ pub enum Cap { X86UserSpaceMsr = KVM_CAP_X86_USER_SPACE_MSR, #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ExitHypercall = KVM_CAP_EXIT_HYPERCALL, + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + MemoryFaultInfo = KVM_CAP_MEMORY_FAULT_INFO, } From e2c7a913a20df4850f6d4390dfc4bec3b81d6c73 Mon Sep 17 00:00:00 2001 From: Nikita Kalyazin Date: Thu, 1 Aug 2024 11:32:20 +0000 Subject: [PATCH 282/332] Propagate MemoryFault exit reason in KVM_RUN Signed-off-by: Nikita Kalyazin --- src/ioctls/vcpu.rs | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index afa75ad..b3c25de 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -166,6 +166,15 @@ pub enum VcpuExit<'a> { X86Rdmsr(ReadMsrExit<'a>), /// Corresponds to KVM_EXIT_X86_WRMSR. X86Wrmsr(WriteMsrExit<'a>), + /// Corresponds to KVM_EXIT_MEMORY_FAULT. + MemoryFault { + /// flags + flags: u64, + /// gpa + gpa: u64, + /// size + size: u64, + }, /// Corresponds to an exit reason that is unknown from the current version /// of the kvm-ioctls crate. Let the consumer decide about what to do with /// it. @@ -1569,7 +1578,30 @@ impl VcpuFd { r => Ok(VcpuExit::Unsupported(r)), } } else { - Err(errno::Error::last()) + let errno = errno::Error::last(); + let run = self.kvm_run_ptr.as_mut_ref(); + // From https://docs.kernel.org/virt/kvm/api.html#kvm-run : + // + // KVM_EXIT_MEMORY_FAULT is unique among all KVM exit reasons in that it accompanies + // a return code of ‘-1’, not ‘0’! errno will always be set to EFAULT or EHWPOISON + // when KVM exits with KVM_EXIT_MEMORY_FAULT, userspace should assume kvm_run.exit_reason + // is stale/undefined for all other error numbers. + if ret == -1 + && (errno == errno::Error::new(libc::EFAULT) + || errno == errno::Error::new(libc::EHWPOISON)) + && run.exit_reason == KVM_EXIT_MEMORY_FAULT + { + // SAFETY: Safe because the exit_reason (which comes from the kernel) told us + // which union field to use. + let fault = unsafe { &mut run.__bindgen_anon_1.memory_fault }; + Ok(VcpuExit::MemoryFault { + flags: fault.flags, + gpa: fault.gpa, + size: fault.size, + }) + } else { + Err(errno) + } } } From 645533cb235c4375d160dc5048c9b130f0de836b Mon Sep 17 00:00:00 2001 From: Nikita Kalyazin Date: Thu, 1 Aug 2024 11:39:41 +0000 Subject: [PATCH 283/332] Update changelog Signed-off-by: Nikita Kalyazin --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5977697..73f2a61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Added - [[#267](https://github.com/rust-vmm/kvm-ioctls/pull/267)]: Added `HypercallExit` field to `VcpuExit::Hypercall` and added `ExitHypercall` to `Cap`. +- [[#270](https://github.com/rust-vmm/kvm-ioctls/pull/270)]: Added `MemoryFaultInfo` to `Cap` and propagated `MemoryFault` exit reason in `KVM_RUN`. ### Changed From 6a90e8a22dbf66eafcb823ced0fb5f2347ffe2a2 Mon Sep 17 00:00:00 2001 From: Nikita Kalyazin Date: Thu, 1 Aug 2024 16:37:57 +0000 Subject: [PATCH 284/332] Update changelog Signed-off-by: Nikita Kalyazin --- CHANGELOG.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73f2a61..4ffbb96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,12 @@ ### Added -- [[#267](https://github.com/rust-vmm/kvm-ioctls/pull/267)]: Added `HypercallExit` field to `VcpuExit::Hypercall` and added `ExitHypercall` to `Cap`. -- [[#270](https://github.com/rust-vmm/kvm-ioctls/pull/270)]: Added `MemoryFaultInfo` to `Cap` and propagated `MemoryFault` exit reason in `KVM_RUN`. +- [[#264](https://github.com/rust-vmm/kvm-ioctls/pull/264)]: Added `KVM_SET_USER_MEMORY_REGION2`, + `KVM_CREATE_GUEST_MEMFD` and `KVM_SET_MEMORY_ATTRIBUTES` ioctls. +- [[#267](https://github.com/rust-vmm/kvm-ioctls/pull/267)]: Added `HypercallExit` field to + `VcpuExit::Hypercall` and added `ExitHypercall` to `Cap`. +- [[#270](https://github.com/rust-vmm/kvm-ioctls/pull/270)]: Added `MemoryFaultInfo` to `Cap` and + propagated `MemoryFault` exit reason in `KVM_RUN`. ### Changed From 50f0567c17326c77d5f2af4d1ad99d65ea0e067f Mon Sep 17 00:00:00 2001 From: Nikita Kalyazin Date: Thu, 1 Aug 2024 16:39:47 +0000 Subject: [PATCH 285/332] Prepare 0.18.0 release Signed-off-by: Nikita Kalyazin --- CHANGELOG.md | 8 ++++++-- Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ffbb96..f2b3703 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ ### Added +### Changed + +## v0.18.0 + +### Added + - [[#264](https://github.com/rust-vmm/kvm-ioctls/pull/264)]: Added `KVM_SET_USER_MEMORY_REGION2`, `KVM_CREATE_GUEST_MEMFD` and `KVM_SET_MEMORY_ATTRIBUTES` ioctls. - [[#267](https://github.com/rust-vmm/kvm-ioctls/pull/267)]: Added `HypercallExit` field to @@ -11,8 +17,6 @@ - [[#270](https://github.com/rust-vmm/kvm-ioctls/pull/270)]: Added `MemoryFaultInfo` to `Cap` and propagated `MemoryFault` exit reason in `KVM_RUN`. -### Changed - ## v0.17.0 ### Changed diff --git a/Cargo.toml b/Cargo.toml index 574f80c..a826fe3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kvm-ioctls" -version = "0.17.0" +version = "0.18.0" authors = ["Amazon Firecracker Team "] description = "Safe wrappers over KVM ioctls" repository = "https://github.com/rust-vmm/kvm-ioctls" From 47665cbeb280b46dd1166248afce1e7e3d87678c Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Mon, 12 Aug 2024 16:28:21 +0200 Subject: [PATCH 286/332] Mark DeviceFd::get_device_attr as unsafe KVM_GET_DEVICE_ATTR causes the kernel to write to an arbitrary address. This is unsafe, as it can allow writing to an address Rust believes to be immutable. I discovered this because an optimisation change[1] in Rust 1.80.0 caused a Cloud Hypervisor test to start failing when built in release mode, because it was setting the addr passed to get_device_attr() to the address of an immutable variable. [1]: https://github.com/rust-lang/rust/commit/d2d24e395a1e4fcee62ca17bf4cbddb1f903af97 Fixes: 8ea124b ("Add support for `KVM_GET_DEVICE_ATTR` ioctl") Signed-off-by: Alyssa Ross --- CHANGELOG.md | 3 +++ src/ioctls/device.rs | 25 ++++++++++++++++--------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2b3703..ebe3eb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ ### Changed +- [[#273](https://github.com/rust-vmm/kvm-ioctls/pull/273)]: `DeviceFd::get_device_attr` is now + marked as unsafe. + ## v0.18.0 ### Added diff --git a/src/ioctls/device.rs b/src/ioctls/device.rs index 6669570..a4a0f49 100644 --- a/src/ioctls/device.rs +++ b/src/ioctls/device.rs @@ -102,6 +102,11 @@ impl DeviceFd { /// * `device_attr` - The `addr` field of the `device_attr` structure will point to /// the device attribute data. /// + /// # Safety + /// + /// The caller is responsible for the validity of the `device_attr` argument, + /// including that it is safe to write to the `addr` member. + /// /// # Examples /// ```rust /// # extern crate kvm_ioctls; @@ -138,14 +143,16 @@ impl DeviceFd { /// let mut data: u32 = 0; /// let mut gic_attr = kvm_bindings::kvm_device_attr::default(); /// gic_attr.group = KVM_DEV_ARM_VGIC_GRP_NR_IRQS; - /// gic_attr.addr = &mut data as *const u32 as u64; + /// gic_attr.addr = &mut data as *mut u32 as u64; /// - /// device_fd.get_device_attr(&mut gic_attr).unwrap(); + /// // SAFETY: gic_attr.addr is safe to write to. + /// unsafe { device_fd.get_device_attr(&mut gic_attr) }.unwrap(); /// } /// ``` - pub fn get_device_attr(&self, device_attr: &mut kvm_device_attr) -> Result<()> { - // SAFETY: We are calling this function with a Device fd, and we trust the kernel. - let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_DEVICE_ATTR(), device_attr) }; + pub unsafe fn get_device_attr(&self, device_attr: &mut kvm_device_attr) -> Result<()> { + // SAFETY: Caller has ensured device_attr.addr is safe to write to. + // We are calling this function with a Device fd, we trust the kernel. + let ret = ioctl_with_mut_ref(self, KVM_GET_DEVICE_ATTR(), device_attr); if ret != 0 { return Err(errno::Error::last()); } @@ -234,7 +241,7 @@ mod tests { // We are just creating a test device. Creating a real device would make the CI dependent // on host configuration (like having /dev/vfio). We expect this to fail. assert!(device_fd.has_device_attr(&dist_attr).is_err()); - assert!(device_fd.get_device_attr(&mut dist_attr_mut).is_err()); + assert!(unsafe { device_fd.get_device_attr(&mut dist_attr_mut) }.is_err()); assert!(device_fd.set_device_attr(&dist_attr).is_err()); assert_eq!(errno::Error::last().errno(), 25); } @@ -307,11 +314,11 @@ mod tests { // Without properly providing the address to where the // value will be stored, the ioctl fails with EFAULT. - let res = device_fd.get_device_attr(&mut gic_attr); + let res = unsafe { device_fd.get_device_attr(&mut gic_attr) }; assert_eq!(res, Err(Error::new(libc::EFAULT))); - gic_attr.addr = &mut data as *const u32 as u64; - assert!(device_fd.get_device_attr(&mut gic_attr).is_ok()); + gic_attr.addr = &mut data as *mut u32 as u64; + assert!(unsafe { device_fd.get_device_attr(&mut gic_attr) }.is_ok()); // The maximum supported number of IRQs should be 128, same as the value // when we initialize the GIC. assert_eq!(data, 128); From a56194d943a361034569acf9f4cba57f8f9f3f89 Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Mon, 12 Aug 2024 16:48:25 +0200 Subject: [PATCH 287/332] Fix IO safety runtime errors in tests If debug_assertions is enabled, Rust 1.80.0 and later will abort the process when attempting to close a file descriptor that is already closed[1]. This means that, in tests, where an invalid file descriptor object is deliberately constructed, we have to avoid that object being dropped. Using into_raw_fd() allows the object's memory to be freed (unlike std::mem::forget()), without attempting to close the file descriptor. [1]: https://github.com/rust-lang/rust/commit/cb4940645775f60d74aee2e018d6c516c5aa9ed7 Signed-off-by: Alyssa Ross --- src/ioctls/system.rs | 5 +++++ src/ioctls/vcpu.rs | 18 +++++++++++++++--- src/ioctls/vm.rs | 6 +++++- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index fc8b19a..f9176b2 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -730,6 +730,7 @@ mod tests { #![allow(clippy::undocumented_unsafe_blocks)] use super::*; use libc::{fcntl, FD_CLOEXEC, F_GETFD}; + use std::os::fd::IntoRawFd; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use vmm_sys_util::fam::FamStruct; @@ -978,5 +979,9 @@ mod tests { ); } assert_eq!(faulty_kvm.create_vm().err().unwrap().errno(), badf_errno); + + // Don't drop the File object, or it'll notice the file it's trying to close is + // invalid and abort the process. + faulty_kvm.kvm.into_raw_fd(); } } diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index b3c25de..14acc49 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -2543,7 +2543,7 @@ mod tests { target_arch = "aarch64" ))] fn test_faulty_vcpu_fd() { - use std::os::unix::io::FromRawFd; + use std::os::unix::io::{FromRawFd, IntoRawFd}; let badf_errno = libc::EBADF; @@ -2579,12 +2579,16 @@ mod tests { badf_errno ); assert_eq!(faulty_vcpu_fd.run().unwrap_err().errno(), badf_errno); + + // Don't drop the File object, or it'll notice the file it's trying to close is + // invalid and abort the process. + faulty_vcpu_fd.vcpu.into_raw_fd(); } #[test] #[cfg(target_arch = "x86_64")] fn test_faulty_vcpu_fd_x86_64() { - use std::os::unix::io::FromRawFd; + use std::os::unix::io::{FromRawFd, IntoRawFd}; let badf_errno = libc::EBADF; @@ -2699,6 +2703,10 @@ mod tests { assert!(faulty_vcpu_fd.get_tsc_khz().is_err()); assert!(faulty_vcpu_fd.set_tsc_khz(1000000).is_err()); assert!(faulty_vcpu_fd.translate_gva(u64::MAX).is_err()); + + // Don't drop the File object, or it'll notice the file it's trying to close is + // invalid and abort the process. + faulty_vcpu_fd.vcpu.into_raw_fd(); } #[test] @@ -2721,7 +2729,7 @@ mod tests { #[test] #[cfg(target_arch = "aarch64")] fn test_faulty_vcpu_fd_aarch64() { - use std::os::unix::io::FromRawFd; + use std::os::unix::io::{FromRawFd, IntoRawFd}; let badf_errno = libc::EBADF; @@ -2793,6 +2801,10 @@ mod tests { .errno(), badf_errno ); + + // Don't drop the File object, or it'll notice the file it's trying to close is + // invalid and abort the process. + faulty_vcpu_fd.vcpu.into_raw_fd(); } #[test] diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index d6ad5c5..1f49077 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -1949,7 +1949,7 @@ mod tests { use crate::Kvm; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - use std::{fs::OpenOptions, ptr::null_mut}; + use std::{fs::OpenOptions, os::fd::IntoRawFd, ptr::null_mut}; use libc::EFD_NONBLOCK; @@ -2395,6 +2395,10 @@ mod tests { faulty_vm_fd.get_dirty_log(0, 0).unwrap_err().errno(), badf_errno ); + + // Don't drop the File object, or it'll notice the file it's trying to close is + // invalid and abort the process. + faulty_vm_fd.vm.into_raw_fd(); } #[test] From 0df74bb9295143aaaeac03cca1579e11d747162c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 10:35:06 +0000 Subject: [PATCH 288/332] Bump rust-vmm-ci from `9f641f2` to `5e818dc` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `9f641f2` to `5e818dc`. - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/9f641f269b987aeec4a771f459f6d2af28cf7288...5e818dc729574693731b4567c6a3bc07788b53b0) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 9f641f2..5e818dc 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 9f641f269b987aeec4a771f459f6d2af28cf7288 +Subproject commit 5e818dc729574693731b4567c6a3bc07788b53b0 From 1b8a08001c926954a94bc1a1d65caa40970c2520 Mon Sep 17 00:00:00 2001 From: Bo Chen Date: Thu, 5 Sep 2024 11:24:53 -0700 Subject: [PATCH 289/332] Remove trailing whitespaces from changelog Signed-off-by: Bo Chen --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ebe3eb3..c7d1652 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,7 +66,7 @@ support for vCPU SVE feature. `KVM_GET_MSR_FEATURE_INDEX_LIST` and `KVM_GET_MSRS` system ioctls. - [[#221](https://github.com/rust-vmm/kvm-ioctls/pull/221)] Add `Cap::ArmPmuV3`. - + ### Changed - [[#223](https://github.com/rust-vmm/kvm-ioctls/pull/223)] aarch64: @@ -84,10 +84,10 @@ support for vCPU SVE feature. ### Added -- [[#187](https://github.com/rust-vmm/kvm-ioctls/pull/187)] Support for +- [[#187](https://github.com/rust-vmm/kvm-ioctls/pull/187)] Support for `KVM_SET_IDENTITY_MAP_ADDR` - Derive Debug for all exported structs and enums -- [[#189](https://github.com/rust-vmm/kvm-ioctls/pull/189)] Expose `KVM_SET_` and +- [[#189](https://github.com/rust-vmm/kvm-ioctls/pull/189)] Expose `KVM_SET_` and `KVM_HAS_DEVICE_ATTR` for vcpu - [[#191](https://github.com/rust-vmm/kvm-ioctls/pull/191)] Add `KVM_TRANSLATE` support and the `translate_gva` function that translates guest virtual address to the physical address @@ -112,7 +112,7 @@ support for vCPU SVE feature. - [[#195](https://github.com/rust-vmm/kvm-ioctls/pull/195)] Do not panic on unsupported `KVM_EXIT` reason - [[#196](https://github.com/rust-vmm/kvm-ioctls/pull/196)] Expose a mutable reference - to the `kvm_run` structure to allow proper handling of unsupported exit reasons + to the `kvm_run` structure to allow proper handling of unsupported exit reasons - [[#200](https://github.com/rust-vmm/kvm-ioctls/pull/200)] Fix wrong `target_arch` gate preventing `set_guest_debug` from being exported on ARM - [[#206](https://github.com/rust-vmm/kvm-ioctls/pull/206)] use `u128` in `get/set_on_reg` From 283511653a118e1279cc998f069ffd577cf94a56 Mon Sep 17 00:00:00 2001 From: Bo Chen Date: Thu, 5 Sep 2024 11:20:52 -0700 Subject: [PATCH 290/332] Update kvm-bindings to 0.9.1 Signed-off-by: Bo Chen --- CHANGELOG.md | 1 + Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7d1652..1b6a888 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - [[#273](https://github.com/rust-vmm/kvm-ioctls/pull/273)]: `DeviceFd::get_device_attr` is now marked as unsafe. +- [[#277](https://github.com/rust-vmm/kvm-ioctls/pull/277)]: Updated kvm-bindings to 0.9.1. ## v0.18.0 diff --git a/Cargo.toml b/Cargo.toml index a826fe3..75d8c37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ edition = "2021" [dependencies] libc = "0.2.39" -kvm-bindings = { version = "0.9.0", features = ["fam-wrappers"] } +kvm-bindings = { version = "0.9.1", features = ["fam-wrappers"] } vmm-sys-util = "0.12.1" bitflags = "2.4.1" From 8d824b994ac33d3b0e345b207efb6d5c53860198 Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Tue, 13 Aug 2024 09:14:40 +0200 Subject: [PATCH 291/332] README: stop saying that aarch64 is experimental The linked issue, and the other one linked from it have been closed for a long time, and this crate is widely used on aarch64. Signed-off-by: Alyssa Ross --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index faedf93..e0c164e 100644 --- a/README.md +++ b/README.md @@ -18,10 +18,7 @@ as the code documentation. ## Supported Platforms -The kvm-ioctls can be used on x86_64 and aarch64. Right now the aarch64 support -is considered experimental. For a production ready version, please check the -progress in the corresponding -[GitHub issue](https://github.com/rust-vmm/kvm-ioctls/issues/8). +The kvm-ioctls can be used on x86_64 and aarch64. ## Running the tests From d31e54bc9edb1d7591dd218c738c1a92b99fb0ad Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Tue, 24 Sep 2024 05:01:02 +0000 Subject: [PATCH 292/332] build: Bump kvm-bindings from 0.9.1 to 0.10.0 kvm-bindings 0.10.0 incorporates RISC-V bindings. Signed-off-by: Ruoqing He --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 75d8c37..56768e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ edition = "2021" [dependencies] libc = "0.2.39" -kvm-bindings = { version = "0.9.1", features = ["fam-wrappers"] } +kvm-bindings = { version = "0.10.0", features = ["fam-wrappers"] } vmm-sys-util = "0.12.1" bitflags = "2.4.1" From 8037fd510f1e8e5d40085a23dd0e6c3a9dd529c0 Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Wed, 25 Sep 2024 02:33:20 +0000 Subject: [PATCH 293/332] build: Fix unexpected `target_arch` warning Replace `s390` with `s390x`, `ppc` with `powerpc` in `target_arch` predicates. Signed-off-by: Ruoqing He --- src/cap.rs | 2 +- src/ioctls/vcpu.rs | 10 +++++----- src/kvm_ioctls.rs | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/cap.rs b/src/cap.rs index 08265d8..af370ee 100644 --- a/src/cap.rs +++ b/src/cap.rs @@ -46,7 +46,7 @@ pub enum Cap { target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", - target_arch = "s390" + target_arch = "s390x" ))] SetGuestDebug = KVM_CAP_SET_GUEST_DEBUG, #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 14acc49..c4bf833 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -796,7 +796,7 @@ impl VcpuFd { target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", - target_arch = "s390" + target_arch = "s390x" ))] pub fn get_mp_state(&self) -> Result { let mut mp_state = Default::default(); @@ -834,7 +834,7 @@ impl VcpuFd { target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", - target_arch = "s390" + target_arch = "s390x" ))] pub fn set_mp_state(&self, mp_state: kvm_mp_state) -> Result<()> { // SAFETY: Here we trust the kernel not to read past the end of the kvm_mp_state struct. @@ -1279,8 +1279,8 @@ impl VcpuFd { target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64", - target_arch = "s390", - target_arch = "ppc" + target_arch = "s390x", + target_arch = "powerpc" ))] pub fn set_guest_debug(&self, debug_struct: &kvm_guest_debug) -> Result<()> { // SAFETY: Safe because we allocated the structure and we trust the kernel. @@ -2234,7 +2234,7 @@ mod tests { target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", - target_arch = "s390" + target_arch = "s390x" ))] #[test] fn mpstate_test() { diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index ed5d87e..60b294d 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -58,7 +58,7 @@ ioctl_iowr_nr!(KVM_CREATE_GUEST_MEMFD, KVMIO, 0xd4, kvm_create_guest_memfd); target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", - target_arch = "s390" + target_arch = "s390x" ))] ioctl_io_nr!(KVM_CREATE_IRQCHIP, KVMIO, 0x60); /* Available with KVM_CAP_IRQCHIP */ @@ -97,7 +97,7 @@ ioctl_iow_nr!(KVM_SET_GSI_ROUTING, KVMIO, 0x6a, kvm_irq_routing); target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", - target_arch = "s390" + target_arch = "s390x" ))] ioctl_iow_nr!(KVM_IRQFD, KVMIO, 0x76, kvm_irqfd); /* Available with KVM_CAP_PIT2 */ @@ -182,7 +182,7 @@ ioctl_iowr_nr!(KVM_GET_CPUID2, KVMIO, 0x91, kvm_cpuid2); target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", - target_arch = "s390" + target_arch = "s390x" ))] ioctl_ior_nr!(KVM_GET_MP_STATE, KVMIO, 0x98, kvm_mp_state); /* Available with KVM_CAP_MP_STATE */ @@ -191,7 +191,7 @@ ioctl_ior_nr!(KVM_GET_MP_STATE, KVMIO, 0x98, kvm_mp_state); target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", - target_arch = "s390" + target_arch = "s390x" ))] ioctl_iow_nr!(KVM_SET_MP_STATE, KVMIO, 0x99, kvm_mp_state); /* Available with KVM_CAP_USER_NMI */ From adaf1ee6ad0751911b175bc38cfd1a17abac7019 Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Wed, 25 Sep 2024 02:44:24 +0000 Subject: [PATCH 294/332] build: Fix unexpected attribute warning Add `has_sev` to expected attribute. Signed-off-by: Ruoqing He --- build.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.rs b/build.rs index 60b655a..067a65d 100644 --- a/build.rs +++ b/build.rs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT fn main() { + // Add `has_sev` to expected attributes. + println!("cargo:rustc-check-cfg=cfg(has_sev)"); // Define a `has_sev` attribute, which is used for conditional // execution of SEV-specific tests and examples. if std::path::Path::new("/dev/sev").exists() { From 963b046d6986fe55fad20129248a388a1c60bb99 Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Thu, 26 Sep 2024 05:23:37 +0000 Subject: [PATCH 295/332] doc test: Replace `MaxVcpuId` with `MaxVcpus` RISC-V does not implement `KVM_CAP_MAX_VCPU_ID`, use `MaxVcpus` capability to test `check_extension` functionality on all platforms. Signed-off-by: Ruoqing He --- src/ioctls/system.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index f9176b2..b10040b 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -193,7 +193,7 @@ impl Kvm { /// use kvm_ioctls::Cap; /// /// let kvm = Kvm::new().unwrap(); - /// assert!(kvm.check_extension_raw(Cap::MaxVcpuId as c_ulong) > 0); + /// assert!(kvm.check_extension_raw(Cap::MaxVcpus as c_ulong) > 0); /// ``` pub fn check_extension_raw(&self, c: c_ulong) -> i32 { // SAFETY: Safe because we know that our file is a KVM fd. @@ -217,7 +217,7 @@ impl Kvm { /// use kvm_ioctls::Cap; /// /// let kvm = Kvm::new().unwrap(); - /// assert!(kvm.check_extension_int(Cap::MaxVcpuId) > 0); + /// assert!(kvm.check_extension_int(Cap::MaxVcpus) > 0); /// ``` pub fn check_extension_int(&self, c: Cap) -> i32 { self.check_extension_raw(c as c_ulong) From f20ce773a9e53c55faa9ec82abe186781c3d7542 Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Thu, 26 Sep 2024 05:27:27 +0000 Subject: [PATCH 296/332] build: Rename config to config.toml According to warning emitted by cargo, replacing `config` with `config.toml` since it's deprecated. Signed-off-by: Ruoqing He --- .cargo/{config => config.toml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .cargo/{config => config.toml} (100%) diff --git a/.cargo/config b/.cargo/config.toml similarity index 100% rename from .cargo/config rename to .cargo/config.toml From 1d67227dddeb7ade34fc03279568167dae80342c Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Thu, 26 Sep 2024 05:55:34 +0000 Subject: [PATCH 297/332] doc test: Remove redundent `target_arch` predicate Removed unnecessary target arhictecture predicates since the related methods would not present for disabled architecture. Disabled `KVM_CAP_ENABLE_CAP` and g/set method of `kvm_regs` for RISC-V platform. Signed-off-by: Ruoqing He --- src/ioctls/vcpu.rs | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index c4bf833..8835f00 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -220,7 +220,6 @@ impl VcpuFd { /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// let vcpu = vm.create_vcpu(0).unwrap(); - /// #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] /// let regs = vcpu.get_regs().unwrap(); /// ``` #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] @@ -333,14 +332,11 @@ impl VcpuFd { /// let vm = kvm.create_vm().unwrap(); /// let vcpu = vm.create_vcpu(0).unwrap(); /// - /// #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] - /// { - /// // Get the current vCPU registers. - /// let mut regs = vcpu.get_regs().unwrap(); - /// // Set a new value for the Instruction Pointer. - /// regs.rip = 0x100; - /// vcpu.set_regs(®s).unwrap(); - /// } + /// // Get the current vCPU registers. + /// let mut regs = vcpu.get_regs().unwrap(); + /// // Set a new value for the Instruction Pointer. + /// regs.rip = 0x100; + /// vcpu.set_regs(®s).unwrap(); /// ``` #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] pub fn set_regs(&self, regs: &kvm_regs) -> Result<()> { @@ -367,7 +363,6 @@ impl VcpuFd { /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// let vcpu = vm.create_vcpu(0).unwrap(); - /// #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] /// let sregs = vcpu.get_sregs().unwrap(); /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] @@ -397,14 +392,12 @@ impl VcpuFd { /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// let vcpu = vm.create_vcpu(0).unwrap(); - /// #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] - /// { - /// let mut sregs = vcpu.get_sregs().unwrap(); - /// // Update the code segment (cs). - /// sregs.cs.base = 0; - /// sregs.cs.selector = 0; - /// vcpu.set_sregs(&sregs).unwrap(); - /// } + /// + /// let mut sregs = vcpu.get_sregs().unwrap(); + /// // Update the code segment (cs). + /// sregs.cs.base = 0; + /// sregs.cs.selector = 0; + /// vcpu.set_sregs(&sregs).unwrap(); /// ``` #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn set_sregs(&self, sregs: &kvm_sregs) -> Result<()> { From ae039f245db0cd40ac20c32049ec536222b6dea7 Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Thu, 26 Sep 2024 08:03:59 +0000 Subject: [PATCH 298/332] vm: Create `irqchip` before use PIT ioctls As this patch [1] documents, and pointed out by @roypat, create an in-kernel `irqchip` before invoking any ioctls relate to PIT. [1] https://lore.kernel.org/kvm/CA+i-1C1LOn19FcddyC5kV8idGQq5KDdAjWBo80ANpRGn8DCx3g@mail.gmail.com/T/ Signed-off-by: Ruoqing He --- src/ioctls/vm.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 1f49077..1b1364d 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -392,6 +392,7 @@ impl VmFd { /// /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); + /// vm.create_irq_chip().unwrap(); /// let pit_config = kvm_pit_config::default(); /// vm.create_pit2(pit_config).unwrap(); /// ``` @@ -425,6 +426,7 @@ impl VmFd { /// # use kvm_ioctls::Kvm; /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); + /// vm.create_irq_chip().unwrap(); /// /// let pit_config = kvm_pit_config::default(); /// vm.create_pit2(pit_config).unwrap(); @@ -460,6 +462,7 @@ impl VmFd { /// # use kvm_ioctls::Kvm; /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); + /// vm.create_irq_chip().unwrap(); /// /// let pit_config = kvm_pit_config::default(); /// vm.create_pit2(pit_config).unwrap(); @@ -2063,6 +2066,9 @@ mod tests { fn test_pit2() { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); + assert!(kvm.check_extension(Cap::Irqchip)); + assert!(vm.create_irq_chip().is_ok()); + assert!(vm.create_pit2(kvm_pit_config::default()).is_ok()); let pit2 = vm.get_pit2().unwrap(); From 5e82393ffee1e048e871cc240d88cfefb97383a5 Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Thu, 26 Sep 2024 15:33:50 +0000 Subject: [PATCH 299/332] aarch64: Reorganize `test_create_device` Add `has_device_attr` check in both `set_supported_nr_irqs` and `request_gic_init`, effectively enabling reuse of `request_gic_init` in `test_create_device`. Signed-off-by: Ruoqing He --- src/ioctls/device.rs | 19 ++++--------------- src/ioctls/vm.rs | 2 ++ 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/ioctls/device.rs b/src/ioctls/device.rs index a4a0f49..cb2effa 100644 --- a/src/ioctls/device.rs +++ b/src/ioctls/device.rs @@ -196,10 +196,7 @@ mod tests { KVM_DEV_VFIO_GROUP, KVM_DEV_VFIO_GROUP_ADD, }; #[cfg(target_arch = "aarch64")] - use kvm_bindings::{ - KVM_DEV_ARM_VGIC_CTRL_INIT, KVM_DEV_ARM_VGIC_GRP_CTRL, KVM_DEV_VFIO_GROUP, - KVM_DEV_VFIO_GROUP_ADD, - }; + use kvm_bindings::{KVM_DEV_VFIO_GROUP, KVM_DEV_VFIO_GROUP_ADD}; use kvm_bindings::KVM_CREATE_DEVICE_TEST; @@ -249,7 +246,7 @@ mod tests { #[test] #[cfg(target_arch = "aarch64")] fn test_create_device() { - use crate::ioctls::vm::{create_gic_device, set_supported_nr_irqs}; + use crate::ioctls::vm::{create_gic_device, request_gic_init, set_supported_nr_irqs}; use kvm_bindings::{ kvm_device_type_KVM_DEV_TYPE_FSL_MPIC_20, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, }; @@ -291,16 +288,8 @@ mod tests { // Set maximum supported number of IRQs of the vGIC device to 128. set_supported_nr_irqs(&device_fd, 128); - // Following attribute works with VGIC, they should be accepted. - let dist_attr = kvm_bindings::kvm_device_attr { - group: KVM_DEV_ARM_VGIC_GRP_CTRL, - attr: u64::from(KVM_DEV_ARM_VGIC_CTRL_INIT), - addr: 0x0, - flags: 0, - }; - - assert!(device_fd.has_device_attr(&dist_attr).is_ok()); - assert!(device_fd.set_device_attr(&dist_attr).is_ok()); + // Initialize valid vGIC device. + request_gic_init(&device_fd); // Test `get_device_attr`. Here we try to extract the maximum supported number of IRQs. // This value should be saved in the address provided to the ioctl. diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 1b1364d..1ccbcc8 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -1925,6 +1925,7 @@ pub(crate) fn set_supported_nr_irqs(vgic: &DeviceFd, nr_irqs: u32) { addr: &nr_irqs as *const u32 as u64, flags: 0, }; + assert!(vgic.has_device_attr(&vgic_attr).is_ok()); assert!(vgic.set_device_attr(&vgic_attr).is_ok()); } @@ -1942,6 +1943,7 @@ pub(crate) fn request_gic_init(vgic: &DeviceFd) { addr: 0, flags: 0, }; + assert!(vgic.has_device_attr(&vgic_attr).is_ok()); assert!(vgic.set_device_attr(&vgic_attr).is_ok()); } From a2f55b9b8c8d44b6c07f5c95600679d7a88fd7a5 Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Mon, 30 Sep 2024 11:25:56 +0000 Subject: [PATCH 300/332] chore: Eliminate use of `assert!((...).is_ok())` As @roypat pointed out, and quote: "Asserting on .is_ok()/.is_err() leads to hard to debug failures (as if the test fails, it will only say "assertion failed: false". We replace these with `.unwrap()`, which also prints the exact error variant that was unexpectedly encountered (we can to this these days thanks to efforts to implement Display and Debug for our error types). If the assert!((...).is_ok()) was followed by an .unwrap() anyway, we just drop the assert." Signed-off-by: Ruoqing He --- src/ioctls/device.rs | 14 ++-- src/ioctls/system.rs | 13 ++- src/ioctls/vcpu.rs | 48 +++++------ src/ioctls/vm.rs | 186 +++++++++++++++++++++---------------------- 4 files changed, 130 insertions(+), 131 deletions(-) diff --git a/src/ioctls/device.rs b/src/ioctls/device.rs index cb2effa..e2d2d76 100644 --- a/src/ioctls/device.rs +++ b/src/ioctls/device.rs @@ -212,7 +212,7 @@ mod tests { flags: KVM_CREATE_DEVICE_TEST, }; // This fails on x86_64 because there is no VGIC there. - assert!(vm.create_device(&mut gic_device).is_err()); + vm.create_device(&mut gic_device).unwrap_err(); gic_device.type_ = kvm_device_type_KVM_DEV_TYPE_VFIO; @@ -237,9 +237,9 @@ mod tests { // We are just creating a test device. Creating a real device would make the CI dependent // on host configuration (like having /dev/vfio). We expect this to fail. - assert!(device_fd.has_device_attr(&dist_attr).is_err()); - assert!(unsafe { device_fd.get_device_attr(&mut dist_attr_mut) }.is_err()); - assert!(device_fd.set_device_attr(&dist_attr).is_err()); + device_fd.has_device_attr(&dist_attr).unwrap_err(); + unsafe { device_fd.get_device_attr(&mut dist_attr_mut) }.unwrap_err(); + device_fd.set_device_attr(&dist_attr).unwrap_err(); assert_eq!(errno::Error::last().errno(), 25); } @@ -262,7 +262,7 @@ mod tests { }; // This fails on aarch64 as it does not use MPIC (MultiProcessor Interrupt Controller), // it uses the VGIC. - assert!(vm.create_device(&mut gic_device).is_err()); + vm.create_device(&mut gic_device).unwrap_err(); let device_fd = create_gic_device(&vm, 0); @@ -283,7 +283,7 @@ mod tests { addr: 0x0, flags: 0, }; - assert!(device_fd.has_device_attr(&dist_attr).is_err()); + device_fd.has_device_attr(&dist_attr).unwrap_err(); // Set maximum supported number of IRQs of the vGIC device to 128. set_supported_nr_irqs(&device_fd, 128); @@ -307,7 +307,7 @@ mod tests { assert_eq!(res, Err(Error::new(libc::EFAULT))); gic_attr.addr = &mut data as *mut u32 as u64; - assert!(unsafe { device_fd.get_device_attr(&mut gic_attr) }.is_ok()); + unsafe { device_fd.get_device_attr(&mut gic_attr) }.unwrap(); // The maximum supported number of IRQs should be 128, same as the value // when we initialize the GIC. assert_eq!(data, 128); diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index b10040b..6b1aff0 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -857,14 +857,13 @@ mod tests { kvm.create_vm_with_ipa_size(host_ipa_limit as u32).unwrap(); // Test invalid input values // Case 1: IPA size is smaller than 32. - assert!(kvm.create_vm_with_ipa_size(31).is_err()); + kvm.create_vm_with_ipa_size(31).unwrap_err(); // Case 2: IPA size is bigger than Host_IPA_Limit. - assert!(kvm - .create_vm_with_ipa_size((host_ipa_limit + 1) as u32) - .is_err()); + kvm.create_vm_with_ipa_size((host_ipa_limit + 1) as u32) + .unwrap_err(); } else { // Unsupported, we can't provide an IPA size. Only KVM type=0 works. - assert!(kvm.create_vm_with_type(0).is_err()); + kvm.create_vm_with_type(0).unwrap_err(); } } @@ -879,7 +878,7 @@ mod tests { // Test case for more than MAX entries let cpuid_err = kvm.get_emulated_cpuid(KVM_MAX_CPUID_ENTRIES + 1_usize); - assert!(cpuid_err.is_err()); + cpuid_err.unwrap_err(); } #[test] @@ -893,7 +892,7 @@ mod tests { // Test case for more than MAX entries let cpuid_err = kvm.get_emulated_cpuid(KVM_MAX_CPUID_ENTRIES + 1_usize); - assert!(cpuid_err.is_err()); + cpuid_err.unwrap_err(); } #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 8835f00..7ef9e1a 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -2009,7 +2009,7 @@ mod tests { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); - assert!(vm.create_vcpu(0).is_ok()); + vm.create_vcpu(0).unwrap(); } #[cfg(target_arch = "x86_64")] @@ -2154,7 +2154,7 @@ mod tests { assert!(kvm.check_extension(Cap::Irqchip)); let vm = kvm.create_vm().unwrap(); // The get_lapic ioctl will fail if there is no irqchip created beforehand. - assert!(vm.create_irq_chip().is_ok()); + vm.create_irq_chip().unwrap(); let vcpu = vm.create_vcpu(0).unwrap(); let mut klapic: kvm_lapic_state = vcpu.get_lapic().unwrap(); @@ -2636,7 +2636,7 @@ mod tests { ); // `kvm_lapic_state` does not implement debug by default so we cannot // use unwrap_err here. - assert!(faulty_vcpu_fd.get_lapic().is_err()); + faulty_vcpu_fd.get_lapic().unwrap_err(); assert_eq!( faulty_vcpu_fd .set_lapic(&unsafe { std::mem::zeroed() }) @@ -2693,9 +2693,9 @@ mod tests { faulty_vcpu_fd.kvmclock_ctrl().unwrap_err().errno(), badf_errno ); - assert!(faulty_vcpu_fd.get_tsc_khz().is_err()); - assert!(faulty_vcpu_fd.set_tsc_khz(1000000).is_err()); - assert!(faulty_vcpu_fd.translate_gva(u64::MAX).is_err()); + faulty_vcpu_fd.get_tsc_khz().unwrap_err(); + faulty_vcpu_fd.set_tsc_khz(1000000).unwrap_err(); + faulty_vcpu_fd.translate_gva(u64::MAX).unwrap_err(); // Don't drop the File object, or it'll notice the file it's trying to close is // invalid and abort the process. @@ -2716,7 +2716,7 @@ mod tests { ..Default::default() }; - assert!(vcpu.vcpu_init(&kvi).is_err()); + vcpu.vcpu_init(&kvi).unwrap_err(); } #[test] @@ -2811,7 +2811,7 @@ mod tests { vm.get_preferred_target(&mut kvi) .expect("Cannot get preferred target"); - assert!(vcpu.vcpu_init(&kvi).is_ok()); + vcpu.vcpu_init(&kvi).unwrap(); } #[test] @@ -2828,7 +2828,7 @@ mod tests { let data: u128 = 0; let reg_id: u64 = 0; - assert!(vcpu.set_one_reg(reg_id, &data.to_le_bytes()).is_err()); + vcpu.set_one_reg(reg_id, &data.to_le_bytes()).unwrap_err(); // Exercising KVM_SET_ONE_REG by trying to alter the data inside the PSTATE register (which is a // specific aarch64 register). // This regiseter is 64 bit wide (8 bytes). @@ -2837,7 +2837,7 @@ mod tests { .expect("Failed to set pstate register"); // Trying to set 8 byte register with 7 bytes must fail. - assert!(vcpu.set_one_reg(PSTATE_REG_ID, &[0_u8; 7]).is_err()); + vcpu.set_one_reg(PSTATE_REG_ID, &[0_u8; 7]).unwrap_err(); } #[test] @@ -2873,7 +2873,7 @@ mod tests { assert_eq!(data, PSTATE_FAULT_BITS_64 as u128); // Trying to get 8 byte register with 7 bytes must fail. - assert!(vcpu.get_one_reg(PSTATE_REG_ID, &mut [0_u8; 7]).is_err()); + vcpu.get_one_reg(PSTATE_REG_ID, &mut [0_u8; 7]).unwrap_err(); } #[test] @@ -2905,7 +2905,7 @@ mod tests { // SAFETY: This structure is a result from a specific vCPU ioctl let mut reg_list = RegList::new(unsafe { reg_list.as_mut_fam_struct() }.n as usize).unwrap(); - assert!(vcpu.get_reg_list(&mut reg_list).is_ok()); + vcpu.get_reg_list(&mut reg_list).unwrap() } #[test] @@ -2957,7 +2957,7 @@ mod tests { let vcpu = vm.create_vcpu(0).unwrap(); if !kvm.check_extension(Cap::GetTscKhz) { - assert!(vcpu.get_tsc_khz().is_err()) + vcpu.get_tsc_khz().unwrap_err(); } else { assert!(vcpu.get_tsc_khz().unwrap() > 0); } @@ -2972,11 +2972,11 @@ mod tests { let freq = vcpu.get_tsc_khz().unwrap(); if !(kvm.check_extension(Cap::GetTscKhz) && kvm.check_extension(Cap::TscControl)) { - assert!(vcpu.set_tsc_khz(0).is_err()); + vcpu.set_tsc_khz(0).unwrap_err(); } else { - assert!(vcpu.set_tsc_khz(freq - 500000).is_ok()); + vcpu.set_tsc_khz(freq - 500000).unwrap(); assert_eq!(vcpu.get_tsc_khz().unwrap(), freq - 500000); - assert!(vcpu.set_tsc_khz(freq + 500000).is_ok()); + vcpu.set_tsc_khz(freq + 500000).unwrap(); assert_eq!(vcpu.get_tsc_khz().unwrap(), freq + 500000); } } @@ -3112,13 +3112,13 @@ mod tests { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); let vcpu = vm.create_vcpu(0).unwrap(); - assert!(vcpu.translate_gva(0x10000).is_ok()); + vcpu.translate_gva(0x10000).unwrap(); assert_eq!(vcpu.translate_gva(0x10000).unwrap().valid, 1); assert_eq!( vcpu.translate_gva(0x10000).unwrap().physical_address, 0x10000 ); - assert!(vcpu.translate_gva(u64::MAX).is_ok()); + vcpu.translate_gva(u64::MAX).unwrap(); assert_eq!(vcpu.translate_gva(u64::MAX).unwrap().valid, 0); } @@ -3136,15 +3136,15 @@ mod tests { flags: 0, }; - assert!(vcpu.has_device_attr(&dist_attr).is_err()); - assert!(vcpu.set_device_attr(&dist_attr).is_err()); + vcpu.has_device_attr(&dist_attr).unwrap_err(); + vcpu.set_device_attr(&dist_attr).unwrap_err(); let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default(); vm.get_preferred_target(&mut kvi) .expect("Cannot get preferred target"); kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PSCI_0_2 | 1 << KVM_ARM_VCPU_PMU_V3; - assert!(vcpu.vcpu_init(&kvi).is_ok()); - assert!(vcpu.has_device_attr(&dist_attr).is_ok()); - assert!(vcpu.set_device_attr(&dist_attr).is_ok()); + vcpu.vcpu_init(&kvi).unwrap(); + vcpu.has_device_attr(&dist_attr).unwrap(); + vcpu.set_device_attr(&dist_attr).unwrap(); } #[test] @@ -3163,7 +3163,7 @@ mod tests { if kvm.check_extension(Cap::ArmPtrAuthGeneric) { kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PTRAUTH_GENERIC; } - assert!(vcpu.vcpu_init(&kvi).is_ok()); + vcpu.vcpu_init(&kvi).unwrap(); } #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 1ccbcc8..2d488d9 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -1643,7 +1643,7 @@ impl VmFd { /// let vm = kvm.create_vm().unwrap(); /// /// // Check whether SEV is enabled, optional. - /// assert!(unsafe { vm.encrypt_op(null_mut() as *mut c_void) }.is_ok()); + /// unsafe { vm.encrypt_op(null_mut() as *mut c_void) }.unwrap(); /// /// // Initialize the SEV platform context. /// let mut init: kvm_sev_cmd = Default::default(); @@ -1689,7 +1689,7 @@ impl VmFd { /// /// // Initialize the SEV platform context. /// let mut init: kvm_sev_cmd = Default::default(); - /// assert!(vm.encrypt_op_sev(&mut init).is_ok()); + /// vm.encrypt_op_sev(&mut init).unwrap(); /// /// // Create the memory encryption context. /// let start_data: kvm_sev_launch_start = Default::default(); @@ -1699,7 +1699,7 @@ impl VmFd { /// sev_fd: sev.as_raw_fd() as _, /// ..Default::default() /// }; - /// assert!(vm.encrypt_op_sev(&mut start).is_ok()); + /// vm.encrypt_op_sev(&mut start).unwrap(); /// /// let addr = unsafe { /// libc::mmap( @@ -1765,7 +1765,7 @@ impl VmFd { /// /// // Initialize the SEV platform context. /// let mut init: kvm_sev_cmd = Default::default(); - /// assert!(vm.encrypt_op_sev(&mut init).is_ok()); + /// vm.encrypt_op_sev(&mut init).unwrap(); /// /// // Create the memory encryption context. /// let start_data: kvm_sev_launch_start = Default::default(); @@ -1775,7 +1775,7 @@ impl VmFd { /// sev_fd: sev.as_raw_fd() as _, /// ..Default::default() /// }; - /// assert!(vm.encrypt_op_sev(&mut start).is_ok()); + /// vm.encrypt_op_sev(&mut start).unwrap(); /// /// let addr = unsafe { /// libc::mmap( @@ -1925,8 +1925,8 @@ pub(crate) fn set_supported_nr_irqs(vgic: &DeviceFd, nr_irqs: u32) { addr: &nr_irqs as *const u32 as u64, flags: 0, }; - assert!(vgic.has_device_attr(&vgic_attr).is_ok()); - assert!(vgic.set_device_attr(&vgic_attr).is_ok()); + vgic.has_device_attr(&vgic_attr).unwrap(); + vgic.set_device_attr(&vgic_attr).unwrap(); } /// Request the initialization of the vGIC. @@ -1943,8 +1943,8 @@ pub(crate) fn request_gic_init(vgic: &DeviceFd) { addr: 0, flags: 0, }; - assert!(vgic.has_device_attr(&vgic_attr).is_ok()); - assert!(vgic.set_device_attr(&vgic_attr).is_ok()); + vgic.has_device_attr(&vgic_attr).unwrap(); + vgic.set_device_attr(&vgic_attr).unwrap(); } #[cfg(test)] @@ -1969,7 +1969,7 @@ mod tests { userspace_addr: 0, flags: 0, }; - assert!(unsafe { vm.set_user_memory_region(invalid_mem_region) }.is_err()); + unsafe { vm.set_user_memory_region(invalid_mem_region) }.unwrap_err(); } #[test] @@ -1987,7 +1987,7 @@ mod tests { pad1: 0, pad2: [0; 14], }; - assert!(unsafe { vm.set_user_memory_region2(invalid_mem_region) }.is_err()); + unsafe { vm.set_user_memory_region2(invalid_mem_region) }.unwrap_err(); } #[test] @@ -1995,7 +1995,7 @@ mod tests { fn test_set_tss_address() { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); - assert!(vm.set_tss_address(0xfffb_d000).is_ok()); + vm.set_tss_address(0xfffb_d000).unwrap(); } #[test] @@ -2004,10 +2004,10 @@ mod tests { let kvm = Kvm::new().unwrap(); if kvm.check_extension(Cap::SetIdentityMapAddr) { let vm = kvm.create_vm().unwrap(); - assert!(vm.set_identity_map_address(0xfffb_c000).is_ok()); + vm.set_identity_map_address(0xfffb_c000).unwrap(); vm.create_vcpu(0).unwrap(); // Setting the identity map after creating a vCPU must fail. - assert!(vm.set_identity_map_address(0xfffb_c000).is_err()); + vm.set_identity_map_address(0xfffb_c000).unwrap_err(); } } @@ -2019,7 +2019,7 @@ mod tests { let kvm = Kvm::new().unwrap(); assert!(kvm.check_extension(Cap::Irqchip)); let vm = kvm.create_vm().unwrap(); - assert!(vm.create_irq_chip().is_ok()); + vm.create_irq_chip().unwrap(); let mut irqchip = kvm_irqchip { chip_id: KVM_IRQCHIP_PIC_MASTER, @@ -2027,7 +2027,7 @@ mod tests { }; // Set the irq_base to a non-default value to check that set & get work. irqchip.chip.pic.irq_base = 10; - assert!(vm.set_irqchip(&irqchip).is_ok()); + vm.set_irqchip(&irqchip).unwrap(); // We initialize a dummy irq chip (`other_irqchip`) in which the // function `get_irqchip` returns its result. @@ -2035,7 +2035,7 @@ mod tests { chip_id: KVM_IRQCHIP_PIC_MASTER, ..Default::default() }; - assert!(vm.get_irqchip(&mut other_irqchip).is_ok()); + vm.get_irqchip(&mut other_irqchip).unwrap(); // Safe because we know that the irqchip type is PIC. unsafe { assert_eq!(irqchip.chip.pic, other_irqchip.chip.pic) }; @@ -2069,9 +2069,9 @@ mod tests { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); assert!(kvm.check_extension(Cap::Irqchip)); - assert!(vm.create_irq_chip().is_ok()); + vm.create_irq_chip().unwrap(); - assert!(vm.create_pit2(kvm_pit_config::default()).is_ok()); + vm.create_pit2(kvm_pit_config::default()).unwrap(); let pit2 = vm.get_pit2().unwrap(); vm.set_pit2(&pit2).unwrap(); @@ -2114,24 +2114,24 @@ mod tests { let kvm = Kvm::new().unwrap(); let vm_fd = kvm.create_vm().unwrap(); let evtfd = EventFd::new(EFD_NONBLOCK).unwrap(); - assert!(vm_fd + vm_fd .register_ioevent(&evtfd, &IoEventAddress::Pio(0xf4), NoDatamatch) - .is_ok()); - assert!(vm_fd + .unwrap(); + vm_fd .register_ioevent(&evtfd, &IoEventAddress::Mmio(0x1000), NoDatamatch) - .is_ok()); - assert!(vm_fd + .unwrap(); + vm_fd .register_ioevent(&evtfd, &IoEventAddress::Pio(0xc1), 0x7fu8) - .is_ok()); - assert!(vm_fd + .unwrap(); + vm_fd .register_ioevent(&evtfd, &IoEventAddress::Pio(0xc2), 0x1337u16) - .is_ok()); - assert!(vm_fd + .unwrap(); + vm_fd .register_ioevent(&evtfd, &IoEventAddress::Pio(0xc4), 0xdead_beefu32) - .is_ok()); - assert!(vm_fd + .unwrap(); + vm_fd .register_ioevent(&evtfd, &IoEventAddress::Pio(0xc8), 0xdead_beef_dead_beefu64) - .is_ok()); + .unwrap() } #[test] @@ -2145,29 +2145,29 @@ mod tests { let mmio_addr = IoEventAddress::Mmio(0x1000); // First try to unregister addresses which have not been registered. - assert!(vm_fd + vm_fd .unregister_ioevent(&evtfd, &pio_addr, NoDatamatch) - .is_err()); - assert!(vm_fd + .unwrap_err(); + vm_fd .unregister_ioevent(&evtfd, &mmio_addr, NoDatamatch) - .is_err()); + .unwrap_err(); // Now register the addresses - assert!(vm_fd + vm_fd .register_ioevent(&evtfd, &pio_addr, NoDatamatch) - .is_ok()); - assert!(vm_fd + .unwrap(); + vm_fd .register_ioevent(&evtfd, &mmio_addr, 0x1337u16) - .is_ok()); + .unwrap(); // Try again unregistering the addresses. This time it should work // since they have been previously registered. - assert!(vm_fd + vm_fd .unregister_ioevent(&evtfd, &pio_addr, NoDatamatch) - .is_ok()); - assert!(vm_fd + .unwrap(); + vm_fd .unregister_ioevent(&evtfd, &mmio_addr, 0x1337u16) - .is_ok()); + .unwrap(); } #[test] @@ -2181,31 +2181,31 @@ mod tests { let evtfd4 = EventFd::new(EFD_NONBLOCK).unwrap(); let resamplefd = EventFd::new(EFD_NONBLOCK).unwrap(); - assert!(vm_fd.create_irq_chip().is_ok()); + vm_fd.create_irq_chip().unwrap(); - assert!(vm_fd.register_irqfd(&evtfd1, 4).is_ok()); - assert!(vm_fd.register_irqfd(&evtfd2, 8).is_ok()); - assert!(vm_fd.register_irqfd(&evtfd3, 4).is_ok()); - assert!(vm_fd.unregister_irqfd(&evtfd2, 8).is_ok()); + vm_fd.register_irqfd(&evtfd1, 4).unwrap(); + vm_fd.register_irqfd(&evtfd2, 8).unwrap(); + vm_fd.register_irqfd(&evtfd3, 4).unwrap(); + vm_fd.unregister_irqfd(&evtfd2, 8).unwrap(); // KVM irqfd doesn't report failure on this case:( - assert!(vm_fd.unregister_irqfd(&evtfd2, 8).is_ok()); + vm_fd.unregister_irqfd(&evtfd2, 8).unwrap(); // Duplicated eventfd registration. // On x86_64 this fails as the event fd was already matched with a GSI. - assert!(vm_fd.register_irqfd(&evtfd3, 4).is_err()); - assert!(vm_fd.register_irqfd(&evtfd3, 5).is_err()); + vm_fd.register_irqfd(&evtfd3, 4).unwrap_err(); + vm_fd.register_irqfd(&evtfd3, 5).unwrap_err(); // KVM irqfd doesn't report failure on this case:( - assert!(vm_fd.unregister_irqfd(&evtfd3, 5).is_ok()); + vm_fd.unregister_irqfd(&evtfd3, 5).unwrap(); if vm_fd.check_extension(Cap::IrqfdResample) { - assert!(vm_fd + vm_fd .register_irqfd_with_resample(&evtfd4, &resamplefd, 6) - .is_ok()); - assert!(vm_fd.unregister_irqfd(&evtfd4, 6).is_ok()); + .unwrap(); + vm_fd.unregister_irqfd(&evtfd4, 6).unwrap(); } else { - assert!(vm_fd + vm_fd .register_irqfd_with_resample(&evtfd4, &resamplefd, 6) - .is_err()); + .unwrap_err(); } } @@ -2232,30 +2232,30 @@ mod tests { // Request the initialization of the vGIC. request_gic_init(&vgic_fd); - assert!(vm_fd.register_irqfd(&evtfd1, 4).is_ok()); - assert!(vm_fd.register_irqfd(&evtfd2, 8).is_ok()); - assert!(vm_fd.register_irqfd(&evtfd3, 4).is_ok()); - assert!(vm_fd.unregister_irqfd(&evtfd2, 8).is_ok()); + vm_fd.register_irqfd(&evtfd1, 4).unwrap(); + vm_fd.register_irqfd(&evtfd2, 8).unwrap(); + vm_fd.register_irqfd(&evtfd3, 4).unwrap(); + vm_fd.unregister_irqfd(&evtfd2, 8).unwrap(); // KVM irqfd doesn't report failure on this case:( - assert!(vm_fd.unregister_irqfd(&evtfd2, 8).is_ok()); + vm_fd.unregister_irqfd(&evtfd2, 8).unwrap(); // Duplicated eventfd registration. // On aarch64, this fails because setting up the interrupt controller is mandatory before // registering any IRQ. - assert!(vm_fd.register_irqfd(&evtfd3, 4).is_err()); - assert!(vm_fd.register_irqfd(&evtfd3, 5).is_err()); + vm_fd.register_irqfd(&evtfd3, 4).unwrap_err(); + vm_fd.register_irqfd(&evtfd3, 5).unwrap_err(); // KVM irqfd doesn't report failure on this case:( - assert!(vm_fd.unregister_irqfd(&evtfd3, 5).is_ok()); + vm_fd.unregister_irqfd(&evtfd3, 5).unwrap(); if vm_fd.check_extension(Cap::IrqfdResample) { - assert!(vm_fd + vm_fd .register_irqfd_with_resample(&evtfd4, &resamplefd, 6) - .is_ok()); - assert!(vm_fd.unregister_irqfd(&evtfd4, 6).is_ok()); + .unwrap(); + vm_fd.unregister_irqfd(&evtfd4, 6).unwrap(); } else { - assert!(vm_fd + vm_fd .register_irqfd_with_resample(&evtfd4, &resamplefd, 6) - .is_err()); + .unwrap_err(); } } @@ -2265,11 +2265,11 @@ mod tests { let kvm = Kvm::new().unwrap(); let vm_fd = kvm.create_vm().unwrap(); - assert!(vm_fd.create_irq_chip().is_ok()); + vm_fd.create_irq_chip().unwrap(); - assert!(vm_fd.set_irq_line(4, true).is_ok()); - assert!(vm_fd.set_irq_line(4, false).is_ok()); - assert!(vm_fd.set_irq_line(4, true).is_ok()); + vm_fd.set_irq_line(4, true).unwrap(); + vm_fd.set_irq_line(4, false).unwrap(); + vm_fd.set_irq_line(4, true).unwrap(); } #[test] @@ -2296,14 +2296,14 @@ mod tests { // - irq_type[1]: in-kernel GIC: SPI, irq_id between 32 and 1019 (incl.) (the vcpu_index field is ignored) // - irq_type[2]: in-kernel GIC: PPI, irq_id between 16 and 31 (incl.) // Hence, using irq_type = 1, irq_id = 32 (decimal), the irq field in hex is: 0x01_00_0020 - assert!(vm_fd.set_irq_line(0x01_00_0020, true).is_ok()); - assert!(vm_fd.set_irq_line(0x01_00_0020, false).is_ok()); - assert!(vm_fd.set_irq_line(0x01_00_0020, true).is_ok()); + vm_fd.set_irq_line(0x01_00_0020, true).unwrap(); + vm_fd.set_irq_line(0x01_00_0020, false).unwrap(); + vm_fd.set_irq_line(0x01_00_0020, true).unwrap(); // Case 2: using irq_type = 2, vcpu_index = 0, irq_id = 16 (decimal), the irq field in hex is: 0x02_00_0010 - assert!(vm_fd.set_irq_line(0x02_00_0010, true).is_ok()); - assert!(vm_fd.set_irq_line(0x02_00_0010, false).is_ok()); - assert!(vm_fd.set_irq_line(0x02_00_0010, true).is_ok()); + vm_fd.set_irq_line(0x02_00_0010, true).unwrap(); + vm_fd.set_irq_line(0x02_00_0010, false).unwrap(); + vm_fd.set_irq_line(0x02_00_0010, true).unwrap(); } #[test] @@ -2415,7 +2415,7 @@ mod tests { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default(); - assert!(vm.get_preferred_target(&mut kvi).is_ok()); + vm.get_preferred_target(&mut kvi).unwrap(); } /// As explained in the example code related to signal_msi(), sending @@ -2432,7 +2432,7 @@ mod tests { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); let msi = kvm_msi::default(); - assert!(vm.signal_msi(msi).is_err()); + vm.signal_msi(msi).unwrap_err(); } #[test] @@ -2443,7 +2443,7 @@ mod tests { let cap: kvm_enable_cap = Default::default(); // Providing the `kvm_enable_cap` structure filled with default() should // always result in a failure as it is not a valid capability. - assert!(vm.enable_cap(&cap).is_err()); + vm.enable_cap(&cap).unwrap_err(); } #[test] @@ -2467,7 +2467,7 @@ mod tests { // Because an IOAPIC supports 24 pins, that's the reason why this test // picked this number as reference. cap.args[0] = 24; - assert!(vm.enable_cap(&cap).is_ok()); + vm.enable_cap(&cap).unwrap(); } #[test] @@ -2483,11 +2483,11 @@ mod tests { if cfg!(target_arch = "x86") || cfg!(target_arch = "x86_64") { let irq_routing = kvm_irq_routing::default(); // Expect failure for x86 since the irqchip is not created yet. - assert!(vm.set_gsi_routing(&irq_routing).is_err()); + vm.set_gsi_routing(&irq_routing).unwrap_err(); vm.create_irq_chip().unwrap(); } let irq_routing = kvm_irq_routing::default(); - assert!(vm.set_gsi_routing(&irq_routing).is_ok()); + vm.set_gsi_routing(&irq_routing).unwrap(); } #[test] @@ -2501,8 +2501,8 @@ mod tests { // Fails when input `id` = `max_vcpu_id` let max_vcpu_id = kvm.get_max_vcpu_id(); - let vcpu = vm.create_vcpu((max_vcpu_id - 1) as u64); - assert!(vcpu.is_ok()); + vm.create_vcpu((max_vcpu_id - 1) as u64).unwrap(); + let vcpu_err = vm.create_vcpu(max_vcpu_id as u64).err(); assert_eq!(vcpu_err.unwrap().errno(), libc::EINVAL); } @@ -2522,7 +2522,7 @@ mod tests { let vm = kvm.create_vm().unwrap(); let mut init: kvm_sev_cmd = Default::default(); - assert!(vm.encrypt_op_sev(&mut init).is_ok()); + vm.encrypt_op_sev(&mut init).unwrap(); } #[test] @@ -2542,7 +2542,7 @@ mod tests { // https://www.kernel.org/doc/Documentation/virtual/kvm/amd-memory-encryption.rst let mut init: kvm_sev_cmd = Default::default(); - assert!(vm.encrypt_op_sev(&mut init).is_ok()); + vm.encrypt_op_sev(&mut init).unwrap(); let start_data: kvm_sev_launch_start = Default::default(); let mut start = kvm_sev_cmd { @@ -2551,7 +2551,7 @@ mod tests { sev_fd: sev.as_raw_fd() as _, ..Default::default() }; - assert!(vm.encrypt_op_sev(&mut start).is_ok()); + vm.encrypt_op_sev(&mut start).unwrap(); let addr = unsafe { libc::mmap( @@ -2588,7 +2588,7 @@ mod tests { .errno(), libc::EINVAL ); - assert!(vm.register_enc_memory_region(&memory_region).is_ok()); - assert!(vm.unregister_enc_memory_region(&memory_region).is_ok()); + vm.register_enc_memory_region(&memory_region).unwrap(); + vm.unregister_enc_memory_region(&memory_region).unwrap(); } } From 488a2d3426a7be6db67de3dd52e2bc1e1e1169cb Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Mon, 30 Sep 2024 11:55:42 +0000 Subject: [PATCH 301/332] chore: Enable clippy::assertions_on_result_states As @roypat pointed out, and quote: "This lint disallows asserttions on is_ok()/is_err() in favor of either using unwrap (so that at least if the test fails, we the failure message will contain the actual failure reason instead of just "was not ok/err"), or actually matching the specific variant." Signed-off-by: Ruoqing He --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index c1d1ec5..333c508 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ #![deny(missing_docs)] #![deny(missing_copy_implementations)] #![deny(missing_debug_implementations)] +#![warn(clippy::assertions_on_result_states)] //! A safe wrapper around the kernel's KVM interface. //! From d1b3eb40c7c4d6cb6c9146da7a82b7de318c8b11 Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Tue, 1 Oct 2024 12:05:24 +0000 Subject: [PATCH 302/332] Bumps rust-vmm-ci from `5e818dc` to `007a406`. Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `5e818dc` to `007a406`. - [Commits](rust-vmm/rust-vmm-ci@5e818dc...007a406) Signed-off-by: Ruoqing He --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 5e818dc..007a406 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 5e818dc729574693731b4567c6a3bc07788b53b0 +Subproject commit 007a40657de4cfca8d08af125d2f43f5cfe7bfc5 From 1001cceae6328214aa2ade5cd5f450a58beb956f Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Tue, 1 Oct 2024 10:50:18 +0000 Subject: [PATCH 303/332] chore: Fix clippy Fix as `doc_lazy_continuation`, `manual_c_str_literals` and `unused_must_use` clippy lints. Signed-off-by: Ruoqing He --- src/ioctls/system.rs | 6 +++--- src/ioctls/vcpu.rs | 7 +++---- src/ioctls/vm.rs | 5 ++--- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index 6b1aff0..1660a8e 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -96,7 +96,7 @@ impl Kvm { /// ``` pub fn open_with_cloexec(close_on_exec: bool) -> Result { // SAFETY: Safe because we give a constant nul-terminated string. - let kvm_path = unsafe { CStr::from_bytes_with_nul_unchecked(b"/dev/kvm\0") }; + let kvm_path = c"/dev/kvm"; Self::open_with_cloexec_at(kvm_path, close_on_exec) } @@ -741,7 +741,7 @@ mod tests { #[test] fn test_kvm_new_with_path() { - let kvm_path = unsafe { CStr::from_bytes_with_nul_unchecked(b"/dev/kvm\0") }; + let kvm_path = c"/dev/kvm"; Kvm::new_with_path(kvm_path).unwrap(); } @@ -981,6 +981,6 @@ mod tests { // Don't drop the File object, or it'll notice the file it's trying to close is // invalid and abort the process. - faulty_kvm.kvm.into_raw_fd(); + let _ = faulty_kvm.kvm.into_raw_fd(); } } diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 7ef9e1a..15246ca 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1088,7 +1088,6 @@ impl VcpuFd { target_arch = "arm", target_arch = "aarch64" ))] - pub fn set_vcpu_events(&self, vcpu_events: &kvm_vcpu_events) -> Result<()> { // SAFETY: Here we trust the kernel not to read past the end of the kvm_vcpu_events struct. let ret = unsafe { ioctl_with_ref(self, KVM_SET_VCPU_EVENTS(), vcpu_events) }; @@ -1639,7 +1638,7 @@ impl VcpuFd { /// # Arguments /// /// * `freq` - The frequency unit is KHz as per the KVM API documentation - /// for `KVM_SET_TSC_KHZ`. + /// for `KVM_SET_TSC_KHZ`. /// /// # Example /// @@ -2575,7 +2574,7 @@ mod tests { // Don't drop the File object, or it'll notice the file it's trying to close is // invalid and abort the process. - faulty_vcpu_fd.vcpu.into_raw_fd(); + let _ = faulty_vcpu_fd.vcpu.into_raw_fd(); } #[test] @@ -2699,7 +2698,7 @@ mod tests { // Don't drop the File object, or it'll notice the file it's trying to close is // invalid and abort the process. - faulty_vcpu_fd.vcpu.into_raw_fd(); + let _ = faulty_vcpu_fd.vcpu.into_raw_fd(); } #[test] diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 2d488d9..a166816 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -913,8 +913,7 @@ impl VmFd { // For ease of access we are saving the bitmap in a u64 vector. We are using ceil to // make sure we count all dirty pages even when `memory_size` is not a multiple of // `page_size * 64`. - let div_ceil = |dividend, divisor| (dividend + divisor - 1) / divisor; - let bitmap_size = div_ceil(memory_size, page_size * 64); + let bitmap_size = memory_size.div_ceil(page_size * 64); let mut bitmap = vec![0u64; bitmap_size]; let dirtylog = kvm_dirty_log { slot, @@ -2406,7 +2405,7 @@ mod tests { // Don't drop the File object, or it'll notice the file it's trying to close is // invalid and abort the process. - faulty_vm_fd.vm.into_raw_fd(); + let _ = faulty_vm_fd.vm.into_raw_fd(); } #[test] From 6ba88a4e89581599cff820c499381fbe1a04798b Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Tue, 1 Oct 2024 12:13:12 +0000 Subject: [PATCH 304/332] chore: Update coverage Signed-off-by: Ruoqing He --- coverage_config_x86_64.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index 903a58b..08c0327 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 87.32, + "coverage_score": 92.61, "exclude_path": "", "crate_features": "" } From e75abe5f7b0293cc2318012a941d5884cd6abd42 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 11:12:18 +0000 Subject: [PATCH 305/332] Bump rust-vmm-ci from `007a406` to `cdb4a2d` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `007a406` to `cdb4a2d`. - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/007a40657de4cfca8d08af125d2f43f5cfe7bfc5...cdb4a2d5cd207846b51407c96a01a175efc0b274) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 007a406..cdb4a2d 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 007a40657de4cfca8d08af125d2f43f5cfe7bfc5 +Subproject commit cdb4a2d5cd207846b51407c96a01a175efc0b274 From 175340a69776a86ca7ae88f9a46421f1c05212fb Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Wed, 25 Sep 2024 02:17:04 +0000 Subject: [PATCH 306/332] config: Remove rule to cross compile riscv64gc The `riscv64gc` target is to be built on "native" RISC-V platform, removed `linker` specification used on other platform. Signed-off-by: Ruoqing He --- .cargo/config.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index d23b917..f4a0713 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,5 +1,2 @@ [target.aarch64-unknown-linux-musl] rustflags = [ "-C", "target-feature=+crt-static", "-C", "link-arg=-lgcc" ] - -[target.riscv64gc-unknown-linux-gnu] -linker = "riscv64-unknown-linux-gnu-gcc" From c2df5f6304050119817e6cb24cc258bf53897dfa Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Wed, 25 Sep 2024 03:22:27 +0000 Subject: [PATCH 307/332] riscv64: Introduce basic ioctls Enable g/set ioctls of `kvm_mp_state`, `kvm_one_reg`; register/unregister of `irq_fd`; `get_reg_list`, `signal_msi`, `irq_line` and `set_gsi_routing`. Signed-off-by: Ruoqing He --- src/ioctls/vcpu.rs | 10 ++++++---- src/ioctls/vm.rs | 18 ++++++++++++------ src/kvm_ioctls.rs | 18 ++++++++++++------ src/lib.rs | 2 +- 4 files changed, 31 insertions(+), 17 deletions(-) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 15246ca..d53972d 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -18,7 +18,7 @@ use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ref}; use vmm_sys_util::ioctl::{ioctl_with_mut_ptr, ioctl_with_ptr, ioctl_with_val}; /// Helper method to obtain the size of the register through its id -#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] +#[cfg(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64"))] pub fn reg_size(reg_id: u64) -> usize { 2_usize.pow(((reg_id & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT) as u32) } @@ -789,6 +789,7 @@ impl VcpuFd { target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", + target_arch = "riscv64", target_arch = "s390x" ))] pub fn get_mp_state(&self) -> Result { @@ -827,6 +828,7 @@ impl VcpuFd { target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", + target_arch = "riscv64", target_arch = "s390x" ))] pub fn set_mp_state(&self, mp_state: kvm_mp_state) -> Result<()> { @@ -1219,7 +1221,7 @@ impl VcpuFd { /// vcpu.get_reg_list(&mut reg_list).unwrap(); /// assert!(reg_list.as_fam_struct_ref().n > 0); /// ``` - #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + #[cfg(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64"))] pub fn get_reg_list(&self, reg_list: &mut RegList) -> Result<()> { let ret = // SAFETY: This is safe because we allocated the struct and we trust the kernel will read @@ -1297,7 +1299,7 @@ impl VcpuFd { /// /// `data` should be equal or bigger then the register size /// oterwise function will return EINVAL error - #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + #[cfg(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64"))] pub fn set_one_reg(&self, reg_id: u64, data: &[u8]) -> Result { let reg_size = reg_size(reg_id); if data.len() < reg_size { @@ -1329,7 +1331,7 @@ impl VcpuFd { /// /// `data` should be equal or bigger then the register size /// oterwise function will return EINVAL error - #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + #[cfg(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64"))] pub fn get_one_reg(&self, reg_id: u64, data: &mut [u8]) -> Result { let reg_size = reg_size(reg_id); if data.len() < reg_size { diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index a166816..e4ede37 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -581,7 +581,8 @@ impl VmFd { target_arch = "x86", target_arch = "x86_64", target_arch = "arm", - target_arch = "aarch64" + target_arch = "aarch64", + target_arch = "riscv64" ))] pub fn signal_msi(&self, msi: kvm_msi) -> Result { // SAFETY: Safe because we allocated the structure and we know the kernel @@ -628,7 +629,8 @@ impl VmFd { target_arch = "x86", target_arch = "x86_64", target_arch = "arm", - target_arch = "aarch64" + target_arch = "aarch64", + target_arch = "riscv64" ))] pub fn set_gsi_routing(&self, irq_routing: &kvm_irq_routing) -> Result<()> { // SAFETY: Safe because we allocated the structure and we know the kernel @@ -961,7 +963,8 @@ impl VmFd { target_arch = "x86", target_arch = "x86_64", target_arch = "arm", - target_arch = "aarch64" + target_arch = "aarch64", + target_arch = "riscv64" ))] pub fn register_irqfd(&self, fd: &EventFd, gsi: u32) -> Result<()> { let irqfd = kvm_irqfd { @@ -1013,7 +1016,8 @@ impl VmFd { target_arch = "x86", target_arch = "x86_64", target_arch = "arm", - target_arch = "aarch64" + target_arch = "aarch64", + target_arch = "riscv64" ))] pub fn register_irqfd_with_resample( &self, @@ -1072,7 +1076,8 @@ impl VmFd { target_arch = "x86", target_arch = "x86_64", target_arch = "arm", - target_arch = "aarch64" + target_arch = "aarch64", + target_arch = "riscv64" ))] pub fn unregister_irqfd(&self, fd: &EventFd, gsi: u32) -> Result<()> { let irqfd = kvm_irqfd { @@ -1143,7 +1148,8 @@ impl VmFd { target_arch = "x86", target_arch = "x86_64", target_arch = "arm", - target_arch = "aarch64" + target_arch = "aarch64", + target_arch = "riscv64" ))] pub fn set_irq_line(&self, irq: u32, active: bool) -> Result<()> { let mut irq_level = kvm_irq_level::default(); diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index 60b294d..aa69624 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -66,7 +66,8 @@ ioctl_io_nr!(KVM_CREATE_IRQCHIP, KVMIO, 0x60); target_arch = "x86", target_arch = "x86_64", target_arch = "arm", - target_arch = "aarch64" + target_arch = "aarch64", + target_arch = "riscv64" ))] ioctl_iow_nr!(KVM_IRQ_LINE, KVMIO, 0x61, kvm_irq_level); /* Available with KVM_CAP_COALESCED_MMIO / KVM_CAP_COALESCED_PIO */ @@ -88,7 +89,8 @@ ioctl_iow_nr!( target_arch = "x86", target_arch = "x86_64", target_arch = "arm", - target_arch = "aarch64" + target_arch = "aarch64", + target_arch = "riscv64" ))] ioctl_iow_nr!(KVM_SET_GSI_ROUTING, KVMIO, 0x6a, kvm_irq_routing); /* Available with KVM_CAP_IRQFD */ @@ -97,6 +99,7 @@ ioctl_iow_nr!(KVM_SET_GSI_ROUTING, KVMIO, 0x6a, kvm_irq_routing); target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", + target_arch = "riscv64", target_arch = "s390x" ))] ioctl_iow_nr!(KVM_IRQFD, KVMIO, 0x76, kvm_irqfd); @@ -182,6 +185,7 @@ ioctl_iowr_nr!(KVM_GET_CPUID2, KVMIO, 0x91, kvm_cpuid2); target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", + target_arch = "riscv64", target_arch = "s390x" ))] ioctl_ior_nr!(KVM_GET_MP_STATE, KVMIO, 0x98, kvm_mp_state); @@ -191,6 +195,7 @@ ioctl_ior_nr!(KVM_GET_MP_STATE, KVMIO, 0x98, kvm_mp_state); target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", + target_arch = "riscv64", target_arch = "s390x" ))] ioctl_iow_nr!(KVM_SET_MP_STATE, KVMIO, 0x99, kvm_mp_state); @@ -250,19 +255,20 @@ ioctl_iow_nr!(KVM_ENABLE_CAP, KVMIO, 0xa3, kvm_enable_cap); target_arch = "x86", target_arch = "x86_64", target_arch = "arm", - target_arch = "aarch64" + target_arch = "aarch64", + target_arch = "riscv64" ))] ioctl_iow_nr!(KVM_SIGNAL_MSI, KVMIO, 0xa5, kvm_msi); /* Available with KVM_CAP_ONE_REG */ -#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] +#[cfg(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64"))] ioctl_iow_nr!(KVM_GET_ONE_REG, KVMIO, 0xab, kvm_one_reg); -#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] +#[cfg(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64"))] ioctl_iow_nr!(KVM_SET_ONE_REG, KVMIO, 0xac, kvm_one_reg); #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] ioctl_iow_nr!(KVM_ARM_VCPU_INIT, KVMIO, 0xae, kvm_vcpu_init); #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] ioctl_ior_nr!(KVM_ARM_PREFERRED_TARGET, KVMIO, 0xaf, kvm_vcpu_init); -#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] +#[cfg(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64"))] ioctl_iowr_nr!(KVM_GET_REG_LIST, KVMIO, 0xb0, kvm_reg_list); /* Available with KVM_CAP_X86_SMM */ diff --git a/src/lib.rs b/src/lib.rs index 333c508..0d01467 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -220,7 +220,7 @@ mod ioctls; pub use cap::Cap; pub use ioctls::device::DeviceFd; pub use ioctls::system::Kvm; -#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] +#[cfg(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64"))] pub use ioctls::vcpu::reg_size; pub use ioctls::vcpu::{HypercallExit, VcpuExit, VcpuFd}; From ab7892b1561a400ab7b15576dc3ea2db9bee3649 Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Thu, 26 Sep 2024 11:11:46 +0000 Subject: [PATCH 308/332] riscv64: Disable `KVM_ENABLE_CAP` & `KVM_G/SET_REGS` Disable `KVM_ENABLE_CAP`, `KVM_GET_REGS` and `KVM_SET_REGS`, and related ioctls, tests, doc tests on RISC-V 64-bit platform. Signed-off-by: Ruoqing He --- src/ioctls/vcpu.rs | 4 ++-- src/ioctls/vm.rs | 4 ++-- src/kvm_ioctls.rs | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index d53972d..7af17de 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -222,7 +222,7 @@ impl VcpuFd { /// let vcpu = vm.create_vcpu(0).unwrap(); /// let regs = vcpu.get_regs().unwrap(); /// ``` - #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] + #[cfg(not(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64")))] pub fn get_regs(&self) -> Result { let mut regs = kvm_regs::default(); // SAFETY: Safe because we know that our file is a vCPU fd, we know the kernel will only @@ -338,7 +338,7 @@ impl VcpuFd { /// regs.rip = 0x100; /// vcpu.set_regs(®s).unwrap(); /// ``` - #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] + #[cfg(not(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64")))] pub fn set_regs(&self, regs: &kvm_regs) -> Result<()> { // SAFETY: Safe because we know that our file is a vCPU fd, we know the kernel will only // read the correct amount of memory from our pointer, and we verify the return result. diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index e4ede37..3650be1 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -1376,7 +1376,7 @@ impl VmFd { /// vm.enable_cap(&cap).unwrap(); /// } /// ``` - #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] + #[cfg(not(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64")))] pub fn enable_cap(&self, cap: &kvm_enable_cap) -> Result<()> { // SAFETY: The ioctl is safe because we allocated the struct and we know the // kernel will write exactly the size of the struct. @@ -2441,7 +2441,7 @@ mod tests { } #[test] - #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] + #[cfg(not(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64")))] fn test_enable_cap_failure() { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index aa69624..9b8b6d5 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -139,9 +139,9 @@ ioctl_ior_nr!(KVM_MEMORY_ENCRYPT_UNREG_REGION, KVMIO, 0xbc, kvm_enc_region); // Ioctls for VCPU fds. ioctl_io_nr!(KVM_RUN, KVMIO, 0x80); -#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] +#[cfg(not(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64")))] ioctl_ior_nr!(KVM_GET_REGS, KVMIO, 0x81, kvm_regs); -#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] +#[cfg(not(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64")))] ioctl_iow_nr!(KVM_SET_REGS, KVMIO, 0x82, kvm_regs); #[cfg(any( target_arch = "x86", @@ -248,7 +248,7 @@ ioctl_io_nr!(KVM_SET_TSC_KHZ, KVMIO, 0xa2); ioctl_io_nr!(KVM_GET_TSC_KHZ, KVMIO, 0xa3); /* Available with KVM_CAP_ENABLE_CAP */ -#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] +#[cfg(not(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64")))] ioctl_iow_nr!(KVM_ENABLE_CAP, KVMIO, 0xa3, kvm_enable_cap); /* Available with KVM_CAP_SIGNAL_MSI */ #[cfg(any( From 66ba5f3a909f8978229b04017e0b80861c7baa24 Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Thu, 26 Sep 2024 12:38:01 +0000 Subject: [PATCH 309/332] riscv64: Introduce AIA related methods Implement `create_aia_device`, `set_supported_nr_irqs` and `request_aia_init` method to manipulate KVM AIA device. Signed-off-by: Ruoqing He --- src/ioctls/vm.rs | 55 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 3650be1..2456737 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -1952,6 +1952,61 @@ pub(crate) fn request_gic_init(vgic: &DeviceFd) { vgic.set_device_attr(&vgic_attr).unwrap(); } +/// Create a dummy AIA device. +/// +/// # Arguments +/// +/// * `vm` - The vm file descriptor. +/// * `flags` - Flags to be passed to `KVM_CREATE_DEVICE`. +#[cfg(test)] +#[cfg(target_arch = "riscv64")] +pub(crate) fn create_aia_device(vm: &VmFd, flags: u32) -> DeviceFd { + let mut aia_device = kvm_bindings::kvm_create_device { + type_: kvm_device_type_KVM_DEV_TYPE_RISCV_AIA, + fd: 0, + flags, + }; + vm.create_device(&mut aia_device) + .expect("Cannot create KVM vAIA device") +} + +/// Set supported number of IRQs for vAIA. +/// +/// # Arguments +/// +/// * `vaia` - The vAIA file descriptor. +/// * `nr_irqs` - Number of IRQs. +#[cfg(test)] +#[cfg(target_arch = "riscv64")] +pub(crate) fn set_supported_nr_irqs(vaia: &DeviceFd, nr_irqs: u32) { + let vaia_attr = kvm_bindings::kvm_device_attr { + group: kvm_bindings::KVM_DEV_RISCV_AIA_GRP_CONFIG, + attr: u64::from(kvm_bindings::KVM_DEV_RISCV_AIA_CONFIG_SRCS), + addr: &nr_irqs as *const u32 as u64, + flags: 0, + }; + vaia.has_device_attr(&vaia_attr).unwrap(); + vaia.set_device_attr(&vaia_attr).unwrap(); +} + +/// Request the initialization of the vAIA. +/// +/// # Arguments +/// +/// * `vaia` - The vAIA file descriptor. +#[cfg(test)] +#[cfg(target_arch = "riscv64")] +pub(crate) fn request_aia_init(vaia: &DeviceFd) { + let vaia_attr = kvm_bindings::kvm_device_attr { + group: kvm_bindings::KVM_DEV_RISCV_AIA_GRP_CTRL, + attr: u64::from(kvm_bindings::KVM_DEV_RISCV_AIA_CTRL_INIT), + addr: 0, + flags: 0, + }; + vaia.has_device_attr(&vaia_attr).unwrap(); + vaia.set_device_attr(&vaia_attr).unwrap(); +} + #[cfg(test)] mod tests { #![allow(clippy::undocumented_unsafe_blocks)] From 6ac9f42b87a76d6d429ddb6de1a55d5f031f81ce Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Wed, 2 Oct 2024 11:09:37 +0000 Subject: [PATCH 310/332] riscv64: Disable `get_reg_list` doc test Limit `get_reg_list` doc example to run on ARM64 only, since `get_reg_list` is for ARM64 and RISC-V64 but 500 registers are too big for RISC-V (a value between 180~200). Signed-off-by: Ruoqing He --- src/ioctls/vcpu.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 7af17de..b05c01a 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1212,14 +1212,17 @@ impl VcpuFd { /// let vm = kvm.create_vm().unwrap(); /// let vcpu = vm.create_vcpu(0).unwrap(); /// - /// // KVM_GET_REG_LIST demands that the vcpus be initalized. - /// let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default(); - /// vm.get_preferred_target(&mut kvi).unwrap(); - /// vcpu.vcpu_init(&kvi).expect("Cannot initialize vcpu"); + /// // KVM_GET_REG_LIST on Aarch64 demands that the vcpus be initialized. + /// #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + /// { + /// let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default(); + /// vm.get_preferred_target(&mut kvi).unwrap(); + /// vcpu.vcpu_init(&kvi).expect("Cannot initialize vcpu"); /// - /// let mut reg_list = RegList::new(500).unwrap(); - /// vcpu.get_reg_list(&mut reg_list).unwrap(); - /// assert!(reg_list.as_fam_struct_ref().n > 0); + /// let mut reg_list = RegList::new(500).unwrap(); + /// vcpu.get_reg_list(&mut reg_list).unwrap(); + /// assert!(reg_list.as_fam_struct_ref().n > 0); + /// } /// ``` #[cfg(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64"))] pub fn get_reg_list(&self, reg_list: &mut RegList) -> Result<()> { From 908ce9b2d0b560a211aadbdf8ef231712a640ec5 Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Wed, 2 Oct 2024 12:32:57 +0000 Subject: [PATCH 311/332] riscv64: Enable `set_device_attr` doc test Originally `set_device_attr` uses VFIO device for testing, but that's not available yet on RISC-V 64-bit platform, the QEMU patch [1] and kernel patch [2] are not upstreamed. Disabling VFIO device test for RISC-V at the time being. [1] https://lore.kernel.org/all/20240903201633.93182-1-dbarboza@ventanamicro.com/ [2] https://github.com/ventanamicro/linux/tree/dev-upstream Signed-off-by: Ruoqing He --- src/ioctls/device.rs | 50 ++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/src/ioctls/device.rs b/src/ioctls/device.rs index e2d2d76..f0ecd57 100644 --- a/src/ioctls/device.rs +++ b/src/ioctls/device.rs @@ -41,36 +41,46 @@ impl DeviceFd { /// /// # Example /// + /// Configuring a VFIO device using `set_device_attr`. Note that VFIO + /// devices are not yet available on RISC-V The patch for QEMU: + /// https://lore.kernel.org/all/20240903201633.93182-1-dbarboza@ventanamicro.com/ + /// and patch for linux kernel + /// https://github.com/ventanamicro/linux/tree/dev-upstream are both not + /// upstreamed. Disabling VFIO device test for RISC-V at the time being. + /// /// ```rust /// # extern crate kvm_ioctls; /// # extern crate kvm_bindings; /// # use kvm_ioctls::Kvm; - /// # use kvm_bindings::{ - /// kvm_device_type_KVM_DEV_TYPE_VFIO, - /// KVM_DEV_VFIO_GROUP, KVM_DEV_VFIO_GROUP_ADD, KVM_CREATE_DEVICE_TEST - /// }; /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// - /// let mut device = kvm_bindings::kvm_create_device { - /// type_: kvm_device_type_KVM_DEV_TYPE_VFIO, - /// fd: 0, - /// flags: KVM_CREATE_DEVICE_TEST, - /// }; + /// #[cfg(not(target_arch = "riscv64"))] + /// { + /// # use kvm_bindings::{ + /// # kvm_device_type_KVM_DEV_TYPE_VFIO, + /// # KVM_DEV_VFIO_GROUP, KVM_DEV_VFIO_GROUP_ADD, KVM_CREATE_DEVICE_TEST + /// # }; + /// let mut device = kvm_bindings::kvm_create_device { + /// type_: kvm_device_type_KVM_DEV_TYPE_VFIO, + /// fd: 0, + /// flags: KVM_CREATE_DEVICE_TEST, + /// }; /// - /// let device_fd = vm - /// .create_device(&mut device) - /// .expect("Cannot create KVM device"); + /// let device_fd = vm + /// .create_device(&mut device) + /// .expect("Cannot create KVM device"); /// - /// let dist_attr = kvm_bindings::kvm_device_attr { - /// group: KVM_DEV_VFIO_GROUP, - /// attr: u64::from(KVM_DEV_VFIO_GROUP_ADD), - /// addr: 0x0, - /// flags: 0, - /// }; + /// let dist_attr = kvm_bindings::kvm_device_attr { + /// group: KVM_DEV_VFIO_GROUP, + /// attr: u64::from(KVM_DEV_VFIO_GROUP_ADD), + /// addr: 0x0, + /// flags: 0, + /// }; /// - /// if (device_fd.has_device_attr(&dist_attr).is_ok()) { - /// device_fd.set_device_attr(&dist_attr).unwrap(); + /// if (device_fd.has_device_attr(&dist_attr).is_ok()) { + /// device_fd.set_device_attr(&dist_attr).unwrap(); + /// } /// } /// ``` pub fn set_device_attr(&self, device_attr: &kvm_device_attr) -> Result<()> { From 9fdcb4c2c8f1d18774d85232014cb18c766b0143 Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Wed, 2 Oct 2024 13:21:28 +0000 Subject: [PATCH 312/332] riscv64: Enable `create_device` doc test Complete `create_device` doc test to create the creation of AIA device on RISC-V 64-bit platform. Signed-off-by: Ruoqing He --- src/ioctls/vm.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 2456737..a058b40 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -1259,7 +1259,8 @@ impl VmFd { /// # use kvm_ioctls::Kvm; /// use kvm_bindings::{ /// kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2, kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, - /// kvm_device_type_KVM_DEV_TYPE_VFIO, KVM_CREATE_DEVICE_TEST, + /// kvm_device_type_KVM_DEV_TYPE_RISCV_AIA, kvm_device_type_KVM_DEV_TYPE_VFIO, + /// KVM_CREATE_DEVICE_TEST, /// }; /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); @@ -1272,6 +1273,8 @@ impl VmFd { /// type_: kvm_device_type_KVM_DEV_TYPE_VFIO, /// #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] /// type_: kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, + /// #[cfg(target_arch = "riscv64")] + /// type_: kvm_device_type_KVM_DEV_TYPE_RISCV_AIA, /// fd: 0, /// flags: KVM_CREATE_DEVICE_TEST, /// }; @@ -1286,6 +1289,8 @@ impl VmFd { /// vm.create_device(&mut device) /// .expect("Cannot create vGIC device") /// } + /// #[cfg(target_arch = "riscv64")] + /// panic!("Cannot create vAIA device."); /// }); /// ``` pub fn create_device(&self, device: &mut kvm_create_device) -> Result { From 3a16618efe9c426269f869ebb95a1b48d0d668da Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Thu, 3 Oct 2024 05:41:46 +0000 Subject: [PATCH 313/332] riscv64: Enable `set_gsi_routing` doc test RISC-V 64-bit requires an in-kernel interrupt chip (AIA) to be created in advance of committing `gsi_routing_table`. Signed-off-by: Ruoqing He --- src/ioctls/vm.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index a058b40..1eb3f20 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -619,9 +619,18 @@ impl VmFd { /// /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); + /// /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] /// vm.create_irq_chip().unwrap(); /// + /// #[cfg(target_arch = "riscv64")] + /// vm.create_device(&mut kvm_bindings::kvm_create_device { + /// type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_RISCV_AIA, + /// fd: 0, + /// flags: 0, + /// }) + /// .expect("Cannot create KVM vAIA device."); + /// /// let irq_routing = kvm_irq_routing::default(); /// vm.set_gsi_routing(&irq_routing).unwrap(); /// ``` From f4e96e9b56d3c56553e3a34c1459653f3f78cdf7 Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Thu, 3 Oct 2024 15:28:58 +0000 Subject: [PATCH 314/332] riscv64: Enable `get_dirty_log` doc test Add assembly code which does dirty one page and forces MMIO exit on RISC-V 64-bit platform to complete `get_dirty_log` doc test. Signed-off-by: Ruoqing He --- src/ioctls/vm.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 1eb3f20..ac21cb1 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -855,6 +855,13 @@ impl VmFd { /// 0x00, 0x00, 0x00, /// 0x14, /* b ; shouldn't get here, but if so loop forever */ /// ]; + /// #[cfg(target_arch = "riscv64")] + /// let asm_code = [ + /// 0x17, 0x03, 0x00, 0x00, // auipc t1, 0; -> t1 + /// 0xa3, 0x23, 0x73, 0x00, // sw t2, t1 + 7; dirty current page + /// 0x23, 0x20, 0x75, 0x00, // sw t2, a0; trigger MMIO exit + /// 0x6f, 0x00, 0x00, 0x00, // j .;shouldn't get here, but if so loop forever + /// ]; /// /// // Write the code in the guest memory. This will generate a dirty page. /// unsafe { @@ -894,6 +901,14 @@ impl VmFd { /// vcpu_fd.set_one_reg(core_reg_base + 2 * 0, &mmio_addr.to_le_bytes()); // set X0 /// } /// + /// #[cfg(target_arch = "riscv64")] + /// { + /// let core_reg_base: u64 = 0x8030_0000_0200_0000; + /// let mmio_addr: u64 = guest_addr + mem_size as u64; + /// vcpu_fd.set_one_reg(core_reg_base, &guest_addr.to_le_bytes()); // set PC + /// vcpu_fd.set_one_reg(core_reg_base + 10, &mmio_addr.to_le_bytes()); // set A0 + /// } + /// /// loop { /// match vcpu_fd.run().expect("run failed") { /// VcpuExit::MmioWrite(addr, data) => { From b5c920564068b43f5218260724697b5f7c5c6342 Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Thu, 3 Oct 2024 16:28:24 +0000 Subject: [PATCH 315/332] riscv64: Add create VM with one vCPU example Add example which triggers an MMIO exit after creating a dirty page. Signed-off-by: Ruoqing He --- src/lib.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 0d01467..aeead3a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +// Copyright © 2024 Institute of Software, CAS. All rights reserved. +// // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR MIT // @@ -21,6 +23,7 @@ //! //! - x86_64 //! - arm64 (experimental) +//! - riscv64 (experimental) //! //! **NOTE:** The list of available ioctls is not extensive. //! @@ -30,6 +33,7 @@ //! On the vCPU we are running machine specific code. This example is based on //! the [LWN article](https://lwn.net/Articles/658511/) on using the KVM API. //! The aarch64 example was modified accordingly. +//! The riscv64 example was modified accordingly. //! //! To get code running on the vCPU we are going through the following steps: //! @@ -93,6 +97,15 @@ //! 0x14, /* b ; shouldn't get here, but if so loop forever */ //! ]; //! } +//! #[cfg(target_arch = "riscv64")] +//! { +//! asm_code = &[ +//! 0x17, 0x03, 0x00, 0x00, // auipc t1, 0; -> t1 +//! 0xa3, 0x23, 0x73, 0x00, // sw t2, t1 + 7; dirty current page +//! 0x23, 0x20, 0x75, 0x00, // sw t2, a0; trigger MMIO exit +//! 0x6f, 0x00, 0x00, 0x00, // j .;shouldn't get here, but if so loop forever +//! ]; +//! } //! //! // 1. Instantiate KVM. //! let kvm = Kvm::new().unwrap(); @@ -165,6 +178,17 @@ //! vcpu_fd.set_one_reg(core_reg_base + 2 * 0, &mmio_addr.to_le_bytes()); //! } //! +//! #[cfg(target_arch = "riscv64")] +//! { +//! // riscv64 specific register setup. +//! let core_reg_base: u64 = 0x8030_0000_0200_0000; +//! let mmio_addr: u64 = guest_addr + mem_size as u64; +//! // set PC +//! vcpu_fd.set_one_reg(core_reg_base, &guest_addr.to_le_bytes()); +//! // set A0 +//! vcpu_fd.set_one_reg(core_reg_base + 10, &mmio_addr.to_le_bytes()); +//! } +//! //! // 6. Run code on the vCPU. //! loop { //! match vcpu_fd.run().expect("run failed") { @@ -195,7 +219,7 @@ //! // Since on aarch64 there is not halt instruction, //! // we break immediately after the last known instruction //! // of the asm code example so that we avoid an infinite loop. -//! #[cfg(target_arch = "aarch64")] +//! #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] //! break; //! } //! VcpuExit::Hlt => { From e6abfa2bf0b4f04b4e5358afb513a39467955511 Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Fri, 4 Oct 2024 09:58:38 +0000 Subject: [PATCH 316/332] riscv64: Add `vcpu` related unit-test Add: - `test_run_code` - `mpstate_test` - `test_faulty_vcpu_fd` - `test_faulty_vcpu_fd_riscv64` - `test_get_reg_list` - `test_set_one_reg` - `test_get_one_reg` tests. Signed-off-by: Ruoqing He --- src/ioctls/vcpu.rs | 229 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 228 insertions(+), 1 deletion(-) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index b05c01a..7958415 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1,3 +1,5 @@ +// Copyright © 2024 Institute of Software, CAS. All rights reserved. +// // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR MIT // @@ -2231,6 +2233,7 @@ mod tests { target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", + target_arch = "riscv64", target_arch = "s390x" ))] #[test] @@ -2405,6 +2408,98 @@ mod tests { } } + #[cfg(target_arch = "riscv64")] + #[test] + fn test_run_code() { + use std::io::Write; + + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + #[rustfmt::skip] + let code = [ + 0x13, 0x05, 0x50, 0x40, // li a0, 0x0405; + 0x23, 0x20, 0xac, 0x00, // sw a0, 0(s8); test physical memory write + 0x03, 0xa5, 0x0c, 0x00, // lw a0, 0(s9); test MMIO read + 0x93, 0x05, 0x70, 0x60, // li a1, 0x0607; + 0x23, 0xa0, 0xbc, 0x00, // sw a1, 0(s9); test MMIO write + 0x6f, 0x00, 0x00, 0x00, // j .; shouldn't get here, but if so loop forever + ]; + + let mem_size = 0x20000; + let load_addr = mmap_anonymous(mem_size).as_ptr(); + let guest_addr: u64 = 0x10000; + let slot: u32 = 0; + let mem_region = kvm_userspace_memory_region { + slot, + guest_phys_addr: guest_addr, + memory_size: mem_size as u64, + userspace_addr: load_addr as u64, + flags: KVM_MEM_LOG_DIRTY_PAGES, + }; + unsafe { + vm.set_user_memory_region(mem_region).unwrap(); + } + + unsafe { + // Get a mutable slice of `mem_size` from `load_addr`. + // This is safe because we mapped it before. + let mut slice = std::slice::from_raw_parts_mut(load_addr, mem_size); + slice.write_all(&code).unwrap(); + } + + let mut vcpu_fd = vm.create_vcpu(0).unwrap(); + + let core_reg_base: u64 = 0x8030_0000_0200_0000; + let mmio_addr: u64 = guest_addr + mem_size as u64; + + // Set the PC to the guest address where we loaded the code. + vcpu_fd + .set_one_reg(core_reg_base, &(guest_addr as u128).to_le_bytes()) + .unwrap(); + + // Set s8 and s9 to the addresses the guest test code needs + vcpu_fd + .set_one_reg( + core_reg_base + 24, + &(guest_addr as u128 + 0x10000).to_le_bytes(), + ) + .unwrap(); + vcpu_fd + .set_one_reg(core_reg_base + 25, &(mmio_addr as u128).to_le_bytes()) + .unwrap(); + + loop { + match vcpu_fd.run().expect("run failed") { + VcpuExit::MmioRead(addr, data) => { + assert_eq!(addr, mmio_addr); + assert_eq!(data.len(), 4); + data[3] = 0x0; + data[2] = 0x0; + data[1] = 0x5; + data[0] = 0x6; + } + VcpuExit::MmioWrite(addr, data) => { + assert_eq!(addr, mmio_addr); + assert_eq!(data.len(), 4); + assert_eq!(data[3], 0x0); + assert_eq!(data[2], 0x0); + assert_eq!(data[1], 0x6); + assert_eq!(data[0], 0x7); + // The code snippet dirties one page at guest_addr + 0x10000. + // The code page should not be dirty, as it's not written by the guest. + let dirty_pages_bitmap = vm.get_dirty_log(slot, mem_size).unwrap(); + let dirty_pages: u32 = dirty_pages_bitmap + .into_iter() + .map(|page| page.count_ones()) + .sum(); + assert_eq!(dirty_pages, 1); + break; + } + r => panic!("unexpected exit reason: {:?}", r), + } + } + } + #[cfg(target_arch = "x86_64")] #[test] fn test_run_code() { @@ -2537,7 +2632,8 @@ mod tests { target_arch = "x86", target_arch = "x86_64", target_arch = "arm", - target_arch = "aarch64" + target_arch = "aarch64", + target_arch = "riscv64" ))] fn test_faulty_vcpu_fd() { use std::os::unix::io::{FromRawFd, IntoRawFd}; @@ -2564,10 +2660,22 @@ mod tests { .errno(), badf_errno ); + #[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" + ))] assert_eq!( faulty_vcpu_fd.get_vcpu_events().unwrap_err().errno(), badf_errno ); + #[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" + ))] assert_eq!( faulty_vcpu_fd .set_vcpu_events(&kvm_vcpu_events::default()) @@ -2804,6 +2912,52 @@ mod tests { faulty_vcpu_fd.vcpu.into_raw_fd(); } + #[test] + #[cfg(target_arch = "riscv64")] + fn test_faulty_vcpu_fd_riscv64() { + use std::os::unix::io::{FromRawFd, IntoRawFd}; + + let badf_errno = libc::EBADF; + + let faulty_vcpu_fd = VcpuFd { + vcpu: unsafe { File::from_raw_fd(-2) }, + kvm_run_ptr: KvmRunWrapper { + kvm_run_ptr: mmap_anonymous(10).cast(), + mmap_size: 10, + }, + coalesced_mmio_ring: None, + }; + + let reg_id = 0x8030_0000_0200_000a; + let mut reg_data = 0u128.to_le_bytes(); + + assert_eq!( + faulty_vcpu_fd + .get_reg_list(&mut RegList::new(200).unwrap()) + .unwrap_err() + .errno(), + badf_errno + ); + assert_eq!( + faulty_vcpu_fd + .set_one_reg(reg_id, ®_data) + .unwrap_err() + .errno(), + badf_errno + ); + assert_eq!( + faulty_vcpu_fd + .get_one_reg(reg_id, &mut reg_data) + .unwrap_err() + .errno(), + badf_errno + ); + + // Don't drop the File object, or it'll notice the file it's trying to close is + // invalid and abort the process. + faulty_vcpu_fd.vcpu.into_raw_fd(); + } + #[test] #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] fn test_get_preferred_target() { @@ -2912,6 +3066,79 @@ mod tests { vcpu.get_reg_list(&mut reg_list).unwrap() } + #[test] + #[cfg(target_arch = "riscv64")] + fn test_set_one_reg() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let vcpu = vm.create_vcpu(0).unwrap(); + + let data: u128 = 0; + let reg_id: u64 = 0; + + vcpu.set_one_reg(reg_id, &data.to_le_bytes()).unwrap_err(); + // Exercising KVM_SET_ONE_REG by trying to alter the data inside the A0 + // register. + // This regiseter is 64 bit wide (8 bytes). + const A0_REG_ID: u64 = 0x8030_0000_0200_000a; + vcpu.set_one_reg(A0_REG_ID, &data.to_le_bytes()) + .expect("Failed to set a0 register"); + + // Trying to set 8 byte register with 7 bytes must fail. + vcpu.set_one_reg(A0_REG_ID, &[0_u8; 7]).unwrap_err(); + } + + #[test] + #[cfg(target_arch = "riscv64")] + fn test_get_one_reg() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let vcpu = vm.create_vcpu(0).unwrap(); + + const PRESET: u64 = 0x7; + let data: u128 = PRESET as u128; + const A0_REG_ID: u64 = 0x8030_0000_0200_000a; + vcpu.set_one_reg(A0_REG_ID, &data.to_le_bytes()) + .expect("Failed to set a0 register"); + + let mut bytes = [0_u8; 16]; + vcpu.get_one_reg(A0_REG_ID, &mut bytes) + .expect("Failed to get a0 register"); + let data = u128::from_le_bytes(bytes); + assert_eq!(data, PRESET as u128); + + // Trying to get 8 byte register with 7 bytes must fail. + vcpu.get_one_reg(A0_REG_ID, &mut [0_u8; 7]).unwrap_err(); + } + + #[test] + #[cfg(target_arch = "riscv64")] + fn test_get_reg_list() { + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let vcpu = vm.create_vcpu(0).unwrap(); + + let mut reg_list = RegList::new(1).unwrap(); + + // KVM_GET_REG_LIST offers us a number of registers for which we have + // not allocated memory, so the first time it fails. + let err = vcpu.get_reg_list(&mut reg_list).unwrap_err(); + assert!(err.errno() == libc::E2BIG); + // SAFETY: This structure is a result from a specific vCPU ioctl + assert!(unsafe { reg_list.as_mut_fam_struct() }.n > 0); + + // We make use of the number of registers returned to allocate memory and + // try one more time. + // SAFETY: This structure is a result from a specific vCPU ioctl + let mut reg_list = + RegList::new(unsafe { reg_list.as_mut_fam_struct() }.n as usize).unwrap(); + vcpu.get_reg_list(&mut reg_list).unwrap(); + + // Test get a register list contains 200 registers explicitly + let mut reg_list = RegList::new(200).unwrap(); + vcpu.get_reg_list(&mut reg_list).unwrap(); + } + #[test] fn test_get_kvm_run() { let kvm = Kvm::new().unwrap(); From ea36c410928ab8cadbcc0ad45ce337e5cd24fbb8 Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Fri, 4 Oct 2024 11:08:53 +0000 Subject: [PATCH 317/332] riscv64: Add `device` related unit-tests Add `test_create_device` to verify that vAIA device works on riscv64. Signed-off-by: Ruoqing He --- src/ioctls/device.rs | 87 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/src/ioctls/device.rs b/src/ioctls/device.rs index f0ecd57..c19c6db 100644 --- a/src/ioctls/device.rs +++ b/src/ioctls/device.rs @@ -1,3 +1,5 @@ +// Copyright © 2024 Institute of Software, CAS. All rights reserved. +// // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR MIT @@ -322,4 +324,89 @@ mod tests { // when we initialize the GIC. assert_eq!(data, 128); } + + #[test] + #[cfg(target_arch = "riscv64")] + fn test_create_device() { + use crate::ioctls::vm::{create_aia_device, request_aia_init, set_supported_nr_irqs}; + use kvm_bindings::{ + kvm_device_attr, kvm_device_type_KVM_DEV_TYPE_FSL_MPIC_20, + KVM_DEV_RISCV_AIA_ADDR_APLIC, KVM_DEV_RISCV_AIA_CONFIG_SRCS, + KVM_DEV_RISCV_AIA_GRP_ADDR, KVM_DEV_RISCV_AIA_GRP_CONFIG, + }; + use vmm_sys_util::errno::Error; + + let kvm = Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + + let mut aia_device = kvm_bindings::kvm_create_device { + type_: kvm_device_type_KVM_DEV_TYPE_FSL_MPIC_20, + fd: 0, + flags: KVM_CREATE_DEVICE_TEST, + }; + // This fails on riscv64 as it does not use MPIC (MultiProcessor Interrupt Controller), + // it uses the vAIA. + vm.create_device(&mut aia_device).unwrap_err(); + + let device_fd = create_aia_device(&vm, 0); + + // AIA on riscv64 requires at least one online vCPU prior to setting + // device attributes. Otherwise it would fail when trying to set address + // of IMSIC. + vm.create_vcpu(0).unwrap(); + + // Following lines to re-construct device_fd are used to test + // DeviceFd::from_raw_fd() and DeviceFd::as_raw_fd(). + let raw_fd = unsafe { libc::dup(device_fd.as_raw_fd()) }; + assert!(raw_fd >= 0); + let device_fd = unsafe { DeviceFd::from_raw_fd(raw_fd) }; + + // Set maximum supported number of IRQs of the vAIA device to 128. + set_supported_nr_irqs(&device_fd, 128); + + // Before request vAIA device to initialize, APLIC and IMSIC must be set + let aplic_addr: u64 = 0x4000; + device_fd + .set_device_attr(&kvm_device_attr { + group: KVM_DEV_RISCV_AIA_GRP_ADDR, + attr: u64::from(KVM_DEV_RISCV_AIA_ADDR_APLIC), + addr: &aplic_addr as *const u64 as u64, + flags: 0, + }) + .unwrap(); + let imsic_addr: u64 = 0x8000; + device_fd + .set_device_attr(&kvm_device_attr { + group: KVM_DEV_RISCV_AIA_GRP_ADDR, + attr: 1u64, + addr: &imsic_addr as *const u64 as u64, + flags: 0, + }) + .unwrap(); + + // Initialize valid vAIA device. + request_aia_init(&device_fd); + + // Test `get_device_attr`. Here we try to extract the maximum supported number of IRQs. + // This value should be saved in the address provided to the ioctl. + let mut data: u32 = 0; + + let mut aia_attr = kvm_bindings::kvm_device_attr { + group: KVM_DEV_RISCV_AIA_GRP_CONFIG, + attr: u64::from(KVM_DEV_RISCV_AIA_CONFIG_SRCS), + addr: data as u64, + ..Default::default() + }; + + // Without properly providing the address to where the + // value will be stored, the ioctl fails with EFAULT. + let res = unsafe { device_fd.get_device_attr(&mut aia_attr) }; + assert_eq!(res, Err(Error::new(libc::EFAULT))); + + aia_attr.addr = &mut data as *mut u32 as u64; + unsafe { device_fd.get_device_attr(&mut aia_attr) }.unwrap(); + // The maximum supported number of IRQs should be 128, same as the value + // when we initialize the AIA. + assert_eq!(data, 128); + } } From 4e1c6c42b2dfa90b599c636be39b32978e4e2f00 Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Fri, 4 Oct 2024 12:11:44 +0000 Subject: [PATCH 318/332] riscv64: Add `vm` related unit-tests Add: - `test_register_unregister_irqfd` - `test_set_irq_line` - `test_singal_msi_failure` - `test_set_gsi_routing` tests. Signed-off-by: Ruoqing He --- src/ioctls/vm.rs | 118 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 115 insertions(+), 3 deletions(-) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index ac21cb1..fa2fa17 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -1,3 +1,5 @@ +// Copyright © 2024 Institute of Software, CAS. All rights reserved. +// // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR MIT // @@ -22,7 +24,14 @@ use vmm_sys_util::errno; use vmm_sys_util::eventfd::EventFd; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use vmm_sys_util::ioctl::ioctl_with_mut_ptr; -use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_val}; +#[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64" +))] +use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref}; +use vmm_sys_util::ioctl::{ioctl_with_ref, ioctl_with_val}; /// An address either in programmable I/O space or in memory mapped I/O space. /// @@ -2348,6 +2357,64 @@ mod tests { } } + #[test] + #[cfg(target_arch = "riscv64")] + fn test_register_unregister_irqfd() { + let kvm = Kvm::new().unwrap(); + let vm_fd = kvm.create_vm().unwrap(); + let evtfd1 = EventFd::new(EFD_NONBLOCK).unwrap(); + let evtfd2 = EventFd::new(EFD_NONBLOCK).unwrap(); + let evtfd3 = EventFd::new(EFD_NONBLOCK).unwrap(); + + // Create the vAIA device. + let vaia_fd = create_aia_device(&vm_fd, 0); + + // AIA on riscv64 requires at least one online vCPU prior to setting + // device attributes. Otherwise it would fail when trying ot set address + // of IMSIC. + vm_fd.create_vcpu(0).unwrap(); + + // Set maximum supported number of IRQs of the vAIA device to 128. + set_supported_nr_irqs(&vaia_fd, 128); + + // Before request vAIA device to initialize, APLIC and IMSIC must be set + let aplic_addr: u64 = 0x4000; + vaia_fd + .set_device_attr(&kvm_device_attr { + group: KVM_DEV_RISCV_AIA_GRP_ADDR, + attr: u64::from(KVM_DEV_RISCV_AIA_ADDR_APLIC), + addr: &aplic_addr as *const u64 as u64, + flags: 0, + }) + .unwrap(); + let imsic_addr: u64 = 0x8000; + vaia_fd + .set_device_attr(&kvm_device_attr { + group: KVM_DEV_RISCV_AIA_GRP_ADDR, + attr: 1u64, + addr: &imsic_addr as *const u64 as u64, + flags: 0, + }) + .unwrap(); + + // Initialize valid vAIA device. + request_aia_init(&vaia_fd); + + vm_fd.register_irqfd(&evtfd1, 4).unwrap(); + vm_fd.register_irqfd(&evtfd2, 8).unwrap(); + vm_fd.register_irqfd(&evtfd3, 4).unwrap(); + vm_fd.unregister_irqfd(&evtfd2, 8).unwrap(); + // KVM irqfd doesn't report failure on this case:( + vm_fd.unregister_irqfd(&evtfd2, 8).unwrap(); + + // Duplicated eventfd registration. + // On riscv64 this fails as the event fd was already matched with a GSI. + vm_fd.register_irqfd(&evtfd3, 4).unwrap_err(); + vm_fd.register_irqfd(&evtfd3, 5).unwrap_err(); + // KVM irqfd doesn't report failure on this case:( + vm_fd.unregister_irqfd(&evtfd3, 5).unwrap(); + } + #[test] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn test_set_irq_line() { @@ -2395,6 +2462,46 @@ mod tests { vm_fd.set_irq_line(0x02_00_0010, true).unwrap(); } + #[test] + #[cfg(target_arch = "riscv64")] + fn test_set_irq_line() { + let kvm = Kvm::new().unwrap(); + let vm_fd = kvm.create_vm().unwrap(); + vm_fd.create_vcpu(0).unwrap(); + + // Create the vAIA device. + let vaia_fd = create_aia_device(&vm_fd, 0); + // Set maximum supported number of IRQs of the vAIA device to 128. + set_supported_nr_irqs(&vaia_fd, 128); + + // Before request vAIA device to initialize, APLIC and IMSIC must be set + let aplic_addr: u64 = 0x4000; + vaia_fd + .set_device_attr(&kvm_device_attr { + group: KVM_DEV_RISCV_AIA_GRP_ADDR, + attr: u64::from(KVM_DEV_RISCV_AIA_ADDR_APLIC), + addr: &aplic_addr as *const u64 as u64, + flags: 0, + }) + .unwrap(); + let imsic_addr: u64 = 0x8000; + vaia_fd + .set_device_attr(&kvm_device_attr { + group: KVM_DEV_RISCV_AIA_GRP_ADDR, + attr: 1u64, + addr: &imsic_addr as *const u64 as u64, + flags: 0, + }) + .unwrap(); + + // Initialize valid vAIA device. + request_aia_init(&vaia_fd); + + vm_fd.set_irq_line(7, true).unwrap(); + vm_fd.set_irq_line(7, false).unwrap(); + vm_fd.set_irq_line(7, true).unwrap(); + } + #[test] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn test_faulty_vm_fd() { @@ -2515,7 +2622,8 @@ mod tests { target_arch = "x86", target_arch = "x86_64", target_arch = "arm", - target_arch = "aarch64" + target_arch = "aarch64", + target_arch = "riscv64" ))] fn test_signal_msi_failure() { let kvm = Kvm::new().unwrap(); @@ -2564,7 +2672,8 @@ mod tests { target_arch = "x86", target_arch = "x86_64", target_arch = "arm", - target_arch = "aarch64" + target_arch = "aarch64", + target_arch = "riscv64" ))] fn test_set_gsi_routing() { let kvm = Kvm::new().unwrap(); @@ -2575,6 +2684,9 @@ mod tests { vm.set_gsi_routing(&irq_routing).unwrap_err(); vm.create_irq_chip().unwrap(); } + // RISC-V 64-bit expect an AIA device to be created in advance of committing irq_routing table. + #[cfg(target_arch = "riscv64")] + create_aia_device(&vm, 0); let irq_routing = kvm_irq_routing::default(); vm.set_gsi_routing(&irq_routing).unwrap(); } From 7274d86e57c82f4652bea132090e9a1bacb575b3 Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Fri, 4 Oct 2024 12:29:15 +0000 Subject: [PATCH 319/332] x86: Repurpose `test_set_gsi_routing` Compilation would fail with previous code on other architectures don't have `create_irq_chip` available. Repurpose this unit test to work on all architectures. Signed-off-by: Ruoqing He --- src/ioctls/vm.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index fa2fa17..fa747d1 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -2678,16 +2678,19 @@ mod tests { fn test_set_gsi_routing() { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); - if cfg!(target_arch = "x86") || cfg!(target_arch = "x86_64") { - let irq_routing = kvm_irq_routing::default(); - // Expect failure for x86 since the irqchip is not created yet. - vm.set_gsi_routing(&irq_routing).unwrap_err(); - vm.create_irq_chip().unwrap(); - } - // RISC-V 64-bit expect an AIA device to be created in advance of committing irq_routing table. + let irq_routing = kvm_irq_routing::default(); + + // Expect failure for x86 since the irqchip is not created yet. + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + vm.set_gsi_routing(&irq_routing).unwrap_err(); + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + vm.create_irq_chip().unwrap(); + + // RISC-V 64-bit expect an AIA device to be created in advance of + // committing irq_routing table. #[cfg(target_arch = "riscv64")] create_aia_device(&vm, 0); - let irq_routing = kvm_irq_routing::default(); + vm.set_gsi_routing(&irq_routing).unwrap(); } From 9f67a1af344f003967d9a24a18d7085b80fd3aae Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Fri, 4 Oct 2024 12:36:32 +0000 Subject: [PATCH 320/332] ci: Enable riscv64 CI Add `.platform` to enable CI on riscv64 platform. Signed-off-by: Ruoqing He --- .platform | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .platform diff --git a/.platform b/.platform new file mode 100644 index 0000000..c9db5a6 --- /dev/null +++ b/.platform @@ -0,0 +1,3 @@ +x86_64 +aarch64 +riscv64 From 1f1b34eb19ad9167983a9e48b864231bc02b4c2a Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Fri, 4 Oct 2024 12:38:18 +0000 Subject: [PATCH 321/332] chore: Update CHANGELOG.md Update `CHANGELOG.md` to document added riscv64 ioctls. Signed-off-by: Ruoqing He --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b6a888..6bdfca7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### Added +- [[#275](https://github.com/rust-vmm/kvm-ioctls/pull/275)]: Introduce `riscv64` ioctls. + ### Changed - [[#273](https://github.com/rust-vmm/kvm-ioctls/pull/273)]: `DeviceFd::get_device_attr` is now From b26d74c4a29c051fe72819b307e2621b29fab072 Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Sat, 5 Oct 2024 02:45:20 +0000 Subject: [PATCH 322/332] riscv64: Update README.md Update `README.md` since riscv64 is introduced as experimental. Signed-off-by: Ruoqing He --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e0c164e..2b4a5fd 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ as the code documentation. ## Supported Platforms -The kvm-ioctls can be used on x86_64 and aarch64. +The kvm-ioctls can be used on x86_64, aarch64 and riscv64 (experimental). ## Running the tests @@ -28,14 +28,16 @@ For the complete list of tests, check our [CI pipeline](https://buildkite.com/rust-vmm/kvm-ioctls-ci). Each individual test runs in a container. To reproduce a test locally, you can -use the dev-container on both x86 and arm64. +use the dev-container on x86_64, arm64 and riscv64. ```bash +# For running riscv64 tests, replace v47 with v47-riscv. This provides an +# emulated riscv64 environment on a x86_64 host. docker run --device=/dev/kvm \ -it \ --security-opt seccomp=unconfined \ --volume $(pwd)/kvm-ioctls:/kvm-ioctls \ - rustvmm/dev:v16 + rustvmm/dev:v47 cd kvm-ioctls/ cargo test ``` From e8932c589d82615b2c9a813719a12d8ea726118e Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Thu, 17 Oct 2024 20:48:41 +0800 Subject: [PATCH 323/332] x86: Drop x86 architecture support As @roypat pointed out: The x86 32-bit is not supported in other rust-vmm crates, dropping the `target_arch = "x86"` predicates to stop supporting x86 32-bit. Signed-off-by: Ruoqing He --- src/cap.rs | 34 +++++------ src/ioctls/device.rs | 4 +- src/ioctls/system.rs | 32 +++++------ src/ioctls/vcpu.rs | 131 ++++++++++++++++--------------------------- src/ioctls/vm.rs | 104 ++++++++++++++-------------------- src/kvm_ioctls.rs | 95 +++++++++++++------------------ src/lib.rs | 8 +-- 7 files changed, 165 insertions(+), 243 deletions(-) diff --git a/src/cap.rs b/src/cap.rs index af370ee..2bec3ce 100644 --- a/src/cap.rs +++ b/src/cap.rs @@ -42,39 +42,33 @@ pub enum Cap { DestroyMemoryRegionWorks = KVM_CAP_DESTROY_MEMORY_REGION_WORKS, UserNmi = KVM_CAP_USER_NMI, #[cfg(any( - target_arch = "x86", target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", target_arch = "s390x" ))] SetGuestDebug = KVM_CAP_SET_GUEST_DEBUG, - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] ReinjectControl = KVM_CAP_REINJECT_CONTROL, IrqRouting = KVM_CAP_IRQ_ROUTING, IrqInjectStatus = KVM_CAP_IRQ_INJECT_STATUS, AssignDevIrq = KVM_CAP_ASSIGN_DEV_IRQ, JoinMemoryRegionsWorks = KVM_CAP_JOIN_MEMORY_REGIONS_WORKS, - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] Mce = KVM_CAP_MCE, Irqfd = KVM_CAP_IRQFD, - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] Pit2 = KVM_CAP_PIT2, SetBootCpuId = KVM_CAP_SET_BOOT_CPU_ID, - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] PitState2 = KVM_CAP_PIT_STATE2, Ioeventfd = KVM_CAP_IOEVENTFD, SetIdentityMapAddr = KVM_CAP_SET_IDENTITY_MAP_ADDR, - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] XenHvm = KVM_CAP_XEN_HVM, AdjustClock = KVM_CAP_ADJUST_CLOCK, InternalErrorData = KVM_CAP_INTERNAL_ERROR_DATA, - #[cfg(any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64" - ))] + #[cfg(any(target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64"))] VcpuEvents = KVM_CAP_VCPU_EVENTS, S390Psw = KVM_CAP_S390_PSW, PpcSegstate = KVM_CAP_PPC_SEGSTATE, @@ -84,15 +78,15 @@ pub enum Cap { PciSegment = KVM_CAP_PCI_SEGMENT, PpcPairedSingles = KVM_CAP_PPC_PAIRED_SINGLES, IntrShadow = KVM_CAP_INTR_SHADOW, - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] Debugregs = KVM_CAP_DEBUGREGS, X86RobustSinglestep = KVM_CAP_X86_ROBUST_SINGLESTEP, PpcOsi = KVM_CAP_PPC_OSI, PpcUnsetIrq = KVM_CAP_PPC_UNSET_IRQ, EnableCap = KVM_CAP_ENABLE_CAP, - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] Xsave = KVM_CAP_XSAVE, - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] Xcrs = KVM_CAP_XCRS, PpcGetPvinfo = KVM_CAP_PPC_GET_PVINFO, PpcIrqLevel = KVM_CAP_PPC_IRQ_LEVEL, @@ -145,9 +139,9 @@ pub enum Cap { PpcEnableHcall = KVM_CAP_PPC_ENABLE_HCALL, CheckExtensionVm = KVM_CAP_CHECK_EXTENSION_VM, S390UserSigp = KVM_CAP_S390_USER_SIGP, - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] X86Smm = KVM_CAP_X86_SMM, - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] SplitIrqchip = KVM_CAP_SPLIT_IRQCHIP, ArmPmuV3 = KVM_CAP_ARM_PMU_V3, ImmediateExit = KVM_CAP_IMMEDIATE_EXIT, @@ -165,10 +159,10 @@ pub enum Cap { ArmPtrAuthAddress = KVM_CAP_ARM_PTRAUTH_ADDRESS, #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] ArmPtrAuthGeneric = KVM_CAP_ARM_PTRAUTH_GENERIC, - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] X86UserSpaceMsr = KVM_CAP_X86_USER_SPACE_MSR, - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] ExitHypercall = KVM_CAP_EXIT_HYPERCALL, - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] MemoryFaultInfo = KVM_CAP_MEMORY_FAULT_INFO, } diff --git a/src/ioctls/device.rs b/src/ioctls/device.rs index c19c6db..fc13104 100644 --- a/src/ioctls/device.rs +++ b/src/ioctls/device.rs @@ -202,7 +202,7 @@ mod tests { #![allow(clippy::undocumented_unsafe_blocks)] use super::*; use crate::ioctls::system::Kvm; - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] use kvm_bindings::{ kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, kvm_device_type_KVM_DEV_TYPE_VFIO, KVM_DEV_VFIO_GROUP, KVM_DEV_VFIO_GROUP_ADD, @@ -213,7 +213,7 @@ mod tests { use kvm_bindings::KVM_CREATE_DEVICE_TEST; #[test] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] fn test_create_device() { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); diff --git a/src/ioctls/system.rs b/src/ioctls/system.rs index 1660a8e..83d41fc 100644 --- a/src/ioctls/system.rs +++ b/src/ioctls/system.rs @@ -16,10 +16,10 @@ use crate::ioctls::Result; use crate::kvm_ioctls::*; #[cfg(target_arch = "aarch64")] use kvm_bindings::KVM_VM_TYPE_ARM_IPA_SIZE_MASK; -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] use kvm_bindings::{CpuId, MsrList, Msrs, KVM_MAX_CPUID_ENTRIES, KVM_MAX_MSR_ENTRIES}; use vmm_sys_util::errno; -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] use vmm_sys_util::ioctl::ioctl_with_mut_ptr; use vmm_sys_util::ioctl::{ioctl, ioctl_with_val}; @@ -352,7 +352,7 @@ impl Kvm { } } - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] fn get_cpuid(&self, kind: u64, num_entries: usize) -> Result { if num_entries > KVM_MAX_CPUID_ENTRIES { // Returns the same error the underlying `ioctl` would have sent. @@ -395,7 +395,7 @@ impl Kvm { /// let cpuid_entries = cpuid.as_mut_slice(); /// assert!(cpuid_entries.len() <= KVM_MAX_CPUID_ENTRIES); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn get_emulated_cpuid(&self, num_entries: usize) -> Result { self.get_cpuid(KVM_GET_EMULATED_CPUID(), num_entries) } @@ -424,7 +424,7 @@ impl Kvm { /// let cpuid_entries = cpuid.as_mut_slice(); /// assert!(cpuid_entries.len() <= KVM_MAX_CPUID_ENTRIES); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn get_supported_cpuid(&self, num_entries: usize) -> Result { self.get_cpuid(KVM_GET_SUPPORTED_CPUID(), num_entries) } @@ -441,7 +441,7 @@ impl Kvm { /// let kvm = Kvm::new().unwrap(); /// let msr_index_list = kvm.get_msr_index_list().unwrap(); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn get_msr_index_list(&self) -> Result { let mut msr_list = MsrList::new(KVM_MAX_MSR_ENTRIES).map_err(|_| errno::Error::new(libc::ENOMEM))?; @@ -477,7 +477,7 @@ impl Kvm { /// let kvm = Kvm::new().unwrap(); /// let msr_feature_index_list = kvm.get_msr_feature_index_list().unwrap(); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn get_msr_feature_index_list(&self) -> Result { let mut msr_list = MsrList::new(KVM_MAX_MSR_ENTRIES).map_err(|_| errno::Error::new(libc::ENOMEM))?; @@ -531,7 +531,7 @@ impl Kvm { /// .unwrap(); /// let ret = kvm.get_msrs(&mut msrs).unwrap(); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn get_msrs(&self, msrs: &mut Msrs) -> Result { // SAFETY: Here we trust the kernel not to read past the end of the kvm_msrs struct. let ret = unsafe { ioctl_with_mut_ptr(self, KVM_GET_MSRS(), msrs.as_mut_fam_struct_ptr()) }; @@ -731,7 +731,7 @@ mod tests { use super::*; use libc::{fcntl, FD_CLOEXEC, F_GETFD}; use std::os::fd::IntoRawFd; - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] use vmm_sys_util::fam::FamStruct; #[test] @@ -867,7 +867,7 @@ mod tests { } } - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] #[test] fn test_get_supported_cpuid() { let kvm = Kvm::new().unwrap(); @@ -882,7 +882,7 @@ mod tests { } #[test] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] fn test_get_emulated_cpuid() { let kvm = Kvm::new().unwrap(); let mut cpuid = kvm.get_emulated_cpuid(KVM_MAX_CPUID_ENTRIES).unwrap(); @@ -895,7 +895,7 @@ mod tests { cpuid_err.unwrap_err(); } - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] #[test] fn test_cpuid_clone() { let kvm = Kvm::new().unwrap(); @@ -910,7 +910,7 @@ mod tests { } #[test] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] fn get_msr_index_list() { let kvm = Kvm::new().unwrap(); let msr_list = kvm.get_msr_index_list().unwrap(); @@ -918,7 +918,7 @@ mod tests { } #[test] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] fn get_msr_feature_index_list() { let kvm = Kvm::new().unwrap(); let msr_feature_index_list = kvm.get_msr_feature_index_list().unwrap(); @@ -926,7 +926,7 @@ mod tests { } #[test] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] fn get_msrs() { use kvm_bindings::kvm_msr_entry; @@ -961,7 +961,7 @@ mod tests { ); assert_eq!(faulty_kvm.get_nr_vcpus(), 4); assert_eq!(faulty_kvm.get_nr_memslots(), 32); - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] { assert_eq!( faulty_kvm.get_emulated_cpuid(4).err().unwrap().errno(), diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 7958415..acc11d0 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -16,7 +16,7 @@ use crate::ioctls::{KvmCoalescedIoRing, KvmRunWrapper, Result}; use crate::kvm_ioctls::*; use vmm_sys_util::errno; use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ref}; -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] use vmm_sys_util::ioctl::{ioctl_with_mut_ptr, ioctl_with_ptr, ioctl_with_val}; /// Helper method to obtain the size of the register through its id @@ -195,7 +195,7 @@ pub struct VcpuFd { /// KVM Sync Registers used to tell KVM which registers to sync #[repr(u32)] #[derive(Debug, Copy, Clone)] -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] pub enum SyncReg { /// General purpose registers, Register = KVM_SYNC_X86_REGS, @@ -367,7 +367,7 @@ impl VcpuFd { /// let vcpu = vm.create_vcpu(0).unwrap(); /// let sregs = vcpu.get_sregs().unwrap(); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn get_sregs(&self) -> Result { let mut regs = kvm_sregs::default(); // SAFETY: Safe because we know that our file is a vCPU fd, we know the kernel will only @@ -401,7 +401,7 @@ impl VcpuFd { /// sregs.cs.selector = 0; /// vcpu.set_sregs(&sregs).unwrap(); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn set_sregs(&self, sregs: &kvm_sregs) -> Result<()> { // SAFETY: Safe because we know that our file is a vCPU fd, we know the kernel will only // read the correct amount of memory from our pointer, and we verify the return result. @@ -426,10 +426,10 @@ impl VcpuFd { /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// let vcpu = vm.create_vcpu(0).unwrap(); - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// #[cfg(target_arch = "x86_64")] /// let fpu = vcpu.get_fpu().unwrap(); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn get_fpu(&self) -> Result { let mut fpu = kvm_fpu::default(); // SAFETY: Here we trust the kernel not to read past the end of the kvm_fpu struct. @@ -457,7 +457,7 @@ impl VcpuFd { /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// let vcpu = vm.create_vcpu(0).unwrap(); - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// #[cfg(target_arch = "x86_64")] /// { /// let KVM_FPU_CWD: u16 = 0x37f; /// let fpu = kvm_fpu { @@ -467,7 +467,7 @@ impl VcpuFd { /// vcpu.set_fpu(&fpu).unwrap(); /// } /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn set_fpu(&self, fpu: &kvm_fpu) -> Result<()> { // SAFETY: Here we trust the kernel not to read past the end of the kvm_fpu struct. let ret = unsafe { ioctl_with_ref(self, KVM_SET_FPU(), fpu) }; @@ -512,7 +512,7 @@ impl VcpuFd { /// vcpu.set_cpuid2(&kvm_cpuid).unwrap(); /// ``` /// - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn set_cpuid2(&self, cpuid: &CpuId) -> Result<()> { // SAFETY: Here we trust the kernel not to read past the end of the kvm_cpuid2 struct. let ret = unsafe { ioctl_with_ptr(self, KVM_SET_CPUID2(), cpuid.as_fam_struct_ptr()) }; @@ -545,7 +545,7 @@ impl VcpuFd { /// let cpuid = vcpu.get_cpuid2(KVM_MAX_CPUID_ENTRIES).unwrap(); /// ``` /// - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn get_cpuid2(&self, num_entries: usize) -> Result { if num_entries > KVM_MAX_CPUID_ENTRIES { // Returns the same error the underlying `ioctl` would have sent. @@ -581,7 +581,7 @@ impl VcpuFd { /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// let mut cap: kvm_enable_cap = Default::default(); - /// if cfg!(target_arch = "x86") || cfg!(target_arch = "x86_64") { + /// if cfg!(target_arch = "x86_64") { /// // KVM_CAP_HYPERV_SYNIC needs KVM_CAP_SPLIT_IRQCHIP enabled /// cap.cap = KVM_CAP_SPLIT_IRQCHIP; /// cap.args[0] = 24; @@ -596,7 +596,7 @@ impl VcpuFd { /// } /// ``` /// - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn enable_cap(&self, cap: &kvm_enable_cap) -> Result<()> { // SAFETY: The ioctl is safe because we allocated the struct and we know the // kernel will write exactly the size of the struct. @@ -626,7 +626,7 @@ impl VcpuFd { /// let vcpu = vm.create_vcpu(0).unwrap(); /// let lapic = vcpu.get_lapic().unwrap(); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn get_lapic(&self) -> Result { let mut klapic = kvm_lapic_state::default(); @@ -671,7 +671,7 @@ impl VcpuFd { /// // Update the value of LAPIC. /// vcpu.set_lapic(&lapic).unwrap(); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn set_lapic(&self, klapic: &kvm_lapic_state) -> Result<()> { // SAFETY: The ioctl is safe because the kernel will only read from the klapic struct. let ret = unsafe { ioctl_with_ref(self, KVM_SET_LAPIC(), klapic) }; @@ -717,7 +717,7 @@ impl VcpuFd { /// let read = vcpu.get_msrs(&mut msrs).unwrap(); /// assert_eq!(read, 2); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn get_msrs(&self, msrs: &mut Msrs) -> Result { // SAFETY: Here we trust the kernel not to read past the end of the kvm_msrs struct. let ret = unsafe { ioctl_with_mut_ptr(self, KVM_GET_MSRS(), msrs.as_mut_fam_struct_ptr()) }; @@ -756,7 +756,7 @@ impl VcpuFd { /// let written = vcpu.set_msrs(&msrs).unwrap(); /// assert_eq!(written, 1); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn set_msrs(&self, msrs: &Msrs) -> Result { // SAFETY: Here we trust the kernel not to read past the end of the kvm_msrs struct. let ret = unsafe { ioctl_with_ptr(self, KVM_SET_MSRS(), msrs.as_fam_struct_ptr()) }; @@ -787,7 +787,6 @@ impl VcpuFd { /// let mp_state = vcpu.get_mp_state().unwrap(); /// ``` #[cfg(any( - target_arch = "x86", target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", @@ -826,7 +825,6 @@ impl VcpuFd { /// vcpu.set_mp_state(mp_state).unwrap(); /// ``` #[cfg(any( - target_arch = "x86", target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", @@ -861,7 +859,7 @@ impl VcpuFd { /// let vcpu = vm.create_vcpu(0).unwrap(); /// let xsave = vcpu.get_xsave().unwrap(); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn get_xsave(&self) -> Result { let mut xsave = Default::default(); // SAFETY: Here we trust the kernel not to read past the end of the kvm_xsave struct. @@ -893,7 +891,7 @@ impl VcpuFd { /// // Your `xsave` manipulation here. /// vcpu.set_xsave(&xsave).unwrap(); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn set_xsave(&self, xsave: &kvm_xsave) -> Result<()> { // SAFETY: Here we trust the kernel not to read past the end of the kvm_xsave struct. let ret = unsafe { ioctl_with_ref(self, KVM_SET_XSAVE(), xsave) }; @@ -922,7 +920,7 @@ impl VcpuFd { /// let vcpu = vm.create_vcpu(0).unwrap(); /// let xcrs = vcpu.get_xcrs().unwrap(); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn get_xcrs(&self) -> Result { let mut xcrs = Default::default(); // SAFETY: Here we trust the kernel not to read past the end of the kvm_xcrs struct. @@ -954,7 +952,7 @@ impl VcpuFd { /// // Your `xcrs` manipulation here. /// vcpu.set_xcrs(&xcrs).unwrap(); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn set_xcrs(&self, xcrs: &kvm_xcrs) -> Result<()> { // SAFETY: Here we trust the kernel not to read past the end of the kvm_xcrs struct. let ret = unsafe { ioctl_with_ref(self, KVM_SET_XCRS(), xcrs) }; @@ -983,7 +981,7 @@ impl VcpuFd { /// let vcpu = vm.create_vcpu(0).unwrap(); /// let debug_regs = vcpu.get_debug_regs().unwrap(); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn get_debug_regs(&self) -> Result { let mut debug_regs = Default::default(); // SAFETY: Here we trust the kernel not to read past the end of the kvm_debugregs struct. @@ -1015,7 +1013,7 @@ impl VcpuFd { /// // Your `debug_regs` manipulation here. /// vcpu.set_debug_regs(&debug_regs).unwrap(); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn set_debug_regs(&self, debug_regs: &kvm_debugregs) -> Result<()> { // SAFETY: Here we trust the kernel not to read past the end of the kvm_debugregs struct. let ret = unsafe { ioctl_with_ref(self, KVM_SET_DEBUGREGS(), debug_regs) }; @@ -1047,12 +1045,7 @@ impl VcpuFd { /// let vcpu_events = vcpu.get_vcpu_events().unwrap(); /// } /// ``` - #[cfg(any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64" - ))] + #[cfg(any(target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64"))] pub fn get_vcpu_events(&self) -> Result { let mut vcpu_events = Default::default(); // SAFETY: Here we trust the kernel not to read past the end of the kvm_vcpu_events struct. @@ -1086,12 +1079,7 @@ impl VcpuFd { /// vcpu.set_vcpu_events(&vcpu_events).unwrap(); /// } /// ``` - #[cfg(any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64" - ))] + #[cfg(any(target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64"))] pub fn set_vcpu_events(&self, vcpu_events: &kvm_vcpu_events) -> Result<()> { // SAFETY: Here we trust the kernel not to read past the end of the kvm_vcpu_events struct. let ret = unsafe { ioctl_with_ref(self, KVM_SET_VCPU_EVENTS(), vcpu_events) }; @@ -1260,7 +1248,7 @@ impl VcpuFd { /// let vm = kvm.create_vm().unwrap(); /// let vcpu = vm.create_vcpu(0).unwrap(); /// - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))] + /// #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] /// { /// let debug_struct = kvm_guest_debug { /// // Configure the vcpu so that a KVM_DEBUG_EXIT would be generated @@ -1275,7 +1263,6 @@ impl VcpuFd { /// } /// ``` #[cfg(any( - target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64", target_arch = "s390x", @@ -1359,7 +1346,7 @@ impl VcpuFd { /// /// See the documentation for `KVM_KVMCLOCK_CTRL` in the /// [KVM API documentation](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt). - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn kvmclock_ctrl(&self) -> Result<()> { // SAFETY: Safe because we know that our file is a KVM fd and that the request // is one of the ones defined by kernel. @@ -1628,7 +1615,7 @@ impl VcpuFd { /// let tsc_khz = vcpu.get_tsc_khz().unwrap(); /// ``` /// - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn get_tsc_khz(&self) -> Result { // SAFETY: Safe because we know that our file is a KVM fd and that the request is one of // the ones defined by kernel. @@ -1660,7 +1647,7 @@ impl VcpuFd { /// } /// ``` /// - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn set_tsc_khz(&self, freq: u32) -> Result<()> { // SAFETY: Safe because we know that our file is a KVM fd and that the request is one of // the ones defined by kernel. @@ -1690,10 +1677,10 @@ impl VcpuFd { /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// let vcpu = vm.create_vcpu(0).unwrap(); - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// #[cfg(target_arch = "x86_64")] /// let tr = vcpu.translate_gva(0x10000).unwrap(); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn translate_gva(&self, gva: u64) -> Result { let mut tr = kvm_translation { linear_address: gva, @@ -1727,7 +1714,7 @@ impl VcpuFd { /// vcpu.set_sync_valid_reg(SyncReg::SystemRegister); /// vcpu.set_sync_valid_reg(SyncReg::VcpuEvents); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn set_sync_valid_reg(&mut self, reg: SyncReg) { let kvm_run: &mut kvm_run = self.kvm_run_ptr.as_mut_ref(); kvm_run.kvm_valid_regs |= reg as u64; @@ -1749,7 +1736,7 @@ impl VcpuFd { /// let mut vcpu = vm.create_vcpu(0).unwrap(); /// vcpu.set_sync_dirty_reg(SyncReg::Register); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn set_sync_dirty_reg(&mut self, reg: SyncReg) { let kvm_run: &mut kvm_run = self.kvm_run_ptr.as_mut_ref(); kvm_run.kvm_dirty_regs |= reg as u64; @@ -1771,7 +1758,7 @@ impl VcpuFd { /// let mut vcpu = vm.create_vcpu(0).unwrap(); /// vcpu.clear_sync_valid_reg(SyncReg::Register); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn clear_sync_valid_reg(&mut self, reg: SyncReg) { let kvm_run: &mut kvm_run = self.kvm_run_ptr.as_mut_ref(); kvm_run.kvm_valid_regs &= !(reg as u64); @@ -1793,7 +1780,7 @@ impl VcpuFd { /// let mut vcpu = vm.create_vcpu(0).unwrap(); /// vcpu.clear_sync_dirty_reg(SyncReg::Register); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn clear_sync_dirty_reg(&mut self, reg: SyncReg) { let kvm_run: &mut kvm_run = self.kvm_run_ptr.as_mut_ref(); kvm_run.kvm_dirty_regs &= !(reg as u64); @@ -1815,7 +1802,7 @@ impl VcpuFd { /// let guest_rax = vcpu.sync_regs().regs.rax; /// } /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn sync_regs(&self) -> kvm_sync_regs { let kvm_run = self.kvm_run_ptr.as_ref(); @@ -1844,7 +1831,7 @@ impl VcpuFd { /// vcpu.run(); /// } /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn sync_regs_mut(&mut self) -> &mut kvm_sync_regs { let kvm_run: &mut kvm_run = self.kvm_run_ptr.as_mut_ref(); @@ -1867,7 +1854,7 @@ impl VcpuFd { /// vcpu.smi().unwrap(); /// } /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn smi(&self) -> Result<()> { // SAFETY: Safe because we call this with a Vcpu fd and we trust the kernel. let ret = unsafe { ioctl(self, KVM_SMI()) }; @@ -1893,7 +1880,7 @@ impl VcpuFd { /// vcpu.nmi().unwrap(); /// } /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn nmi(&self) -> Result<()> { // SAFETY: Safe because we call this with a Vcpu fd and we trust the kernel. let ret = unsafe { ioctl(self, KVM_NMI()) }; @@ -1978,12 +1965,7 @@ mod tests { extern crate byteorder; use super::*; - #[cfg(any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64" - ))] + #[cfg(any(target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64"))] use crate::cap::Cap; use crate::ioctls::system::Kvm; use std::ptr::NonNull; @@ -2229,7 +2211,6 @@ mod tests { } #[cfg(any( - target_arch = "x86", target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", @@ -2247,7 +2228,7 @@ mod tests { assert_eq!(mp_state, other_mp_state); } - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] #[test] fn xsave_test() { let kvm = Kvm::new().unwrap(); @@ -2259,7 +2240,7 @@ mod tests { assert_eq!(&xsave.region[..], &other_xsave.region[..]); } - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] #[test] fn xcrs_test() { let kvm = Kvm::new().unwrap(); @@ -2271,7 +2252,7 @@ mod tests { assert_eq!(xcrs, other_xcrs); } - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] #[test] fn debugregs_test() { let kvm = Kvm::new().unwrap(); @@ -2283,12 +2264,7 @@ mod tests { assert_eq!(debugregs, other_debugregs); } - #[cfg(any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64" - ))] + #[cfg(any(target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64"))] #[test] fn vcpu_events_test() { let kvm = Kvm::new().unwrap(); @@ -2629,7 +2605,6 @@ mod tests { #[test] #[cfg(any( - target_arch = "x86", target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", @@ -2660,22 +2635,12 @@ mod tests { .errno(), badf_errno ); - #[cfg(any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64" - ))] + #[cfg(any(target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64"))] assert_eq!( faulty_vcpu_fd.get_vcpu_events().unwrap_err().errno(), badf_errno ); - #[cfg(any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64" - ))] + #[cfg(any(target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64"))] assert_eq!( faulty_vcpu_fd .set_vcpu_events(&kvm_vcpu_events::default()) @@ -3397,7 +3362,7 @@ mod tests { vcpu.vcpu_init(&kvi).unwrap(); } - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] #[test] fn test_userspace_rdmsr_exit() { use std::io::Write; @@ -3466,7 +3431,7 @@ mod tests { } } - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] #[test] fn test_userspace_hypercall_exit() { use std::io::Write; @@ -3563,7 +3528,7 @@ mod tests { } } - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] #[test] fn test_userspace_wrmsr_exit() { use std::io::Write; diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index fa747d1..c8c11f4 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -22,14 +22,9 @@ use crate::ioctls::{KvmRunWrapper, Result}; use crate::kvm_ioctls::*; use vmm_sys_util::errno; use vmm_sys_util::eventfd::EventFd; -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] use vmm_sys_util::ioctl::ioctl_with_mut_ptr; -#[cfg(any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64" -))] +#[cfg(any(target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64"))] use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref}; use vmm_sys_util::ioctl::{ioctl_with_ref, ioctl_with_val}; @@ -229,7 +224,7 @@ impl VmFd { /// let vm = kvm.create_vm().unwrap(); /// vm.set_tss_address(0xfffb_d000).unwrap(); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn set_tss_address(&self, offset: usize) -> Result<()> { // SAFETY: Safe because we know that our file is a VM fd and we verify the return result. let ret = unsafe { ioctl_with_val(self, KVM_SET_TSS_ADDR(), offset as c_ulong) }; @@ -257,7 +252,7 @@ impl VmFd { /// let vm = kvm.create_vm().unwrap(); /// vm.set_identity_map_address(0xfffb_c000).unwrap(); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn set_identity_map_address(&self, address: u64) -> Result<()> { // SAFETY: Safe because we know that our file is a VM fd and we verify the return result. let ret = unsafe { ioctl_with_ref(self, KVM_SET_IDENTITY_MAP_ADDR(), &address) }; @@ -281,7 +276,7 @@ impl VmFd { /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// #[cfg(target_arch = "x86_64")] /// vm.create_irq_chip().unwrap(); /// #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] /// { @@ -298,12 +293,7 @@ impl VmFd { /// } /// } /// ``` - #[cfg(any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64" - ))] + #[cfg(any(target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64"))] pub fn create_irq_chip(&self) -> Result<()> { // SAFETY: Safe because we know that our file is a VM fd and we verify the return result. let ret = unsafe { ioctl(self, KVM_CREATE_IRQCHIP()) }; @@ -338,7 +328,7 @@ impl VmFd { /// irqchip.chip_id = KVM_IRQCHIP_PIC_MASTER; /// vm.get_irqchip(&mut irqchip).unwrap(); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn get_irqchip(&self, irqchip: &mut kvm_irqchip) -> Result<()> { // SAFETY: Here we trust the kernel not to read past the end of the kvm_irqchip struct. let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_IRQCHIP(), irqchip) }; @@ -374,7 +364,7 @@ impl VmFd { /// // Your `irqchip` manipulation here. /// vm.set_irqchip(&mut irqchip).unwrap(); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn set_irqchip(&self, irqchip: &kvm_irqchip) -> Result<()> { // SAFETY: Here we trust the kernel not to read past the end of the kvm_irqchip struct. let ret = unsafe { ioctl_with_ref(self, KVM_SET_IRQCHIP(), irqchip) }; @@ -405,7 +395,7 @@ impl VmFd { /// let pit_config = kvm_pit_config::default(); /// vm.create_pit2(pit_config).unwrap(); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn create_pit2(&self, pit_config: kvm_pit_config) -> Result<()> { // SAFETY: Safe because we know that our file is a VM fd, we know the kernel will only read // the correct amount of memory from our pointer, and we verify the return result. @@ -441,7 +431,7 @@ impl VmFd { /// vm.create_pit2(pit_config).unwrap(); /// let pitstate = vm.get_pit2().unwrap(); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn get_pit2(&self) -> Result { let mut pitstate = Default::default(); // SAFETY: Here we trust the kernel not to read past the end of the kvm_pit_state2 struct. @@ -479,7 +469,7 @@ impl VmFd { /// // Your `pitstate` manipulation here. /// vm.set_pit2(&mut pitstate).unwrap(); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn set_pit2(&self, pitstate: &kvm_pit_state2) -> Result<()> { // SAFETY: Here we trust the kernel not to read past the end of the kvm_pit_state2 struct. let ret = unsafe { ioctl_with_ref(self, KVM_SET_PIT2(), pitstate) }; @@ -508,7 +498,7 @@ impl VmFd { /// let vm = kvm.create_vm().unwrap(); /// let clock = vm.get_clock().unwrap(); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn get_clock(&self) -> Result { let mut clock = Default::default(); // SAFETY: Here we trust the kernel not to read past the end of the kvm_clock_data struct. @@ -541,7 +531,7 @@ impl VmFd { /// let mut clock = kvm_clock_data::default(); /// vm.set_clock(&mut clock).unwrap(); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn set_clock(&self, clock: &kvm_clock_data) -> Result<()> { // SAFETY: Here we trust the kernel not to read past the end of the kvm_clock_data struct. let ret = unsafe { ioctl_with_ref(self, KVM_SET_CLOCK(), clock) }; @@ -582,12 +572,11 @@ impl VmFd { /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// let msi = kvm_msi::default(); - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// #[cfg(target_arch = "x86_64")] /// vm.create_irq_chip().unwrap(); /// //vm.signal_msi(msi).unwrap(); /// ``` #[cfg(any( - target_arch = "x86", target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", @@ -629,7 +618,7 @@ impl VmFd { /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// #[cfg(target_arch = "x86_64")] /// vm.create_irq_chip().unwrap(); /// /// #[cfg(target_arch = "riscv64")] @@ -644,7 +633,6 @@ impl VmFd { /// vm.set_gsi_routing(&irq_routing).unwrap(); /// ``` #[cfg(any( - target_arch = "x86", target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", @@ -853,7 +841,7 @@ impl VmFd { /// }; /// unsafe { vm.set_user_memory_region(mem_region).unwrap() }; /// - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// #[cfg(target_arch = "x86_64")] /// // ASM code that just forces a MMIO Write. /// let asm_code = [0xc6, 0x06, 0x00, 0x80, 0x00]; /// #[cfg(target_arch = "aarch64")] @@ -880,7 +868,7 @@ impl VmFd { /// /// let mut vcpu_fd = vm.create_vcpu(0).unwrap(); /// - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// #[cfg(target_arch = "x86_64")] /// { /// // x86_64 specific registry setup. /// let mut vcpu_sregs = vcpu_fd.get_sregs().unwrap(); @@ -986,14 +974,13 @@ impl VmFd { /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// let evtfd = EventFd::new(EFD_NONBLOCK).unwrap(); - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// #[cfg(target_arch = "x86_64")] /// { /// vm.create_irq_chip().unwrap(); /// vm.register_irqfd(&evtfd, 0).unwrap(); /// } /// ``` #[cfg(any( - target_arch = "x86", target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", @@ -1038,7 +1025,7 @@ impl VmFd { /// let vm = kvm.create_vm().unwrap(); /// let evtfd = EventFd::new(EFD_NONBLOCK).unwrap(); /// let resamplefd = EventFd::new(EFD_NONBLOCK).unwrap(); - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// #[cfg(target_arch = "x86_64")] /// { /// vm.create_irq_chip().unwrap(); /// vm.register_irqfd_with_resample(&evtfd, &resamplefd, 0) @@ -1046,7 +1033,6 @@ impl VmFd { /// } /// ``` #[cfg(any( - target_arch = "x86", target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", @@ -1095,7 +1081,7 @@ impl VmFd { /// let vm = kvm.create_vm().unwrap(); /// let evtfd = EventFd::new(EFD_NONBLOCK).unwrap(); /// let resamplefd = EventFd::new(EFD_NONBLOCK).unwrap(); - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// #[cfg(target_arch = "x86_64")] /// { /// vm.create_irq_chip().unwrap(); /// vm.register_irqfd(&evtfd, 0).unwrap(); @@ -1106,7 +1092,6 @@ impl VmFd { /// } /// ``` #[cfg(any( - target_arch = "x86", target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", @@ -1152,7 +1137,7 @@ impl VmFd { /// fn arch_setup(vm_fd: &VmFd) { /// // Arch-specific setup: /// // For x86 architectures, it simply means calling vm.create_irq_chip().unwrap(). - /// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// # #[cfg(target_arch = "x86_64")] /// # vm_fd.create_irq_chip().unwrap(); /// // For Arm architectures, the IRQ controllers need to be setup first. /// // Details please refer to the kernel documentation. @@ -1166,7 +1151,7 @@ impl VmFd { /// let kvm = Kvm::new().unwrap(); /// let vm = kvm.create_vm().unwrap(); /// arch_setup(&vm); - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// #[cfg(target_arch = "x86_64")] /// { /// vm.set_irq_line(4, true); /// // ... @@ -1178,7 +1163,6 @@ impl VmFd { /// } /// ``` #[cfg(any( - target_arch = "x86", target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", @@ -1302,7 +1286,7 @@ impl VmFd { /// // whether the device type is supported. This will not create the device. /// // To create the device the flag needs to be removed. /// let mut device = kvm_bindings::kvm_create_device { - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// #[cfg(target_arch = "x86_64")] /// type_: kvm_device_type_KVM_DEV_TYPE_VFIO, /// #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] /// type_: kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, @@ -1314,7 +1298,7 @@ impl VmFd { /// // On ARM, creating VGICv3 may fail due to hardware dependency. /// // Retry to create VGICv2 in that case. /// let device_fd = vm.create_device(&mut device).unwrap_or_else(|_| { - /// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// #[cfg(target_arch = "x86_64")] /// panic!("Cannot create VFIO device."); /// #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] /// { @@ -1397,7 +1381,7 @@ impl VmFd { /// let mut cap: kvm_enable_cap = Default::default(); /// // This example cannot enable an arm/aarch64 capability since there /// // is no capability available for these architectures. - /// if cfg!(target_arch = "x86") || cfg!(target_arch = "x86_64") { + /// if cfg!(target_arch = "x86_64") { /// cap.cap = KVM_CAP_SPLIT_IRQCHIP; /// // As per the KVM documentation, KVM_CAP_SPLIT_IRQCHIP only emulates /// // the local APIC in kernel, expecting that a userspace IOAPIC will @@ -1650,7 +1634,7 @@ impl VmFd { /// let mut init: kvm_sev_cmd = Default::default(); /// unsafe { vm.encrypt_op(&mut init).unwrap() }; /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub unsafe fn encrypt_op(&self, op: *mut T) -> Result<()> { let ret = ioctl_with_mut_ptr(self, KVM_MEMORY_ENCRYPT_OP(), op); if ret == 0 { @@ -1692,7 +1676,7 @@ impl VmFd { /// let mut init: kvm_sev_cmd = Default::default(); /// vm.encrypt_op_sev(&mut init).unwrap(); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn encrypt_op_sev(&self, op: &mut kvm_sev_cmd) -> Result<()> { // SAFETY: Safe because we know that kernel will only read the correct amount of memory // from our pointer and we know where it will write it (op.error). @@ -1762,7 +1746,7 @@ impl VmFd { /// }; /// vm.register_enc_memory_region(&memory_region).unwrap(); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn register_enc_memory_region(&self, memory_region: &kvm_enc_region) -> Result<()> { // SAFETY: Safe because we know that our file is a VM fd, we know the kernel will only read // the correct amount of memory from our pointer, and we verify the return result. @@ -1839,7 +1823,7 @@ impl VmFd { /// vm.register_enc_memory_region(&memory_region).unwrap(); /// vm.unregister_enc_memory_region(&memory_region).unwrap(); /// ``` - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] pub fn unregister_enc_memory_region(&self, memory_region: &kvm_enc_region) -> Result<()> { // SAFETY: Safe because we know that our file is a VM fd, we know the kernel will only read // the correct amount of memory from our pointer, and we verify the return result. @@ -2051,7 +2035,7 @@ mod tests { use super::*; use crate::Kvm; - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] use std::{fs::OpenOptions, os::fd::IntoRawFd, ptr::null_mut}; use libc::EFD_NONBLOCK; @@ -2089,7 +2073,7 @@ mod tests { } #[test] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] fn test_set_tss_address() { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); @@ -2097,7 +2081,7 @@ mod tests { } #[test] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] fn test_set_identity_map_address() { let kvm = Kvm::new().unwrap(); if kvm.check_extension(Cap::SetIdentityMapAddr) { @@ -2110,7 +2094,7 @@ mod tests { } #[test] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] fn test_irq_chip() { use Cap; @@ -2162,7 +2146,7 @@ mod tests { } #[test] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] fn test_pit2() { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); @@ -2181,7 +2165,7 @@ mod tests { assert_eq!(pit2, other_pit2); } - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] #[test] fn test_clock() { let kvm = Kvm::new().unwrap(); @@ -2269,7 +2253,7 @@ mod tests { } #[test] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] fn test_register_unregister_irqfd() { let kvm = Kvm::new().unwrap(); let vm_fd = kvm.create_vm().unwrap(); @@ -2416,7 +2400,7 @@ mod tests { } #[test] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] fn test_set_irq_line() { let kvm = Kvm::new().unwrap(); let vm_fd = kvm.create_vm().unwrap(); @@ -2503,7 +2487,7 @@ mod tests { } #[test] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] fn test_faulty_vm_fd() { let badf_errno = libc::EBADF; @@ -2619,7 +2603,6 @@ mod tests { /// previously allocated from the guest itself. #[test] #[cfg(any( - target_arch = "x86", target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", @@ -2644,7 +2627,7 @@ mod tests { } #[test] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] fn test_enable_split_irqchip_cap() { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); @@ -2669,7 +2652,6 @@ mod tests { #[test] #[cfg(any( - target_arch = "x86", target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", @@ -2681,9 +2663,9 @@ mod tests { let irq_routing = kvm_irq_routing::default(); // Expect failure for x86 since the irqchip is not created yet. - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] vm.set_gsi_routing(&irq_routing).unwrap_err(); - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] vm.create_irq_chip().unwrap(); // RISC-V 64-bit expect an AIA device to be created in advance of @@ -2719,7 +2701,7 @@ mod tests { } #[test] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] #[cfg_attr(not(has_sev), ignore)] fn test_encrypt_op_sev() { let kvm = Kvm::new().unwrap(); @@ -2730,7 +2712,7 @@ mod tests { } #[test] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[cfg(target_arch = "x86_64")] #[cfg_attr(not(has_sev), ignore)] fn test_register_unregister_enc_memory_region() { let sev = OpenOptions::new() diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index 9b8b6d5..402f5de 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -13,18 +13,18 @@ use kvm_bindings::*; ioctl_io_nr!(KVM_GET_API_VERSION, KVMIO, 0x00); ioctl_io_nr!(KVM_CREATE_VM, KVMIO, 0x01); -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_iowr_nr!(KVM_GET_MSR_INDEX_LIST, KVMIO, 0x02, kvm_msr_list); ioctl_io_nr!(KVM_CHECK_EXTENSION, KVMIO, 0x03); ioctl_io_nr!(KVM_GET_VCPU_MMAP_SIZE, KVMIO, 0x04); /* Available with KVM_CAP_EXT_CPUID */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_iowr_nr!(KVM_GET_SUPPORTED_CPUID, KVMIO, 0x05, kvm_cpuid2); /* Available with KVM_CAP_EXT_EMUL_CPUID */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_iowr_nr!(KVM_GET_EMULATED_CPUID, KVMIO, 0x09, kvm_cpuid2); /* Available with KVM_CAP_GET_MSR_FEATURES */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_iowr_nr!(KVM_GET_MSR_FEATURE_INDEX_LIST, KVMIO, 0x0a, kvm_msr_list); // Ioctls for VM fds. @@ -45,16 +45,15 @@ ioctl_iow_nr!( kvm_userspace_memory_region2 ); /* Available with KVM_CAP_SET_TSS_ADDR */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_io_nr!(KVM_SET_TSS_ADDR, KVMIO, 0x47); /* Available with KVM_CAP_SET_IDENTITY_MAP_ADDR */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_iow_nr!(KVM_SET_IDENTITY_MAP_ADDR, KVMIO, 0x48, u64); #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] ioctl_iowr_nr!(KVM_CREATE_GUEST_MEMFD, KVMIO, 0xd4, kvm_create_guest_memfd); /* Available with KVM_CAP_IRQCHIP */ #[cfg(any( - target_arch = "x86", target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", @@ -63,7 +62,6 @@ ioctl_iowr_nr!(KVM_CREATE_GUEST_MEMFD, KVMIO, 0xd4, kvm_create_guest_memfd); ioctl_io_nr!(KVM_CREATE_IRQCHIP, KVMIO, 0x60); /* Available with KVM_CAP_IRQCHIP */ #[cfg(any( - target_arch = "x86", target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", @@ -86,7 +84,6 @@ ioctl_iow_nr!( ); /* Available with KVM_CAP_IRQ_ROUTING */ #[cfg(any( - target_arch = "x86", target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", @@ -95,7 +92,6 @@ ioctl_iow_nr!( ioctl_iow_nr!(KVM_SET_GSI_ROUTING, KVMIO, 0x6a, kvm_irq_routing); /* Available with KVM_CAP_IRQFD */ #[cfg(any( - target_arch = "x86", target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", @@ -104,36 +100,36 @@ ioctl_iow_nr!(KVM_SET_GSI_ROUTING, KVMIO, 0x6a, kvm_irq_routing); ))] ioctl_iow_nr!(KVM_IRQFD, KVMIO, 0x76, kvm_irqfd); /* Available with KVM_CAP_PIT2 */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_iow_nr!(KVM_CREATE_PIT2, KVMIO, 0x77, kvm_pit_config); /* Available with KVM_CAP_IOEVENTFD */ ioctl_iow_nr!(KVM_IOEVENTFD, KVMIO, 0x79, kvm_ioeventfd); /* Available with KVM_CAP_IRQCHIP */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_iowr_nr!(KVM_GET_IRQCHIP, KVMIO, 0x62, kvm_irqchip); /* Available with KVM_CAP_IRQCHIP */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_ior_nr!(KVM_SET_IRQCHIP, KVMIO, 0x63, kvm_irqchip); /* Available with KVM_CAP_ADJUST_CLOCK */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_iow_nr!(KVM_SET_CLOCK, KVMIO, 0x7b, kvm_clock_data); /* Available with KVM_CAP_ADJUST_CLOCK */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_ior_nr!(KVM_GET_CLOCK, KVMIO, 0x7c, kvm_clock_data); /* Available with KVM_CAP_PIT_STATE2 */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_ior_nr!(KVM_GET_PIT2, KVMIO, 0x9f, kvm_pit_state2); /* Available with KVM_CAP_PIT_STATE2 */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_iow_nr!(KVM_SET_PIT2, KVMIO, 0xa0, kvm_pit_state2); /* KVM_MEMORY_ENCRYPT_OP. Takes opaque platform dependent type: i.e. TDX or SEV */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_iowr_nr!(KVM_MEMORY_ENCRYPT_OP, KVMIO, 0xba, std::os::raw::c_ulong); /* Available on SEV-enabled guests. */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_ior_nr!(KVM_MEMORY_ENCRYPT_REG_REGION, KVMIO, 0xbb, kvm_enc_region); /* Available on SEV-enabled guests. */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_ior_nr!(KVM_MEMORY_ENCRYPT_UNREG_REGION, KVMIO, 0xbc, kvm_enc_region); // Ioctls for VCPU fds. @@ -144,44 +140,41 @@ ioctl_ior_nr!(KVM_GET_REGS, KVMIO, 0x81, kvm_regs); #[cfg(not(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64")))] ioctl_iow_nr!(KVM_SET_REGS, KVMIO, 0x82, kvm_regs); #[cfg(any( - target_arch = "x86", target_arch = "x86_64", target_arch = "powerpc", target_arch = "powerpc64" ))] ioctl_ior_nr!(KVM_GET_SREGS, KVMIO, 0x83, kvm_sregs); #[cfg(any( - target_arch = "x86", target_arch = "x86_64", target_arch = "powerpc", target_arch = "powerpc64" ))] ioctl_iow_nr!(KVM_SET_SREGS, KVMIO, 0x84, kvm_sregs); -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_iowr_nr!(KVM_TRANSLATE, KVMIO, 0x85, kvm_translation); -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_iowr_nr!(KVM_GET_MSRS, KVMIO, 0x88, kvm_msrs); -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_iow_nr!(KVM_SET_MSRS, KVMIO, 0x89, kvm_msrs); -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_ior_nr!(KVM_GET_FPU, KVMIO, 0x8c, kvm_fpu); -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_iow_nr!(KVM_SET_FPU, KVMIO, 0x8d, kvm_fpu); /* Available with KVM_CAP_IRQCHIP */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_ior_nr!(KVM_GET_LAPIC, KVMIO, 0x8e, kvm_lapic_state); /* Available with KVM_CAP_IRQCHIP */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_iow_nr!(KVM_SET_LAPIC, KVMIO, 0x8f, kvm_lapic_state); /* Available with KVM_CAP_EXT_CPUID */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_iow_nr!(KVM_SET_CPUID2, KVMIO, 0x90, kvm_cpuid2); /* Available with KVM_CAP_EXT_CPUID */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_iowr_nr!(KVM_GET_CPUID2, KVMIO, 0x91, kvm_cpuid2); /* Available with KVM_CAP_MP_STATE */ #[cfg(any( - target_arch = "x86", target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", @@ -191,7 +184,6 @@ ioctl_iowr_nr!(KVM_GET_CPUID2, KVMIO, 0x91, kvm_cpuid2); ioctl_ior_nr!(KVM_GET_MP_STATE, KVMIO, 0x98, kvm_mp_state); /* Available with KVM_CAP_MP_STATE */ #[cfg(any( - target_arch = "x86", target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", @@ -200,51 +192,41 @@ ioctl_ior_nr!(KVM_GET_MP_STATE, KVMIO, 0x98, kvm_mp_state); ))] ioctl_iow_nr!(KVM_SET_MP_STATE, KVMIO, 0x99, kvm_mp_state); /* Available with KVM_CAP_USER_NMI */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_io_nr!(KVM_NMI, KVMIO, 0x9a); /* Available with KVM_CAP_VCPU_EVENTS */ -#[cfg(any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64" -))] +#[cfg(any(target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64"))] ioctl_ior_nr!(KVM_GET_VCPU_EVENTS, KVMIO, 0x9f, kvm_vcpu_events); /* Available with KVM_CAP_VCPU_EVENTS */ -#[cfg(any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64" -))] +#[cfg(any(target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64"))] ioctl_iow_nr!(KVM_SET_VCPU_EVENTS, KVMIO, 0xa0, kvm_vcpu_events); /* Available with KVM_CAP_DEBUGREGS */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_ior_nr!(KVM_GET_DEBUGREGS, KVMIO, 0xa1, kvm_debugregs); /* Available with KVM_CAP_DEBUGREGS */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_iow_nr!(KVM_SET_DEBUGREGS, KVMIO, 0xa2, kvm_debugregs); /* Available with KVM_CAP_XSAVE */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_ior_nr!(KVM_GET_XSAVE, KVMIO, 0xa4, kvm_xsave); /* Available with KVM_CAP_XSAVE */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_iow_nr!(KVM_SET_XSAVE, KVMIO, 0xa5, kvm_xsave); /* Available with KVM_CAP_XCRS */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_ior_nr!(KVM_GET_XCRS, KVMIO, 0xa6, kvm_xcrs); /* Available with KVM_CAP_XCRS */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_iow_nr!(KVM_SET_XCRS, KVMIO, 0xa7, kvm_xcrs); /* Available with KVM_CAP_KVMCLOCK_CTRL */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_io_nr!(KVM_KVMCLOCK_CTRL, KVMIO, 0xad); /* Available with KVM_CAP_TSC_CONTROL */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_io_nr!(KVM_SET_TSC_KHZ, KVMIO, 0xa2); /* Available with KVM_CAP_GET_TSC_KHZ */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_io_nr!(KVM_GET_TSC_KHZ, KVMIO, 0xa3); /* Available with KVM_CAP_ENABLE_CAP */ @@ -252,7 +234,6 @@ ioctl_io_nr!(KVM_GET_TSC_KHZ, KVMIO, 0xa3); ioctl_iow_nr!(KVM_ENABLE_CAP, KVMIO, 0xa3, kvm_enable_cap); /* Available with KVM_CAP_SIGNAL_MSI */ #[cfg(any( - target_arch = "x86", target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64", @@ -272,7 +253,7 @@ ioctl_ior_nr!(KVM_ARM_PREFERRED_TARGET, KVMIO, 0xaf, kvm_vcpu_init); ioctl_iowr_nr!(KVM_GET_REG_LIST, KVMIO, 0xb0, kvm_reg_list); /* Available with KVM_CAP_X86_SMM */ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] ioctl_io_nr!(KVM_SMI, KVMIO, 0xb7); /* Available with KVM_CAP_ARM_SVE */ diff --git a/src/lib.rs b/src/lib.rs index aeead3a..2c5577e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -73,7 +73,7 @@ //! let asm_code: &[u8]; //! //! // Setting up architectural dependent values. -//! #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +//! #[cfg(target_arch = "x86_64")] //! { //! asm_code = &[ //! 0xba, 0xf8, 0x03, /* mov $0x3f8, %dx */ @@ -147,7 +147,7 @@ //! let mut vcpu_fd = vm.create_vcpu(0).unwrap(); //! //! // 5. Initialize general purpose and special registers. -//! #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +//! #[cfg(target_arch = "x86_64")] //! { //! // x86_64 specific registry setup. //! let mut vcpu_sregs = vcpu_fd.get_sregs().unwrap(); @@ -248,7 +248,7 @@ pub use ioctls::system::Kvm; pub use ioctls::vcpu::reg_size; pub use ioctls::vcpu::{HypercallExit, VcpuExit, VcpuFd}; -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[cfg(target_arch = "x86_64")] pub use ioctls::vcpu::{MsrExitReason, ReadMsrExit, SyncReg, WriteMsrExit}; pub use ioctls::vm::{IoEventAddress, NoDatamatch, VmFd}; @@ -257,7 +257,7 @@ pub use ioctls::vm::{IoEventAddress, NoDatamatch, VmFd}; /// # Example /// /// ``` -/// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +/// #[cfg(target_arch = "x86_64")] /// use kvm_ioctls::{Error, KvmRunWrapper}; /// ``` pub use ioctls::KvmRunWrapper; From 86231e360705ce746709b3fe091249f2c0d5b345 Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Thu, 17 Oct 2024 20:58:21 +0800 Subject: [PATCH 324/332] arm: Drop arm architecture support As @roypat pointed out: KVM on ARM32 hosts were dropped since v5.7 [1], dropping `target_arch = "arm"` predicates to stop supporting ARM 32-bit architecture. [1] https://www.phoronix.com/news/Linux-5.7-Kill-32-bit-ARM-KVM Signed-off-by: Ruoqing He --- src/cap.rs | 13 ++++--------- src/ioctls/vcpu.rs | 40 ++++++++++++++++++---------------------- src/ioctls/vm.rs | 38 +++++++++++++++----------------------- src/kvm_ioctls.rs | 33 +++++++++++---------------------- src/lib.rs | 2 +- 5 files changed, 49 insertions(+), 77 deletions(-) diff --git a/src/cap.rs b/src/cap.rs index 2bec3ce..617c2a9 100644 --- a/src/cap.rs +++ b/src/cap.rs @@ -41,12 +41,7 @@ pub enum Cap { Iommu = KVM_CAP_IOMMU, DestroyMemoryRegionWorks = KVM_CAP_DESTROY_MEMORY_REGION_WORKS, UserNmi = KVM_CAP_USER_NMI, - #[cfg(any( - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64", - target_arch = "s390x" - ))] + #[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "s390x"))] SetGuestDebug = KVM_CAP_SET_GUEST_DEBUG, #[cfg(target_arch = "x86_64")] ReinjectControl = KVM_CAP_REINJECT_CONTROL, @@ -68,7 +63,7 @@ pub enum Cap { XenHvm = KVM_CAP_XEN_HVM, AdjustClock = KVM_CAP_ADJUST_CLOCK, InternalErrorData = KVM_CAP_INTERNAL_ERROR_DATA, - #[cfg(any(target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64"))] + #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] VcpuEvents = KVM_CAP_VCPU_EVENTS, S390Psw = KVM_CAP_S390_PSW, PpcSegstate = KVM_CAP_PPC_SEGSTATE, @@ -155,9 +150,9 @@ pub enum Cap { CoalescedPio = KVM_CAP_COALESCED_PIO, #[cfg(target_arch = "aarch64")] ArmSve = KVM_CAP_ARM_SVE, - #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + #[cfg(target_arch = "aarch64")] ArmPtrAuthAddress = KVM_CAP_ARM_PTRAUTH_ADDRESS, - #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + #[cfg(target_arch = "aarch64")] ArmPtrAuthGeneric = KVM_CAP_ARM_PTRAUTH_GENERIC, #[cfg(target_arch = "x86_64")] X86UserSpaceMsr = KVM_CAP_X86_USER_SPACE_MSR, diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index acc11d0..536d59f 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -20,7 +20,7 @@ use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ref}; use vmm_sys_util::ioctl::{ioctl_with_mut_ptr, ioctl_with_ptr, ioctl_with_val}; /// Helper method to obtain the size of the register through its id -#[cfg(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64"))] +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] pub fn reg_size(reg_id: u64) -> usize { 2_usize.pow(((reg_id & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT) as u32) } @@ -224,7 +224,7 @@ impl VcpuFd { /// let vcpu = vm.create_vcpu(0).unwrap(); /// let regs = vcpu.get_regs().unwrap(); /// ``` - #[cfg(not(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64")))] + #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] pub fn get_regs(&self) -> Result { let mut regs = kvm_regs::default(); // SAFETY: Safe because we know that our file is a vCPU fd, we know the kernel will only @@ -340,7 +340,7 @@ impl VcpuFd { /// regs.rip = 0x100; /// vcpu.set_regs(®s).unwrap(); /// ``` - #[cfg(not(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64")))] + #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] pub fn set_regs(&self, regs: &kvm_regs) -> Result<()> { // SAFETY: Safe because we know that our file is a vCPU fd, we know the kernel will only // read the correct amount of memory from our pointer, and we verify the return result. @@ -788,7 +788,6 @@ impl VcpuFd { /// ``` #[cfg(any( target_arch = "x86_64", - target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64", target_arch = "s390x" @@ -826,7 +825,6 @@ impl VcpuFd { /// ``` #[cfg(any( target_arch = "x86_64", - target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64", target_arch = "s390x" @@ -1045,7 +1043,7 @@ impl VcpuFd { /// let vcpu_events = vcpu.get_vcpu_events().unwrap(); /// } /// ``` - #[cfg(any(target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64"))] + #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] pub fn get_vcpu_events(&self) -> Result { let mut vcpu_events = Default::default(); // SAFETY: Here we trust the kernel not to read past the end of the kvm_vcpu_events struct. @@ -1079,7 +1077,7 @@ impl VcpuFd { /// vcpu.set_vcpu_events(&vcpu_events).unwrap(); /// } /// ``` - #[cfg(any(target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64"))] + #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] pub fn set_vcpu_events(&self, vcpu_events: &kvm_vcpu_events) -> Result<()> { // SAFETY: Here we trust the kernel not to read past the end of the kvm_vcpu_events struct. let ret = unsafe { ioctl_with_ref(self, KVM_SET_VCPU_EVENTS(), vcpu_events) }; @@ -1115,7 +1113,7 @@ impl VcpuFd { /// vm.get_preferred_target(&mut kvi).unwrap(); /// vcpu.vcpu_init(&kvi).unwrap(); /// ``` - #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + #[cfg(target_arch = "aarch64")] pub fn vcpu_init(&self, kvi: &kvm_vcpu_init) -> Result<()> { // SAFETY: This is safe because we allocated the struct and we know the kernel will read // exactly the size of the struct. @@ -1203,7 +1201,7 @@ impl VcpuFd { /// let vcpu = vm.create_vcpu(0).unwrap(); /// /// // KVM_GET_REG_LIST on Aarch64 demands that the vcpus be initialized. - /// #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + /// #[cfg(target_arch = "aarch64")] /// { /// let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default(); /// vm.get_preferred_target(&mut kvi).unwrap(); @@ -1214,7 +1212,7 @@ impl VcpuFd { /// assert!(reg_list.as_fam_struct_ref().n > 0); /// } /// ``` - #[cfg(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64"))] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] pub fn get_reg_list(&self, reg_list: &mut RegList) -> Result<()> { let ret = // SAFETY: This is safe because we allocated the struct and we trust the kernel will read @@ -1291,7 +1289,7 @@ impl VcpuFd { /// /// `data` should be equal or bigger then the register size /// oterwise function will return EINVAL error - #[cfg(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64"))] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] pub fn set_one_reg(&self, reg_id: u64, data: &[u8]) -> Result { let reg_size = reg_size(reg_id); if data.len() < reg_size { @@ -1323,7 +1321,7 @@ impl VcpuFd { /// /// `data` should be equal or bigger then the register size /// oterwise function will return EINVAL error - #[cfg(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64"))] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] pub fn get_one_reg(&self, reg_id: u64, data: &mut [u8]) -> Result { let reg_size = reg_size(reg_id); if data.len() < reg_size { @@ -1965,7 +1963,7 @@ mod tests { extern crate byteorder; use super::*; - #[cfg(any(target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64"))] + #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] use crate::cap::Cap; use crate::ioctls::system::Kvm; use std::ptr::NonNull; @@ -2212,7 +2210,6 @@ mod tests { #[cfg(any( target_arch = "x86_64", - target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64", target_arch = "s390x" @@ -2264,7 +2261,7 @@ mod tests { assert_eq!(debugregs, other_debugregs); } - #[cfg(any(target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64"))] + #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] #[test] fn vcpu_events_test() { let kvm = Kvm::new().unwrap(); @@ -2606,7 +2603,6 @@ mod tests { #[test] #[cfg(any( target_arch = "x86_64", - target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64" ))] @@ -2635,12 +2631,12 @@ mod tests { .errno(), badf_errno ); - #[cfg(any(target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64"))] + #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] assert_eq!( faulty_vcpu_fd.get_vcpu_events().unwrap_err().errno(), badf_errno ); - #[cfg(any(target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64"))] + #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] assert_eq!( faulty_vcpu_fd .set_vcpu_events(&kvm_vcpu_events::default()) @@ -2924,7 +2920,7 @@ mod tests { } #[test] - #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + #[cfg(target_arch = "aarch64")] fn test_get_preferred_target() { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); @@ -2938,7 +2934,7 @@ mod tests { } #[test] - #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + #[cfg(target_arch = "aarch64")] fn test_set_one_reg() { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); @@ -2964,7 +2960,7 @@ mod tests { } #[test] - #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + #[cfg(target_arch = "aarch64")] fn test_get_one_reg() { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); @@ -3000,7 +2996,7 @@ mod tests { } #[test] - #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + #[cfg(target_arch = "aarch64")] fn test_get_reg_list() { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index c8c11f4..861526d 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -24,7 +24,7 @@ use vmm_sys_util::errno; use vmm_sys_util::eventfd::EventFd; #[cfg(target_arch = "x86_64")] use vmm_sys_util::ioctl::ioctl_with_mut_ptr; -#[cfg(any(target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64"))] +#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref}; use vmm_sys_util::ioctl::{ioctl_with_ref, ioctl_with_val}; @@ -278,7 +278,7 @@ impl VmFd { /// /// #[cfg(target_arch = "x86_64")] /// vm.create_irq_chip().unwrap(); - /// #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + /// #[cfg(target_arch = "aarch64")] /// { /// use kvm_bindings::{ /// kvm_create_device, kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2, KVM_CREATE_DEVICE_TEST, @@ -293,7 +293,7 @@ impl VmFd { /// } /// } /// ``` - #[cfg(any(target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64"))] + #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] pub fn create_irq_chip(&self) -> Result<()> { // SAFETY: Safe because we know that our file is a VM fd and we verify the return result. let ret = unsafe { ioctl(self, KVM_CREATE_IRQCHIP()) }; @@ -578,7 +578,6 @@ impl VmFd { /// ``` #[cfg(any( target_arch = "x86_64", - target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64" ))] @@ -634,7 +633,6 @@ impl VmFd { /// ``` #[cfg(any( target_arch = "x86_64", - target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64" ))] @@ -982,7 +980,6 @@ impl VmFd { /// ``` #[cfg(any( target_arch = "x86_64", - target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64" ))] @@ -1034,7 +1031,6 @@ impl VmFd { /// ``` #[cfg(any( target_arch = "x86_64", - target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64" ))] @@ -1093,7 +1089,6 @@ impl VmFd { /// ``` #[cfg(any( target_arch = "x86_64", - target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64" ))] @@ -1142,7 +1137,7 @@ impl VmFd { /// // For Arm architectures, the IRQ controllers need to be setup first. /// // Details please refer to the kernel documentation. /// // https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt - /// # #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] { + /// # #[cfg(target_arch = "aarch64")] { /// # vm_fd.create_vcpu(0).unwrap(); /// # // ... rest of setup for Arm goes here /// # } @@ -1156,7 +1151,7 @@ impl VmFd { /// vm.set_irq_line(4, true); /// // ... /// } - /// #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + /// #[cfg(target_arch = "aarch64")] /// { /// vm.set_irq_line(0x01_00_0020, true); /// // .... @@ -1164,7 +1159,6 @@ impl VmFd { /// ``` #[cfg(any( target_arch = "x86_64", - target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64" ))] @@ -1288,7 +1282,7 @@ impl VmFd { /// let mut device = kvm_bindings::kvm_create_device { /// #[cfg(target_arch = "x86_64")] /// type_: kvm_device_type_KVM_DEV_TYPE_VFIO, - /// #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + /// #[cfg(target_arch = "aarch64")] /// type_: kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, /// #[cfg(target_arch = "riscv64")] /// type_: kvm_device_type_KVM_DEV_TYPE_RISCV_AIA, @@ -1300,7 +1294,7 @@ impl VmFd { /// let device_fd = vm.create_device(&mut device).unwrap_or_else(|_| { /// #[cfg(target_arch = "x86_64")] /// panic!("Cannot create VFIO device."); - /// #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + /// #[cfg(target_arch = "aarch64")] /// { /// device.type_ = kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2; /// vm.create_device(&mut device) @@ -1344,7 +1338,7 @@ impl VmFd { /// let mut kvi = kvm_vcpu_init::default(); /// vm.get_preferred_target(&mut kvi).unwrap(); /// ``` - #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + #[cfg(target_arch = "aarch64")] pub fn get_preferred_target(&self, kvi: &mut kvm_vcpu_init) -> Result<()> { // SAFETY: The ioctl is safe because we allocated the struct and we know the // kernel will write exactly the size of the struct. @@ -1398,7 +1392,7 @@ impl VmFd { /// vm.enable_cap(&cap).unwrap(); /// } /// ``` - #[cfg(not(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64")))] + #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] pub fn enable_cap(&self, cap: &kvm_enable_cap) -> Result<()> { // SAFETY: The ioctl is safe because we allocated the struct and we know the // kernel will write exactly the size of the struct. @@ -1920,7 +1914,7 @@ impl AsRawFd for VmFd { /// * `vm` - The vm file descriptor. /// * `flags` - Flags to be passed to `KVM_CREATE_DEVICE`. #[cfg(test)] -#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] +#[cfg(target_arch = "aarch64")] pub(crate) fn create_gic_device(vm: &VmFd, flags: u32) -> DeviceFd { let mut gic_device = kvm_bindings::kvm_create_device { type_: kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, @@ -1944,7 +1938,7 @@ pub(crate) fn create_gic_device(vm: &VmFd, flags: u32) -> DeviceFd { /// * `vgic` - The vGIC file descriptor. /// * `nr_irqs` - Number of IRQs. #[cfg(test)] -#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] +#[cfg(target_arch = "aarch64")] pub(crate) fn set_supported_nr_irqs(vgic: &DeviceFd, nr_irqs: u32) { let vgic_attr = kvm_bindings::kvm_device_attr { group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS, @@ -1962,7 +1956,7 @@ pub(crate) fn set_supported_nr_irqs(vgic: &DeviceFd, nr_irqs: u32) { /// /// * `vgic` - The vGIC file descriptor. #[cfg(test)] -#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] +#[cfg(target_arch = "aarch64")] pub(crate) fn request_gic_init(vgic: &DeviceFd) { let vgic_attr = kvm_bindings::kvm_device_attr { group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, @@ -2124,7 +2118,7 @@ mod tests { } #[test] - #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + #[cfg(target_arch = "aarch64")] fn test_irq_chip() { use Cap; @@ -2590,7 +2584,7 @@ mod tests { } #[test] - #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + #[cfg(target_arch = "aarch64")] fn test_get_preferred_target() { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); @@ -2604,7 +2598,6 @@ mod tests { #[test] #[cfg(any( target_arch = "x86_64", - target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64" ))] @@ -2616,7 +2609,7 @@ mod tests { } #[test] - #[cfg(not(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64")))] + #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] fn test_enable_cap_failure() { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); @@ -2653,7 +2646,6 @@ mod tests { #[test] #[cfg(any( target_arch = "x86_64", - target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64" ))] diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index 402f5de..192b602 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -53,17 +53,11 @@ ioctl_iow_nr!(KVM_SET_IDENTITY_MAP_ADDR, KVMIO, 0x48, u64); #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] ioctl_iowr_nr!(KVM_CREATE_GUEST_MEMFD, KVMIO, 0xd4, kvm_create_guest_memfd); /* Available with KVM_CAP_IRQCHIP */ -#[cfg(any( - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64", - target_arch = "s390x" -))] +#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "s390x"))] ioctl_io_nr!(KVM_CREATE_IRQCHIP, KVMIO, 0x60); /* Available with KVM_CAP_IRQCHIP */ #[cfg(any( target_arch = "x86_64", - target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64" ))] @@ -85,7 +79,6 @@ ioctl_iow_nr!( /* Available with KVM_CAP_IRQ_ROUTING */ #[cfg(any( target_arch = "x86_64", - target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64" ))] @@ -93,7 +86,6 @@ ioctl_iow_nr!(KVM_SET_GSI_ROUTING, KVMIO, 0x6a, kvm_irq_routing); /* Available with KVM_CAP_IRQFD */ #[cfg(any( target_arch = "x86_64", - target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64", target_arch = "s390x" @@ -135,9 +127,9 @@ ioctl_ior_nr!(KVM_MEMORY_ENCRYPT_UNREG_REGION, KVMIO, 0xbc, kvm_enc_region); // Ioctls for VCPU fds. ioctl_io_nr!(KVM_RUN, KVMIO, 0x80); -#[cfg(not(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64")))] +#[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] ioctl_ior_nr!(KVM_GET_REGS, KVMIO, 0x81, kvm_regs); -#[cfg(not(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64")))] +#[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] ioctl_iow_nr!(KVM_SET_REGS, KVMIO, 0x82, kvm_regs); #[cfg(any( target_arch = "x86_64", @@ -176,7 +168,6 @@ ioctl_iowr_nr!(KVM_GET_CPUID2, KVMIO, 0x91, kvm_cpuid2); /* Available with KVM_CAP_MP_STATE */ #[cfg(any( target_arch = "x86_64", - target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64", target_arch = "s390x" @@ -185,7 +176,6 @@ ioctl_ior_nr!(KVM_GET_MP_STATE, KVMIO, 0x98, kvm_mp_state); /* Available with KVM_CAP_MP_STATE */ #[cfg(any( target_arch = "x86_64", - target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64", target_arch = "s390x" @@ -195,10 +185,10 @@ ioctl_iow_nr!(KVM_SET_MP_STATE, KVMIO, 0x99, kvm_mp_state); #[cfg(target_arch = "x86_64")] ioctl_io_nr!(KVM_NMI, KVMIO, 0x9a); /* Available with KVM_CAP_VCPU_EVENTS */ -#[cfg(any(target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64"))] +#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] ioctl_ior_nr!(KVM_GET_VCPU_EVENTS, KVMIO, 0x9f, kvm_vcpu_events); /* Available with KVM_CAP_VCPU_EVENTS */ -#[cfg(any(target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64"))] +#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] ioctl_iow_nr!(KVM_SET_VCPU_EVENTS, KVMIO, 0xa0, kvm_vcpu_events); /* Available with KVM_CAP_DEBUGREGS */ #[cfg(target_arch = "x86_64")] @@ -230,26 +220,25 @@ ioctl_io_nr!(KVM_SET_TSC_KHZ, KVMIO, 0xa2); ioctl_io_nr!(KVM_GET_TSC_KHZ, KVMIO, 0xa3); /* Available with KVM_CAP_ENABLE_CAP */ -#[cfg(not(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64")))] +#[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] ioctl_iow_nr!(KVM_ENABLE_CAP, KVMIO, 0xa3, kvm_enable_cap); /* Available with KVM_CAP_SIGNAL_MSI */ #[cfg(any( target_arch = "x86_64", - target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64" ))] ioctl_iow_nr!(KVM_SIGNAL_MSI, KVMIO, 0xa5, kvm_msi); /* Available with KVM_CAP_ONE_REG */ -#[cfg(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64"))] +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] ioctl_iow_nr!(KVM_GET_ONE_REG, KVMIO, 0xab, kvm_one_reg); -#[cfg(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64"))] +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] ioctl_iow_nr!(KVM_SET_ONE_REG, KVMIO, 0xac, kvm_one_reg); -#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] +#[cfg(target_arch = "aarch64")] ioctl_iow_nr!(KVM_ARM_VCPU_INIT, KVMIO, 0xae, kvm_vcpu_init); -#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] +#[cfg(target_arch = "aarch64")] ioctl_ior_nr!(KVM_ARM_PREFERRED_TARGET, KVMIO, 0xaf, kvm_vcpu_init); -#[cfg(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64"))] +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] ioctl_iowr_nr!(KVM_GET_REG_LIST, KVMIO, 0xb0, kvm_reg_list); /* Available with KVM_CAP_X86_SMM */ diff --git a/src/lib.rs b/src/lib.rs index 2c5577e..278e21b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -244,7 +244,7 @@ mod ioctls; pub use cap::Cap; pub use ioctls::device::DeviceFd; pub use ioctls::system::Kvm; -#[cfg(any(target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64"))] +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] pub use ioctls::vcpu::reg_size; pub use ioctls::vcpu::{HypercallExit, VcpuExit, VcpuFd}; From 65c754c63ade0271efb4940049a0126616c9f36d Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Thu, 17 Oct 2024 21:04:31 +0800 Subject: [PATCH 325/332] chore: Update CHANGELOG.md Drop `x86` and `arm` 32-bit architecture support. Signed-off-by: Ruoqing He --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bdfca7..ecaec0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Added +- [[#289](https://github.com/rust-vmm/kvm-ioctls/pull/289)]: Drop `x86` and `arm` support. - [[#275](https://github.com/rust-vmm/kvm-ioctls/pull/275)]: Introduce `riscv64` ioctls. ### Changed From 2b4eec59b98446bbe8620d43d07d0577b1659f49 Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Thu, 17 Oct 2024 21:37:31 +0800 Subject: [PATCH 326/332] chore: Remove redundant prefixing Since `use kvm_bindings::*` is in effect, remove redundant prefixing to keep the code clean. Signed-off-by: Ruoqing He --- src/ioctls/vcpu.rs | 28 ++++++++++++++-------------- src/ioctls/vm.rs | 30 +++++++++++++++--------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index 536d59f..8b2caa9 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -1203,7 +1203,7 @@ impl VcpuFd { /// // KVM_GET_REG_LIST on Aarch64 demands that the vcpus be initialized. /// #[cfg(target_arch = "aarch64")] /// { - /// let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default(); + /// let mut kvi = kvm_bindings::kvm_vcpu_init::default(); /// vm.get_preferred_target(&mut kvi).unwrap(); /// vcpu.vcpu_init(&kvi).expect("Cannot initialize vcpu"); /// @@ -2321,7 +2321,7 @@ mod tests { } let mut vcpu_fd = vm.create_vcpu(0).unwrap(); - let mut kvi = kvm_bindings::kvm_vcpu_init::default(); + let mut kvi = kvm_vcpu_init::default(); vm.get_preferred_target(&mut kvi).unwrap(); kvi.features[0] |= 1 << KVM_ARM_VCPU_PSCI_0_2; vcpu_fd.vcpu_init(&kvi).unwrap(); @@ -2784,7 +2784,7 @@ mod tests { // KVM defines valid targets as 0 to KVM_ARM_NUM_TARGETS-1, so pick a big raw number // greater than that as target to be invalid - let kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init { + let kvi = kvm_vcpu_init { target: 300, ..Default::default() }; @@ -2808,7 +2808,7 @@ mod tests { coalesced_mmio_ring: None, }; - let device_attr = kvm_bindings::kvm_device_attr { + let device_attr = kvm_device_attr { group: KVM_ARM_VCPU_PMU_V3_CTRL, attr: u64::from(KVM_ARM_VCPU_PMU_V3_INIT), addr: 0x0, @@ -2926,7 +2926,7 @@ mod tests { let vm = kvm.create_vm().unwrap(); let vcpu = vm.create_vcpu(0).unwrap(); - let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default(); + let mut kvi = kvm_vcpu_init::default(); vm.get_preferred_target(&mut kvi) .expect("Cannot get preferred target"); @@ -2940,7 +2940,7 @@ mod tests { let vm = kvm.create_vm().unwrap(); let vcpu = vm.create_vcpu(0).unwrap(); - let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default(); + let mut kvi = kvm_vcpu_init::default(); vm.get_preferred_target(&mut kvi) .expect("Cannot get preferred target"); vcpu.vcpu_init(&kvi).expect("Cannot initialize vcpu"); @@ -2966,7 +2966,7 @@ mod tests { let vm = kvm.create_vm().unwrap(); let vcpu = vm.create_vcpu(0).unwrap(); - let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default(); + let mut kvi = kvm_vcpu_init::default(); vm.get_preferred_target(&mut kvi) .expect("Cannot get preferred target"); vcpu.vcpu_init(&kvi).expect("Cannot initialize vcpu"); @@ -3007,7 +3007,7 @@ mod tests { let err = vcpu.get_reg_list(&mut reg_list).unwrap_err(); assert!(err.errno() == libc::ENOEXEC); - let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default(); + let mut kvi = kvm_vcpu_init::default(); vm.get_preferred_target(&mut kvi) .expect("Cannot get preferred target"); vcpu.vcpu_init(&kvi).expect("Cannot initialize vcpu"); @@ -3321,7 +3321,7 @@ mod tests { let vm = kvm.create_vm().unwrap(); let vcpu = vm.create_vcpu(0).unwrap(); - let dist_attr = kvm_bindings::kvm_device_attr { + let dist_attr = kvm_device_attr { group: KVM_ARM_VCPU_PMU_V3_CTRL, attr: u64::from(KVM_ARM_VCPU_PMU_V3_INIT), addr: 0x0, @@ -3330,10 +3330,10 @@ mod tests { vcpu.has_device_attr(&dist_attr).unwrap_err(); vcpu.set_device_attr(&dist_attr).unwrap_err(); - let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default(); + let mut kvi: kvm_vcpu_init = kvm_vcpu_init::default(); vm.get_preferred_target(&mut kvi) .expect("Cannot get preferred target"); - kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PSCI_0_2 | 1 << KVM_ARM_VCPU_PMU_V3; + kvi.features[0] |= 1 << KVM_ARM_VCPU_PSCI_0_2 | 1 << KVM_ARM_VCPU_PMU_V3; vcpu.vcpu_init(&kvi).unwrap(); vcpu.has_device_attr(&dist_attr).unwrap(); vcpu.set_device_attr(&dist_attr).unwrap(); @@ -3346,14 +3346,14 @@ mod tests { let vm = kvm.create_vm().unwrap(); let vcpu = vm.create_vcpu(0).unwrap(); - let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default(); + let mut kvi = kvm_vcpu_init::default(); vm.get_preferred_target(&mut kvi) .expect("Cannot get preferred target"); if kvm.check_extension(Cap::ArmPtrAuthAddress) { - kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PTRAUTH_ADDRESS; + kvi.features[0] |= 1 << KVM_ARM_VCPU_PTRAUTH_ADDRESS; } if kvm.check_extension(Cap::ArmPtrAuthGeneric) { - kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PTRAUTH_GENERIC; + kvi.features[0] |= 1 << KVM_ARM_VCPU_PTRAUTH_GENERIC; } vcpu.vcpu_init(&kvi).unwrap(); } diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index 861526d..e4c7c02 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -1916,7 +1916,7 @@ impl AsRawFd for VmFd { #[cfg(test)] #[cfg(target_arch = "aarch64")] pub(crate) fn create_gic_device(vm: &VmFd, flags: u32) -> DeviceFd { - let mut gic_device = kvm_bindings::kvm_create_device { + let mut gic_device = kvm_create_device { type_: kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, fd: 0, flags, @@ -1940,8 +1940,8 @@ pub(crate) fn create_gic_device(vm: &VmFd, flags: u32) -> DeviceFd { #[cfg(test)] #[cfg(target_arch = "aarch64")] pub(crate) fn set_supported_nr_irqs(vgic: &DeviceFd, nr_irqs: u32) { - let vgic_attr = kvm_bindings::kvm_device_attr { - group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS, + let vgic_attr = kvm_device_attr { + group: KVM_DEV_ARM_VGIC_GRP_NR_IRQS, attr: 0, addr: &nr_irqs as *const u32 as u64, flags: 0, @@ -1958,9 +1958,9 @@ pub(crate) fn set_supported_nr_irqs(vgic: &DeviceFd, nr_irqs: u32) { #[cfg(test)] #[cfg(target_arch = "aarch64")] pub(crate) fn request_gic_init(vgic: &DeviceFd) { - let vgic_attr = kvm_bindings::kvm_device_attr { - group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, - attr: u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT), + let vgic_attr = kvm_device_attr { + group: KVM_DEV_ARM_VGIC_GRP_CTRL, + attr: u64::from(KVM_DEV_ARM_VGIC_CTRL_INIT), addr: 0, flags: 0, }; @@ -1977,7 +1977,7 @@ pub(crate) fn request_gic_init(vgic: &DeviceFd) { #[cfg(test)] #[cfg(target_arch = "riscv64")] pub(crate) fn create_aia_device(vm: &VmFd, flags: u32) -> DeviceFd { - let mut aia_device = kvm_bindings::kvm_create_device { + let mut aia_device = kvm_create_device { type_: kvm_device_type_KVM_DEV_TYPE_RISCV_AIA, fd: 0, flags, @@ -1995,9 +1995,9 @@ pub(crate) fn create_aia_device(vm: &VmFd, flags: u32) -> DeviceFd { #[cfg(test)] #[cfg(target_arch = "riscv64")] pub(crate) fn set_supported_nr_irqs(vaia: &DeviceFd, nr_irqs: u32) { - let vaia_attr = kvm_bindings::kvm_device_attr { - group: kvm_bindings::KVM_DEV_RISCV_AIA_GRP_CONFIG, - attr: u64::from(kvm_bindings::KVM_DEV_RISCV_AIA_CONFIG_SRCS), + let vaia_attr = kvm_device_attr { + group: KVM_DEV_RISCV_AIA_GRP_CONFIG, + attr: u64::from(KVM_DEV_RISCV_AIA_CONFIG_SRCS), addr: &nr_irqs as *const u32 as u64, flags: 0, }; @@ -2013,9 +2013,9 @@ pub(crate) fn set_supported_nr_irqs(vaia: &DeviceFd, nr_irqs: u32) { #[cfg(test)] #[cfg(target_arch = "riscv64")] pub(crate) fn request_aia_init(vaia: &DeviceFd) { - let vaia_attr = kvm_bindings::kvm_device_attr { - group: kvm_bindings::KVM_DEV_RISCV_AIA_GRP_CTRL, - attr: u64::from(kvm_bindings::KVM_DEV_RISCV_AIA_CTRL_INIT), + let vaia_attr = kvm_device_attr { + group: KVM_DEV_RISCV_AIA_GRP_CTRL, + attr: u64::from(KVM_DEV_RISCV_AIA_CTRL_INIT), addr: 0, flags: 0, }; @@ -2129,7 +2129,7 @@ mod tests { // On ARM/arm64, a GICv2 is created. It's better to check ahead whether GICv2 // can be emulated or not. - let mut gic_device = kvm_bindings::kvm_create_device { + let mut gic_device = kvm_create_device { type_: kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2, fd: 0, flags: KVM_CREATE_DEVICE_TEST, @@ -2588,7 +2588,7 @@ mod tests { fn test_get_preferred_target() { let kvm = Kvm::new().unwrap(); let vm = kvm.create_vm().unwrap(); - let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default(); + let mut kvi = kvm_vcpu_init::default(); vm.get_preferred_target(&mut kvi).unwrap(); } From 701517ad59a7b292b4bc3869cd73e3efe6f4df56 Mon Sep 17 00:00:00 2001 From: Ruoqing He Date: Thu, 17 Oct 2024 22:52:34 +0800 Subject: [PATCH 327/332] chore: Prepare 0.19.0 release Update kvm-ioctls from v0.18.0 to v0.19.0 to incorporate RISC-V support. Signed-off-by: Ruoqing He --- CHANGELOG.md | 8 +++++++- Cargo.toml | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecaec0f..022066e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,17 @@ ## Upcoming Release +## v0.19.0 + ### Added -- [[#289](https://github.com/rust-vmm/kvm-ioctls/pull/289)]: Drop `x86` and `arm` support. - [[#275](https://github.com/rust-vmm/kvm-ioctls/pull/275)]: Introduce `riscv64` ioctls. +### Removed + +- [[#289](https://github.com/rust-vmm/kvm-ioctls/pull/289)]: Drop `x86` 32-bit + and `arm` 32-bit support. + ### Changed - [[#273](https://github.com/rust-vmm/kvm-ioctls/pull/273)]: `DeviceFd::get_device_attr` is now diff --git a/Cargo.toml b/Cargo.toml index 56768e2..3a68b11 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kvm-ioctls" -version = "0.18.0" +version = "0.19.0" authors = ["Amazon Firecracker Team "] description = "Safe wrappers over KVM ioctls" repository = "https://github.com/rust-vmm/kvm-ioctls" From fcf324741824b261362137f705591c35d44a3791 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Tue, 29 Oct 2024 12:37:49 +0200 Subject: [PATCH 328/332] Move kvm-ioctls crate to own directory In preparation for creating a common workspace for kvm-bindings and kvm-ioctls, move the kvm-ioctls crate to its own directory. A workspace Cargo.toml is not added yet because it makes git detect it as a modification and treat kvm-ioctls/Cargo.toml as a new file instead of a rename. Signed-off-by: Manos Pitsidianakis --- CHANGELOG.md => kvm-ioctls/CHANGELOG.md | 0 Cargo.toml => kvm-ioctls/Cargo.toml | 0 LICENSE-APACHE => kvm-ioctls/LICENSE-APACHE | 0 LICENSE-MIT => kvm-ioctls/LICENSE-MIT | 0 README.md => kvm-ioctls/README.md | 0 THIRD-PARTY => kvm-ioctls/THIRD-PARTY | 0 build.rs => kvm-ioctls/build.rs | 0 .../coverage_config_aarch64.json | 0 .../coverage_config_x86_64.json | 0 {src => kvm-ioctls/src}/cap.rs | 0 {src => kvm-ioctls/src}/ioctls/device.rs | 0 {src => kvm-ioctls/src}/ioctls/mod.rs | 0 {src => kvm-ioctls/src}/ioctls/system.rs | 0 {src => kvm-ioctls/src}/ioctls/vcpu.rs | 0 {src => kvm-ioctls/src}/ioctls/vm.rs | 0 {src => kvm-ioctls/src}/kvm_ioctls.rs | 0 {src => kvm-ioctls/src}/lib.rs | 0 17 files changed, 0 insertions(+), 0 deletions(-) rename CHANGELOG.md => kvm-ioctls/CHANGELOG.md (100%) rename Cargo.toml => kvm-ioctls/Cargo.toml (100%) rename LICENSE-APACHE => kvm-ioctls/LICENSE-APACHE (100%) rename LICENSE-MIT => kvm-ioctls/LICENSE-MIT (100%) rename README.md => kvm-ioctls/README.md (100%) rename THIRD-PARTY => kvm-ioctls/THIRD-PARTY (100%) rename build.rs => kvm-ioctls/build.rs (100%) rename coverage_config_aarch64.json => kvm-ioctls/coverage_config_aarch64.json (100%) rename coverage_config_x86_64.json => kvm-ioctls/coverage_config_x86_64.json (100%) rename {src => kvm-ioctls/src}/cap.rs (100%) rename {src => kvm-ioctls/src}/ioctls/device.rs (100%) rename {src => kvm-ioctls/src}/ioctls/mod.rs (100%) rename {src => kvm-ioctls/src}/ioctls/system.rs (100%) rename {src => kvm-ioctls/src}/ioctls/vcpu.rs (100%) rename {src => kvm-ioctls/src}/ioctls/vm.rs (100%) rename {src => kvm-ioctls/src}/kvm_ioctls.rs (100%) rename {src => kvm-ioctls/src}/lib.rs (100%) diff --git a/CHANGELOG.md b/kvm-ioctls/CHANGELOG.md similarity index 100% rename from CHANGELOG.md rename to kvm-ioctls/CHANGELOG.md diff --git a/Cargo.toml b/kvm-ioctls/Cargo.toml similarity index 100% rename from Cargo.toml rename to kvm-ioctls/Cargo.toml diff --git a/LICENSE-APACHE b/kvm-ioctls/LICENSE-APACHE similarity index 100% rename from LICENSE-APACHE rename to kvm-ioctls/LICENSE-APACHE diff --git a/LICENSE-MIT b/kvm-ioctls/LICENSE-MIT similarity index 100% rename from LICENSE-MIT rename to kvm-ioctls/LICENSE-MIT diff --git a/README.md b/kvm-ioctls/README.md similarity index 100% rename from README.md rename to kvm-ioctls/README.md diff --git a/THIRD-PARTY b/kvm-ioctls/THIRD-PARTY similarity index 100% rename from THIRD-PARTY rename to kvm-ioctls/THIRD-PARTY diff --git a/build.rs b/kvm-ioctls/build.rs similarity index 100% rename from build.rs rename to kvm-ioctls/build.rs diff --git a/coverage_config_aarch64.json b/kvm-ioctls/coverage_config_aarch64.json similarity index 100% rename from coverage_config_aarch64.json rename to kvm-ioctls/coverage_config_aarch64.json diff --git a/coverage_config_x86_64.json b/kvm-ioctls/coverage_config_x86_64.json similarity index 100% rename from coverage_config_x86_64.json rename to kvm-ioctls/coverage_config_x86_64.json diff --git a/src/cap.rs b/kvm-ioctls/src/cap.rs similarity index 100% rename from src/cap.rs rename to kvm-ioctls/src/cap.rs diff --git a/src/ioctls/device.rs b/kvm-ioctls/src/ioctls/device.rs similarity index 100% rename from src/ioctls/device.rs rename to kvm-ioctls/src/ioctls/device.rs diff --git a/src/ioctls/mod.rs b/kvm-ioctls/src/ioctls/mod.rs similarity index 100% rename from src/ioctls/mod.rs rename to kvm-ioctls/src/ioctls/mod.rs diff --git a/src/ioctls/system.rs b/kvm-ioctls/src/ioctls/system.rs similarity index 100% rename from src/ioctls/system.rs rename to kvm-ioctls/src/ioctls/system.rs diff --git a/src/ioctls/vcpu.rs b/kvm-ioctls/src/ioctls/vcpu.rs similarity index 100% rename from src/ioctls/vcpu.rs rename to kvm-ioctls/src/ioctls/vcpu.rs diff --git a/src/ioctls/vm.rs b/kvm-ioctls/src/ioctls/vm.rs similarity index 100% rename from src/ioctls/vm.rs rename to kvm-ioctls/src/ioctls/vm.rs diff --git a/src/kvm_ioctls.rs b/kvm-ioctls/src/kvm_ioctls.rs similarity index 100% rename from src/kvm_ioctls.rs rename to kvm-ioctls/src/kvm_ioctls.rs diff --git a/src/lib.rs b/kvm-ioctls/src/lib.rs similarity index 100% rename from src/lib.rs rename to kvm-ioctls/src/lib.rs From 9fb980c7f776dcf0ad72de78712e0f562f444867 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Tue, 29 Oct 2024 12:30:41 +0200 Subject: [PATCH 329/332] Move kvm-bindings crate to own directory In preparation for creating a common workspace for kvm-bindings and kvm-ioctls, move the kvm-bindings crate to its own directory. A workspace Cargo.toml is not added yet because it makes git detect it as a modification and treat kvm-bindings/Cargo.toml as a new file instead of a rename. Signed-off-by: Manos Pitsidianakis --- .buildkite/custom-tests.json | 8 ++++---- CHANGELOG.md => kvm-bindings/CHANGELOG.md | 0 CONTRIBUTING.md => kvm-bindings/CONTRIBUTING.md | 0 Cargo.toml => kvm-bindings/Cargo.toml | 0 LICENSE => kvm-bindings/LICENSE | 0 README.md => kvm-bindings/README.md | 0 .../coverage_config_aarch64.json | 0 .../coverage_config_x86_64.json | 0 {src => kvm-bindings/src}/arm64/bindings.rs | 0 {src => kvm-bindings/src}/arm64/fam_wrappers.rs | 0 {src => kvm-bindings/src}/arm64/mod.rs | 0 {src => kvm-bindings/src}/arm64/serialize.rs | 0 {src => kvm-bindings/src}/lib.rs | 0 {src => kvm-bindings/src}/riscv64/bindings.rs | 0 {src => kvm-bindings/src}/riscv64/fam_wrappers.rs | 0 {src => kvm-bindings/src}/riscv64/mod.rs | 0 {src => kvm-bindings/src}/riscv64/serialize.rs | 0 {src => kvm-bindings/src}/serialize.rs | 0 {src => kvm-bindings/src}/x86_64/bindings.rs | 0 {src => kvm-bindings/src}/x86_64/fam_wrappers.rs | 0 {src => kvm-bindings/src}/x86_64/mod.rs | 0 {src => kvm-bindings/src}/x86_64/serialize.rs | 0 22 files changed, 4 insertions(+), 4 deletions(-) rename CHANGELOG.md => kvm-bindings/CHANGELOG.md (100%) rename CONTRIBUTING.md => kvm-bindings/CONTRIBUTING.md (100%) rename Cargo.toml => kvm-bindings/Cargo.toml (100%) rename LICENSE => kvm-bindings/LICENSE (100%) rename README.md => kvm-bindings/README.md (100%) rename coverage_config_aarch64.json => kvm-bindings/coverage_config_aarch64.json (100%) rename coverage_config_x86_64.json => kvm-bindings/coverage_config_x86_64.json (100%) rename {src => kvm-bindings/src}/arm64/bindings.rs (100%) rename {src => kvm-bindings/src}/arm64/fam_wrappers.rs (100%) rename {src => kvm-bindings/src}/arm64/mod.rs (100%) rename {src => kvm-bindings/src}/arm64/serialize.rs (100%) rename {src => kvm-bindings/src}/lib.rs (100%) rename {src => kvm-bindings/src}/riscv64/bindings.rs (100%) rename {src => kvm-bindings/src}/riscv64/fam_wrappers.rs (100%) rename {src => kvm-bindings/src}/riscv64/mod.rs (100%) rename {src => kvm-bindings/src}/riscv64/serialize.rs (100%) rename {src => kvm-bindings/src}/serialize.rs (100%) rename {src => kvm-bindings/src}/x86_64/bindings.rs (100%) rename {src => kvm-bindings/src}/x86_64/fam_wrappers.rs (100%) rename {src => kvm-bindings/src}/x86_64/mod.rs (100%) rename {src => kvm-bindings/src}/x86_64/serialize.rs (100%) diff --git a/.buildkite/custom-tests.json b/.buildkite/custom-tests.json index 9100775..7bdae17 100644 --- a/.buildkite/custom-tests.json +++ b/.buildkite/custom-tests.json @@ -2,7 +2,7 @@ "tests": [ { "test_name": "build-fam-gnu", - "command": "cargo build --release --features=fam-wrappers", + "command": "cd kvm-bindings && cargo build --release --features=fam-wrappers", "platform": [ "x86_64", "aarch64", @@ -11,7 +11,7 @@ }, { "test_name": "build-fam-musl", - "command": "cargo build --release --features=fam-wrappers --target {target_platform}-unknown-linux-musl", + "command": "cd kvm-bindings && cargo build --release --features=fam-wrappers --target {target_platform}-unknown-linux-musl", "platform": [ "x86_64", "aarch64" @@ -19,7 +19,7 @@ }, { "test_name": "build-serde-gnu", - "command": "cargo build --release --features=serde", + "command": "cd kvm-bindings && cargo build --release --features=serde", "platform": [ "x86_64", "aarch64", @@ -28,7 +28,7 @@ }, { "test_name": "build-serde-musl", - "command": "cargo build --release --features=serde --target {target_platform}-unknown-linux-musl", + "command": "cd kvm-bindings && cargo build --release --features=serde --target {target_platform}-unknown-linux-musl", "platform": [ "x86_64", "aarch64" diff --git a/CHANGELOG.md b/kvm-bindings/CHANGELOG.md similarity index 100% rename from CHANGELOG.md rename to kvm-bindings/CHANGELOG.md diff --git a/CONTRIBUTING.md b/kvm-bindings/CONTRIBUTING.md similarity index 100% rename from CONTRIBUTING.md rename to kvm-bindings/CONTRIBUTING.md diff --git a/Cargo.toml b/kvm-bindings/Cargo.toml similarity index 100% rename from Cargo.toml rename to kvm-bindings/Cargo.toml diff --git a/LICENSE b/kvm-bindings/LICENSE similarity index 100% rename from LICENSE rename to kvm-bindings/LICENSE diff --git a/README.md b/kvm-bindings/README.md similarity index 100% rename from README.md rename to kvm-bindings/README.md diff --git a/coverage_config_aarch64.json b/kvm-bindings/coverage_config_aarch64.json similarity index 100% rename from coverage_config_aarch64.json rename to kvm-bindings/coverage_config_aarch64.json diff --git a/coverage_config_x86_64.json b/kvm-bindings/coverage_config_x86_64.json similarity index 100% rename from coverage_config_x86_64.json rename to kvm-bindings/coverage_config_x86_64.json diff --git a/src/arm64/bindings.rs b/kvm-bindings/src/arm64/bindings.rs similarity index 100% rename from src/arm64/bindings.rs rename to kvm-bindings/src/arm64/bindings.rs diff --git a/src/arm64/fam_wrappers.rs b/kvm-bindings/src/arm64/fam_wrappers.rs similarity index 100% rename from src/arm64/fam_wrappers.rs rename to kvm-bindings/src/arm64/fam_wrappers.rs diff --git a/src/arm64/mod.rs b/kvm-bindings/src/arm64/mod.rs similarity index 100% rename from src/arm64/mod.rs rename to kvm-bindings/src/arm64/mod.rs diff --git a/src/arm64/serialize.rs b/kvm-bindings/src/arm64/serialize.rs similarity index 100% rename from src/arm64/serialize.rs rename to kvm-bindings/src/arm64/serialize.rs diff --git a/src/lib.rs b/kvm-bindings/src/lib.rs similarity index 100% rename from src/lib.rs rename to kvm-bindings/src/lib.rs diff --git a/src/riscv64/bindings.rs b/kvm-bindings/src/riscv64/bindings.rs similarity index 100% rename from src/riscv64/bindings.rs rename to kvm-bindings/src/riscv64/bindings.rs diff --git a/src/riscv64/fam_wrappers.rs b/kvm-bindings/src/riscv64/fam_wrappers.rs similarity index 100% rename from src/riscv64/fam_wrappers.rs rename to kvm-bindings/src/riscv64/fam_wrappers.rs diff --git a/src/riscv64/mod.rs b/kvm-bindings/src/riscv64/mod.rs similarity index 100% rename from src/riscv64/mod.rs rename to kvm-bindings/src/riscv64/mod.rs diff --git a/src/riscv64/serialize.rs b/kvm-bindings/src/riscv64/serialize.rs similarity index 100% rename from src/riscv64/serialize.rs rename to kvm-bindings/src/riscv64/serialize.rs diff --git a/src/serialize.rs b/kvm-bindings/src/serialize.rs similarity index 100% rename from src/serialize.rs rename to kvm-bindings/src/serialize.rs diff --git a/src/x86_64/bindings.rs b/kvm-bindings/src/x86_64/bindings.rs similarity index 100% rename from src/x86_64/bindings.rs rename to kvm-bindings/src/x86_64/bindings.rs diff --git a/src/x86_64/fam_wrappers.rs b/kvm-bindings/src/x86_64/fam_wrappers.rs similarity index 100% rename from src/x86_64/fam_wrappers.rs rename to kvm-bindings/src/x86_64/fam_wrappers.rs diff --git a/src/x86_64/mod.rs b/kvm-bindings/src/x86_64/mod.rs similarity index 100% rename from src/x86_64/mod.rs rename to kvm-bindings/src/x86_64/mod.rs diff --git a/src/x86_64/serialize.rs b/kvm-bindings/src/x86_64/serialize.rs similarity index 100% rename from src/x86_64/serialize.rs rename to kvm-bindings/src/x86_64/serialize.rs From 1938ace924fb3b36694bbf6b710cb70630aface6 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Tue, 29 Oct 2024 12:34:38 +0200 Subject: [PATCH 330/332] Add a toplevel workspace Cargo.toml manifest In preparation for merging the kvm-ioctls tree into this workspace. Signed-off-by: Manos Pitsidianakis --- Cargo.toml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 Cargo.toml diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..3d72bde --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[workspace] +resolver = "2" + +members = [ + "kvm-bindings", +] From 7ffcc0bae48d9c83428c66ff5d6a23dcb308de72 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Tue, 29 Oct 2024 13:01:05 +0200 Subject: [PATCH 331/332] Unify LICENSE* files by using symbolic links Signed-off-by: Manos Pitsidianakis --- LICENSE-APACHE | 202 +++++++++++++++++++++++++++++++++++++ LICENSE-MIT | 24 +++++ kvm-bindings/LICENSE | 203 +------------------------------------- kvm-ioctls/LICENSE-APACHE | 203 +------------------------------------- kvm-ioctls/LICENSE-MIT | 25 +---- 5 files changed, 229 insertions(+), 428 deletions(-) create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT mode change 100644 => 120000 kvm-bindings/LICENSE mode change 100644 => 120000 kvm-ioctls/LICENSE-APACHE mode change 100644 => 120000 kvm-ioctls/LICENSE-MIT diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..5c6a646 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,24 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + diff --git a/kvm-bindings/LICENSE b/kvm-bindings/LICENSE deleted file mode 100644 index d645695..0000000 --- a/kvm-bindings/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/kvm-bindings/LICENSE b/kvm-bindings/LICENSE new file mode 120000 index 0000000..965b606 --- /dev/null +++ b/kvm-bindings/LICENSE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/kvm-ioctls/LICENSE-APACHE b/kvm-ioctls/LICENSE-APACHE deleted file mode 100644 index d645695..0000000 --- a/kvm-ioctls/LICENSE-APACHE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/kvm-ioctls/LICENSE-APACHE b/kvm-ioctls/LICENSE-APACHE new file mode 120000 index 0000000..965b606 --- /dev/null +++ b/kvm-ioctls/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/kvm-ioctls/LICENSE-MIT b/kvm-ioctls/LICENSE-MIT deleted file mode 100644 index 5c6a646..0000000 --- a/kvm-ioctls/LICENSE-MIT +++ /dev/null @@ -1,24 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. - diff --git a/kvm-ioctls/LICENSE-MIT b/kvm-ioctls/LICENSE-MIT new file mode 120000 index 0000000..76219eb --- /dev/null +++ b/kvm-ioctls/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file From 566201bca3de8ff808a9ecabb093f18794484c8e Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Tue, 29 Oct 2024 13:04:24 +0200 Subject: [PATCH 332/332] kvm-ioctls: use local kvm-bindings dependency Use `path = ` syntax in Cargo.toml to use the local copy of kvm-bindings if present. --- kvm-ioctls/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kvm-ioctls/Cargo.toml b/kvm-ioctls/Cargo.toml index 3a68b11..3ea2ed3 100644 --- a/kvm-ioctls/Cargo.toml +++ b/kvm-ioctls/Cargo.toml @@ -11,7 +11,7 @@ edition = "2021" [dependencies] libc = "0.2.39" -kvm-bindings = { version = "0.10.0", features = ["fam-wrappers"] } +kvm-bindings = { path = "../kvm-bindings", version = "0.10.0", features = ["fam-wrappers"] } vmm-sys-util = "0.12.1" bitflags = "2.4.1"