├── .dockerignore ├── .gitignore ├── Dockerfile ├── LICENSE ├── Package.swift ├── README.md ├── Sources ├── CHypervisorKit │ ├── CHypervisorKit.c │ └── include │ │ ├── CHypervisorKit.h │ │ └── linux.h └── HypervisorKit │ ├── Extensions │ └── NSLockExtras.swift │ ├── RegisterCache.swift │ ├── RegisterSet.swift │ ├── Registers.swift │ ├── VMError.swift │ ├── VirtualMachine.swift │ ├── cpu.swift │ ├── linux │ ├── kvm_registers.swift │ ├── kvm_vcpu.swift │ ├── kvm_vm.swift │ └── kvmexit.swift │ ├── macos │ ├── checkvmcs.swift │ ├── emulate.swift │ ├── hvf_registers.swift │ ├── hvf_vcpu.swift │ ├── hvf_vm.swift │ ├── hvf_vmexit.swift │ ├── vmcs.swift │ ├── vmx.swift │ └── vmxmsr.swift │ ├── memoryregion.swift │ ├── physicaladdress.swift │ ├── vcpu.swift │ └── vmexit.swift ├── Tests ├── HypervisorKitTests │ ├── RealModeTests.swift │ ├── VMMKitTests.swift │ ├── XCTestManifests.swift │ ├── real_mode_test.asm │ └── real_mode_test.bin └── LinuxMain.swift ├── asm.sh ├── docs.sh └── docs ├── Classes.html ├── Classes ├── MemoryRegion.html ├── VirtualMachine.html └── VirtualMachine │ ├── VCPU.html │ └── VCPU │ └── Registers.html ├── Enums.html ├── Enums ├── VMError.html ├── VMExit.html └── VMExit │ ├── DataRead.html │ ├── DataWrite.html │ ├── ExceptionInfo.html │ ├── ExceptionInfo │ └── Exception.html │ ├── MemoryViolation.html │ └── MemoryViolation │ └── Access.html ├── Extensions.html ├── Extensions └── VirtualMachine.html ├── Structs.html ├── Structs ├── CPU.html ├── CPU │ ├── CR0Register.html │ └── RFLAGS.html ├── DescriptorTable.html ├── InternalError.html ├── PhysicalAddress.html ├── RegisterSet.html └── SegmentRegister.html ├── Typealiases.html ├── badge.svg ├── css ├── highlight.css └── jazzy.css ├── img ├── carat.png ├── dash.png ├── gh.png └── spinner.gif ├── index.html ├── js ├── jazzy.js ├── jazzy.search.js ├── jquery.min.js ├── lunr.min.js └── typeahead.jquery.js └── search.json /.dockerignore: -------------------------------------------------------------------------------- 1 | # ignore all files by default 2 | * 3 | # Files to copy, as exceptions to the wildcard above 4 | !Package.swift 5 | !Sources 6 | !Tests 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | DerivedData 7 | .swiftpm 8 | Package.resolved 9 | *~ 10 | *.o 11 | \#* 12 | .\#* 13 | 14 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Run using: docker build --tag=hypervisorkit-tests:$(date +%s) . 2 | FROM swift:5.1.2 3 | 4 | COPY . /root/ 5 | WORKDIR /root 6 | RUN swift test -v 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | #if !arch(x86_64) 7 | fatalError("HypervisorKit is currently only supported on x86_64") 8 | #endif 9 | 10 | let package = Package( 11 | name: "hypervisor-kit", 12 | platforms: [ 13 | .macOS(.v10_15), 14 | ], 15 | products: [ 16 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 17 | .library( 18 | name: "HypervisorKit", 19 | targets: ["HypervisorKit"]), 20 | ], 21 | dependencies: [ 22 | .package(url: "https://github.com/apple/swift-log.git", from: "1.0.0"), 23 | .package(url: "https://github.com/spevans/swift-babab.git", from: "0.0.2"), 24 | ], 25 | targets: [ 26 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 27 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 28 | .target( 29 | name: "CHypervisorKit", 30 | dependencies:[]), 31 | .target( 32 | name: "HypervisorKit", 33 | dependencies:[ 34 | "CHypervisorKit", 35 | .product(name: "Logging", package: "swift-log"), 36 | .product(name: "BABAB", package: "swift-babab"), 37 | ] 38 | ), 39 | .testTarget( 40 | name: "HypervisorKitTests", 41 | dependencies: ["HypervisorKit"], 42 | exclude: ["real_mode_test.asm"], 43 | resources: [ .copy("real_mode_test.bin") ] 44 | ), 45 | ] 46 | ) 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HypervisorKit 2 | 3 | A Swift library providing an interface for writing X86 hypervisors on macOS (Hypervisor.framework) and Linux (KVM). 4 | 5 | Documentation is available [here](https://spevans.github.io/hypervisor-kit). 6 | 7 | On macOS, [Hypervisor.framework](https://developer.apple.com/documentation/hypervisor) provides a very thin wrapper 8 | over the Intel VMX virtualisation instructions, allowing direct access to VMCS (Virtual machine control structures). 9 | Some functionality has to be implemented by the kernel, eg setting up EPT (Extended Page Tables) to map a process's 10 | memory into the virtual machine's address space. 11 | 12 | Conversely, Linux KVM provides a more abstract interface as it is designed to work across multiple CPU architectures, 13 | eg x86_64, PPC etc and uses file descriptors and ioctl calls. KVM provides its own handling for vmexits and converts 14 | this to a smaller, simplified collection of KVMExits. 15 | 16 | HypervisorKit aims to provide equivalent functionality on macOS to match KVM allowing cross platform virtual machines 17 | to be written for both macOS and Linux. 18 | -------------------------------------------------------------------------------- /Sources/CHypervisorKit/CHypervisorKit.c: -------------------------------------------------------------------------------- 1 | // 2 | // CHypervisorKit.c 3 | // HypervisorKit 4 | // 5 | // Created by Simon Evans on 27/12/2019. 6 | // Copyright © 2019 Simon Evans. All rights reserved. 7 | // 8 | 9 | 10 | // This package exists for any C files and headers that need to be included 11 | #include "CHypervisorKit.h" 12 | -------------------------------------------------------------------------------- /Sources/CHypervisorKit/include/CHypervisorKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // CHypervisorKit.h 3 | // HypervisorKit 4 | // 5 | // Created by Simon Evans on 25/12/2019. 6 | // Copyright © 2019 Simon Evans. All rights reserved. 7 | // 8 | // Header file for convinent c bits 9 | // 10 | 11 | #include 12 | #include "linux.h" 13 | 14 | static inline uint32_t 15 | unaligned_load32(const void * _Nonnull ptr) { 16 | uint8_t *bytes = (uint8_t *)ptr; 17 | #if __LITTLE_ENDIAN__ 18 | uint32_t result = (uint32_t)bytes[0]; 19 | result |= ((uint32_t)bytes[1] << 8); 20 | result |= ((uint32_t)bytes[2] << 16); 21 | result |= ((uint32_t)bytes[3] << 24); 22 | #else 23 | uint32_t result = (uint32_t)bytes[0] << 24; 24 | result |= ((uint32_t)bytes[1] << 16); 25 | result |= ((uint32_t)bytes[2] << 8); 26 | result |= (uint32_t)bytes[3]; 27 | #endif 28 | return result; 29 | } 30 | 31 | static inline uint16_t 32 | unaligned_load16(const void * _Nonnull ptr) { 33 | uint8_t *bytes = (uint8_t *)ptr; 34 | #if __LITTLE_ENDIAN__ 35 | uint16_t result = (uint16_t)bytes[0]; 36 | result |= ((uint16_t)bytes[1] << 8); 37 | #else 38 | result |= ((uint16_t)bytes[0] << 8); 39 | result |= (uint16_t)bytes[1]; 40 | #endif 41 | return result; 42 | } 43 | 44 | 45 | static inline void 46 | unaligned_store32(void * _Nonnull ptr, uint32_t value) { 47 | uint8_t *bytes = (uint8_t *)ptr; 48 | #if __LITTLE_ENDIAN__ 49 | bytes[0] = (uint8_t)(value & 0xff); 50 | bytes[1] = (uint8_t)((value >> 8) & 0xff); 51 | bytes[2] = (uint8_t)((value >> 16) & 0xff); 52 | bytes[3] = (uint8_t)((value >> 24) & 0xff); 53 | #else 54 | bytes[0] = (uint8_t)((value >> 24) & 0xff); 55 | bytes[1] = (uint8_t)((value >> 16) & 0xff); 56 | bytes[2] = (uint8_t)((value >> 8) & 0xff); 57 | bytes[3] = (uint8_t)((value >> 0) & 0xff); 58 | #endif 59 | } 60 | 61 | static inline void 62 | unaligned_store16(void * _Nonnull ptr, uint16_t value) { 63 | uint8_t *bytes = (uint8_t *)ptr; 64 | #if __LITTLE_ENDIAN__ 65 | bytes[0] = (uint8_t)(value & 0xff); 66 | bytes[1] = (uint8_t)((value >> 8) & 0xff); 67 | #else 68 | bytes[0] = (uint8_t)((value >> 84 & 0xff); 69 | bytes[1] = (uint8_t)((value >> 25) & 0xff); 70 | #endif 71 | } 72 | 73 | 74 | union cpuid_result { 75 | struct { 76 | uint32_t eax; 77 | uint32_t ebx; 78 | uint32_t ecx; 79 | uint32_t edx; 80 | } regs; 81 | // Used to access the result as a string 82 | // for functions returning cpu name etc 83 | char bytes[33]; 84 | }; 85 | 86 | // Returns a pointer to the char array for ease of converting to a String 87 | static inline const char * _Nonnull 88 | cpuid(const uint32_t function, union cpuid_result * _Nonnull result) 89 | { 90 | uint32_t eax, ebx, ecx, edx; 91 | asm volatile ("cpuid" 92 | : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) 93 | : "a" (function) 94 | : ); 95 | result->regs.eax = eax; 96 | result->regs.ebx = ebx; 97 | if (function == 0) { 98 | result->regs.ecx = edx; 99 | result->regs.edx = ecx; 100 | } else { 101 | result->regs.ecx = ecx; 102 | result->regs.edx = edx; 103 | } 104 | 105 | result->bytes[32] = '\0'; 106 | 107 | return result->bytes; 108 | } 109 | 110 | // Returns a pointer to the char array for ease of converting to a String 111 | static inline const char * _Nonnull 112 | cpuid2(const uint32_t function, const uint32_t extra, union cpuid_result * _Nonnull result) 113 | { 114 | uint32_t eax, ebx, ecx, edx; 115 | asm volatile ("cpuid" 116 | : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) 117 | : "a" (function), "c" (extra) 118 | : ); 119 | result->regs.eax = eax; 120 | result->regs.ebx = ebx; 121 | if (function == 0) { 122 | result->regs.ecx = edx; 123 | result->regs.edx = ecx; 124 | } else { 125 | result->regs.ecx = ecx; 126 | result->regs.edx = edx; 127 | } 128 | 129 | result->bytes[32] = '\0'; 130 | 131 | return result->bytes; 132 | } 133 | 134 | -------------------------------------------------------------------------------- /Sources/CHypervisorKit/include/linux.h: -------------------------------------------------------------------------------- 1 | // 2 | // linux.h 3 | // HypervisorKit 4 | // 5 | // Created by Simon Evans on 01/12/2019. 6 | // Copyright © 2019 Simon Evans. All rights reserved. 7 | // 8 | 9 | #if defined(__linux__) 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | 21 | static unsigned long _IOCTL_KVM_GET_API_VERSION = KVM_GET_API_VERSION; 22 | static unsigned long _IOCTL_KVM_GET_VCPU_MMAP_SIZE = KVM_GET_VCPU_MMAP_SIZE; 23 | static unsigned long _IOCTL_KVM_SET_USER_MEMORY_REGION = KVM_SET_USER_MEMORY_REGION; 24 | static unsigned long _IOCTL_KVM_GET_SREGS = KVM_GET_SREGS; 25 | static unsigned long _IOCTL_KVM_SET_SREGS = KVM_SET_SREGS; 26 | static unsigned long _IOCTL_KVM_GET_REGS = KVM_GET_REGS; 27 | static unsigned long _IOCTL_KVM_SET_REGS = KVM_SET_REGS; 28 | static unsigned long _IOCTL_KVM_RUN = KVM_RUN; 29 | static unsigned long _IOCTL_KVM_CREATE_VM = KVM_CREATE_VM; 30 | static unsigned long _IOCTL_KVM_CREATE_VCPU = KVM_CREATE_VCPU; 31 | static unsigned long _IOCTL_KVM_CREATE_IRQCHIP = KVM_CREATE_IRQCHIP; 32 | static unsigned long _IOCTL_KVM_CREATE_PIT2 = KVM_CREATE_PIT2; 33 | static unsigned long _IOCTL_KVM_GET_PIT2 = KVM_GET_PIT2; 34 | static unsigned long _IOCTL_KVM_SET_PIT2 = KVM_SET_PIT2; 35 | static unsigned long _IOCTL_KVM_INTERRUPT = KVM_INTERRUPT; 36 | 37 | static __u32 _KVM_PIT_SPEAKER_DUMMY = KVM_PIT_SPEAKER_DUMMY; 38 | 39 | static inline int open2arg(const char *pathname, int flags) { 40 | return open(pathname, flags); 41 | } 42 | 43 | static inline int ioctl2arg(int fd, unsigned long request) { 44 | return ioctl(fd, request, 0); 45 | } 46 | 47 | 48 | static inline int ioctl3arg(int fd, unsigned long request, const void *ptr) { 49 | return ioctl(fd, request, ptr); 50 | } 51 | 52 | #endif // __linux__ 53 | -------------------------------------------------------------------------------- /Sources/HypervisorKit/Extensions/NSLockExtras.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSLockExtrans.swift 3 | // HypervisorKit 4 | // 5 | // Created by Simon Evans on 25/12/2019. 6 | // Copyright © 2019 Simon Evans. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension NSLock { 12 | internal func performLocked(_ closure: () throws -> T) rethrows -> T { 13 | self.lock() 14 | defer { self.unlock() } 15 | return try closure() 16 | } 17 | } 18 | 19 | extension NSCondition { 20 | internal func performLocked(_ closure: () throws -> T) rethrows -> T { 21 | self.lock() 22 | defer { self.unlock() } 23 | return try closure() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Sources/HypervisorKit/RegisterCache.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RegisterCache.swift 3 | // HypervisorKit 4 | // 5 | // Created by Simon Evans on 22/03/2021. 6 | // Copyright © 2021 Simon Evans. All rights reserved. 7 | // 8 | // Internal data for caching vCPU register values. 9 | 10 | 11 | // For per OS implementation. 12 | internal protocol RegisterCacheControlProtocol { 13 | mutating func readRegisters(_ registerSet: RegisterSet) throws 14 | mutating func setupRegisters() throws 15 | mutating func clearCache() 16 | mutating func makeReadOnly() 17 | } 18 | 19 | 20 | // Underlying cache of registers initially set to nil until read from the 21 | // vCPU or set. 22 | internal struct RegisterCache { 23 | internal var updatedRegisters = RegisterSet() 24 | 25 | internal var _cs: SegmentRegister? 26 | internal var _ds: SegmentRegister? 27 | internal var _es: SegmentRegister? 28 | internal var _fs: SegmentRegister? 29 | internal var _gs: SegmentRegister? 30 | internal var _ss: SegmentRegister? 31 | internal var _taskRegister: SegmentRegister? 32 | internal var _ldtr: SegmentRegister? 33 | internal var _gdt: DescriptorTable? 34 | internal var _idt: DescriptorTable? 35 | internal var _rflags: CPU.RFLAGS? 36 | 37 | internal var _rax: UInt64? 38 | internal var _rbx: UInt64? 39 | internal var _rcx: UInt64? 40 | internal var _rdx: UInt64? 41 | internal var _rsi: UInt64? 42 | internal var _rdi: UInt64? 43 | internal var _rsp: UInt64? 44 | internal var _rbp: UInt64? 45 | internal var _r8: UInt64? 46 | internal var _r9: UInt64? 47 | internal var _r10: UInt64? 48 | internal var _r11: UInt64? 49 | internal var _r12: UInt64? 50 | internal var _r13: UInt64? 51 | internal var _r14: UInt64? 52 | internal var _r15: UInt64? 53 | internal var _rip: UInt64? 54 | internal var _cr0: UInt64? 55 | internal var _cr2: UInt64? 56 | internal var _cr3: UInt64? 57 | internal var _cr4: UInt64? 58 | internal var _efer: UInt64? 59 | } 60 | -------------------------------------------------------------------------------- /Sources/HypervisorKit/RegisterSet.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RegisterSet.swift 3 | // HypervisorKit 4 | // 5 | // Created by Simon Evans on 22/03/2021. 6 | // Copyright © 2021 Simon Evans. All rights reserved. 7 | // 8 | 9 | /// A RegisterSet is used to tell the VCPU the registers to read into an internal cache between VMExits. 10 | /// Reading from and writing to registers in the VCPU is an expensive operation and this allows specifiying 11 | /// just the registers that are required to be accessed when processing a VMExit. 12 | public struct RegisterSet: OptionSet { 13 | public let rawValue: Int 14 | 15 | public init(rawValue: Int) { 16 | self.rawValue = rawValue 17 | } 18 | 19 | /// Passed in an option set to `readRegisters()` to cache the RAX register from the vCPU. 20 | public static let rax = RegisterSet(rawValue: 1 << 0) 21 | 22 | /// Passed in an option set to `readRegisters()` to cache the RBX register from the vCPU. 23 | public static let rbx = RegisterSet(rawValue: 1 << 1) 24 | 25 | /// Passed in an option set to `readRegisters()` to cache the RCX register from the vCPU. 26 | public static let rcx = RegisterSet(rawValue: 1 << 2) 27 | 28 | /// Passed in an option set to `readRegisters()` to cache the RDX register from the vCPU. 29 | public static let rdx = RegisterSet(rawValue: 1 << 3) 30 | 31 | /// Passed in an option set to `readRegisters()` to cache the RDI register from the vCPU. 32 | public static let rdi = RegisterSet(rawValue: 1 << 4) 33 | 34 | /// Passed in an option set to `readRegisters()` to cache the RSI register from the vCPU. 35 | public static let rsi = RegisterSet(rawValue: 1 << 5) 36 | 37 | /// Passed in an option set to `readRegisters()` to cache the RBP register from the vCPU. 38 | public static let rbp = RegisterSet(rawValue: 1 << 6) 39 | 40 | /// Passed in an option set to `readRegisters()` to cache the RSP register from the vCPU. 41 | public static let rsp = RegisterSet(rawValue: 1 << 7) 42 | 43 | /// Passed in an option set to `readRegisters()` to cache the R8 register from the vCPU. 44 | public static let r8 = RegisterSet(rawValue: 1 << 8) 45 | 46 | /// Passed in an option set to `readRegisters()` to cache the R9 register from the vCPU. 47 | public static let r9 = RegisterSet(rawValue: 1 << 9) 48 | 49 | /// Passed in an option set to `readRegisters()` to cache the R10 register from the vCPU. 50 | public static let r10 = RegisterSet(rawValue: 1 << 10) 51 | 52 | /// Passed in an option set to `readRegisters()` to cache the R11 register from the vCPU. 53 | public static let r11 = RegisterSet(rawValue: 1 << 11) 54 | 55 | /// Passed in an option set to `readRegisters()` to cache the R12 register from the vCPU. 56 | public static let r12 = RegisterSet(rawValue: 1 << 12) 57 | 58 | /// Passed in an option set to `readRegisters()` to cache the R13 register from the vCPU. 59 | public static let r13 = RegisterSet(rawValue: 1 << 13) 60 | 61 | /// Passed in an option set to `readRegisters()` to cache the R14 register from the vCPU. 62 | public static let r14 = RegisterSet(rawValue: 1 << 14) 63 | 64 | /// Passed in an option set to `readRegisters()` to cache the R15 register from the vCPU. 65 | public static let r15 = RegisterSet(rawValue: 1 << 15) 66 | 67 | /// Passed in an option set to `readRegisters()` to cache the RIP register from the vCPU. 68 | public static let rip = RegisterSet(rawValue: 1 << 16) 69 | 70 | /// Passed in an option set to `readRegisters()` to cache the RFLAGS from the vCPU. 71 | public static let rflags = RegisterSet(rawValue: 1 << 17) 72 | 73 | /// Passed in an option set to `readRegisters()` to cache the CR0 register from the vCPU. 74 | public static let cr0 = RegisterSet(rawValue: 1 << 18) 75 | 76 | /// Passed in an option set to `readRegisters()` to cache the CR2 register from the vCPU. 77 | public static let cr2 = RegisterSet(rawValue: 1 << 19) 78 | 79 | /// Passed in an option set to `readRegisters()` to cache the CR3 register from the vCPU. 80 | public static let cr3 = RegisterSet(rawValue: 1 << 20) 81 | 82 | /// Passed in an option set to `readRegisters()` to cache the CR4 register from the vCPU. 83 | public static let cr4 = RegisterSet(rawValue: 1 << 21) 84 | 85 | /// Passed in an option set to `readRegisters()` to cache the EFER value from the vCPU. 86 | public static let efer = RegisterSet(rawValue: 1 << 22) 87 | 88 | /// Passed in an option set to `readRegisters()` to cache the CS register from the vCPU. 89 | public static let cs = RegisterSet(rawValue: 1 << 24) 90 | 91 | /// Passed in an option set to `readRegisters()` to cache the SS register from the vCPU. 92 | public static let ss = RegisterSet(rawValue: 1 << 25) 93 | 94 | /// Passed in an option set to `readRegisters()` to cache the DS register from the vCPU. 95 | public static let ds = RegisterSet(rawValue: 1 << 26) 96 | 97 | /// Passed in an option set to `readRegisters()` to cache the ES register from the vCPU. 98 | public static let es = RegisterSet(rawValue: 1 << 27) 99 | 100 | /// Passed in an option set to `readRegisters()` to cache the FS register from the vCPU. 101 | public static let fs = RegisterSet(rawValue: 1 << 28) 102 | 103 | /// Passed in an option set to `readRegisters()` to cache the GS register from the vCPU. 104 | public static let gs = RegisterSet(rawValue: 1 << 29) 105 | 106 | /// Passed in an option set to `readRegisters()` to cache all of the segment registers from the vCPU. 107 | public static let segmentRegisters: RegisterSet = [.cs, .ss, .ds, .es, .fs, .gs] 108 | 109 | /// Passed in an option set to `readRegisters()` to cache the GDT (Global Descriptor Table) from the vCPU. 110 | public static let gdt = RegisterSet(rawValue: 1 << 30) 111 | 112 | /// Passed in an option set to `readRegisters()` to cache the IDT (Interrup Descriptor Table) from the vCPU. 113 | public static let idt = RegisterSet(rawValue: 1 << 31) 114 | 115 | /// Passed in an option set to `readRegisters()` to cache the LDTR (Local Descriptor Table Register) from the vCPU. 116 | public static let ldtr = RegisterSet(rawValue: 1 << 32) 117 | 118 | /// Passed in an option set to `readRegisters()` to cache the TR (Task Register) from the vCPU. 119 | public static let taskRegister = RegisterSet(rawValue: 1 << 33) 120 | 121 | /// Passed in an option set to `readRegisters()` to cache the all of the registers from the vCPU. 122 | public static let all = RegisterSet(rawValue: Int.max) 123 | } 124 | -------------------------------------------------------------------------------- /Sources/HypervisorKit/VMError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VMError.swift 3 | // HypervisorKit 4 | // 5 | // Created by Simon Evans on 23/03/2021. 6 | // Copyright © 2019 Simon Evans. All rights reserved. 7 | // 8 | 9 | /// A type that enumerates errors thrown by `HypervisorKit`. 10 | public enum VMError: Error, CustomStringConvertible { 11 | // General 12 | /// Failed to initialise the VM subsystem. 13 | case vmCreateVMFailure 14 | 15 | /// Errror shutting down the VM subsystem. 16 | case vmShutdownFailure 17 | 18 | /// General failure adding a vCPU to the VM. 19 | case vcpuCreateFailure 20 | 21 | /// Trying to `start()` a vCPU that is not waiting to be started. Either it is being setup or has 22 | /// already been started. 23 | case vcpuNotWaitingToStart 24 | 25 | /// Trying to `shutdown()` the VM but one or more vCPUs are still running. 26 | case vcpusStillRunning 27 | 28 | /// Trying to read or write the vCPU registers when the vCPU has already been shutdown. 29 | case vcpuHasBeenShutdown 30 | 31 | /// Error reading the registers from the vCPU. 32 | case vcpuReadRegisterFailed 33 | 34 | /// Physical address is not valid in any `MemoryRegion`. 35 | case invalidMemoryRegion 36 | 37 | /// Cannot allocate memory to add to a VM. 38 | case memoryAllocationFailure 39 | 40 | /// A `MemoryRegion` is too small to load binary data into it. 41 | case memoryRegionTooSmall 42 | 43 | /// A error occured adding a`MemoryRegion` to the VM. 44 | case addMemoryFailure 45 | 46 | // Linux specific errors 47 | 48 | /// KVM: Cannot open `/dev/kvm` 49 | case kvmCannotAccessSubsystem 50 | 51 | /// KVM: Getting API version using `KVM_GET_API_VERSION` failed or API is not version 12. 52 | case kvmApiTooOld 53 | 54 | /// KVM: Creating Virtual Machine using`KVM_CREATE_VM` failed. 55 | case kvmCannotCreateVM 56 | 57 | /// KVM: Setting `MemoryRegion` using `KVM_SET_USER_MEMORY_REGION` failed. 58 | case kvmMemoryError 59 | 60 | /// KVM: Adding virtual PIC chip using `KVM_CREATE_IRQCHIP` failed. 61 | case kvmCannotAddPic 62 | 63 | /// KVM: Adding virtual PIT chip using `KVM_CREATE_PIT2` failed. 64 | case kvmCannotAddPit 65 | 66 | /// KVM: Creating a vCPU using `KVM_CREATE_VCPU` failed. 67 | case kvmCannotCreateVcpu 68 | 69 | /// KVM: Getting vCPU `mmap` region size using `KVM_GET_VCPU_MMAP_SIZE` failed. 70 | case kvmCannotGetVcpuSize 71 | 72 | /// KVM: `mmap` of VCPU failed. 73 | case kvmCannotMmapVcpu 74 | 75 | /// KVM: Runnign vCPU using `KVM_RUN` failed. 76 | case kvmRunError 77 | 78 | /// KVM: Queuing IRQ using `KVM_INTERRUPT` returned `EEXIST`. IRQ has already been queued. 79 | case irqAlreadyQueued 80 | 81 | /// KVM: Queuing IRQ using `KVM_INTERRUPT` returned `EINVAL`. IRQ number is invalid. 82 | case irqNumberInvalid 83 | 84 | /// KVM: Queuing IRQ using `KVM_INTERRUPT` returned `ENXIO`. IRQ queuing is handled by the KVM PIC. 85 | case irqAlreadyHandledByKernelPIC 86 | 87 | /// KVM: Reading vCPU registers using`KVM_GET_REGS` failed. 88 | case kvmGetRegisters 89 | 90 | /// KVM: Writing vCPU registers using`KVM_SET_REGS` failed. 91 | case kvmSetRegisters 92 | 93 | /// KVM: Reading vCPU special registers using`KVM_GET_SREGS` failed. 94 | case kvmGetSpecialRegisters 95 | 96 | /// KVM: Writing vCPU special registers using`KVM_SET_SREGS` failed. 97 | case kvmSetSpecialRegisters 98 | 99 | // Hypervisor.framework (macOS) specific 100 | case hvError 101 | case hvBusy 102 | case hvBadArgument 103 | case hvNoResources 104 | case hvNoDevice 105 | case hvDenied 106 | case hvUnsupported 107 | case hvUnknownError(UInt32) 108 | 109 | public var description: String { 110 | switch self { 111 | case .hvDenied: return "No permission to use HVF" 112 | default: return self.localizedDescription 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Sources/HypervisorKit/VirtualMachine.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VirtualMachine.swift 3 | // HypervisorKit 4 | // 5 | // Created by Simon Evans on 01/01/2020. 6 | // Copyright © 2020 - 2022 Simon Evans. All rights reserved. 7 | // 8 | // Main class encapsulating a VM. 9 | // 10 | 11 | import Logging 12 | import Dispatch 13 | import Foundation 14 | 15 | 16 | public typealias PIOOutHandler = ((IOPort, VMExit.DataWrite) throws -> Void) 17 | public typealias PIOInHandler = ((IOPort, VMExit.DataRead) throws -> VMExit.DataWrite) 18 | public typealias MMIOOutHandler = ((PhysicalAddress, VMExit.DataWrite) throws -> Void) 19 | public typealias MMIOInHandler = ((PhysicalAddress, VMExit.DataRead) throws -> VMExit.DataWrite) 20 | 21 | /// A type representing a full VM (virtual machine) including cpus and memory. 22 | public final class VirtualMachine { 23 | private var isShutdown = true 24 | internal let logger: Logger 25 | #if os(Linux) 26 | internal var vm_fd: Int32 = -1 27 | #endif 28 | 29 | public private(set) var vcpus: [VCPU] = [] 30 | public private(set) var memoryRegions: [MemoryRegion] = [] 31 | public var pioOutHandler: PIOOutHandler? 32 | public var pioInHandler: PIOInHandler? 33 | public var mmioOutHandler: MMIOOutHandler? 34 | public var mmioInHandler: MMIOInHandler? 35 | 36 | /// Initialise the VM subsystem 37 | /// 38 | /// - parameter logger: `Logger` object for `debug` and `trace` messages. 39 | /// - throws: `VMError.vmCreateVMFailure` 40 | public init(logger: Logger) throws { 41 | self.logger = logger 42 | do { 43 | try self._createVM() 44 | isShutdown = false 45 | } catch { 46 | logger.error("Cannot create VM: \(error)") 47 | throw VMError.vmCreateVMFailure 48 | } 49 | } 50 | 51 | deinit { 52 | guard isShutdown == true else { 53 | fatalError("VM has not been shutdown().") 54 | } 55 | } 56 | 57 | /// Adds a memory region into the physical address space of the VM. 58 | /// ``` 59 | /// The VM requires at least one MemoryRegion to be added before starting the vCPU. 60 | /// ``` 61 | /// - parameter guestAddress: Physical Address inside the VM where the region will start. 62 | /// - parameter size: Size in bytes of the memory region. 63 | /// - parameter readOnly: Flag to indicate if the memory should be treated as ROM or RAM by the Virtual CPUs. 64 | /// - returns: The new `MemoryRegion`. 65 | /// - precondition: `guestAddress` must be page aligned. 66 | /// - precondition: `size` is non-zero and is a multiple of the page size of the VM. 67 | /// - throws: `VMError.addMemoryFailure`. 68 | public func addMemory(at guestAddress: UInt64, size: UInt64, readOnly: Bool = false) throws -> MemoryRegion { 69 | try addMemory(at: guestAddress, sizes: [size], readOnly: readOnly) 70 | } 71 | 72 | public func addMemory(at guestAddress: UInt64, sizes: [UInt64], readOnly: Bool = false) throws -> MemoryRegion { 73 | let totalSize = sizes.reduce(0, +) 74 | logger.trace("Adding \(totalSize) bytes at address 0x\(String(guestAddress, radix: 16))") 75 | 76 | precondition(guestAddress & 0xfff == 0) 77 | do { 78 | let memRegion = try _createMemory(at: guestAddress, sizes: sizes, readOnly: readOnly) 79 | memoryRegions.append(memRegion) 80 | logger.trace("Added memory") 81 | return memRegion 82 | } catch { 83 | logger.debug("Cannot add MemoryRegion: \(error)") 84 | throw VMError.addMemoryFailure 85 | } 86 | } 87 | 88 | /// Returns the memory region containing a specific physical address in the address space of the VM. 89 | /// - parameter guestAddress: The Physical Address in the VM. 90 | /// - returns: The `MemoryRegion` containing the address or `nil` if no region is found. 91 | public func memoryRegion(containing guestAddress: PhysicalAddress) -> MemoryRegion? { 92 | for region in memoryRegions { 93 | if region.guestAddress <= guestAddress && region.guestAddress + region.size >= guestAddress { 94 | return region 95 | } 96 | } 97 | return nil 98 | } 99 | 100 | /// Returns an `UnsafeMutableRawPointer` to a region of bytes at a specified physical address. 101 | /// - parameter guestAddress: Physical Address inside the VM where the region will start. 102 | /// - parameter count: The size in bytes of the region. 103 | /// - returns: An `UnsafeMutableRawPointer` pointing to a region of `count` bytes. 104 | /// - throws: `HVError.invalidMemory` if the region in not inside a specif `MemoryRegion`. 105 | public func memory(at guestAddress: PhysicalAddress, count: UInt64) throws -> UnsafeMutableRawPointer { 106 | for region in memoryRegions { 107 | if region.guestAddress <= guestAddress && region.guestAddress + region.size >= guestAddress + count { 108 | let offset = guestAddress - region.guestAddress 109 | return region.pointer.advanced(by: Int(offset)) 110 | } 111 | } 112 | throw VMError.invalidMemoryRegion 113 | } 114 | 115 | /// Add a VCPU to the Virtual Machine. 116 | /// ``` 117 | /// Creates a new Thread and initialises a vCPU in that thread. The `startup` function 118 | /// is executed to setup the vCPU and then it waits until the `.start()` method is called 119 | /// to begin executing code. 120 | /// ``` 121 | /// - returns: The `VCPU` that has been added to the VM. 122 | /// - throws: `VMError.vcpuCreateFailure`. 123 | @discardableResult 124 | public func addVCPU() throws -> VCPU { 125 | var vcpu: VCPU? = nil 126 | var createError: Error? = nil 127 | let semaphore = DispatchSemaphore(value: 0) 128 | 129 | let thread = Thread { 130 | do { 131 | let _vcpu = try self._createVCPU() 132 | vcpu = _vcpu 133 | _vcpu.setupRealMode() 134 | try _vcpu.preflightCheck() 135 | _vcpu.status = .waitingToStart 136 | semaphore.signal() 137 | _vcpu.runVCPU() 138 | } catch { 139 | createError = error 140 | semaphore.signal() 141 | return 142 | } 143 | } 144 | thread.start() 145 | semaphore.wait() 146 | if let error = createError { 147 | logger.debug("Cannot create VCPU: \(error)") 148 | throw VMError.vcpuCreateFailure 149 | } 150 | vcpus.append(vcpu!) 151 | return vcpu! 152 | } 153 | 154 | /// Query all of the vCPUs to determine if they have been shutdown 155 | /// ``` 156 | /// Use this method to check the shutdown status of all of the vCPUs. 157 | /// Before shutting down the VirtualMachine, all of the vCPUs must be 158 | /// in the shutdown state. 159 | /// The `.shutdownAllVcpus` method can be called to shutdown all the 160 | /// vCPUS. 161 | /// ``` 162 | /// - returns: `true` if all vCPUs have been shutdown, `false` if any are still running. 163 | public func areVcpusShutdown() -> Bool { 164 | vcpus.allSatisfy { $0.status == .shutdown } 165 | } 166 | 167 | /// Request all of the vCPUs to enter the shutdown state. 168 | /// ``` 169 | /// Use this method to request all vCPUs to shutdown. 170 | /// ``` 171 | /// - returns: `true` if all vCPUs have been shutdown, `false` if any are still running. 172 | @discardableResult 173 | public func shutdownAllVcpus() -> Bool { 174 | vcpus.forEach { _ = $0.shutdown() } 175 | return areVcpusShutdown() 176 | } 177 | 178 | /// Shutdown the VM. 179 | /// ``` 180 | /// shutdown() must be called before the VirtualMachine object is deallocated. 181 | /// Before calling, all vCPUS must be individually shutdown and a check is run 182 | /// to ensure the vCPUS are in the shutdown state. 183 | /// All MemoryRegions and VCPUS are deallocated by this method and the underlying 184 | /// hypervisor is shutdown by the OS. 185 | /// ``` 186 | /// - throws: `VMError.vcpusStillRunning` if any vCPU is still running. 187 | /// - throws: `VMError.vmShutdownFailure` if an internal subsystem error occurs. 188 | public func shutdown() throws { 189 | logger.trace("Shutting down VM - deinit") 190 | precondition(isShutdown == false) 191 | guard areVcpusShutdown() else { 192 | throw VMError.vcpusStillRunning 193 | } 194 | 195 | do { 196 | vcpus = [] 197 | for region in memoryRegions { 198 | try _destroyMemory(region: region) 199 | } 200 | memoryRegions = [] 201 | try _shutdownVM() 202 | isShutdown = true 203 | } catch { 204 | logger.debug("Error Shutting down VM: \(error)") 205 | throw VMError.vmShutdownFailure 206 | } 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /Sources/HypervisorKit/linux/kvm_registers.swift: -------------------------------------------------------------------------------- 1 | // 2 | // kvm_registers.swift 3 | // HypervisorKit 4 | // 5 | // Created by Simon Evans on 01/03/2021. 6 | // Copyright © 2021 Simon Evans. All rights reserved. 7 | // 8 | // Cached VCPU registers between VMExits. 9 | // 10 | 11 | #if os(Linux) 12 | 13 | @_implementationOnly import CHypervisorKit 14 | import BABAB 15 | 16 | extension SegmentRegister { 17 | 18 | init(_ kvmSegment: kvm_segment) { 19 | selector = kvmSegment.selector 20 | base = kvmSegment.base 21 | limit = kvmSegment.limit 22 | 23 | var bitArray = BitField32() 24 | bitArray[0...3] = UInt32(kvmSegment.type) 25 | bitArray[4] = Bool(kvmSegment.s) 26 | bitArray[5...6] = UInt32(kvmSegment.dpl) 27 | bitArray[7] = Bool(kvmSegment.present) 28 | bitArray[8...11] = 0 // reserverd 29 | bitArray[12] = Bool(kvmSegment.avl) 30 | bitArray[13] = Bool(kvmSegment.l) 31 | bitArray[14] = Bool(kvmSegment.db) 32 | bitArray[15] = Bool(kvmSegment.g) 33 | bitArray[16] = false // usable 34 | accessRights = bitArray.rawValue 35 | } 36 | 37 | var kvmSegment: kvm_segment { 38 | let bitArray = BitField32(accessRights) 39 | return kvm_segment(base: base, 40 | limit: limit, 41 | selector: selector, 42 | type: UInt8(bitArray[0...3]), 43 | present: UInt8(bitArray[7]), 44 | dpl: UInt8(bitArray[5...6]), 45 | db: UInt8(bitArray[14]), 46 | s: UInt8(bitArray[4]), 47 | l: UInt8(bitArray[13]), 48 | g: UInt8(bitArray[15]), 49 | avl: UInt8(bitArray[12]), 50 | unusable: 0, padding: 0) 51 | } 52 | } 53 | 54 | extension DescriptorTable { 55 | 56 | init(_ kvmDtable: kvm_dtable) { 57 | base = kvmDtable.base 58 | limit = kvmDtable.limit 59 | } 60 | 61 | var kvmDtable: kvm_dtable { 62 | return kvm_dtable(base: base, limit: limit, padding: (0, 0, 0)) 63 | } 64 | } 65 | 66 | internal struct RegisterCacheControl: RegisterCacheControlProtocol { 67 | 68 | private var vcpu_fd: Int32? 69 | private var _regs: kvm_regs? 70 | private var _sregs: kvm_sregs? 71 | internal var cache = RegisterCache() 72 | 73 | static let regsRegisterSet: RegisterSet = [ 74 | .rax, .rbx, .rcx, .rdx, .rsi, .rdi, .rsp, .rbp, .r8, .r9, .r10, .r11, .r12, .r13, .r14, .r15, .rip, .rflags 75 | ] 76 | static let sregsRegisterSet: RegisterSet = [ 77 | .cs, .ds, .es, .fs, .gs, .ss, .taskRegister, .ldtr, .gdt, .idt, .cr0, .cr2, .cr4, .efer 78 | ] 79 | 80 | internal init(vcpu_fd: Int32) { 81 | self.vcpu_fd = vcpu_fd 82 | } 83 | 84 | /// readRegisters(registerSet:) must be called for a specific register before reading that register so that it can be loaded11 85 | /// from the vCPU. It is not required before writing to a full width register (eg RAX) but writing to a narrower register (EAX, AX, AH, AL) 86 | /// does require it to be read first. 87 | internal mutating func readRegisters(_ registerSet: RegisterSet) throws { 88 | guard vcpu_fd != nil else { 89 | // If these values are nil then this should be a Register created when the 90 | // vcpu finished so all of the cache values should be set, so just return 91 | return 92 | } 93 | 94 | if !registerSet.isDisjoint(with: Self.regsRegisterSet) { 95 | let regs = try getRegs() 96 | if registerSet.contains(.rax), cache._rax == nil { cache._rax = regs.rax } 97 | if registerSet.contains(.rbx), cache._rbx == nil { cache._rbx = regs.rbx } 98 | if registerSet.contains(.rcx), cache._rcx == nil { cache._rcx = regs.rcx } 99 | if registerSet.contains(.rdx), cache._rdx == nil { cache._rdx = regs.rdx } 100 | if registerSet.contains(.rdi), cache._rdi == nil { cache._rdi = regs.rdi } 101 | if registerSet.contains(.rsi), cache._rsi == nil { cache._rsi = regs.rsi } 102 | if registerSet.contains(.rbp), cache._rbp == nil { cache._rbp = regs.rbp } 103 | if registerSet.contains(.rsp), cache._rsp == nil { cache._rsp = regs.rsp } 104 | if registerSet.contains(.r8), cache._r8 == nil { cache._r8 = regs.r8 } 105 | if registerSet.contains(.r9), cache._r9 == nil { cache._r9 = regs.r9 } 106 | if registerSet.contains(.r10), cache._r10 == nil { cache._r10 = regs.r10 } 107 | if registerSet.contains(.r11), cache._r11 == nil { cache._r11 = regs.r11 } 108 | if registerSet.contains(.r12), cache._r12 == nil { cache._r12 = regs.r12 } 109 | if registerSet.contains(.r13), cache._r13 == nil { cache._r13 = regs.r13 } 110 | if registerSet.contains(.r14), cache._r14 == nil { cache._r14 = regs.r14 } 111 | if registerSet.contains(.r15), cache._r15 == nil { cache._r15 = regs.r15 } 112 | if registerSet.contains(.rip), cache._rip == nil { cache._rip = regs.rip } 113 | if registerSet.contains(.rflags), cache._rflags == nil { cache._rflags = CPU.RFLAGS(regs.rflags) } 114 | } 115 | 116 | if !registerSet.isDisjoint(with: Self.sregsRegisterSet) { 117 | let sregs = try getSregs() 118 | if registerSet.contains(.cr0), cache._cr0 == nil { cache._cr0 = sregs.cr0 } 119 | if registerSet.contains(.cr2), cache._cr2 == nil { cache._cr2 = sregs.cr2 } 120 | if registerSet.contains(.cr3), cache._cr3 == nil { cache._cr3 = sregs.cr3 } 121 | if registerSet.contains(.cr4), cache._cr4 == nil { cache._cr4 = sregs.cr4 } 122 | if registerSet.contains(.efer), cache._efer == nil { cache._efer = sregs.efer } 123 | 124 | if registerSet.contains(.cs), cache._cs == nil { cache._cs = SegmentRegister(sregs.cs) } 125 | if registerSet.contains(.ss), cache._ss == nil { cache._ss = SegmentRegister(sregs.ss) } 126 | if registerSet.contains(.ds), cache._ds == nil { cache._ds = SegmentRegister(sregs.ds) } 127 | if registerSet.contains(.es), cache._es == nil { cache._es = SegmentRegister(sregs.es) } 128 | if registerSet.contains(.fs), cache._fs == nil { cache._fs = SegmentRegister(sregs.fs) } 129 | if registerSet.contains(.gs), cache._gs == nil { cache._gs = SegmentRegister(sregs.gs) } 130 | if registerSet.contains(.ldtr), cache._ldtr == nil { cache._ldtr = SegmentRegister(sregs.ldt) } 131 | if registerSet.contains(.taskRegister), cache._taskRegister == nil { cache._taskRegister = SegmentRegister(sregs.tr) } 132 | if registerSet.contains(.gdt), cache._gdt == nil { cache._gdt = DescriptorTable(sregs.gdt) } 133 | if registerSet.contains(.idt), cache._idt == nil { cache._idt = DescriptorTable(sregs.idt) } 134 | } 135 | } 136 | 137 | internal mutating func setupRegisters() throws { 138 | guard let vcpu_fd = vcpu_fd else { 139 | throw VMError.vcpuHasBeenShutdown 140 | } 141 | 142 | if !cache.updatedRegisters.isDisjoint(with: Self.regsRegisterSet) { 143 | var regs = try getRegs() 144 | if cache.updatedRegisters.contains(.rax) { regs.rax = cache._rax! } 145 | if cache.updatedRegisters.contains(.rbx) { regs.rbx = cache._rbx! } 146 | if cache.updatedRegisters.contains(.rcx) { regs.rcx = cache._rcx! } 147 | if cache.updatedRegisters.contains(.rdx) { regs.rdx = cache._rdx! } 148 | if cache.updatedRegisters.contains(.rdi) { regs.rdi = cache._rdi! } 149 | if cache.updatedRegisters.contains(.rsi) { regs.rsi = cache._rsi! } 150 | if cache.updatedRegisters.contains(.rbp) { regs.rbp = cache._rbp! } 151 | if cache.updatedRegisters.contains(.rsp) { regs.rsp = cache._rsp! } 152 | if cache.updatedRegisters.contains(.r8) { regs.r8 = cache._r8! } 153 | if cache.updatedRegisters.contains(.r9) { regs.r9 = cache._r9! } 154 | if cache.updatedRegisters.contains(.r10) { regs.r10 = cache._r10! } 155 | if cache.updatedRegisters.contains(.r11) { regs.r11 = cache._r11! } 156 | if cache.updatedRegisters.contains(.r12) { regs.r12 = cache._r12! } 157 | if cache.updatedRegisters.contains(.r13) { regs.r13 = cache._r13! } 158 | if cache.updatedRegisters.contains(.r14) { regs.r14 = cache._r14! } 159 | if cache.updatedRegisters.contains(.r15) { regs.r15 = cache._r15! } 160 | if cache.updatedRegisters.contains(.rip) { regs.rip = cache._rip! } 161 | if cache.updatedRegisters.contains(.rflags) { regs.rflags = cache._rflags!.rawValue } 162 | 163 | guard ioctl3arg(vcpu_fd, _IOCTL_KVM_SET_REGS, ®s) >= 0 else { 164 | throw VMError.kvmSetRegisters 165 | } 166 | } 167 | 168 | if !cache.updatedRegisters.isDisjoint(with: Self.sregsRegisterSet) { 169 | var sregs = try getSregs() 170 | 171 | if cache.updatedRegisters.contains(.cr0) { sregs.cr0 = cache._cr0! } 172 | if cache.updatedRegisters.contains(.cr2) { sregs.cr2 = cache._cr2! } 173 | if cache.updatedRegisters.contains(.cr3) { sregs.cr3 = cache._cr3! } 174 | if cache.updatedRegisters.contains(.cr4) { sregs.cr4 = cache._cr4! } 175 | if cache.updatedRegisters.contains(.efer) { sregs.efer = cache._efer! } 176 | if cache.updatedRegisters.contains(.cs) { sregs.cs = cache._cs!.kvmSegment } 177 | if cache.updatedRegisters.contains(.ss) { sregs.ss = cache._ss!.kvmSegment } 178 | if cache.updatedRegisters.contains(.ds) { sregs.ds = cache._ds!.kvmSegment } 179 | if cache.updatedRegisters.contains(.es) { sregs.es = cache._es!.kvmSegment } 180 | if cache.updatedRegisters.contains(.fs) { sregs.fs = cache._fs!.kvmSegment } 181 | if cache.updatedRegisters.contains(.gs) { sregs.gs = cache._gs!.kvmSegment } 182 | if cache.updatedRegisters.contains(.ldtr) { sregs.ldt = cache._ldtr!.kvmSegment } 183 | if cache.updatedRegisters.contains(.taskRegister) { sregs.tr = cache._taskRegister!.kvmSegment } 184 | if cache.updatedRegisters.contains(.gdt) { sregs.gdt = cache._gdt!.kvmDtable } 185 | if cache.updatedRegisters.contains(.idt) { sregs.idt = cache._idt!.kvmDtable } 186 | 187 | guard ioctl3arg(vcpu_fd, _IOCTL_KVM_SET_SREGS, &sregs) >= 0 else { 188 | throw VMError.kvmSetSpecialRegisters 189 | } 190 | } 191 | cache.updatedRegisters = [] 192 | } 193 | 194 | internal mutating func clearCache() { 195 | cache = RegisterCache() 196 | _regs = nil 197 | _sregs = nil 198 | } 199 | 200 | internal mutating func makeReadOnly() { 201 | self.vcpu_fd = nil 202 | } 203 | 204 | private mutating func getRegs() throws -> kvm_regs { 205 | if let regs = _regs { return regs } 206 | guard let vcpu_fd = vcpu_fd else { 207 | throw VMError.vcpuHasBeenShutdown 208 | } 209 | var regs = kvm_regs() 210 | guard ioctl3arg(vcpu_fd, _IOCTL_KVM_GET_REGS, ®s) >= 0 else { 211 | throw VMError.kvmGetRegisters 212 | } 213 | _regs = regs 214 | return regs 215 | } 216 | 217 | private mutating func getSregs() throws -> kvm_sregs { 218 | if let sregs = _sregs { return sregs } 219 | guard let vcpu_fd = vcpu_fd else { 220 | throw VMError.vcpuHasBeenShutdown 221 | } 222 | var sregs = kvm_sregs() 223 | guard ioctl3arg(vcpu_fd, _IOCTL_KVM_GET_SREGS, &sregs) >= 0 else { 224 | throw VMError.kvmGetSpecialRegisters 225 | } 226 | _sregs = sregs 227 | return sregs 228 | } 229 | } 230 | 231 | #endif 232 | -------------------------------------------------------------------------------- /Sources/HypervisorKit/linux/kvm_vcpu.swift: -------------------------------------------------------------------------------- 1 | // 2 | // kvm_vcpu.swift 3 | // HypervisorKit 4 | // 5 | // Created by Simon Evans on 26/12/2019. 6 | // Copyright © 2019 Simon Evans. All rights reserved. 7 | // 8 | 9 | #if os(Linux) 10 | 11 | @_implementationOnly import CHypervisorKit 12 | import Foundation 13 | import Dispatch 14 | 15 | 16 | extension VirtualMachine.VCPU { 17 | 18 | // This must be run on the vcpu's thread 19 | internal func preflightCheck() throws { 20 | } 21 | 22 | 23 | internal func destroy() throws { 24 | munmap(kvmRunPtr, Int(kvm_run_mmap_size)) 25 | close(vcpu_fd) 26 | } 27 | 28 | 29 | internal func runOnce() throws -> VMExit { 30 | while true { 31 | try registers.registerCacheControl.setupRegisters() 32 | 33 | try registers.registerCacheControl.readRegisters(.rflags) 34 | if registers.rflags.interruptEnable { 35 | if let irq = nextPendingIRQ() { 36 | var interrupt = kvm_interrupt(irq: UInt32(irq)) 37 | vm.logger.trace("_IOCTL_KVM_INTERRUPT: \(interrupt)") 38 | let result = ioctl3arg(vcpu_fd, _IOCTL_KVM_INTERRUPT, &interrupt) 39 | switch result { 40 | case 0: break 41 | case -EEXIST: throw VMError.irqAlreadyQueued 42 | case -EINVAL: throw VMError.irqNumberInvalid 43 | case -ENXIO: throw VMError.irqAlreadyHandledByKernelPIC 44 | default: fatalError("KVM_INTERRUPT returned \(result)") // Includes EFAULT for bad memory location 45 | } 46 | } 47 | } 48 | 49 | let ret = ioctl2arg(vcpu_fd, _IOCTL_KVM_RUN) 50 | guard ret >= 0 else { 51 | throw VMError.kvmRunError 52 | } 53 | 54 | // Reset the register cache 55 | registers.registerCacheControl.clearCache() 56 | guard let exitReason = try self.vmExit() else { continue } 57 | return exitReason 58 | } 59 | } 60 | } 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /Sources/HypervisorKit/linux/kvm_vm.swift: -------------------------------------------------------------------------------- 1 | // 2 | // kvm_vm.swift 3 | // HypervisorKit 4 | // 5 | // Created by Simon Evans on 01/12/2019. 6 | // Copyright © 2019 Simon Evans. All rights reserved. 7 | // 8 | 9 | #if os(Linux) 10 | 11 | @_implementationOnly import CHypervisorKit 12 | import Logging 13 | 14 | 15 | private let KVM_DEVICE = "/dev/kvm" 16 | 17 | extension VirtualMachine { 18 | 19 | static var apiVersion: Int32? = { 20 | return try? ioctl2arg(vmFD(), _IOCTL_KVM_GET_API_VERSION) 21 | }() 22 | 23 | 24 | static var vcpuMmapSize: Int32? = { 25 | return try? ioctl2arg(vmFD(), _IOCTL_KVM_GET_VCPU_MMAP_SIZE) 26 | }() 27 | 28 | static private var _vmfd: Int32 = -1 29 | static private func vmFD() throws -> Int32 { 30 | if _vmfd == -1 { 31 | _vmfd = open2arg(KVM_DEVICE, O_RDWR) 32 | guard _vmfd >= 0 else { 33 | throw VMError.kvmCannotAccessSubsystem 34 | } 35 | } 36 | return _vmfd 37 | } 38 | 39 | 40 | internal func _createVM() throws { 41 | let dev_fd = try Self.vmFD() 42 | guard let apiVersion = VirtualMachine.apiVersion, apiVersion >= 12 else { 43 | close(dev_fd) 44 | throw VMError.kvmApiTooOld 45 | } 46 | let fd = ioctl2arg(dev_fd, _IOCTL_KVM_CREATE_VM) 47 | guard fd >= 0 else { 48 | close(dev_fd) 49 | throw VMError.kvmCannotCreateVM 50 | } 51 | vm_fd = fd 52 | } 53 | 54 | 55 | internal func _shutdownVM() throws { 56 | close(vm_fd) 57 | vm_fd = -1 58 | } 59 | 60 | public func setMemoryRegionProtection(gpa: PhysicalAddress, size: UInt64, readable: Bool, writable: Bool) throws { 61 | if let memoryRegion = self.memoryRegion(containing: gpa) { 62 | try memoryRegion.modifySubRegion(gpa: gpa, size: size) { (subRegion) -> MemoryRegion.SubRegion in 63 | logger.debug("Updating memory protection on \(subRegion.kvmRegion) to writable: \(writable)") 64 | if subRegion.isWritable != writable { 65 | var kvmRegion = subRegion.kvmRegion 66 | // TODO: Check if a memory region can be modified without a delete first. It seemed to fail without 67 | // the delete but it could be version specific. 68 | var deleteRegion = kvmRegion 69 | deleteRegion.memory_size = 0 70 | guard ioctl3arg(vm_fd, _IOCTL_KVM_SET_USER_MEMORY_REGION, &deleteRegion) >= 0 else { 71 | logger.error("kvm: Error deleteing \(deleteRegion)") 72 | throw VMError.kvmMemoryError 73 | } 74 | kvmRegion.flags = !writable ? MemoryRegion.KVM_MEM_READONLY : 0 75 | guard ioctl3arg(vm_fd, _IOCTL_KVM_SET_USER_MEMORY_REGION, &kvmRegion) >= 0 else { 76 | logger.error("kvm: Error updating memory protection on \(kvmRegion)") 77 | throw VMError.kvmMemoryError 78 | } 79 | return MemoryRegion.SubRegion(kvmRegion: kvmRegion) 80 | } else { 81 | return subRegion 82 | } 83 | } 84 | } 85 | } 86 | 87 | internal func _createMemory(at guestAddress: UInt64, sizes: [UInt64], readOnly: Bool) throws -> MemoryRegion { 88 | let nextSlot = memoryRegions.reduce(0, { $0 + $1.subRegions.count }) 89 | let memRegion = try MemoryRegion(sizes: sizes, at: guestAddress, slot: nextSlot) 90 | 91 | for subRegion in memRegion.subRegions { 92 | var kvmRegion = subRegion.kvmRegion 93 | guard ioctl3arg(vm_fd, _IOCTL_KVM_SET_USER_MEMORY_REGION, &kvmRegion) >= 0 else { 94 | logger.error("kvm: Error setting \(kvmRegion)") 95 | throw VMError.kvmMemoryError 96 | } 97 | } 98 | return memRegion 99 | } 100 | 101 | internal func _destroyMemory(region: MemoryRegion) throws { 102 | for subRegion in region.subRegions { 103 | var kvmRegion = subRegion.kvmRegion 104 | kvmRegion.memory_size = 0 105 | guard ioctl3arg(vm_fd, _IOCTL_KVM_SET_USER_MEMORY_REGION, &kvmRegion) >= 0 else { 106 | logger.error("kvm: Error destroying \(kvmRegion)") 107 | throw VMError.kvmMemoryError 108 | } 109 | } 110 | } 111 | 112 | /// This runs inside its own thread. 113 | internal func _createVCPU() throws -> VCPU { 114 | let vcpu_fd = ioctl2arg(self.vm_fd, _IOCTL_KVM_CREATE_VCPU) 115 | guard vcpu_fd >= 0 else { 116 | throw VMError.kvmCannotCreateVcpu 117 | } 118 | return try VCPU(vm: self, vcpu_fd: vcpu_fd) 119 | } 120 | 121 | 122 | public func addPICandPIT() throws { 123 | // Enabling IRQCHIP stops vmexits due to HLT 124 | guard ioctl2arg(vm_fd, _IOCTL_KVM_CREATE_IRQCHIP) == 0 else { 125 | logger.error("Cant add IRQCHIP") 126 | throw VMError.kvmCannotAddPic 127 | } 128 | 129 | var pit_config = kvm_pit_config() 130 | guard ioctl3arg(vm_fd, _IOCTL_KVM_CREATE_PIT2, &pit_config) == 0 else { 131 | logger.error("Cant create PIT") 132 | throw VMError.kvmCannotAddPit 133 | } 134 | } 135 | } 136 | 137 | #endif 138 | -------------------------------------------------------------------------------- /Sources/HypervisorKit/linux/kvmexit.swift: -------------------------------------------------------------------------------- 1 | // 2 | // kvmexit.swift 3 | // HypervisorKit 4 | // 5 | // Created by Simon Evans on 10/12/2019. 6 | // Copyright © 2019 - 2022 Simon Evans. All rights reserved. 7 | // 8 | 9 | #if os(Linux) 10 | @_implementationOnly import CHypervisorKit 11 | 12 | typealias KVM_RUN_PTR = UnsafeMutablePointer 13 | 14 | public struct InternalError: Equatable { 15 | let subError: UInt32 16 | let nData: UInt32 17 | let data: (UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64) 18 | 19 | public static func == (lhs: Self, rhs: Self) -> Bool { 20 | (lhs.subError == rhs.subError) && (lhs.nData == rhs.nData) 21 | } 22 | } 23 | 24 | // X86 exits only 25 | enum KVMExit: UInt32 { 26 | 27 | case unknown = 0 28 | case exception = 1 29 | case io = 2 30 | case debug = 4 31 | case hlt = 5 32 | case mmio = 6 33 | case irqWindowOpen = 7 34 | case shutdown = 8 35 | case failEntry = 9 36 | case intr = 10 37 | case setTpr = 11 38 | case tprAccess = 12 39 | case nmi = 16 40 | case internalError = 17 41 | case systemEvent = 24 42 | case ioapicEoi = 26 43 | case hyperV = 27 44 | } 45 | 46 | 47 | extension VirtualMachine.VCPU { 48 | 49 | internal func vmExit() throws -> VMExit? { 50 | guard let exitReason = KVMExit(rawValue: kvmRunPtr.pointee.exit_reason) else { 51 | fatalError("Invalid KVM exit reason: \(kvmRunPtr.pointee.exit_reason)") 52 | } 53 | 54 | switch exitReason { 55 | 56 | case .unknown: 57 | return VMExit.unknown(kvmRunPtr.pointee.hw.hardware_exit_reason) 58 | 59 | case .exception: 60 | return VMExit.exception(VMExit.ExceptionInfo(exception: kvmRunPtr.pointee.ex.exception, errorCode: kvmRunPtr.pointee.ex.error_code)!) 61 | 62 | case .io: 63 | let io = kvmRunPtr.pointee.io 64 | var dataOffset = Int(io.data_offset) 65 | let bitWidth = io.size * 8 66 | 67 | if io.direction == 0 { // In 68 | if let dataRead = VMExit.DataRead(bitWidth: bitWidth) { 69 | var count = io.count 70 | while count > 0 { 71 | let data = try self.vm.pioInHandler!(io.port, dataRead) 72 | guard data.bitWidth == bitWidth else { 73 | fatalError("Bitwith mismatch, have \(data.bitWidth) want \(bitWidth)") 74 | } 75 | 76 | let ptr = UnsafeMutableRawPointer(kvmRunPtr).advanced(by: dataOffset) 77 | switch data { 78 | case .byte(let value): ptr.storeBytes(of: value, as: UInt8.self) 79 | case .word(let value): ptr.storeBytes(of: value, as: UInt16.self) 80 | case .dword(let value): ptr.storeBytes(of: value, as: UInt32.self) 81 | case .qword(_): fatalError("Illegal bitWidth \(bitWidth) for IN") 82 | } 83 | dataOffset += Int(io.size) 84 | count -= 1 85 | } 86 | } 87 | 88 | } else { 89 | var count = io.count 90 | while count > 0 { 91 | 92 | let ptr = UnsafeMutableRawPointer(kvmRunPtr).advanced(by: dataOffset) 93 | let dataWrite: VMExit.DataWrite 94 | switch bitWidth { 95 | case 8: dataWrite = .byte(ptr.load(as: UInt8.self)) 96 | case 16: dataWrite = .word(ptr.load(as: UInt16.self)) 97 | case 32: dataWrite = .dword(ptr.load(as: UInt32.self)) 98 | default: fatalError("Illegal bitWidth \(bitWidth) for OUT") 99 | } 100 | try self.vm.pioOutHandler!(io.port, dataWrite) 101 | dataOffset += Int(io.size) 102 | count -= 1 103 | } 104 | } 105 | return nil 106 | 107 | case .debug: 108 | let debugInfo = kvmRunPtr.pointee.debug.arch 109 | return .debug(VMExit.Debug(rip: debugInfo.pc, dr6: debugInfo.dr6, dr7: debugInfo.dr7, exception: debugInfo.exception)) 110 | 111 | case .hlt: 112 | return VMExit.hlt 113 | 114 | case .mmio: 115 | let mmio = kvmRunPtr.pointee.mmio 116 | let address = PhysicalAddress(mmio.phys_addr) 117 | if Bool(mmio.is_write) { 118 | switch mmio.len { 119 | case 1: return .mmioWriteOperation(address, .byte(mmio.data.0)) 120 | case 2: return .mmioWriteOperation(address, .word(UInt16(bytes: (mmio.data.0, mmio.data.1)))) 121 | case 4: return .mmioWriteOperation(address, .dword(UInt32(bytes: (mmio.data.0, mmio.data.1, mmio.data.2, mmio.data.3)))) 122 | case 8: return .mmioWriteOperation(address, .qword(UInt64(bytes: mmio.data))) 123 | default: break 124 | } 125 | } else { 126 | switch mmio.len { 127 | case 1: return .mmioReadOperation(address, .byte) 128 | case 2: return .mmioReadOperation(address, .word) 129 | case 4: return .mmioReadOperation(address, .dword) 130 | case 8: return .mmioReadOperation(address, .qword) 131 | default: break 132 | } 133 | } 134 | fatalError("Cant handle MMIO exit: \(mmio)") 135 | 136 | 137 | case .irqWindowOpen: 138 | return VMExit.irqWindowOpen 139 | 140 | case .shutdown: 141 | return VMExit.shutdown 142 | 143 | case .failEntry: 144 | return VMExit.entryFailed(kvmRunPtr.pointee.fail_entry.hardware_entry_failure_reason) 145 | 146 | case .intr: 147 | return VMExit.interrupt 148 | 149 | case .setTpr: 150 | return VMExit.setTpr 151 | 152 | case .tprAccess: 153 | return VMExit.tprAccess(VMExit.TPRAccess()) 154 | 155 | case .nmi: 156 | return VMExit.nmi 157 | 158 | case .internalError: 159 | let error = kvmRunPtr.pointee.internal 160 | return VMExit.internalError(InternalError(subError: error.suberror, nData: error.ndata, data: error.data)) 161 | 162 | case .systemEvent: 163 | return VMExit.systemEvent(VMExit.SystemEvent()) 164 | 165 | case .ioapicEoi: 166 | return VMExit.ioapicEOI(kvmRunPtr.pointee.eoi.vector) 167 | 168 | case .hyperV: 169 | return VMExit.hyperV(VMExit.HyperV()) 170 | } 171 | } 172 | } 173 | 174 | #endif 175 | 176 | -------------------------------------------------------------------------------- /Sources/HypervisorKit/macos/emulate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // emulate.swift 3 | // HypervisorKit 4 | // 5 | // Created by Simon Evans on 30/07/2022. 6 | // Copyright © 2022 Simon Evans. All rights reserved. 7 | // 8 | // Emulate a CPU instruction used for MMIO. 9 | // 10 | 11 | #if os(macOS) 12 | 13 | import BABAB 14 | 15 | extension VirtualMachine.VCPU { 16 | func emulateInstruction() throws { 17 | let instrLen = try vmcs.vmExitInstructionLength() 18 | precondition(instrLen <= 15) 19 | try registers.registerCacheControl.readRegisters(.rip) 20 | let gpa = try vmcs.guestPhysicalAddress() 21 | 22 | let cr0 = try vmcs.guestCR0() 23 | 24 | let rip = try vmcs.guestCSBase() + registers.rip 25 | let ripGpa: UInt64 26 | if cr0.paging { 27 | ripGpa = translateAddress(rip) 28 | } else { 29 | ripGpa = registers.rip 30 | } 31 | vm.logger.debug("RIP: \(registers.rip.hex()) => \(ripGpa.hex())") 32 | 33 | // Read the instruction 34 | let pointer = try vm.memory(at: PhysicalAddress(ripGpa), count: UInt64(instrLen)) 35 | let buffer = UnsafeRawBufferPointer(start: pointer, count: Int(instrLen)) 36 | let instruction = Array(buffer) 37 | vm.logger.debug("instruction: \(hexDump(instruction, startAddress: ripGpa))") 38 | 39 | switch instruction[0] { 40 | case 0xa1 where instrLen == 5: // mov eax, 32bit address 41 | let addr = UnsafeRawPointer(pointer).unalignedLoad(fromByteOffset: 1, as: UInt32.self) 42 | precondition(PhysicalAddress(UInt64(addr)) == gpa) 43 | let result = try self.mmioRead(gpa: gpa, operation: .dword) 44 | switch result { 45 | case .dword(let value): 46 | vm.logger.debug("Read value: \(value.hex())") 47 | try registers.registerCacheControl.readRegisters(.rax) 48 | registers.eax = value 49 | default: break 50 | } 51 | 52 | case 0xa3 where instrLen == 5: // 53 | let addr = UnsafeRawPointer(pointer).unalignedLoad(fromByteOffset: 1, as: UInt32.self) 54 | precondition(PhysicalAddress(UInt64(addr)) == gpa) 55 | try registers.registerCacheControl.readRegisters(.rax) 56 | try self.mmioWrite(gpa: gpa, operation: .dword(registers.eax)) 57 | 58 | case 0xc7 where instruction[1] == 0x5 && instrLen == 10: 59 | let addr = UnsafeRawPointer(pointer).unalignedLoad(fromByteOffset: 2, as: UInt32.self) 60 | let value = UnsafeRawPointer(pointer).unalignedLoad(fromByteOffset: 6, as: UInt32.self) 61 | precondition(PhysicalAddress(UInt64(addr)) == gpa) 62 | try self.mmioWrite(gpa: gpa, operation: .dword(value)) 63 | 64 | default: fatalError("Unimplemented instruction") 65 | } 66 | registers.rip += UInt64(instrLen) 67 | 68 | return 69 | } 70 | 71 | func translateAddress(_ address: UInt64) -> UInt64 { 72 | fatalError("todo") 73 | } 74 | 75 | 76 | private func mmioRead(gpa: PhysicalAddress, operation: VMExit.DataRead) throws -> VMExit.DataWrite { 77 | if gpa.value >= 0xfee00000, gpa.value <= 0xfee01000 { 78 | return apicRead(gpa: gpa, operation: operation) 79 | } 80 | let result = try self.vm.mmioInHandler!(gpa, operation) 81 | guard operation.bitWidth == result.bitWidth else { 82 | fatalError("MMIOIn GPA: \(gpa) bitWidth mismatch, requested \(operation) result: \(operation))") 83 | } 84 | return result 85 | } 86 | 87 | private func mmioWrite(gpa: PhysicalAddress, operation: VMExit.DataWrite) throws { 88 | if gpa.value >= 0xfee00000, gpa.value <= 0xfee01000 { 89 | return apicWrite(gpa: gpa, operation: operation) 90 | } 91 | try self.vm.mmioOutHandler!(gpa, operation) 92 | } 93 | 94 | 95 | private func apicRead(gpa: PhysicalAddress, operation: VMExit.DataRead) -> VMExit.DataWrite { 96 | guard operation == .dword else { 97 | vm.logger.error("APIC read @ \(gpa) not 32bit: \(operation)") 98 | return .dword(UInt32.max) 99 | } 100 | switch gpa.value { 101 | case 0xfee000f0: return .dword(apicSVR) 102 | case 0xfee00300: return .dword(apicLoIcr) 103 | case 0xfee00310: return .dword(apicHiIcr) 104 | case 0xfee00350: return .dword(apicLint0) 105 | case 0xfee00360: return .dword(apicLint1) 106 | default: fatalError("apicRead unhandled GPA: \(gpa)") 107 | } 108 | } 109 | 110 | 111 | private func apicWrite(gpa: PhysicalAddress, operation: VMExit.DataWrite) { 112 | guard case let .dword(value) = operation else { 113 | self.vm.logger.error("APIC read @ \(gpa) not 32bit: \(operation)") 114 | return 115 | } 116 | switch gpa.value { 117 | case 0xfee000f0: apicSVR = value 118 | case 0xfee00300: apicLoIcr = value 119 | case 0xfee00310: apicHiIcr = value 120 | case 0xfee00350: apicLint0 = value 121 | case 0xfee00360: apicLint1 = value 122 | default: fatalError("apicRead unhandled GPA: \(gpa)") 123 | } 124 | } 125 | } 126 | 127 | #endif 128 | -------------------------------------------------------------------------------- /Sources/HypervisorKit/macos/hvf_vcpu.swift: -------------------------------------------------------------------------------- 1 | // 2 | // hvf_vcpu.swift 3 | // HypervisorKit 4 | // 5 | // Created by Simon Evans on 08/12/2019. 6 | // Copyright © 2019 Simon Evans. All rights reserved. 7 | // 8 | 9 | #if os(macOS) 10 | 11 | import Hypervisor 12 | import Foundation 13 | 14 | extension VirtualMachine.VCPU { 15 | 16 | // This must be run on the vcpu's thread. It is run after the vCPU has been setup but before 17 | // being run for the first time. 18 | internal func preflightCheck() throws { 19 | try registers.registerCacheControl.setupRegisters() 20 | try vmcs.checkFieldsAreValid() 21 | } 22 | 23 | internal func destroy() throws { 24 | try hvError(hv_vcpu_destroy(vcpuId)) 25 | } 26 | 27 | // FIXME, runOnce should only run once. 28 | internal func runOnce() throws -> VMExit { 29 | 30 | var activityState = try vmcs.guestActivityState() 31 | while true { 32 | try registers.registerCacheControl.setupRegisters() 33 | 34 | try registers.registerCacheControl.readRegisters(.rflags) 35 | if registers.rflags.interruptEnable { 36 | if hltState { 37 | // TODO, better check for NMI/IRQ and STI 38 | vm.logger.trace("In HLT state waiting for IRQ") 39 | waitForPendingIRQ() 40 | vm.logger.trace("IRQ is not pending") 41 | hltState = false 42 | } 43 | if let irq = nextPendingIRQ() { 44 | let interruptInfo = VMCS.VMEntryInterruptionInfoField(vector: irq, type: .external, deliverErrorCode: false) 45 | vm.logger.trace("Injecting interrupt: \(interruptInfo)") 46 | try vmcs.vmEntryInterruptInfo(interruptInfo) 47 | var interruptibilityState = try vmcs.guestInterruptibilityState() 48 | interruptibilityState.blockingBySTI = false 49 | interruptibilityState.blockingByMovSS = false 50 | try vmcs.guestInterruptibilityState(interruptibilityState) 51 | if vm.logger.logLevel <= .debug { 52 | try vmcs.checkFieldsAreValid() 53 | } 54 | } 55 | } 56 | 57 | try hvError(hv_vcpu_run(vcpuId)) 58 | // Reset the register cache 59 | registers.registerCacheControl.clearCache() 60 | 61 | exitCount += 1 62 | activityState = try vmcs.guestActivityState() 63 | if activityState == .shutdown { return .shutdown } 64 | guard let exitReason = try self.vmExit() else { continue } 65 | return exitReason 66 | } 67 | } 68 | 69 | 70 | internal func skipInstruction() throws { 71 | let instrLen = try vmcs.vmExitInstructionLength() 72 | try registers.registerCacheControl.readRegisters(.rip) 73 | registers.rip += UInt64(instrLen) 74 | } 75 | } 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /Sources/HypervisorKit/macos/hvf_vm.swift: -------------------------------------------------------------------------------- 1 | // 2 | // hvf_vm.swift 3 | // HypervisorKit 4 | // 5 | // Created by Simon Evans on 01/12/2019. 6 | // Copyright © 2019 Simon Evans. All rights reserved. 7 | // 8 | 9 | #if os(macOS) 10 | 11 | import Hypervisor 12 | import Logging 13 | 14 | 15 | // Hypervisor Framework return codes 16 | func hvError(_ error: hv_return_t) throws { 17 | let error = UInt32(bitPattern: error) 18 | switch error { 19 | case 0: return // HV_SUCCESS 20 | case 0xfae94001: throw VMError.hvError 21 | case 0xfae94002: throw VMError.hvBusy 22 | case 0xfae94003: throw VMError.hvBadArgument 23 | case 0xfae94005: throw VMError.hvNoResources 24 | case 0xfae94006: throw VMError.hvNoDevice 25 | case 0xfae94007: throw VMError.hvDenied 26 | case 0xfae9400f: throw VMError.hvUnsupported 27 | default: throw VMError.hvUnknownError(error) 28 | } 29 | } 30 | 31 | 32 | extension VirtualMachine { 33 | 34 | static private(set) var vmx_cap_pinbased: UInt64 = 0 35 | static private(set) var vmx_cap_procbased: UInt64 = 0 36 | static private(set) var vmx_cap_procbased2: UInt64 = 0 37 | static private(set) var vmx_cap_entry: UInt64 = 0 38 | static private(set) var vmx_cap_exit: UInt64 = 0 39 | 40 | 41 | internal func _createVM() throws { 42 | 43 | func printCap(_ name: String, _ value: UInt64) { 44 | let hi = String(UInt32(value >> 32), radix: 16) 45 | let lo = String(UInt32(value & 0xffff_ffff), radix: 16) 46 | logger.debug("\(name): \(hi)\t\(lo)") 47 | } 48 | 49 | var vmCreated = false 50 | do { 51 | try hvError(hv_vm_create(hv_vm_options_t(HV_VM_DEFAULT))) 52 | vmCreated = true 53 | /* get hypervisor enforced capabilities of the machine, (see Intel docs) */ 54 | try hvError(hv_vmx_read_capability(HV_VMX_CAP_PINBASED, &VirtualMachine.vmx_cap_pinbased)) 55 | try hvError(hv_vmx_read_capability(HV_VMX_CAP_PROCBASED, &VirtualMachine.vmx_cap_procbased)) 56 | try hvError(hv_vmx_read_capability(HV_VMX_CAP_PROCBASED2, &VirtualMachine.vmx_cap_procbased2)) 57 | try hvError(hv_vmx_read_capability(HV_VMX_CAP_ENTRY, &VirtualMachine.vmx_cap_entry)) 58 | try hvError(hv_vmx_read_capability(HV_VMX_CAP_EXIT, &VirtualMachine.vmx_cap_exit)) 59 | } catch { 60 | if vmCreated { 61 | hv_vm_destroy() 62 | } 63 | throw error 64 | } 65 | } 66 | 67 | 68 | internal func _shutdownVM() throws { 69 | try hvError(hv_vm_destroy()) 70 | } 71 | 72 | public func setMemoryRegionProtection(gpa: PhysicalAddress, size: UInt64, readable: Bool, writable: Bool) throws { 73 | if let memoryRegion = self.memoryRegion(containing: gpa) { 74 | try memoryRegion.modifySubRegion(gpa: gpa, size: size) { (subRegion) -> MemoryRegion.SubRegion in 75 | if subRegion.isReadable != readable || subRegion.isWritable != writable { 76 | var flags = HV_MEMORY_EXEC 77 | if readable { 78 | flags |= HV_MEMORY_READ 79 | } 80 | if writable { 81 | flags |= HV_MEMORY_WRITE 82 | } 83 | try hvError(hv_vm_protect(gpa.value, Int(size), hv_memory_flags_t(flags))) 84 | var newSubRegion = subRegion 85 | newSubRegion.isReadable = readable 86 | newSubRegion.isWritable = writable 87 | return newSubRegion 88 | } else { 89 | return subRegion 90 | } 91 | } 92 | } 93 | } 94 | 95 | internal func _createMemory(at guestAddress: UInt64, sizes: [UInt64], readOnly: Bool) throws -> MemoryRegion { 96 | return try MemoryRegion(sizes: sizes, at: guestAddress, readOnly: readOnly) 97 | } 98 | 99 | internal func _destroyMemory(region: MemoryRegion) throws { 100 | try hvError(hv_vm_unmap(region.guestAddress.rawValue, Int(region.size))) 101 | } 102 | 103 | 104 | /// This runs inside its own thread. 105 | internal func _createVCPU() throws -> VCPU { 106 | try VCPU.init(vm: self) 107 | } 108 | 109 | } 110 | 111 | #endif 112 | -------------------------------------------------------------------------------- /Sources/HypervisorKit/macos/vmx.swift: -------------------------------------------------------------------------------- 1 | // 2 | // vmx.swift 3 | // HypervisorKit 4 | // 5 | // Created by Simon Evans on 10/12/2019. 6 | // Copyright © 2019 Simon Evans. All rights reserved. 7 | // 8 | 9 | #if os(macOS) 10 | 11 | import BABAB 12 | 13 | struct VMXExit: Error { 14 | let value: BitField32 15 | 16 | var exitReason: VMXExitReason { VMXExitReason(rawValue: UInt16(value[0...15]))! } 17 | var vmExitInEnclaveMode: Bool { Bool(value[27]) } 18 | var pendingMTFvmExit: Bool { Bool(value[28]) } 19 | var vmExitFromVMXrootOperation: Bool { Bool(value[29]) } 20 | var vmEntryFailure: Bool { Bool(value[31]) } 21 | 22 | init(_ result: UInt32) { 23 | value = BitField32(result) 24 | } 25 | } 26 | 27 | enum VMXError: Error, Equatable { 28 | 29 | enum VMFailValidError: UInt32 { 30 | case vmcallInVMXRootOperation = 1 31 | case vmclearWithInvalidAddress = 2 32 | case vmclearWithVxmonPointer = 3 33 | case vmlaunchWithNonClearVMCS = 4 34 | case vmresumeWithNonLaunchedVMCS = 5 35 | case vmresumeWithVMXOFF = 6 36 | case vmentryWithInvalidControlField = 7 37 | case vmentryWithInvalidHostStateField = 8 38 | case vmptrldWithInvalidPhysicalAddress = 9 39 | case vmptrldWithVxmonPointer = 10 40 | case vmptrldWithIncorrectVMCSRevisionId = 11 41 | case readWriteUsingUnsupportedVMCSComponent = 12 42 | case vmwriteToReadonlyComponent = 13 43 | case vxmonExecutedInVMXRootOperation = 15 44 | case vmentryWithInvalidExecutiveVMCSPointer = 16 45 | case vmentryWithNonLaunchedExecutiveVMCS = 17 46 | case vmentryWithExecutiveVMCSPointer = 18 47 | case vmcallWithNonClearVMCS = 19 48 | case vmcallWithInvalidVMExitControlFields = 20 49 | case vmcallWithIncorrectMSEGRevisionId = 22 50 | case vmxoffUnderDualMonitorTreatment = 23 51 | case vmcallWithInvalidSMMMonitorFeatures = 24 52 | case vmentryWithInvalidVMExecutionControlFields = 25 53 | case vmentryWithEventsBlockedByMOVSS = 26 54 | case InvalidOperandToInveptOrInvvpid = 28 55 | case unknownError = 0xffff 56 | } 57 | 58 | 59 | case vmSucceed 60 | case vmFailInvalid 61 | case vmFailValid(UInt32) //(VMFailValidError) 62 | case vmEntryFailure(VMXExitReason) 63 | 64 | init(_ error: UInt64) { 65 | switch(error) { 66 | case 0x0: self = .vmSucceed 67 | case 0x1: self = .vmFailInvalid 68 | default: fatalError("Invalid VMX error state: \(String(error, radix: 16))") 69 | } 70 | } 71 | } 72 | 73 | enum VMXExitReason: UInt16, CustomStringConvertible { 74 | case exceptionOrNMI = 0 75 | case externalINT = 1 76 | case tripleFault = 2 77 | case initSignal = 3 78 | case startupIPI = 4 79 | case ioSMI = 5 80 | case otherSMI = 6 81 | case intWindow = 7 82 | case nmiWindow = 8 83 | case taskSwitch = 9 84 | case cpuid = 10 85 | case getsec = 11 86 | case hlt = 12 87 | case invd = 13 88 | case invlpg = 14 89 | case rdpmc = 15 90 | case rdtsc = 16 91 | case rsm = 17 92 | case vmcall = 18 93 | case vmclear = 19 94 | case vmlaunch = 20 95 | case vmptrld = 21 96 | case vmptrst = 22 97 | case vmread = 23 98 | case vmresume = 24 99 | case vmwrite = 25 100 | case vmxoff = 26 101 | case vmxon = 27 102 | case crAccess = 28 103 | case drAccess = 29 104 | case ioInstruction = 30 105 | case rdmsr = 31 106 | case wrmsr = 32 107 | case vmentryFailInvalidGuestState = 33 108 | case vmentryFailMSRLoading = 34 109 | case mwait = 36 110 | case monitorTrapFlag = 37 111 | case monitor = 39 112 | case pause = 40 113 | case vmentryFailMCE = 41 114 | case tprBelowThreshold = 43 115 | case apicAccess = 44 116 | case virtualisedEOI = 45 117 | case accessToGDTRorIDTR = 46 118 | case accessToLDTRorTR = 47 119 | case eptViolation = 48 120 | case eptMisconfiguration = 49 121 | case invept = 50 122 | case rdtscp = 51 123 | case vmxPreemptionTimerExpired = 52 124 | case invvpid = 53 125 | case wbinvd = 54 126 | case xsetbv = 55 127 | case apicWrite = 56 128 | case rdrand = 57 129 | case invpcid = 58 130 | case vmfunc = 59 131 | case encls = 60 132 | case rdseed = 61 133 | case pmlFull = 62 134 | case xsaves = 63 135 | case xrstors = 64 136 | case subPagePermissionEvent = 66 137 | case umwait = 67 138 | case tpause = 68 139 | 140 | var description: String { 141 | switch self { 142 | case .exceptionOrNMI: 143 | return "Exception or NMI" 144 | case .externalINT: 145 | return "External Interrupt" 146 | case .tripleFault: 147 | return "Triple Fault" 148 | case .initSignal: 149 | return "INIT Signal arrived" 150 | case .startupIPI: 151 | return "Start-up IPI arrived" 152 | case .ioSMI: 153 | return "I/O SMI arrived" 154 | case .otherSMI: 155 | return "Non-I/O SMI arrived" 156 | case .intWindow: 157 | return "Interrupt window" 158 | case .nmiWindow: 159 | return "NMI window" 160 | case .taskSwitch: 161 | return "Guest attempted Task Switch" 162 | case .cpuid: 163 | return "Guest attempted CPUID" 164 | case .getsec: 165 | return "Guest attempted GETSEC" 166 | case .hlt: 167 | return "Guest attempted HLT" 168 | case .invd: 169 | return "Guest attempted INVD" 170 | case .invlpg: 171 | return "Guest attempted INVLPG" 172 | case .rdpmc: 173 | return "Guest attempted RDPMC" 174 | case .rdtsc: 175 | return "Guest attempted RSTSC" 176 | case .rsm: 177 | return "Guest attempted RSM" 178 | case .vmcall: 179 | return "Guest executed VMCALL" 180 | case .vmclear: 181 | return "Guest attempted VMCLEAR" 182 | case .vmlaunch: 183 | return "Guest attempted VMLAUNCH" 184 | case .vmptrld: 185 | return "Guest attempted VMPTRLD" 186 | case .vmptrst: 187 | return "Guest attempted VMPTRST" 188 | case .vmread: 189 | return "Guest attempted VMMREAD" 190 | case .vmresume: 191 | return "Guest attempted VMRESUME" 192 | case .vmwrite: 193 | return "Guest attempted VMWRITE" 194 | case .vmxoff: 195 | return "Guest attempted VMXOFF" 196 | case .vmxon: 197 | return "Guest attempted VMXON" 198 | case .crAccess: 199 | return "Guest attempted CR access" 200 | case .drAccess: 201 | return "Guest attempted DR access" 202 | case .ioInstruction: 203 | return "Guest attempted I/O instruction" 204 | case .rdmsr: 205 | return "Guest attempted RDMSR" 206 | case .wrmsr: 207 | return "Guest attempted WRMSR" 208 | case .vmentryFailInvalidGuestState: 209 | return "VMEntry failed due to invalid Guest State" 210 | case .vmentryFailMSRLoading: 211 | return "VMEntry failed due to MSR loading" 212 | case .mwait: 213 | return "Guest attempted MWAIT" 214 | case .monitorTrapFlag: 215 | return "Monitor trap flag" 216 | case .monitor: 217 | return "Guest attempted MONITOR" 218 | case .pause: 219 | return "Guest attempted PAUSE" 220 | case .vmentryFailMCE: 221 | return "VMEntry failed due to MCE" 222 | case .tprBelowThreshold: 223 | return "TPR below threshold" 224 | case .apicAccess: 225 | return "Guest attempted APIC access" 226 | case .virtualisedEOI: 227 | return "Virtualised EOI" 228 | case .accessToGDTRorIDTR: 229 | return "Guest attempted access to GDTR or IDTR" 230 | case .accessToLDTRorTR: 231 | return "Guest attempted access to LDTR or TR" 232 | case .eptViolation: 233 | return "EPT violation" 234 | case .eptMisconfiguration: 235 | return "EPT miscconfiguration" 236 | case .invept: 237 | return "Guest attempted INVEPT" 238 | case .rdtscp: 239 | return "Guest attempted RDTSCP" 240 | case .vmxPreemptionTimerExpired: 241 | return "VMX-preemption timer expired" 242 | case .invvpid: 243 | return "Guest attempted INVVPID" 244 | case .wbinvd: 245 | return "Guest attempted WBINVD" 246 | case .xsetbv: 247 | return "Guest attempted XSETBV" 248 | case .apicWrite: 249 | return "APIC write" 250 | case .rdrand: 251 | return "Guest attempted RDRAND" 252 | case .invpcid: 253 | return "Guest attempted INVPCID" 254 | case .vmfunc: 255 | return "Guest attempted VMFUNC" 256 | case .encls: 257 | return "Guest attempted ENCLS" 258 | case .rdseed: 259 | return "Guest attempted RDSEED" 260 | case .pmlFull: 261 | return "Page modification log full" 262 | case .xsaves: 263 | return "Guest attempted XSAVES" 264 | case .xrstors: 265 | return "Guest attempted XRSTORS" 266 | case .subPagePermissionEvent: 267 | return "Sub-page permission event" 268 | case .umwait: 269 | return "Guest attempted UMWAIT" 270 | case .tpause: 271 | return "Guest attempted TPAUSE" 272 | } 273 | } 274 | } 275 | 276 | #endif 277 | -------------------------------------------------------------------------------- /Sources/HypervisorKit/memoryregion.swift: -------------------------------------------------------------------------------- 1 | // 2 | // memoryregion.swift 3 | // HypervisorKit 4 | // 5 | // Created by Simon Evans on 27/12/2019. 6 | // Copyright © 2019 Simon Evans. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | @_implementationOnly import CHypervisorKit 11 | import BABAB 12 | 13 | #if os(macOS) 14 | import Hypervisor 15 | #endif 16 | 17 | 18 | public final class MemoryRegion { 19 | 20 | #if os(macOS) 21 | struct SubRegion { 22 | let pointer: UnsafeMutableRawPointer 23 | let guestAddress: PhysicalAddress 24 | let size: UInt64 25 | var isReadable: Bool 26 | var isWritable: Bool 27 | var rawBuffer: UnsafeMutableRawBufferPointer { 28 | UnsafeMutableRawBufferPointer(start: pointer, count: Int(size)) 29 | } 30 | } 31 | #endif 32 | 33 | #if os(Linux) 34 | struct SubRegion { 35 | internal let kvmRegion: kvm_userspace_memory_region 36 | var pointer: UnsafeMutableRawPointer { UnsafeMutableRawPointer(bitPattern: UInt(kvmRegion.userspace_addr))! } 37 | var guestAddress: PhysicalAddress { PhysicalAddress(kvmRegion.guest_phys_addr) } 38 | var size: UInt64 { kvmRegion.memory_size } 39 | let isReadable = true 40 | var isWritable: Bool { kvmRegion.flags & KVM_MEM_READONLY == 0 } 41 | var rawBuffer: UnsafeMutableRawBufferPointer { 42 | UnsafeMutableRawBufferPointer(start: pointer, count: Int(size)) 43 | } 44 | } 45 | #endif 46 | 47 | 48 | static private let pageSize = 4096 49 | private(set) internal var subRegions: [SubRegion] 50 | internal let pointer: UnsafeMutableRawPointer 51 | public var rawBuffer: UnsafeMutableRawBufferPointer { UnsafeMutableRawBufferPointer(start: pointer, count: Int(size)) } 52 | 53 | public let guestAddress: PhysicalAddress 54 | public let size: UInt64 55 | 56 | #if os(macOS) 57 | 58 | // If memory region is not read-only, then set a flag when a page in the memory region is first written to. 59 | private var dirtyPageLog: [Bool] = [] 60 | private let pageCount: Int 61 | 62 | 63 | init(sizes: [UInt64], at address: RawAddress, readOnly: Bool = false) throws { 64 | precondition(address & 0xfff == 0) 65 | precondition(!sizes.isEmpty) 66 | 67 | subRegions = [] 68 | subRegions.reserveCapacity(sizes.count) 69 | var totalSize = 0 70 | for size in sizes { 71 | precondition(size & 0xfff == 0) 72 | totalSize += Int(size); 73 | } 74 | 75 | // 4KB Aligned memory 76 | var ptr: UnsafeMutableRawPointer? = nil 77 | 78 | guard posix_memalign(&ptr, MemoryRegion.pageSize, totalSize) == 0, let _pointer = ptr else { 79 | throw VMError.memoryAllocationFailure 80 | } 81 | pointer = _pointer 82 | pointer.initializeMemory(as: UInt8.self, repeating: 0, count: totalSize) 83 | 84 | let flags: hv_memory_flags_t 85 | if readOnly { 86 | flags = hv_memory_flags_t(HV_MEMORY_READ | HV_MEMORY_EXEC) 87 | } else { 88 | flags = hv_memory_flags_t(HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC) 89 | } 90 | 91 | var offset: UInt64 = 0 92 | for size in sizes { 93 | let ptr = pointer.advanced(by: Int(offset)) 94 | let guestAddress = address + offset 95 | try hvError(hv_vm_map(ptr, guestAddress, Int(size), flags)) 96 | let subRegion = SubRegion(pointer: ptr, 97 | guestAddress: PhysicalAddress(guestAddress), 98 | size: size, 99 | isReadable: true, 100 | isWritable: !readOnly) 101 | subRegions.append(subRegion) 102 | offset += size 103 | } 104 | 105 | guestAddress = PhysicalAddress(address) 106 | self.size = UInt64(totalSize) 107 | self.pageCount = (totalSize + MemoryRegion.pageSize - 1) / MemoryRegion.pageSize 108 | dirtyPageLog.reserveCapacity(pageCount) 109 | for _ in 0.. String { 190 | let ptr = self.rawBuffer.baseAddress!.advanced(by: offset) 191 | let buffer = UnsafeRawBufferPointer(start: ptr, count: count) 192 | 193 | var idx = 0 194 | var output = "\(UInt16(offset + idx).hex()): " 195 | for byte in buffer { 196 | output += byte.hex() 197 | output += " " 198 | idx += 1 199 | if idx == count { break } 200 | if idx.isMultiple(of: 16) { 201 | output += "\n\(UInt16(offset + idx).hex()): " 202 | } 203 | } 204 | return output 205 | } 206 | 207 | public func isAddressReadable(gpa: PhysicalAddress) -> Bool { 208 | if let index = findSubRegionIndex(containing: gpa) { 209 | return subRegions[index].isReadable 210 | } else { 211 | return false 212 | } 213 | } 214 | 215 | public func isAddressWritable(gpa: PhysicalAddress) -> Bool { 216 | if let index = findSubRegionIndex(containing: gpa) { 217 | return subRegions[index].isWritable 218 | } else { 219 | return false 220 | } 221 | } 222 | 223 | internal func modifySubRegion(gpa: PhysicalAddress, size: UInt64, modifier: (SubRegion) throws -> SubRegion) throws { 224 | if let index = findSubRegionIndex(containing: gpa) { 225 | let region = self.subRegions[index] 226 | if region.guestAddress == gpa && region.size == size { 227 | self.subRegions[index] = try modifier(region) 228 | return 229 | } 230 | } 231 | throw VMError.invalidMemoryRegion 232 | } 233 | 234 | private func findSubRegionIndex(containing gpa: PhysicalAddress) -> Int? { 235 | for index in subRegions.startIndex..= region.guestAddress && gpa < region.guestAddress + region.size { 238 | return index 239 | } 240 | } 241 | return nil 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /Sources/HypervisorKit/physicaladdress.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PhysicalAddress.swift 3 | // HypervisorKit 4 | // 5 | // Created by Simon Evans on 14/12/2019. 6 | // Copyright © 2019 Simon Evans. All rights reserved. 7 | // 8 | 9 | import BABAB 10 | 11 | public typealias RawAddress = UInt64 12 | //typealias VirtualAddress = UInt64 13 | public typealias LinearAddress = UInt64 14 | 15 | 16 | let PAGE_SIZE = 4096 17 | let PAGE_MASK = PAGE_SIZE - 1 18 | 19 | public struct PhysicalAddress: Comparable, Hashable, CustomStringConvertible { 20 | let rawValue: RawAddress 21 | var value: UInt64 { rawValue } 22 | 23 | public var description: String { 24 | return "0x\(String(rawValue, radix: 16))" 25 | } 26 | 27 | public init(_ rawValue: UInt64) { 28 | self.rawValue = RawAddress(rawValue) 29 | } 30 | 31 | public init(_ rawValue: UInt) { 32 | self.rawValue = RawAddress(rawValue) 33 | } 34 | 35 | public func isAligned(to size: Int) -> Bool { 36 | precondition(size.nonzeroBitCount == 1) 37 | return rawValue & (RawAddress(size) - 1) == 0 38 | } 39 | 40 | public func advanced(by n: Int) -> PhysicalAddress { 41 | return PhysicalAddress(rawValue + RawAddress(n)) 42 | } 43 | 44 | public func advanced(by n: UInt) -> PhysicalAddress { 45 | return PhysicalAddress(rawValue + RawAddress(n)) 46 | } 47 | 48 | public func advanced(by n: UInt64) -> PhysicalAddress { 49 | return PhysicalAddress(rawValue + RawAddress(n)) 50 | } 51 | 52 | public func distance(to n: PhysicalAddress) -> Int { 53 | if n.rawValue > rawValue { 54 | return Int(n.rawValue - rawValue) 55 | } else { 56 | return Int(rawValue - n.rawValue) 57 | } 58 | } 59 | 60 | public static func +(lhs: PhysicalAddress, rhs: UInt) -> PhysicalAddress { 61 | return lhs.advanced(by: rhs) 62 | } 63 | 64 | public static func +(lhs: PhysicalAddress, rhs: Int) -> PhysicalAddress { 65 | return lhs.advanced(by: rhs) 66 | } 67 | 68 | public static func +(lhs: PhysicalAddress, rhs: UInt64) -> PhysicalAddress { 69 | return lhs.advanced(by: rhs) 70 | } 71 | 72 | public static func -(lhs: PhysicalAddress, rhs: UInt) -> PhysicalAddress { 73 | return PhysicalAddress(lhs.rawValue - RawAddress(rhs)) 74 | } 75 | 76 | public static func -(lhs: PhysicalAddress, rhs: PhysicalAddress) -> Int { 77 | return lhs.distance(to: rhs) 78 | } 79 | 80 | public static func <(lhs: PhysicalAddress, rhs: PhysicalAddress) -> Bool { 81 | return lhs.rawValue < rhs.rawValue 82 | } 83 | 84 | public static func <=(lhs: PhysicalAddress, rhs: PhysicalAddress) -> Bool { 85 | return lhs.rawValue <= rhs.rawValue 86 | } 87 | } 88 | 89 | 90 | 91 | struct LogicalMemoryAccess { 92 | 93 | enum SegmentRegister: Int { 94 | case es = 0 95 | case cs = 1 96 | case ss = 2 97 | case ds = 3 98 | case fs = 4 99 | case gs = 5 100 | } 101 | 102 | enum Register: Int { 103 | case rax = 0 104 | case rcx = 1 105 | case rdx = 2 106 | case rbx = 3 107 | case rsp = 4 108 | case rbp = 5 109 | case rsi = 6 110 | case rdi = 7 111 | case r8 = 8 112 | case r9 = 9 113 | case r10 = 10 114 | case r11 = 11 115 | case r12 = 12 116 | case r13 = 13 117 | case r14 = 14 118 | case r15 = 15 119 | } 120 | 121 | // The bit layout is taken from the VM-Exit Instruction Information Field 122 | private let value: BitField32 123 | var rawValue: UInt32 { value.rawValue } 124 | 125 | var scaling: Int? { value[22] ? nil : 1 << Int(value[0...1]) } 126 | var addressSize: Int { 16 << Int(value[7...9]) } // 16, 32, 64 127 | var segmentRegister: SegmentRegister { SegmentRegister(rawValue: Int(value[15...17]))! } 128 | var indexRegister: Register? { value[22] ? nil : Register(rawValue: Int(value[18...21])) } 129 | var baseRegister: Register? { value[27] ? nil : Register(rawValue: Int(value[23...26])) } 130 | 131 | /* 132 | // FIXME: Take paging and GDT/LDT into account 133 | func physicalAddress(using registers: VirtualMachine.VCPU.Registers ) -> PhysicalAddress { 134 | let baseAddress: PhysicalAddress 135 | switch segmentRegister { 136 | case .cs: baseAddress = Physical(registers.cs.base) 137 | case .ss: baseAddress = Physical(registers.cs.base) 138 | case .ds: baseAddress = Physical(registers.ds.base) 139 | case .es: baseAddress = Physical(registers.es.base) 140 | case .fs: baseAddress = Physical(registers.fs.base) 141 | case .gs: baseAddress = Physical(registers.gs.base) 142 | } 143 | 144 | } 145 | */ 146 | 147 | init(addressSize: Int, segmentRegister: SegmentRegister, register: Register) { 148 | precondition([16, 32, 64].contains(addressSize)) 149 | 150 | var tmp = BitField32() 151 | tmp[7...9] = UInt32(addressSize >> 5) 152 | tmp[15...17] = UInt32(segmentRegister.rawValue) 153 | 154 | // No Index register 155 | tmp[22] = true 156 | 157 | // Base Register 158 | tmp[23...26] = UInt32(register.rawValue) 159 | tmp[27] = false 160 | value = tmp 161 | } 162 | 163 | init(rawValue: UInt32) { 164 | value = BitField32(rawValue) 165 | } 166 | } 167 | 168 | // Addresses Logical => Linear => Physical 169 | 170 | extension VirtualMachine.VCPU { 171 | 172 | func linearAddress(_ memory: LogicalMemoryAccess) throws -> LinearAddress? { 173 | // FIXME: Asssumes real mode for now 174 | // FIXME: Check for overflow / wraparound/ unrealmode 175 | // FIXME: Check segment access rights / limits 176 | 177 | try registers.registerCacheControl.readRegisters(.all) 178 | let segmentRegister: SegmentRegister 179 | switch memory.segmentRegister { 180 | case .cs: segmentRegister = registers.cs 181 | case .ds: segmentRegister = registers.ds 182 | case .es: segmentRegister = registers.es 183 | case .fs: segmentRegister = registers.fs 184 | case .gs: segmentRegister = registers.gs 185 | case .ss: segmentRegister = registers.ss 186 | } 187 | 188 | 189 | var offset = LinearAddress(0) 190 | if let baseReg = memory.baseRegister { 191 | switch baseReg { 192 | case .rax: offset = registers.rax 193 | case .rbx: offset = registers.rbx 194 | case .rcx: offset = registers.rcx 195 | case .rdx: offset = registers.rdx 196 | case .rsi: offset = registers.rsi 197 | case .rdi: offset = registers.rdi 198 | case .rbp: offset = registers.rbp 199 | case .rsp: offset = registers.rsp 200 | case .r8: offset = registers.r8 201 | case .r9: offset = registers.r9 202 | case .r10: offset = registers.r10 203 | case .r11: offset = registers.r11 204 | case .r12: offset = registers.r12 205 | case .r13: offset = registers.r13 206 | case .r14: offset = registers.r14 207 | case .r15: offset = registers.r15 208 | } 209 | } 210 | 211 | if let scaling = memory.scaling, let indexReg = memory.indexRegister { 212 | let scaling = UInt64(scaling) 213 | switch indexReg { 214 | case .rax: offset += (registers.rax * scaling) 215 | case .rbx: offset += (registers.rbx * scaling) 216 | case .rcx: offset += (registers.rcx * scaling) 217 | case .rdx: offset += (registers.rdx * scaling) 218 | case .rsi: offset += (registers.rsi * scaling) 219 | case .rdi: offset += (registers.rdi * scaling) 220 | case .rbp: offset += (registers.rbp * scaling) 221 | case .rsp: offset += (registers.rsp * scaling) 222 | case .r8: offset += (registers.r8 * scaling) 223 | case .r9: offset += (registers.r9 * scaling) 224 | case .r10: offset += (registers.r10 * scaling) 225 | case .r11: offset += (registers.r11 * scaling) 226 | case .r12: offset += (registers.r12 * scaling) 227 | case .r13: offset += (registers.r13 * scaling) 228 | case .r14: offset += (registers.r14 * scaling) 229 | case .r15: offset += (registers.r15 * scaling) 230 | } 231 | } 232 | let cr0 = CPU.CR0Register(registers.cr0) 233 | if cr0.protectionEnable { 234 | fatalError("Logical To Linear not impleneted for Protected mode") 235 | } 236 | // Realmode lookup 237 | return LinearAddress(segmentRegister.base) + offset 238 | } 239 | 240 | 241 | func physicalAddress(for laddr: LinearAddress) throws -> PhysicalAddress? { 242 | try registers.registerCacheControl.readRegisters(.cr0) 243 | let cr0 = CPU.CR0Register(registers.cr0) 244 | if !cr0.paging { 245 | // No paging, Physical Address = Linear Address 246 | return PhysicalAddress(laddr) 247 | } 248 | fatalError("Page table lookup not implemented") 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /Sources/HypervisorKit/vmexit.swift: -------------------------------------------------------------------------------- 1 | // 2 | // vmexit.swift 3 | // HypervisorKit 4 | // 5 | // Created by Simon Evans on 10/12/2019. 6 | // Copyright © 2019 Simon Evans. All rights reserved. 7 | // 8 | 9 | public typealias IOPort = UInt16 10 | 11 | public enum VMExit: Equatable { 12 | 13 | // Used by IN/OUT and MMIO instructions 14 | public enum DataRead: Equatable { 15 | case byte 16 | case word 17 | case dword 18 | case qword 19 | 20 | public init?(bitWidth: UInt8) { 21 | switch bitWidth { 22 | case 8: self = .byte 23 | case 16: self = .word 24 | case 32: self = .dword 25 | case 64: self = .qword 26 | default: return nil 27 | } 28 | } 29 | 30 | public var bitWidth: UInt8 { 31 | switch self { 32 | case .byte: return 8 33 | case .word: return 16 34 | case .dword: return 32 35 | case .qword: return 64 36 | } 37 | } 38 | 39 | public var bytes: Int { 40 | switch self { 41 | case .byte: return 1 42 | case .word: return 2 43 | case .dword: return 4 44 | case .qword: return 8 45 | } 46 | } 47 | } 48 | 49 | public enum DataWrite: Equatable, CustomStringConvertible { 50 | case byte(UInt8) 51 | case word(UInt16) 52 | case dword(UInt32) 53 | case qword(UInt64) 54 | 55 | public init(_ data: UInt8) { 56 | self = .byte(data) 57 | } 58 | 59 | public init(_ data: UInt16) { 60 | self = .word(data) 61 | } 62 | 63 | public init(_ data: UInt32) { 64 | self = .dword(data) 65 | } 66 | 67 | public init(_ data: UInt64) { 68 | self = .qword(data) 69 | } 70 | 71 | public init?(bitWidth: UInt8, value: UInt64) { 72 | switch bitWidth { 73 | case 8: self = .byte(UInt8(truncatingIfNeeded: value)) 74 | case 16: self = .word(UInt16(truncatingIfNeeded: value)) 75 | case 32: self = .dword(UInt32(truncatingIfNeeded: value)) 76 | case 64: self = .qword(value) 77 | default: return nil 78 | } 79 | } 80 | 81 | public var description: String { 82 | switch self { 83 | case .byte(let value): return String(value, radix: 16) 84 | case .word(let value): return String(value, radix: 16) 85 | case .dword(let value): return String(value, radix: 16) 86 | case .qword(let value): return String(value, radix: 16) 87 | } 88 | } 89 | 90 | public var bitWidth: UInt8 { 91 | switch self { 92 | case .byte: return 8 93 | case .word: return 16 94 | case .dword: return 32 95 | case .qword: return 64 96 | } 97 | } 98 | 99 | public var bytes: Int { 100 | switch self { 101 | case .byte: return 1 102 | case .word: return 2 103 | case .dword: return 4 104 | case .qword: return 8 105 | } 106 | } 107 | } 108 | 109 | 110 | public struct ExceptionInfo: Equatable { 111 | public enum Exception: UInt32 { 112 | case divideError = 0 113 | case debug = 1 114 | case nmi = 2 115 | case breakpoint = 3 116 | case overflow = 4 117 | case boundRangeExceeded = 5 118 | case undefinedOpcode = 6 119 | case deviceNotAvailable = 7 120 | case doubleFault = 8 121 | case coprocessorSegmentOverrun = 9 122 | case invalidTSS = 10 123 | case segmentNotPresent = 11 124 | case stackSegmentationFalue = 12 125 | case generalProtection = 13 126 | case pageFault = 14 127 | case reserved15 = 15 128 | case floatingPintError = 16 129 | case alignmentCheck = 17 130 | case machineCheck = 18 131 | case simdFloatingPointException = 19 132 | case virtualizationException = 20 133 | case controlProtectionException = 21 134 | case reserved22 = 22 135 | case reserved23 = 23 136 | case reserved24 = 24 137 | case reserved25 = 25 138 | case reserved26 = 26 139 | case reserved27 = 27 140 | case reserved28 = 28 141 | case reserved29 = 29 142 | case reserved30 = 30 143 | case reserved31 = 31 144 | } 145 | 146 | public let exception: Exception 147 | public let errorCode: UInt32? 148 | 149 | init?(exception: UInt32, errorCode: UInt32? = nil) { 150 | guard let e = Exception(rawValue: exception) else { return nil } 151 | self.exception = e 152 | self.errorCode = errorCode 153 | } 154 | } 155 | 156 | public struct Debug: Equatable { 157 | let rip: UInt64 158 | let dr6: UInt64 159 | let dr7: UInt64 160 | let exception: UInt32 161 | } 162 | 163 | public struct TPRAccess: Equatable { 164 | // TODO 165 | } 166 | 167 | public struct HyperV: Equatable { 168 | // TODO 169 | } 170 | 171 | public struct SystemEvent: Equatable { 172 | // TODO 173 | } 174 | 175 | 176 | public struct MemoryViolation: Equatable, CustomStringConvertible { 177 | public enum Access { 178 | case read 179 | case write 180 | case instructionFetch 181 | } 182 | 183 | public let access: Access 184 | public let readable: Bool 185 | public let writeable: Bool 186 | public let executable: Bool 187 | public let guestPhysicalAddress: PhysicalAddress 188 | public let guestLinearAddress: UInt64? 189 | 190 | public var description: String { 191 | let perms = (readable ? "r" : "-") + (writeable ? "w" : "-") + (executable ? "x" : "-") 192 | let gla = guestLinearAddress == nil ? "none" : "0x" + String(guestLinearAddress!, radix: 16) 193 | return "MemoryViolation(access: \(access), perms: \(perms) GPA: \(guestPhysicalAddress.description) GLA: \(gla)" 194 | } 195 | } 196 | 197 | 198 | case unknown(UInt64) 199 | case exception(ExceptionInfo) 200 | case debug(Debug) 201 | case hlt 202 | case mmioReadOperation(PhysicalAddress, DataRead) 203 | case mmioWriteOperation(PhysicalAddress, DataWrite) 204 | case irqWindowOpen 205 | case shutdown 206 | case entryFailed(UInt64) 207 | case interrupt 208 | case setTpr 209 | case tprAccess(TPRAccess) 210 | case nmi 211 | #if os(Linux) 212 | case internalError(InternalError) 213 | #endif 214 | case systemEvent(SystemEvent) 215 | case ioapicEOI(UInt8) 216 | case hyperV(HyperV) 217 | } 218 | -------------------------------------------------------------------------------- /Tests/HypervisorKitTests/VMMKitTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HypervisorKitTests.swift 3 | // HypervisorKit 4 | // 5 | // Created by Simon Evans on 08/12/2019. 6 | // Copyright © 2019 Simon Evans. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import Foundation 11 | 12 | @testable import HypervisorKit 13 | 14 | 15 | final class HypervisorKitTests: XCTestCase { 16 | 17 | func testCreateVM() throws { 18 | let vm: VirtualMachine 19 | do { 20 | vm = try VirtualMachine(logger: logger) 21 | } catch { 22 | XCTFail("Failed to create VM: \(error)") 23 | return 24 | } 25 | XCTAssertNoThrow(try vm.addMemory(at: 0x1000, size: 8192)) 26 | XCTAssertNoThrow(try vm.addMemory(at: 0x4000, size: 4096)) 27 | let vcpu = try vm.addVCPU() 28 | XCTAssertEqual(vm.memoryRegions.count, 2) 29 | XCTAssertTrue(vcpu.shutdown()) 30 | XCTAssertNoThrow(try vm.shutdown()) 31 | } 32 | 33 | static var allTests: [(String, (HypervisorKitTests) -> () throws -> Void)] = [ 34 | ("testCreateVM", testCreateVM), 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /Tests/HypervisorKitTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XCTestManifests.swift 3 | // HypervisorKit 4 | // 5 | // Created by Simon Evans on 05/12/2019. 6 | // Copyright © 2019 Simon Evans. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import Logging 11 | 12 | let logger: Logger = { 13 | LoggingSystem.bootstrap(StreamLogHandler.standardError) 14 | var logger = Logger(label: "HypervisorKitTests") 15 | logger.logLevel = .debug 16 | return logger 17 | }() 18 | 19 | 20 | #if !canImport(ObjectiveC) 21 | public func allTests() -> [XCTestCaseEntry] { 22 | return [ 23 | testCase(HypervisorKitTests.allTests), 24 | testCase(RealModeTests.allTests), 25 | ] 26 | } 27 | #endif 28 | -------------------------------------------------------------------------------- /Tests/HypervisorKitTests/real_mode_test.asm: -------------------------------------------------------------------------------- 1 | ;; 2 | ;; real_mode_test.asm 3 | ;; HypervisorKit 4 | ;; 5 | ;; Created by Simon Evans on 08/12/2019. 6 | ;; Copyright © 2019 Simon Evans. All rights reserved. 7 | ;; 8 | ;; 16bit real mode code to be used for tests in HypervisorKitTests:RealModeTests 9 | ;; 10 | 11 | %macro OFFSET 1 12 | times %1 - ($ - $$) db 0x90 13 | %endmacro 14 | BITS 16 15 | ORG 0x1000 16 | 17 | mov bx, jump_table 18 | shl ax, 1 19 | add bx, ax 20 | jmp [bx] 21 | 22 | test_hlt: hlt 23 | 24 | read_write_memory: 25 | mov sp, 0x1ffe 26 | push ds 27 | pop es 28 | mov ax, [0x1200] 29 | inc ax 30 | mov [0x1200], ax 31 | 32 | cld 33 | mov bx, 0x200 34 | mov es, bx 35 | mov si, src_data 36 | mov di, 0 37 | mov cx, 2 38 | rep movsw 39 | hlt 40 | 41 | io_out_test: 42 | ;; mov al, 1 43 | ;; mov dx, 0x60 44 | ;; out dx, al 45 | ;; mov bx, 0x1234 46 | 47 | cld 48 | xor ax, ax 49 | mov es, ax 50 | mov si, 0x1300 51 | mov dx, 0x60 52 | mov cx, 24 53 | rep outsb 54 | 55 | sub si, 2 56 | std 57 | mov cx, 12 58 | rep outsw 59 | 60 | add si, 2 61 | cld 62 | mov cx, 6 63 | rep outsd 64 | 65 | ;; Unaligned words and dwords 66 | mov si, 0x1301 67 | mov cx, 5 68 | rep outsd 69 | 70 | std 71 | sub si, 4 72 | mov cx, 10 73 | rep outsw 74 | 75 | 76 | ;; Test Segment Overrides 77 | jmp 0x100:next - 0x1000 ; Set CS to 0x1000 78 | next: 79 | cld 80 | mov si, 0x300 81 | mov cx, 4 82 | rep cs outsb 83 | 84 | mov ax, 0x100 85 | mov ds, ax 86 | mov si, 0x304 87 | mov cx, 4 88 | rep ds outsb 89 | 90 | mov ax, 0x110 91 | mov es, ax 92 | mov si, 0x208 93 | mov cx, 4 94 | rep es outsb 95 | 96 | mov ax, 0x120 97 | mov fs, ax 98 | mov si, 0x10C 99 | mov cx, 4 100 | rep fs outsb 101 | 102 | mov ax, 0x130 103 | mov gs, ax 104 | mov si, 0x10 105 | mov cx, 4 106 | rep gs outsb 107 | 108 | mov ax, 0x30 109 | mov ss, ax 110 | mov si, 0x1014 111 | mov cx, 4 112 | rep ss outsb 113 | 114 | hlt 115 | 116 | io_in_test: 117 | in al, 0x60 118 | hlt 119 | in ax, 0x60 120 | hlt 121 | in eax, 0x60 122 | hlt 123 | 124 | mmio_read: 125 | mov ebx, 0x87654321 126 | mov bl, [0x8008] 127 | mov bx, [0x8008] 128 | mov ebx, [0x8010] 129 | 130 | mov [0x8000], bl 131 | mov [0x8008], bx 132 | mov [0x8010], ebx 133 | 134 | hlt 135 | 136 | 137 | OFFSET 0x100 138 | instruction_prefixes: 139 | db 0xf0, 0xf2, 0xf3, 0xaa 140 | db 0xf0, 0xf2, 0x2e, 0x67, 0x46, 0x0f, 0x3a, 0x7a, 0x22, 0x8e, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f 141 | lock es add word [0x1200], ax 142 | 143 | OFFSET 0x300 144 | db 0x12, 0x34, 0x56, 0x78 145 | db 0x11, 0x22, 0x33, 0x44 146 | db 0x00, 0x00, 0x00, 0x01 147 | db 0xaa, 0xbb, 0xcc, 0xdd 148 | db 0xfe, 0xdc, 0xba, 0x98 149 | db 0x55, 0xaa, 0xcc, 0xdd 150 | 151 | OFFSET 0x320 152 | src_data: db 0xaa, 0xbb, 0xcc, 0xdd 153 | dest_data db 0, 0, 0, 0 154 | 155 | 156 | jump_table: 157 | dw test_hlt ; 0: testHLT() 158 | dw read_write_memory ; 1: testReadWriteMemory() 159 | dw io_out_test ; 2: testOut() 160 | dw io_in_test ; 3: testIn() 161 | dw mmio_read ; 4: testMMIO() 162 | dw instruction_prefixes ; 5: testInstructionPrefixes() 163 | 164 | -------------------------------------------------------------------------------- /Tests/HypervisorKitTests/real_mode_test.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spevans/hypervisor-kit/19f4e71ce6955193f54d293aa798fc2d38bacb71/Tests/HypervisorKitTests/real_mode_test.bin -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LinuxMain.swift 3 | // HypervisorKit 4 | // 5 | // Created by Simon Evans on 05/12/2019. 6 | // Copyright © 2019 Simon Evans. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import HypervisorKitTests 11 | 12 | var tests = [XCTestCaseEntry]() 13 | tests += HypervisorKitTests.allTests() 14 | XCTMain(tests) 15 | -------------------------------------------------------------------------------- /asm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | nasm -Werror -f bin -l real_mode_test.lst -o Tests/HypervisorKitTests/real_mode_test.bin Tests/HypervisorKitTests/real_mode_test.asm 4 | -------------------------------------------------------------------------------- /docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | jazzy --clean --author "Simon Evans" --author_url https://github.com/spevans --github_url https://github.com/spevans/hypervisor-kit --module HypervisorKit --output docs 3 | rm -r docs/docsets docs/undocumented.json 4 | -------------------------------------------------------------------------------- /docs/Classes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Classes Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |

HypervisorKit Docs (50% documented)

21 |

View on GitHub

22 |

23 |

24 | 25 |
26 |

27 |
28 |
29 |
30 | 35 |
36 |
37 | 147 |
148 |
149 |
150 |

Classes

151 |

The following classes are available globally.

152 | 153 |
154 |
155 |
156 |
    157 |
  • 158 |
    159 | 160 | 161 | 162 | VirtualMachine 163 | 164 |
    165 |
    166 |
    167 |
    168 |
    169 |
    170 |

    A type representing a full VM (virtual machine) including cpus and memory.

    171 | 172 | See more 173 |
    174 |
    175 |

    Declaration

    176 |
    177 |

    Swift

    178 |
    public final class VirtualMachine
    179 | 180 |
    181 |
    182 |
    183 |
    184 |
  • 185 |
  • 186 |
    187 | 188 | 189 | 190 | MemoryRegion 191 | 192 |
    193 |
    194 |
    195 |
    196 |
    197 |
    198 |

    Undocumented

    199 | 200 | See more 201 |
    202 |
    203 |

    Declaration

    204 |
    205 |

    Swift

    206 |
    public final class MemoryRegion
    207 | 208 |
    209 |
    210 |
    211 |
    212 |
  • 213 |
214 |
215 |
216 |
217 | 221 |
222 |
223 | 224 | 225 | 226 | -------------------------------------------------------------------------------- /docs/Classes/VirtualMachine/VCPU.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | VCPU Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |

HypervisorKit Docs (50% documented)

21 |

View on GitHub

22 |

23 |

24 | 25 |
26 |

27 |
28 |
29 |
30 | 35 |
36 |
37 | 147 |
148 |
149 |
150 |

VCPU

151 | 152 |
153 |
154 |
155 |
    156 |
  • 157 |
    158 | 159 | 160 | 161 | setIn(data:) 162 | 163 |
    164 |
    165 |
    166 |
    167 |
    168 |
    169 |

    Used to satisfy the IO In read performed by the VCPU

    170 | 171 |
    172 |
    173 |
    174 |
  • 175 |
176 |
177 |
178 |
179 | 183 |
184 |
185 | 186 | 187 | 188 | -------------------------------------------------------------------------------- /docs/Enums.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Enumerations Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |

HypervisorKit Docs (50% documented)

21 |

View on GitHub

22 |

23 |

24 | 25 |
26 |

27 |
28 |
29 |
30 | 35 |
36 |
37 | 147 |
148 |
149 |
150 |

Enumerations

151 |

The following enumerations are available globally.

152 | 153 |
154 |
155 |
156 |
    157 |
  • 158 |
    159 | 160 | 161 | 162 | VMError 163 | 164 |
    165 |
    166 |
    167 |
    168 |
    169 |
    170 |

    A type that enumerates errors thrown by HypervisorKit.

    171 | 172 | See more 173 |
    174 |
    175 |

    Declaration

    176 |
    177 |

    Swift

    178 |
    public enum VMError : Error
    179 | 180 |
    181 |
    182 |
    183 |
    184 |
  • 185 |
  • 186 |
    187 | 188 | 189 | 190 | VMExit 191 | 192 |
    193 |
    194 |
    195 |
    196 |
    197 |
    198 |

    Undocumented

    199 | 200 | See more 201 |
    202 |
    203 |

    Declaration

    204 |
    205 |

    Swift

    206 |
    public enum VMExit : Equatable
    207 | 208 |
    209 |
    210 |
    211 |
    212 |
  • 213 |
214 |
215 |
216 |
217 | 221 |
222 |
223 | 224 | 225 | 226 | -------------------------------------------------------------------------------- /docs/Extensions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Extensions Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |

HypervisorKit Docs (50% documented)

21 |

View on GitHub

22 |

23 |

24 | 25 |
26 |

27 |
28 |
29 |
30 | 35 |
36 |
37 | 147 |
148 |
149 |
150 |

Extensions

151 |

The following extensions are available globally.

152 | 153 |
154 |
155 |
156 |
    157 |
  • 158 |
    159 | 160 | 161 | 162 | VirtualMachine 163 | 164 |
    165 |
    166 |
    167 |
    168 |
    169 |
    170 | 171 | See more 172 |
    173 |
    174 |
    175 |
  • 176 |
177 |
178 |
179 |
180 | 184 |
185 |
186 | 187 | 188 | 189 | -------------------------------------------------------------------------------- /docs/Extensions/VirtualMachine.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | VirtualMachine Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |

HypervisorKit Docs (50% documented)

21 |

View on GitHub

22 |

23 |

24 | 25 |
26 |

27 |
28 |
29 |
30 | 35 |
36 |
37 | 147 |
148 |
149 |
150 |

VirtualMachine

151 | 152 |
153 |
154 |
155 |
    156 |
  • 157 |
    158 | 159 | 160 | 161 | addPICandPIT() 162 | 163 |
    164 |
    165 |
    166 |
    167 |
    168 |
    169 | 170 |
    171 |
    172 |
    173 |
  • 174 |
175 |
176 |
177 |
178 | 182 |
183 |
184 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /docs/Structs/CPU.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CPU Structure Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |

HypervisorKit Docs (50% documented)

21 |

View on GitHub

22 |

23 |

24 | 25 |
26 |

27 |
28 |
29 |
30 | 35 |
36 |
37 | 147 |
148 |
149 |
150 |

CPU

151 |
152 |
153 | 154 |
public struct CPU
155 | 156 |
157 |
158 |

Undocumented

159 | 160 |
161 |
162 |
163 |
    164 |
  • 165 |
    166 | 167 | 168 | 169 | RFLAGS 170 | 171 |
    172 |
    173 |
    174 |
    175 |
    176 |
    177 |

    Undocumented

    178 | 179 | See more 180 |
    181 |
    182 |

    Declaration

    183 |
    184 |

    Swift

    185 |
    public struct RFLAGS : CustomStringConvertible
    186 | 187 |
    188 |
    189 |
    190 |
    191 |
  • 192 |
  • 193 |
    194 | 195 | 196 | 197 | CR0Register 198 | 199 |
    200 |
    201 |
    202 |
    203 |
    204 |
    205 |

    Undocumented

    206 | 207 | See more 208 |
    209 |
    210 |

    Declaration

    211 |
    212 |

    Swift

    213 |
    public struct CR0Register : CustomStringConvertible
    214 | 215 |
    216 |
    217 |
    218 |
    219 |
  • 220 |
221 |
222 |
223 |
224 | 228 |
229 |
230 | 231 | 232 | 233 | -------------------------------------------------------------------------------- /docs/Structs/InternalError.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | InternalError Structure Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |

HypervisorKit Docs (50% documented)

21 |

View on GitHub

22 |

23 |

24 | 25 |
26 |

27 |
28 |
29 |
30 | 35 |
36 |
37 | 147 |
148 |
149 |
150 |

InternalError

151 | 152 |
153 |
154 |
155 |
    156 |
  • 157 |
    158 | 159 | 160 | 161 | ==(_:_:) 162 | 163 |
    164 |
    165 |
    166 |
    167 |
    168 |
    169 | 170 |
    171 |
    172 |
    173 |
  • 174 |
175 |
176 |
177 |
178 | 182 |
183 |
184 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /docs/badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | documentation 17 | 18 | 19 | documentation 20 | 21 | 22 | 50% 23 | 24 | 25 | 50% 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /docs/css/jazzy.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, h1, h3, h4, p, a, code, em, img, ul, li, table, tbody, tr, td { 2 | background: transparent; 3 | border: 0; 4 | margin: 0; 5 | outline: 0; 6 | padding: 0; 7 | vertical-align: baseline; } 8 | 9 | body { 10 | background-color: #f2f2f2; 11 | font-family: Helvetica, freesans, Arial, sans-serif; 12 | font-size: 14px; 13 | -webkit-font-smoothing: subpixel-antialiased; 14 | word-wrap: break-word; } 15 | 16 | h1, h2, h3 { 17 | margin-top: 0.8em; 18 | margin-bottom: 0.3em; 19 | font-weight: 100; 20 | color: black; } 21 | 22 | h1 { 23 | font-size: 2.5em; } 24 | 25 | h2 { 26 | font-size: 2em; 27 | border-bottom: 1px solid #e2e2e2; } 28 | 29 | h4 { 30 | font-size: 13px; 31 | line-height: 1.5; 32 | margin-top: 21px; } 33 | 34 | h5 { 35 | font-size: 1.1em; } 36 | 37 | h6 { 38 | font-size: 1.1em; 39 | color: #777; } 40 | 41 | .section-name { 42 | color: gray; 43 | display: block; 44 | font-family: Helvetica; 45 | font-size: 22px; 46 | font-weight: 100; 47 | margin-bottom: 15px; } 48 | 49 | pre, code { 50 | font: 0.95em Menlo, monospace; 51 | color: #777; 52 | word-wrap: normal; } 53 | 54 | p code, li code { 55 | background-color: #eee; 56 | padding: 2px 4px; 57 | border-radius: 4px; } 58 | 59 | pre > code { 60 | padding: 0; } 61 | 62 | a { 63 | color: #0088cc; 64 | text-decoration: none; } 65 | a code { 66 | color: inherit; } 67 | 68 | ul { 69 | padding-left: 15px; } 70 | 71 | li { 72 | line-height: 1.8em; } 73 | 74 | img { 75 | max-width: 100%; } 76 | 77 | blockquote { 78 | margin-left: 0; 79 | padding: 0 10px; 80 | border-left: 4px solid #ccc; } 81 | 82 | .content-wrapper { 83 | margin: 0 auto; 84 | width: 980px; } 85 | 86 | header { 87 | font-size: 0.85em; 88 | line-height: 32px; 89 | background-color: #414141; 90 | position: fixed; 91 | width: 100%; 92 | z-index: 3; } 93 | header img { 94 | padding-right: 6px; 95 | vertical-align: -4px; 96 | height: 16px; } 97 | header a { 98 | color: #fff; } 99 | header p { 100 | float: left; 101 | color: #999; } 102 | header .header-right { 103 | float: right; 104 | margin-left: 16px; } 105 | 106 | #breadcrumbs { 107 | background-color: #f2f2f2; 108 | height: 21px; 109 | padding-top: 17px; 110 | position: fixed; 111 | width: 100%; 112 | z-index: 2; 113 | margin-top: 32px; } 114 | #breadcrumbs #carat { 115 | height: 10px; 116 | margin: 0 5px; } 117 | 118 | .sidebar { 119 | background-color: #f9f9f9; 120 | border: 1px solid #e2e2e2; 121 | overflow-y: auto; 122 | overflow-x: hidden; 123 | position: fixed; 124 | top: 70px; 125 | bottom: 0; 126 | width: 230px; 127 | word-wrap: normal; } 128 | 129 | .nav-groups { 130 | list-style-type: none; 131 | background: #fff; 132 | padding-left: 0; } 133 | 134 | .nav-group-name { 135 | border-bottom: 1px solid #e2e2e2; 136 | font-size: 1.1em; 137 | font-weight: 100; 138 | padding: 15px 0 15px 20px; } 139 | .nav-group-name > a { 140 | color: #333; } 141 | 142 | .nav-group-tasks { 143 | margin-top: 5px; } 144 | 145 | .nav-group-task { 146 | font-size: 0.9em; 147 | list-style-type: none; 148 | white-space: nowrap; } 149 | .nav-group-task a { 150 | color: #888; } 151 | 152 | .main-content { 153 | background-color: #fff; 154 | border: 1px solid #e2e2e2; 155 | margin-left: 246px; 156 | position: absolute; 157 | overflow: hidden; 158 | padding-bottom: 20px; 159 | top: 70px; 160 | width: 734px; } 161 | .main-content p, .main-content a, .main-content code, .main-content em, .main-content ul, .main-content table, .main-content blockquote { 162 | margin-bottom: 1em; } 163 | .main-content p { 164 | line-height: 1.8em; } 165 | .main-content section .section:first-child { 166 | margin-top: 0; 167 | padding-top: 0; } 168 | .main-content section .task-group-section .task-group:first-of-type { 169 | padding-top: 10px; } 170 | .main-content section .task-group-section .task-group:first-of-type .section-name { 171 | padding-top: 15px; } 172 | .main-content section .heading:before { 173 | content: ""; 174 | display: block; 175 | padding-top: 70px; 176 | margin: -70px 0 0; } 177 | .main-content .section-name p { 178 | margin-bottom: inherit; 179 | line-height: inherit; } 180 | .main-content .section-name code { 181 | background-color: inherit; 182 | padding: inherit; 183 | color: inherit; } 184 | 185 | .section { 186 | padding: 0 25px; } 187 | 188 | .highlight { 189 | background-color: #eee; 190 | padding: 10px 12px; 191 | border: 1px solid #e2e2e2; 192 | border-radius: 4px; 193 | overflow-x: auto; } 194 | 195 | .declaration .highlight { 196 | overflow-x: initial; 197 | padding: 0 40px 40px 0; 198 | margin-bottom: -25px; 199 | background-color: transparent; 200 | border: none; } 201 | 202 | .section-name { 203 | margin: 0; 204 | margin-left: 18px; } 205 | 206 | .task-group-section { 207 | margin-top: 10px; 208 | padding-left: 6px; 209 | border-top: 1px solid #e2e2e2; } 210 | 211 | .task-group { 212 | padding-top: 0px; } 213 | 214 | .task-name-container a[name]:before { 215 | content: ""; 216 | display: block; 217 | padding-top: 70px; 218 | margin: -70px 0 0; } 219 | 220 | .section-name-container { 221 | position: relative; 222 | display: inline-block; } 223 | .section-name-container .section-name-link { 224 | position: absolute; 225 | top: 0; 226 | left: 0; 227 | bottom: 0; 228 | right: 0; 229 | margin-bottom: 0; } 230 | .section-name-container .section-name { 231 | position: relative; 232 | pointer-events: none; 233 | z-index: 1; } 234 | .section-name-container .section-name a { 235 | pointer-events: auto; } 236 | 237 | .item { 238 | padding-top: 8px; 239 | width: 100%; 240 | list-style-type: none; } 241 | .item a[name]:before { 242 | content: ""; 243 | display: block; 244 | padding-top: 70px; 245 | margin: -70px 0 0; } 246 | .item code { 247 | background-color: transparent; 248 | padding: 0; } 249 | .item .token, .item .direct-link { 250 | display: inline-block; 251 | text-indent: -20px; 252 | padding-left: 3px; 253 | margin-left: 35px; 254 | font-size: 11.9px; 255 | transition: all 300ms; } 256 | .item .token-open { 257 | margin-left: 20px; } 258 | .item .discouraged { 259 | text-decoration: line-through; } 260 | .item .declaration-note { 261 | font-size: .85em; 262 | color: gray; 263 | font-style: italic; } 264 | 265 | .pointer-container { 266 | border-bottom: 1px solid #e2e2e2; 267 | left: -23px; 268 | padding-bottom: 13px; 269 | position: relative; 270 | width: 110%; } 271 | 272 | .pointer { 273 | background: #f9f9f9; 274 | border-left: 1px solid #e2e2e2; 275 | border-top: 1px solid #e2e2e2; 276 | height: 12px; 277 | left: 21px; 278 | top: -7px; 279 | -webkit-transform: rotate(45deg); 280 | -moz-transform: rotate(45deg); 281 | -o-transform: rotate(45deg); 282 | transform: rotate(45deg); 283 | position: absolute; 284 | width: 12px; } 285 | 286 | .height-container { 287 | display: none; 288 | left: -25px; 289 | padding: 0 25px; 290 | position: relative; 291 | width: 100%; 292 | overflow: hidden; } 293 | .height-container .section { 294 | background: #f9f9f9; 295 | border-bottom: 1px solid #e2e2e2; 296 | left: -25px; 297 | position: relative; 298 | width: 100%; 299 | padding-top: 10px; 300 | padding-bottom: 5px; } 301 | 302 | .aside, .language { 303 | padding: 6px 12px; 304 | margin: 12px 0; 305 | border-left: 5px solid #dddddd; 306 | overflow-y: hidden; } 307 | .aside .aside-title, .language .aside-title { 308 | font-size: 9px; 309 | letter-spacing: 2px; 310 | text-transform: uppercase; 311 | padding-bottom: 0; 312 | margin: 0; 313 | color: #aaa; 314 | -webkit-user-select: none; } 315 | .aside p:last-child, .language p:last-child { 316 | margin-bottom: 0; } 317 | 318 | .language { 319 | border-left: 5px solid #cde9f4; } 320 | .language .aside-title { 321 | color: #4b8afb; } 322 | 323 | .aside-warning, .aside-deprecated, .aside-unavailable { 324 | border-left: 5px solid #ff6666; } 325 | .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { 326 | color: #ff0000; } 327 | 328 | .graybox { 329 | border-collapse: collapse; 330 | width: 100%; } 331 | .graybox p { 332 | margin: 0; 333 | word-break: break-word; 334 | min-width: 50px; } 335 | .graybox td { 336 | border: 1px solid #e2e2e2; 337 | padding: 5px 25px 5px 10px; 338 | vertical-align: middle; } 339 | .graybox tr td:first-of-type { 340 | text-align: right; 341 | padding: 7px; 342 | vertical-align: top; 343 | word-break: normal; 344 | width: 40px; } 345 | 346 | .slightly-smaller { 347 | font-size: 0.9em; } 348 | 349 | #footer { 350 | position: relative; 351 | top: 10px; 352 | bottom: 0px; 353 | margin-left: 25px; } 354 | #footer p { 355 | margin: 0; 356 | color: #aaa; 357 | font-size: 0.8em; } 358 | 359 | html.dash header, html.dash #breadcrumbs, html.dash .sidebar { 360 | display: none; } 361 | 362 | html.dash .main-content { 363 | width: 980px; 364 | margin-left: 0; 365 | border: none; 366 | width: 100%; 367 | top: 0; 368 | padding-bottom: 0; } 369 | 370 | html.dash .height-container { 371 | display: block; } 372 | 373 | html.dash .item .token { 374 | margin-left: 0; } 375 | 376 | html.dash .content-wrapper { 377 | width: auto; } 378 | 379 | html.dash #footer { 380 | position: static; } 381 | 382 | form[role=search] { 383 | float: right; } 384 | form[role=search] input { 385 | font: Helvetica, freesans, Arial, sans-serif; 386 | margin-top: 6px; 387 | font-size: 13px; 388 | line-height: 20px; 389 | padding: 0px 10px; 390 | border: none; 391 | border-radius: 1em; } 392 | .loading form[role=search] input { 393 | background: white url(../img/spinner.gif) center right 4px no-repeat; } 394 | form[role=search] .tt-menu { 395 | margin: 0; 396 | min-width: 300px; 397 | background: #fff; 398 | color: #333; 399 | border: 1px solid #e2e2e2; 400 | z-index: 4; } 401 | form[role=search] .tt-highlight { 402 | font-weight: bold; } 403 | form[role=search] .tt-suggestion { 404 | font: Helvetica, freesans, Arial, sans-serif; 405 | font-size: 14px; 406 | padding: 0 8px; } 407 | form[role=search] .tt-suggestion span { 408 | display: table-cell; 409 | white-space: nowrap; } 410 | form[role=search] .tt-suggestion .doc-parent-name { 411 | width: 100%; 412 | text-align: right; 413 | font-weight: normal; 414 | font-size: 0.9em; 415 | padding-left: 16px; } 416 | form[role=search] .tt-suggestion:hover, 417 | form[role=search] .tt-suggestion.tt-cursor { 418 | cursor: pointer; 419 | background-color: #4183c4; 420 | color: #fff; } 421 | form[role=search] .tt-suggestion:hover .doc-parent-name, 422 | form[role=search] .tt-suggestion.tt-cursor .doc-parent-name { 423 | color: #fff; } 424 | -------------------------------------------------------------------------------- /docs/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spevans/hypervisor-kit/19f4e71ce6955193f54d293aa798fc2d38bacb71/docs/img/carat.png -------------------------------------------------------------------------------- /docs/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spevans/hypervisor-kit/19f4e71ce6955193f54d293aa798fc2d38bacb71/docs/img/dash.png -------------------------------------------------------------------------------- /docs/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spevans/hypervisor-kit/19f4e71ce6955193f54d293aa798fc2d38bacb71/docs/img/gh.png -------------------------------------------------------------------------------- /docs/img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spevans/hypervisor-kit/19f4e71ce6955193f54d293aa798fc2d38bacb71/docs/img/spinner.gif -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | HypervisorKit Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |

HypervisorKit Docs (50% documented)

20 |

View on GitHub

21 |

22 |

23 | 24 |
25 |

26 |
27 |
28 |
29 | 34 |
35 |
36 | 146 |
147 |
148 |
149 | 150 |

HypervisorKit

151 | 152 |

A Swift library providing an interface for writing X86 hypervisors on macOS (Hypervisor.framework) and Linux (KVM).

153 | 154 |

Documentation is available here.

155 | 156 |

On macOS, Hypervisor.framework provides a very thin wrapper 157 | over the Intel VMX virtualisation instructions, allowing direct access to VMCS (Virtual machine control structures). 158 | Some functionality has to be implemented by the kernel, eg setting up EPT (Extended Page Tables) to map a process’s 159 | memory into the virtual machine’s address space.

160 | 161 |

Conversely, Linux KVM provides a more abstract interface as it is designed to work across multiple CPU architectures, 162 | eg x86_64, PPC etc and uses file descriptors and ioctl calls. KVM provides its own handling for vmexits and converts 163 | this to a smaller, simplified collection of KVMExits.

164 | 165 |

HypervisorKit aims to provide equivalent functionality on macOS to match KVM allowing cross platform Virtual machines 166 | to be written for both macOS and Linux.

167 | 168 |
169 |
170 | 174 |
175 |
176 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /docs/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | function toggleItem($link, $content) { 12 | var animationDuration = 300; 13 | $link.toggleClass('token-open'); 14 | $content.slideToggle(animationDuration); 15 | } 16 | 17 | function itemLinkToContent($link) { 18 | return $link.parent().parent().next(); 19 | } 20 | 21 | // On doc load + hash-change, open any targetted item 22 | function openCurrentItemIfClosed() { 23 | if (window.jazzy.docset) { 24 | return; 25 | } 26 | var $link = $(`a[name="${location.hash.substring(1)}"]`).nextAll('.token'); 27 | $content = itemLinkToContent($link); 28 | if ($content.is(':hidden')) { 29 | toggleItem($link, $content); 30 | } 31 | } 32 | 33 | $(openCurrentItemIfClosed); 34 | $(window).on('hashchange', openCurrentItemIfClosed); 35 | 36 | // On item link ('token') click, toggle its discussion 37 | $('.token').on('click', function(event) { 38 | if (window.jazzy.docset) { 39 | return; 40 | } 41 | var $link = $(this); 42 | toggleItem($link, itemLinkToContent($link)); 43 | 44 | // Keeps the document from jumping to the hash. 45 | var href = $link.attr('href'); 46 | if (history.pushState) { 47 | history.pushState({}, '', href); 48 | } else { 49 | location.hash = href; 50 | } 51 | event.preventDefault(); 52 | }); 53 | 54 | // Clicks on links to the current, closed, item need to open the item 55 | $("a:not('.token')").on('click', function() { 56 | if (location == this.href) { 57 | openCurrentItemIfClosed(); 58 | } 59 | }); 60 | 61 | // KaTeX rendering 62 | if ("katex" in window) { 63 | $($('.math').each( (_, element) => { 64 | katex.render(element.textContent, element, { 65 | displayMode: $(element).hasClass('m-block'), 66 | throwOnError: false, 67 | trust: true 68 | }); 69 | })) 70 | } 71 | -------------------------------------------------------------------------------- /docs/js/jazzy.search.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | var $typeahead = $('[data-typeahead]'); 3 | var $form = $typeahead.parents('form'); 4 | var searchURL = $form.attr('action'); 5 | 6 | function displayTemplate(result) { 7 | return result.name; 8 | } 9 | 10 | function suggestionTemplate(result) { 11 | var t = '
'; 12 | t += '' + result.name + ''; 13 | if (result.parent_name) { 14 | t += '' + result.parent_name + ''; 15 | } 16 | t += '
'; 17 | return t; 18 | } 19 | 20 | $typeahead.one('focus', function() { 21 | $form.addClass('loading'); 22 | 23 | $.getJSON(searchURL).then(function(searchData) { 24 | const searchIndex = lunr(function() { 25 | this.ref('url'); 26 | this.field('name'); 27 | this.field('abstract'); 28 | for (const [url, doc] of Object.entries(searchData)) { 29 | this.add({url: url, name: doc.name, abstract: doc.abstract}); 30 | } 31 | }); 32 | 33 | $typeahead.typeahead( 34 | { 35 | highlight: true, 36 | minLength: 3, 37 | autoselect: true 38 | }, 39 | { 40 | limit: 10, 41 | display: displayTemplate, 42 | templates: { suggestion: suggestionTemplate }, 43 | source: function(query, sync) { 44 | const lcSearch = query.toLowerCase(); 45 | const results = searchIndex.query(function(q) { 46 | q.term(lcSearch, { boost: 100 }); 47 | q.term(lcSearch, { 48 | boost: 10, 49 | wildcard: lunr.Query.wildcard.TRAILING 50 | }); 51 | }).map(function(result) { 52 | var doc = searchData[result.ref]; 53 | doc.url = result.ref; 54 | return doc; 55 | }); 56 | sync(results); 57 | } 58 | } 59 | ); 60 | $form.removeClass('loading'); 61 | $typeahead.trigger('focus'); 62 | }); 63 | }); 64 | 65 | var baseURL = searchURL.slice(0, -"search.json".length); 66 | 67 | $typeahead.on('typeahead:select', function(e, result) { 68 | window.location = baseURL + result.url; 69 | }); 70 | }); 71 | --------------------------------------------------------------------------------