From 960900e631367a69cc3792f610e2425a51bcbdcd Mon Sep 17 00:00:00 2001 From: Jenny Mankin <44877542+jennymankin@users.noreply.github.com> Date: Fri, 19 Apr 2019 19:42:18 -0700 Subject: [PATCH] Addressed PR feedback Changed licenses, fixed compilation annotations, created separate files for explicitly-defined Windows data structures, and moved all tests to a module at the end of the file. --- Cargo.toml | 4 + LICENSE => LICENSE-APACHE | 1 + LICENSE-MIT | 24 + src/arm.rs | 32 +- src/lib.rs | 17 +- src/vcpu.rs | 28 +- src/x86_64.rs | 419 ------------- src/x86_64/mod.rs | 236 ++++++++ src/x86_64/windows.rs | 1185 +++++++++++++++++++++++++++++++++++++ 9 files changed, 1468 insertions(+), 478 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 3961ee5..cee769e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,10 @@ 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 = "*" 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 e0fb811..98a4829 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,22 +1,11 @@ // 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 + +pub mod vcpu; #[cfg(unix)] extern crate kvm_bindings; -pub mod vcpu; - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub mod x86_64; diff --git a/src/vcpu.rs b/src/vcpu.rs index e625067..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; #[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 b91f633..0000000 --- a/src/x86_64.rs +++ /dev/null @@ -1,419 +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; - -/// -/// Single MSR to be read/written -/// -#[cfg(windows)] -#[repr(C)] -#[derive(Debug, Default)] -pub struct MsrEntry { - pub index: u32, - pub reserved: u32, - pub data: u64, -} - -#[cfg(unix)] -pub use kvm_bindings::kvm_msr_entry as MsrEntry; - -/// -/// Array of MSR entries -/// -#[cfg(windows)] -#[repr(C)] -#[derive(Debug, Default)] -pub struct MsrEntries { - pub nmsrs: u32, - pub pad: u32, - pub entries: __IncompleteArrayField, -} - -#[cfg(unix)] -pub use kvm_bindings::kvm_msrs as MsrEntries; - -/// -/// Standard registers (general purpose plus instruction pointer and flags) -/// -#[cfg(windows)] -#[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, -} - -#[cfg(unix)] -pub use kvm_bindings::kvm_regs as StandardRegisters; - -/// -/// Special registers (segment, task, descriptor table, control, and additional -/// registers, plus the interrupt bitmap) -/// -#[cfg(windows)] -#[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], -} - -#[cfg(unix)] -pub use kvm_bindings::kvm_sregs as SpecialRegisters; - -/// -/// Segment register (used for CS, DS, ES, FS, GS, SS) -/// -#[cfg(windows)] -#[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, -} - -#[cfg(unix)] -pub use kvm_bindings::kvm_segment as SegmentRegister; - -/// -/// Descriptor Table -/// -#[cfg(windows)] -#[repr(C)] -#[derive(Debug, Default)] -pub struct DescriptorTable { - pub base: u64, - pub limit: u16, - pub padding: [u16; 3usize], -} - -#[cfg(unix)] -pub use kvm_bindings::kvm_dtable as DescriptorTable; - -/// -/// Floating Point Unit State -/// -#[cfg(windows)] -#[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, -} - -#[cfg(unix)] -pub use kvm_bindings::kvm_fpu as FpuState; - -/// -/// Entry describing a CPUID feature/leaf. Features can be set as responses to -/// the CPUID instruction. -/// -#[cfg(windows)] -#[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], -} - -#[cfg(unix)] -use kvm_bindings::kvm_cpuid_entry2 as CpuIdEntry2; - -/// -/// Array of CpuId2 entries, each of which describing a feature/leaf to be set -/// -#[cfg(windows)] -#[repr(C)] -#[derive(Debug, Default)] -pub struct CpuId2 { - pub nent: u32, - pub padding: u32, - pub entries: __IncompleteArrayField, -} - -#[cfg(unix)] -use kvm_bindings::kvm_cpuid2 as CpuId2; - -/// 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::(), - 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) - ) - ); -} - -/// 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. -#[cfg(unix)] -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`. -pub 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) -} - -/// 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. - cpuid: Vec, - // Number of `CpuIdEntry2` structs at the end of CpuId2. - allocated_len: usize, -} - -impl Clone for CpuId { - fn clone(&self) -> Self { - let mut cpuid = Vec::with_capacity(self.cpuid.len()); - for _ in 0..self.cpuid.len() { - cpuid.push(CpuId2::default()); - } - - let num_bytes = self.cpuid.len() * size_of::(); - - let src_byte_slice = - unsafe { std::slice::from_raw_parts(self.cpuid.as_ptr() as *const u8, num_bytes) }; - - let dst_byte_slice = - unsafe { std::slice::from_raw_parts_mut(cpuid.as_mut_ptr() as *mut u8, num_bytes) }; - - dst_byte_slice.copy_from_slice(src_byte_slice); - - CpuId { - cpuid, - allocated_len: self.allocated_len, - } - } -} - -impl PartialEq for CpuId { - fn eq(&self, other: &CpuId) -> bool { - let entries: &[CpuIdEntry2] = - unsafe { self.cpuid[0].entries.as_slice(self.allocated_len) }; - let other_entries: &[CpuIdEntry2] = - unsafe { self.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 cpuid = vec_with_array_field::(array_len); - cpuid[0].nent = array_len as u32; - - CpuId { - 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.cpuid[0].nent as usize > self.allocated_len { - self.cpuid[0].nent = self.allocated_len as u32; - } - let nent = self.cpuid[0].nent as usize; - unsafe { self.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.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.cpuid[0] - } -} - -#[cfg(windows)] -#[repr(C)] -#[derive(Default)] -pub struct __IncompleteArrayField(::std::marker::PhantomData, [T; 0]); - -#[cfg(windows)] -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) - } -} - -#[cfg(windows)] -impl ::std::fmt::Debug for __IncompleteArrayField { - fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - fmt.write_str("__IncompleteArrayField") - } -} - -#[cfg(windows)] -impl ::std::clone::Clone for __IncompleteArrayField { - #[inline] - fn clone(&self) -> Self { - Self::new() - } -} diff --git a/src/x86_64/mod.rs b/src/x86_64/mod.rs new file mode 100644 index 0000000..8ddb621 --- /dev/null +++ b/src/x86_64/mod.rs @@ -0,0 +1,236 @@ +// 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. + /// + // Example: + // NOTE: Example is KVM-specific and thus not going to run on all platforms + // (and doc-comment examples do not yet support conditional compilation), so + // the example is left as a regular comment. + // + // ```rust + // # extern crate kvm_ioctls; + // + // use kvm_ioctls::Kvm; + // use vmm_vcpu::x86_64::{CpuId, MAX_CPUID_ENTRIES}; + // let kvm = Kvm::new().unwrap(); + // let mut cpuid = kvm.get_supported_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); + // let cpuid_entries = cpuid.mut_entries_slice(); + // ``` + /// + pub fn mut_entries_slice(&mut self) -> &mut [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) + ) + ); + } + +}