1// Copyright 2017 The Fuchsia Authors 2// 3// Use of this source code is governed by a MIT-style 4// license that can be found in the LICENSE file or at 5// https://opensource.org/licenses/MIT 6 7#include <arch/x86/apic.h> 8#include <arch/x86/feature.h> 9#include <zircon/syscalls/hypervisor.h> 10 11#include "vmx_cpu_state_priv.h" 12 13static void ignore_msr(VmxPage* msr_bitmaps_page, bool ignore_writes, uint32_t msr) { 14 // From Volume 3, Section 24.6.9. 15 uint8_t* msr_bitmaps = msr_bitmaps_page->VirtualAddress<uint8_t>(); 16 if (msr >= 0xc0000000) { 17 msr_bitmaps += 1 << 10; 18 } 19 20 uint16_t msr_low = msr & 0x1fff; 21 uint16_t msr_byte = msr_low / 8; 22 uint8_t msr_bit = msr_low % 8; 23 24 // Ignore reads to the MSR. 25 msr_bitmaps[msr_byte] &= (uint8_t) ~(1 << msr_bit); 26 27 if (ignore_writes) { 28 // Ignore writes to the MSR. 29 msr_bitmaps += 2 << 10; 30 msr_bitmaps[msr_byte] &= (uint8_t) ~(1 << msr_bit); 31 } 32} 33 34// static 35zx_status_t Guest::Create(fbl::unique_ptr<Guest>* out) { 36 // Check that the CPU supports VMX. 37 if (!x86_feature_test(X86_FEATURE_VMX)) { 38 return ZX_ERR_NOT_SUPPORTED; 39 } 40 41 zx_status_t status = alloc_vmx_state(); 42 if (status != ZX_OK) { 43 return status; 44 } 45 46 fbl::AllocChecker ac; 47 fbl::unique_ptr<Guest> guest(new (&ac) Guest); 48 if (!ac.check()) { 49 return ZX_ERR_NO_MEMORY; 50 } 51 52 status = hypervisor::GuestPhysicalAddressSpace::Create(&guest->gpas_); 53 if (status != ZX_OK) { 54 return status; 55 } 56 57 // Setup common MSR bitmaps. 58 VmxInfo vmx_info; 59 status = guest->msr_bitmaps_page_.Alloc(vmx_info, UINT8_MAX); 60 if (status != ZX_OK) { 61 return status; 62 } 63 64 ignore_msr(&guest->msr_bitmaps_page_, true, X86_MSR_IA32_PAT); 65 ignore_msr(&guest->msr_bitmaps_page_, true, X86_MSR_IA32_EFER); 66 ignore_msr(&guest->msr_bitmaps_page_, true, X86_MSR_IA32_FS_BASE); 67 ignore_msr(&guest->msr_bitmaps_page_, true, X86_MSR_IA32_GS_BASE); 68 ignore_msr(&guest->msr_bitmaps_page_, true, X86_MSR_IA32_KERNEL_GS_BASE); 69 ignore_msr(&guest->msr_bitmaps_page_, true, X86_MSR_IA32_STAR); 70 ignore_msr(&guest->msr_bitmaps_page_, true, X86_MSR_IA32_LSTAR); 71 ignore_msr(&guest->msr_bitmaps_page_, true, X86_MSR_IA32_FMASK); 72 ignore_msr(&guest->msr_bitmaps_page_, true, X86_MSR_IA32_TSC_ADJUST); 73 ignore_msr(&guest->msr_bitmaps_page_, true, X86_MSR_IA32_TSC_AUX); 74 ignore_msr(&guest->msr_bitmaps_page_, true, X86_MSR_IA32_SYSENTER_CS); 75 ignore_msr(&guest->msr_bitmaps_page_, true, X86_MSR_IA32_SYSENTER_ESP); 76 ignore_msr(&guest->msr_bitmaps_page_, true, X86_MSR_IA32_SYSENTER_EIP); 77 78 // Setup VPID allocator 79 fbl::AutoLock lock(&guest->vcpu_mutex_); 80 status = guest->vpid_allocator_.Init(); 81 if (status != ZX_OK) { 82 return status; 83 } 84 85 *out = fbl::move(guest); 86 return ZX_OK; 87} 88 89Guest::~Guest() { 90 free_vmx_state(); 91} 92 93zx_status_t Guest::SetTrap(uint32_t kind, zx_vaddr_t addr, size_t len, 94 fbl::RefPtr<PortDispatcher> port, uint64_t key) { 95 if (len == 0) { 96 return ZX_ERR_INVALID_ARGS; 97 } else if (SIZE_MAX - len < addr) { 98 return ZX_ERR_OUT_OF_RANGE; 99 } 100 101 switch (kind) { 102 case ZX_GUEST_TRAP_MEM: 103 if (port) { 104 return ZX_ERR_INVALID_ARGS; 105 } 106 break; 107 case ZX_GUEST_TRAP_BELL: 108 if (!port) { 109 return ZX_ERR_INVALID_ARGS; 110 } 111 break; 112 case ZX_GUEST_TRAP_IO: 113 if (port) { 114 return ZX_ERR_INVALID_ARGS; 115 } else if (addr + len > UINT16_MAX) { 116 return ZX_ERR_OUT_OF_RANGE; 117 } 118 return traps_.InsertTrap(kind, addr, len, fbl::move(port), key); 119 default: 120 return ZX_ERR_INVALID_ARGS; 121 } 122 123 // Common logic for memory-based traps. 124 if (!IS_PAGE_ALIGNED(addr) || !IS_PAGE_ALIGNED(len)) { 125 return ZX_ERR_INVALID_ARGS; 126 } 127 zx_status_t status = gpas_->UnmapRange(addr, len); 128 if (status != ZX_OK) { 129 return status; 130 } 131 return traps_.InsertTrap(kind, addr, len, fbl::move(port), key); 132} 133 134zx_status_t Guest::AllocVpid(uint16_t* vpid) { 135 fbl::AutoLock lock(&vcpu_mutex_); 136 return vpid_allocator_.AllocId(vpid); 137} 138 139zx_status_t Guest::FreeVpid(uint16_t vpid) { 140 fbl::AutoLock lock(&vcpu_mutex_); 141 return vpid_allocator_.FreeId(vpid); 142} 143