]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/kern/ia32/vm_vmx.cpp
6d8ebd6d52464fd209aad84e4e7c49dcb94585f8
[l4.git] / kernel / fiasco / src / kern / ia32 / vm_vmx.cpp
1 INTERFACE [vmx]:
2
3 #include "config.h"
4 #include "per_cpu_data.h"
5 #include "vm.h"
6 #include "vmx.h"
7
8 class Vmcs;
9
10 class Vm_vmx : public Vm
11 {
12 private:
13   static unsigned long resume_vm_vmx(Vcpu_state *regs)
14     asm("resume_vm_vmx") __attribute__((__regparm__(3)));
15
16   enum
17   {
18     EFER_LME = 1 << 8,
19     EFER_LMA = 1 << 10,
20   };
21
22 };
23
24 //----------------------------------------------------------------------------
25 IMPLEMENTATION [vmx]:
26
27 #include "context.h"
28 #include "mem_space.h"
29 #include "fpu.h"
30 #include "ref_ptr.h"
31 #include "thread.h" // XXX: circular dep, move this out here!
32 #include "thread_state.h" // XXX: circular dep, move this out here!
33 #include "virt.h"
34 #include "idt.h"
35
36
37 PUBLIC
38 Vm_vmx::Vm_vmx(Ram_quota *q)
39   : Vm(q)
40 {}
41
42 PUBLIC inline
43 void *
44 Vm_vmx::operator new (size_t size, void *p) throw()
45 {
46   (void)size;
47   assert (size == sizeof (Vm_vmx));
48   return p;
49 }
50
51 PUBLIC
52 void
53 Vm_vmx::operator delete (void *ptr)
54 {
55   Vm_vmx *t = reinterpret_cast<Vm_vmx*>(ptr);
56   allocator<Vm_vmx>()->q_free(t->ram_quota(), ptr);
57 }
58
59
60
61
62 PRIVATE static inline
63 void *
64 Vm_vmx::field_offset(void *vmcs, unsigned field)
65 {
66   return (void *)((char *)vmcs
67                           + ((field >> 13) * 4 + ((field >> 10) & 3) + 1) * 0x80);
68 }
69
70 PRIVATE static inline
71 unsigned
72 Vm_vmx::field_width(unsigned field)
73 {
74   static const char widths[4] = { 2, 8, 4, sizeof(Mword) };
75   return widths[field >> 13];
76 }
77
78
79 PRIVATE inline
80 template<typename T>
81 Vmx_info::Flags<T>
82 Vm_vmx::load(unsigned field, void *vmcs, Vmx_info::Bit_defs<T> const &m)
83 {
84   T res = m.apply(read<T>(vmcs, field));
85   Vmx::vmwrite(field, res);
86   return Vmx_info::Flags<T>(res);
87 }
88
89 PRIVATE inline
90 void
91 Vm_vmx::load(unsigned field_first, unsigned field_last, void *vmcs)
92 {
93   for (; field_first <= field_last; field_first += 2)
94     load(field_first, vmcs);
95 }
96
97 PRIVATE inline static
98 template< typename T >
99 T
100 Vm_vmx::_internal_read(void *vmcs, unsigned field)
101 {
102   vmcs = field_offset(vmcs, field);
103   return *((T *)vmcs + ((field >> 1) & 0xff));
104 }
105
106 PRIVATE inline static
107 template< typename T >
108 void
109 Vm_vmx::_internal_write(void *vmcs, unsigned field, T value)
110 {
111   vmcs = field_offset(vmcs, field);
112   *((T*)vmcs + ((field >> 1) & 0xff)) = value;
113 }
114
115 PRIVATE inline
116 void
117 Vm_vmx::load(unsigned field, void *vmcs)
118 {
119   switch (field >> 13)
120     {
121     case 0: Vmx::vmwrite(field, _internal_read<Unsigned16>(vmcs, field)); break;
122     case 1: Vmx::vmwrite(field, _internal_read<Unsigned64>(vmcs, field)); break;
123     case 2: Vmx::vmwrite(field, _internal_read<Unsigned32>(vmcs, field)); break;
124     case 3: Vmx::vmwrite(field, _internal_read<Mword>(vmcs, field)); break;
125     }
126 }
127
128 PRIVATE inline
129 void
130 Vm_vmx::store(unsigned field, void *vmcs)
131 {
132   switch (field >> 13)
133     {
134     case 0: _internal_write(vmcs, field, Vmx::vmread<Unsigned16>(field)); break;
135     case 1: _internal_write(vmcs, field, Vmx::vmread<Unsigned64>(field)); break;
136     case 2: _internal_write(vmcs, field, Vmx::vmread<Unsigned32>(field)); break;
137     case 3: _internal_write(vmcs, field, Vmx::vmread<Mword>(field)); break;
138     }
139 }
140
141 PRIVATE inline
142 void
143 Vm_vmx::store(unsigned field_first, unsigned field_last, void *vmcs)
144 {
145   for (; field_first <= field_last; field_first += 2)
146     store(field_first, vmcs);
147 }
148
149 PRIVATE inline static
150 template< typename T >
151 void
152 Vm_vmx::write(void *vmcs, unsigned field, T value)
153 {
154   switch (field >> 13)
155     {
156     case 0: _internal_write(vmcs, field, (Unsigned16)value); break;
157     case 1: _internal_write(vmcs, field, (Unsigned64)value); break;
158     case 2: _internal_write(vmcs, field, (Unsigned32)value); break;
159     case 3: _internal_write(vmcs, field, (Mword)value); break;
160     }
161 }
162
163 PRIVATE inline static
164 template< typename T >
165 T
166 Vm_vmx::read(void *vmcs, unsigned field)
167 {
168   switch (field >> 13)
169     {
170     case 0: return _internal_read<Unsigned16>(vmcs, field);
171     case 1: return _internal_read<Unsigned64>(vmcs, field);
172     case 2: return _internal_read<Unsigned32>(vmcs, field);
173     case 3: return _internal_read<Mword>(vmcs, field);
174     }
175   return 0;
176 }
177
178
179 PRIVATE
180 void
181 Vm_vmx::load_guest_state(unsigned cpu, void *src)
182 {
183   Vmx &vmx = Vmx::cpus.cpu(cpu);
184
185   // read VM-entry controls, apply filter and keep for later
186   Vmx_info::Flags<Unsigned32> entry_ctls
187     = load<Unsigned32>(Vmx::F_entry_ctls, src, vmx.info.entry_ctls);
188
189   Vmx_info::Flags<Unsigned32> pinbased_ctls
190     = load<Unsigned32>(Vmx::F_pin_based_ctls, src, vmx.info.pinbased_ctls);
191
192   Vmx_info::Flags<Unsigned32> procbased_ctls
193     = load<Unsigned32>(Vmx::F_proc_based_ctls, src, vmx.info.procbased_ctls);
194
195   Vmx_info::Flags<Unsigned32> procbased_ctls_2;
196   if (procbased_ctls.test(Vmx::PRB1_enable_proc_based_ctls_2))
197     procbased_ctls_2 = load<Unsigned32>(Vmx::F_proc_based_ctls_2, src, vmx.info.procbased_ctls2);
198   else
199     procbased_ctls_2 = Vmx_info::Flags<Unsigned32>(0);
200
201   load<Unsigned32>(Vmx::F_exit_ctls, src, vmx.info.exit_ctls);
202
203   // write 16-bit fields
204   load(0x800, 0x80e, src);
205
206   // write 64-bit fields
207   load(0x2802, src);
208
209   // check if the following bits are allowed to be set in entry_ctls
210   if (entry_ctls.test(14)) // PAT load requested
211     load(0x2804, src);
212
213   if (entry_ctls.test(15)) // EFER load requested
214     load(0x2806, src);
215
216   if (entry_ctls.test(13)) // IA32_PERF_GLOBAL_CTRL load requested
217     load(0x2808, src);
218
219   // this is Fiasco.OC internal state
220 #if 0
221   if (vmx.has_ept())
222     load(0x280a, 0x2810, src);
223 #endif
224
225   // write 32-bit fields
226   load(0x4800, 0x482a, src);
227
228   if (pinbased_ctls.test(6)) // activate vmx-preemption timer
229     load(0x482e, src);
230
231   // write natural-width fields
232   load<Mword>(0x6800, src, vmx.info.cr0_defs);
233
234   if (sizeof(long) > sizeof(int))
235     {
236       if (read<Mword>(src, 0x2806) & EFER_LME)
237         Vmx::vmwrite(0x6802, (Mword)phys_dir());
238       else
239         WARN("VMX: No, not possible\n");
240     }
241   else
242     {
243       // for 32bit we can just load the Vm pdbr
244       Vmx::vmwrite(0x6802, (Mword)phys_dir());
245     }
246
247   load<Mword>(0x6804, src, vmx.info.cr4_defs);
248   load(0x6806, 0x6826, src);
249
250   // VPID must be virtualized in Fiasco
251 #if 0
252   if (procbased_ctls_2 & Vmx::PB2_enable_vpid)
253     load(Vmx::F_vpid, src);
254 #endif
255
256   // currently io-bitmaps are unsupported
257   // currently msr-bitmaps are unsupported
258
259   // load(0x200C, src); for SMM virtualization
260   load(Vmx::F_tsc_offset, src);
261
262   // no virtual APIC yet, and has to be managed in kernel somehow
263 #if 0
264   if (procbased_ctls.test(Vmx::PRB1_tpr_shadow))
265     load(0x2012, src);
266 #endif
267
268   if (procbased_ctls_2.test(Vmx::PRB2_virtualize_apic))
269     load(Vmx::F_apic_access_addr, src);
270
271   // exception bit map and pf error-code stuff
272   load(0x4004, 0x4008, src);
273
274   // vm entry control stuff
275   Unsigned32 irq_info = read<Unsigned32>(src, Vmx::F_entry_int_info);
276   if (irq_info & (1UL << 31))
277     {
278       // do event injection
279
280       // load error code, if required
281       if (irq_info & (1UL << 11))
282         load(Vmx::F_entry_exc_error_code, src);
283
284       // types, that require an insn length have bit 10 set (type 4, 5, and 6)
285       if (irq_info & (1UL << 10))
286         load(Vmx::F_entry_insn_len, src);
287
288       Vmx::vmwrite(Vmx::F_entry_int_info, irq_info);
289     }
290
291   // hm, we have to check for sanitizing the cr0 and cr4 shadow stuff
292   load(0x6000, 0x6006, src);
293
294   // no cr3 target values supported
295 }
296
297
298 PRIVATE
299 void
300 Vm_vmx::store_guest_state(unsigned cpu, void *dest)
301 {
302   // read 16-bit fields
303   store(0x800, 0x80e, dest);
304
305   // read 64-bit fields
306   store(0x2802, dest);
307
308   Vmx_info &vmx_info = Vmx::cpus.cpu(cpu).info;
309   Vmx_info::Flags<Unsigned32> exit_ctls
310     = Vmx_info::Flags<Unsigned32>(vmx_info.exit_ctls.apply(read<Unsigned32>(dest, Vmx::F_exit_ctls)));
311
312   if (exit_ctls.test(18)) store(Vmx::F_guest_pat, dest);
313   if (exit_ctls.test(20)) store(Vmx::F_guest_efer, dest);
314   if (exit_ctls.test(22)) store(Vmx::F_preempt_timer, dest);
315
316   // EPT and PAE handling missing
317 #if 0
318   if (Vmx::cpus.cpu(cpu).has_ept())
319     store(0x280a, 0x2810, dest);
320 #endif
321
322   // read 32-bit fields
323   store(0x4800, 0x4826, dest);
324
325   // sysenter msr is not saved here, because we trap all msr accesses right now
326   if (0)
327     {
328       store(0x482a, dest);
329       store(0x6824, 0x6826, dest);
330     }
331
332   // read natural-width fields
333   store(0x6800, dest);
334   // skip cr3
335   store(0x6804, 0x6822, dest);
336 }
337
338 PRIVATE
339 void
340 Vm_vmx::store_exit_info(unsigned cpu, void *dest)
341 {
342   (void)cpu;
343   // read 64-bit fields, that is a EPT pf thing
344 #if 0
345   if (Vmx::cpus.cpu(cpu).has_ept())
346     store(0x2400, dest);
347 #endif
348
349   // clear the valid bit in Vm-entry interruption information
350     {
351       Unsigned32 tmp = read<Unsigned32>(dest, Vmx::F_entry_int_info);
352       if (tmp & (1UL << 31))
353         write(dest, Vmx::F_entry_int_info, tmp & ~((Unsigned32)1 << 31));
354     }
355
356   // read 32-bit fields
357   store(0x4400, 0x440e, dest);
358
359   // read natural-width fields
360   store(0x6400, 0x640a, dest);
361 }
362
363 PRIVATE
364 void
365 Vm_vmx::dump(void *v, unsigned f, unsigned t)
366 {
367   for (; f <= t; f += 2)
368     printf("%04x: VMCS: %16lx   V: %16lx\n",
369            f, Vmx::vmread<Mword>(f), read<Mword>(v, f));
370 }
371
372 PRIVATE
373 void
374 Vm_vmx::dump_state(void *v)
375 {
376   dump(v, 0x0800, 0x080e);
377   dump(v, 0x0c00, 0x0c0c);
378   dump(v, 0x2000, 0x201a);
379   dump(v, 0x2800, 0x2810);
380   dump(v, 0x2c00, 0x2804);
381   dump(v, 0x4000, 0x4022);
382   dump(v, 0x4400, 0x4420);
383   dump(v, 0x4800, 0x482a);
384   dump(v, 0x6800, 0x6826);
385   dump(v, 0x6c00, 0x6c16);
386 }
387
388 PRIVATE inline NOEXPORT
389 int
390 Vm_vmx::do_resume_vcpu(Context *ctxt, Vcpu_state *vcpu, void *vmcs_s)
391 {
392   assert (cpu_lock.test());
393
394   /* these 4 must not use ldt entries */
395   assert (!(Cpu::get_cs() & (1 << 2)));
396   assert (!(Cpu::get_ss() & (1 << 2)));
397   assert (!(Cpu::get_ds() & (1 << 2)));
398   assert (!(Cpu::get_es() & (1 << 2)));
399
400   unsigned cpu = current_cpu();
401   Vmx &v = Vmx::cpus.cpu(cpu);
402
403   if (!v.vmx_enabled())
404     {
405       WARNX(Info, "VMX: not supported/enabled\n");
406       return -L4_err::ENodev;
407     }
408
409   // XXX:
410   // This generates a circular dep between thread<->task, this cries for a
411   // new abstraction...
412   if (!(ctxt->state() & Thread_fpu_owner))
413     {
414       if (EXPECT_FALSE(!static_cast<Thread*>(ctxt)->switchin_fpu()))
415         {
416           WARN("VMX: switchin_fpu failed\n");
417           return -L4_err::EInval;
418         }
419     }
420
421 #if 0
422   if (EXPECT_FALSE(read<Unsigned32>(vmcs_s, 0x201a) != 0)) // EPT POINTER
423     {
424       WARN("VMX: no nested paging available\n");
425       return commit_result(-L4_err::EInval);
426     }
427 #endif
428
429   // increment our refcount, and drop it at the end automatically
430   Ref_ptr<Vm_vmx> pin_myself(this);
431
432   // set volatile host state
433   Vmx::vmwrite<Mword>(Vmx::F_host_cr3, Cpu::get_pdbr()); // host_area.cr3
434
435   load_guest_state(cpu, vmcs_s);
436
437   Unsigned16 ldt = Cpu::get_ldt();
438
439   // set guest CR2
440   asm volatile("mov %0, %%cr2" : : "r" (read<Mword>(vmcs_s, Vmx::F_guest_cr2)));
441
442   unsigned long ret = resume_vm_vmx(vcpu);
443   // vmread error?
444   if (EXPECT_FALSE(ret & 0x40))
445     return -L4_err::EInval;
446
447   // save guest cr2
448     {
449       Mword cpu_cr2;
450       asm volatile("mov %%cr2, %0" : "=r" (cpu_cr2));
451       write(vmcs_s, Vmx::F_guest_cr2, cpu_cr2);
452     }
453
454   Cpu::set_ldt(ldt);
455
456   // reload TSS, we use I/O bitmaps
457   // ... do this lazy ...
458   {
459     // clear busy flag
460     Gdt_entry *e = &(*Cpu::cpus.cpu(cpu).get_gdt())[Gdt::gdt_tss / 8];
461     e->access &= ~(1 << 1);
462     asm volatile("" : : "m" (*e));
463     Cpu::set_tr(Gdt::gdt_tss);
464   }
465
466   store_guest_state(cpu, vmcs_s);
467   store_exit_info(cpu, vmcs_s);
468
469   if ((read<Unsigned32>(vmcs_s, Vmx::F_exit_reason) & 0xffff) == 1)
470     return 1;
471
472   vcpu->state &= ~(Vcpu_state::F_traps | Vcpu_state::F_user_mode);
473   return 0;
474 }
475
476 PUBLIC
477 int
478 Vm_vmx::resume_vcpu(Context *ctxt, Vcpu_state *vcpu, bool user_mode)
479 {
480   (void)user_mode;
481   assert_kdb (user_mode);
482
483   if (EXPECT_FALSE(!(ctxt->state(true) & Thread_ext_vcpu_enabled)))
484     return -L4_err::EInval;
485
486   void *vmcs_s = reinterpret_cast<char *>(vcpu) + 0x400;
487
488   for (;;)
489     {
490       // in the case of disabled IRQs and a pending IRQ directly simulate an
491       // external interrupt intercept
492       if (   !(vcpu->_saved_state & Vcpu_state::F_irqs)
493           && (vcpu->sticky_flags & Vcpu_state::Sf_irq_pending))
494         {
495           // XXX: check if this is correct, we set external irq exit as reason
496           write<Unsigned32>(vmcs_s, Vmx::F_exit_reason, 1);
497           return 1; // return 1 to indicate pending IRQs (IPCs)
498         }
499
500       int r = do_resume_vcpu(ctxt, vcpu, vmcs_s);
501
502       // test for error or non-IRQ exit reason
503       if (r <= 0)
504         return r;
505
506       // check for IRQ exits and allow to handle the IRQ
507       if (r == 1)
508         Proc::preemption_point();
509
510       // Check if the current context got a message delivered.
511       // This is done by testing for a valid continuation.
512       // When a continuation is set we have to directly
513       // leave the kernel to not overwrite the vcpu-regs
514       // with bogus state.
515       Thread *t = nonull_static_cast<Thread*>(ctxt);
516       if (t->continuation_test_and_restore())
517         t->fast_return_to_user(vcpu->_entry_ip, vcpu->_entry_sp,
518                                t->vcpu_state().usr().get());
519     }
520 }