From 8c6b98ab69738684b289a95d4ef1176081d6d39d Mon Sep 17 00:00:00 2001 From: Jenny Mankin Date: Mon, 15 Apr 2019 19:40:51 -0700 Subject: [PATCH 1/3] Implementation of vCPU crate Created a crate with trait abstractions for virtual CPUs, including exporting necessary architecture-specific generic structures. --- Cargo.toml | 11 ++ LICENSE | 201 ++++++++++++++++++++++++++++++ README.md | 43 ++++++- THIRD-PARTY | 27 ++++ src/arm.rs | 28 +++++ src/lib.rs | 23 ++++ src/vcpu.rs | 222 +++++++++++++++++++++++++++++++++ src/x86_64.rs | 331 ++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 885 insertions(+), 1 deletion(-) create mode 100644 Cargo.toml create mode 100644 LICENSE create mode 100644 THIRD-PARTY create mode 100644 src/arm.rs create mode 100644 src/lib.rs create mode 100644 src/vcpu.rs create mode 100644 src/x86_64.rs 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..119895b --- /dev/null +++ b/src/vcpu.rs @@ -0,0 +1,222 @@ +// 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 = "arm", target_arch = "aarch64"))] +pub use arm::{VcpuInit}; + +#[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: &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.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] + } +} From 8f775bdc89cb59e1e0f21ae151a214b6c433fa87 Mon Sep 17 00:00:00 2001 From: Jenny Mankin Date: Wed, 17 Apr 2019 20:26:45 -0700 Subject: [PATCH 2/3] Improvements based on PR comments - Removed dependency on kvm_bindings for Windows platforms - Used conditional compilation to export wrappers of kvm_bindings as generically-named structures for Unix-based platforms, and explicit definition of the same structures for Windows-based platforms. - Exported mode and data types as public - Changed licenses - Created separate files for explicitly-defined Windows structures - Moved all tests to a module at the end of a file --- Cargo.toml | 10 + LICENSE => LICENSE-APACHE | 1 + LICENSE-MIT | 24 + src/arm.rs | 32 +- src/lib.rs | 20 +- src/vcpu.rs | 30 +- src/x86_64.rs | 331 ----------- src/x86_64/mod.rs | 235 ++++++++ src/x86_64/windows.rs | 1185 +++++++++++++++++++++++++++++++++++++ 9 files changed, 1476 insertions(+), 392 deletions(-) rename LICENSE => LICENSE-APACHE (99%) create mode 100644 LICENSE-MIT delete mode 100644 src/x86_64.rs create mode 100644 src/x86_64/mod.rs create mode 100644 src/x86_64/windows.rs diff --git a/Cargo.toml b/Cargo.toml index d79d1db..cba140f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,8 +4,18 @@ 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" \ No newline at end of file diff --git a/LICENSE b/LICENSE-APACHE similarity index 99% rename from LICENSE rename to LICENSE-APACHE index 261eeb9..d645695 100644 --- a/LICENSE +++ b/LICENSE-APACHE @@ -1,3 +1,4 @@ + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ 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/src/arm.rs b/src/arm.rs index 03c6684..34a362f 100644 --- a/src/arm.rs +++ b/src/arm.rs @@ -1,28 +1,16 @@ // 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. +// SPDX-License-Identifier: Apache-2.0 OR MIT -/// -/// 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 -/// +/// ARM-specific data structures. +/// /// 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"))] +#[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 index 198635d..98a4829 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,23 +1,13 @@ // 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; +// SPDX-License-Identifier: Apache-2.0 OR MIT pub mod vcpu; +#[cfg(unix)] +extern crate kvm_bindings; + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -mod x86_64; +pub mod x86_64; #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] mod arm; diff --git a/src/vcpu.rs b/src/vcpu.rs index 119895b..ae29b00 100644 --- a/src/vcpu.rs +++ b/src/vcpu.rs @@ -1,8 +1,8 @@ // Copyright 2018-2019 CrowdStrike, Inc. -// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: Apache-2.0 or MIT // // Portions Copyright 2018 Cloudbase Solutions Srl -// SPDX-License-Identifier: Apache-2.0 +// 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 @@ -15,32 +15,15 @@ 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 = "arm", target_arch = "aarch64"))] -pub use arm::{VcpuInit}; +pub use arm::VcpuInit; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub use x86_64::{ - DescriptorTable, FpuState, MsrEntries, MsrEntry, SegmentRegister, SpecialRegisters, - StandardRegisters, + StandardRegisters, SpecialRegisters, FpuState, MsrEntries, MsrEntry, + CpuId, LapicState }; -/// -/// 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. /// @@ -54,7 +37,6 @@ pub enum VcpuExit<'a> { UnrecoverableException, InvalidVpRegisterValue, UnsupportedFeature, - InterruptWindow, MsrAccess, Cpuid, Canceled, @@ -75,7 +57,6 @@ pub enum VcpuExit<'a> { Unknown, Hypercall, Debug, - IrqWindowOpen, Shutdown, FailEntry, Intr, @@ -99,6 +80,7 @@ pub enum VcpuExit<'a> { Hlt, IoapicEoi, Exception, + IrqWindowOpen, } pub type Result = result::Result; diff --git a/src/x86_64.rs b/src/x86_64.rs deleted file mode 100644 index 7c797e0..0000000 --- a/src/x86_64.rs +++ /dev/null @@ -1,331 +0,0 @@ -// 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] - } -} diff --git a/src/x86_64/mod.rs b/src/x86_64/mod.rs new file mode 100644 index 0000000..23a7924 --- /dev/null +++ b/src/x86_64/mod.rs @@ -0,0 +1,235 @@ +// Copyright 2018-2019 CrowdStrike, Inc. +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#[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::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..c9c8386 --- /dev/null +++ b/src/x86_64/windows.rs @@ -0,0 +1,1185 @@ +// 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. + +/// +/// Single MSR to be read/written +/// +#[repr(C)] +#[derive(Debug, Default)] +pub struct MsrEntry { + pub index: u32, + pub reserved: u32, + pub data: u64, +} + +/// +/// Array of MSR entries +/// +#[repr(C)] +#[derive(Debug, Default)] +pub struct MsrEntries { + pub nmsrs: u32, + pub pad: u32, + pub entries: __IncompleteArrayField, +} + +/// +/// Standard registers (general purpose plus instruction pointer and flags) +/// +#[repr(C)] +#[derive(Debug, Default)] +pub struct StandardRegisters { + 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, +} + +/// +/// Special registers (segment, task, descriptor table, control, and additional +/// registers, plus the interrupt bitmap) +/// +#[repr(C)] +#[derive(Debug, Default)] +pub struct SpecialRegisters { + pub cs: SegmentRegister, + pub ds: SegmentRegister, + pub es: SegmentRegister, + pub fs: SegmentRegister, + pub gs: SegmentRegister, + pub ss: SegmentRegister, + pub tr: SegmentRegister, + pub ldt: SegmentRegister, + pub gdt: DescriptorTable, + pub idt: DescriptorTable, + 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], +} + +/// +/// Segment register (used for CS, DS, ES, FS, GS, SS) +/// +#[repr(C)] +#[derive(Debug, Default)] +pub struct SegmentRegister { + 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, +} + +/// +/// Descriptor Table +/// +#[repr(C)] +#[derive(Debug, Default)] +pub struct DescriptorTable { + pub base: u64, + pub limit: u16, + pub padding: [u16; 3usize], +} + +/// +/// Floating Point Unit State +/// +#[repr(C)] +#[derive(Debug, Default)] +pub struct FpuState { + 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, +} + +/// +/// 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 { + 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], +} + +/// +/// Array of CpuId2 entries, each of which describing a feature/leaf to be set +/// +#[repr(C)] +#[derive(Debug, Default)] +pub struct CpuId2 { + pub nent: u32, + pub padding: u32, + 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 { + 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)] +pub struct __IncompleteArrayField(::std::marker::PhantomData, [T; 0]); + +impl __IncompleteArrayField { + #[inline] + pub fn new() -> Self { + __IncompleteArrayField(::std::marker::PhantomData, []) + } + #[inline] + pub unsafe fn as_ptr(&self) -> *const T { + ::std::mem::transmute(self) + } + #[inline] + pub unsafe fn as_mut_ptr(&mut self) -> *mut T { + ::std::mem::transmute(self) + } + #[inline] + pub unsafe fn as_slice(&self, len: usize) -> &[T] { + ::std::slice::from_raw_parts(self.as_ptr(), len) + } + #[inline] + 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) + ) + ); + } + +} From ec9817b5ad11f084686e4c4676d662578ee3a046 Mon Sep 17 00:00:00 2001 From: Jenny Mankin <44877542+jennymankin@users.noreply.github.com> Date: Tue, 30 Apr 2019 15:01:34 -0700 Subject: [PATCH 3/3] Addressed PR comments - Added #[deny(missing_docs)] and added documentation everywhere - Divided VcpuExits by platform (conditionally compiled) and reordered by definition in platform-specific headers - Fixed licenses --- CODEOWNERS | 2 + Cargo.toml | 2 +- README.md | 6 +- src/lib.rs | 19 +++++- src/vcpu.rs | 85 ++++++++++++++++++++----- src/x86_64/mod.rs | 38 +++++------ src/x86_64/windows.rs | 145 ++++++++++++++++++++++++++++++++++-------- 7 files changed, 234 insertions(+), 63 deletions(-) create mode 100644 CODEOWNERS 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 index cba140f..5b63497 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,4 +18,4 @@ kvm-bindings = "0.1" [target.'cfg(unix)'.dev-dependencies] kvm-ioctls = "0.1.0" -libc = ">=0.2.39" \ No newline at end of file +libc = ">=0.2.39" diff --git a/README.md b/README.md index 4db6579..d1894c3 100644 --- a/README.md +++ b/README.md @@ -39,4 +39,8 @@ 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 +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/src/lib.rs b/src/lib.rs index 98a4829..de00620 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,13 +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"))] -mod arm; +/// Module defining arm architecture-dependent structures +pub mod arm; diff --git a/src/vcpu.rs b/src/vcpu.rs index ae29b00..feb81ea 100644 --- a/src/vcpu.rs +++ b/src/vcpu.rs @@ -25,64 +25,118 @@ pub use x86_64::{ }; /// -/// Reasons for vCPU exits. -/// -/// Exit reasons are drawn a superset of exit reasons for various VMMs. +/// Reasons for vCPU exits for Windows (Hyper-V) platforms /// #[derive(Debug)] -pub enum VcpuExit<'a> { +#[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 Vcpu::run() is called again. + /// The given slice should be filled in before [run()](struct.VcpuFd.html#method.run) + /// is called again. IoIn(u16 /* port */, &'a mut [u8] /* data */), + /// 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 Vcpu::run() is called again. + /// The given slice should be filled in before [run()](struct.VcpuFd.html#method.run) /// is called again. MmioRead(u64 /* address */, &'a mut [u8]), /// A write instruction was run against the given MMIO address with the given data. MmioWrite(u64 /* address */, &'a [u8]), - Unknown, - Hypercall, - Debug, + /// 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, - Hyperv, - - Hlt, + /// Corresponds to KVM_EXIT_IOAPIC_EOI. IoapicEoi, - Exception, - IrqWindowOpen, + /// 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; /// @@ -92,6 +146,7 @@ pub type Result = result::Result; /// functionality /// pub trait Vcpu { + /// Associated type representing the run context on a vCPU exit type RunContextType; /// Reads the standard registers from the virtual CPU @@ -137,7 +192,7 @@ pub trait Vcpu { /// 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 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 diff --git a/src/x86_64/mod.rs b/src/x86_64/mod.rs index 23a7924..48116a8 100644 --- a/src/x86_64/mod.rs +++ b/src/x86_64/mod.rs @@ -1,6 +1,7 @@ // Copyright 2018-2019 CrowdStrike, Inc. // SPDX-License-Identifier: Apache-2.0 OR MIT +/// Windows definitions of data structures #[cfg(windows)] pub mod windows; @@ -17,6 +18,7 @@ pub use { self::windows::MsrEntries, self::windows::MsrEntry, self::windows::SegmentRegister, + self::windows::DescriptorTable, self::windows::CpuId2, self::windows::CpuIdEntry2, self::windows::LapicState, @@ -39,7 +41,7 @@ pub use { kvm_bindings::kvm_lapic_state as LapicState, }; -// Returns a `Vec` with a size in bytes at least as large as `size_in_bytes`. +/// 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); @@ -49,21 +51,21 @@ fn vec_with_size_in_bytes(size_in_bytes: usize) -> Vec { 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. +/// 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; @@ -82,9 +84,9 @@ pub const MAX_CPUID_ENTRIES: usize = 80; /// 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. + /// Wrapper over `CpuId2` from which we only use the first element. pub cpuid_vec: Vec, - // Number of `CpuIdEntry2` structs at the end of CpuId2. + /// Number of `CpuIdEntry2` structs at the end of CpuId2. pub allocated_len: usize, } diff --git a/src/x86_64/windows.rs b/src/x86_64/windows.rs index c9c8386..87a2139 100644 --- a/src/x86_64/windows.rs +++ b/src/x86_64/windows.rs @@ -11,51 +11,47 @@ // Use of this source code is governed by a BSD-style license that can be // found in the THIRD-PARTY file. -/// -/// Single MSR to be read/written -/// -#[repr(C)] -#[derive(Debug, Default)] -pub struct MsrEntry { - pub index: u32, - pub reserved: u32, - pub data: u64, -} - -/// -/// Array of MSR entries -/// -#[repr(C)] -#[derive(Debug, Default)] -pub struct MsrEntries { - pub nmsrs: u32, - pub pad: u32, - pub entries: __IncompleteArrayField, -} - /// /// Standard registers (general purpose plus instruction pointer and flags) /// #[repr(C)] -#[derive(Debug, Default)] +#[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, } @@ -64,25 +60,43 @@ pub struct StandardRegisters { /// registers, plus the interrupt bitmap) /// #[repr(C)] -#[derive(Debug, Default)] +#[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], } @@ -90,20 +104,33 @@ pub struct SpecialRegisters { /// Segment register (used for CS, DS, ES, FS, GS, SS) /// #[repr(C)] -#[derive(Debug, Default)] +#[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, } @@ -111,10 +138,13 @@ pub struct SegmentRegister { /// Descriptor Table /// #[repr(C)] -#[derive(Debug, Default)] +#[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], } @@ -122,21 +152,60 @@ pub struct DescriptorTable { /// Floating Point Unit State /// #[repr(C)] -#[derive(Debug, Default)] +#[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. @@ -144,13 +213,24 @@ pub struct FpuState { #[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], } @@ -160,8 +240,11 @@ pub struct CpuIdEntry2 { #[repr(C)] #[derive(Debug, Default)] pub struct CpuId2 { + /// Number of entries pub nent: u32, + /// Padding pub padding: u32, + /// Array of entries pub entries: __IncompleteArrayField, } @@ -172,6 +255,7 @@ pub struct CpuId2 { #[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], } @@ -189,26 +273,33 @@ impl ::std::fmt::Debug for LapicState { #[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) }