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 "vmx_cpu_state_priv.h"
8 
9 #include <assert.h>
10 #include <bits.h>
11 #include <string.h>
12 
13 #include <hypervisor/cpu.h>
14 #include <kernel/auto_lock.h>
15 #include <kernel/mp.h>
16 
17 #include <fbl/mutex.h>
18 
19 static fbl::Mutex guest_mutex;
20 static size_t num_guests TA_GUARDED(guest_mutex) = 0;
21 static fbl::Array<VmxPage> vmxon_pages TA_GUARDED(guest_mutex);
22 
vmxon(paddr_t pa)23 static zx_status_t vmxon(paddr_t pa) {
24     uint8_t err;
25 
26     __asm__ volatile(
27         "vmxon %[pa];" VMX_ERR_CHECK(err)
28         : [err] "=r"(err)
29         : [pa] "m"(pa)
30         : "cc", "memory");
31 
32     return err ? ZX_ERR_INTERNAL : ZX_OK;
33 }
34 
vmxoff()35 static zx_status_t vmxoff() {
36     uint8_t err;
37 
38     __asm__ volatile(
39         "vmxoff;" VMX_ERR_CHECK(err)
40         : [err] "=r"(err)
41         :
42         : "cc");
43 
44     return err ? ZX_ERR_INTERNAL : ZX_OK;
45 }
46 
VmxInfo()47 VmxInfo::VmxInfo() {
48     // From Volume 3, Appendix A.1.
49     uint64_t basic_info = read_msr(X86_MSR_IA32_VMX_BASIC);
50     revision_id = static_cast<uint32_t>(BITS(basic_info, 30, 0));
51     region_size = static_cast<uint16_t>(BITS_SHIFT(basic_info, 44, 32));
52     write_back = BITS_SHIFT(basic_info, 53, 50) == VMX_MEMORY_TYPE_WRITE_BACK;
53     io_exit_info = BIT_SHIFT(basic_info, 54);
54     vmx_controls = BIT_SHIFT(basic_info, 55);
55 }
56 
EptInfo()57 EptInfo::EptInfo() {
58     // From Volume 3, Appendix A.10.
59     uint64_t ept_info = read_msr(X86_MSR_IA32_VMX_EPT_VPID_CAP);
60     page_walk_4 = BIT_SHIFT(ept_info, 6);
61     write_back = BIT_SHIFT(ept_info, 14);
62     invept =
63         // INVEPT instruction is supported.
64         BIT_SHIFT(ept_info, 20) &&
65         // Single-context INVEPT type is supported.
66         BIT_SHIFT(ept_info, 25) &&
67         // All-context INVEPT type is supported.
68         BIT_SHIFT(ept_info, 26);
69 }
70 
Alloc(const VmxInfo & vmx_info,uint8_t fill)71 zx_status_t VmxPage::Alloc(const VmxInfo& vmx_info, uint8_t fill) {
72     // From Volume 3, Appendix A.1: Bits 44:32 report the number of bytes that
73     // software should allocate for the VMXON region and any VMCS region. It is
74     // a value greater than 0 and at most 4096 (bit 44 is set if and only if
75     // bits 43:32 are clear).
76     if (vmx_info.region_size > PAGE_SIZE)
77         return ZX_ERR_NOT_SUPPORTED;
78 
79     // Check use of write-back memory for VMX regions is supported.
80     if (!vmx_info.write_back)
81         return ZX_ERR_NOT_SUPPORTED;
82 
83     // The maximum size for a VMXON or VMCS region is 4096, therefore
84     // unconditionally allocating a page is adequate.
85     return hypervisor::Page::Alloc(fill);
86 }
87 
vmxon_task(void * context,cpu_num_t cpu_num)88 static zx_status_t vmxon_task(void* context, cpu_num_t cpu_num) {
89     auto pages = static_cast<fbl::Array<VmxPage>*>(context);
90     VmxPage& page = (*pages)[cpu_num];
91 
92     // Check that we have instruction information when we VM exit on IO.
93     VmxInfo vmx_info;
94     if (!vmx_info.io_exit_info)
95         return ZX_ERR_NOT_SUPPORTED;
96 
97     // Check that full VMX controls are supported.
98     if (!vmx_info.vmx_controls)
99         return ZX_ERR_NOT_SUPPORTED;
100 
101     // Check that a page-walk length of 4 is supported.
102     EptInfo ept_info;
103     if (!ept_info.page_walk_4)
104         return ZX_ERR_NOT_SUPPORTED;
105 
106     // Check use write-back memory for EPT is supported.
107     if (!ept_info.write_back)
108         return ZX_ERR_NOT_SUPPORTED;
109 
110     // Check that the INVEPT instruction is supported.
111     if (!ept_info.invept)
112         return ZX_ERR_NOT_SUPPORTED;
113 
114     // Enable VMXON, if required.
115     uint64_t feature_control = read_msr(X86_MSR_IA32_FEATURE_CONTROL);
116     if (!(feature_control & X86_MSR_IA32_FEATURE_CONTROL_LOCK) ||
117         !(feature_control & X86_MSR_IA32_FEATURE_CONTROL_VMXON)) {
118         if ((feature_control & X86_MSR_IA32_FEATURE_CONTROL_LOCK) &&
119             !(feature_control & X86_MSR_IA32_FEATURE_CONTROL_VMXON)) {
120             return ZX_ERR_NOT_SUPPORTED;
121         }
122         feature_control |= X86_MSR_IA32_FEATURE_CONTROL_LOCK;
123         feature_control |= X86_MSR_IA32_FEATURE_CONTROL_VMXON;
124         write_msr(X86_MSR_IA32_FEATURE_CONTROL, feature_control);
125     }
126 
127     // Check control registers are in a VMX-friendly state.
128     uint64_t cr0 = x86_get_cr0();
129     if (cr_is_invalid(cr0, X86_MSR_IA32_VMX_CR0_FIXED0, X86_MSR_IA32_VMX_CR0_FIXED1))
130         return ZX_ERR_BAD_STATE;
131     uint64_t cr4 = x86_get_cr4() | X86_CR4_VMXE;
132     if (cr_is_invalid(cr4, X86_MSR_IA32_VMX_CR4_FIXED0, X86_MSR_IA32_VMX_CR4_FIXED1))
133         return ZX_ERR_BAD_STATE;
134 
135     // Enable VMX using the VMXE bit.
136     x86_set_cr4(cr4);
137 
138     // Setup VMXON page.
139     VmxRegion* region = page.VirtualAddress<VmxRegion>();
140     region->revision_id = vmx_info.revision_id;
141 
142     // Execute VMXON.
143     zx_status_t status = vmxon(page.PhysicalAddress());
144     if (status != ZX_OK) {
145         dprintf(CRITICAL, "Failed to turn on VMX on CPU %u\n", cpu_num);
146         return status;
147     }
148 
149     return ZX_OK;
150 }
151 
vmxoff_task(void * arg)152 static void vmxoff_task(void* arg) {
153     // Execute VMXOFF.
154     zx_status_t status = vmxoff();
155     if (status != ZX_OK) {
156         dprintf(CRITICAL, "Failed to turn off VMX on CPU %u\n", arch_curr_cpu_num());
157         return;
158     }
159 
160     // Disable VMX.
161     x86_set_cr4(x86_get_cr4() & ~X86_CR4_VMXE);
162 }
163 
alloc_vmx_state()164 zx_status_t alloc_vmx_state() {
165     fbl::AutoLock lock(&guest_mutex);
166     if (num_guests == 0) {
167         fbl::AllocChecker ac;
168         size_t num_cpus = arch_max_num_cpus();
169         VmxPage* pages_ptr = new (&ac) VmxPage[num_cpus];
170         if (!ac.check())
171             return ZX_ERR_NO_MEMORY;
172         fbl::Array<VmxPage> pages(pages_ptr, num_cpus);
173         VmxInfo vmx_info;
174         for (auto& page : pages) {
175             zx_status_t status = page.Alloc(vmx_info, 0);
176             if (status != ZX_OK)
177                 return status;
178         }
179 
180         // Enable VMX for all online CPUs.
181         cpu_mask_t cpu_mask = percpu_exec(vmxon_task, &pages);
182         if (cpu_mask != mp_get_online_mask()) {
183             mp_sync_exec(MP_IPI_TARGET_MASK, cpu_mask, vmxoff_task, nullptr);
184             return ZX_ERR_NOT_SUPPORTED;
185         }
186 
187         vmxon_pages = ktl::move(pages);
188     }
189     num_guests++;
190     return ZX_OK;
191 }
192 
free_vmx_state()193 zx_status_t free_vmx_state() {
194     fbl::AutoLock lock(&guest_mutex);
195     num_guests--;
196     if (num_guests == 0) {
197         mp_sync_exec(MP_IPI_TARGET_ALL, 0, vmxoff_task, nullptr);
198         vmxon_pages.reset();
199     }
200     return ZX_OK;
201 }
202 
cr_is_invalid(uint64_t cr_value,uint32_t fixed0_msr,uint32_t fixed1_msr)203 bool cr_is_invalid(uint64_t cr_value, uint32_t fixed0_msr, uint32_t fixed1_msr) {
204     uint64_t fixed0 = read_msr(fixed0_msr);
205     uint64_t fixed1 = read_msr(fixed1_msr);
206     return ~(cr_value | ~fixed0) != 0 || ~(~cr_value | fixed1) != 0;
207 }
208