diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..da8a7ad --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,2 @@ +# Default owners for everything in the repo. Later matches take precedence. +* @jennymankin diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..5b63497 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "vmm-vcpu" +version = "0.1.0" +authors = ["Jenny Mankin ", + "Timo Kreuzer ", + "Alessandro Pilotti "] +description = "Hypervisor-agnostic abstraction crate over virtual CPU functionality" +repository = "https://github.com/rust-vmm/vmm-vcpu" +readme = "README.md" +license = "Apache-2.0 or MIT" + +[dependencies] +byteorder = "*" + +[target.'cfg(unix)'.dependencies] +kvm-bindings = "0.1" + +[target.'cfg(unix)'.dev-dependencies] +kvm-ioctls = "0.1.0" + +libc = ">=0.2.39" 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/README.md b/README.md index a846a49..d1894c3 100644 --- a/README.md +++ b/README.md @@ -1 +1,46 @@ -# 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 either of: + +* Apache License, Version 2.0 (LICENSE-APACHE or (https://www.apache.org/licenses/LICENSE-2.0)) + +* MIT license (LICENSE-MIT or (https://opensource.org/licenses/MIT)) \ 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..34a362f --- /dev/null +++ b/src/arm.rs @@ -0,0 +1,16 @@ +// Copyright 2018-2019 CrowdStrike, Inc. +// SPDX-License-Identifier: Apache-2.0 OR MIT + +/// ARM-specific data structures. + +/// +/// Type of CPU to present to the guest, and the optional features it should have. +/// +#[cfg(windows)] +pub struct VcpuInit { + pub target: u32, + pub features: [u32; 7usize], +} + +#[cfg(unix)] +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..de00620 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,30 @@ +// Copyright 2018-2019 CrowdStrike, Inc. +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#![allow(unused)] +#![deny(missing_docs)] + +//! A generic abstraction around virtual CPU (vCPU) functionality +//! +//! This crate offers a trait abstraction for vCPUs, as well as architecture +//! and platform-dependent structure definitions necessary for vCPU functions. +//! +//! # Platform support +//! +//! - x86_64 +//! - arm64 (experimental) +//! + +/// Module defining vCPU trait and required data structures +pub mod vcpu; + +#[cfg(unix)] +extern crate kvm_bindings; + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +/// Module defining x86_64 architecture-dependent structures +pub mod x86_64; + +#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] +/// Module defining arm architecture-dependent structures +pub mod arm; diff --git a/src/vcpu.rs b/src/vcpu.rs new file mode 100644 index 0000000..feb81ea --- /dev/null +++ b/src/vcpu.rs @@ -0,0 +1,259 @@ +// Copyright 2018-2019 CrowdStrike, Inc. +// SPDX-License-Identifier: Apache-2.0 or MIT +// +// Portions Copyright 2018 Cloudbase Solutions Srl +// SPDX-License-Identifier: Apache-2.0 or MIT +// +// 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}; + +#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] +pub use arm::VcpuInit; + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +pub use x86_64::{ + StandardRegisters, SpecialRegisters, FpuState, MsrEntries, MsrEntry, + CpuId, LapicState +}; + +/// +/// Reasons for vCPU exits for Windows (Hyper-V) platforms +/// +#[derive(Debug)] +#[cfg(windows)] +pub enum VcpuExit { + + /// Corresponds to WHvRunVpExitReasonNone + None, + /// Corresponds to WHvRunVpExitReasonMemoryAccess + MemoryAccess, + /// Corresponds to WHvRunVpExitReasonX64IoPortAccess + IoPortAccess, + /// Corresponds to WHvRunVpExitReasonUnrecoverableException + UnrecoverableException, + /// Corresponds to WHvRunVpExitReasonInvalidVpRegisterValue + InvalidVpRegisterValue, + /// Corresponds to WHvRunVpExitReasonUnsupportedFeature + UnsupportedFeature, + /// Corresponds to WHvRunVpExitReasonX64InterruptWindow + IrqWindowOpen, + /// Corresponds to WHvRunVpExitReasonX64Halt + Hlt, + /// Corresponds to WHvRunVpExitReasonX64ApicEoi + IoapicEoi, + /// Corresponds to WHvRunVpExitReasonX64MsrAccess + MsrAccess, + /// Corresponds to WHvRunVpExitReasonX64Cpuid + Cpuid, + /// Corresponds to WHvRunVpExitReasonException + Exception, + /// Corresponds to WHvRunVpExitReasonCanceled + Canceled, +} + +/// +/// Reasons for vCPU exits for Unix-based (KVM) platforms +/// +#[derive(Debug)] +#[cfg(unix)] +pub enum VcpuExit<'a> { + /// Corresponds to KVM_EXIT_UNKNOWN. + Unknown, + /// Corresponds to KVM_EXIT_EXCEPTION. + Exception, + /// 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 [run()](struct.VcpuFd.html#method.run) + /// is called again. + IoIn(u16 /* port */, &'a mut [u8] /* data */), + /// Corresponds to KVM_EXIT_HYPERCALL. + Hypercall, + /// Corresponds to KVM_EXIT_DEBUG. + Debug, + /// Corresponds to KVM_EXIT_HLT. + Hlt, + /// A read instruction was run against the given MMIO address. + /// + /// 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]), + /// 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 specialized `Result` type for VCPU operations +/// +/// 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; + +/// +/// Trait to represent a virtual CPU +/// +/// This crate provides a hypervisor-agnostic interface to common virtual CPU +/// functionality +/// +pub trait Vcpu { + /// Associated type representing the run context on a vCPU exit + 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 Point 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: &VcpuInit) -> 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/mod.rs b/src/x86_64/mod.rs new file mode 100644 index 0000000..48116a8 --- /dev/null +++ b/src/x86_64/mod.rs @@ -0,0 +1,237 @@ +// Copyright 2018-2019 CrowdStrike, Inc. +// SPDX-License-Identifier: Apache-2.0 OR MIT + +/// Windows definitions of data structures +#[cfg(windows)] +pub mod windows; + +use std::mem::size_of; + +/// +/// Export generically-named explicitly-defined structures for Windows platforms +/// +#[cfg(windows)] +pub use { + self::windows::StandardRegisters, + self::windows::SpecialRegisters, + self::windows::FpuState, + self::windows::MsrEntries, + self::windows::MsrEntry, + self::windows::SegmentRegister, + self::windows::DescriptorTable, + self::windows::CpuId2, + self::windows::CpuIdEntry2, + self::windows::LapicState, + }; + +/// +/// Export generically-named wrappers of kvm-bindings for Unix-based platforms +/// +#[cfg(unix)] +pub use { + kvm_bindings::kvm_regs as StandardRegisters, + kvm_bindings::kvm_sregs as SpecialRegisters, + kvm_bindings::kvm_msr_entry as MsrEntry, + kvm_bindings::kvm_msrs as MsrEntries, + kvm_bindings::kvm_segment as SegmentRegister, + kvm_bindings::kvm_dtable as DescriptorTable, + kvm_bindings::kvm_fpu as FpuState, + kvm_bindings::kvm_cpuid_entry2 as CpuIdEntry2, + kvm_bindings::kvm_cpuid2 as CpuId2, + kvm_bindings::kvm_lapic_state as LapicState, + }; + +/// 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. +pub 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) +} + +/// 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_CPUID_ENTRIES: usize = 80; + +/// Wrapper over the `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 `CpuId2` from which we only use the first element. + pub cpuid_vec: Vec, + /// Number of `CpuIdEntry2` structs at the end of CpuId2. + pub allocated_len: usize, +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +impl Clone for CpuId { + fn clone(&self) -> Self { + let mut cpuid_vec = Vec::with_capacity(self.cpuid_vec.len()); + for _ in 0..self.cpuid_vec.len() { + cpuid_vec.push(CpuId2::default()); + } + + let num_bytes = self.cpuid_vec.len() * size_of::(); + + let src_byte_slice = + unsafe { std::slice::from_raw_parts(self.cpuid_vec.as_ptr() as *const u8, num_bytes) }; + + let dst_byte_slice = + unsafe { std::slice::from_raw_parts_mut(cpuid_vec.as_mut_ptr() as *mut u8, num_bytes) }; + + dst_byte_slice.copy_from_slice(src_byte_slice); + + CpuId { + cpuid_vec, + allocated_len: self.allocated_len, + } + } +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +impl PartialEq for CpuId { + fn eq(&self, other: &CpuId) -> bool { + let entries: &[CpuIdEntry2] = + unsafe { self.cpuid_vec[0].entries.as_slice(self.allocated_len) }; + let other_entries: &[CpuIdEntry2] = + unsafe { self.cpuid_vec[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 contains at most `array_len` CPUID entries. + /// + /// # Arguments + /// + /// * `array_len` - Maximum number of CPUID entries. + /// + /// # Example + /// + /// ``` + /// use vmm_vcpu::x86_64::CpuId; + /// let cpu_id = CpuId::new(32); + /// ``` + pub fn new(array_len: usize) -> CpuId { + let mut cpuid_vec = vec_with_array_field::(array_len); + cpuid_vec[0].nent = array_len as u32; + + CpuId { + cpuid_vec, + allocated_len: array_len, + } + } + + /// Creates a new `CpuId` structure based on a supplied vector of `CpuIdEntry2`. + /// + /// # Arguments + /// + /// * `entries` - The vector of `CpuIdEntry2` entries. + /// + /// # Example + /// + /// ```rust + /// # extern crate vmm_vcpu; + /// + /// use vmm_vcpu::x86_64::CpuIdEntry2; + /// use vmm_vcpu::x86_64::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 = CpuIdEntry2 { + /// 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: &[CpuIdEntry2]) -> CpuId { + let mut cpuid_vec = vec_with_array_field::(entries.len()); + cpuid_vec[0].nent = entries.len() as u32; + + unsafe { + cpuid_vec[0] + .entries + .as_mut_slice(entries.len()) + .copy_from_slice(entries); + } + + CpuId { + cpuid_vec, + allocated_len: entries.len(), + } + } + + /// Returns the mutable entries slice so they can be modified before passing to the VCPU. + /// + /// ```cfg(unix) + /// extern crate kvm_ioctls; + /// + /// use kvm_ioctls::Kvm; + /// use vmm_vcpu::x86_64::{CpuId, MAX_CPUID_ENTRIES}; + /// + /// # fn main() { + /// let kvm = Kvm::new().unwrap(); + /// let mut cpuid = kvm.get_supported_cpuid(MAX_CPUID_ENTRIES).unwrap(); + /// let cpuid_entries = cpuid.mut_entries_slice(); + /// # } + /// + /// ``` + /// + 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.cpuid_vec[0].nent as usize > self.allocated_len { + self.cpuid_vec[0].nent = self.allocated_len as u32; + } + let nent = self.cpuid_vec[0].nent as usize; + unsafe { self.cpuid_vec[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.cpuid_vec[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.cpuid_vec[0] + } +} diff --git a/src/x86_64/windows.rs b/src/x86_64/windows.rs new file mode 100644 index 0000000..87a2139 --- /dev/null +++ b/src/x86_64/windows.rs @@ -0,0 +1,1276 @@ +// 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. + +/// +/// Standard registers (general purpose plus instruction pointer and flags) +/// +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct StandardRegisters { + /// RAX general purpose register + pub rax: u64, + /// RBX general purpose register + pub rbx: u64, + /// RCX general purpose register + pub rcx: u64, + /// RDX general purpose register + pub rdx: u64, + /// Source index general purpose register + pub rsi: u64, + /// Destination index general purpose register + pub rdi: u64, + /// Stack Pointer register + pub rsp: u64, + /// Base Pointer register + pub rbp: u64, + /// R8 general purpose register + pub r8: u64, + /// R9 general purpose register + pub r9: u64, + /// R10 general purpose register + pub r10: u64, + /// R11 general purpose register + pub r11: u64, + /// R12 general purpose register + pub r12: u64, + /// R13 general purpose register + pub r13: u64, + /// R14 general purpose register + pub r14: u64, + /// R15 general purpose register + pub r15: u64, + /// Instruction Pointer register + pub rip: u64, + /// RFLAGS register + pub rflags: u64, +} + +/// +/// Special registers (segment, task, descriptor table, control, and additional +/// registers, plus the interrupt bitmap) +/// +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct SpecialRegisters { + /// Code Segment register + pub cs: SegmentRegister, + /// Data Segment register + pub ds: SegmentRegister, + /// Extra Segment register + pub es: SegmentRegister, + /// Additional segment register + pub fs: SegmentRegister, + /// Additional segment register + pub gs: SegmentRegister, + /// Stack Segment register + pub ss: SegmentRegister, + /// Task register + pub tr: SegmentRegister, + /// Local Descriptor Table register + pub ldt: SegmentRegister, + /// Global Descriptor Table register + pub gdt: DescriptorTable, + /// Interrupt Descriptor Table register + pub idt: DescriptorTable, + /// Control Register 0 + pub cr0: u64, + /// Control Register 2 + pub cr2: u64, + /// Control Register 3 + pub cr3: u64, + /// Control Register 4 + pub cr4: u64, + /// Control Register 8 + pub cr8: u64, + /// Extended Feature Enable register + pub efer: u64, + /// APIC base register + pub apic_base: u64, + /// Bitmap of external interrupts (unused) + pub interrupt_bitmap: [u64; 4usize], +} + +/// +/// Segment register (used for CS, DS, ES, FS, GS, SS) +/// +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct SegmentRegister { + /// Starting memory address of the segment + pub base: u64, + /// Length of the segment + pub limit: u32, + /// Offset of a descriptor table entry + pub selector: u16, + /// Segment type + pub type_: u8, + /// Present + pub present: u8, + /// Descriptor privilege level + pub dpl: u8, + /// Default (set to 0 when Long set) + pub db: u8, + /// System-segment field + pub s: u8, + /// Long field + pub l: u8, + /// Granularity field + pub g: u8, + /// Available + pub avl: u8, + /// Unused + pub unusable: u8, + /// Padding + pub padding: u8, +} + +/// +/// Descriptor Table +/// +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct DescriptorTable { + /// Base address + pub base: u64, + /// Limit (size of table in bytes) + pub limit: u16, + /// Padding + pub padding: [u16; 3usize], +} + +/// +/// Floating Point Unit State +/// +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct FpuState { + /// Floating point registers + pub fpr: [[u8; 16usize]; 8usize], + /// Floating Point Control register + pub fcw: u16, + /// Floating Point Status register + pub fsw: u16, + /// Floating Point Tag register + pub ftwx: u8, + /// Padding + pub pad1: u8, + /// Floating point exception state - last opcode + pub last_opcode: u16, + /// Floating point exception state - last instruction pointer + pub last_ip: u64, + /// Floating point exception state - last data pointer + pub last_dp: u64, + /// SSE registers + pub xmm: [[u8; 16usize]; 16usize], + /// Media control and status register + pub mxcsr: u32, + /// Padding + pub pad2: u32, +} + +/// +/// Single MSR to be read/written +/// +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct MsrEntry { + /// MSR address index + pub index: u32, + /// Reserved + pub reserved: u32, + /// MSR data + pub data: u64, +} + +/// +/// Array of MSR entries +/// +#[repr(C)] +#[derive(Debug, Default)] +pub struct MsrEntries { + /// Number of MSR entries in array + pub nmsrs: u32, + /// Padding + pub pad: u32, + /// Array of MSR entries + pub entries: __IncompleteArrayField, +} + +/// +/// Entry describing a CPUID feature/leaf. Features can be set as responses to +/// the CPUID instruction. +/// +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct CpuIdEntry2 { + /// The EAX value used to obtain the entry + pub function: u32, + /// The ECX value used to obtain the entry (if affected) + pub index: u32, + /// An OR of zero or more of the following: + /// - The index field is valid + /// - CPUID for this function returns different values for successive invocations + /// - Set if this entry is the first entry to be read by a CPU + pub flags: u32, + /// Value returned in EAX by CPUID for this function/index combination + pub eax: u32, + /// Value returned in EBX by CPUID for this function/index combination + pub ebx: u32, + /// Value returned in ECX by CPUID for this function/index combination + pub ecx: u32, + /// Value returned in EDX by CPUID for this function/index combination + pub edx: u32, + /// Padding + pub padding: [u32; 3usize], +} + +/// +/// Array of CpuId2 entries, each of which describing a feature/leaf to be set +/// +#[repr(C)] +#[derive(Debug, Default)] +pub struct CpuId2 { + /// Number of entries + pub nent: u32, + /// Padding + pub padding: u32, + /// Array of entries + pub entries: __IncompleteArrayField, +} + +/// 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. +#[repr(C)] +#[derive(Copy, Clone)] +pub struct LapicState { + /// Set of all registers that describe the Local APIC state + pub regs: [::std::os::raw::c_char; 4096usize], +} + +impl Default for LapicState { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} + +impl ::std::fmt::Debug for LapicState { + fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + self.regs[..].fmt(fmt) + } +} + +#[repr(C)] +#[derive(Default)] +/// Zero-sized array +pub struct __IncompleteArrayField(::std::marker::PhantomData, [T; 0]); + +impl __IncompleteArrayField { + #[inline] + /// Creates a new `__IncompleteArrayField` structure + pub fn new() -> Self { + __IncompleteArrayField(::std::marker::PhantomData, []) + } + #[inline] + /// Get a pointer. Using this pointer is unsafe. + pub unsafe fn as_ptr(&self) -> *const T { + ::std::mem::transmute(self) + } + #[inline] + /// Get a mutable pointer. Using this pointer is unsafe. + pub unsafe fn as_mut_ptr(&mut self) -> *mut T { + ::std::mem::transmute(self) + } + #[inline] + /// Extracts a slice ontaining the entire array. Using this slice is unsafe. + pub unsafe fn as_slice(&self, len: usize) -> &[T] { + ::std::slice::from_raw_parts(self.as_ptr(), len) + } + #[inline] + /// Extacts a mutable slice containing the entire array. Using this slice + /// is unsafe. + pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] { + ::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len) + } +} + +impl ::std::fmt::Debug for __IncompleteArrayField { + fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + fmt.write_str("__IncompleteArrayField") + } +} + +#[cfg(test)] +impl ::std::clone::Clone for __IncompleteArrayField { + #[inline] + fn clone(&self) -> Self { + Self::new() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn vcpu_test_layout_lapic_state() { + assert_eq!( + ::std::mem::size_of::(), + 4096usize, + 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) + ) + ); + } + + #[test] + fn vcpu_test_layout_standard_registers() { + assert_eq!( + ::std::mem::size_of::(), + 144usize, + concat!("Size of: ", stringify!(StandardRegisters)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(StandardRegisters)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).rax as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(StandardRegisters), + "::", + stringify!(rax) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).rbx as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(StandardRegisters), + "::", + stringify!(rbx) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).rcx as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(StandardRegisters), + "::", + stringify!(rcx) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).rdx as *const _ as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(StandardRegisters), + "::", + stringify!(rdx) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).rsi as *const _ as usize }, + 32usize, + concat!( + "Offset of field: ", + stringify!(StandardRegisters), + "::", + stringify!(rsi) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).rdi as *const _ as usize }, + 40usize, + concat!( + "Offset of field: ", + stringify!(StandardRegisters), + "::", + stringify!(rdi) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).rsp as *const _ as usize }, + 48usize, + concat!( + "Offset of field: ", + stringify!(StandardRegisters), + "::", + stringify!(rsp) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).rbp as *const _ as usize }, + 56usize, + concat!( + "Offset of field: ", + stringify!(StandardRegisters), + "::", + stringify!(rbp) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).r8 as *const _ as usize }, + 64usize, + concat!( + "Offset of field: ", + stringify!(StandardRegisters), + "::", + stringify!(r8) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).r9 as *const _ as usize }, + 72usize, + concat!( + "Offset of field: ", + stringify!(StandardRegisters), + "::", + stringify!(r9) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).r10 as *const _ as usize }, + 80usize, + concat!( + "Offset of field: ", + stringify!(StandardRegisters), + "::", + stringify!(r10) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).r11 as *const _ as usize }, + 88usize, + concat!( + "Offset of field: ", + stringify!(StandardRegisters), + "::", + stringify!(r11) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).r12 as *const _ as usize }, + 96usize, + concat!( + "Offset of field: ", + stringify!(StandardRegisters), + "::", + stringify!(r12) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).r13 as *const _ as usize }, + 104usize, + concat!( + "Offset of field: ", + stringify!(StandardRegisters), + "::", + stringify!(r13) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).r14 as *const _ as usize }, + 112usize, + concat!( + "Offset of field: ", + stringify!(StandardRegisters), + "::", + stringify!(r14) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).r15 as *const _ as usize }, + 120usize, + concat!( + "Offset of field: ", + stringify!(StandardRegisters), + "::", + stringify!(r15) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).rip as *const _ as usize }, + 128usize, + concat!( + "Offset of field: ", + stringify!(StandardRegisters), + "::", + stringify!(rip) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).rflags as *const _ as usize }, + 136usize, + concat!( + "Offset of field: ", + stringify!(StandardRegisters), + "::", + stringify!(rflags) + ) + ); + } + + #[test] + fn vcpu_test_layout_special_registers() { + assert_eq!( + ::std::mem::size_of::(), + 312usize, + concat!("Size of: ", stringify!(SpecialRegisters)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(SpecialRegisters)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).cs as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(SpecialRegisters), + "::", + stringify!(cs) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).ds as *const _ as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(SpecialRegisters), + "::", + stringify!(ds) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).es as *const _ as usize }, + 48usize, + concat!( + "Offset of field: ", + stringify!(SpecialRegisters), + "::", + stringify!(es) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).fs as *const _ as usize }, + 72usize, + concat!( + "Offset of field: ", + stringify!(SpecialRegisters), + "::", + stringify!(fs) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).gs as *const _ as usize }, + 96usize, + concat!( + "Offset of field: ", + stringify!(SpecialRegisters), + "::", + stringify!(gs) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).ss as *const _ as usize }, + 120usize, + concat!( + "Offset of field: ", + stringify!(SpecialRegisters), + "::", + stringify!(ss) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).tr as *const _ as usize }, + 144usize, + concat!( + "Offset of field: ", + stringify!(SpecialRegisters), + "::", + stringify!(tr) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).ldt as *const _ as usize }, + 168usize, + concat!( + "Offset of field: ", + stringify!(SpecialRegisters), + "::", + stringify!(ldt) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).gdt as *const _ as usize }, + 192usize, + concat!( + "Offset of field: ", + stringify!(SpecialRegisters), + "::", + stringify!(gdt) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).idt as *const _ as usize }, + 208usize, + concat!( + "Offset of field: ", + stringify!(SpecialRegisters), + "::", + stringify!(idt) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).cr0 as *const _ as usize }, + 224usize, + concat!( + "Offset of field: ", + stringify!(SpecialRegisters), + "::", + stringify!(cr0) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).cr2 as *const _ as usize }, + 232usize, + concat!( + "Offset of field: ", + stringify!(SpecialRegisters), + "::", + stringify!(cr2) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).cr3 as *const _ as usize }, + 240usize, + concat!( + "Offset of field: ", + stringify!(SpecialRegisters), + "::", + stringify!(cr3) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).cr4 as *const _ as usize }, + 248usize, + concat!( + "Offset of field: ", + stringify!(SpecialRegisters), + "::", + stringify!(cr4) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).cr8 as *const _ as usize }, + 256usize, + concat!( + "Offset of field: ", + stringify!(SpecialRegisters), + "::", + stringify!(cr8) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).efer as *const _ as usize }, + 264usize, + concat!( + "Offset of field: ", + stringify!(SpecialRegisters), + "::", + stringify!(efer) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).apic_base as *const _ as usize }, + 272usize, + concat!( + "Offset of field: ", + stringify!(SpecialRegisters), + "::", + stringify!(apic_base) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).interrupt_bitmap as *const _ as usize }, + 280usize, + concat!( + "Offset of field: ", + stringify!(SpecialRegisters), + "::", + stringify!(interrupt_bitmap) + ) + ); + } + + #[test] + fn vcpu_test_layout_msr_entry() { + assert_eq!( + ::std::mem::size_of::(), + 16usize, + concat!("Size of: ", stringify!(MsrEntry)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(MsrEntry)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).index as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(MsrEntry), + "::", + stringify!(index) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).reserved as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(MsrEntry), + "::", + stringify!(reserved) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).data as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(MsrEntry), + "::", + stringify!(data) + ) + ); + } + + #[test] + fn vcpu_test_layout_msr_entries() { + assert_eq!( + ::std::mem::size_of::(), + 8usize, + concat!("Size of: ", stringify!(MsrEntries)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(MsrEntries)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).nmsrs as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(MsrEntries), + "::", + stringify!(nmsrs) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).pad as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(MsrEntries), + "::", + stringify!(pad) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).entries as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(MsrEntries), + "::", + stringify!(entries) + ) + ); + } + + #[test] + fn vcpu_test_layout_segment_register() { + assert_eq!( + ::std::mem::size_of::(), + 24usize, + concat!("Size of: ", stringify!(SegmentRegister)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(SegmentRegister)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).base as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(SegmentRegister), + "::", + stringify!(base) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).limit as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(SegmentRegister), + "::", + stringify!(limit) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).selector as *const _ as usize }, + 12usize, + concat!( + "Offset of field: ", + stringify!(SegmentRegister), + "::", + stringify!(selector) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).type_ as *const _ as usize }, + 14usize, + concat!( + "Offset of field: ", + stringify!(SegmentRegister), + "::", + stringify!(type_) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).present as *const _ as usize }, + 15usize, + concat!( + "Offset of field: ", + stringify!(SegmentRegister), + "::", + stringify!(present) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).dpl as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(SegmentRegister), + "::", + stringify!(dpl) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).db as *const _ as usize }, + 17usize, + concat!( + "Offset of field: ", + stringify!(SegmentRegister), + "::", + stringify!(db) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).s as *const _ as usize }, + 18usize, + concat!( + "Offset of field: ", + stringify!(SegmentRegister), + "::", + stringify!(s) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).l as *const _ as usize }, + 19usize, + concat!( + "Offset of field: ", + stringify!(SegmentRegister), + "::", + stringify!(l) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).g as *const _ as usize }, + 20usize, + concat!( + "Offset of field: ", + stringify!(SegmentRegister), + "::", + stringify!(g) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).avl as *const _ as usize }, + 21usize, + concat!( + "Offset of field: ", + stringify!(SegmentRegister), + "::", + stringify!(avl) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).unusable as *const _ as usize }, + 22usize, + concat!( + "Offset of field: ", + stringify!(SegmentRegister), + "::", + stringify!(unusable) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).padding as *const _ as usize }, + 23usize, + concat!( + "Offset of field: ", + stringify!(SegmentRegister), + "::", + stringify!(padding) + ) + ); + } + + #[test] + fn vcpu_test_layout_descriptor_table() { + assert_eq!( + ::std::mem::size_of::(), + 16usize, + concat!("Size of: ", stringify!(DescriptorTable)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(DescriptorTable)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).base as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(DescriptorTable), + "::", + stringify!(base) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).limit as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(DescriptorTable), + "::", + stringify!(limit) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).padding as *const _ as usize }, + 10usize, + concat!( + "Offset of field: ", + stringify!(DescriptorTable), + "::", + stringify!(padding) + ) + ); + } + + #[test] + fn vcpu_test_layout_fpu_state() { + assert_eq!( + ::std::mem::size_of::(), + 416usize, + concat!("Size of: ", stringify!(FpuState)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(FpuState)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).fpr as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(FpuState), + "::", + stringify!(fpr) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).fcw as *const _ as usize }, + 128usize, + concat!( + "Offset of field: ", + stringify!(FpuState), + "::", + stringify!(fcw) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).fsw as *const _ as usize }, + 130usize, + concat!( + "Offset of field: ", + stringify!(FpuState), + "::", + stringify!(fsw) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).ftwx as *const _ as usize }, + 132usize, + concat!( + "Offset of field: ", + stringify!(FpuState), + "::", + stringify!(ftwx) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).pad1 as *const _ as usize }, + 133usize, + concat!( + "Offset of field: ", + stringify!(FpuState), + "::", + stringify!(pad1) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).last_opcode as *const _ as usize }, + 134usize, + concat!( + "Offset of field: ", + stringify!(FpuState), + "::", + stringify!(last_opcode) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).last_ip as *const _ as usize }, + 136usize, + concat!( + "Offset of field: ", + stringify!(FpuState), + "::", + stringify!(last_ip) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).last_dp as *const _ as usize }, + 144usize, + concat!( + "Offset of field: ", + stringify!(FpuState), + "::", + stringify!(last_dp) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).xmm as *const _ as usize }, + 152usize, + concat!( + "Offset of field: ", + stringify!(FpuState), + "::", + stringify!(xmm) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).mxcsr as *const _ as usize }, + 408usize, + concat!( + "Offset of field: ", + stringify!(FpuState), + "::", + stringify!(mxcsr) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).pad2 as *const _ as usize }, + 412usize, + concat!( + "Offset of field: ", + stringify!(FpuState), + "::", + stringify!(pad2) + ) + ); + } + + #[test] + fn vcpu_test_layout_cpuid_entry2() { + assert_eq!( + ::std::mem::size_of::(), + 40usize, + concat!("Size of: ", stringify!(CpuIdEntry2)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(CpuIdEntry2)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).function as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(CpuIdEntry2), + "::", + stringify!(function) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).index as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(CpuIdEntry2), + "::", + stringify!(index) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).flags as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(CpuIdEntry2), + "::", + stringify!(flags) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).eax as *const _ as usize }, + 12usize, + concat!( + "Offset of field: ", + stringify!(CpuIdEntry2), + "::", + stringify!(eax) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).ebx as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(CpuIdEntry2), + "::", + stringify!(ebx) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).ecx as *const _ as usize }, + 20usize, + concat!( + "Offset of field: ", + stringify!(CpuIdEntry2), + "::", + stringify!(ecx) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).edx as *const _ as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(CpuIdEntry2), + "::", + stringify!(edx) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).padding as *const _ as usize }, + 28usize, + concat!( + "Offset of field: ", + stringify!(CpuIdEntry2), + "::", + stringify!(padding) + ) + ); + } + + #[test] + fn vcpu_test_layout_cpuid2() { + assert_eq!( + ::std::mem::size_of::(), + 8usize, + concat!("Size of: ", stringify!(CpuId2)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(CpuId2)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).nent as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(CpuId2), + "::", + stringify!(nent) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).padding as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(CpuId2), + "::", + stringify!(padding) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).entries as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(CpuId2), + "::", + stringify!(entries) + ) + ); + } + +}