1/**
2 * \file
3 * \brief Contains VMKit kernel interface for version using VMX extensions.
4 */
5
6/*
7 * Copyright (c) 2014, University of Washington.
8 * All rights reserved.
9 *
10 * This file is distributed under the terms in the attached LICENSE file.
11 * If you do not find this file, copies can be found by writing to:
12 * ETH Zurich D-INFK, CAB F.78, Universitaetstr. 6, CH-8092 Zurich.
13 * Attn: Systems Group.
14 */
15
16#ifndef VMX_VMKIT_H
17#define VMX_VMKIT_H
18
19#include <arch/x86/x86.h>
20#include <arch/x86_64/x86.h>
21#include <barrelfish_kpi/vmkit.h>
22#include <barrelfish_kpi/vmx_controls.h>
23#include <barrelfish_kpi/vmx_encodings.h>
24#include <barrelfish_kpi/vmx_exit_reasons.h>
25
26#include <dev/ia32_dev.h>
27
28// Number of MSRs that are loaded on VM-exit
29#define VMX_MSR_COUNT 5
30
31// Size of the host MSR-load area
32#define VMX_MSR_AREA_SIZE (VMX_MSR_COUNT * 16)
33
34#define MSR_KERNEL_GS_BASE 0xc0000102
35#define MSR_STAR           0xc0000081
36#define MSR_LSTAR          0xc0000082
37#define MSR_CSTAR          0xc0000083
38#define MSR_SFMASK         0xc0000084
39
40#define CPUID_PA_WIDTH (0x80000008)
41#define VMX_PA_WIDTH_MASK (0xFF)
42#define CPUID_VMX (0x1)
43#define VMX_SUPPORT (1 << 5)
44
45#define TYPE_EXT_INTR (0)
46#define TYPE_NMI (2)
47#define TYPE_HW_EXCP (3)
48#define TYPE_SW_EXCP (6)
49
50#define RFLAGS_IF (1 << 9)
51#define RFLAGS_VM (1 << 17)
52
53#define EFER_LME (1UL << 8)
54#define EFER_LMA (1UL << 10)
55#define EFER_NXE (1UL << 11)
56
57#define CR0_PE (1 << 0)
58#define CR0_NW (1 << 29)
59#define CR0_CD (1 << 30)
60#define CR0_PG (1 << 31)
61
62#define CR4_PAE (1UL << 5)
63#define CR4_MCE (1UL << 6)
64#define CR4_PGE (1UL << 7)
65#define CR4_PCE (1UL << 8)
66#define CR4_VMXE (1UL << 13)
67#define CR4_PCIDE (1UL << 17)
68
69#define DWORD_MS(val) ((val >> 32) & 0xFFFFFFFF)
70#define DWORD_LS(val) (val & 0xFFFFFFFF)
71
72union vmcs_prelude {
73    uint32_t raw;
74    struct {
75        uint32_t revision_id :31;
76        uint32_t shadow      :1;
77    } p;
78};
79
80// Represents a VMCS structure which is comprised of up to 4-KB,
81// the data portion of the structure is implemenation-specific.
82struct vmcs {
83    union vmcs_prelude prelude;
84    uint32_t vmx_abort;
85    char data[PAGE_SIZE - 8];
86} __attribute__ ((aligned(PAGE_SIZE)));
87
88static const int benign_exceptions[] = {1,2,3,4,5,6,7,9,16,17,18,19};
89static inline bool benign_exception(int vector)
90{
91    for (int i = 0; i < sizeof(benign_exceptions); i++) {
92        if (benign_exceptions[i] == vector) return true;
93    }
94    return false;
95}
96
97static const int contributory_exceptions[] = {0,10,11,12,13};
98static inline bool contributory_exception(int vector)
99{
100    for (int i = 0; i < sizeof(contributory_exceptions); i++) {
101        if (contributory_exceptions[i] == vector) return true;
102    }
103    return false;
104}
105
106// Returns true if secondary processor-based VM-execution controls
107// are used.
108static inline bool sec_ctls_used(uint64_t pp_controls)
109{
110    return !!(pp_controls & PP_CLTS_SEC_CTLS);
111}
112
113// Returns the canonical form of the address addr.
114static inline uint64_t canonical_form(uint64_t addr)
115{
116    if ((addr >> 47) & 0x1) {
117        return (addr | ~0xffffffffffffUL);
118    } else {
119        return (addr & 0xffffffffffffUL);
120    }
121}
122
123// Functions for reading segment registers are used in saving the
124// host state (via vmwrite instructions) prior to VM-entry
125
126static inline uint16_t rd_es(void)
127{
128    uint16_t es;
129    __asm volatile("mov %%es, %[es]" : [es] "=r" (es));
130    return es;
131}
132
133static inline uint16_t rd_cs(void)
134{
135    uint16_t cs;
136    __asm volatile("mov %%cs, %[cs]" : [cs] "=r" (cs));
137    return cs;
138}
139
140static inline uint16_t rd_ss(void)
141{
142    uint16_t ss;
143    __asm volatile("mov %%ss, %[ss]" : [ss] "=r" (ss));
144    return ss;
145}
146
147static inline uint16_t rd_ds(void)
148{
149    uint16_t ds;
150    __asm volatile("mov %%ds, %[ds]" : [ds] "=r" (ds));
151    return ds;
152}
153
154static inline uint16_t rd_fs(void)
155{
156    uint16_t fs;
157    __asm volatile("mov %%fs, %[fs]" : [fs] "=r" (fs));
158    return fs;
159}
160
161static inline uint16_t rd_gs(void)
162{
163    uint16_t gs;
164    __asm volatile("mov %%gs, %[gs]" : [gs] "=r" (gs));
165    return gs;
166}
167
168static inline uint16_t rd_tr(void)
169{
170    uint16_t tr;
171    __asm volatile("str %[tr]" : [tr] "=r" (tr));
172    return tr;
173}
174
175static inline uint64_t rd_idtr(void)
176{
177    uint64_t idtr;
178    __asm volatile("sidt %[idtr]" : [idtr] "=m" (idtr));
179    return idtr;
180}
181
182static inline uint16_t rd_ldtr(void)
183{
184    uint16_t ldtr;
185    __asm volatile("sldt %[ldtr]" : [ldtr] "=r" (ldtr));
186    return ldtr;
187}
188
189static inline void wr_ldtr(uint16_t val)
190{
191  __asm volatile("lldt %[val]" :: [val] "m" (val) : "memory");
192}
193
194static inline uint64_t rd_gdtr(void)
195{
196    uint64_t gdtr;
197    __asm volatile("sgdt %[gdtr]" : [gdtr] "=m" (gdtr));
198    return gdtr;
199}
200
201static inline uint64_t gdtr_addr(uint64_t gdtr)
202{
203    return canonical_form(gdtr >> 16);
204}
205
206static inline uint64_t idtr_addr(uint64_t idtr)
207{
208    return canonical_form(idtr >> 16);
209}
210
211// Return true if the segment selector is in the the LDT (the TI flag
212// is set), else false, meaning that it's contained in the GDT.
213static inline int seg_in_ldt(uint16_t seg_sel)
214{
215    return ((seg_sel >> 2) & 0x1);
216}
217
218// The index of corresponding segment descriptor in the LDT or GDT.
219static inline int seg_sel_index(uint16_t seg_sel)
220{
221    return ((seg_sel >> 3) & 0x1FFF);
222}
223
224static inline uint64_t tr_addr(uint16_t tr_sel, uint64_t gdtr_base)
225{
226    int tss_index_low  = seg_sel_index(tr_sel);
227    int tss_index_high = tss_index_low + 1;
228
229    uint64_t *gdt_new = (uint64_t *)gdtr_base;
230    uint64_t tss_low = gdt_new[tss_index_low];
231    uint64_t tss_high = gdt_new[tss_index_high];
232
233    uint64_t tss_base = (((tss_low >> 16) & 0xFFFFFF)   |
234                         ((tss_low >> 32) & 0xFF000000) |
235                         (tss_high << 32));
236    return tss_base;
237}
238
239static inline void vmlaunch(void)
240{
241    __asm volatile("vmlaunch");
242}
243
244static inline void vmresume(void)
245{
246    __asm volatile("vmresume");
247}
248
249static inline void enable_vmx(void)
250{
251    wrcr4(rdcr4() | CR4_VMXE);
252}
253
254static inline int vmx_enabled(void)
255{
256    return (rdcr4() & CR4_VMXE);
257}
258
259static inline void disable_vmx(void)
260{
261    wrcr4(rdcr4() & ~CR4_VMXE);
262}
263
264static inline uint64_t vmx_fixed_cr0(void)
265{
266    uint64_t cr0_fixed0 = ia32_vmx_cr0_fixed0_rd(NULL);
267    uint64_t cr0_fixed1 = ia32_vmx_cr0_fixed1_rd(NULL);
268
269    uint64_t cr0_1s_mask = (cr0_fixed0 & cr0_fixed1);
270    uint64_t cr0_0s_mask = (~cr0_fixed0 & ~cr0_fixed1);
271    return ((rdcr0() | cr0_1s_mask) & ~cr0_0s_mask);
272}
273
274static inline uint64_t vmx_fixed_cr4(void)
275{
276    uint64_t cr4_fixed0 = ia32_vmx_cr4_fixed0_rd(NULL);
277    uint64_t cr4_fixed1 = ia32_vmx_cr4_fixed1_rd(NULL);
278
279    uint64_t cr4_1s_mask = (cr4_fixed0 & cr4_fixed1);
280    uint64_t cr4_0s_mask = (~cr4_fixed0 & ~cr4_fixed1);
281    return ((rdcr4() | cr4_1s_mask) & ~cr4_0s_mask);
282}
283
284static inline bool is_canonical(uint64_t addr)
285{
286    uint64_t canonical_addr = canonical_form(addr);
287    return (canonical_addr == addr);
288}
289
290// Returns the physical-address width supported by the logical
291// processor.
292static inline uint64_t physical_address_width(void)
293{
294    uint32_t cpuid_eax;
295    cpuid(CPUID_PA_WIDTH, &cpuid_eax, NULL, NULL, NULL);
296    return (cpuid_eax & VMX_PA_WIDTH_MASK);
297}
298
299// Returns a mask for the bits 0:N-1, where N is the physical-address
300// width supported by the logical processor.
301static inline uint64_t pa_width_mask(void)
302{
303    uint64_t pa_width = physical_address_width();
304    uint64_t physical_addr_width_mask = (1UL << pa_width) - 1;
305    return physical_addr_width_mask;
306}
307
308errval_t vmptrld(lpaddr_t vmcs_base);
309lpaddr_t vmptrst(void);
310errval_t vmclear(lpaddr_t vmcs_base);
311errval_t vmread(uintptr_t encoding, lvaddr_t *dest_addr);
312errval_t vmwrite(uintptr_t encoding, uintptr_t source);
313errval_t vmxon(lpaddr_t base_addr);
314errval_t vmxoff(void);
315
316errval_t initialize_vmcs(lpaddr_t vmcs_base);
317void vmx_set_exec_ctls(void);
318
319errval_t vmx_enable_virtualization (void);
320void __attribute__ ((noreturn)) vmx_vmkit_vmenter (struct dcb *dcb);
321
322#endif // VMX_VMKIT_H
323