diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..d79d1db --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "vmm-vcpu" +version = "0.1.0" +authors = ["Jenny Mankin ", + "Timo Kreuzer ", + "Alessandro Pilotti "] + +[dependencies] +byteorder = "*" +kvm-bindings = "0.1" +libc = ">=0.2.39" \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + 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/README.md b/README.md index a846a49..4db6579 100644 --- a/README.md +++ b/README.md @@ -1 +1,42 @@ -# vmm-vcpu \ No newline at end of file +# vmm-vcpu + +A library to provide a hypervisor-agnostic abstraction on top of Virtual CPU (vCPU) +functionality. + +## Platform Support + +* Arch: x86, AMD64, ARM64 +* Operating System: Unix/Linux/Windows + +## Usage + +The `vmm-vcpu` crate is not functional standalone, but should be used to implement +hypervisor-specific virtual CPU functionality. In order to create an +implementation of the `vmm-vcpu` crate to be consumed by VMMs: + +First, add the following to `Cargo.toml`: + +``` +vmm-vcpu = "0.1" +``` + +Next, add the following to the crate root: + +``` +extern crate vmm-vcpu +``` + +Then, implement each of the functions of the `Vcpu` trait. Any irrelevant/ +unused functions can be completed with the `unimplemented!();` macro. + +## Design + +The `vmm-vcpu` crate itself is quite simple, requiring only exposing a public +`Vcpu` trait with the functions that comprise common vCPU functionality, as +well as exported wrappers on top of common data structures (including +generically named structures wrapping data structures from `kvm_bindings` when +architecturally, rather than hypervisor, specific.) + +## License + +This project is licensed under Apache License, Version 2.0, (LICENSE or http://www.apache.org/licenses/LICENSE-2.0) \ No newline at end of file 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. diff --git a/src/arm.rs b/src/arm.rs new file mode 100644 index 0000000..03c6684 --- /dev/null +++ b/src/arm.rs @@ -0,0 +1,28 @@ +// Copyright 2018-2019 CrowdStrike, Inc. +// +// 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. + +/// +/// Use kvm_bindings behind the scenes as these are architectural structures and +/// not actually KVM-dependent, but export as generically-named data +/// structures to be consumed by any VMM's vCPU implementation +/// + +/// Type of CPU to present to the guest, and the optional features it should have. +/// +/// pub struct kvm_vcpu_init { +/// pub target: __u32, +/// pub features: [__u32; 7usize], +/// } +#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] +pub use kvm_bindings::kvm_vcpu_init as VcpuInit; diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..198635d --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,23 @@ +// Copyright 2018-2019 CrowdStrike, Inc. +// +// 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. + +extern crate kvm_bindings; + +pub mod vcpu; + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +mod x86_64; + +#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] +mod arm; diff --git a/src/vcpu.rs b/src/vcpu.rs new file mode 100644 index 0000000..8fbc0df --- /dev/null +++ b/src/vcpu.rs @@ -0,0 +1,219 @@ +// Copyright 2018-2019 CrowdStrike, Inc. +// SPDX-License-Identifier: Apache-2.0 +// +// Portions Copyright 2018 Cloudbase Solutions Srl +// SPDX-License-Identifier: Apache-2.0 +// +// Portions 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. + +//! Trait to access the functionality of a virtual CPU (vCPU). + +use std::{io, result}; + +/// +/// Generic types used in the virtual CPU trait definition and exported for +/// public consumption. +/// +/// These types use kvm_bindings under the hood, as they are not necessarily KVM- +/// specific, but rather generic x86/x86/ARM structures. Generic naming makes +/// them more intuitive for consumption by non-KVM VMMs. +/// +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +pub use x86_64::{ + DescriptorTable, FpuState, MsrEntries, MsrEntry, SegmentRegister, SpecialRegisters, + StandardRegisters, +}; + +/// +/// Generic types used in the virtual CPU trait definition and exported for +/// public consumption. +/// +/// These types are explicitly defined in x86_64 +/// +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +pub use x86_64::{CpuId, LapicState}; + +/// +/// Reasons for vCPU exits. +/// +/// Exit reasons are drawn a superset of exit reasons for various VMMs. +/// +#[derive(Debug)] +pub enum VcpuExit<'a> { + None, + MemoryAccess, + IoPortAccess, + UnrecoverableException, + InvalidVpRegisterValue, + UnsupportedFeature, + InterruptWindow, + MsrAccess, + Cpuid, + Canceled, + + /// 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. + /// 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]), + Unknown, + Hypercall, + Debug, + IrqWindowOpen, + Shutdown, + FailEntry, + Intr, + SetTpr, + TprAccess, + S390Sieic, + S390Reset, + Dcr, + Nmi, + InternalError, + Osi, + PaprHcall, + S390Ucontrol, + Watchdog, + S390Tsch, + Epr, + SystemEvent, + S390Stsi, + Hyperv, + + Hlt, + IoapicEoi, + Exception, +} + +pub type Result = result::Result; + +/// +/// Trait to represent a virtual CPU +/// +/// This crate provides a hypervisor-agnostic interface to common virtual CPU +/// functionality +/// +pub trait Vcpu { + type RunContextType; + + /// Reads the standard registers from the virtual CPU + /// + /// StandardRegisters: + /// - General Purpose Registers: RAX, RBX, RCX, RDX, RSI, RDI, RSP, RBP, R8 - R15 + /// - Instruction Pointer and Flags: RIP, RFLAGS + #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] + fn get_regs(&self) -> Result; + + /// Writes the standard registers into the virtual CPU + /// + /// StandardRegisters: + /// - General Purpose Registers: RAX, RBX, RCX, RDX, RSI, RDI, RSP, RBP, R8 - R15 + /// - Instruction Pointer and Flags: RIP, RFLAGS + #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] + fn set_regs(&self, regs: &StandardRegisters) -> Result<()>; + + /// Reads the special registers from the virtual CPU + /// + /// SpecialRegisters: + /// - Segment Registers: CS, DS, ES, FS, GS, SS + /// - Task and Descriptor Table Registers: TR, LDT, GDT, IDT + /// - Control Registers: CR0, CR2, CR3, CR4, CR8 + /// - Additional EFER, APIC base + /// - Interrupt_bitmap: Bitmap of pending external interrupts (KVM only, may + /// be unused by vCPU implementations of other VMMs) + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn get_sregs(&self) -> Result; + + /// Writes the special registers into the virtual CPU + /// + /// SpecialRegisters: + /// - Segment Registers: CS, DS, ES, FS, GS, SS + /// - Task and Descriptor Table Registers: TR, LDT, GDT, IDT + /// - Control Registers: CR0, CR2, CR3, CR4, CR8 + /// - Additional: EFER, APIC base + /// - Interrupt_bitmap: Bitmap of pending external interrupts (KVM only, may + /// be unused by vCPU implementations of other VMMs) + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn set_sregs(&self, sregs: &SpecialRegisters) -> Result<()>; + + /// Reads the floating point state from the vCPU + /// + /// - Floating Point MMX Registers 0-7 + /// - Floating Pointer Control, Status, Tag Registers (FCW, FSW, FTW) + /// - Floating point exception state (Last FIP, FOP, FCS, FDS, FDP) + /// - XMM Registers 0-15 + /// - MXCSR Control and Status Register + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn get_fpu(&self) -> Result; + + /// Writes the floating point state to the vCPU + /// + /// - Floating Point MMX Registers 0-7 + /// - Floating Pointer Control, Status, Tag Registers (FCW, FSW, FTW) + /// - Floating point exception state (Last FIP, FOP, FCS, FDS, FDP) + /// - XMM Registers 0-15 + /// - MXCSR Control and Status Register + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn set_fpu(&self, fpu: &FpuState) -> Result<()>; + + /// Defines the vCPU responses to the CPUID instruction + /// + /// The information contained in these responses can be set to be consistent + /// with hardware, kernel, userspace capabilities, and user requirements. + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn set_cpuid2(&self, cpuid: &CpuId) -> Result<()>; + + /// Read the Local APIC state from the vCPU + /// + /// LAPIC state is comprised of the memory-mapped Local APIC registers. + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn get_lapic(&self) -> Result; + + /// Write the Local APIC state to the vCPU + /// + /// LAPIC state is comprised of the memory-mapped Local APIC registers + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn set_lapic(&self, klapic: &LapicState) -> Result<()>; + + /// Read the Model-Specific Registers from the vCPU based on MSR indices + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn get_msrs(&self, msrs: &mut MsrEntries) -> Result<(i32)>; + + /// Write the Model-Specific Registers to the vCPU based on MSR indices + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn set_msrs(&self, msrs: &MsrEntries) -> Result<()>; + + /// Initialize a vCPU + /// + /// Specifies the CPU to present to the guest, and the optional features + /// it should have + #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + fn vcpu_init(&self, kvi: &kvm_vcpu_init) -> Result<()>; + + /// Set a single register in the virtual CPU + #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] + fn set_one_reg(&self, reg_id: u64, data: u64) -> Result<()>; + + /// Run a virtual CPU until there is a VM exit + /// + /// The output will specify the type of VM exit + fn run(&self) -> Result; + + /// Get the context from the last vCPU run. + /// + /// The context contains information related to the latest VM exit + fn get_run_context(&self) -> Self::RunContextType; +} diff --git a/src/x86_64.rs b/src/x86_64.rs new file mode 100644 index 0000000..7c797e0 --- /dev/null +++ b/src/x86_64.rs @@ -0,0 +1,331 @@ +// Copyright 2018-2019 CrowdStrike, Inc. +// SPDX-License-Identifier: Apache-2.0 +// +// Portions Copyright 2018 Cloudbase Solutions Srl +// SPDX-License-Identifier: Apache-2.0 +// +// Portions 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. + +use std::mem::size_of; + +/// +/// Use kvm_bindings behind the scenes as these are architectural structures and +/// not actually KVM-dependent, but export as generically-named data +/// structures to be consumed by any VMM's vCPU implementation +/// + +/// Single MSR to be read/written +/// +/// pub struct kvm_msr_entry { +/// pub index: __u32, +/// pub reserved: __u32, +/// pub data: __u64, +/// } +pub use kvm_bindings::kvm_msr_entry as MsrEntry; + +/// Array of MSR entries +/// +/// +/// pub struct kvm_msrs { +/// pub nmsrs: __u32, +/// pub pad: __u32, +/// pub entries: __IncompleteArrayField, +/// } +pub use kvm_bindings::kvm_msrs as MsrEntries; + +/// Standard registers (general purpose plus instruction pointer and flags) +/// +/// pub struct kvm_regs { +/// pub rax: __u64, +/// pub rbx: __u64, +/// pub rcx: __u64, +/// pub rdx: __u64, +/// pub rsi: __u64, +/// pub rdi: __u64, +/// pub rsp: __u64, +/// pub rbp: __u64, +/// pub r8: __u64, +/// pub r9: __u64, +/// pub r10: __u64, +/// pub r11: __u64, +/// pub r12: __u64, +/// pub r13: __u64, +/// pub r14: __u64, +/// pub r15: __u64, +/// pub rip: __u64, +/// pub rflags: __u64, +/// } +pub use kvm_bindings::kvm_regs as StandardRegisters; + +/// Special registers (segment, task, descriptor table, control, and additional +/// registers, plus the interrupt bitmap) +/// +/// pub struct kvm_sregs { +/// pub cs: kvm_segment, +/// pub ds: kvm_segment, +/// pub es: kvm_segment, +/// pub fs: kvm_segment, +/// pub gs: kvm_segment, +/// pub ss: kvm_segment, +/// pub tr: kvm_segment, +/// pub ldt: kvm_segment, +/// pub gdt: kvm_dtable, +/// pub idt: kvm_dtable, +/// pub cr0: __u64, +/// pub cr2: __u64, +/// pub cr3: __u64, +/// pub cr4: __u64, +/// pub cr8: __u64, +/// pub efer: __u64, +/// pub apic_base: __u64, +/// pub interrupt_bitmap: [__u64; 4usize], +/// } +pub use kvm_bindings::kvm_sregs as SpecialRegisters; + +/// Segment register (used for CS, DS, ES, FS, GS, SS) +/// +/// pub struct kvm_segment { +/// pub base: __u64, +/// pub limit: __u32, +/// pub selector: __u16, +/// pub type_: __u8, +/// pub present: __u8, +/// pub dpl: __u8, +/// pub db: __u8, +/// pub s: __u8, +/// pub l: __u8, +/// pub g: __u8, +/// pub avl: __u8, +/// pub unusable: __u8, +/// pub padding: __u8, +/// } +pub use kvm_bindings::kvm_segment as SegmentRegister; + +/// Descriptor Table +/// +/// pub struct kvm_dtable { +/// pub base: __u64, +/// pub limit: __u16, +/// pub padding: [__u16; 3usize], +/// } +pub use kvm_bindings::kvm_dtable as DescriptorTable; + +/// Floating Pointe Unit State +/// +/// pub struct kvm_fpu { +/// pub fpr: [[__u8; 16usize]; 8usize], +/// pub fcw: __u16, +/// pub fsw: __u16, +/// pub ftwx: __u8, +/// pub pad1: __u8, +/// pub last_opcode: __u16, +/// pub last_ip: __u64, +/// pub last_dp: __u64, +/// pub xmm: [[__u8; 16usize]; 16usize], +/// pub mxcsr: __u32, +/// pub pad2: __u32, +/// } +pub use kvm_bindings::kvm_fpu as FpuState; + +/// Entry describing a CPUID feature/leaf. Features can be set as responses to +/// the CPUID instruction. +/// pub struct kvm_cpuid_entry2 { +/// pub function: __u32, +/// pub index: __u32, +/// pub flags: __u32, +/// pub eax: __u32, +/// pub ebx: __u32, +/// pub ecx: __u32, +/// pub edx: __u32, +/// pub padding: [__u32; 3usize], +/// } +use kvm_bindings::kvm_cpuid_entry2 as CpuIdEntry2; + +/// Array of CpuId2 entries, each of which describing a feature/leaf to be set +/// pub struct kvm_cpuid2 { +/// pub nent: __u32, +/// pub padding: __u32, +/// pub entries: __IncompleteArrayField, +/// } +use kvm_bindings::kvm_cpuid2 as CpuId2; + +/// Unix definition of the LAPIC state, the set of memory mapped registers that +/// describe the Local APIC. Unix-based VMMs only require 1KB of memory to +/// describe the LAPIC state. +/// +/// pub struct kvm_lapic_state { +/// pub regs: [::std::os::raw::c_char; 1024usize], +/// } +#[cfg(unix)] +pub use kvm_bindings::kvm_lapic_state as LapicState; + +/// Windows definition of the LAPIC state, the set of memory mapped registers +/// that describe the Local APIC. Windows-based VMMs require 4KB of memory to +/// describe the LAPIC state, or the Windows APIs will fail, even though the +/// architecture-specified space requirement is only 1KB. +#[cfg(windows)] +#[repr(C)] +#[derive(Copy, Clone)] +pub struct LapicState { + pub regs: [::std::os::raw::c_char; 4096usize], +} + +#[cfg(windows)] +impl Default for LapicState { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} + +#[cfg(windows)] +impl ::std::fmt::Debug for LapicState { + fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + self.regs[..].fmt(fmt) + } +} + +#[test] +fn vcpu_test_layout_lapic_state() { + assert_eq!( + ::std::mem::size_of::(), + 1024usize, + concat!("Size of: ", stringify!(LapicState)) + ); + assert_eq!( + ::std::mem::align_of::(), + 1usize, + concat!("Alignment of ", stringify!(LapicState)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).regs as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(LapicState), + "::", + stringify!(regs) + ) + ); +} + +// 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 `CpuId2` which has a zero length array at the end. +/// Hides the zero length array behind a bounds check. +pub struct CpuId { + /// Wrapper over `CpuId2` from which we only use the first element. + kvm_cpuid: Vec, + // Number of `CpuIdEntry2` structs at the end of CpuId2. + allocated_len: usize, +} + +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(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)] +impl PartialEq for CpuId { + fn eq(&self, other: &CpuId) -> bool { + let entries: &[CpuIdEntry2] = + unsafe { self.kvm_cpuid[0].entries.as_slice(self.allocated_len) }; + let other_entries: &[CpuIdEntry2] = + unsafe { self.kvm_cpuid[0].entries.as_slice(other.allocated_len) }; + self.allocated_len == other.allocated_len && entries == other_entries + } +} + +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 [CpuIdEntry2] { + // 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 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 CpuId2 { + &mut self.kvm_cpuid[0] + } +}