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 
ignore_msr(VmxPage * msr_bitmaps_page,bool ignore_writes,uint32_t msr)13 static 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
Create(ktl::unique_ptr<Guest> * out)35 zx_status_t Guest::Create(ktl::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     ktl::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 = ktl::move(guest);
86     return ZX_OK;
87 }
88 
~Guest()89 Guest::~Guest() {
90     free_vmx_state();
91 }
92 
SetTrap(uint32_t kind,zx_vaddr_t addr,size_t len,fbl::RefPtr<PortDispatcher> port,uint64_t key)93 zx_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, ktl::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, ktl::move(port), key);
132 }
133 
AllocVpid(uint16_t * vpid)134 zx_status_t Guest::AllocVpid(uint16_t* vpid) {
135     fbl::AutoLock lock(&vcpu_mutex_);
136     return vpid_allocator_.AllocId(vpid);
137 }
138 
FreeVpid(uint16_t vpid)139 zx_status_t Guest::FreeVpid(uint16_t vpid) {
140     fbl::AutoLock lock(&vcpu_mutex_);
141     return vpid_allocator_.FreeId(vpid);
142 }
143