]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/kern/ia32/vmx.cpp
update
[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 PUBLIC
377 Vmx::Vmx(unsigned cpu)
378   : _vmx_enabled(false), _has_vpid(false)
379 {
380   Cpu &c = Cpu::cpus.cpu(cpu);
381   if (!c.vmx())
382     {
383       printf("VMX: Not supported\n");
384       return;
385     }
386
387   printf("VMX: Enabling\n");
388
389   // check whether vmx is enabled by BIOS
390   Unsigned64 feature = 0;
391   feature = Cpu::rdmsr(0x3a);
392
393   // vmxon outside SMX allowed?
394   if (!(feature & 0x4))
395     {
396       printf("VMX: CPU has VMX support but it is disabled\n");
397       return;
398     }
399
400   // check whether lock bit is set otherwise vmxon
401   // will cause a general-protection exception
402   if (!(feature & 0x1))
403     {
404       printf("VMX: Cannot enable VMX, lock bit not set\n");
405       return;
406     }
407
408   info.init();
409
410   // check for EPT support
411   if (info.procbased_ctls2.allowed(1))
412     printf("VMX:  EPT supported\n");
413   else
414     printf("VMX:  No EPT available\n");
415
416   // check for vpid support
417   if (info.procbased_ctls2.allowed(5))
418     _has_vpid = true;
419
420   c.set_cr4(c.get_cr4() | (1 << 13)); // set CR4.VMXE to 1
421
422   // if NE bit is not set vmxon will fail
423   c.set_cr0(c.get_cr0() | (1 << 5));
424
425   enum
426   {
427     Vmcs_size = 0x1000, // actual size may be different
428   };
429
430   Unsigned32 vmcs_size = ((info.basic & (0x1fffULL << 32)) >> 32);
431
432   if (vmcs_size > Vmcs_size)
433     {
434       printf("VMX: VMCS size of %d bytes not supported\n", vmcs_size);
435       return;
436     }
437
438   // allocate a 4kb region for kernel vmcs
439   check(_kernel_vmcs = Mapped_allocator::allocator()->alloc(12));
440   _kernel_vmcs_pa = Kmem::virt_to_phys(_kernel_vmcs);
441   // clean vmcs
442   memset(_kernel_vmcs, 0, vmcs_size);
443   // init vmcs with revision identifier
444   *(int *)_kernel_vmcs = (info.basic & 0xFFFFFFFF);
445
446   // allocate a 4kb aligned region for VMXON
447   check(_vmxon = Mapped_allocator::allocator()->alloc(12));
448
449   _vmxon_base_pa = Kmem::virt_to_phys(_vmxon);
450
451   // init vmxon region with vmcs revision identifier
452   // which is stored in the lower 32 bits of MSR 0x480
453   *(unsigned *)_vmxon = (info.basic & 0xFFFFFFFF);
454
455   // enable vmx operation
456   asm volatile("vmxon %0" : :"m"(_vmxon_base_pa):);
457   _vmx_enabled = true;
458
459   printf("VMX: initialized\n");
460
461   Mword eflags;
462   asm volatile("vmclear %1 \n\t"
463                "pushf      \n\t"
464                "pop %0     \n\t" : "=r"(eflags) : "m"(_kernel_vmcs_pa):);
465   if (eflags & 0x41)
466     panic("VMX: vmclear: VMFailInvalid, vmcs pointer not valid\n");
467
468   // make kernel vmcs current
469   asm volatile("vmptrld %1 \n\t"
470                "pushf      \n\t"
471                "pop %0     \n\t" : "=r"(eflags) : "m"(_kernel_vmcs_pa):);
472
473   if (eflags & 0x41)
474     panic("VMX: vmptrld: VMFailInvalid, vmcs pointer not valid\n");
475
476   extern char entry_sys_fast_ipc_c[];
477   extern char vm_vmx_exit_vec[];
478
479   vmwrite(F_host_es_selector, GDT_DATA_KERNEL);
480   vmwrite(F_host_cs_selector, GDT_CODE_KERNEL);
481   vmwrite(F_host_ss_selector, GDT_DATA_KERNEL);
482   vmwrite(F_host_ds_selector, GDT_DATA_KERNEL);
483
484   Unsigned16 tr = c.get_tr();
485   vmwrite(F_host_tr_selector, tr);
486
487   vmwrite(F_host_tr_base, ((*c.get_gdt())[tr / 8]).base());
488   vmwrite(F_host_rip, vm_vmx_exit_vec);
489   vmwrite<Mword>(F_host_sysenter_cs, Gdt::gdt_code_kernel);
490   vmwrite(F_host_sysenter_esp, &c.kernel_sp());
491   vmwrite(F_host_sysenter_eip, entry_sys_fast_ipc_c);
492
493   if (c.features() & FEAT_PAT && info.exit_ctls.allowed(19))
494     vmwrite(F_host_ia32_pat, Cpu::rdmsr(MSR_PAT));
495   else
496     {
497       // We have no proper PAT support, so disallow PAT load store for
498       // guest too
499       info.exit_ctls.enforce(18, false);
500       info.entry_ctls.enforce(14, false);
501     }
502
503   if (info.exit_ctls.allowed(21)) // Load IA32_EFER
504     vmwrite(F_host_ia32_efer, Cpu::rdmsr(MSR_EFER));
505   else
506     {
507       // We have no EFER load for host, so disallow EFER load store for
508       // guest too
509       info.exit_ctls.enforce(20, false);
510       info.entry_ctls.enforce(15, false);
511     }
512
513   if (info.exit_ctls.allowed(12))
514     vmwrite(F_host_ia32_perf_global_ctrl, Cpu::rdmsr(0x199));
515   else
516     // do not allow Load IA32_PERF_GLOBAL_CTRL on entry
517     info.entry_ctls.enforce(13, false);
518
519   vmwrite(F_host_cr0, info.cr0_defs.apply(Cpu::get_cr0()));
520   vmwrite(F_host_cr4, info.cr4_defs.apply(Cpu::get_cr4()));
521
522   Pseudo_descriptor pseudo;
523   c.get_gdt()->get(&pseudo);
524
525   vmwrite(F_host_gdtr_base, pseudo.base());
526
527   Idt::get(&pseudo);
528   vmwrite(F_host_idtr_base, pseudo.base());
529
530   // init static guest area stuff
531   vmwrite(0x2800, ~0ULL); // link pointer
532   vmwrite(F_cr3_target_cnt, 0);
533
534   // MSR load / store disbaled
535   vmwrite(F_exit_msr_load_cnt, 0);
536   vmwrite(F_exit_msr_store_cnt, 0);
537   vmwrite(F_entry_msr_load_cnt, 0);
538
539 }
540
541 PUBLIC
542 void *
543 Vmx::kernel_vmcs() const
544 { return _kernel_vmcs; }
545
546 PUBLIC
547 Address
548 Vmx::kernel_vmcs_pa() const
549 { return _kernel_vmcs_pa; }
550
551 PUBLIC
552 bool
553 Vmx::vmx_enabled() const
554 { return _vmx_enabled; }
555
556 PUBLIC
557 bool
558 Vmx::has_vpid() const
559 { return _has_vpid; }