]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/kern/ia32/vmx.cpp
762308c1c6caa5c4aa68dfe184749252e12bf6e8
[l4.git] / kernel / fiasco / src / kern / ia32 / vmx.cpp
1 INTERFACE [vmx]:
2
3 #include "per_cpu_data.h"
4 #include <cstdio>
5
6 class Vmx_info
7 {
8 public:
9   static bool nested_paging() { return false; }
10
11   template<typename T>
12   class Bit_defs
13   {
14   protected:
15     T _or;
16     T _and;
17
18   public:
19     Bit_defs() {}
20     Bit_defs(T _or, T _and) : _or(_or), _and(_and) {}
21 #if 0
22     template<typename T2>
23     explicit Bit_defs(Bit_defs<T2> const &o)
24     : _or(o.must_be_one()), _and(o.may_be_one()) {}
25 #endif
26     T must_be_one() const { return _or; }
27     T may_be_one() const { return _and; }
28
29   private:
30     void enforce_bits(T m, bool value = true)
31     {
32       if (value)
33         _or |= m;
34       else
35         _and &= ~m;
36     }
37
38     bool allowed_bits(T m, bool value = true) const
39     {
40       if (value)
41         return _and & m;
42       else
43         return !(_or & m);
44     }
45
46   public:
47     void enforce(unsigned char bit, bool value = true)
48     { enforce_bits((T)1 << (T)bit, value); }
49
50     bool allowed(unsigned char bit, bool value = true) const
51     { return allowed_bits((T)1 << (T)bit, value); }
52
53     T apply(T v) const { return (v | _or) & _and; }
54
55     void print(char const *name) const
56     {
57       if (sizeof(T) <= 4)
58         printf("%20s = %8x %8x\n", name, (unsigned)_and, (unsigned)_or);
59       else if (sizeof(T) <= 8)
60         printf("%20s = %16llx %16llx\n", name, (unsigned long long)_and, (unsigned long long)_or);
61     }
62   };
63
64   class Bit_defs_32 : public Bit_defs<Unsigned32>
65   {
66   public:
67     Bit_defs_32() {}
68     Bit_defs_32(Unsigned64 v) : Bit_defs<Unsigned32>(v, v >> 32) {}
69   };
70
71   typedef Bit_defs<Unsigned64> Bit_defs_64;
72
73   template<typename T>
74   class Flags
75   {
76   public:
77     Flags() {}
78     explicit Flags(T v) : _f(v) {}
79
80     T test(unsigned char bit) const { return _f & ((T)1 << (T)bit); }
81   private:
82     T _f;
83   };
84
85   Unsigned64 basic;
86
87   Bit_defs_32 pinbased_ctls;
88   Bit_defs_32 procbased_ctls;
89
90   Bit_defs_32 exit_ctls;
91   Bit_defs_32 entry_ctls;
92   Unsigned64 misc;
93
94   Bit_defs<Mword> cr0_defs;
95   Bit_defs<Mword> cr4_defs;
96   Bit_defs_32 procbased_ctls2;
97
98   Unsigned64 ept_vpid_cap;
99   Unsigned64 true_pinbased_ctls;
100   Unsigned64 true_procbased_ctls;
101   Unsigned64 true_exit_ctls;
102   Unsigned64 true_entry_ctls;
103 };
104
105 INTERFACE:
106
107 class Vmx
108 {
109 public:
110   enum Vmcs_fields
111   {
112     F_vpid               = 0x0,
113
114     F_host_es_selector   = 0x0c00,
115     F_host_cs_selector   = 0x0c02,
116     F_host_ss_selector   = 0x0c04,
117     F_host_ds_selector   = 0x0c06,
118     F_host_fs_selector   = 0x0c08,
119     F_host_gs_selector   = 0x0c0a,
120     F_host_tr_selector   = 0x0c0c,
121
122     F_tsc_offset         = 0x2010,
123     F_apic_access_addr   = 0x2014,
124
125     F_guest_pat          = 0x2804,
126     F_guest_efer         = 0x2806,
127
128     F_host_ia32_pat              = 0x2c00,
129     F_host_ia32_efer             = 0x2c02,
130     F_host_ia32_perf_global_ctrl = 0x2c04,
131
132
133     F_pin_based_ctls     = 0x4000,
134     F_proc_based_ctls    = 0x4002,
135
136     F_cr3_target_cnt     = 0x400a,
137     F_exit_ctls          = 0x400c,
138     F_exit_msr_store_cnt = 0x400e,
139     F_exit_msr_load_cnt  = 0x4010,
140     F_entry_ctls         = 0x4012,
141     F_entry_msr_load_cnt = 0x4014,
142     F_entry_int_info     = 0x4016,
143     F_entry_exc_error_code = 0x4018,
144     F_entry_insn_len     = 0x401a,
145     F_proc_based_ctls_2  = 0x401e,
146
147     F_preempt_timer      = 0x482e,
148
149     F_host_sysenter_cs   = 0x4c00,
150
151     F_guest_cr2          = 0x6830,
152
153     F_host_cr0           = 0x6c00,
154     F_host_cr3           = 0x6c02,
155     F_host_cr4           = 0x6c04,
156     F_host_fs_base       = 0x6c06,
157     F_host_gs_base       = 0x6c08,
158     F_host_tr_base       = 0x6c0a,
159     F_host_gdtr_base     = 0x6c0c,
160     F_host_idtr_base     = 0x6c0e,
161     F_host_sysenter_esp  = 0x6c10,
162     F_host_sysenter_eip  = 0x6c12,
163     F_host_rip           = 0x6c16,
164   };
165
166   enum Pin_based_ctls
167   {
168     PIB_ext_int_exit = 0,
169     PIB_nmi_exit     = 3,
170   };
171
172   enum Primary_proc_based_ctls
173   {
174     PRB1_tpr_shadow               = 21,
175     PRB1_unconditional_io_exit    = 24,
176     PRB1_use_io_bitmaps           = 25,
177     PRB1_use_msr_bitmaps          = 28,
178     PRB1_enable_proc_based_ctls_2 = 31,
179   };
180
181   enum Secondary_proc_based_ctls
182   {
183     PRB2_virtualize_apic = 0,
184     PRB2_enable_ept      = 1,
185     PRB2_enable_vpid     = 5,
186   };
187
188 };
189
190 INTERFACE [vmx]:
191
192 #include "virt.h"
193 #include "cpu_lock.h"
194
195 class Vmx_info;
196
197 EXTENSION class Vmx
198 {
199 public:
200   static Per_cpu<Vmx> cpus;
201   Vmx_info info;
202 private:
203   void *_vmxon;
204   bool _vmx_enabled;
205   bool _has_vpid;
206   Unsigned64 _vmxon_base_pa;
207   void *_kernel_vmcs;
208   Unsigned64 _kernel_vmcs_pa;
209 };
210
211 class Vmx_info_msr
212 {
213 private:
214   Unsigned64 _data;
215 };
216
217 //-----------------------------------------------------------------------------
218 INTERFACE [vmx && ia32]:
219
220 EXTENSION class Vmx
221 {
222 public:
223   enum { Gpregs_words = 11 };
224 };
225
226 //-----------------------------------------------------------------------------
227 INTERFACE [vmx && amd64]:
228
229 EXTENSION class Vmx
230 {
231 public:
232   enum { Gpregs_words = 19 };
233 };
234
235
236 // -----------------------------------------------------------------------
237 IMPLEMENTATION[vmx]:
238
239 #include "cpu.h"
240 #include "kmem.h"
241 #include "l4_types.h"
242 #include <cstring>
243 #include "idt.h"
244
245 Per_cpu<Vmx> DEFINE_PER_CPU_LATE Vmx::cpus(true);
246
247 PUBLIC
248 void
249 Vmx_info::init()
250 {
251   basic = Cpu::rdmsr(0x480);
252   pinbased_ctls = Cpu::rdmsr(0x481);
253   procbased_ctls = Cpu::rdmsr(0x482);
254   exit_ctls = Cpu::rdmsr(0x483);
255   entry_ctls = Cpu::rdmsr(0x484);
256   misc = Cpu::rdmsr(0x485);
257
258   cr0_defs = Bit_defs<Mword>(Cpu::rdmsr(0x486), Cpu::rdmsr(0x487));
259   cr4_defs = Bit_defs<Mword>(Cpu::rdmsr(0x488), Cpu::rdmsr(0x489));
260
261   if (basic & (1ULL << 55))
262     {
263       true_pinbased_ctls = Cpu::rdmsr(0x48d);
264       true_procbased_ctls = Cpu::rdmsr(0x48e);
265       true_exit_ctls = Cpu::rdmsr(0x48f);
266       true_entry_ctls = Cpu::rdmsr(0x490);
267     }
268
269   if (0)
270     dump("as read from hardware");
271
272   pinbased_ctls.enforce(Vmx::PIB_ext_int_exit);
273   pinbased_ctls.enforce(Vmx::PIB_nmi_exit);
274
275
276   // currently we IO-passthrough is missing, disable I/O bitmaps and enforce
277   // unconditional io exiting
278   procbased_ctls.enforce(Vmx::PRB1_use_io_bitmaps, false);
279   procbased_ctls.enforce(Vmx::PRB1_unconditional_io_exit);
280
281   // virtual APIC not yet supported
282   procbased_ctls.enforce(Vmx::PRB1_tpr_shadow, false);
283
284   if (procbased_ctls.allowed(31))
285     {
286       procbased_ctls2 = Cpu::rdmsr(0x48b);
287       if (procbased_ctls2.allowed(1))
288         ept_vpid_cap = Cpu::rdmsr(0x48c);
289
290       // we disable VPID so far, need to handle virtualize it in Fiasco,
291       // as done for AMDs ASIDs
292       procbased_ctls2.enforce(Vmx::PRB2_enable_vpid, false);
293
294       // no EPT support yet
295       procbased_ctls2.enforce(Vmx::PRB2_enable_ept, false);
296     }
297   else
298     procbased_ctls2 = 0;
299
300   // never automatically ack interrupts on exit
301   exit_ctls.enforce(15, false);
302
303   // host-state is 64bit or not
304   exit_ctls.enforce(9, sizeof(long) > sizeof(int));
305
306   if (!nested_paging()) // needs to be per VM
307     {
308       // always enable paging
309       cr0_defs.enforce(31);
310       // always PE
311       cr0_defs.enforce(0);
312       cr4_defs.enforce(4); // PSE
313
314       // enforce PAE on 64bit, and disallow it on 32bit
315       cr4_defs.enforce(5, sizeof(long) > sizeof(int));
316     }
317
318   if (0)
319     dump("as modified");
320 }
321
322 PUBLIC
323 void
324 Vmx_info::dump(const char *tag) const
325 {
326   printf("VMX MSRs %s:\n", tag);
327   printf("basic                = %16llx\n", basic);
328   pinbased_ctls.print("pinbased_ctls");
329   procbased_ctls.print("procbased_ctls");
330   exit_ctls.print("exit_ctls");
331   entry_ctls.print("entry_ctls");
332   printf("misc                 = %16llx\n", misc);
333   cr0_defs.print("cr0_fixed");
334   cr4_defs.print("cr4_fixed");
335   procbased_ctls2.print("procbased_ctls2");
336   printf("ept_vpid_cap         = %16llx\n", ept_vpid_cap);
337   printf("true_pinbased_ctls   = %16llx\n", true_pinbased_ctls);
338   printf("true_procbased_ctls  = %16llx\n", true_procbased_ctls);
339   printf("true_exit_ctls       = %16llx\n", true_exit_ctls);
340   printf("true_entry_ctls      = %16llx\n", true_entry_ctls);
341 }
342
343 PRIVATE static inline
344 Mword
345 Vmx::vmread_insn(Mword field)
346 {
347   Mword val;
348   asm volatile("vmread %1, %0" : "=r" (val) : "r" (field));
349   return val;
350 }
351
352 PUBLIC static inline NEEDS[Vmx::vmread_insn]
353 template< typename T >
354 T
355 Vmx::vmread(Mword field)
356 {
357   if (sizeof(T) <= sizeof(Mword))
358     return vmread_insn(field);
359
360   return vmread_insn(field) | ((Unsigned64)vmread_insn(field + 1) << 32);
361 }
362
363 PUBLIC static inline
364 template< typename T >
365 void
366 Vmx::vmwrite(Mword field, T value)
367 {
368   Mword err;
369   asm volatile("vmwrite %1, %2; pushf; pop %0" : "=r" (err) : "r" ((Mword)value), "r" (field));
370   if (EXPECT_FALSE(err & 0x41))
371     printf("FAILED vmwrite(%lx): field=%04lx with value %llx\n", err, field, (Unsigned64)value);
372   if (sizeof(T) > sizeof(Mword))
373     asm volatile("vmwrite %0, %1" : : "r" ((Unsigned64)value >> 32), "r" (field + 1));
374 }
375
376 static void vmxoff(void)
377 {
378   asm volatile("vmxoff");
379 }
380
381 PUBLIC
382 Vmx::Vmx(unsigned cpu)
383   : _vmx_enabled(false), _has_vpid(false)
384 {
385   Cpu &c = Cpu::cpus.cpu(cpu);
386   if (!c.vmx())
387     {
388       printf("VMX: Not supported\n");
389       return;
390     }
391
392   printf("VMX: Enabling\n");
393
394   // check whether vmx is enabled by BIOS
395   Unsigned64 feature = 0;
396   feature = Cpu::rdmsr(0x3a);
397
398   // vmxon outside SMX allowed?
399   if (!(feature & 0x4))
400     {
401       printf("VMX: CPU has VMX support but it is disabled\n");
402       return;
403     }
404
405   // check whether lock bit is set otherwise vmxon
406   // will cause a general-protection exception
407   if (!(feature & 0x1))
408     {
409       printf("VMX: Cannot enable VMX, lock bit not set\n");
410       return;
411     }
412
413   info.init();
414
415   // check for EPT support
416   if (info.procbased_ctls2.allowed(1))
417     printf("VMX:  EPT supported\n");
418   else
419     printf("VMX:  No EPT available\n");
420
421   // check for vpid support
422   if (info.procbased_ctls2.allowed(5))
423     _has_vpid = true;
424
425   c.set_cr4(c.get_cr4() | (1 << 13)); // set CR4.VMXE to 1
426
427   // if NE bit is not set vmxon will fail
428   c.set_cr0(c.get_cr0() | (1 << 5));
429
430   enum
431   {
432     Vmcs_size = 0x1000, // actual size may be different
433   };
434
435   Unsigned32 vmcs_size = ((info.basic & (0x1fffULL << 32)) >> 32);
436
437   if (vmcs_size > Vmcs_size)
438     {
439       printf("VMX: VMCS size of %d bytes not supported\n", vmcs_size);
440       return;
441     }
442
443   // allocate a 4kb region for kernel vmcs
444   check(_kernel_vmcs = Mapped_allocator::allocator()->alloc(12));
445   _kernel_vmcs_pa = Kmem::virt_to_phys(_kernel_vmcs);
446   // clean vmcs
447   memset(_kernel_vmcs, 0, vmcs_size);
448   // init vmcs with revision identifier
449   *(int *)_kernel_vmcs = (info.basic & 0xFFFFFFFF);
450
451   // allocate a 4kb aligned region for VMXON
452   check(_vmxon = Mapped_allocator::allocator()->alloc(12));
453
454   _vmxon_base_pa = Kmem::virt_to_phys(_vmxon);
455
456   // init vmxon region with vmcs revision identifier
457   // which is stored in the lower 32 bits of MSR 0x480
458   *(unsigned *)_vmxon = (info.basic & 0xFFFFFFFF);
459
460   // enable vmx operation
461   asm volatile("vmxon %0" : :"m"(_vmxon_base_pa):);
462   _vmx_enabled = true;
463
464   atexit(vmxoff);
465
466   printf("VMX: initialized\n");
467
468   Mword eflags;
469   asm volatile("vmclear %1 \n\t"
470                "pushf      \n\t"
471                "pop %0     \n\t" : "=r"(eflags) : "m"(_kernel_vmcs_pa):);
472   if (eflags & 0x41)
473     panic("VMX: vmclear: VMFailInvalid, vmcs pointer not valid\n");
474
475   // make kernel vmcs current
476   asm volatile("vmptrld %1 \n\t"
477                "pushf      \n\t"
478                "pop %0     \n\t" : "=r"(eflags) : "m"(_kernel_vmcs_pa):);
479
480   if (eflags & 0x41)
481     panic("VMX: vmptrld: VMFailInvalid, vmcs pointer not valid\n");
482
483   extern char entry_sys_fast_ipc_c[];
484   extern char vm_vmx_exit_vec[];
485
486   vmwrite(F_host_es_selector, GDT_DATA_KERNEL);
487   vmwrite(F_host_cs_selector, GDT_CODE_KERNEL);
488   vmwrite(F_host_ss_selector, GDT_DATA_KERNEL);
489   vmwrite(F_host_ds_selector, GDT_DATA_KERNEL);
490
491   Unsigned16 tr = c.get_tr();
492   vmwrite(F_host_tr_selector, tr);
493
494   vmwrite(F_host_tr_base, ((*c.get_gdt())[tr / 8]).base());
495   vmwrite(F_host_rip, vm_vmx_exit_vec);
496   vmwrite<Mword>(F_host_sysenter_cs, Gdt::gdt_code_kernel);
497   vmwrite(F_host_sysenter_esp, &c.kernel_sp());
498   vmwrite(F_host_sysenter_eip, entry_sys_fast_ipc_c);
499
500   if (c.features() & FEAT_PAT && info.exit_ctls.allowed(19))
501     vmwrite(F_host_ia32_pat, Cpu::rdmsr(MSR_PAT));
502   else
503     {
504       // We have no proper PAT support, so disallow PAT load store for
505       // guest too
506       info.exit_ctls.enforce(18, false);
507       info.entry_ctls.enforce(14, false);
508     }
509
510   if (info.exit_ctls.allowed(21)) // Load IA32_EFER
511     vmwrite(F_host_ia32_efer, Cpu::rdmsr(MSR_EFER));
512   else
513     {
514       // We have no EFER load for host, so disallow EFER load store for
515       // guest too
516       info.exit_ctls.enforce(20, false);
517       info.entry_ctls.enforce(15, false);
518     }
519
520   if (info.exit_ctls.allowed(12))
521     vmwrite(F_host_ia32_perf_global_ctrl, Cpu::rdmsr(0x199));
522   else
523     // do not allow Load IA32_PERF_GLOBAL_CTRL on entry
524     info.entry_ctls.enforce(13, false);
525
526   vmwrite(F_host_cr0, info.cr0_defs.apply(Cpu::get_cr0()));
527   vmwrite(F_host_cr4, info.cr4_defs.apply(Cpu::get_cr4()));
528
529   Pseudo_descriptor pseudo;
530   c.get_gdt()->get(&pseudo);
531
532   vmwrite(F_host_gdtr_base, pseudo.base());
533
534   Idt::get(&pseudo);
535   vmwrite(F_host_idtr_base, pseudo.base());
536
537   // init static guest area stuff
538   vmwrite(0x2800, ~0ULL); // link pointer
539   vmwrite(F_cr3_target_cnt, 0);
540
541   // MSR load / store disbaled
542   vmwrite(F_exit_msr_load_cnt, 0);
543   vmwrite(F_exit_msr_store_cnt, 0);
544   vmwrite(F_entry_msr_load_cnt, 0);
545
546 }
547
548 PUBLIC
549 void *
550 Vmx::kernel_vmcs() const
551 { return _kernel_vmcs; }
552
553 PUBLIC
554 Address
555 Vmx::kernel_vmcs_pa() const
556 { return _kernel_vmcs_pa; }
557
558 PUBLIC
559 bool
560 Vmx::vmx_enabled() const
561 { return _vmx_enabled; }
562
563 PUBLIC
564 bool
565 Vmx::has_vpid() const
566 { return _has_vpid; }