├── .gitattributes ├── .gitignore ├── examples ├── payload │ ├── payload.ld │ ├── Makefile │ ├── helpers.c │ ├── helpers.h │ ├── handler.asm │ ├── interrupt.h │ └── payload.c └── demo.rs ├── Cargo.toml ├── src ├── lib.rs ├── win_memory.rs ├── common.rs ├── win_hv_emulation_defs.rs ├── interrupts.rs ├── memory.rs ├── win_hv_platform_defs_internal.rs ├── x86_64.rs ├── macros.rs ├── win_hv_emulation.rs ├── win_hv_platform.rs ├── instruction_emulator.rs ├── debug.rs └── platform.rs ├── README.md └── LICENSE /.gitattributes: -------------------------------------------------------------------------------- 1 | *.rs text eol=lf 2 | *.toml text eol=lf 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target 3 | **/*.rs.bk 4 | Cargo.lock 5 | *.code-workspace 6 | .vscode/ 7 | examples/payload/payload.img 8 | examples/payload/payload.o 9 | *.o 10 | -------------------------------------------------------------------------------- /examples/payload/payload.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT(binary) 2 | SECTIONS 3 | { 4 | .start : { *(.start) } 5 | .text : { *(.text*) } 6 | .rodata : { *(.rodata) } 7 | .data : { *(.data) } 8 | } 9 | -------------------------------------------------------------------------------- /examples/payload/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -Wall -Wextra -Werror -O1 2 | SFLAGS = -S -O1 3 | 4 | .PHONY : all 5 | all : payload.img 6 | 7 | handler.o: handler.asm 8 | $(AS) -o $@ $< 9 | 10 | payload.o: payload.c 11 | $(CC) $(CFLAGS) -m64 -ffreestanding -fno-pic -c -o $@ $^ 12 | 13 | helpers.o: helpers.c 14 | $(CC) $(CFLAGS) -m64 -ffreestanding -fno-pic -c -o $@ $^ 15 | 16 | payload.img: handler.o helpers.o payload.o 17 | $(LD) -T payload.ld $^ -o $@ 18 | 19 | .PHONY: clean 20 | clean: 21 | $(RM) handler.o payload.o helpers.o payload.img 22 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libwhp" 3 | version = "0.1.4" 4 | authors = ["Alessandro Pilotti ", 5 | "Jenny Mankin 2 | 3 | uint32_t itoa(int value, char str[]){ 4 | char const char_digit[] = "0123456789"; 5 | char* p = str; 6 | int len = 0; 7 | int temp_value; 8 | 9 | if (value < 0) { 10 | *p++ = '-'; 11 | value *= -1; 12 | } 13 | 14 | temp_value = value; 15 | 16 | do { 17 | len++; 18 | ++p; 19 | temp_value = temp_value / 10; 20 | } while (temp_value); 21 | 22 | *p = '\0'; 23 | 24 | do { 25 | *--p = char_digit[value % 10]; 26 | value = value / 10; 27 | } while (value); 28 | 29 | return len; 30 | } 31 | -------------------------------------------------------------------------------- /examples/payload/helpers.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Cloudbase Solutions Srl 2 | // Copyright 2018-2019 CrowdStrike, Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | #ifndef _HELPERS_H_ 17 | #define _HELPERS_H_ 18 | 19 | uint32_t itoa(int i, char b[]); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Cloudbase Solutions Srl 2 | // Copyright 2018-2019 CrowdStrike, Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | extern crate libc; 17 | 18 | #[macro_use] 19 | mod macros; 20 | 21 | pub mod common; 22 | pub mod debug; 23 | pub mod instruction_emulator; 24 | pub mod interrupts; 25 | pub mod platform; 26 | mod win_hv_emulation; 27 | mod win_hv_emulation_defs; 28 | mod win_hv_platform; 29 | mod win_hv_platform_defs; 30 | mod win_hv_platform_defs_internal; 31 | mod win_memory; 32 | pub mod x86_64; 33 | 34 | pub use common::*; 35 | pub use platform::*; 36 | pub mod memory; 37 | 38 | #[macro_use] 39 | extern crate bitflags; 40 | extern crate byteorder; 41 | -------------------------------------------------------------------------------- /src/win_memory.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Cloudbase Solutions Srl 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | #![allow(non_camel_case_types)] 16 | 17 | use common::*; 18 | 19 | pub const MEM_COMMIT: DWORD = 0x00001000; 20 | pub const MEM_RESERVE: DWORD = 0x00002000; 21 | pub const MEM_RELEASE: DWORD = 0x00008000; 22 | pub const PAGE_READWRITE: DWORD = 0x04; 23 | 24 | #[allow(non_snake_case)] 25 | #[link(name = "Kernel32")] 26 | extern "stdcall" { 27 | pub fn VirtualAlloc( 28 | lpAddress: *mut VOID, 29 | dwSize: SIZE_T, 30 | flAllocationType: DWORD, 31 | flProtect: DWORD, 32 | ) -> *mut VOID; 33 | pub fn VirtualFree(lpAddress: *mut VOID, dwSize: SIZE_T, dwFreeType: DWORD) -> BOOL; 34 | } 35 | 36 | #[cfg(test)] 37 | mod tests { 38 | use super::*; 39 | use std; 40 | 41 | #[test] 42 | fn test_virtual_alloc_free() { 43 | const SIZE: SIZE_T = 0x1000; 44 | let addr = unsafe { 45 | VirtualAlloc( 46 | std::ptr::null_mut(), 47 | SIZE, 48 | MEM_COMMIT | MEM_RESERVE, 49 | PAGE_READWRITE, 50 | ) 51 | }; 52 | assert!(addr != std::ptr::null_mut(), "VirtualAlloc failed"); 53 | 54 | let result = unsafe { VirtualFree(addr, 0, MEM_RELEASE) }; 55 | assert!(result != 0, "VirtualFree failed"); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libwhp 2 | 3 | Windows Hypervisor Platform API for Rust: 4 | https://docs.microsoft.com/en-us/virtualization/api/ 5 | 6 | This crate takes advantage of the safety, lifetime, memory management and 7 | error handling features available in Rust while retaining the original design 8 | of the native Windows Hypervisor Platform (WHP) API. 9 | 10 | ## Prerequisites 11 | 12 | Make sure to have at least: 13 | 14 | * Windows 10 build 17134 (RS5 or above) 15 | * Windows Server 1809 (RS5 or above) 16 | 17 | Enable the Windows Hypervisor Platform and reboot: 18 | 19 | ``` 20 | Dism /Online /Enable-Feature /FeatureName:HypervisorPlatform 21 | shutdown /r /t 0 22 | ``` 23 | 24 | Last but not least, install [Rust on Windows](https://www.rust-lang.org/en-US/install.html). 25 | 26 | ## Running the demo example 27 | 28 | 1. Clone the project's repository: 29 | ``` 30 | git clone https://github.com/insula-rs/libwhp 31 | cd libwhp 32 | ``` 33 | 34 | 2. This example includes a payload (the "guest" binary) that needs 35 | to be compiled using GCC, e.g. with WSL 36 | ([Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/install-win10)). 37 | All we need is make, gcc and ld. On Ubuntu: 38 | 39 | ``` 40 | wsl sudo apt-get update 41 | wsl sudo apt-get dist-upgrade -y 42 | wsl sudo apt-get install gcc make binutils -y 43 | ``` 44 | 45 | 3. Build the payload: 46 | 47 | ``` 48 | pushd examples\payload 49 | wsl make 50 | popd 51 | ``` 52 | 53 | 4. Build and run the example: 54 | 55 | ``` 56 | cargo run --example demo 57 | ``` 58 | 59 | Here's what it does: 60 | 61 | * Checks for the hypervisor presence 62 | * Creates a partition 63 | * Sets various partition properties, like the allowed exit types and CPUID results 64 | * Allocates and maps memory 65 | * Creates a vCPU 66 | * Sets up registers for long mode (64 bit) 67 | * Reads the payload in memory (payload.img) 68 | * Sets up the MMIO / IO port intruction emulator and related callbacks 69 | * Starts the vCPU loop 70 | * Handles various types of exits: CPUID, MSR read / write, IO port, MMIO, Halt, etc 71 | -------------------------------------------------------------------------------- /examples/payload/handler.asm: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Cloudbase Solutions Srl 2 | // Copyright 2018-2019 CrowdStrike, Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | .globl interrupt_handler_host 17 | .globl interrupt_handler_guest 18 | .align 4 19 | .intel_syntax noprefix 20 | 21 | .macro SAVE_VOLATILE_REGS 22 | mov [rsp-0x08], rax 23 | mov [rsp-0x10], rcx 24 | mov [rsp-0x18], rdx 25 | mov [rsp-0x20], r8 26 | mov [rsp-0x28], r9 27 | mov [rsp-0x30], r10 28 | mov [rsp-0x38], r11 29 | movdqu xmmword ptr [rsp-0x48], xmm0 30 | movdqu xmmword ptr [rsp-0x58], xmm1 31 | movdqu xmmword ptr [rsp-0x68], xmm2 32 | movdqu xmmword ptr [rsp-0x78], xmm3 33 | movdqu xmmword ptr [rsp-0x88], xmm4 34 | movdqu xmmword ptr [rsp-0x98], xmm5 35 | sub rsp, 0x98 36 | .endm 37 | 38 | .macro RESTORE_VOLATILE_REGS 39 | add rsp, 0x98 40 | mov rax, [rsp-0x08] 41 | mov rcx, [rsp-0x10] 42 | mov rdx, [rsp-0x18] 43 | mov r8, [rsp-0x20] 44 | mov r9, [rsp-0x28] 45 | mov r10, [rsp-0x30] 46 | mov r11, [rsp-0x38] 47 | movdqu xmm0, xmmword ptr [rsp-0x48] 48 | movdqu xmm1, xmmword ptr [rsp-0x58] 49 | movdqu xmm2, xmmword ptr [rsp-0x68] 50 | movdqu xmm3, xmmword ptr [rsp-0x78] 51 | movdqu xmm4, xmmword ptr [rsp-0x88] 52 | movdqu xmm5, xmmword ptr [rsp-0x98] 53 | .endm 54 | 55 | interrupt_handler_host: 56 | SAVE_VOLATILE_REGS 57 | call log_interrupt_from_host 58 | RESTORE_VOLATILE_REGS 59 | iretq 60 | 61 | interrupt_handler_guest: 62 | SAVE_VOLATILE_REGS 63 | call log_interrupt_from_guest 64 | RESTORE_VOLATILE_REGS 65 | iretq 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/common.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Cloudbase Solutions Srl 2 | // Copyright 2018-2019 CrowdStrike, Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | #![allow(non_camel_case_types)] 17 | 18 | use std::error::Error; 19 | use std::fmt; 20 | use std::os::raw::{c_int, c_longlong, c_uchar, c_uint, c_ulonglong, c_ushort, c_void}; 21 | 22 | pub type HRESULT = c_int; 23 | pub type UINT8 = c_uchar; 24 | pub type UINT16 = c_ushort; 25 | pub type UINT32 = c_uint; 26 | pub type UINT64 = c_ulonglong; 27 | pub type INT32 = c_int; 28 | pub type INT64 = c_longlong; 29 | pub type VOID = c_void; 30 | pub type BOOL = c_int; 31 | pub type DWORD = c_uint; 32 | pub type SIZE_T = c_ulonglong; 33 | 34 | pub const FALSE: i32 = 0; 35 | pub const TRUE: i32 = 1; 36 | 37 | pub const S_OK: HRESULT = 0; 38 | pub const E_FAIL: HRESULT = -2147467259; // 0x80004005; 39 | pub const ERROR_HV_NOT_PRESENT: HRESULT = -1070264320; // 0xC0351000 40 | pub const E_INVALIDARG: HRESULT = -2147024809; // 0x80070057 41 | 42 | #[derive(Debug)] 43 | pub struct WHPError { 44 | result: HRESULT, 45 | } 46 | 47 | impl WHPError { 48 | pub fn new(result: HRESULT) -> WHPError { 49 | WHPError { result: result } 50 | } 51 | 52 | pub fn result(&self) -> HRESULT { 53 | self.result 54 | } 55 | } 56 | 57 | impl Error for WHPError { 58 | fn description(&self) -> &str { 59 | "WHP error" 60 | } 61 | } 62 | 63 | impl fmt::Display for WHPError { 64 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 65 | write!(f, "WHP error code: 0x{:X}", self.result) 66 | } 67 | } 68 | 69 | // TODO (alexpilotti): transform into a macro 70 | pub fn check_result(res: HRESULT) -> Result<(), WHPError> { 71 | match res { 72 | S_OK => Ok(()), 73 | _ => Err(WHPError::new(res)), 74 | } 75 | } 76 | 77 | #[cfg(test)] 78 | mod tests { 79 | use super::*; 80 | 81 | #[test] 82 | fn test_check_result_ok() { 83 | check_result(S_OK).unwrap(); 84 | } 85 | 86 | #[test] 87 | #[should_panic] 88 | fn test_check_result_fail() { 89 | check_result(E_INVALIDARG).unwrap(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/win_hv_emulation_defs.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Cloudbase Solutions Srl 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | #![allow(non_camel_case_types)] 16 | 17 | use common::*; 18 | use win_hv_platform_defs::*; 19 | 20 | pub type WHV_EMULATOR_HANDLE = *mut VOID; 21 | 22 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)] 23 | #[allow(non_snake_case)] 24 | #[repr(C)] 25 | pub struct WHV_EMULATOR_STATUS { 26 | pub AsUINT32: UINT32, 27 | } 28 | 29 | bitfield!(WHV_EMULATOR_STATUS AsUINT32: UINT32[ 30 | EmulationSuccessful set_EmulationSuccessful[0..1], 31 | InternalEmulationFailure set_InternalEmulationFailure[1..2], 32 | IoPortCallbackFailed set_IoPortCallbackFailed[2..3], 33 | MemoryCallbackFailed set_MemoryCallbackFailed[3..4], 34 | TranslateGvaPageCallbackFailed set_TranslateGvaPageCallbackFailed[4..5], 35 | TranslateGvaPageCallbackGpaIsNotAligned set_TranslateGvaPageCallbackGpaIsNotAligned[5..6], 36 | GetVirtualProcessorRegistersCallbackFailed set_GetVirtualProcessorRegistersCallbackFailed[6..7], 37 | SetVirtualProcessorRegistersCallbackFailed set_SetVirtualProcessorRegistersCallbackFailed[7..8], 38 | InterruptCausedIntercept set_InterruptCausedIntercept[8..9], 39 | GuestCannotBeFaulted set_GuestCannotBeFaulted[9..10], 40 | Reserved set_Reserved[10..32], 41 | ]); 42 | 43 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)] 44 | #[allow(non_snake_case)] 45 | #[repr(C)] 46 | pub struct WHV_EMULATOR_MEMORY_ACCESS_INFO { 47 | pub GpaAddress: WHV_GUEST_PHYSICAL_ADDRESS, 48 | pub Direction: UINT8, 49 | pub AccessSize: UINT8, 50 | pub Data: [UINT8; 8], 51 | } 52 | 53 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)] 54 | #[allow(non_snake_case)] 55 | #[repr(C)] 56 | pub struct WHV_EMULATOR_IO_ACCESS_INFO { 57 | pub Direction: UINT8, 58 | pub Port: UINT16, 59 | pub AccessSize: UINT16, 60 | pub Data: UINT32, 61 | } 62 | 63 | #[cfg(test)] 64 | mod tests { 65 | use super::*; 66 | use std; 67 | 68 | #[test] 69 | fn test_data_type_sizes() { 70 | // Make sure all unions and structs have a size that matches the value 71 | // obtained with a sizeof() in C. 72 | assert_eq!(std::mem::size_of::(), 24); 73 | assert_eq!(std::mem::size_of::(), 12); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/interrupts.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2019 CrowdStrike, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. 16 | // Use of this source code is governed by a BSD-style license that can be 17 | // found in the LICENSE file. 18 | 19 | use std::fmt::{self, Display}; 20 | use std::io::Cursor; 21 | use std::mem; 22 | use std::result; 23 | 24 | use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; 25 | 26 | use common::WHPError; 27 | use platform::VirtualProcessor; 28 | use x86_64::{LapicStateRaw, APIC_REG_OFFSET}; 29 | 30 | #[derive(Debug)] 31 | pub enum Error { 32 | GetLapic(WHPError), 33 | SetLapic(WHPError), 34 | } 35 | pub type Result = result::Result; 36 | 37 | impl Display for Error { 38 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 39 | write!(f, "Interrupt Error: {}", 40 | match self { 41 | &Error::GetLapic(_) => "GetLapic ioctl failed", 42 | &Error::SetLapic(_) => "SetLapic ioctl failed", 43 | }) 44 | } 45 | } 46 | 47 | pub fn get_lapic_reg(lapic: &LapicStateRaw, reg_offset: APIC_REG_OFFSET) -> u32 { 48 | let sliceu8 = unsafe { 49 | // This array is only accessed as parts of a u32 word, so interpret it as a u8 array. 50 | // Cursors are only readable on arrays of u8, not i8(c_char). 51 | mem::transmute::<&[i8], &[u8]>(&lapic.regs[reg_offset as usize..]) 52 | }; 53 | let mut reader = Cursor::new(sliceu8); 54 | // read_u32 can't fail if the offsets defined above are correct. 55 | reader.read_u32::().unwrap() 56 | } 57 | 58 | pub fn set_lapic_reg(lapic: &mut LapicStateRaw, reg_offset: APIC_REG_OFFSET, value: u32) { 59 | let sliceu8 = unsafe { 60 | // This array is only accessed as parts of a u32 word, so interpret it as a u8 array. 61 | // Cursors are only readable on arrays of u8, not i8(c_char). 62 | mem::transmute::<&mut [i8], &mut [u8]>(&mut lapic.regs[reg_offset as usize..]) 63 | }; 64 | let mut writer = Cursor::new(sliceu8); 65 | // read_u32 can't fail if the offsets defined above are correct. 66 | writer.write_u32::(value).unwrap() 67 | } 68 | 69 | pub fn get_reg_from_lapic(vcpu: &VirtualProcessor, reg_offset: APIC_REG_OFFSET) -> u32 { 70 | let lapic: LapicStateRaw = vcpu.get_lapic().map_err(Error::GetLapic).unwrap(); 71 | 72 | get_lapic_reg(&lapic, reg_offset) 73 | } 74 | 75 | pub fn set_reg_in_lapic(vcpu: &VirtualProcessor, reg_offset: APIC_REG_OFFSET, value: u32) { 76 | let mut lapic: LapicStateRaw = vcpu.get_lapic().map_err(Error::GetLapic).unwrap(); 77 | 78 | set_lapic_reg(&mut lapic, reg_offset, value); 79 | 80 | vcpu.set_lapic(&lapic).map_err(Error::SetLapic).unwrap(); 81 | } 82 | -------------------------------------------------------------------------------- /src/memory.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Cloudbase Solutions Srl 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | #![allow(non_camel_case_types)] 16 | 17 | use common::*; 18 | use std; 19 | use std::error::Error; 20 | use std::fmt; 21 | use win_memory::*; 22 | 23 | pub trait Memory { 24 | fn as_slice_mut(&mut self) -> &mut [u8]; 25 | fn as_slice(&self) -> &[u8]; 26 | fn as_ptr(&self) -> *const VOID; 27 | fn get_size(&self) -> usize; 28 | } 29 | 30 | #[derive(Debug)] 31 | pub struct MemoryError {} 32 | 33 | impl MemoryError { 34 | pub fn new() -> MemoryError { 35 | MemoryError {} 36 | } 37 | } 38 | 39 | impl Error for MemoryError { 40 | fn description(&self) -> &str { 41 | "Memory error" 42 | } 43 | } 44 | 45 | impl fmt::Display for MemoryError { 46 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 47 | write!(f, "Failed to allocate memory") 48 | } 49 | } 50 | 51 | pub struct VirtualMemory { 52 | address: *mut VOID, 53 | size: usize, 54 | } 55 | 56 | impl VirtualMemory { 57 | pub fn new(size: usize) -> Result { 58 | let address = unsafe { 59 | VirtualAlloc( 60 | std::ptr::null_mut(), 61 | size as SIZE_T, 62 | MEM_COMMIT | MEM_RESERVE, 63 | PAGE_READWRITE, 64 | ) 65 | }; 66 | 67 | match address as u64 { 68 | 0 => Err(MemoryError::new()), 69 | _ => Ok(VirtualMemory { 70 | address: address, 71 | size: size, 72 | }), 73 | } 74 | } 75 | } 76 | 77 | impl Memory for VirtualMemory { 78 | fn as_slice_mut(&mut self) -> &mut [u8] { 79 | unsafe { std::slice::from_raw_parts_mut(self.address as *mut u8, self.size) } 80 | } 81 | 82 | fn as_slice(&self) -> &[u8] { 83 | unsafe { std::slice::from_raw_parts(self.address as *mut u8, self.size) } 84 | } 85 | 86 | fn as_ptr(&self) -> *const VOID { 87 | self.address 88 | } 89 | 90 | fn get_size(&self) -> usize { 91 | self.size 92 | } 93 | } 94 | 95 | impl Drop for VirtualMemory { 96 | fn drop(&mut self) { 97 | unsafe { VirtualFree(self.address, 0, MEM_RELEASE) }; 98 | } 99 | } 100 | 101 | #[cfg(test)] 102 | mod tests { 103 | use super::*; 104 | 105 | #[test] 106 | fn test_virtual_alloc_free() { 107 | const SIZE: usize = 0x1000; 108 | let mut mem = VirtualMemory::new(SIZE).unwrap(); 109 | let addr = mem.address; 110 | 111 | assert_eq!(mem.get_size(), SIZE); 112 | assert_eq!(mem.as_ptr(), addr); 113 | 114 | let slice = mem.as_slice_mut(); 115 | assert_eq!(slice.as_ptr() as *const VOID, addr); 116 | assert_eq!(slice.len(), SIZE); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /examples/payload/interrupt.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Cloudbase Solutions Srl 2 | // Copyright 2018-2019 CrowdStrike, Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | #ifndef _INTERRUPT_H_ 17 | #define _INTERRUPT_H_ 18 | 19 | #define NUM_IDT_ENTRIES 256 20 | #define NUM_GDT_ENTRIES 8 21 | 22 | #define APIC_MMIO_OFFSET_EOI_LOW 0x0b0 23 | #define APIC_MMIO_OFFSET_EOI_HIGH 0x0c0 24 | #define APIC_MMIO_OFFSET_ICR_LOW 0x300 25 | #define APIC_MMIO_OFFSET_ICR_HIGH 0x310 26 | 27 | #define APIC_BASE_MSR 0x1b8 28 | 29 | #define APIC_BASE 0x0fee0000 30 | 31 | #define GDT_NULL 0x0000 32 | #define GDT_R0_CODE 0x0008 33 | #define GDT_R0_DATA 0x0010 34 | #define GDT_R3_DATA 0x0018 35 | #define GDT_R3_CODE 0x0020 36 | #define GDT_SYS_TSS 0x0028 // System GDT entries are twice as wide 37 | 38 | #define HOST_INT_VECTOR 0x35 39 | #define GUEST_INT_VECTOR 0x36 40 | 41 | // Struct for a single IDT entry 42 | typedef union _IDT_ENTRY64 43 | { 44 | struct 45 | { 46 | uint16_t offset_low; 47 | uint16_t selector; /* GDT R0 Code Segment */ 48 | uint16_t ist_index:3; 49 | uint16_t reserved0:5; 50 | uint16_t type:5; 51 | uint16_t dpl:2; 52 | uint16_t present:1; 53 | uint16_t offset_middle; 54 | uint32_t offset_high; 55 | uint32_t reserved1; 56 | 57 | } fields; 58 | uint64_t alignment; 59 | } IDT_ENTRY64, *PIDT_ENTRY64; 60 | 61 | // This structure contains the value of one GDT entry. 62 | typedef struct _GDT_ENTRY64 63 | { 64 | uint16_t limit_low; // The lower 16 bits of the limit. 65 | uint16_t base_low; // The lower 16 bits of the base. 66 | uint8_t base_middle; // The next 8 bits of the base. 67 | uint8_t access; // Access flags, determine what ring this segment can be used in. 68 | uint8_t flags_limit_high; 69 | uint8_t base_high; // The last 8 bits of the base. 70 | } __attribute__((packed)) GDT_ENTRY64, *PGDT_ENTRY64; 71 | 72 | // Struct to describe a descriptor table (IDT, GDT), required for LIDT/LGDT 73 | typedef struct _DT_PTR64 74 | { 75 | uint16_t padding[3]; 76 | uint16_t limit; 77 | uint64_t base; 78 | } __attribute__((packed)) DT_PTR64, *PDT_PTR64; 79 | 80 | void load_idt_register(void* idt_ptr) 81 | { 82 | __asm__ __volatile__("lidt %0" :: "m"(*(uint64_t*)idt_ptr)); 83 | } 84 | 85 | void load_gdt_register(void* gdt_ptr) 86 | { 87 | __asm__ __volatile__("lgdt %0" :: "m"(*(uint64_t*)gdt_ptr)); 88 | } 89 | 90 | void enable_interrupt_flag() 91 | { 92 | __asm__ __volatile__("sti"); 93 | } 94 | 95 | void clear_interrupt_flag() 96 | { 97 | __asm__ __volatile__("cli"); 98 | } 99 | 100 | void store_idt_register(void* idt_ptr) 101 | { 102 | __asm__ __volatile__("sidt %0" : "=m"(*(uint64_t*)idt_ptr)); 103 | } 104 | 105 | #endif 106 | -------------------------------------------------------------------------------- /src/win_hv_platform_defs_internal.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2019 CrowdStrike, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | #![allow(non_camel_case_types)] 16 | #![allow(non_upper_case_globals)] 17 | 18 | use common::*; 19 | use win_hv_platform_defs::*; 20 | 21 | /* 22 | * This is our own union structure to provide a 23 | * generic interface to WHvGetProcessorCounters. If new set types 24 | * are added to that WHV_PROCESSOR_COUNTER_SET enum (and new corresponding 25 | * structs created), this union should also be updated to reflect the 26 | * new WHV_PROCESSOR_COUNTER_SET types. 27 | */ 28 | #[derive(Copy, Clone)] 29 | #[allow(non_snake_case)] 30 | #[repr(C)] 31 | pub union WHV_PROCESSOR_COUNTERS { 32 | pub RuntimeCounters: WHV_PROCESSOR_RUNTIME_COUNTERS, 33 | pub InterceptCounters: WHV_PROCESSOR_INTERCEPT_COUNTERS, 34 | pub EventCounters: WHV_PROCESSOR_EVENT_COUNTERS, 35 | pub ApicCounters: WHV_PROCESSOR_APIC_COUNTERS, 36 | } 37 | 38 | impl Default for WHV_PROCESSOR_COUNTERS { 39 | fn default() -> Self { 40 | unsafe { ::std::mem::zeroed() } 41 | } 42 | } 43 | 44 | /* 45 | * This is our own union structure to provide a 46 | * generic interface to WHvGetPartitionCounters. If new set types 47 | * are added to that WHV_PARTITION_COUNTER_SET enum (and new corresponding 48 | * structs created), this union should also be updated to reflect the 49 | * new WHV_PARTITION_COUNTER_SET types. 50 | */ 51 | #[derive(Copy, Clone)] 52 | #[allow(non_snake_case)] 53 | #[repr(C)] 54 | pub union WHV_PARTITION_COUNTERS { 55 | pub MemoryCounters: WHV_PARTITION_MEMORY_COUNTERS, 56 | } 57 | 58 | impl Default for WHV_PARTITION_COUNTERS { 59 | fn default() -> Self { 60 | unsafe { ::std::mem::zeroed() } 61 | } 62 | } 63 | 64 | /* 65 | * MSI-related structures and definitions 66 | */ 67 | pub const MSI_DATA_VECTOR_SHIFT: UINT32 = 0; 68 | pub const MSI_DATA_VECTOR_MASK: UINT32 = 0x000000ff; 69 | 70 | pub const MSI_DATA_DELIVERY_MODE_SHIFT: UINT32 = 8; 71 | pub const MSI_DATA_LEVEL_SHIFT: UINT32 = 14; 72 | pub const MSI_DATA_TRIGGER_SHIFT: UINT32 = 15; 73 | 74 | /* 75 | * Shift/mask fields for msi address 76 | */ 77 | 78 | pub const MSI_ADDR_DEST_MODE_SHIFT: UINT32 = 2; 79 | pub const MSI_ADDR_REDIRECTION_SHIFT: UINT32 = 3; 80 | pub const MSI_ADDR_DEST_ID_SHIFT: UINT32 = 12; 81 | pub const MSI_ADDR_DEST_IDX_SHIFT: UINT32 = 4; 82 | pub const MSI_ADDR_DEST_ID_MASK: UINT32 = 0x000ff000; 83 | 84 | #[derive(Copy, Clone, Default)] 85 | #[allow(non_snake_case)] 86 | #[repr(C)] 87 | pub struct WHV_MSI_ENTRY_anon_struct { 88 | pub Address: UINT32, 89 | pub Data: UINT32, 90 | } 91 | 92 | #[derive(Copy, Clone)] 93 | #[allow(non_snake_case)] 94 | #[repr(C)] 95 | pub union WHV_MSI_ENTRY { 96 | pub AsUINT64: UINT64, 97 | pub anon_struct: WHV_MSI_ENTRY_anon_struct, 98 | } 99 | 100 | impl Default for WHV_MSI_ENTRY { 101 | fn default() -> Self { 102 | unsafe { ::std::mem::zeroed() } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/x86_64.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2019 CrowdStrike, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. 16 | // Use of this source code is governed by a BSD-style license that can be 17 | // found in the THIRD-PARTY file. 18 | 19 | #![allow(non_camel_case_types)] 20 | #![allow(non_upper_case_globals)] 21 | 22 | use common::*; 23 | 24 | pub const APIC_LVT_NB: usize = 6; 25 | pub const NUM_REGS: usize = 8; 26 | 27 | #[repr(C)] 28 | #[derive(Copy, Clone)] 29 | pub struct LapicStateRaw { 30 | pub regs: [::std::os::raw::c_char; 4096usize], 31 | } 32 | 33 | impl Default for LapicStateRaw { 34 | fn default() -> Self { 35 | unsafe { ::std::mem::zeroed() } 36 | } 37 | } 38 | 39 | impl ::std::fmt::Debug for LapicStateRaw { 40 | fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { 41 | self.regs[..].fmt(fmt) 42 | } 43 | } 44 | 45 | // MMIO offsets to find registers within the APIC state 46 | #[repr(C)] 47 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 48 | pub enum APIC_REG_OFFSET { 49 | LocalApicId = 0x020, 50 | LocalApicVersion = 0x030, 51 | TaskPriority = 0x080, // TPR 52 | ProcessorPriority = 0x0a0, // PPR 53 | Eoi = 0x0b0, // EOI 54 | LogicalDestination = 0x0d0, // LDR 55 | DestinationFormat = 0x0e0, // DFR 56 | SpuriousVector = 0x0f0, // SVR 57 | 58 | // Interrupt Request Register (IRR) for various bits, one bit per vector 59 | InterruptRequest0 = 0x200, // Bits 31:0 60 | InterruptRequest1 = 0x210, // Bits 63:32 ... 61 | InterruptRequest2 = 0x220, 62 | InterruptRequest3 = 0x230, 63 | InterruptRequest4 = 0x240, 64 | InterruptRequest5 = 0x250, 65 | InterruptRequest6 = 0x260, 66 | InterruptRequest7 = 0x270, 67 | 68 | // In-Service Register (ISR) for various bits, one bit per vector 69 | InService0 = 0x100, // Bits 31:0 70 | InService1 = 0x110, // Bits 63:32 ... 71 | InService2 = 0x120, 72 | InService3 = 0x130, 73 | InService4 = 0x140, 74 | InService5 = 0x150, 75 | InService6 = 0x160, 76 | InService7 = 0x170, 77 | 78 | // Trigger Mode Registers (TMR) for various bits, one bit per vector 79 | TriggerMode0 = 0x180, // Bits 31:0 80 | TriggerMode1 = 0x190, // Bits 63:32 ... 81 | TriggerMode2 = 0x1a0, 82 | TriggerMode3 = 0x1b0, 83 | TriggerMode4 = 0x1c0, 84 | TriggerMode5 = 0x1d0, 85 | TriggerMode6 = 0x1e0, 86 | TriggerMode7 = 0x1f0, 87 | 88 | ErrorStatus = 0x280, 89 | LvtCmci = 0x2f0, 90 | InterruptCommand0 = 0x300, 91 | InterruptCommand1 = 0x310, 92 | 93 | // Local Vector Table (LVT) 94 | LvtTimer = 0x320, 95 | LvtThermalSensor = 0x330, 96 | LvtPerfMon = 0x340, 97 | LvtLint0 = 0x350, 98 | LvtLint1 = 0x360, 99 | LvtError = 0x370, 100 | TimerInitialCount = 0x380, 101 | TimerCurrentCount = 0x390, 102 | TimerDivideConfiguration = 0x3e0, 103 | } 104 | 105 | #[repr(C)] 106 | #[derive(Copy, Clone)] 107 | pub struct XsaveArea { 108 | pub region: [UINT32; 1024usize], 109 | } 110 | 111 | impl Default for XsaveArea { 112 | fn default() -> Self { 113 | unsafe { ::std::mem::zeroed() } 114 | } 115 | } 116 | 117 | impl ::std::fmt::Debug for XsaveArea { 118 | fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { 119 | self.region[..].fmt(fmt) 120 | } 121 | } 122 | 123 | #[cfg(test)] 124 | mod tests { 125 | use super::*; 126 | 127 | #[test] 128 | fn test_layout_lapic_state_raw() { 129 | assert_eq!( 130 | ::std::mem::size_of::(), 131 | 4096usize, 132 | concat!("Size of: ", stringify!(LAPICState)) 133 | ); 134 | assert_eq!( 135 | ::std::mem::align_of::(), 136 | 1usize, 137 | concat!("Alignment of ", stringify!(LapicStateRaw)) 138 | ); 139 | } 140 | 141 | #[test] 142 | fn test_layout_xsave_area() { 143 | assert_eq!( 144 | ::std::mem::size_of::(), 145 | 4096usize, 146 | concat!("Size of: ", stringify!(XsaveArea)) 147 | ); 148 | assert_eq!( 149 | ::std::mem::align_of::(), 150 | 4usize, 151 | concat!("Alignment of ", stringify!(XsaveArea)) 152 | ); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Cloudbase Solutions Srl 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | // Based on https://github.com/retep998/winapi-rs/blob/becb23f4522bec535fdc3417e7c15b390851f2de/src/macros.rs#L308-L326 16 | macro_rules! bitfield { 17 | ($base:ident $field:ident: $fieldtype:ty [ 18 | $($thing:ident $set_thing:ident[$r:expr],)+ 19 | ]) => { 20 | impl $base {$( 21 | #[inline] 22 | #[allow(non_snake_case)] 23 | pub fn $thing(&self) -> $fieldtype { 24 | let size = $crate::std::mem::size_of::<$fieldtype>() * 8; 25 | self.$field << (size - $r.end) >> (size - $r.end + $r.start) 26 | } 27 | #[inline] 28 | #[allow(non_snake_case)] 29 | pub fn $set_thing(&mut self, val: $fieldtype) { 30 | let mask = ((1 << ($r.end - $r.start)) - 1) << $r.start; 31 | self.$field &= !mask; 32 | self.$field |= (val << $r.start) & mask; 33 | } 34 | )+} 35 | 36 | impl std::fmt::Display for $base { 37 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 38 | writeln!(f, "{}:", stringify!($base)).unwrap(); 39 | $( 40 | writeln!(f, " {}: 0x{:x?}", stringify!($thing), 41 | self.$thing()).unwrap(); 42 | )+ 43 | Ok(()) 44 | } 45 | } 46 | } 47 | } 48 | 49 | // Assumes that WHV_UINT128 is used. 50 | macro_rules! bitfield128 { 51 | ($base:ident $field:ident: $fieldtype:ty [ 52 | $($thing:ident $set_thing:ident[$r:expr],)+ 53 | ]) => { 54 | impl $base {$( 55 | #[inline] 56 | #[allow(non_snake_case)] 57 | pub fn $thing(&self) -> $fieldtype { 58 | // We assume that $r.start < $r.end and both are 59 | // either smaller or greater than 64. 60 | let diff = ($r.end + $r.start) & 0x3f; 61 | if $r.start < 64 { 62 | self.$field.Low64 << 63 | match $r.end { 64 | 64 => 0, 65 | _ => 64 - $r.end 66 | } >> (64 - diff) 67 | } 68 | else { 69 | self.$field.High64 << 70 | match $r.end { 71 | 128 => 0, 72 | _ => 128 - $r.end 73 | } >> (64 - diff) 74 | } 75 | } 76 | #[inline] 77 | #[allow(non_snake_case)] 78 | pub fn $set_thing(&mut self, val: $fieldtype) { 79 | let mask; 80 | let diff = $r.end - $r.start; 81 | if (diff >= 64) { 82 | mask = u64::MAX << ($r.start % 64); 83 | } 84 | else { 85 | mask = ((1 << diff) - 1) << ($r.start % 64); 86 | } 87 | if $r.start < 64 { 88 | self.$field.Low64 &= !mask; 89 | self.$field.Low64 |= val << ($r.start % 64) & mask; 90 | } 91 | else { 92 | self.$field.High64 &= !mask; 93 | self.$field.High64 |= val << ($r.start % 64) & mask; 94 | } 95 | } 96 | )+} 97 | 98 | impl std::fmt::Display for $base { 99 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 100 | writeln!(f, "{}:", stringify!($base)).unwrap(); 101 | writeln!(f, " {}: {:?}", stringify!($field), 102 | self.$field).unwrap(); 103 | $( 104 | writeln!(f, " {}: 0x{:x?}", stringify!($thing), 105 | self.$thing()).unwrap(); 106 | )+ 107 | Ok(()) 108 | } 109 | } 110 | } 111 | } 112 | 113 | #[cfg(test)] 114 | #[allow(dead_code)] 115 | mod tests { 116 | use common::*; 117 | use win_hv_platform_defs::WHV_UINT128; 118 | 119 | #[test] 120 | fn test_bitfield() { 121 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)] 122 | #[allow(non_snake_case)] 123 | #[repr(C)] 124 | struct TEST_STRUCT_U8 { 125 | pub other: UINT8, 126 | pub AsUINT8: UINT8, 127 | } 128 | 129 | bitfield!(TEST_STRUCT_U8 AsUINT8: UINT8[ 130 | a set_a[0..2], 131 | b set_b[2..4], 132 | c set_c[4..8], 133 | ]); 134 | 135 | let mut obj = TEST_STRUCT_U8 { 136 | other: 5, 137 | AsUINT8: 4 138 | }; 139 | 140 | assert_eq!(0, obj.a()); 141 | assert_eq!(1, obj.b()); 142 | assert_eq!(0, obj.c()); 143 | 144 | obj.set_c(1 as u8); 145 | 146 | assert_eq!(1, obj.c()); 147 | assert_eq!(20, obj.AsUINT8); 148 | assert_eq!(5, obj.other); 149 | } 150 | 151 | #[test] 152 | fn test_bitfield128() { 153 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)] 154 | #[allow(non_snake_case)] 155 | #[repr(C)] 156 | struct TEST_STRUCT_U128 { 157 | pub AsUINT128: WHV_UINT128, 158 | } 159 | 160 | bitfield128!(TEST_STRUCT_U128 AsUINT128: UINT64[ 161 | a set_a[0..32], 162 | b set_b[32..64], 163 | c set_c[64..96], 164 | d set_d[96..128], 165 | ]); 166 | 167 | let mut obj = TEST_STRUCT_U128 { 168 | AsUINT128: WHV_UINT128 { 169 | Low64: 1 << 32, 170 | High64: 1 << 32 171 | } 172 | }; 173 | 174 | assert_eq!(0, obj.a()); 175 | assert_eq!(1, obj.b()); 176 | assert_eq!(0, obj.c()); 177 | assert_eq!(1, obj.d()); 178 | 179 | obj.set_c(2 as u64); 180 | 181 | assert_eq!(2, obj.c()); 182 | assert_eq!( 183 | WHV_UINT128 { 184 | Low64: 1 << 32, 185 | High64: (1 << 32) | 2 186 | }, 187 | obj.AsUINT128); 188 | 189 | obj.set_a(0x10111213); 190 | obj.set_b(0x14151617); 191 | obj.set_c(0x18191a1b); 192 | obj.set_d(0x1c1d1e1f); 193 | 194 | assert_eq!(0x10111213, obj.a()); 195 | assert_eq!(0x14151617, obj.b()); 196 | assert_eq!(0x18191a1b, obj.c()); 197 | assert_eq!(0x1c1d1e1f, obj.d()); 198 | assert_eq!(0x1415161710111213, obj.AsUINT128.Low64); 199 | assert_eq!(0x1c1d1e1f18191a1b, obj.AsUINT128.High64); 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/win_hv_emulation.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Cloudbase Solutions Srl 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | #![allow(non_camel_case_types)] 16 | 17 | use common::*; 18 | use win_hv_emulation_defs::*; 19 | use win_hv_platform_defs::*; 20 | 21 | pub type WHV_EMULATOR_IO_PORT_CALLBACK = 22 | extern "stdcall" fn(Context: *mut VOID, IoAccess: *mut WHV_EMULATOR_IO_ACCESS_INFO) -> HRESULT; 23 | 24 | pub type WHV_EMULATOR_MEMORY_CALLBACK = extern "stdcall" fn( 25 | Context: *mut VOID, 26 | MemoryAccess: *mut WHV_EMULATOR_MEMORY_ACCESS_INFO, 27 | ) -> HRESULT; 28 | 29 | pub type WHV_EMULATOR_GET_VIRTUAL_PROCESSOR_REGISTERS_CALLBACK = extern "stdcall" fn( 30 | Context: *mut VOID, 31 | RegisterNames: *const WHV_REGISTER_NAME, 32 | RegisterCount: UINT32, 33 | RegisterValues: *mut WHV_REGISTER_VALUE, 34 | ) -> HRESULT; 35 | 36 | pub type WHV_EMULATOR_SET_VIRTUAL_PROCESSOR_REGISTERS_CALLBACK = extern "stdcall" fn( 37 | Context: *mut VOID, 38 | RegisterNames: *const WHV_REGISTER_NAME, 39 | RegisterCount: UINT32, 40 | RegisterValues: *const WHV_REGISTER_VALUE, 41 | ) -> HRESULT; 42 | 43 | pub type WHV_EMULATOR_TRANSLATE_GVA_PAGE_CALLBACK = extern "stdcall" fn( 44 | Context: *mut VOID, 45 | Gva: WHV_GUEST_VIRTUAL_ADDRESS, 46 | TranslateFlags: WHV_TRANSLATE_GVA_FLAGS, 47 | TranslationResult: *mut WHV_TRANSLATE_GVA_RESULT_CODE, 48 | Gpa: *mut WHV_GUEST_PHYSICAL_ADDRESS, 49 | ) -> HRESULT; 50 | 51 | #[derive(Copy, Clone)] 52 | #[allow(non_snake_case)] 53 | #[repr(C)] 54 | pub struct WHV_EMULATOR_CALLBACKS { 55 | pub Size: UINT32, 56 | pub Reserved: UINT32, 57 | pub WHvEmulatorIoPortCallback: WHV_EMULATOR_IO_PORT_CALLBACK, 58 | pub WHvEmulatorMemoryCallback: WHV_EMULATOR_MEMORY_CALLBACK, 59 | pub WHvEmulatorGetVirtualProcessorRegisters: 60 | WHV_EMULATOR_GET_VIRTUAL_PROCESSOR_REGISTERS_CALLBACK, 61 | pub WHvEmulatorSetVirtualProcessorRegisters: 62 | WHV_EMULATOR_SET_VIRTUAL_PROCESSOR_REGISTERS_CALLBACK, 63 | pub WHvEmulatorTranslateGvaPage: WHV_EMULATOR_TRANSLATE_GVA_PAGE_CALLBACK, 64 | } 65 | 66 | #[allow(non_snake_case)] 67 | #[link(name = "WinHvEmulation")] 68 | extern "stdcall" { 69 | pub fn WHvEmulatorCreateEmulator( 70 | Callbacks: *const WHV_EMULATOR_CALLBACKS, 71 | Emulator: *mut WHV_EMULATOR_HANDLE, 72 | ) -> HRESULT; 73 | pub fn WHvEmulatorDestroyEmulator(Emulator: WHV_EMULATOR_HANDLE) -> HRESULT; 74 | pub fn WHvEmulatorTryIoEmulation( 75 | Emulator: WHV_EMULATOR_HANDLE, 76 | Context: *mut VOID, 77 | VpContext: *const WHV_VP_EXIT_CONTEXT, 78 | IoInstructionContext: *const WHV_X64_IO_PORT_ACCESS_CONTEXT, 79 | EmulatorReturnStatus: *mut WHV_EMULATOR_STATUS, 80 | ) -> HRESULT; 81 | pub fn WHvEmulatorTryMmioEmulation( 82 | Emulator: WHV_EMULATOR_HANDLE, 83 | Context: *mut VOID, 84 | VpContext: *const WHV_VP_EXIT_CONTEXT, 85 | MmioInstructionContext: *const WHV_MEMORY_ACCESS_CONTEXT, 86 | EmulatorReturnStatus: *mut WHV_EMULATOR_STATUS, 87 | ) -> HRESULT; 88 | } 89 | 90 | #[cfg(test)] 91 | mod tests { 92 | use super::*; 93 | use std; 94 | 95 | extern "stdcall" fn io_port_cb( 96 | _context: *mut VOID, 97 | _io_access: *mut WHV_EMULATOR_IO_ACCESS_INFO, 98 | ) -> HRESULT { 99 | S_OK 100 | } 101 | 102 | extern "stdcall" fn memory_cb( 103 | _context: *mut VOID, 104 | _memory_access: *mut WHV_EMULATOR_MEMORY_ACCESS_INFO, 105 | ) -> HRESULT { 106 | S_OK 107 | } 108 | 109 | extern "stdcall" fn get_vp_registers_cb( 110 | _context: *mut VOID, 111 | _register_names: *const WHV_REGISTER_NAME, 112 | _register_count: UINT32, 113 | _register_values: *mut WHV_REGISTER_VALUE, 114 | ) -> HRESULT { 115 | S_OK 116 | } 117 | 118 | extern "stdcall" fn set_vp_registers_cb( 119 | _context: *mut VOID, 120 | _register_names: *const WHV_REGISTER_NAME, 121 | _register_count: UINT32, 122 | _register_values: *const WHV_REGISTER_VALUE, 123 | ) -> HRESULT { 124 | S_OK 125 | } 126 | 127 | extern "stdcall" fn translate_gva_page_cb( 128 | _context: *mut VOID, 129 | _gva: WHV_GUEST_VIRTUAL_ADDRESS, 130 | _translate_flags: WHV_TRANSLATE_GVA_FLAGS, 131 | _translation_result: *mut WHV_TRANSLATE_GVA_RESULT_CODE, 132 | _gpa: *mut WHV_GUEST_PHYSICAL_ADDRESS, 133 | ) -> HRESULT { 134 | S_OK 135 | } 136 | 137 | fn with_emulator(action: F) 138 | where 139 | F: Fn(WHV_EMULATOR_HANDLE), 140 | { 141 | let mut emulator: WHV_EMULATOR_HANDLE = std::ptr::null_mut(); 142 | 143 | let callbacks = WHV_EMULATOR_CALLBACKS { 144 | Size: std::mem::size_of::() as UINT32, 145 | Reserved: 0, 146 | WHvEmulatorIoPortCallback: io_port_cb, 147 | WHvEmulatorMemoryCallback: memory_cb, 148 | WHvEmulatorGetVirtualProcessorRegisters: get_vp_registers_cb, 149 | WHvEmulatorSetVirtualProcessorRegisters: set_vp_registers_cb, 150 | WHvEmulatorTranslateGvaPage: translate_gva_page_cb, 151 | }; 152 | 153 | let result = unsafe { WHvEmulatorCreateEmulator(&callbacks, &mut emulator) }; 154 | assert_eq!( 155 | result, S_OK, 156 | "WHvEmulatorCreateEmulator failed with 0x{:X}", 157 | result 158 | ); 159 | 160 | action(emulator); 161 | 162 | let result = unsafe { WHvEmulatorDestroyEmulator(emulator) }; 163 | assert_eq!( 164 | result, S_OK, 165 | "WHvEmulatorDestroyEmulator failed with 0x{:X}", 166 | result 167 | ); 168 | } 169 | 170 | #[test] 171 | fn test_create_destroy_emulator() { 172 | with_emulator(|_emulator| {}); 173 | } 174 | 175 | #[test] 176 | fn test_try_io_emulation() { 177 | with_emulator(|emulator| { 178 | let context = std::ptr::null_mut(); 179 | let mut vp_context: WHV_VP_EXIT_CONTEXT = Default::default(); 180 | let io_instruction_context: WHV_X64_IO_PORT_ACCESS_CONTEXT = Default::default(); 181 | let mut emulator_status: WHV_EMULATOR_STATUS = Default::default(); 182 | 183 | // Without this WHvEmulatorTryIoEmulation returns E_INVALIDARG 184 | vp_context.InstructionLengthCr8 = 0xF; 185 | 186 | let result = unsafe { 187 | WHvEmulatorTryIoEmulation( 188 | emulator, 189 | context, 190 | &vp_context, 191 | &io_instruction_context, 192 | &mut emulator_status, 193 | ) 194 | }; 195 | 196 | assert_eq!( 197 | result, S_OK, 198 | "WHvEmulatorTryIoEmulation failed with 0x{:X}", 199 | result 200 | ); 201 | }); 202 | } 203 | 204 | #[test] 205 | fn test_try_mmiio_emulation() { 206 | with_emulator(|emulator| { 207 | let context = std::ptr::null_mut(); 208 | let vp_context: WHV_VP_EXIT_CONTEXT = Default::default(); 209 | let mmio_instruction_context: WHV_MEMORY_ACCESS_CONTEXT = Default::default(); 210 | let mut emulator_status: WHV_EMULATOR_STATUS = Default::default(); 211 | 212 | let result = unsafe { 213 | WHvEmulatorTryMmioEmulation( 214 | emulator, 215 | context, 216 | &vp_context, 217 | &mmio_instruction_context, 218 | &mut emulator_status, 219 | ) 220 | }; 221 | 222 | assert_eq!( 223 | result, S_OK, 224 | "WHvEmulatorTryMmioEmulation failed with 0x{:X}", 225 | result 226 | ); 227 | }); 228 | } 229 | 230 | #[test] 231 | fn test_data_type_sizes() { 232 | // Make sure all unions and structs have a size that matches the value 233 | // obtained with a sizeof() in C. 234 | assert_eq!(std::mem::size_of::(), 48); 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | -------------------------------------------------------------------------------- /examples/payload/payload.c: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Cloudbase Solutions Srl 2 | // Copyright 2018-2019 CrowdStrike, Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "helpers.h" 22 | #include "interrupt.h" 23 | 24 | extern void interrupt_handler_host(); 25 | extern void interrupt_handler_guest(); 26 | 27 | #define CPUID_EXT_HYPERVISOR ((uint32_t)(1 << 31)) 28 | 29 | #define LOG_PORT 42 30 | #define MAX_VALUE_CHARS 11 31 | #define BUF_SIZE 1024 32 | 33 | static void out_string(uint16_t port, char* value); 34 | 35 | // IDT and GDT for this guest 36 | IDT_ENTRY64 Idt[NUM_IDT_ENTRIES]; 37 | GDT_ENTRY64 Gdt[NUM_GDT_ENTRIES]; 38 | 39 | // Set that interrupts have been enabled when handling interrupt from host 40 | int interrupts_enabled = 0; 41 | 42 | static void outb(uint16_t port, uint8_t value) { 43 | asm("outb %0, %1" : /* empty */ : "a" (value), "Nd" (port) : "memory"); 44 | } 45 | 46 | __attribute__((unused)) 47 | static void inb(uint16_t port, uint8_t* value) { 48 | asm volatile("inb %1, %0" : "=a"(*value) : "Nd"(port)); 49 | } 50 | 51 | static void out_string(uint16_t port, char* value) { 52 | const char* p; 53 | for (p = value; *p; ++p) 54 | outb(port, *p); 55 | } 56 | 57 | static void out_string_max(uint16_t port, char* value, uint32_t max_len) { 58 | const char* p; 59 | for (p = value; *p && (p - value) < max_len; ++p) 60 | outb(port, *p); 61 | } 62 | 63 | static void get_cpuid(unsigned leaf, unsigned* regs) { 64 | asm volatile("cpuid": "=a" (regs[0]), "=b" (regs[1]), 65 | "=c" (regs[2]), "=d" (regs[3]) : "a" (leaf)); 66 | } 67 | 68 | static void cpu_set_msr(uint32_t msr, uint32_t lo, uint32_t hi) { 69 | asm volatile("wrmsr" : : "a"(lo), "d"(hi), "c"(msr)); 70 | } 71 | 72 | static void cpu_get_msr(uint32_t msr, uint32_t *lo, uint32_t *hi) { 73 | asm volatile("rdmsr" : "=a"(*lo), "=d"(*hi) : "c"(msr)); 74 | } 75 | 76 | static void halt(uint32_t value) { 77 | asm("hlt" : /* empty */ : "a" (value) : "memory"); 78 | } 79 | 80 | static inline void mmio_writeq(uint64_t val, volatile void *addr) 81 | { 82 | asm volatile("mov" "q" " %0, %1": : 83 | "r" (val), "m" (*(volatile uint64_t *)addr) :"memory"); 84 | } 85 | 86 | static inline uint64_t mmio_readq(volatile void *addr) 87 | { 88 | uint64_t val; 89 | asm volatile("mov" "q" " %1, %0": 90 | "=r" (val): "m" (*(volatile uint64_t *)addr) :"memory"); 91 | return val; 92 | } 93 | 94 | static inline void mmio_writeb(uint8_t val, volatile void *addr) 95 | { 96 | asm volatile("mov %0, %1": : 97 | "r" (val), "m" (*(volatile uint8_t *)addr) :"memory"); 98 | } 99 | 100 | static inline uint8_t mmio_readb(volatile void *addr) 101 | { 102 | uint8_t val; 103 | asm volatile("mov" "b" " %1, %0": 104 | "=r" (val): "m" (*(volatile uint8_t *)addr) :"memory"); 105 | return val; 106 | } 107 | 108 | /* Small helper function to print a decimal value and a corresponding 109 | description, lacking APIs like snprintf. 110 | */ 111 | __attribute__((unused)) 112 | void print_dec(uint32_t value, char* desc) 113 | { 114 | char buf[BUF_SIZE]; 115 | char value_str[MAX_VALUE_CHARS]; 116 | uint32_t len; 117 | 118 | len = itoa(value, value_str); 119 | 120 | memcpy(buf, value_str, MAX_VALUE_CHARS); 121 | memcpy(&buf[len], desc, BUF_SIZE - MAX_VALUE_CHARS - 1); 122 | 123 | out_string(LOG_PORT, buf); 124 | } 125 | 126 | static void gdt_set_descriptor(PGDT_ENTRY64 gdt, uint32_t index, uint32_t base, 127 | uint32_t limit, uint8_t access, uint8_t flags) 128 | { 129 | gdt[index].base_low = (base & 0xffff); 130 | gdt[index].base_middle = (base >> 16) & 0xff; 131 | gdt[index].base_high = (base >> 24) & 0xff; 132 | 133 | gdt[index].limit_low = (limit & 0xffff); 134 | 135 | gdt[index].flags_limit_high = (limit >> 16) & 0xf; 136 | gdt[index].flags_limit_high |= flags & 0xf0; 137 | 138 | gdt[index].access = access; 139 | } 140 | 141 | void 142 | initialize_gdt(PGDT_ENTRY64 gdt) 143 | { 144 | DT_PTR64 gdt_ptr; 145 | uint32_t gdt_size = sizeof(GDT_ENTRY64) * NUM_GDT_ENTRIES; 146 | 147 | memset(&gdt_ptr, 0, sizeof(gdt_ptr)); 148 | memset(gdt, 0, gdt_size); 149 | 150 | // Fill in the special GDT pointer to be loaded into the GDT Register 151 | gdt_ptr.limit = gdt_size - 1; 152 | gdt_ptr.base = (uintptr_t)gdt; 153 | 154 | gdt_set_descriptor(gdt, 0, 0, 0, 0, 0); // Null segment 155 | gdt_set_descriptor(gdt, 1, 0, 0xffffffff, 0x98, 0x20); // KM Code segment 156 | gdt_set_descriptor(gdt, 2, 0, 0xffffffff, 0x93, 0xcf); // KM Data segment 157 | gdt_set_descriptor(gdt, 3, 0, 0xffffffff, 0xfa, 0xcf); // UM code segment 158 | gdt_set_descriptor(gdt, 4, 0, 0xffffffff, 0xf2, 0xcf); // UM data segment 159 | 160 | load_gdt_register(&(gdt_ptr.limit)); 161 | } 162 | 163 | void initialize_idt(PIDT_ENTRY64 idt) 164 | { 165 | DT_PTR64 idt_ptr; 166 | 167 | memset(&idt_ptr, 0, sizeof(idt_ptr)); 168 | 169 | uint32_t idt_size = sizeof(IDT_ENTRY64) * NUM_IDT_ENTRIES; 170 | 171 | memset(idt, 0, idt_size); 172 | 173 | // Fill in the special IDT pointer to be loaded into the IDT Register 174 | idt_ptr.limit = idt_size - 1; 175 | idt_ptr.base = (uintptr_t)idt; 176 | 177 | // Point the processor's internal register to the new IDT 178 | load_idt_register(&(idt_ptr.limit)); 179 | } 180 | 181 | static void register_interrupt_handler(PIDT_ENTRY64 idt, uint32_t vector, void* handler) 182 | { 183 | PIDT_ENTRY64 idte; 184 | 185 | // Get the address of the IDT entry at the specified vector 186 | idte = &idt[vector]; 187 | 188 | // Set the data at that entry 189 | idte->fields.offset_low = (uint16_t)((uint64_t)handler & 0xffff); 190 | idte->fields.offset_middle = (uint16_t)(((uint64_t)handler >> 16) & 0xffff); 191 | idte->fields.offset_high = (uint32_t)((uint64_t)handler >> 32); 192 | idte->fields.selector = GDT_R0_CODE; 193 | idte->fields.ist_index = 0; 194 | idte->fields.reserved0 = 0; 195 | idte->fields.type = 0xe; 196 | idte->fields.dpl = 0; 197 | idte->fields.present = 1; 198 | idte->fields.reserved1 = 0; 199 | } 200 | 201 | void clear_apic_eoi() 202 | { 203 | uint32_t* eoi_reg_low = (uint32_t*)(APIC_BASE + APIC_MMIO_OFFSET_EOI_LOW); 204 | uint32_t* eoi_reg_high = (uint32_t*)(APIC_BASE + APIC_MMIO_OFFSET_EOI_HIGH); 205 | *eoi_reg_high = 0; 206 | *eoi_reg_low = 0; 207 | } 208 | 209 | void log_interrupt_from_host() 210 | { 211 | out_string(LOG_PORT, "Interrupt sent from host received.\n"); 212 | 213 | // Interrupt received. Interrupts are enabled on the host. 214 | interrupts_enabled = 1; 215 | clear_apic_eoi(); 216 | } 217 | 218 | void log_interrupt_from_guest() 219 | { 220 | out_string(LOG_PORT, "Interrupt sent from guest received.\n"); 221 | clear_apic_eoi(); 222 | } 223 | 224 | /* 225 | Send the IPI by writing to the two Interrupt Command Registers (ICRs). They 226 | are memory mapped at APIC_BASE + 0x300 (low register) and APIC_BASE + 0x310 227 | (high register) 228 | Since our virtual memory is identity mapped, we can just write to the 229 | expected physical address of APIC_BASE + ICR. 230 | */ 231 | static void apic_send_ipi(uint64_t apic_base, uint32_t high, uint32_t low) 232 | { 233 | uint32_t* icr_low = (uint32_t*)(apic_base + APIC_MMIO_OFFSET_ICR_LOW); 234 | uint32_t* icr_high = (uint32_t*)(apic_base + APIC_MMIO_OFFSET_ICR_HIGH); 235 | 236 | out_string(LOG_PORT, "Sending IPI from the guest\n"); 237 | 238 | // From the manual, the act of writing to the low doubleword of the ICR 239 | // causes the IPI to be sent 240 | *icr_high = high; 241 | *icr_low = low; 242 | } 243 | 244 | void 245 | __attribute__((section(".start"))) 246 | _start(void) { 247 | initialize_gdt(Gdt); 248 | initialize_idt(Idt); 249 | 250 | out_string(LOG_PORT, "Greetings from the guest!\n"); 251 | 252 | register_interrupt_handler(Idt, HOST_INT_VECTOR, (void*)interrupt_handler_host); 253 | register_interrupt_handler(Idt, GUEST_INT_VECTOR, (void*)interrupt_handler_guest); 254 | 255 | enable_interrupt_flag(); 256 | 257 | unsigned regs[] = {0, 0, 0, 0}; 258 | get_cpuid(1, regs); 259 | if (regs[2] == CPUID_EXT_HYPERVISOR) { 260 | out_string(LOG_PORT, "Hypervisor present\n"); 261 | } 262 | 263 | memset(regs, 0, sizeof(regs)); 264 | get_cpuid(0x40000000, regs); 265 | 266 | char id[13] = {0}; 267 | memcpy(id, ®s[1], 12); 268 | id[12] = 0; 269 | 270 | out_string(LOG_PORT, "Hypervisor ID: "); 271 | out_string_max(LOG_PORT, id, sizeof(id)); 272 | out_string(LOG_PORT, "\n"); 273 | 274 | unsigned char in_byte; 275 | inb(43, &in_byte); 276 | print_dec((uint32_t)in_byte, ": Value obtained via INB IO Port read\n"); 277 | 278 | uint32_t lo, hi = 0; 279 | cpu_get_msr(1, &lo, &hi); 280 | cpu_set_msr(1, lo + 1, hi + 1); 281 | 282 | // Take advantage of identity memory mapping to read/write an unmapped 283 | // memory location to generate an MMIO exit 284 | unsigned char* mmio_buf = (unsigned char*)0x3f00000; 285 | 286 | // Do tests of quad word 287 | uint64_t data = 0; 288 | data = mmio_readq(mmio_buf); 289 | print_dec(data, ": Qword read via MMIO read\n"); 290 | mmio_writeq(data + 1, mmio_buf); 291 | 292 | // Do tests of a single byte 293 | uint8_t byte = mmio_readb(mmio_buf); 294 | print_dec((uint32_t)byte, ": Byte read via MMIO read\n"); 295 | mmio_writeb(byte + 1, mmio_buf); 296 | 297 | 298 | // Send an IPI to the vector we registered earlier. The host will also use 299 | // this as a signal to terminate the guest. 300 | // - Level = 1 = Assert (Bit 14 on) (Must be set to 1 for fixed interrupt 301 | // type) 302 | // - Destination Shorthand = 01 = Self (Bit 18 on). Ignore destination 303 | // register (ICR high) and send the IPI to the issuing APIC (self) 304 | // - Delivery mode = 000 (Fixed) 305 | // - Destination mode = 0 (Physical) 306 | uint32_t icr_low_val = 0x00044000 | GUEST_INT_VECTOR; 307 | apic_send_ipi(APIC_BASE, 0, icr_low_val); 308 | 309 | // Only halt the vcpu if interrupts are not enabled. Otherwise, the host 310 | // will take care of terminating the guest. 311 | if (interrupts_enabled == 0) { 312 | halt(0); 313 | } 314 | 315 | return; 316 | } 317 | -------------------------------------------------------------------------------- /src/win_hv_platform.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Cloudbase Solutions Srl 2 | // Copyright 2018-2019 CrowdStrike, Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | #![allow(non_camel_case_types)] 17 | 18 | use common::*; 19 | use win_hv_platform_defs::*; 20 | 21 | #[allow(non_snake_case)] 22 | #[link(name = "WinHvPlatform")] 23 | extern "stdcall" { 24 | pub fn WHvCreatePartition(Partition: *mut WHV_PARTITION_HANDLE) -> HRESULT; 25 | pub fn WHvSetupPartition(Partition: WHV_PARTITION_HANDLE) -> HRESULT; 26 | pub fn WHvDeletePartition(Partition: WHV_PARTITION_HANDLE) -> HRESULT; 27 | pub fn WHvCreateVirtualProcessor( 28 | Partition: WHV_PARTITION_HANDLE, 29 | VpIndex: UINT32, 30 | Flags: UINT32, 31 | ) -> HRESULT; 32 | pub fn WHvDeleteVirtualProcessor(Partition: WHV_PARTITION_HANDLE, VpIndex: UINT32) -> HRESULT; 33 | pub fn WHvSetPartitionProperty( 34 | Partition: WHV_PARTITION_HANDLE, 35 | PropertyCode: WHV_PARTITION_PROPERTY_CODE, 36 | PropertyBuffer: *const VOID, 37 | PropertyBufferSizeInBytes: UINT32, 38 | ) -> HRESULT; 39 | pub fn WHvGetPartitionProperty( 40 | Partition: WHV_PARTITION_HANDLE, 41 | PropertyCode: WHV_PARTITION_PROPERTY_CODE, 42 | PropertyBuffer: *mut VOID, 43 | PropertyBufferSizeInBytes: UINT32, 44 | WrittenSizeInBytes: *mut UINT32, 45 | ) -> HRESULT; 46 | pub fn WHvGetCapability( 47 | CapabilityCode: WHV_CAPABILITY_CODE, 48 | CapabilityBuffer: *mut VOID, 49 | CapabilityBufferSizeInBytes: UINT32, 50 | WrittenSizeInBytes: *mut UINT32, 51 | ) -> HRESULT; 52 | pub fn WHvCancelRunVirtualProcessor( 53 | Partition: WHV_PARTITION_HANDLE, 54 | VpIndex: UINT32, 55 | Flags: UINT32, 56 | ) -> HRESULT; 57 | pub fn WHvRunVirtualProcessor( 58 | Partition: WHV_PARTITION_HANDLE, 59 | VpIndex: UINT32, 60 | ExitContext: *mut VOID, 61 | ExitContextSizeInBytes: UINT32, 62 | ) -> HRESULT; 63 | pub fn WHvSetVirtualProcessorRegisters( 64 | Partition: WHV_PARTITION_HANDLE, 65 | VpIndex: UINT32, 66 | RegisterNames: *const WHV_REGISTER_NAME, 67 | RegisterCount: UINT32, 68 | RegisterValues: *const WHV_REGISTER_VALUE, 69 | ) -> HRESULT; 70 | pub fn WHvGetVirtualProcessorRegisters( 71 | Partition: WHV_PARTITION_HANDLE, 72 | VpIndex: UINT32, 73 | RegisterNames: *const WHV_REGISTER_NAME, 74 | RegisterCount: UINT32, 75 | RegisterValues: *mut WHV_REGISTER_VALUE, 76 | ) -> HRESULT; 77 | pub fn WHvMapGpaRange( 78 | Partition: WHV_PARTITION_HANDLE, 79 | SourceAddress: *const VOID, 80 | GuestAddress: WHV_GUEST_PHYSICAL_ADDRESS, 81 | SizeInBytes: UINT64, 82 | Flags: WHV_MAP_GPA_RANGE_FLAGS, 83 | ) -> HRESULT; 84 | pub fn WHvUnmapGpaRange( 85 | Partition: WHV_PARTITION_HANDLE, 86 | GuestAddress: WHV_GUEST_PHYSICAL_ADDRESS, 87 | SizeInBytes: UINT64, 88 | ) -> HRESULT; 89 | pub fn WHvTranslateGva( 90 | Partition: WHV_PARTITION_HANDLE, 91 | VpIndex: UINT32, 92 | Gva: WHV_GUEST_VIRTUAL_ADDRESS, 93 | TranslateFlags: WHV_TRANSLATE_GVA_FLAGS, 94 | TranslationResult: *mut WHV_TRANSLATE_GVA_RESULT, 95 | Gpa: *mut WHV_GUEST_PHYSICAL_ADDRESS, 96 | ) -> HRESULT; 97 | pub fn WHvGetVirtualProcessorInterruptControllerState( 98 | Partition: WHV_PARTITION_HANDLE, 99 | VpIndex: UINT32, 100 | State: *mut VOID, 101 | StateSize: UINT32, 102 | WrittenSize: *mut UINT32, 103 | ) -> HRESULT; 104 | pub fn WHvSetVirtualProcessorInterruptControllerState( 105 | Partition: WHV_PARTITION_HANDLE, 106 | VpIndex: UINT32, 107 | State: *const VOID, 108 | StateSize: UINT32, 109 | ) -> HRESULT; 110 | pub fn WHvRequestInterrupt( 111 | Partition: WHV_PARTITION_HANDLE, 112 | Interrupt: *const WHV_INTERRUPT_CONTROL, 113 | InterruptControlSize: UINT32, 114 | ) -> HRESULT; 115 | pub fn WHvQueryGpaRangeDirtyBitmap( 116 | Partition: WHV_PARTITION_HANDLE, 117 | GuestAddress: WHV_GUEST_PHYSICAL_ADDRESS, 118 | RangeSizeInBytes: UINT64, 119 | Bitmap: *mut UINT64, 120 | BitmapSizeInBytes: UINT32, 121 | ) -> HRESULT; 122 | pub fn WHvGetPartitionCounters( 123 | Partition: WHV_PARTITION_HANDLE, 124 | CounterSet: WHV_PARTITION_COUNTER_SET, 125 | Buffer: *mut VOID, 126 | BufferSizeInBytes: UINT32, 127 | BytesWritten: *mut UINT32, 128 | ) -> HRESULT; 129 | pub fn WHvGetVirtualProcessorCounters( 130 | Partition: WHV_PARTITION_HANDLE, 131 | VpIndex: UINT32, 132 | CounterSet: WHV_PROCESSOR_COUNTER_SET, 133 | Buffer: *mut VOID, 134 | BufferSizeInBytes: UINT32, 135 | BytesWritten: *mut UINT32, 136 | ) -> HRESULT; 137 | pub fn WHvGetVirtualProcessorXsaveState( 138 | Partition: WHV_PARTITION_HANDLE, 139 | VpIndex: UINT32, 140 | Buffer: *mut VOID, 141 | BufferSizeInBytes: UINT32, 142 | BytesWritten: *mut UINT32, 143 | ) -> HRESULT; 144 | pub fn WHvSetVirtualProcessorXsaveState( 145 | Partition: WHV_PARTITION_HANDLE, 146 | VpIndex: UINT32, 147 | Buffer: *const VOID, 148 | BufferSizeInBytes: UINT32, 149 | ) -> HRESULT; 150 | } 151 | 152 | #[cfg(test)] 153 | mod tests { 154 | use super::*; 155 | use std; 156 | 157 | fn check_hypervisor_present() { 158 | let mut capability: WHV_CAPABILITY = Default::default(); 159 | let mut written_size: UINT32 = 0; 160 | 161 | let result = unsafe { 162 | WHvGetCapability( 163 | WHV_CAPABILITY_CODE::WHvCapabilityCodeHypervisorPresent, 164 | &mut capability as *mut _ as *mut VOID, 165 | std::mem::size_of::() as UINT32, 166 | &mut written_size, 167 | ) 168 | }; 169 | 170 | assert_eq!(result, S_OK, "WHvGetCapability failed with 0x{:X}", result); 171 | 172 | assert_eq!( 173 | std::mem::size_of::() as UINT32, 174 | written_size, 175 | "WrittenSizeInBytes does not match BOOL size {}", 176 | written_size 177 | ); 178 | 179 | unsafe { 180 | assert_eq!(capability.HypervisorPresent, TRUE, "Hypervisor not present"); 181 | }; 182 | } 183 | 184 | fn with_partition(action: F) 185 | where 186 | F: Fn(WHV_PARTITION_HANDLE), 187 | { 188 | check_hypervisor_present(); 189 | 190 | let mut part: WHV_PARTITION_HANDLE = std::ptr::null_mut(); 191 | 192 | let result = unsafe { WHvCreatePartition(&mut part) }; 193 | assert_eq!( 194 | result, S_OK, 195 | "WHvCreatePartition failed with 0x{:X}", 196 | result 197 | ); 198 | 199 | action(part); 200 | 201 | let result = unsafe { WHvDeletePartition(part) }; 202 | assert_eq!( 203 | result, S_OK, 204 | "WHvDeletePartition failed with 0x{:X}", 205 | result 206 | ); 207 | } 208 | 209 | fn with_vcpu(part: WHV_PARTITION_HANDLE, action: F) 210 | where 211 | F: Fn(UINT32), 212 | { 213 | let vp_index = 0; 214 | let mut prop: WHV_PARTITION_PROPERTY = Default::default(); 215 | 216 | let result = unsafe { 217 | prop.ProcessorCount = 1; 218 | 219 | WHvSetPartitionProperty( 220 | part, 221 | WHV_PARTITION_PROPERTY_CODE::WHvPartitionPropertyCodeProcessorCount, 222 | &mut prop as *mut _ as *mut VOID, 223 | std::mem::size_of::() as UINT32, 224 | ) 225 | }; 226 | assert_eq!( 227 | result, S_OK, 228 | "WHvSetPartitionProperty failed with 0x{:X}", 229 | result 230 | ); 231 | 232 | let result = unsafe { WHvSetupPartition(part) }; 233 | assert_eq!(result, S_OK, "WHvSetupPartition failed with 0x{:X}", result); 234 | 235 | let result = unsafe { WHvCreateVirtualProcessor(part, vp_index, 0) }; 236 | assert_eq!( 237 | result, S_OK, 238 | "WHvCreateVirtualProcessor failed with 0x{:X}", 239 | result 240 | ); 241 | 242 | action(vp_index); 243 | 244 | let result = unsafe { WHvDeleteVirtualProcessor(part, vp_index) }; 245 | assert_eq!( 246 | result, S_OK, 247 | "WHvDeleteVirtualProcessor failed with 0x{:X}", 248 | result 249 | ); 250 | } 251 | 252 | #[test] 253 | fn test_get_capability() { 254 | let mut capability: WHV_CAPABILITY = Default::default(); 255 | let mut written_size: UINT32 = 0; 256 | 257 | let result = unsafe { 258 | WHvGetCapability( 259 | WHV_CAPABILITY_CODE::WHvCapabilityCodeFeatures, 260 | &mut capability as *mut _ as *mut VOID, 261 | std::mem::size_of::() as UINT32, 262 | &mut written_size, 263 | ) 264 | }; 265 | 266 | assert_eq!(result, S_OK, "WHvGetCapability failed with 0x{:X}", result); 267 | assert_eq!( 268 | std::mem::size_of::() as UINT32, 269 | written_size, 270 | "WrittenSizeInBytes does not match UINT64 size {}", 271 | written_size 272 | ); 273 | } 274 | 275 | #[test] 276 | fn test_create_delete_partition() { 277 | with_partition(|_part| {}); 278 | } 279 | 280 | #[test] 281 | fn test_set_get_partition_property() { 282 | with_partition(|part| { 283 | let mut prop: WHV_PARTITION_PROPERTY = Default::default(); 284 | let mut prop_out: WHV_PARTITION_PROPERTY = Default::default(); 285 | let mut written_size: UINT32 = 0; 286 | 287 | let result = unsafe { 288 | prop.ProcessorCount = 1; 289 | 290 | WHvSetPartitionProperty( 291 | part, 292 | WHV_PARTITION_PROPERTY_CODE::WHvPartitionPropertyCodeProcessorCount, 293 | &prop as *const _ as *const VOID, 294 | std::mem::size_of::() as UINT32, 295 | ) 296 | }; 297 | 298 | assert_eq!( 299 | result, S_OK, 300 | "WHvSetPartitionProperty failed with 0x{:X}", 301 | result 302 | ); 303 | 304 | let result = unsafe { 305 | WHvGetPartitionProperty( 306 | part, 307 | WHV_PARTITION_PROPERTY_CODE::WHvPartitionPropertyCodeProcessorCount, 308 | &mut prop_out as *mut _ as *mut VOID, 309 | std::mem::size_of::() as UINT32, 310 | &mut written_size, 311 | ) 312 | }; 313 | 314 | unsafe { 315 | assert_eq!( 316 | prop.ProcessorCount, prop_out.ProcessorCount, 317 | "Partition property value not matching" 318 | ); 319 | } 320 | 321 | assert_eq!( 322 | result, S_OK, 323 | "WHvGetPartitionProperty failed with 0x{:X}", 324 | result 325 | ); 326 | assert_eq!( 327 | std::mem::size_of::() as UINT32, 328 | written_size, 329 | "WrittenSizeInBytes does not match BOOL size {}", 330 | written_size 331 | ); 332 | }); 333 | } 334 | 335 | #[test] 336 | fn test_setup_partition() { 337 | with_partition(|part| { 338 | let result = unsafe { WHvSetupPartition(part) }; 339 | assert_eq!( 340 | result, WHV_E_INVALID_PARTITION_CONFIG, 341 | "WHvSetupPartition failed with 0x{:X}", 342 | result 343 | ); 344 | }); 345 | } 346 | 347 | #[test] 348 | fn test_create_delete_vcpu() { 349 | with_partition(|part| { 350 | with_vcpu(part, |_vp_index| {}); 351 | }); 352 | } 353 | 354 | #[test] 355 | fn test_set_get_vcpu_registers() { 356 | with_partition(|part| { 357 | with_vcpu(part, |vp_index| { 358 | const NUM_REGS: UINT32 = 1; 359 | const REG_VALUE: UINT64 = 11111111; 360 | let mut reg_names: [WHV_REGISTER_NAME; NUM_REGS as usize] = Default::default(); 361 | let mut reg_values: [WHV_REGISTER_VALUE; NUM_REGS as usize] = Default::default(); 362 | let mut reg_values_out: [WHV_REGISTER_VALUE; NUM_REGS as usize] = 363 | Default::default(); 364 | 365 | reg_names[0] = WHV_REGISTER_NAME::WHvX64RegisterRax; 366 | reg_values[0].Reg64 = REG_VALUE; 367 | 368 | let result = unsafe { 369 | WHvSetVirtualProcessorRegisters( 370 | part, 371 | vp_index, 372 | reg_names.as_ptr(), 373 | NUM_REGS, 374 | reg_values.as_ptr(), 375 | ) 376 | }; 377 | assert_eq!( 378 | result, S_OK, 379 | "WHvSetVirtualProcessorRegisters failed with 0x{:X}", 380 | result 381 | ); 382 | 383 | let result = unsafe { 384 | WHvGetVirtualProcessorRegisters( 385 | part, 386 | vp_index, 387 | reg_names.as_ptr(), 388 | NUM_REGS, 389 | reg_values_out.as_mut_ptr(), 390 | ) 391 | }; 392 | assert_eq!( 393 | result, S_OK, 394 | "WHvGetVirtualProcessorRegisters failed with 0x{:X}", 395 | result 396 | ); 397 | 398 | unsafe { 399 | assert_eq!( 400 | reg_values_out[0].Reg64, REG_VALUE, 401 | "Registers values fo not match" 402 | ); 403 | } 404 | }); 405 | }); 406 | } 407 | 408 | #[test] 409 | fn test_run_vcpu() { 410 | with_partition(|part| { 411 | with_vcpu(part, |vp_index| { 412 | let mut exit_context: WHV_RUN_VP_EXIT_CONTEXT = Default::default(); 413 | let exit_context_size = std::mem::size_of::() as UINT32; 414 | 415 | let result = unsafe { 416 | WHvRunVirtualProcessor( 417 | part, 418 | vp_index, 419 | &mut exit_context as *mut _ as *mut VOID, 420 | 1, 421 | ) 422 | }; 423 | 424 | assert_eq!( 425 | result, WHV_E_INSUFFICIENT_BUFFER, 426 | "WHvRunVirtualProcessor failed with 0x{:X}", 427 | result 428 | ); 429 | 430 | exit_context = Default::default(); 431 | let result = unsafe { 432 | WHvRunVirtualProcessor( 433 | part, 434 | vp_index, 435 | &mut exit_context as *mut _ as *mut VOID, 436 | exit_context_size, 437 | ) 438 | }; 439 | 440 | assert_eq!( 441 | result, S_OK, 442 | "WHvRunVirtualProcessor failed with 0x{:X}", 443 | result 444 | ); 445 | 446 | assert_eq!( 447 | exit_context.ExitReason, 448 | WHV_RUN_VP_EXIT_REASON::WHvRunVpExitReasonMemoryAccess, 449 | "Unexpected exit reason" 450 | ) 451 | }); 452 | }); 453 | } 454 | 455 | #[test] 456 | fn test_cancel_run_vcpu() { 457 | with_partition(|part| { 458 | with_vcpu(part, |vp_index| { 459 | let result = unsafe { WHvCancelRunVirtualProcessor(part, vp_index, 0) }; 460 | 461 | assert_eq!( 462 | result, S_OK, 463 | "WHvCancelRunVirtualProcessor failed with 0x{:X}", 464 | result 465 | ); 466 | }); 467 | }); 468 | } 469 | 470 | #[test] 471 | fn test_map_gpa_range() { 472 | with_partition(|part| { 473 | const SIZE: UINT64 = 1024; 474 | let source_address = Box::new([0; SIZE as usize]); 475 | let guest_address: WHV_GUEST_PHYSICAL_ADDRESS = 0; 476 | 477 | let result = unsafe { 478 | WHvMapGpaRange( 479 | part, 480 | source_address.as_ptr() as *const VOID, 481 | guest_address, 482 | SIZE, 483 | WHV_MAP_GPA_RANGE_FLAGS::WHvMapGpaRangeFlagRead 484 | | WHV_MAP_GPA_RANGE_FLAGS::WHvMapGpaRangeFlagWrite, 485 | ) 486 | }; 487 | 488 | // TODO(alexpilotti): modify this test to have an S_OK result. 489 | // This error is expected with this test setup 490 | assert_eq!( 491 | result, E_INVALIDARG, 492 | "WHvMapGpaRange failed with 0x{:X}", 493 | result 494 | ); 495 | }); 496 | } 497 | 498 | #[test] 499 | #[ignore] 500 | fn test_unmap_gpa_range_not_found() { 501 | with_partition(|part| { 502 | const SIZE: UINT64 = 1024; 503 | let guest_address: WHV_GUEST_PHYSICAL_ADDRESS = 0; 504 | 505 | let result = unsafe { WHvUnmapGpaRange(part, guest_address, SIZE) }; 506 | 507 | assert_eq!( 508 | result, WHV_E_GPA_RANGE_NOT_FOUND, 509 | "WHvUnmapGpaRange failed with 0x{:X}", 510 | result 511 | ); 512 | }); 513 | } 514 | 515 | #[test] 516 | fn test_translate_gva() { 517 | with_partition(|part| { 518 | with_vcpu(part, |vp_index| { 519 | let gva: WHV_GUEST_PHYSICAL_ADDRESS = 0; 520 | let mut gpa: WHV_GUEST_PHYSICAL_ADDRESS = 0; 521 | let mut translation_result: WHV_TRANSLATE_GVA_RESULT = Default::default(); 522 | 523 | let result = unsafe { 524 | WHvTranslateGva( 525 | part, 526 | vp_index, 527 | gva, 528 | WHV_TRANSLATE_GVA_FLAGS::WHvTranslateGvaFlagValidateRead, 529 | &mut translation_result, 530 | &mut gpa, 531 | ) 532 | }; 533 | 534 | assert_eq!(result, S_OK, "WHvTranslateGva failed with 0x{:X}", result); 535 | 536 | // This API changed, it used to return GpaUnmapped, now it runs Success. 537 | // So support both versions for now. 538 | // 539 | let result = translation_result.ResultCode; 540 | assert!( 541 | result == WHV_TRANSLATE_GVA_RESULT_CODE::WHvTranslateGvaResultGpaUnmapped || 542 | result == WHV_TRANSLATE_GVA_RESULT_CODE::WHvTranslateGvaResultSuccess, 543 | "Unexpected translation result code {:?}", result); 544 | 545 | assert_eq!(gpa, 0, "Unexpected GPA value"); 546 | }); 547 | }); 548 | } 549 | } 550 | -------------------------------------------------------------------------------- /src/instruction_emulator.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Cloudbase Solutions Srl 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | use common::*; 16 | use std; 17 | use win_hv_emulation::*; 18 | pub use win_hv_emulation_defs::*; 19 | use win_hv_platform_defs::*; 20 | 21 | pub trait EmulatorCallbacks { 22 | fn io_port( 23 | &mut self, 24 | io_access: &mut WHV_EMULATOR_IO_ACCESS_INFO, 25 | ) -> HRESULT; 26 | fn memory( 27 | &mut self, 28 | memory_access: &mut WHV_EMULATOR_MEMORY_ACCESS_INFO, 29 | ) -> HRESULT; 30 | fn get_virtual_processor_registers( 31 | &mut self, 32 | register_names: &[WHV_REGISTER_NAME], 33 | register_values: &mut [WHV_REGISTER_VALUE], 34 | ) -> HRESULT; 35 | fn set_virtual_processor_registers( 36 | &mut self, 37 | register_names: &[WHV_REGISTER_NAME], 38 | register_values: &[WHV_REGISTER_VALUE], 39 | ) -> HRESULT; 40 | fn translate_gva_page( 41 | &mut self, 42 | gva: WHV_GUEST_VIRTUAL_ADDRESS, 43 | translate_flags: WHV_TRANSLATE_GVA_FLAGS, 44 | translation_result: &mut WHV_TRANSLATE_GVA_RESULT_CODE, 45 | gpa: &mut WHV_GUEST_PHYSICAL_ADDRESS, 46 | ) -> HRESULT; 47 | } 48 | 49 | #[repr(C)] 50 | struct CallbacksContext<'a, T: EmulatorCallbacks + 'a> { 51 | emulator: &'a Emulator, 52 | context: &'a mut T, 53 | } 54 | 55 | pub struct Emulator { 56 | emulator_handle: WHV_EMULATOR_HANDLE, 57 | dummy: std::marker::PhantomData, 58 | } 59 | 60 | unsafe impl Send for Emulator {} 61 | 62 | impl Emulator { 63 | pub fn new() -> Result { 64 | 65 | let native_callbacks = WHV_EMULATOR_CALLBACKS { 66 | Size: std::mem::size_of::() as UINT32, 67 | Reserved: 0, 68 | WHvEmulatorIoPortCallback: Emulator::::io_port_cb, 69 | WHvEmulatorMemoryCallback: Emulator::::memory_cb, 70 | WHvEmulatorGetVirtualProcessorRegisters: Emulator::::get_vp_registers_cb, 71 | WHvEmulatorSetVirtualProcessorRegisters: Emulator::::set_vp_registers_cb, 72 | WHvEmulatorTranslateGvaPage: Emulator::::translate_gva_page_cb, 73 | }; 74 | 75 | let mut emulator_handle: WHV_EMULATOR_HANDLE = std::ptr::null_mut(); 76 | check_result(unsafe { WHvEmulatorCreateEmulator(&native_callbacks, &mut emulator_handle) })?; 77 | Ok(Emulator { 78 | emulator_handle: emulator_handle, 79 | dummy: Default::default(), 80 | }) 81 | } 82 | 83 | fn catch_unwind_hres HRESULT + std::panic::UnwindSafe>(action: F) -> HRESULT { 84 | // Panics must not unwind across the callback boundary 85 | let res = std::panic::catch_unwind(action); 86 | match res { 87 | Ok(ret_value) => ret_value, 88 | _ => E_FAIL, 89 | } 90 | } 91 | 92 | extern "stdcall" fn io_port_cb( 93 | context: *mut VOID, 94 | io_access: *mut WHV_EMULATOR_IO_ACCESS_INFO, 95 | ) -> HRESULT { 96 | Emulator::::catch_unwind_hres(|| { 97 | let cc = unsafe { &mut *(context as *mut CallbacksContext) }; 98 | T::io_port(cc.context, unsafe { &mut *io_access }) 99 | }) 100 | } 101 | 102 | extern "stdcall" fn memory_cb( 103 | context: *mut VOID, 104 | memory_access: *mut WHV_EMULATOR_MEMORY_ACCESS_INFO, 105 | ) -> HRESULT { 106 | Emulator::::catch_unwind_hres(|| { 107 | let cc = unsafe { &mut *(context as *mut CallbacksContext) }; 108 | T::memory(cc.context, unsafe { &mut *memory_access }) 109 | }) 110 | } 111 | 112 | extern "stdcall" fn get_vp_registers_cb( 113 | context: *mut VOID, 114 | register_names: *const WHV_REGISTER_NAME, 115 | register_count: UINT32, 116 | register_values: *mut WHV_REGISTER_VALUE, 117 | ) -> HRESULT { 118 | Emulator::::catch_unwind_hres(|| { 119 | let cc = unsafe { &mut *(context as *mut CallbacksContext) }; 120 | T::get_virtual_processor_registers( 121 | cc.context, 122 | unsafe { std::slice::from_raw_parts(register_names, register_count as usize) }, 123 | unsafe { std::slice::from_raw_parts_mut(register_values, register_count as usize) }, 124 | ) 125 | }) 126 | } 127 | 128 | extern "stdcall" fn set_vp_registers_cb( 129 | context: *mut VOID, 130 | register_names: *const WHV_REGISTER_NAME, 131 | register_count: UINT32, 132 | register_values: *const WHV_REGISTER_VALUE, 133 | ) -> HRESULT { 134 | Emulator::::catch_unwind_hres(|| { 135 | let cc = unsafe { &mut *(context as *mut CallbacksContext) }; 136 | T::set_virtual_processor_registers( 137 | cc.context, 138 | unsafe { std::slice::from_raw_parts(register_names, register_count as usize) }, 139 | unsafe { std::slice::from_raw_parts(register_values, register_count as usize) }, 140 | ) 141 | }) 142 | } 143 | 144 | extern "stdcall" fn translate_gva_page_cb( 145 | context: *mut VOID, 146 | gva: WHV_GUEST_VIRTUAL_ADDRESS, 147 | translate_flags: WHV_TRANSLATE_GVA_FLAGS, 148 | translation_result: *mut WHV_TRANSLATE_GVA_RESULT_CODE, 149 | gpa: *mut WHV_GUEST_PHYSICAL_ADDRESS, 150 | ) -> HRESULT { 151 | Emulator::::catch_unwind_hres(|| { 152 | let cc = unsafe { &mut *(context as *mut CallbacksContext) }; 153 | T::translate_gva_page( 154 | cc.context, 155 | gva, 156 | translate_flags, 157 | unsafe { &mut *translation_result }, 158 | unsafe { &mut *gpa }, 159 | ) 160 | }) 161 | } 162 | 163 | pub fn try_io_emulation( 164 | &self, 165 | context: &mut T, 166 | vp_context: &WHV_VP_EXIT_CONTEXT, 167 | io_instruction_context: &WHV_X64_IO_PORT_ACCESS_CONTEXT, 168 | ) -> Result { 169 | let mut callbacks_context = CallbacksContext { 170 | emulator: self, 171 | context: context, 172 | }; 173 | 174 | let mut return_status: WHV_EMULATOR_STATUS = Default::default(); 175 | check_result(unsafe { 176 | WHvEmulatorTryIoEmulation( 177 | self.emulator_handle, 178 | &mut callbacks_context as *mut _ as *mut VOID, 179 | vp_context, 180 | io_instruction_context, 181 | &mut return_status, 182 | ) 183 | })?; 184 | Ok(return_status) 185 | } 186 | 187 | pub fn try_mmio_emulation<'a>( 188 | &self, 189 | context: &'a mut T, 190 | vp_context: &WHV_VP_EXIT_CONTEXT, 191 | mmio_instruction_context: &WHV_MEMORY_ACCESS_CONTEXT, 192 | ) -> Result { 193 | let mut callbacks_context = CallbacksContext { 194 | emulator: self, 195 | context: context, 196 | }; 197 | 198 | let mut return_status: WHV_EMULATOR_STATUS = Default::default(); 199 | check_result(unsafe { 200 | WHvEmulatorTryMmioEmulation( 201 | self.emulator_handle, 202 | &mut callbacks_context as *mut _ as *mut VOID, 203 | vp_context, 204 | mmio_instruction_context, 205 | &mut return_status, 206 | ) 207 | })?; 208 | Ok(return_status) 209 | } 210 | } 211 | 212 | impl Drop for Emulator 213 | { 214 | fn drop(&mut self) { 215 | check_result(unsafe { WHvEmulatorDestroyEmulator(self.emulator_handle) }).unwrap(); 216 | } 217 | } 218 | 219 | #[cfg(test)] 220 | mod tests { 221 | use super::*; 222 | 223 | struct TestCallbacks<'a> { 224 | expected_context: &'a str, 225 | expected_io_access_size: UINT16, 226 | expected_memory_access_size: UINT8, 227 | expected_reg_size: UINT32, 228 | expected_reg_names: &'a [WHV_REGISTER_NAME], 229 | expected_reg_values: &'a [WHV_REGISTER_VALUE], 230 | returned_reg_values: &'a [WHV_REGISTER_VALUE], 231 | returned_gpa: WHV_GUEST_PHYSICAL_ADDRESS, 232 | returned_translation_result: WHV_TRANSLATE_GVA_RESULT_CODE, 233 | } 234 | 235 | impl<'a> TestCallbacks<'a> { 236 | fn check_context(&self) { 237 | assert_eq!( 238 | self.expected_context, 239 | "context", 240 | "Unexpected context value" 241 | ); 242 | } 243 | } 244 | 245 | impl<'a> Default for TestCallbacks<'a> { 246 | fn default() -> TestCallbacks<'a> { 247 | TestCallbacks { 248 | expected_context: "context", 249 | expected_io_access_size: 0, 250 | expected_memory_access_size: 0, 251 | expected_reg_size: 0, 252 | expected_reg_names: &[], 253 | expected_reg_values: &[], 254 | returned_reg_values: &[], 255 | returned_gpa: 0, 256 | returned_translation_result: 257 | WHV_TRANSLATE_GVA_RESULT_CODE::WHvTranslateGvaResultSuccess, 258 | } 259 | } 260 | } 261 | 262 | impl<'a> EmulatorCallbacks for TestCallbacks<'a> { 263 | fn io_port( 264 | &mut self, 265 | io_access: &mut WHV_EMULATOR_IO_ACCESS_INFO, 266 | ) -> HRESULT { 267 | self.check_context(); 268 | assert_eq!( 269 | io_access.AccessSize, self.expected_io_access_size, 270 | "Unexpected AccessSize value" 271 | ); 272 | io_access.AccessSize = !io_access.AccessSize; 273 | S_OK 274 | } 275 | fn memory( 276 | &mut self, 277 | memory_access: &mut WHV_EMULATOR_MEMORY_ACCESS_INFO, 278 | ) -> HRESULT { 279 | self.check_context(); 280 | assert_eq!( 281 | memory_access.AccessSize, self.expected_memory_access_size, 282 | "Unexpected AccessSize value" 283 | ); 284 | memory_access.AccessSize = !memory_access.AccessSize; 285 | S_OK 286 | } 287 | fn get_virtual_processor_registers( 288 | &mut self, 289 | register_names: &[WHV_REGISTER_NAME], 290 | register_values: &mut [WHV_REGISTER_VALUE], 291 | ) -> HRESULT { 292 | self.check_context(); 293 | assert_eq!( 294 | register_names.len(), 295 | self.expected_reg_size as usize, 296 | "Unexpected register_names size" 297 | ); 298 | assert_eq!( 299 | register_values.len(), 300 | self.expected_reg_size as usize, 301 | "Unexpected register_values size" 302 | ); 303 | assert_eq!( 304 | register_names, self.expected_reg_names, 305 | "Unexpected reg names" 306 | ); 307 | assert_eq!( 308 | register_values.len(), 309 | self.returned_reg_values.len(), 310 | "{}{}", 311 | "The length of returned_reg_values does not match with the length ", 312 | "of register_values" 313 | ); 314 | register_values[..].copy_from_slice(self.returned_reg_values); 315 | S_OK 316 | } 317 | fn set_virtual_processor_registers( 318 | &mut self, 319 | register_names: &[WHV_REGISTER_NAME], 320 | register_values: &[WHV_REGISTER_VALUE], 321 | ) -> HRESULT { 322 | self.check_context(); 323 | assert_eq!( 324 | register_names.len(), 325 | self.expected_reg_size as usize, 326 | "Unexpected register_names size" 327 | ); 328 | assert_eq!( 329 | register_values.len(), 330 | self.expected_reg_size as usize, 331 | "Unexpected register_values size" 332 | ); 333 | assert_eq!( 334 | register_names, self.expected_reg_names, 335 | "Unexpected reg names" 336 | ); 337 | assert_eq!( 338 | register_values.len(), 339 | self.expected_reg_values.len(), 340 | "{}{}", 341 | "The length of expected_reg_values does not match with the length ", 342 | "of register_values" 343 | ); 344 | for (ai, bi) in register_values.iter().zip(self.expected_reg_values.iter()) { 345 | unsafe { assert_eq!(ai.Reg128, bi.Reg128, "Unexpected reg value") }; 346 | } 347 | S_OK 348 | } 349 | fn translate_gva_page( 350 | &mut self, 351 | _gva: WHV_GUEST_VIRTUAL_ADDRESS, 352 | _translate_flags: WHV_TRANSLATE_GVA_FLAGS, 353 | translation_result: &mut WHV_TRANSLATE_GVA_RESULT_CODE, 354 | gpa: &mut WHV_GUEST_PHYSICAL_ADDRESS, 355 | ) -> HRESULT { 356 | self.check_context(); 357 | *gpa = self.returned_gpa; 358 | *translation_result = self.returned_translation_result; 359 | S_OK 360 | } 361 | } 362 | 363 | #[test] 364 | fn test_create_delete_emulator() { 365 | let e = Emulator::::new().unwrap(); 366 | drop(e); 367 | } 368 | 369 | #[test] 370 | fn test_try_io_emulation() { 371 | let mut vp_context: WHV_VP_EXIT_CONTEXT = Default::default(); 372 | let io_instruction_context: WHV_X64_IO_PORT_ACCESS_CONTEXT = Default::default(); 373 | 374 | // Without this WHvEmulatorTryIoEmulation returns E_INVALIDARG 375 | vp_context.InstructionLengthCr8 = 0xF; 376 | 377 | let mut callbacks = TestCallbacks::default(); 378 | 379 | let e = Emulator::::new().unwrap(); 380 | let _return_status = e 381 | .try_io_emulation( 382 | &mut callbacks, 383 | &vp_context, 384 | &io_instruction_context, 385 | ) 386 | .unwrap(); 387 | } 388 | 389 | #[test] 390 | fn test_try_mmio_emulation() { 391 | let vp_context: WHV_VP_EXIT_CONTEXT = Default::default(); 392 | let mmio_instruction_context: WHV_MEMORY_ACCESS_CONTEXT = Default::default(); 393 | 394 | let mut callbacks = TestCallbacks::default(); 395 | 396 | let e = Emulator::::new().unwrap(); 397 | let _return_status = e 398 | .try_mmio_emulation( 399 | &mut callbacks, 400 | &vp_context, 401 | &mmio_instruction_context, 402 | ) 403 | .unwrap(); 404 | } 405 | 406 | #[test] 407 | fn test_io_port_callback() { 408 | const EXPECTED_IO_ACCESS_SIZE: UINT16 = 1111; 409 | let mut callbacks = TestCallbacks { 410 | expected_io_access_size: EXPECTED_IO_ACCESS_SIZE, 411 | ..Default::default() 412 | }; 413 | 414 | let mut e = Emulator::::new().unwrap(); 415 | 416 | let mut callbacks_context = CallbacksContext { 417 | emulator: &mut e, 418 | context: &mut callbacks, 419 | }; 420 | 421 | let mut io_access: WHV_EMULATOR_IO_ACCESS_INFO = Default::default(); 422 | io_access.AccessSize = EXPECTED_IO_ACCESS_SIZE; 423 | 424 | let ret = Emulator::::io_port_cb( 425 | (&mut callbacks_context) as *mut _ as *mut VOID, 426 | &mut io_access, 427 | ); 428 | assert_eq!(ret, S_OK, "Unexpected io_port_cb return value"); 429 | assert_eq!(io_access.AccessSize, !EXPECTED_IO_ACCESS_SIZE, "Unexpected AccessSizee"); 430 | } 431 | 432 | #[test] 433 | fn test_memory_callback() { 434 | const EXPECTED_MEMORY_ACCESS_SIZE: UINT8 = 111; 435 | let mut callbacks = TestCallbacks { 436 | expected_memory_access_size: EXPECTED_MEMORY_ACCESS_SIZE, 437 | ..Default::default() 438 | }; 439 | 440 | let mut e = Emulator::::new().unwrap(); 441 | 442 | let mut callbacks_context = CallbacksContext { 443 | emulator: &mut e, 444 | context: &mut callbacks, 445 | }; 446 | 447 | let mut mem_access: WHV_EMULATOR_MEMORY_ACCESS_INFO = Default::default(); 448 | mem_access.AccessSize = EXPECTED_MEMORY_ACCESS_SIZE; 449 | 450 | let ret = Emulator::::memory_cb( 451 | &mut callbacks_context as *mut _ as *mut VOID, 452 | &mut mem_access, 453 | ); 454 | assert_eq!(ret, S_OK, "Unexpected memory_cb return value"); 455 | assert_eq!(mem_access.AccessSize, !EXPECTED_MEMORY_ACCESS_SIZE, "Unexpected AccessSizee"); 456 | } 457 | 458 | #[test] 459 | fn test_get_vp_registers_callback() { 460 | const NUM_REGS: UINT32 = 1; 461 | const REG_VALUE: UINT64 = 11111111; 462 | let mut reg_names: [WHV_REGISTER_NAME; NUM_REGS as usize] = Default::default(); 463 | let mut returned_reg_values: [WHV_REGISTER_VALUE; NUM_REGS as usize] = Default::default(); 464 | 465 | reg_names[0] = WHV_REGISTER_NAME::WHvX64RegisterRax; 466 | returned_reg_values[0].Reg64 = REG_VALUE; 467 | 468 | let mut callbacks = TestCallbacks { 469 | expected_reg_names: ®_names, 470 | expected_reg_size: NUM_REGS, 471 | returned_reg_values: &returned_reg_values, 472 | ..Default::default() 473 | }; 474 | 475 | let mut e = Emulator::::new().unwrap(); 476 | 477 | let mut callbacks_context = CallbacksContext { 478 | emulator: &mut e, 479 | context: &mut callbacks, 480 | }; 481 | 482 | let mut reg_values: [WHV_REGISTER_VALUE; NUM_REGS as usize] = Default::default(); 483 | 484 | let ret = Emulator::::get_vp_registers_cb( 485 | &mut callbacks_context as *mut _ as *mut VOID, 486 | reg_names.as_ptr(), 487 | NUM_REGS, 488 | reg_values.as_mut_ptr(), 489 | ); 490 | assert_eq!(ret, S_OK, "Unexpected get_vp_registers_cb return value"); 491 | for (ai, bi) in reg_values.iter().zip(returned_reg_values.iter()) { 492 | unsafe { assert_eq!(ai.Reg128, bi.Reg128, "Unexpected reg value") }; 493 | } 494 | } 495 | 496 | #[test] 497 | fn test_set_vp_registers_callback() { 498 | const NUM_REGS: UINT32 = 1; 499 | const REG_VALUE: UINT64 = 11111111; 500 | let mut reg_names: [WHV_REGISTER_NAME; NUM_REGS as usize] = Default::default(); 501 | let mut reg_values: [WHV_REGISTER_VALUE; NUM_REGS as usize] = Default::default(); 502 | 503 | reg_names[0] = WHV_REGISTER_NAME::WHvX64RegisterRax; 504 | reg_values[0].Reg64 = REG_VALUE; 505 | 506 | let mut callbacks = TestCallbacks { 507 | expected_reg_names: ®_names, 508 | expected_reg_size: NUM_REGS, 509 | expected_reg_values: ®_values, 510 | ..Default::default() 511 | }; 512 | 513 | let mut e = Emulator::::new().unwrap(); 514 | 515 | let mut callbacks_context = CallbacksContext { 516 | emulator: &mut e, 517 | context: &mut callbacks, 518 | }; 519 | 520 | let ret = Emulator::::set_vp_registers_cb( 521 | &mut callbacks_context as *mut _ as *mut VOID, 522 | reg_names.as_ptr(), 523 | NUM_REGS, 524 | reg_values.as_ptr(), 525 | ); 526 | assert_eq!(ret, S_OK, "Unexpected set_vp_registers_cb return value"); 527 | } 528 | 529 | #[test] 530 | fn translate_gva_page() { 531 | const RETURNED_GPA: WHV_GUEST_VIRTUAL_ADDRESS = 11111; 532 | const RETURNED_TRANSLATION_RESULT: WHV_TRANSLATE_GVA_RESULT_CODE = 533 | WHV_TRANSLATE_GVA_RESULT_CODE::WHvTranslateGvaResultGpaUnmapped; 534 | let mut callbacks = TestCallbacks { 535 | returned_gpa: RETURNED_GPA, 536 | returned_translation_result: RETURNED_TRANSLATION_RESULT, 537 | ..Default::default() 538 | }; 539 | 540 | let mut e = Emulator::::new().unwrap(); 541 | 542 | let mut callbacks_context = CallbacksContext { 543 | emulator: &mut e, 544 | context: &mut callbacks, 545 | }; 546 | 547 | let gva: WHV_GUEST_VIRTUAL_ADDRESS = 0; 548 | let translate_flags: WHV_TRANSLATE_GVA_FLAGS = 549 | WHV_TRANSLATE_GVA_FLAGS::WHvTranslateGvaFlagNone; 550 | let mut translation_result: WHV_TRANSLATE_GVA_RESULT_CODE = 551 | WHV_TRANSLATE_GVA_RESULT_CODE::WHvTranslateGvaResultSuccess; 552 | let mut gpa: WHV_GUEST_PHYSICAL_ADDRESS = 0; 553 | 554 | let ret = Emulator::::translate_gva_page_cb( 555 | &mut callbacks_context as *mut _ as *mut VOID, 556 | gva, 557 | translate_flags, 558 | &mut translation_result, 559 | &mut gpa, 560 | ); 561 | assert_eq!(ret, S_OK, "Unexpected translate_gva_page return value"); 562 | assert_eq!(gpa, RETURNED_GPA, "Returned GPA does not match"); 563 | assert_eq!( 564 | translation_result, RETURNED_TRANSLATION_RESULT, 565 | "Returned translation result does not match" 566 | ); 567 | } 568 | } 569 | -------------------------------------------------------------------------------- /examples/demo.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Cloudbase Solutions Srl 2 | // Copyright 2018-2019 CrowdStrike, Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | extern crate libc; 17 | extern crate libwhp; 18 | 19 | use libwhp::instruction_emulator::*; 20 | use libwhp::memory::*; 21 | use libwhp::*; 22 | 23 | use std::cell::RefCell; 24 | use std::fs::File; 25 | use std::io::prelude::*; 26 | use std::io::{self, Write}; 27 | use std::path::PathBuf; 28 | 29 | const CPUID_EXT_HYPERVISOR: UINT32 = 1 << 31; 30 | 31 | const PDE64_PRESENT: u64 = 1; 32 | const PDE64_RW: u64 = 1 << 1; 33 | const PDE64_USER: u64 = 1 << 2; 34 | const PDE64_PS: u64 = 1 << 7; 35 | const CR4_PAE: u64 = 1 << 5; 36 | const CR4_OSFXSR: u64 = 1 << 9; 37 | const CR4_OSXMMEXCPT: u64 = 1 << 10; 38 | 39 | const CR0_PE: u64 = 1; 40 | const CR0_MP: u64 = 1 << 1; 41 | const CR0_ET: u64 = 1 << 4; 42 | const CR0_NE: u64 = 1 << 5; 43 | const CR0_WP: u64 = 1 << 16; 44 | const CR0_AM: u64 = 1 << 18; 45 | const CR0_PG: u64 = 1 << 31; 46 | const EFER_LME: u64 = 1 << 8; 47 | const EFER_LMA: u64 = 1 << 10; 48 | 49 | const INT_VECTOR: u32 = 0x35; 50 | 51 | #[allow(non_snake_case)] 52 | #[derive(Debug, Copy, Clone, Default)] 53 | #[repr(C)] 54 | struct CpuInfo { 55 | apic_enabled: bool, 56 | } 57 | 58 | fn main() { 59 | check_hypervisor(); 60 | 61 | let mut p = Partition::new().unwrap(); 62 | 63 | let apic_present = is_apic_present(); 64 | 65 | let mut cpu_info = CpuInfo { 66 | apic_enabled: false, 67 | }; 68 | 69 | setup_partition(&mut p, &mut cpu_info, apic_present); 70 | 71 | let mem_size = 0x300000; 72 | let mut payload_mem = VirtualMemory::new(mem_size).unwrap(); 73 | 74 | let guest_address: WHV_GUEST_PHYSICAL_ADDRESS = 0; 75 | 76 | let _mapping = p 77 | .map_gpa_range( 78 | &payload_mem, 79 | guest_address, 80 | payload_mem.get_size() as UINT64, 81 | WHV_MAP_GPA_RANGE_FLAGS::WHvMapGpaRangeFlagRead 82 | | WHV_MAP_GPA_RANGE_FLAGS::WHvMapGpaRangeFlagWrite 83 | | WHV_MAP_GPA_RANGE_FLAGS::WHvMapGpaRangeFlagExecute, 84 | ) 85 | .unwrap(); 86 | 87 | let mut vp = p.create_virtual_processor(0).unwrap(); 88 | 89 | setup_long_mode(&mut vp, &payload_mem); 90 | read_payload(&mut payload_mem); 91 | 92 | let vp_ref_cell = RefCell::new(vp); 93 | 94 | let mut callbacks = SampleCallbacks { 95 | vp_ref_cell: &vp_ref_cell, 96 | }; 97 | 98 | let mut e = Emulator::::new().unwrap(); 99 | 100 | if cpu_info.apic_enabled { 101 | // Set the APIC base and send an interrupt to the VCPU 102 | set_apic_base(&mut vp_ref_cell.borrow_mut()); 103 | send_ipi(&mut vp_ref_cell.borrow_mut(), INT_VECTOR); 104 | set_delivery_notifications(&mut vp_ref_cell.borrow_mut()); 105 | } 106 | 107 | loop { 108 | let exit_context = vp_ref_cell.borrow_mut().run().unwrap(); 109 | 110 | match exit_context.ExitReason { 111 | WHV_RUN_VP_EXIT_REASON::WHvRunVpExitReasonX64Halt => { 112 | println!("All done!"); 113 | break; 114 | } 115 | WHV_RUN_VP_EXIT_REASON::WHvRunVpExitReasonException => { 116 | break; 117 | } 118 | WHV_RUN_VP_EXIT_REASON::WHvRunVpExitReasonMemoryAccess => { 119 | handle_mmio_exit(&mut e, &mut callbacks, &exit_context) 120 | } 121 | WHV_RUN_VP_EXIT_REASON::WHvRunVpExitReasonX64IoPortAccess => { 122 | handle_io_port_exit(&mut e, &mut callbacks, &exit_context) 123 | } 124 | WHV_RUN_VP_EXIT_REASON::WHvRunVpExitReasonX64Cpuid => { 125 | handle_cpuid_exit(&mut vp_ref_cell.borrow_mut(), &exit_context) 126 | } 127 | WHV_RUN_VP_EXIT_REASON::WHvRunVpExitReasonX64MsrAccess => { 128 | handle_msr_exit(&mut vp_ref_cell.borrow_mut(), &exit_context) 129 | } 130 | WHV_RUN_VP_EXIT_REASON::WHvRunVpExitReasonX64ApicEoi => { 131 | println!("ApicEoi"); 132 | } 133 | WHV_RUN_VP_EXIT_REASON::WHvRunVpExitReasonX64InterruptWindow => { 134 | println!("Interrupt window"); 135 | } 136 | _ => panic!("Unexpected exit type: {:?}", exit_context.ExitReason), 137 | }; 138 | 139 | // With the APIC enabled, the hlt instruction will not completely halt 140 | // the processor; it'll just halt it until another interrupt is 141 | // received, so we don't receive the VMexit that we used to use to end 142 | // VCPU execution. Since WHV will not let us disable the APIC in the usual 143 | // means (eg, via the global enable flag of the APIC_BASE register, 144 | // etc), teriminate the VCPU execution loop when both interrupts we're 145 | // expecting have been received. Plus we get to exercise the new 146 | // counter APIs. 147 | if all_interrupts_received(&vp_ref_cell.borrow()) { 148 | println!("All interrupts received. All done!"); 149 | break; 150 | } 151 | } 152 | } 153 | 154 | /* 155 | * Terminate VCPU execution when two interrupts have been received by the guest: 156 | * one from the host, and one from the guest 157 | */ 158 | fn all_interrupts_received(vp: &VirtualProcessor) -> bool { 159 | let counters: WHV_PROCESSOR_COUNTERS = vp 160 | .get_processor_counters(WHV_PROCESSOR_COUNTER_SET::WHvProcessorCounterSetApic) 161 | .unwrap(); 162 | let apic_counters = unsafe { counters.ApicCounters }; 163 | 164 | if apic_counters.EoiAccessCount == 2 { 165 | true 166 | } else { 167 | false 168 | } 169 | } 170 | 171 | fn set_apic_base(vp: &mut VirtualProcessor) { 172 | // Page table translations for this guest only cover the first 1GB of memory, 173 | // and the default APIC base falls above that. Set the APIC base to 174 | // something lower, within our range of virtual memory 175 | 176 | // Get the default APIC base register value to start 177 | const NUM_REGS: usize = 1; 178 | let mut reg_names: [WHV_REGISTER_NAME; NUM_REGS] = Default::default(); 179 | let mut reg_values: [WHV_REGISTER_VALUE; NUM_REGS] = Default::default(); 180 | 181 | reg_names[0] = WHV_REGISTER_NAME::WHvX64RegisterApicBase; 182 | 183 | // Get the registers as a baseline 184 | vp.get_registers(®_names, &mut reg_values).unwrap(); 185 | let mut flags = unsafe { reg_values[0].Reg64 }; 186 | 187 | // Mask off the bottom 12 bits, which are used to store flags 188 | flags = flags & 0xfff; 189 | 190 | // Set the APIC base to something lower within our translatable address 191 | // space 192 | let new_apic_base = 0x0fee_0000; 193 | reg_names[0] = WHV_REGISTER_NAME::WHvX64RegisterApicBase; 194 | reg_values[0].Reg64 = new_apic_base | flags; 195 | vp.set_registers(®_names, ®_values).unwrap(); 196 | } 197 | 198 | fn send_msi(vp: &mut VirtualProcessor, message: &WHV_MSI_ENTRY) { 199 | let addr: UINT32 = unsafe { message.anon_struct.Address }; 200 | let data: UINT32 = unsafe { message.anon_struct.Data }; 201 | 202 | let dest = (addr & MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT; 203 | let vector = (data & MSI_DATA_VECTOR_MASK) >> MSI_DATA_VECTOR_SHIFT; 204 | let dest_mode = (addr >> MSI_ADDR_DEST_MODE_SHIFT) & 0x1; 205 | let trigger_mode = (data >> MSI_DATA_TRIGGER_SHIFT) & 0x1; 206 | let delivery = (data >> MSI_DATA_DELIVERY_MODE_SHIFT) & 0x7; 207 | 208 | let mut interrupt: WHV_INTERRUPT_CONTROL = Default::default(); 209 | 210 | interrupt.set_InterruptType(delivery as UINT64); 211 | 212 | if dest_mode == 0 { 213 | interrupt.set_DestinationMode( 214 | WHV_INTERRUPT_DESTINATION_MODE::WHvX64InterruptDestinationModePhysical as UINT64, 215 | ); 216 | } else { 217 | interrupt.set_DestinationMode( 218 | WHV_INTERRUPT_DESTINATION_MODE::WHvX64InterruptDestinationModeLogical as UINT64, 219 | ); 220 | } 221 | 222 | interrupt.set_TriggerMode(trigger_mode as UINT64); 223 | 224 | interrupt.Destination = dest; 225 | interrupt.Vector = vector; 226 | 227 | vp.request_interrupt(&mut interrupt).unwrap(); 228 | } 229 | 230 | fn send_ipi(vp: &mut VirtualProcessor, vector: u32) { 231 | println!("Send IPI from the host to the guest"); 232 | 233 | let mut message: WHV_MSI_ENTRY = Default::default(); 234 | 235 | // - Trigger mode is 'Edge' 236 | // - Interrupt type is 'Fixed' 237 | // - Destination mode is 'Physical' 238 | // - Destination is 0. Since Destination Mode is Physical, bits 56-59 239 | // contain the APIC ID of the target processor (APIC ID = 0) 240 | // Level = 1 and Destination Shorthand = 1, but the underlying API will 241 | // actually ignore this. 242 | message.anon_struct.Data = (0x00044000 | vector) as UINT32; 243 | message.anon_struct.Address = 0; 244 | 245 | send_msi(vp, &message); 246 | } 247 | 248 | fn set_delivery_notifications(vp: &mut VirtualProcessor) { 249 | const NUM_REGS: usize = 1; 250 | let mut reg_values: [WHV_REGISTER_VALUE; NUM_REGS] = Default::default(); 251 | let mut reg_names: [WHV_REGISTER_NAME; NUM_REGS] = Default::default(); 252 | 253 | let mut notifications: WHV_X64_DELIVERABILITY_NOTIFICATIONS_REGISTER = Default::default(); 254 | notifications.set_InterruptNotification(1); 255 | reg_values[0].DeliverabilityNotifications = notifications; 256 | reg_names[0] = WHV_REGISTER_NAME::WHvX64RegisterDeliverabilityNotifications; 257 | vp.set_registers(®_names, ®_values).unwrap(); 258 | } 259 | 260 | fn handle_msr_exit(vp: &mut VirtualProcessor, exit_context: &WHV_RUN_VP_EXIT_CONTEXT) { 261 | let msr_access = unsafe { exit_context.anon_union.MsrAccess }; 262 | 263 | const NUM_REGS: UINT32 = 3; 264 | let mut reg_names: [WHV_REGISTER_NAME; NUM_REGS as usize] = Default::default(); 265 | let mut reg_values: [WHV_REGISTER_VALUE; NUM_REGS as usize] = Default::default(); 266 | 267 | reg_names[0] = WHV_REGISTER_NAME::WHvX64RegisterRip; 268 | reg_names[1] = WHV_REGISTER_NAME::WHvX64RegisterRax; 269 | reg_names[2] = WHV_REGISTER_NAME::WHvX64RegisterRdx; 270 | 271 | reg_values[0].Reg64 = 272 | exit_context.VpContext.Rip + exit_context.VpContext.InstructionLength() as u64; 273 | 274 | match msr_access.MsrNumber { 275 | 1 => { 276 | if msr_access.AccessInfo.IsWrite() == 1 { 277 | println!( 278 | "MSR write. Number: 0x{:x}, Rax: 0x{:x}, Rdx: 0x{:x}", 279 | msr_access.MsrNumber, msr_access.Rax, msr_access.Rdx 280 | ); 281 | } else { 282 | let rax = 0x2000; 283 | let rdx = 0x2001; 284 | reg_values[1].Reg64 = rax; 285 | reg_values[2].Reg64 = rdx; 286 | println!( 287 | "MSR read. Number: 0x{:x}, Rax: 0x{:x}, Rdx: 0x{:x}", 288 | msr_access.MsrNumber, rax, rdx 289 | ); 290 | } 291 | } 292 | _ => { 293 | println!("Unknown MSR number: {:#x}", msr_access.MsrNumber); 294 | } 295 | } 296 | 297 | let mut num_regs_set = NUM_REGS as usize; 298 | if msr_access.AccessInfo.IsWrite() == 1 { 299 | num_regs_set = 1; 300 | } 301 | 302 | vp.set_registers(®_names[0..num_regs_set], ®_values[0..num_regs_set]) 303 | .unwrap(); 304 | } 305 | 306 | fn handle_cpuid_exit(vp: &mut VirtualProcessor, exit_context: &WHV_RUN_VP_EXIT_CONTEXT) { 307 | let cpuid_access = unsafe { exit_context.anon_union.CpuidAccess }; 308 | println!("Got CPUID leaf: {}", cpuid_access.Rax); 309 | 310 | const NUM_REGS: UINT32 = 5; 311 | let mut reg_names: [WHV_REGISTER_NAME; NUM_REGS as usize] = Default::default(); 312 | let mut reg_values: [WHV_REGISTER_VALUE; NUM_REGS as usize] = Default::default(); 313 | 314 | reg_names[0] = WHV_REGISTER_NAME::WHvX64RegisterRip; 315 | reg_names[1] = WHV_REGISTER_NAME::WHvX64RegisterRax; 316 | reg_names[2] = WHV_REGISTER_NAME::WHvX64RegisterRbx; 317 | reg_names[3] = WHV_REGISTER_NAME::WHvX64RegisterRcx; 318 | reg_names[4] = WHV_REGISTER_NAME::WHvX64RegisterRdx; 319 | 320 | reg_values[0].Reg64 = 321 | exit_context.VpContext.Rip + exit_context.VpContext.InstructionLength() as u64; 322 | reg_values[1].Reg64 = cpuid_access.DefaultResultRax; 323 | reg_values[2].Reg64 = cpuid_access.DefaultResultRbx; 324 | reg_values[3].Reg64 = cpuid_access.DefaultResultRcx; 325 | reg_values[4].Reg64 = cpuid_access.DefaultResultRdx; 326 | 327 | match cpuid_access.Rax { 328 | 1 => { 329 | reg_values[3].Reg64 = CPUID_EXT_HYPERVISOR as UINT64; 330 | } 331 | _ => { 332 | println!("Unknown CPUID leaf: {}", cpuid_access.Rax); 333 | } 334 | } 335 | 336 | vp.set_registers(®_names, ®_values).unwrap(); 337 | } 338 | 339 | fn handle_mmio_exit( 340 | e: &mut Emulator, 341 | context: &mut T, 342 | exit_context: &WHV_RUN_VP_EXIT_CONTEXT, 343 | ) { 344 | let mem_access_ctx = unsafe { &exit_context.anon_union.MemoryAccess }; 345 | let _status = e 346 | .try_mmio_emulation( 347 | context, 348 | &exit_context.VpContext, 349 | mem_access_ctx, 350 | ) 351 | .unwrap(); 352 | } 353 | 354 | fn handle_io_port_exit( 355 | e: &mut Emulator, 356 | context: &mut T, 357 | exit_context: &WHV_RUN_VP_EXIT_CONTEXT, 358 | ) { 359 | let io_port_access_ctx = unsafe { &exit_context.anon_union.IoPortAccess }; 360 | let _status = e 361 | .try_io_emulation( 362 | context, 363 | &exit_context.VpContext, 364 | io_port_access_ctx, 365 | ) 366 | .unwrap(); 367 | } 368 | 369 | fn setup_partition(p: &mut Partition, cpu_info: &mut CpuInfo, apic_present: bool) { 370 | let mut property: WHV_PARTITION_PROPERTY = Default::default(); 371 | property.ProcessorCount = 1; 372 | p.set_property( 373 | WHV_PARTITION_PROPERTY_CODE::WHvPartitionPropertyCodeProcessorCount, 374 | &property, 375 | ) 376 | .unwrap(); 377 | 378 | property = Default::default(); 379 | unsafe { 380 | property.ExtendedVmExits.set_X64CpuidExit(1); 381 | property.ExtendedVmExits.set_X64MsrExit(1); 382 | property.ExtendedVmExits.set_ExceptionExit(1); 383 | } 384 | 385 | p.set_property( 386 | WHV_PARTITION_PROPERTY_CODE::WHvPartitionPropertyCodeExtendedVmExits, 387 | &property, 388 | ) 389 | .unwrap(); 390 | 391 | let cpuids: [UINT32; 1] = [1]; 392 | p.set_property_cpuid_exits(&cpuids).unwrap(); 393 | 394 | let mut cpuid_results: [WHV_X64_CPUID_RESULT; 1] = Default::default(); 395 | 396 | cpuid_results[0].Function = 0x40000000; 397 | let mut id_reg_values: [UINT32; 3] = [0; 3]; 398 | let id = "libwhp\0"; 399 | unsafe { 400 | std::ptr::copy_nonoverlapping(id.as_ptr(), id_reg_values.as_mut_ptr() as *mut u8, id.len()); 401 | } 402 | cpuid_results[0].Ebx = id_reg_values[0]; 403 | cpuid_results[0].Ecx = id_reg_values[1]; 404 | cpuid_results[0].Edx = id_reg_values[2]; 405 | 406 | p.set_property_cpuid_results(&cpuid_results).unwrap(); 407 | 408 | if apic_present != false { 409 | enable_apic(p, cpu_info); 410 | } 411 | 412 | p.setup().unwrap(); 413 | } 414 | 415 | fn is_apic_present() -> bool { 416 | let capability: WHV_CAPABILITY = 417 | get_capability(WHV_CAPABILITY_CODE::WHvCapabilityCodeFeatures).unwrap(); 418 | let features: WHV_CAPABILITY_FEATURES = unsafe { capability.Features }; 419 | 420 | if features.LocalApicEmulation() != 0 { 421 | true 422 | } else { 423 | false 424 | } 425 | } 426 | 427 | fn enable_apic(p: &mut Partition, cpu_info: &mut CpuInfo) { 428 | let mut property: WHV_PARTITION_PROPERTY = Default::default(); 429 | property.LocalApicEmulationMode = 430 | WHV_X64_LOCAL_APIC_EMULATION_MODE::WHvX64LocalApicEmulationModeXApic; 431 | 432 | p.set_property( 433 | WHV_PARTITION_PROPERTY_CODE::WHvPartitionPropertyCodeLocalApicEmulationMode, 434 | &property, 435 | ) 436 | .unwrap(); 437 | 438 | cpu_info.apic_enabled = true; 439 | } 440 | 441 | fn initialize_address_space(payload_mem: &VirtualMemory) -> u64 { 442 | let mem_addr = payload_mem.as_ptr() as u64; 443 | 444 | let pml4_addr: u64 = 0x9000; 445 | let pdpt_addr: u64 = 0xa000; 446 | let pd_addr: u64 = 0xb000; 447 | let pml4: u64 = mem_addr + pml4_addr; 448 | let pdpt: u64 = mem_addr + pdpt_addr; 449 | let pd: u64 = mem_addr + pd_addr; 450 | 451 | unsafe { 452 | *(pml4 as *mut u64) = PDE64_PRESENT | PDE64_RW | PDE64_USER | pdpt_addr; 453 | *(pdpt as *mut u64) = PDE64_PRESENT | PDE64_RW | PDE64_USER | pd_addr; 454 | 455 | for i in 0..512 { 456 | *((pd + i * 8) as *mut u64) = 457 | (i << 21) + (PDE64_PRESENT | PDE64_RW | PDE64_USER | PDE64_PS); 458 | } 459 | } 460 | 461 | // Return the PML4 guest physical address so the caller can use it to set CR3 462 | pml4_addr 463 | } 464 | 465 | fn setup_long_mode(vp: &mut VirtualProcessor, payload_mem: &VirtualMemory) { 466 | let pml4_addr = initialize_address_space(payload_mem); 467 | 468 | const NUM_REGS: UINT32 = 13; 469 | let mut reg_names: [WHV_REGISTER_NAME; NUM_REGS as usize] = Default::default(); 470 | let mut reg_values: [WHV_REGISTER_VALUE; NUM_REGS as usize] = Default::default(); 471 | 472 | // Setup paging 473 | reg_names[0] = WHV_REGISTER_NAME::WHvX64RegisterCr3; 474 | reg_values[0].Reg64 = pml4_addr; 475 | reg_names[1] = WHV_REGISTER_NAME::WHvX64RegisterCr4; 476 | reg_values[1].Reg64 = CR4_PAE | CR4_OSFXSR | CR4_OSXMMEXCPT; 477 | 478 | reg_names[2] = WHV_REGISTER_NAME::WHvX64RegisterCr0; 479 | reg_values[2].Reg64 = CR0_PE | CR0_MP | CR0_ET | CR0_NE | CR0_WP | CR0_AM | CR0_PG; 480 | reg_names[3] = WHV_REGISTER_NAME::WHvX64RegisterEfer; 481 | reg_values[3].Reg64 = EFER_LME | EFER_LMA; 482 | 483 | reg_names[4] = WHV_REGISTER_NAME::WHvX64RegisterCs; 484 | unsafe { 485 | let segment = &mut reg_values[4].Segment; 486 | segment.Base = 0; 487 | segment.Limit = 0xffffffff; 488 | segment.Selector = 1 << 3; 489 | segment.set_SegmentType(11); 490 | segment.set_NonSystemSegment(1); 491 | segment.set_Present(1); 492 | segment.set_Long(1); 493 | segment.set_Granularity(1); 494 | } 495 | 496 | reg_names[5] = WHV_REGISTER_NAME::WHvX64RegisterDs; 497 | unsafe { 498 | let segment = &mut reg_values[5].Segment; 499 | segment.Base = 0; 500 | segment.Limit = 0xffffffff; 501 | segment.Selector = 2 << 3; 502 | segment.set_SegmentType(3); 503 | segment.set_NonSystemSegment(1); 504 | segment.set_Present(1); 505 | segment.set_Long(1); 506 | segment.set_Granularity(1); 507 | } 508 | 509 | reg_names[6] = WHV_REGISTER_NAME::WHvX64RegisterEs; 510 | reg_values[6] = reg_values[5]; 511 | 512 | reg_names[7] = WHV_REGISTER_NAME::WHvX64RegisterFs; 513 | reg_values[7] = reg_values[5]; 514 | 515 | reg_names[8] = WHV_REGISTER_NAME::WHvX64RegisterGs; 516 | reg_values[8] = reg_values[5]; 517 | 518 | reg_names[9] = WHV_REGISTER_NAME::WHvX64RegisterSs; 519 | reg_values[9] = reg_values[5]; 520 | 521 | // Start with the Interrupt Flag off; guest will enable it when ready 522 | reg_names[10] = WHV_REGISTER_NAME::WHvX64RegisterRflags; 523 | reg_values[10].Reg64 = 0x002; 524 | 525 | reg_names[11] = WHV_REGISTER_NAME::WHvX64RegisterRip; 526 | reg_values[11].Reg64 = 0; 527 | 528 | // Create stack with stack base at high end of mapped payload 529 | reg_names[12] = WHV_REGISTER_NAME::WHvX64RegisterRsp; 530 | reg_values[12].Reg64 = payload_mem.get_size() as UINT64; 531 | 532 | vp.set_registers(®_names, ®_values).unwrap(); 533 | } 534 | 535 | fn read_payload(mem_addr: &mut VirtualMemory) { 536 | let mut p = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 537 | p.push("examples"); 538 | p.push("payload"); 539 | p.push("payload.img"); 540 | 541 | let mut f = File::open(&p).expect(&format!( 542 | "Cannot find \"{}\". Run \"make\" in the same folder to build it", 543 | &p.to_str().unwrap() 544 | )); 545 | f.read(mem_addr.as_slice_mut()).unwrap(); 546 | } 547 | 548 | fn check_hypervisor() { 549 | let capability = 550 | get_capability(WHV_CAPABILITY_CODE::WHvCapabilityCodeHypervisorPresent).unwrap(); 551 | if unsafe { capability.HypervisorPresent } == FALSE { 552 | panic!("Hypervisor not present"); 553 | } 554 | } 555 | 556 | struct SampleCallbacks<'a> { 557 | vp_ref_cell: &'a RefCell, 558 | } 559 | 560 | impl<'a> EmulatorCallbacks for SampleCallbacks<'a> { 561 | fn io_port( 562 | &mut self, 563 | io_access: &mut WHV_EMULATOR_IO_ACCESS_INFO, 564 | ) -> HRESULT { 565 | if io_access.Direction == 1 { 566 | // Our payload writes to port 42 567 | if io_access.Port == 42 { 568 | let data = unsafe { 569 | std::slice::from_raw_parts( 570 | &io_access.Data as *const _ as *const u8, 571 | io_access.AccessSize as usize, 572 | ) 573 | }; 574 | io::stdout().write(data).unwrap(); 575 | } else { 576 | println!("Unsupported IO port"); 577 | } 578 | } 579 | else { 580 | // Our payload reads from port 43. Set a value in the Data buffer 581 | // to simulate an IO read, that the payload will "read" later 582 | if io_access.Port == 43 { 583 | let data = unsafe { 584 | std::slice::from_raw_parts_mut( 585 | &mut io_access.Data as *mut _ as *mut u8, 586 | io_access.AccessSize as usize, 587 | ) 588 | }; 589 | data[0] = 99; 590 | } 591 | } 592 | S_OK 593 | } 594 | 595 | fn memory( 596 | &mut self, 597 | memory_access: &mut WHV_EMULATOR_MEMORY_ACCESS_INFO, 598 | ) -> HRESULT { 599 | let addr = memory_access.GpaAddress; 600 | match memory_access.AccessSize { 601 | 8 => match memory_access.Direction { 602 | 0 => { 603 | let data = &memory_access.Data as *const _ as *mut u64; 604 | unsafe { 605 | *data = 0x1000; 606 | println!("MMIO read: 0x{:016x} @0x{:x}", *data, addr); 607 | } 608 | } 609 | _ => { 610 | let value = unsafe { *(&memory_access.Data as *const _ as *const u64) }; 611 | println!("MMIO write: 0x{:016x} @0x{:x}", value, addr); 612 | } 613 | }, 614 | 4 => match memory_access.Direction { 615 | 0 => { 616 | let data = &memory_access.Data as *const _ as *mut u32; 617 | unsafe { 618 | *data = 0x1000; 619 | println!("MMIO read: 0x{:08x} @0x{:x}", *data, addr); 620 | } 621 | } 622 | _ => { 623 | let value = unsafe { *(&memory_access.Data as *const _ as *const u32) }; 624 | println!("MMIO write: 0x{:08x} @0x{:x}", value, addr); 625 | } 626 | }, 627 | 2 => match memory_access.Direction { 628 | 0 => { 629 | let data = &memory_access.Data as *const _ as *mut u32; 630 | unsafe { 631 | *data = 0x22bb; 632 | println!("MMIO read: 0x{:04x} @0x{:x}", *data, addr); 633 | } 634 | } 635 | _ => { 636 | let value = unsafe { *(&memory_access.Data as *const _ as *const u32) }; 637 | println!("MMIO write: 0x{:04x} @0x{:x}", value, addr); 638 | } 639 | }, 640 | 1 => match memory_access.Direction { 641 | 0 => { 642 | let data = &memory_access.Data as *const _ as *mut u32; 643 | unsafe { 644 | *data = 0x58; 645 | println!("MMIO read: 0x{:02x} @0x{:x}", *data, addr); 646 | } 647 | } 648 | _ => { 649 | let value = unsafe { *(&memory_access.Data as *const _ as *const u32) }; 650 | println!("MMIO write: 0x{:02x} @0x{:x}", value, addr); 651 | } 652 | }, 653 | _ => println!("Unsupported MMIO access size: {}", memory_access.AccessSize), 654 | } 655 | 656 | S_OK 657 | } 658 | 659 | fn get_virtual_processor_registers( 660 | &mut self, 661 | register_names: &[WHV_REGISTER_NAME], 662 | register_values: &mut [WHV_REGISTER_VALUE], 663 | ) -> HRESULT { 664 | self.vp_ref_cell 665 | .borrow() 666 | .get_registers(register_names, register_values) 667 | .unwrap(); 668 | S_OK 669 | } 670 | 671 | fn set_virtual_processor_registers( 672 | &mut self, 673 | register_names: &[WHV_REGISTER_NAME], 674 | register_values: &[WHV_REGISTER_VALUE], 675 | ) -> HRESULT { 676 | self.vp_ref_cell 677 | .borrow_mut() 678 | .set_registers(register_names, register_values) 679 | .unwrap(); 680 | S_OK 681 | } 682 | 683 | fn translate_gva_page( 684 | &mut self, 685 | gva: WHV_GUEST_VIRTUAL_ADDRESS, 686 | translate_flags: WHV_TRANSLATE_GVA_FLAGS, 687 | translation_result: &mut WHV_TRANSLATE_GVA_RESULT_CODE, 688 | gpa: &mut WHV_GUEST_PHYSICAL_ADDRESS, 689 | ) -> HRESULT { 690 | let (translation_result1, gpa1) = self 691 | .vp_ref_cell 692 | .borrow() 693 | .translate_gva(gva, translate_flags) 694 | .unwrap(); 695 | *translation_result = translation_result1.ResultCode; 696 | *gpa = gpa1; 697 | S_OK 698 | } 699 | } 700 | -------------------------------------------------------------------------------- /src/debug.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 CrowdStrike, Inc. 2 | // Copyright 2019 Cloudbase Solutions 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. 17 | // Use of this source code is governed by a BSD-style license that can be 18 | // found in the THIRD-PARTY file. 19 | 20 | use std::fmt; 21 | 22 | use platform::*; 23 | pub use win_hv_platform_defs::*; 24 | pub use win_hv_platform_defs_internal::*; 25 | 26 | impl fmt::Debug for WHV_RUN_VP_EXIT_CONTEXT { 27 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 28 | writeln!(fmt, "ExitReason: {:?}", self.ExitReason)?; 29 | writeln!(fmt, "Reserved: {}", self.Reserved)?; 30 | writeln!(fmt, "Run context: {:?}", self.VpContext)?; 31 | writeln!(fmt, "Execution state: {}", self.VpContext.ExecutionState)?; 32 | 33 | unsafe { 34 | match self.ExitReason { 35 | WHV_RUN_VP_EXIT_REASON::WHvRunVpExitReasonMemoryAccess => { 36 | writeln!(fmt, "{:?}", self.anon_union.MemoryAccess)?; 37 | } 38 | WHV_RUN_VP_EXIT_REASON::WHvRunVpExitReasonX64IoPortAccess => { 39 | writeln!(fmt, "{:?}", self.anon_union.IoPortAccess)?; 40 | } 41 | WHV_RUN_VP_EXIT_REASON::WHvRunVpExitReasonX64MsrAccess => { 42 | writeln!(fmt, "{:?}", self.anon_union.MsrAccess)?; 43 | } 44 | WHV_RUN_VP_EXIT_REASON::WHvRunVpExitReasonX64Cpuid => { 45 | writeln!(fmt, "{:?}", self.anon_union.CpuidAccess)?; 46 | } 47 | WHV_RUN_VP_EXIT_REASON::WHvRunVpExitReasonException => { 48 | writeln!(fmt, "{:?}", self.anon_union.VpException)?; 49 | } 50 | WHV_RUN_VP_EXIT_REASON::WHvRunVpExitReasonX64InterruptWindow => { 51 | writeln!(fmt, "{:?}", self.anon_union.InterruptWindow)?; 52 | } 53 | WHV_RUN_VP_EXIT_REASON::WHvRunVpExitReasonUnsupportedFeature => { 54 | writeln!(fmt, "{:?}", self.anon_union.UnsupportedFeature)?; 55 | } 56 | WHV_RUN_VP_EXIT_REASON::WHvRunVpExitReasonCanceled => { 57 | writeln!(fmt, "{:?}", self.anon_union.CancelReason)?; 58 | } 59 | WHV_RUN_VP_EXIT_REASON::WHvRunVpExitReasonX64ApicEoi => { 60 | writeln!(fmt, "{:?}", self.anon_union.ApicEoi)?; 61 | } 62 | _ => { 63 | writeln!(fmt, "unexected exit reason!")?; 64 | } 65 | } 66 | } 67 | 68 | writeln!(fmt, "") 69 | } 70 | } 71 | 72 | fn dump_instruction_bytes(fmt: &mut fmt::Formatter, bytes: &[u8]) -> fmt::Result { 73 | for idx in 0..bytes.len() { 74 | if (idx > 0) && (idx % 16 == 0) { 75 | writeln!(fmt, "")?; 76 | } 77 | write!(fmt, "{:02x} ", bytes[idx])?; 78 | } 79 | writeln!(fmt, "") 80 | } 81 | 82 | fn dump_access_info(fmt: &mut fmt::Formatter, access_info: &WHV_MEMORY_ACCESS_INFO) -> fmt::Result { 83 | match access_info.AccessType() { 84 | 0 => write!(fmt, "Read")?, 85 | 1 => write!(fmt, "Write")?, 86 | 2 => write!(fmt, "Execute")?, 87 | _ => write!(fmt, "Unknown")? 88 | }; 89 | 90 | if access_info.GpaUnmapped() == 1 { 91 | write!(fmt, " GpaUnmapped")?; 92 | } 93 | 94 | if access_info.GvaValid() == 1 { 95 | write!(fmt, " GpaValid")?; 96 | } 97 | 98 | writeln!(fmt, "") 99 | } 100 | 101 | impl fmt::Debug for WHV_MEMORY_ACCESS_CONTEXT { 102 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 103 | writeln!(fmt, "MemoryAccess:")?; 104 | writeln!(fmt, " InstructionByteCount: {}", self.InstructionByteCount)?; 105 | write!(fmt, " InstructionBytes: ")?; 106 | dump_instruction_bytes(fmt, &self.InstructionBytes)?; 107 | write!(fmt, " AccessInfo: 0x{:x} - ", self.AccessInfo.AsUINT32)?; 108 | dump_access_info(fmt, &self.AccessInfo)?; 109 | writeln!(fmt, " Gpa: 0x{:x}", self.Gpa)?; 110 | writeln!(fmt, " Gva: 0x{:x}", self.Gva) 111 | } 112 | } 113 | 114 | impl fmt::Debug for WHV_X64_IO_PORT_ACCESS_CONTEXT { 115 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 116 | writeln!(fmt, "IoPortAccess:")?; 117 | 118 | // Context of the virtual processor 119 | writeln!(fmt, 120 | " InstructionByteCount: 0x{:x}", 121 | self.InstructionByteCount 122 | )?; 123 | writeln!(fmt, " Reserved: {:?}", self.Reserved)?; 124 | write!(fmt, " InstructionBytes: ")?; 125 | dump_instruction_bytes(fmt, &self.InstructionBytes)?; 126 | 127 | // I/O port access info 128 | writeln!(fmt, " AccessInfo: {:?}", self.AccessInfo)?; 129 | writeln!(fmt, " PortNumber: 0x{:x}", self.PortNumber)?; 130 | writeln!(fmt, " Reserved2: {:?}", self.Reserved2)?; 131 | writeln!(fmt, 132 | " Rax: 0x{:016x} Rcx: 0x{:016x} Rsi: 0x{:016x} Rdi: 0x{:016x}", 133 | self.Rax, self.Rcx, self.Rsi, self.Rdi 134 | )?; 135 | writeln!(fmt, " Ds: {:?}", self.Ds)?; 136 | writeln!(fmt, " Es: {:?}", self.Es) 137 | } 138 | } 139 | 140 | impl fmt::Debug for WHV_X64_MSR_ACCESS_CONTEXT { 141 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 142 | writeln!(fmt, "MsrAccess:")?; 143 | writeln!(fmt, 144 | " MsrNumber: 0x{:x} AccessInfo: {}", 145 | self.MsrNumber, self.AccessInfo.AsUINT32 146 | )?; 147 | writeln!(fmt, " Rax: 0x{:016x} Rdx: 0x{:016x}", self.Rax, self.Rdx) 148 | } 149 | } 150 | 151 | impl fmt::Debug for WHV_X64_CPUID_ACCESS_CONTEXT { 152 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 153 | writeln!(fmt, "CpuidAccess:")?; 154 | writeln!(fmt, 155 | " Rax: {:016?} Rbx: {:016?} Rcx: {:016?} Rdx: {:016?}", 156 | self.Rax, self.Rbx, self.Rcx, self.Rdx 157 | )?; 158 | writeln!(fmt, 159 | " DefaultResult Rax: {:016?} Rbx: {:016?} Rcx: {:016?} Rdx: {:016?}", 160 | self.DefaultResultRax, 161 | self.DefaultResultRbx, 162 | self.DefaultResultRcx, 163 | self.DefaultResultRdx 164 | ) 165 | } 166 | } 167 | 168 | impl fmt::Debug for VirtualProcessor { 169 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 170 | writeln!(fmt, "VirtualProcessor:")?; 171 | writeln!(fmt, "Index: 0x{:x}", self.index())?; 172 | 173 | dump_gp_regs(fmt, self)?; 174 | dump_segment_regs(fmt, self)?; 175 | dump_table_regs(fmt, self)?; 176 | dump_control_regs(fmt, self)?; 177 | dump_debug_regs(fmt, self)?; 178 | dump_fp_regs(fmt, self)?; 179 | dump_msr_regs(fmt, self)?; 180 | dump_mtr_regs(fmt, self)?; 181 | dump_mtrfix_regs(fmt, self)?; 182 | dump_interrupt_regs(fmt, self)?; 183 | 184 | dump_cpu_counters(fmt, self)?; 185 | Ok(()) 186 | } 187 | } 188 | 189 | fn dump_gp_regs(fmt: &mut fmt::Formatter, vp: &VirtualProcessor) -> fmt::Result { 190 | const NUM_REGS: usize = 18; 191 | let reg_names: [WHV_REGISTER_NAME; NUM_REGS] = [ 192 | WHV_REGISTER_NAME::WHvX64RegisterRax, 193 | WHV_REGISTER_NAME::WHvX64RegisterRcx, 194 | WHV_REGISTER_NAME::WHvX64RegisterRdx, 195 | WHV_REGISTER_NAME::WHvX64RegisterRbx, 196 | WHV_REGISTER_NAME::WHvX64RegisterRsp, 197 | WHV_REGISTER_NAME::WHvX64RegisterRbp, 198 | WHV_REGISTER_NAME::WHvX64RegisterRsi, 199 | WHV_REGISTER_NAME::WHvX64RegisterRdi, 200 | WHV_REGISTER_NAME::WHvX64RegisterR8, 201 | WHV_REGISTER_NAME::WHvX64RegisterR9, 202 | WHV_REGISTER_NAME::WHvX64RegisterR10, 203 | WHV_REGISTER_NAME::WHvX64RegisterR11, 204 | WHV_REGISTER_NAME::WHvX64RegisterR12, 205 | WHV_REGISTER_NAME::WHvX64RegisterR13, 206 | WHV_REGISTER_NAME::WHvX64RegisterR14, 207 | WHV_REGISTER_NAME::WHvX64RegisterR15, 208 | WHV_REGISTER_NAME::WHvX64RegisterRip, 209 | WHV_REGISTER_NAME::WHvX64RegisterRflags, 210 | ]; 211 | let mut reg_values: [WHV_REGISTER_VALUE; NUM_REGS] = Default::default(); 212 | 213 | // Get the registers as a baseline 214 | vp.get_registers(®_names, &mut reg_values).or(Err(fmt::Error))?; 215 | 216 | writeln!(fmt, "Regs:")?; 217 | unsafe { 218 | writeln!(fmt, 219 | " Rax: {:016x} Rcx: {:016x} Rdx: {:016x} Rbx: {:016x}\n\ 220 | \x20 Rsp: {:016x} Rbp: {:016x} Rsi: {:016x} Rdi: {:016x}\n\ 221 | \x20 R8: {:016x} R9: {:016x} R10: {:016x} R11: {:016x}\n\ 222 | \x20 R12: {:016x} R13: {:016x} R14: {:016x} R15: {:016x}\n\ 223 | \x20 Rip: {:016x} Rflags: {:016x}", 224 | reg_values[0].Reg64, 225 | reg_values[1].Reg64, 226 | reg_values[2].Reg64, 227 | reg_values[3].Reg64, 228 | reg_values[4].Reg64, 229 | reg_values[5].Reg64, 230 | reg_values[6].Reg64, 231 | reg_values[7].Reg64, 232 | reg_values[8].Reg64, 233 | reg_values[9].Reg64, 234 | reg_values[10].Reg64, 235 | reg_values[11].Reg64, 236 | reg_values[12].Reg64, 237 | reg_values[13].Reg64, 238 | reg_values[14].Reg64, 239 | reg_values[15].Reg64, 240 | reg_values[16].Reg64, 241 | reg_values[17].Reg64 242 | )?; 243 | } 244 | writeln!(fmt, "") 245 | } 246 | 247 | fn dump_segment_regs(fmt: &mut fmt::Formatter, vp: &VirtualProcessor) -> fmt::Result { 248 | const NUM_REGS: usize = 8; 249 | let reg_names: [WHV_REGISTER_NAME; NUM_REGS] = [ 250 | WHV_REGISTER_NAME::WHvX64RegisterCs, 251 | WHV_REGISTER_NAME::WHvX64RegisterSs, 252 | WHV_REGISTER_NAME::WHvX64RegisterDs, 253 | WHV_REGISTER_NAME::WHvX64RegisterEs, 254 | WHV_REGISTER_NAME::WHvX64RegisterFs, 255 | WHV_REGISTER_NAME::WHvX64RegisterGs, 256 | WHV_REGISTER_NAME::WHvX64RegisterTr, 257 | WHV_REGISTER_NAME::WHvX64RegisterLdtr, 258 | ]; 259 | let mut reg_values: [WHV_REGISTER_VALUE; NUM_REGS] = Default::default(); 260 | 261 | // Get the registers as a baseline 262 | vp.get_registers(®_names, &mut reg_values).or(Err(fmt::Error))?; 263 | 264 | writeln!(fmt, "Segment regs:")?; 265 | unsafe { 266 | writeln!(fmt, 267 | " Cs: {:?}\n\ 268 | \x20 Ss: {:?}\n\ 269 | \x20 Ds: {:?}\n\ 270 | \x20 Es: {:?}\n\ 271 | \x20 Fs: {:?}\n\ 272 | \x20 Gs: {:?}\n\ 273 | \x20 Tr: {:?}\n\ 274 | \x20 Ldtr: {:?}", 275 | reg_values[0].Segment, 276 | reg_values[1].Segment, 277 | reg_values[2].Segment, 278 | reg_values[3].Segment, 279 | reg_values[4].Segment, 280 | reg_values[5].Segment, 281 | reg_values[6].Segment, 282 | reg_values[7].Segment, 283 | )? 284 | } 285 | writeln!(fmt, "") 286 | } 287 | 288 | fn dump_table_regs(fmt: &mut fmt::Formatter, vp: &VirtualProcessor) -> fmt::Result { 289 | const NUM_REGS: usize = 2; 290 | let reg_names: [WHV_REGISTER_NAME; NUM_REGS] = [ 291 | WHV_REGISTER_NAME::WHvX64RegisterIdtr, 292 | WHV_REGISTER_NAME::WHvX64RegisterGdtr, 293 | ]; 294 | let mut reg_values: [WHV_REGISTER_VALUE; NUM_REGS] = Default::default(); 295 | 296 | // Get the registers as a baseline 297 | vp.get_registers(®_names, &mut reg_values).or(Err(fmt::Error))?; 298 | 299 | unsafe { 300 | writeln!(fmt, "Table regs:")?; 301 | writeln!(fmt, "Idtr = {:?}", reg_values[0].Table)?; 302 | writeln!(fmt, "Gdtr = {:0?}", reg_values[1].Table)?; 303 | } 304 | writeln!(fmt, "") 305 | } 306 | 307 | fn dump_control_regs(fmt: &mut fmt::Formatter, vp: &VirtualProcessor) -> fmt::Result { 308 | const NUM_REGS: usize = 5; 309 | let reg_names: [WHV_REGISTER_NAME; NUM_REGS] = [ 310 | WHV_REGISTER_NAME::WHvX64RegisterCr0, 311 | WHV_REGISTER_NAME::WHvX64RegisterCr2, 312 | WHV_REGISTER_NAME::WHvX64RegisterCr3, 313 | WHV_REGISTER_NAME::WHvX64RegisterCr4, 314 | WHV_REGISTER_NAME::WHvX64RegisterCr8, 315 | ]; 316 | let mut reg_values: [WHV_REGISTER_VALUE; NUM_REGS] = Default::default(); 317 | 318 | // Get the registers as a baseline 319 | vp.get_registers(®_names, &mut reg_values).or(Err(fmt::Error))?; 320 | 321 | let mut idx = 0; 322 | writeln!(fmt, "Control regs:")?; 323 | for v in reg_names.iter() { 324 | unsafe { 325 | writeln!(fmt, "{:?} = 0x{:x?}", v, reg_values[idx].Reg64)?; 326 | } 327 | idx += 1; 328 | } 329 | writeln!(fmt, "") 330 | } 331 | 332 | fn dump_debug_regs(fmt: &mut fmt::Formatter, vp: &VirtualProcessor) -> fmt::Result { 333 | const NUM_REGS: usize = 6; 334 | let reg_names: [WHV_REGISTER_NAME; NUM_REGS] = [ 335 | WHV_REGISTER_NAME::WHvX64RegisterDr0, 336 | WHV_REGISTER_NAME::WHvX64RegisterDr1, 337 | WHV_REGISTER_NAME::WHvX64RegisterDr2, 338 | WHV_REGISTER_NAME::WHvX64RegisterDr3, 339 | WHV_REGISTER_NAME::WHvX64RegisterDr6, 340 | WHV_REGISTER_NAME::WHvX64RegisterDr7, 341 | ]; 342 | let mut reg_values: [WHV_REGISTER_VALUE; NUM_REGS] = Default::default(); 343 | 344 | vp.get_registers(®_names, &mut reg_values).or(Err(fmt::Error))?; 345 | 346 | unsafe { 347 | writeln!(fmt, 348 | "Debug regs:\n\ 349 | Dr0={:016x} Dr1={:016x} Dr2={:016x} \n\ 350 | Dr3={:016x} Dr6={:016x} Dr7={:016x}\n", 351 | reg_values[0].Reg64, 352 | reg_values[1].Reg64, 353 | reg_values[2].Reg64, 354 | reg_values[3].Reg64, 355 | reg_values[4].Reg64, 356 | reg_values[5].Reg64, 357 | ) 358 | } 359 | } 360 | 361 | fn dump_fp_regs(fmt: &mut fmt::Formatter, vp: &VirtualProcessor) -> fmt::Result { 362 | const NUM_REGS: usize = 26; 363 | let reg_names: [WHV_REGISTER_NAME; NUM_REGS] = [ 364 | WHV_REGISTER_NAME::WHvX64RegisterXmm0, 365 | WHV_REGISTER_NAME::WHvX64RegisterXmm1, 366 | WHV_REGISTER_NAME::WHvX64RegisterXmm2, 367 | WHV_REGISTER_NAME::WHvX64RegisterXmm3, 368 | WHV_REGISTER_NAME::WHvX64RegisterXmm4, 369 | WHV_REGISTER_NAME::WHvX64RegisterXmm5, 370 | WHV_REGISTER_NAME::WHvX64RegisterXmm6, 371 | WHV_REGISTER_NAME::WHvX64RegisterXmm7, 372 | WHV_REGISTER_NAME::WHvX64RegisterXmm8, 373 | WHV_REGISTER_NAME::WHvX64RegisterXmm9, 374 | WHV_REGISTER_NAME::WHvX64RegisterXmm10, 375 | WHV_REGISTER_NAME::WHvX64RegisterXmm11, 376 | WHV_REGISTER_NAME::WHvX64RegisterXmm12, 377 | WHV_REGISTER_NAME::WHvX64RegisterXmm13, 378 | WHV_REGISTER_NAME::WHvX64RegisterXmm14, 379 | WHV_REGISTER_NAME::WHvX64RegisterXmm15, 380 | WHV_REGISTER_NAME::WHvX64RegisterFpMmx0, 381 | WHV_REGISTER_NAME::WHvX64RegisterFpMmx1, 382 | WHV_REGISTER_NAME::WHvX64RegisterFpMmx2, 383 | WHV_REGISTER_NAME::WHvX64RegisterFpMmx3, 384 | WHV_REGISTER_NAME::WHvX64RegisterFpMmx4, 385 | WHV_REGISTER_NAME::WHvX64RegisterFpMmx5, 386 | WHV_REGISTER_NAME::WHvX64RegisterFpMmx6, 387 | WHV_REGISTER_NAME::WHvX64RegisterFpMmx7, 388 | WHV_REGISTER_NAME::WHvX64RegisterFpControlStatus, 389 | WHV_REGISTER_NAME::WHvX64RegisterXmmControlStatus, 390 | ]; 391 | let mut reg_values: [WHV_REGISTER_VALUE; NUM_REGS] = Default::default(); 392 | 393 | vp.get_registers(®_names, &mut reg_values).or(Err(fmt::Error))?; 394 | 395 | unsafe { 396 | writeln!(fmt, 397 | "Fp regs: \n\ 398 | Xmm0={:016x}{:016x} Xmm1={:016x}{:016x} \n\ 399 | Xmm2={:016x}{:016x} Xmm3={:016x}{:016x} \n\ 400 | Xmm4={:016x}{:016x} Xmm5={:016x}{:016x} \n\ 401 | Xmm6={:016x}{:016x} Xmm7={:016x}{:016x} \n\ 402 | Xmm8={:016x}{:016x} Xmm9={:016x}{:016x} \n\ 403 | Xmm10={:016x}{:016x} Xmm11={:016x}{:016x} \n\ 404 | Xmm12={:016x}{:016x} Xmm13={:016x}{:016x} \n\ 405 | Xmm14={:016x}{:016x} Xmm15={:016x}{:016x} \n\ 406 | Mmx0={:016x} Mmx1={:016x} Mmx2={:016x} \n\ 407 | Mmx3={:016x} Mmx4={:016x} Mmx5={:016x} \n\ 408 | Mmx6={:016x} Mmx7={:016x} \n\ 409 | Csr={:016x} XCsr={:016x}\n", 410 | reg_values[0].Fp.AsUINT128.High64, 411 | reg_values[0].Fp.AsUINT128.Low64, 412 | reg_values[1].Fp.AsUINT128.High64, 413 | reg_values[1].Fp.AsUINT128.Low64, 414 | reg_values[2].Fp.AsUINT128.High64, 415 | reg_values[2].Fp.AsUINT128.Low64, 416 | reg_values[3].Fp.AsUINT128.High64, 417 | reg_values[3].Fp.AsUINT128.Low64, 418 | reg_values[4].Fp.AsUINT128.High64, 419 | reg_values[4].Fp.AsUINT128.Low64, 420 | reg_values[5].Fp.AsUINT128.High64, 421 | reg_values[5].Fp.AsUINT128.Low64, 422 | reg_values[6].Fp.AsUINT128.High64, 423 | reg_values[6].Fp.AsUINT128.Low64, 424 | reg_values[7].Fp.AsUINT128.High64, 425 | reg_values[7].Fp.AsUINT128.Low64, 426 | reg_values[8].Fp.AsUINT128.High64, 427 | reg_values[8].Fp.AsUINT128.Low64, 428 | reg_values[9].Fp.AsUINT128.High64, 429 | reg_values[9].Fp.AsUINT128.Low64, 430 | reg_values[10].Fp.AsUINT128.High64, 431 | reg_values[10].Fp.AsUINT128.Low64, 432 | reg_values[11].Fp.AsUINT128.High64, 433 | reg_values[11].Fp.AsUINT128.Low64, 434 | reg_values[12].Fp.AsUINT128.High64, 435 | reg_values[12].Fp.AsUINT128.Low64, 436 | reg_values[13].Fp.AsUINT128.High64, 437 | reg_values[13].Fp.AsUINT128.Low64, 438 | reg_values[14].Fp.AsUINT128.High64, 439 | reg_values[14].Fp.AsUINT128.Low64, 440 | reg_values[15].Fp.AsUINT128.High64, 441 | reg_values[15].Fp.AsUINT128.Low64, 442 | reg_values[16].Reg64, 443 | reg_values[17].Reg64, 444 | reg_values[18].Reg64, 445 | reg_values[19].Reg64, 446 | reg_values[20].Reg64, 447 | reg_values[21].Reg64, 448 | reg_values[22].Reg64, 449 | reg_values[23].Reg64, 450 | reg_values[24].Reg64, 451 | reg_values[25].Reg64, 452 | ) 453 | } 454 | } 455 | 456 | fn dump_msr_regs(fmt: &mut fmt::Formatter, vp: &VirtualProcessor) -> fmt::Result { 457 | const NUM_REGS: usize = 12; 458 | let reg_names: [WHV_REGISTER_NAME; NUM_REGS] = [ 459 | WHV_REGISTER_NAME::WHvX64RegisterTsc, 460 | WHV_REGISTER_NAME::WHvX64RegisterEfer, 461 | WHV_REGISTER_NAME::WHvX64RegisterKernelGsBase, 462 | WHV_REGISTER_NAME::WHvX64RegisterApicBase, 463 | WHV_REGISTER_NAME::WHvX64RegisterPat, 464 | WHV_REGISTER_NAME::WHvX64RegisterSysenterCs, 465 | WHV_REGISTER_NAME::WHvX64RegisterSysenterEip, 466 | WHV_REGISTER_NAME::WHvX64RegisterSysenterEsp, 467 | WHV_REGISTER_NAME::WHvX64RegisterStar, 468 | WHV_REGISTER_NAME::WHvX64RegisterLstar, 469 | WHV_REGISTER_NAME::WHvX64RegisterCstar, 470 | WHV_REGISTER_NAME::WHvX64RegisterSfmask, 471 | ]; 472 | let mut reg_values: [WHV_REGISTER_VALUE; NUM_REGS] = Default::default(); 473 | 474 | // Get the registers as a baseline 475 | vp.get_registers(®_names, &mut reg_values).or(Err(fmt::Error))?; 476 | 477 | let mut idx = 0; 478 | writeln!(fmt, "Msr regs:")?; 479 | for v in reg_names.iter() { 480 | unsafe { 481 | writeln!(fmt, "{:?} = 0x{:x?}", v, reg_values[idx].Reg64)?; 482 | } 483 | idx += 1; 484 | } 485 | writeln!(fmt, "") 486 | } 487 | 488 | fn dump_mtr_regs(fmt: &mut fmt::Formatter, vp: &VirtualProcessor) -> fmt::Result { 489 | const NUM_REGS: usize = 16; 490 | let reg_names: [WHV_REGISTER_NAME; NUM_REGS] = [ 491 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysBase0, 492 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysMask0, 493 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysBase1, 494 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysMask1, 495 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysBase2, 496 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysMask2, 497 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysBase3, 498 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysMask3, 499 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysBase4, 500 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysMask4, 501 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysBase5, 502 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysMask5, 503 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysBase6, 504 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysMask6, 505 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysBase7, 506 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysMask7, 507 | /* 508 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysBase8, 509 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysMask8, 510 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysBase9, 511 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysMask9, 512 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysBaseA, 513 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysMaskA, 514 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysBaseB, 515 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysMaskB, 516 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysBaseC, 517 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysMaskC, 518 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysBaseD, 519 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysMaskD, 520 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysBaseE, 521 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysMaskE, 522 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysBaseF, 523 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrPhysMaskF, 524 | */ 525 | ]; 526 | let mut reg_values: [WHV_REGISTER_VALUE; NUM_REGS] = Default::default(); 527 | 528 | vp.get_registers(®_names, &mut reg_values).or(Err(fmt::Error))?; 529 | 530 | unsafe { 531 | writeln!(fmt, 532 | "Mttr regs:\n\ 533 | Mtrr0={:016x}, Mask0={:016x}, Mtrr1={:016x}, Mask1={:016x}\n\ 534 | Mtrr2={:016x}, Mask2={:016x}, Mtrr3={:016x}, Mask3={:016x}\n\ 535 | Mtrr4={:016x}, Mask4={:016x}, Mtrr5={:016x}, Mask5={:016x}\n\ 536 | Mtrr6={:016x}, Mask6={:016x}, Mtrr7={:016x}, Mask7={:016x}\n", 537 | reg_values[0].Reg64, 538 | reg_values[1].Reg64, 539 | reg_values[2].Reg64, 540 | reg_values[3].Reg64, 541 | reg_values[4].Reg64, 542 | reg_values[5].Reg64, 543 | reg_values[6].Reg64, 544 | reg_values[7].Reg64, 545 | reg_values[8].Reg64, 546 | reg_values[9].Reg64, 547 | reg_values[10].Reg64, 548 | reg_values[11].Reg64, 549 | reg_values[12].Reg64, 550 | reg_values[13].Reg64, 551 | reg_values[14].Reg64, 552 | reg_values[15].Reg64, 553 | ) 554 | } 555 | } 556 | 557 | fn dump_mtrfix_regs(fmt: &mut fmt::Formatter, vp: &VirtualProcessor) -> fmt::Result { 558 | const NUM_REGS: usize = 11; 559 | let reg_names: [WHV_REGISTER_NAME; NUM_REGS] = [ 560 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrFix64k00000, 561 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrFix16k80000, 562 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrFix16kA0000, 563 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrFix4kC0000, 564 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrFix4kC8000, 565 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrFix4kD0000, 566 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrFix4kD8000, 567 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrFix4kE0000, 568 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrFix4kE8000, 569 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrFix4kF0000, 570 | WHV_REGISTER_NAME::WHvX64RegisterMsrMtrrFix4kF8000, 571 | ]; 572 | let mut reg_values: [WHV_REGISTER_VALUE; NUM_REGS] = Default::default(); 573 | 574 | vp.get_registers(®_names, &mut reg_values).or(Err(fmt::Error))?; 575 | 576 | unsafe { 577 | writeln!(fmt, 578 | "[00000]:{:016x}, [80000]:{:016x}, [A0000]:{:016x},\n\ 579 | [C0000]:{:016x}, [C8000]:{:016x}, \n\ 580 | [D0000]:{:016x}, [D8000]:{:016x}, \n\ 581 | [E0000]:{:016x}, [E8000]:{:016x}, \n\ 582 | [F0000]:{:016x}, [F8000]:{:016x}", 583 | reg_values[0].Reg64, 584 | reg_values[1].Reg64, 585 | reg_values[2].Reg64, 586 | reg_values[3].Reg64, 587 | reg_values[4].Reg64, 588 | reg_values[5].Reg64, 589 | reg_values[6].Reg64, 590 | reg_values[7].Reg64, 591 | reg_values[8].Reg64, 592 | reg_values[9].Reg64, 593 | reg_values[10].Reg64, 594 | ) 595 | } 596 | } 597 | 598 | fn dump_interrupt_regs(fmt: &mut fmt::Formatter, vp: &VirtualProcessor) -> fmt::Result { 599 | const NUM_REGS: usize = 5; 600 | let reg_names: [WHV_REGISTER_NAME; NUM_REGS] = [ 601 | WHV_REGISTER_NAME::WHvRegisterPendingInterruption, 602 | WHV_REGISTER_NAME::WHvRegisterInterruptState, 603 | WHV_REGISTER_NAME::WHvRegisterPendingEvent, 604 | WHV_REGISTER_NAME::WHvX64RegisterDeliverabilityNotifications, 605 | WHV_REGISTER_NAME::WHvRegisterInternalActivityState, 606 | ]; 607 | let mut reg_values: [WHV_REGISTER_VALUE; NUM_REGS] = Default::default(); 608 | 609 | // Get the registers as a baseline 610 | vp.get_registers(®_names, &mut reg_values).or(Err(fmt::Error))?; 611 | 612 | writeln!(fmt, "Interrupt regs:")?; 613 | let mut idx = 0; 614 | unsafe { 615 | writeln!(fmt, 616 | "{:?} = {}", 617 | reg_names[idx], reg_values[idx].PendingInterruption 618 | )?; 619 | } 620 | let event_type = unsafe { reg_values[idx].PendingInterruption.InterruptionType() }; 621 | 622 | idx += 1; 623 | unsafe { 624 | writeln!(fmt, "{:?} = {}", reg_names[idx], reg_values[idx].InterruptState)?; 625 | } 626 | idx += 1; 627 | 628 | if event_type == WHV_X64_PENDING_EVENT_TYPE::WHvX64PendingEventException as u64 { 629 | unsafe { 630 | writeln!(fmt, "{:?} = {}", reg_names[idx], reg_values[idx].ExceptionEvent)?; 631 | } 632 | } else if event_type == WHV_X64_PENDING_EVENT_TYPE::WHvX64PendingEventExtInt as u64 { 633 | unsafe { 634 | writeln!(fmt, "{:?} = {}", reg_names[idx], reg_values[idx].ExtIntEvent)?; 635 | } 636 | } else { 637 | writeln!(fmt, "Unknown event type: {}", event_type)?; 638 | } 639 | idx += 1; 640 | unsafe { 641 | writeln!(fmt, 642 | "{:?} = {}", 643 | reg_names[idx], reg_values[idx].DeliverabilityNotifications 644 | )?; 645 | } 646 | idx += 1; 647 | unsafe { 648 | writeln!(fmt, "{:?} = {}", reg_names[idx], reg_values[idx].Reg128)?; 649 | } 650 | writeln!(fmt, "") 651 | } 652 | 653 | fn dump_cpu_counters(fmt: &mut fmt::Formatter, vp: &VirtualProcessor) -> fmt::Result { 654 | dump_apic_counters(fmt, vp)?; 655 | dump_cpu_runtime_counters(fmt, vp)?; 656 | dump_cpu_intercept_counters(fmt, vp)?; 657 | dump_cpu_event_counters(fmt, vp) 658 | } 659 | 660 | fn dump_apic_counters(fmt: &mut fmt::Formatter, vp: &VirtualProcessor) -> fmt::Result { 661 | let counters: WHV_PROCESSOR_COUNTERS = vp 662 | .get_processor_counters(WHV_PROCESSOR_COUNTER_SET::WHvProcessorCounterSetApic) 663 | .or(Err(fmt::Error))?; 664 | unsafe { 665 | writeln!(fmt, "Apic counters: {:#?}\n", counters.ApicCounters) 666 | } 667 | } 668 | 669 | fn dump_cpu_runtime_counters(fmt: &mut fmt::Formatter, vp: &VirtualProcessor) -> fmt::Result { 670 | let counters: WHV_PROCESSOR_COUNTERS = vp 671 | .get_processor_counters(WHV_PROCESSOR_COUNTER_SET::WHvProcessorCounterSetRuntime) 672 | .or(Err(fmt::Error))?; 673 | unsafe { 674 | writeln!(fmt, "CPU runtime counters: {:#?}\n", counters.RuntimeCounters) 675 | } 676 | } 677 | 678 | fn dump_cpu_intercept_counters(fmt: &mut fmt::Formatter, vp: &VirtualProcessor) -> fmt::Result { 679 | let counters: WHV_PROCESSOR_COUNTERS = vp 680 | .get_processor_counters(WHV_PROCESSOR_COUNTER_SET::WHvProcessorCounterSetIntercepts) 681 | .or(Err(fmt::Error))?; 682 | unsafe { 683 | writeln!(fmt, 684 | "CPU intercept counters: {:#?}\n", 685 | counters.InterceptCounters 686 | ) 687 | } 688 | } 689 | 690 | fn dump_cpu_event_counters(fmt: &mut fmt::Formatter, vp: &VirtualProcessor) -> fmt::Result { 691 | let counters: WHV_PROCESSOR_COUNTERS = vp 692 | .get_processor_counters(WHV_PROCESSOR_COUNTER_SET::WHvProcessorCounterSetEvents) 693 | .or(Err(fmt::Error))?; 694 | unsafe { 695 | writeln!(fmt, "CPU event counters: {:#?}\n", counters.EventCounters) 696 | } 697 | } 698 | -------------------------------------------------------------------------------- /src/platform.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Cloudbase Solutions Srl 2 | // Copyright 2018-2019 CrowdStrike, Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | // not use this file except in compliance with the License. You may obtain 6 | // a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | 16 | use common::*; 17 | use memory::*; 18 | use std; 19 | use std::sync::Arc; 20 | use win_hv_platform::*; 21 | pub use win_hv_platform_defs::*; 22 | pub use win_hv_platform_defs_internal::*; 23 | pub use x86_64::{LapicStateRaw, XsaveArea}; 24 | 25 | pub fn get_capability(capability_code: WHV_CAPABILITY_CODE) -> Result { 26 | let mut capability: WHV_CAPABILITY = Default::default(); 27 | let mut written_size: UINT32 = 0; 28 | 29 | check_result(unsafe { 30 | WHvGetCapability( 31 | capability_code, 32 | &mut capability as *mut _ as *mut VOID, 33 | std::mem::size_of::() as UINT32, 34 | &mut written_size, 35 | ) 36 | })?; 37 | Ok(capability) 38 | } 39 | 40 | pub struct PartitionHandle { 41 | handle: WHV_PARTITION_HANDLE, 42 | } 43 | 44 | // Handles can be safely shared among threads. 45 | unsafe impl Send for PartitionHandle {} 46 | unsafe impl Sync for PartitionHandle {} 47 | 48 | impl PartitionHandle { 49 | fn handle(&self) -> WHV_PARTITION_HANDLE { 50 | self.handle 51 | } 52 | } 53 | 54 | impl Drop for PartitionHandle { 55 | fn drop(&mut self) { 56 | check_result(unsafe { WHvDeletePartition(self.handle) }).unwrap(); 57 | } 58 | } 59 | 60 | pub struct Partition { 61 | partition: Arc, 62 | } 63 | 64 | impl Clone for Partition { 65 | fn clone(&self) -> Partition { 66 | Partition { 67 | partition: self.partition.clone() 68 | } 69 | } 70 | } 71 | 72 | impl Partition { 73 | pub fn new() -> Result { 74 | let mut handle: WHV_PARTITION_HANDLE = std::ptr::null_mut(); 75 | check_result(unsafe { WHvCreatePartition(&mut handle) })?; 76 | Ok(Partition { 77 | partition: Arc::new(PartitionHandle { handle: handle }), 78 | }) 79 | } 80 | 81 | pub fn set_property( 82 | &self, 83 | property_code: WHV_PARTITION_PROPERTY_CODE, 84 | property: &WHV_PARTITION_PROPERTY, 85 | ) -> Result<(), WHPError> { 86 | self.set_property_from_buffer( 87 | property_code, 88 | property as *const _ as *const VOID, 89 | std::mem::size_of::() as UINT32, 90 | )?; 91 | Ok(()) 92 | } 93 | 94 | pub fn set_property_cpuid_exits(&self, cpuids: &[UINT32]) -> Result<(), WHPError> { 95 | self.set_property_from_buffer( 96 | WHV_PARTITION_PROPERTY_CODE::WHvPartitionPropertyCodeCpuidExitList, 97 | cpuids.as_ptr() as *const VOID, 98 | (std::mem::size_of::() * cpuids.len()) as UINT32, 99 | )?; 100 | Ok(()) 101 | } 102 | 103 | pub fn set_property_cpuid_results( 104 | &self, 105 | cpuid_results: &[WHV_X64_CPUID_RESULT], 106 | ) -> Result<(), WHPError> { 107 | self.set_property_from_buffer( 108 | WHV_PARTITION_PROPERTY_CODE::WHvPartitionPropertyCodeCpuidResultList, 109 | cpuid_results.as_ptr() as *const VOID, 110 | (std::mem::size_of::() * cpuid_results.len()) as UINT32, 111 | )?; 112 | Ok(()) 113 | } 114 | 115 | fn set_property_from_buffer( 116 | &self, 117 | property_code: WHV_PARTITION_PROPERTY_CODE, 118 | property: *const VOID, 119 | size: UINT32, 120 | ) -> Result<(), WHPError> { 121 | check_result(unsafe { 122 | WHvSetPartitionProperty( 123 | self.partition.handle(), 124 | property_code, 125 | property, 126 | size, 127 | ) 128 | })?; 129 | Ok(()) 130 | } 131 | 132 | pub fn get_property( 133 | &self, 134 | property_code: WHV_PARTITION_PROPERTY_CODE, 135 | ) -> Result { 136 | let mut property: WHV_PARTITION_PROPERTY = Default::default(); 137 | self.get_property_buffer( 138 | property_code, 139 | &mut property as *mut _ as *mut VOID, 140 | std::mem::size_of::() as UINT32, 141 | )?; 142 | Ok(property) 143 | } 144 | 145 | fn get_property_buffer( 146 | &self, 147 | property_code: WHV_PARTITION_PROPERTY_CODE, 148 | property: *mut VOID, 149 | size: UINT32, 150 | ) -> Result { 151 | let mut written_size: UINT32 = 0; 152 | 153 | check_result(unsafe { 154 | WHvGetPartitionProperty( 155 | self.partition.handle(), 156 | property_code, 157 | property, 158 | size, 159 | &mut written_size, 160 | ) 161 | })?; 162 | Ok(written_size) 163 | } 164 | 165 | pub fn setup(&self) -> Result<(), WHPError> { 166 | check_result(unsafe { WHvSetupPartition(self.partition.handle()) })?; 167 | Ok(()) 168 | } 169 | 170 | pub fn create_virtual_processor(&self, index: UINT32) -> Result { 171 | check_result(unsafe { 172 | WHvCreateVirtualProcessor(self.partition.handle(), index, 0) 173 | })?; 174 | Ok(VirtualProcessor { 175 | partition: Arc::clone(&self.partition), 176 | index: index, 177 | }) 178 | } 179 | 180 | pub fn map_gpa_range( 181 | &self, 182 | source_address: &T, 183 | guest_address: WHV_GUEST_PHYSICAL_ADDRESS, 184 | size: UINT64, 185 | flags: WHV_MAP_GPA_RANGE_FLAGS, 186 | ) -> Result { 187 | check_result(unsafe { 188 | WHvMapGpaRange( 189 | self.partition.handle(), 190 | source_address.as_ptr(), 191 | guest_address, 192 | size, 193 | flags, 194 | ) 195 | })?; 196 | Ok(GPARangeMapping { 197 | partition: Arc::clone(&self.partition), 198 | source_address: source_address.as_ptr(), 199 | guest_address: guest_address, 200 | size: size, 201 | flags: flags, 202 | }) 203 | } 204 | 205 | pub fn request_interrupt(&self, interrupt: &WHV_INTERRUPT_CONTROL) -> Result<(), WHPError> { 206 | check_result(unsafe { 207 | WHvRequestInterrupt( 208 | self.partition.handle(), 209 | interrupt, 210 | std::mem::size_of::() as UINT32, 211 | ) 212 | })?; 213 | Ok(()) 214 | } 215 | } 216 | 217 | pub struct GPARangeMapping { 218 | partition: Arc, 219 | source_address: *const VOID, 220 | guest_address: WHV_GUEST_PHYSICAL_ADDRESS, 221 | size: UINT64, 222 | flags: WHV_MAP_GPA_RANGE_FLAGS, 223 | } 224 | 225 | impl GPARangeMapping { 226 | pub fn get_source_address(&self) -> *const VOID { 227 | self.source_address 228 | } 229 | 230 | pub fn get_guest_address(&self) -> WHV_GUEST_PHYSICAL_ADDRESS { 231 | self.guest_address 232 | } 233 | 234 | pub fn get_size(&self) -> UINT64 { 235 | self.size 236 | } 237 | 238 | pub fn get_flags(&self) -> WHV_MAP_GPA_RANGE_FLAGS { 239 | self.flags 240 | } 241 | } 242 | 243 | impl Drop for GPARangeMapping { 244 | fn drop(&mut self) { 245 | let p = &self.partition; 246 | check_result(unsafe { WHvUnmapGpaRange(p.handle(), self.guest_address, self.size) }) 247 | .unwrap(); 248 | } 249 | } 250 | 251 | pub struct VirtualProcessor { 252 | partition: Arc, 253 | index: UINT32, 254 | } 255 | 256 | impl VirtualProcessor { 257 | pub fn index(&self) -> UINT32 { 258 | return self.index; 259 | } 260 | 261 | pub fn run(&self) -> Result { 262 | let mut exit_context: WHV_RUN_VP_EXIT_CONTEXT = Default::default(); 263 | let exit_context_size = std::mem::size_of::() as UINT32; 264 | 265 | check_result(unsafe { 266 | WHvRunVirtualProcessor( 267 | self.partition.handle(), 268 | self.index, 269 | &mut exit_context as *mut _ as *mut VOID, 270 | exit_context_size, 271 | ) 272 | })?; 273 | Ok(exit_context) 274 | } 275 | 276 | pub fn cancel_run(&self) -> Result<(), WHPError> { 277 | check_result(unsafe { 278 | WHvCancelRunVirtualProcessor(self.partition.handle(), self.index, 0) 279 | })?; 280 | Ok(()) 281 | } 282 | 283 | pub fn set_registers( 284 | &self, 285 | reg_names: &[WHV_REGISTER_NAME], 286 | reg_values: &[WHV_REGISTER_VALUE], 287 | ) -> Result<(), WHPError> { 288 | let num_regs = reg_names.len(); 289 | 290 | if num_regs != reg_values.len() { 291 | panic!("reg_names and reg_values must have the same length") 292 | } 293 | 294 | check_result(unsafe { 295 | WHvSetVirtualProcessorRegisters( 296 | self.partition.handle(), 297 | self.index, 298 | reg_names.as_ptr(), 299 | num_regs as UINT32, 300 | reg_values.as_ptr(), 301 | ) 302 | })?; 303 | Ok(()) 304 | } 305 | 306 | pub fn get_registers( 307 | &self, 308 | reg_names: &[WHV_REGISTER_NAME], 309 | reg_values: &mut [WHV_REGISTER_VALUE], 310 | ) -> Result<(), WHPError> { 311 | let num_regs = reg_names.len(); 312 | 313 | if num_regs != reg_values.len() { 314 | panic!("reg_names and reg_values must have the same length") 315 | } 316 | 317 | check_result(unsafe { 318 | WHvGetVirtualProcessorRegisters( 319 | self.partition.handle(), 320 | self.index, 321 | reg_names.as_ptr(), 322 | num_regs as UINT32, 323 | reg_values.as_mut_ptr(), 324 | ) 325 | })?; 326 | Ok(()) 327 | } 328 | 329 | pub fn translate_gva( 330 | &self, 331 | gva: WHV_GUEST_VIRTUAL_ADDRESS, 332 | flags: WHV_TRANSLATE_GVA_FLAGS, 333 | ) -> Result<(WHV_TRANSLATE_GVA_RESULT, WHV_GUEST_PHYSICAL_ADDRESS), WHPError> { 334 | let mut gpa: WHV_GUEST_PHYSICAL_ADDRESS = 0; 335 | let mut translation_result: WHV_TRANSLATE_GVA_RESULT = Default::default(); 336 | 337 | check_result(unsafe { 338 | WHvTranslateGva( 339 | self.partition.handle(), 340 | self.index, 341 | gva, 342 | flags, 343 | &mut translation_result, 344 | &mut gpa, 345 | ) 346 | })?; 347 | Ok((translation_result, gpa)) 348 | } 349 | 350 | pub fn query_gpa_range_dirty_bitmap( 351 | &self, 352 | gva: WHV_GUEST_PHYSICAL_ADDRESS, 353 | range_size_in_bytes: UINT64, 354 | bitmap_size_in_bytes: UINT32, 355 | ) -> Result, WHPError> { 356 | let num_elem = bitmap_size_in_bytes / std::mem::size_of::() as UINT32; 357 | let mut bitmap: Box<[UINT64]> = vec![0; num_elem as usize].into_boxed_slice(); 358 | 359 | check_result(unsafe { 360 | WHvQueryGpaRangeDirtyBitmap( 361 | self.partition.handle(), 362 | gva, 363 | range_size_in_bytes, 364 | bitmap.as_mut_ptr(), 365 | bitmap_size_in_bytes, 366 | ) 367 | })?; 368 | Ok(bitmap) 369 | } 370 | 371 | pub fn get_lapic(&self) -> Result { 372 | let mut state: LapicStateRaw = Default::default(); 373 | let mut written_size: UINT32 = 0; 374 | 375 | check_result(unsafe { 376 | WHvGetVirtualProcessorInterruptControllerState( 377 | self.partition.handle(), 378 | self.index, 379 | &mut state as *mut _ as *mut VOID, 380 | std::mem::size_of::() as UINT32, 381 | &mut written_size, 382 | ) 383 | })?; 384 | Ok(state) 385 | } 386 | 387 | pub fn set_lapic(&self, state: &LapicStateRaw) -> Result<(), WHPError> { 388 | check_result(unsafe { 389 | WHvSetVirtualProcessorInterruptControllerState( 390 | self.partition.handle(), 391 | self.index, 392 | state as *const _ as *const VOID, 393 | std::mem::size_of::() as UINT32, 394 | ) 395 | })?; 396 | Ok(()) 397 | } 398 | 399 | pub fn request_interrupt(&self, interrupt: &WHV_INTERRUPT_CONTROL) -> Result<(), WHPError> { 400 | check_result(unsafe { 401 | WHvRequestInterrupt( 402 | self.partition.handle(), 403 | interrupt, 404 | std::mem::size_of::() as UINT32, 405 | ) 406 | })?; 407 | Ok(()) 408 | } 409 | 410 | #[allow(unreachable_patterns)] // Future-proof against new WHV_PARTITION_COUNTER_SET values 411 | pub fn get_partition_counters( 412 | &self, 413 | partition_counter_set: WHV_PARTITION_COUNTER_SET, 414 | ) -> Result { 415 | let mut partition_counters: WHV_PARTITION_COUNTERS = Default::default(); 416 | let mut bytes_written: UINT32 = 0; 417 | 418 | let buffer_size_in_bytes = match partition_counter_set { 419 | WHV_PARTITION_COUNTER_SET::WHvPartitionCounterSetMemory => { 420 | std::mem::size_of::() as UINT32 421 | } 422 | _ => panic!("Unknown partition counter set enum value"), 423 | }; 424 | 425 | check_result(unsafe { 426 | WHvGetPartitionCounters( 427 | self.partition.handle(), 428 | partition_counter_set, 429 | &mut partition_counters as *mut _ as *mut VOID, 430 | buffer_size_in_bytes as UINT32, 431 | &mut bytes_written, 432 | ) 433 | })?; 434 | Ok(partition_counters) 435 | } 436 | 437 | #[allow(unreachable_patterns)] // Future-proof against new WHV_PROCESSOR_COUNTER_SET values 438 | pub fn get_processor_counters( 439 | &self, 440 | processor_counter_set: WHV_PROCESSOR_COUNTER_SET, 441 | ) -> Result { 442 | let mut processor_counters: WHV_PROCESSOR_COUNTERS = Default::default(); 443 | let mut bytes_written: UINT32 = 0; 444 | 445 | let buffer_size_in_bytes = match processor_counter_set { 446 | WHV_PROCESSOR_COUNTER_SET::WHvProcessorCounterSetRuntime => { 447 | std::mem::size_of::() 448 | } 449 | WHV_PROCESSOR_COUNTER_SET::WHvProcessorCounterSetIntercepts => { 450 | std::mem::size_of::() 451 | } 452 | WHV_PROCESSOR_COUNTER_SET::WHvProcessorCounterSetEvents => { 453 | std::mem::size_of::() 454 | } 455 | WHV_PROCESSOR_COUNTER_SET::WHvProcessorCounterSetApic => { 456 | std::mem::size_of::() 457 | } 458 | _ => panic!("Unknown processor counter set enum value"), 459 | }; 460 | 461 | check_result(unsafe { 462 | WHvGetVirtualProcessorCounters( 463 | self.partition.handle(), 464 | self.index, 465 | processor_counter_set, 466 | &mut processor_counters as *mut _ as *mut VOID, 467 | buffer_size_in_bytes as UINT32, 468 | &mut bytes_written, 469 | ) 470 | })?; 471 | Ok(processor_counters) 472 | } 473 | 474 | pub fn get_xsave_state(&self) -> Result { 475 | let mut xsave_area: XsaveArea = Default::default(); 476 | let mut bytes_written: UINT32 = 0; 477 | 478 | check_result(unsafe { 479 | WHvGetVirtualProcessorXsaveState( 480 | self.partition.handle(), 481 | self.index, 482 | &mut xsave_area as *mut _ as *mut VOID, 483 | std::mem::size_of::() as UINT32, 484 | &mut bytes_written, 485 | ) 486 | })?; 487 | Ok(xsave_area) 488 | } 489 | 490 | pub fn set_xsave_state(&self, xsave_area: XsaveArea) -> Result<(), WHPError> { 491 | check_result(unsafe { 492 | WHvSetVirtualProcessorXsaveState( 493 | self.partition.handle(), 494 | self.index, 495 | &xsave_area as *const _ as *const VOID, 496 | std::mem::size_of::() as UINT32, 497 | ) 498 | })?; 499 | Ok(()) 500 | } 501 | } 502 | 503 | impl Drop for VirtualProcessor { 504 | fn drop(&mut self) { 505 | check_result(unsafe { 506 | WHvDeleteVirtualProcessor(self.partition.handle(), self.index) 507 | }) 508 | .unwrap(); 509 | } 510 | } 511 | 512 | #[cfg(test)] 513 | mod tests { 514 | use super::*; 515 | use std; 516 | 517 | #[test] 518 | fn test_create_delete_partition() { 519 | println!("CreateDeletePartition"); 520 | let p: Partition = Partition::new().unwrap(); 521 | drop(p); 522 | } 523 | 524 | #[test] 525 | fn test_delete_partition_panic() { 526 | let result = std::panic::catch_unwind(|| { 527 | // Create an invalid partition 528 | let _p = Partition { 529 | partition: Arc::new(PartitionHandle { 530 | handle: std::ptr::null_mut(), 531 | }), 532 | }; 533 | }); 534 | assert!(result.is_err(), "Drop was suppoesed to panic"); 535 | } 536 | 537 | #[test] 538 | fn test_get_capability() { 539 | let _capability: WHV_CAPABILITY = 540 | get_capability(WHV_CAPABILITY_CODE::WHvCapabilityCodeHypervisorPresent).unwrap(); 541 | } 542 | 543 | #[test] 544 | fn test_set_get_partition_property() { 545 | let p: Partition = Partition::new().unwrap(); 546 | let property_code = WHV_PARTITION_PROPERTY_CODE::WHvPartitionPropertyCodeProcessorCount; 547 | let mut property: WHV_PARTITION_PROPERTY = Default::default(); 548 | property.ProcessorCount = 1; 549 | 550 | p.set_property(property_code, &property).unwrap(); 551 | let property_out = p.get_property(property_code).unwrap(); 552 | 553 | unsafe { 554 | assert_eq!( 555 | property.ProcessorCount, property_out.ProcessorCount, 556 | "The property value is not matching" 557 | ); 558 | } 559 | } 560 | 561 | #[test] 562 | fn test_set_get_partition_property_cpuid_exits() { 563 | let p: Partition = Partition::new().unwrap(); 564 | let cpuids: [UINT32; 2] = [1, 2]; 565 | 566 | // Getting this property is not supported 567 | assert_eq!( 568 | p.set_property_cpuid_exits(&cpuids).ok(), 569 | Some(()), 570 | "set_property_cpuid_exits failed" 571 | ); 572 | } 573 | 574 | #[test] 575 | fn test_set_get_partition_property_cpuid_results() { 576 | const CPUID_EXT_HYPERVISOR: UINT32 = 1 << 31; 577 | let p: Partition = Partition::new().unwrap(); 578 | let mut cpuid_results: Vec = Vec::new(); 579 | let mut cpuid_result: WHV_X64_CPUID_RESULT = Default::default(); 580 | cpuid_result.Function = 1; 581 | cpuid_result.Ecx = CPUID_EXT_HYPERVISOR; 582 | cpuid_results.push(cpuid_result); 583 | 584 | // Getting this property is not supported 585 | assert_eq!( 586 | p.set_property_cpuid_results(&cpuid_results).ok(), 587 | Some(()), 588 | "set_property_cpuid_results failed" 589 | ); 590 | } 591 | 592 | #[test] 593 | fn test_setup_partition() { 594 | let p: Partition = Partition::new().unwrap(); 595 | let mut property: WHV_PARTITION_PROPERTY = Default::default(); 596 | property.ProcessorCount = 1; 597 | 598 | // Setup fails without setting at least the number of vcpus 599 | p.set_property( 600 | WHV_PARTITION_PROPERTY_CODE::WHvPartitionPropertyCodeProcessorCount, 601 | &property, 602 | ) 603 | .unwrap(); 604 | } 605 | 606 | #[test] 607 | fn test_setup_partition_fail() { 608 | let p: Partition = Partition::new().unwrap(); 609 | match p.setup() { 610 | Err(e) => assert_eq!( 611 | e.result(), 612 | WHV_E_INVALID_PARTITION_CONFIG, 613 | "Unexpected error code" 614 | ), 615 | Ok(()) => panic!("An error was expected"), 616 | } 617 | } 618 | 619 | fn setup_vcpu_test(p: &mut Partition) { 620 | let mut property: WHV_PARTITION_PROPERTY = Default::default(); 621 | property.ProcessorCount = 1; 622 | 623 | p.set_property( 624 | WHV_PARTITION_PROPERTY_CODE::WHvPartitionPropertyCodeProcessorCount, 625 | &property, 626 | ) 627 | .unwrap(); 628 | p.setup().unwrap(); 629 | } 630 | 631 | #[test] 632 | fn test_create_delete_virtual_processor() { 633 | let mut p: Partition = Partition::new().unwrap(); 634 | setup_vcpu_test(&mut p); 635 | 636 | let vp_index: UINT32 = 0; 637 | let vp = p.create_virtual_processor(vp_index).unwrap(); 638 | drop(vp) 639 | } 640 | 641 | #[test] 642 | fn test_run_virtual_processor() { 643 | let mut p: Partition = Partition::new().unwrap(); 644 | setup_vcpu_test(&mut p); 645 | 646 | let vp_index: UINT32 = 0; 647 | let vp = p.create_virtual_processor(vp_index).unwrap(); 648 | let exit_context: WHV_RUN_VP_EXIT_CONTEXT = vp.run().unwrap(); 649 | 650 | assert_eq!( 651 | exit_context.ExitReason, 652 | WHV_RUN_VP_EXIT_REASON::WHvRunVpExitReasonMemoryAccess, 653 | "Unexpected exit reason" 654 | ) 655 | } 656 | 657 | #[test] 658 | fn test_cancel_virtual_processor() { 659 | let mut p: Partition = Partition::new().unwrap(); 660 | setup_vcpu_test(&mut p); 661 | 662 | let vp_index: UINT32 = 0; 663 | let vp = p.create_virtual_processor(vp_index).unwrap(); 664 | vp.cancel_run().unwrap(); 665 | } 666 | 667 | #[test] 668 | fn test_set_get_virtual_processor_registers() { 669 | let mut p: Partition = Partition::new().unwrap(); 670 | setup_vcpu_test(&mut p); 671 | 672 | let vp_index: UINT32 = 0; 673 | let vp = p.create_virtual_processor(vp_index).unwrap(); 674 | 675 | const NUM_REGS: UINT32 = 1; 676 | const REG_VALUE: UINT64 = 11111111; 677 | let mut reg_names: [WHV_REGISTER_NAME; NUM_REGS as usize] = Default::default(); 678 | let mut reg_values: [WHV_REGISTER_VALUE; NUM_REGS as usize] = Default::default(); 679 | let mut reg_values_out: [WHV_REGISTER_VALUE; NUM_REGS as usize] = Default::default(); 680 | 681 | reg_names[0] = WHV_REGISTER_NAME::WHvX64RegisterRax; 682 | reg_values[0].Reg64 = REG_VALUE; 683 | 684 | vp.set_registers(®_names, ®_values).unwrap(); 685 | vp.get_registers(®_names, &mut reg_values_out).unwrap(); 686 | 687 | unsafe { 688 | assert_eq!( 689 | reg_values_out[0].Reg64, REG_VALUE, 690 | "Registers values do not match" 691 | ); 692 | } 693 | } 694 | 695 | #[test] 696 | fn test_map_gpa_range() { 697 | let mut p: Partition = Partition::new().unwrap(); 698 | setup_vcpu_test(&mut p); 699 | 700 | const SIZE: UINT64 = 0x100000; 701 | let guest_address: WHV_GUEST_PHYSICAL_ADDRESS = 0; 702 | 703 | let mem = VirtualMemory::new(SIZE as usize).unwrap(); 704 | 705 | let mapping = p 706 | .map_gpa_range( 707 | &mem, 708 | guest_address, 709 | SIZE, 710 | WHV_MAP_GPA_RANGE_FLAGS::WHvMapGpaRangeFlagRead, 711 | ) 712 | .unwrap(); 713 | 714 | assert_eq!(mapping.get_size(), SIZE); 715 | assert_eq!(mapping.get_source_address(), mem.as_ptr()); 716 | assert_eq!(mapping.get_guest_address(), guest_address); 717 | assert_eq!( 718 | mapping.get_flags(), 719 | WHV_MAP_GPA_RANGE_FLAGS::WHvMapGpaRangeFlagRead 720 | ); 721 | } 722 | 723 | #[test] 724 | fn test_translate_gva() { 725 | let mut p: Partition = Partition::new().unwrap(); 726 | setup_vcpu_test(&mut p); 727 | 728 | let vp_index: UINT32 = 0; 729 | let vp = p.create_virtual_processor(vp_index).unwrap(); 730 | 731 | let gva: WHV_GUEST_PHYSICAL_ADDRESS = 0; 732 | let (translation_result, gpa) = vp 733 | .translate_gva( 734 | gva, 735 | WHV_TRANSLATE_GVA_FLAGS::WHvTranslateGvaFlagValidateRead, 736 | ) 737 | .unwrap(); 738 | 739 | // This API changed, it used to return GpaUnmapped, now it runs Success. 740 | // So support both versions for now. 741 | // 742 | let result = translation_result.ResultCode; 743 | assert!( 744 | result == WHV_TRANSLATE_GVA_RESULT_CODE::WHvTranslateGvaResultGpaUnmapped || 745 | result == WHV_TRANSLATE_GVA_RESULT_CODE::WHvTranslateGvaResultSuccess, 746 | "Unexpected translation result code {:?}", result); 747 | 748 | assert_eq!(gpa, 0, "Unexpected GPA value"); 749 | } 750 | 751 | #[test] 752 | fn test_virtual_processor_index() { 753 | let mut p: Partition = Partition::new().unwrap(); 754 | setup_vcpu_test(&mut p); 755 | 756 | let vp_index: UINT32 = 0; 757 | let vp = p.create_virtual_processor(vp_index).unwrap(); 758 | 759 | assert_eq!(vp.index(), vp_index, "Index value not matching"); 760 | } 761 | 762 | #[test] 763 | #[allow(unused_variables)] 764 | #[allow(unused_mut)] 765 | fn test_request_interrupt() { 766 | let mut p: Partition = Partition::new().unwrap(); 767 | setup_vcpu_test(&mut p); 768 | 769 | let vp_index: UINT32 = 0; 770 | let mut vp = p.create_virtual_processor(vp_index).unwrap(); 771 | 772 | let mut interrupt_control: WHV_INTERRUPT_CONTROL = Default::default(); 773 | // TriggerMode = 0 (Edge) 774 | // DestinationMode = 0 (Logical) 775 | // InterruptType = 0x0 (Fixed) 776 | interrupt_control.TypeDestinationModeTriggerModeReserved = 0x000; 777 | interrupt_control.Destination = 0; 778 | interrupt_control.Vector = 0x37; 779 | let interrupt_control_size = std::mem::size_of::() as UINT32; 780 | match vp.request_interrupt(&interrupt_control) { 781 | Err(e) => println!("Error"), 782 | Ok(()) => println!("Success"), 783 | } 784 | } 785 | 786 | #[test] 787 | fn test_get_set_xsave_state() { 788 | let mut capability_features: WHV_CAPABILITY_FEATURES = Default::default(); 789 | capability_features.AsUINT64 = 0; 790 | 791 | let capability: WHV_CAPABILITY = 792 | get_capability(WHV_CAPABILITY_CODE::WHvCapabilityCodeFeatures).unwrap(); 793 | unsafe { 794 | capability_features = capability.Features; 795 | } 796 | 797 | if capability_features.Xsave() != 0 { 798 | let mut p: Partition = Partition::new().unwrap(); 799 | setup_vcpu_test(&mut p); 800 | 801 | let vp_index: UINT32 = 0; 802 | let vp = p.create_virtual_processor(vp_index).unwrap(); 803 | 804 | let mut xsave_state: XsaveArea = Default::default(); 805 | assert_eq!(xsave_state.region[7], 0); 806 | 807 | xsave_state = vp.get_xsave_state().unwrap(); 808 | assert_eq!(xsave_state.region[7], 0xffff); 809 | 810 | vp.set_xsave_state(xsave_state).unwrap(); 811 | } 812 | } 813 | 814 | fn initialize_apic(p: &mut Partition) -> bool { 815 | let capability: WHV_CAPABILITY = 816 | get_capability(WHV_CAPABILITY_CODE::WHvCapabilityCodeFeatures).unwrap(); 817 | let features: WHV_CAPABILITY_FEATURES = unsafe { capability.Features }; 818 | let mut apic_enabled = false; 819 | 820 | if features.LocalApicEmulation() != 0 { 821 | let mut property: WHV_PARTITION_PROPERTY = Default::default(); 822 | 823 | property.LocalApicEmulationMode = 824 | WHV_X64_LOCAL_APIC_EMULATION_MODE::WHvX64LocalApicEmulationModeXApic; 825 | 826 | p.set_property( 827 | WHV_PARTITION_PROPERTY_CODE::WHvPartitionPropertyCodeLocalApicEmulationMode, 828 | &property, 829 | ) 830 | .unwrap(); 831 | 832 | apic_enabled = true; 833 | } 834 | 835 | apic_enabled 836 | } 837 | 838 | use interrupts::*; 839 | use x86_64::*; 840 | #[test] 841 | fn test_enable_get_set_apic() { 842 | let mut p: Partition = Partition::new().unwrap(); 843 | 844 | let apic_enabled = initialize_apic(&mut p); 845 | 846 | let mut property: WHV_PARTITION_PROPERTY = Default::default(); 847 | property.ProcessorCount = 1; 848 | 849 | p.set_property( 850 | WHV_PARTITION_PROPERTY_CODE::WHvPartitionPropertyCodeProcessorCount, 851 | &property, 852 | ) 853 | .unwrap(); 854 | 855 | p.setup().unwrap(); 856 | 857 | let vp_index: UINT32 = 0; 858 | let vp = p.create_virtual_processor(vp_index).unwrap(); 859 | 860 | if apic_enabled == true { 861 | let state: LapicStateRaw = vp.get_lapic().unwrap(); 862 | let icr0 = get_lapic_reg(&state, APIC_REG_OFFSET::InterruptCommand0); 863 | assert_eq!(icr0, 0); 864 | 865 | // Uses both get_lapic and set_lapic under the hood 866 | set_reg_in_lapic(&vp, APIC_REG_OFFSET::InterruptCommand0, 0x40); 867 | 868 | let state_out: LapicStateRaw = vp.get_lapic().unwrap(); 869 | let icr0 = get_lapic_reg(&state_out, APIC_REG_OFFSET::InterruptCommand0); 870 | assert_eq!(icr0, 0x40); 871 | } 872 | } 873 | 874 | #[test] 875 | fn test_get_partition_counters() { 876 | let mut p: Partition = Partition::new().unwrap(); 877 | setup_vcpu_test(&mut p); 878 | 879 | let vp_index: UINT32 = 0; 880 | 881 | let mut _vp = p.create_virtual_processor(vp_index).unwrap(); 882 | 883 | let counters: WHV_PARTITION_COUNTERS = _vp 884 | .get_partition_counters(WHV_PARTITION_COUNTER_SET::WHvPartitionCounterSetMemory) 885 | .unwrap(); 886 | let mem_counters = unsafe { counters.MemoryCounters }; 887 | 888 | assert_eq!(mem_counters.Mapped4KPageCount, 0); 889 | assert_eq!(mem_counters.Mapped2MPageCount, 0); 890 | assert_eq!(mem_counters.Mapped1GPageCount, 0); 891 | } 892 | 893 | #[test] 894 | fn test_get_processor_counters() { 895 | let mut p: Partition = Partition::new().unwrap(); 896 | setup_vcpu_test(&mut p); 897 | 898 | let vp_index: UINT32 = 0; 899 | 900 | let vp = p.create_virtual_processor(vp_index).unwrap(); 901 | let counters: WHV_PROCESSOR_COUNTERS = vp 902 | .get_processor_counters(WHV_PROCESSOR_COUNTER_SET::WHvProcessorCounterSetRuntime) 903 | .unwrap(); 904 | let runtime_counters = unsafe { counters.RuntimeCounters }; 905 | assert!(runtime_counters.TotalRuntime100ns > 0); 906 | 907 | let counters: WHV_PROCESSOR_COUNTERS = vp 908 | .get_processor_counters(WHV_PROCESSOR_COUNTER_SET::WHvProcessorCounterSetIntercepts) 909 | .unwrap(); 910 | let intercept_counters = unsafe { counters.InterceptCounters }; 911 | assert_eq!(intercept_counters.PageInvalidations.Count, 0); 912 | 913 | let counters: WHV_PROCESSOR_COUNTERS = vp 914 | .get_processor_counters(WHV_PROCESSOR_COUNTER_SET::WHvProcessorCounterSetEvents) 915 | .unwrap(); 916 | let event_counters = unsafe { counters.EventCounters }; 917 | assert_eq!(event_counters.PageFaultCount, 0); 918 | 919 | let counters: WHV_PROCESSOR_COUNTERS = vp 920 | .get_processor_counters(WHV_PROCESSOR_COUNTER_SET::WHvProcessorCounterSetApic) 921 | .unwrap(); 922 | let apic_counters = unsafe { counters.ApicCounters }; 923 | assert_eq!(apic_counters.SentIpiCount, 0); 924 | } 925 | } 926 | --------------------------------------------------------------------------------