]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/kern/ia32/vm_svm.cpp
a42a422f9b01ff043d1cba6d2821120281bf6490
[l4.git] / kernel / fiasco / src / kern / ia32 / vm_svm.cpp
1 INTERFACE [svm]:
2
3 #include "config.h"
4 #include "vm.h"
5
6 class Vmcb;
7
8 class Vm_svm : public Vm
9 {
10 private:
11   static void resume_vm_svm(Mword phys_vmcb, Vcpu_state *regs)
12     asm("resume_vm_svm") __attribute__((__regparm__(3)));
13   Unsigned8 _asid[Config::Max_num_cpus];
14   Unsigned32 _asid_generation[Config::Max_num_cpus];
15
16   enum
17   {
18     EFER_LME = 1 << 8,
19     EFER_LMA = 1 << 10,
20   };
21 };
22
23 // ------------------------------------------------------------------------
24 INTERFACE [svm && debug]:
25
26 EXTENSION class Vm_svm
27 {
28 protected:
29   struct Log_vm_svm_exit
30   {
31     Mword exitcode, exitinfo1, exitinfo2, rip;
32   };
33
34   static unsigned log_fmt_svm(Tb_entry *, int max, char *buf) asm ("__fmt_vm_svm_exit");
35 };
36
37 // ------------------------------------------------------------------------
38 IMPLEMENTATION [svm]:
39
40 #include "context.h"
41 #include "mem_space.h"
42 #include "fpu.h"
43 #include "ref_ptr.h"
44 #include "svm.h"
45 #include "thread.h" // XXX: circular dep, move this out here!
46 #include "thread_state.h" // XXX: circular dep, move this out here!
47
48
49 // ------------------------------------------------------------------------
50 IMPLEMENTATION [svm && ia32]:
51
52 #include "virt.h"
53
54 PRIVATE inline NEEDS["virt.h"]
55 Address
56 Vm_svm::get_vm_cr3(Vmcb *)
57 {
58   // When running in 32bit mode we already return the page-table of our Vm
59   // object, whether we're running with shadow or nested paging
60   return mem_space()->phys_dir();
61 }
62
63 //----------------------------------------------------------------------------
64 IMPLEMENTATION [svm && amd64]:
65
66 #include "virt.h"
67
68 PRIVATE inline NEEDS["virt.h"]
69 Address
70 Vm_svm::get_vm_cr3(Vmcb *v)
71 {
72   // When we have nested paging, we just return the 4lvl host page-table of
73   // our Vm.
74   if (v->np_enabled())
75      return mem_space()->phys_dir();
76
77   // When running with shadow paging and the guest is running in long mode
78   // and has paging enabled, we can just return the 4lvl page table of our
79   // host Vm object.
80   if (   (v->state_save_area.efer & EFER_LME)
81       && (v->state_save_area.cr0 & CR0_PG))
82     return mem_space()->phys_dir();
83
84   // Now it's getting tricky when running with shadow paging.
85   // We need to obey the following rules:
86   //  - When the guest is not running in 64bit mode the CR3 one can set for
87   //    the page-table must be below 4G physical memory (i.e. bit 32-63 must
88   //    be zero). This is unfortunate when the host has memory above 4G as
89   //    Fiasco gets its memory from the end of physical memory, i.e.
90   //    page-table memory is above 4G.
91   //  - We need an appropriate page-table format for 32bit!
92   //    That means either a 2lvl page-table or a 3lvl PAE one. That would
93   //    require to maintain two page-tables for the guest, one for 32bit
94   //    mode execution and one for 64 bit execution. It is needed either for
95   //    the transition from real to long-mode via protected mode or for
96   //    32bit only guests.
97   //    There's one trick to avoid having two PTs: 4lvl-PTs and 3lvl-PAE-PTs
98   //    have much in common so that it's possible to just take the the PDPE
99   //    one of the host as the 3lvl-PAE-PT for the guest. Well, not quite.
100   //    The problem is that SVM checks that MBZ bits in the PAE-PT entries
101   //    are really 0 as written in the spec. Now the 4lvl PT contains rights
102   //    bits there, so that this type of PT is refused and does not work on
103   //    real hardware.
104   //    So why is the code still here? Well, QEmu isn't so picky about the
105   //    bits in the PDPE and it thus works there...
106   Address vm_cr3 = mem_space()->dir()->walk(Virt_addr(0), 0).e->addr();
107   if (EXPECT_FALSE(!vm_cr3))
108     {
109       // force allocation of new secondary page-table level
110       mem_space()->dir()->alloc_cast<Mem_space_q_alloc>()
111                  ->walk(Virt_addr(0), 1, Mem_space_q_alloc(ram_quota(),
112                         Mapped_allocator::allocator()));
113       vm_cr3 = mem_space()->dir()->walk(Virt_addr(0), 0).e->addr();
114     }
115
116   if (EXPECT_FALSE(vm_cr3 >= 1UL << 32))
117     {
118       WARN("svm: Host page-table not under 4G, sorry.\n");
119       return 0;
120     }
121
122   return vm_cr3;
123 }
124
125 //----------------------------------------------------------------------------
126 IMPLEMENTATION [svm]:
127
128 PRIVATE inline
129 Unsigned8
130 Vm_svm::asid ()
131 {
132   return _asid[current_cpu()];
133 }
134
135 PRIVATE inline
136 void
137 Vm_svm::asid(Unsigned8 asid)
138 {
139   _asid[current_cpu()] = asid;
140 }
141
142 PRIVATE inline
143 Unsigned32
144 Vm_svm::asid_generation()
145 {
146   return _asid_generation[current_cpu()];
147 }
148
149 PRIVATE inline
150 void
151 Vm_svm::asid_generation(Unsigned32 generation)
152 {
153   _asid_generation[current_cpu()] = generation;
154 }
155
156 PUBLIC
157 Vm_svm::Vm_svm(Ram_quota *q)
158   : Vm(q)
159 {
160   memset(_asid, 0, sizeof(_asid));
161   memset(_asid_generation, 0, sizeof(_asid_generation));
162 }
163
164 PUBLIC inline
165 void *
166 Vm_svm::operator new (size_t size, void *p)
167 {
168   (void)size;
169   assert (size == sizeof (Vm_svm));
170   return p;
171 }
172
173 PUBLIC
174 void
175 Vm_svm::operator delete (void *ptr)
176 {
177   Vm_svm *t = reinterpret_cast<Vm_svm*>(ptr);
178   allocator<Vm_svm>()->q_free(t->ram_quota(), ptr);
179 }
180
181
182 // to do:
183 //   - handle cr2
184 //   - force fpu ownership
185 //   - debug registers not covered by VMCB
186
187 PRIVATE
188 void
189 Vm_svm::copy_state_save_area(Vmcb *dest, Vmcb *src)
190 {
191   Vmcb_state_save_area *d = &dest->state_save_area;
192   Vmcb_state_save_area *s = &src->state_save_area;
193
194   d->es_sel       = s->es_sel;
195   d->es_attrib    = s->es_attrib;
196   d->es_limit     = s->es_limit;
197   d->es_base      = s->es_base;
198
199   d->cs_sel       = s->cs_sel;
200   d->cs_attrib    = s->cs_attrib;
201   d->cs_limit     = s->cs_limit;
202   d->cs_base      = s->cs_base;
203
204   d->ss_sel       = s->ss_sel;
205   d->ss_attrib    = s->ss_attrib;
206   d->ss_limit     = s->ss_limit;
207   d->ss_base      = s->ss_base;
208
209   d->ds_sel       = s->ds_sel;
210   d->ds_attrib    = s->ds_attrib;
211   d->ds_limit     = s->ds_limit;
212   d->ds_base      = s->ds_base;
213
214   d->fs_sel       = s->fs_sel;
215   d->fs_attrib    = s->fs_attrib;
216   d->fs_limit     = s->fs_limit;
217   d->fs_base      = s->fs_base;
218
219   d->gs_sel       = s->gs_sel;
220   d->gs_attrib    = s->gs_attrib;
221   d->gs_limit     = s->gs_limit;
222   d->gs_base      = s->gs_base;
223
224   d->gdtr_sel     = s->gdtr_sel;
225   d->gdtr_attrib  = s->gdtr_attrib;
226   d->gdtr_limit   = s->gdtr_limit;
227   d->gdtr_base    = s->gdtr_base;
228
229   d->ldtr_sel     = s->ldtr_sel;
230   d->ldtr_attrib  = s->ldtr_attrib;
231   d->ldtr_limit   = s->ldtr_limit;
232   d->ldtr_base    = s->ldtr_base;
233
234   d->idtr_sel     = s->idtr_sel;
235   d->idtr_attrib  = s->idtr_attrib;
236   d->idtr_limit   = s->idtr_limit;
237   d->idtr_base    = s->idtr_base;
238
239   d->tr_sel       = s->tr_sel;
240   d->tr_attrib    = s->tr_attrib;
241   d->tr_limit     = s->tr_limit;
242   d->tr_base      = s->tr_base;
243
244   d->cpl          = s->cpl;
245   d->efer         = s->efer;
246
247   d->cr4          = s->cr4;
248   d->cr3          = s->cr3;
249   d->cr0          = s->cr0;
250   d->dr7          = s->dr7;
251   d->dr6          = s->dr6;
252   d->rflags       = s->rflags;
253
254   d->rip          = s->rip;
255   d->rsp          = s->rsp;
256   d->rax          = s->rax;
257
258   d->star         = s->star;
259   d->lstar        = s->lstar;
260   d->cstar        = s->cstar;
261   d->sfmask       = s->sfmask;
262   d->kernelgsbase = s->kernelgsbase;
263   d->sysenter_cs  = s->sysenter_cs;
264   d->sysenter_esp = s->sysenter_esp;
265   d->sysenter_eip = s->sysenter_eip;
266   d->cr2          = s->cr2;
267
268   d->g_pat        = s->g_pat;
269   d->dbgctl       = s->dbgctl;
270   d->br_from      = s->br_from;
271   d->br_to        = s->br_to;
272   d->lastexcpfrom = s->lastexcpfrom;
273   d->last_excpto  = s->last_excpto;
274 }
275
276
277 PRIVATE
278 void
279 Vm_svm::copy_control_area(Vmcb *dest, Vmcb *src)
280 {
281   Vmcb_control_area *d = &dest->control_area;
282   Vmcb_control_area *s = &src->control_area;
283
284   d->intercept_rd_crX          = s->intercept_rd_crX;
285   d->intercept_wr_crX          = s->intercept_wr_crX;
286
287   d->intercept_rd_drX          = s->intercept_rd_drX;
288   d->intercept_wr_drX          = s->intercept_wr_drX;
289
290   d->intercept_exceptions      = s->intercept_exceptions;
291
292   d->intercept_instruction0    = s->intercept_instruction0;
293   d->intercept_instruction1    = s->intercept_instruction1;
294
295   // skip iopm_base_pa and msrpm_base_pa
296
297   d->tsc_offset                = s->tsc_offset;
298   d->guest_asid_tlb_ctl        = s->guest_asid_tlb_ctl;
299   d->interrupt_ctl             = s->interrupt_ctl;
300   d->interrupt_shadow          = s->interrupt_shadow;
301   d->exitcode                  = s->exitcode;
302   d->exitinfo1                 = s->exitinfo1;
303   d->exitinfo2                 = s->exitinfo2;
304   d->exitintinfo               = s->exitintinfo;
305   d->np_enable                 = s->np_enable;
306
307   d->eventinj                  = s->eventinj;
308   d->n_cr3                     = s->n_cr3;
309   d->lbr_virtualization_enable = s->lbr_virtualization_enable;
310 }
311
312
313 /* skip anything that does not change */
314 PRIVATE
315 void
316 Vm_svm::copy_control_area_back(Vmcb *dest, Vmcb *src)
317 {
318   Vmcb_control_area *d = &dest->control_area;
319   Vmcb_control_area *s = &src->control_area;
320
321   d->interrupt_ctl    = s->interrupt_ctl;
322   d->interrupt_shadow = s->interrupt_shadow;
323
324   d->exitcode         = s->exitcode;
325   d->exitinfo1        = s->exitinfo1;
326   d->exitinfo2        = s->exitinfo2;
327   d->exitintinfo      = s->exitintinfo;
328
329   d->eventinj = s->eventinj;
330 }
331
332 /** \brief Choose an ASID for this Vm.
333  *
334  * Choose an ASID for this Vm. The ASID provided by userspace is ignored
335  * instead the kernel picks one.
336  * Userspace uses the flush-bit to receive a new ASID for this Vm.
337  * All ASIDs are flushed as soon as the kernel runs out of ASIDs.
338  *
339  * @param vmcb_s external VMCB provided by userspace
340  * @param kernel_vmcb_s our VMCB
341  *
342  */
343 PRIVATE
344 void
345 Vm_svm::configure_asid(Vmcb *vmcb_s, Vmcb *kernel_vmcb_s)
346 {
347   assert (cpu_lock.test());
348
349   Svm &s = Svm::cpus.cpu(current_cpu());
350
351   if (// vmm requests flush
352       ((vmcb_s->control_area.guest_asid_tlb_ctl >> 32) & 1) == 1 ||
353       // our asid is not valid or expired
354       !(s.asid_valid(asid(), asid_generation())))
355     {
356       asid(s.next_asid());
357       asid_generation(s.global_asid_generation());
358     }
359
360   assert(s.asid_valid(asid(), asid_generation()));
361 #if 1
362   kernel_vmcb_s->control_area.guest_asid_tlb_ctl = asid();
363   if (s.flush_all_asids())
364     {
365       kernel_vmcb_s->control_area.guest_asid_tlb_ctl |= (1ULL << 32);
366       s.flush_all_asids(false);
367     }
368 #else
369   kernel_vmcb_s->control_area.guest_asid_tlb_ctl = 1;
370   kernel_vmcb_s->control_area.guest_asid_tlb_ctl |= (1ULL << 32);
371 #endif
372 }
373
374 PRIVATE inline NOEXPORT
375 int
376 Vm_svm::do_resume_vcpu(Context *ctxt, Vcpu_state *vcpu, Vmcb *vmcb_s)
377 {
378   //Mword host_cr0;
379   Unsigned64 orig_cr3, orig_ncr3;
380
381   assert (cpu_lock.test());
382
383   /* these 4 must not use ldt entries */
384   assert (!(Cpu::get_cs() & (1 << 2)));
385   assert (!(Cpu::get_ss() & (1 << 2)));
386   assert (!(Cpu::get_ds() & (1 << 2)));
387   assert (!(Cpu::get_es() & (1 << 2)));
388
389   Svm &s = Svm::cpus.cpu(current_cpu());
390
391   // FIXME: this can be an assertion I think, however, think about MP
392   if (EXPECT_FALSE(!s.svm_enabled()))
393     {
394       WARN("svm: not supported/enabled\n");
395       return -L4_err::EInval;
396     }
397
398   if (EXPECT_FALSE(vmcb_s->np_enabled() && !s.has_npt()))
399     {
400       WARN("svm: No NPT available\n");
401       return -L4_err::EInval;
402     }
403
404   Address vm_cr3 = get_vm_cr3(vmcb_s);
405   // can only fail on 64bit, will be optimized away on 32bit
406   if (EXPECT_FALSE(is_64bit() && !vm_cr3))
407     return -L4_err::ENomem;
408
409   // neither EFER.LME nor EFER.LMA must be set
410   if (EXPECT_FALSE(!is_64bit()
411                    && (vmcb_s->state_save_area.efer & (EFER_LME | EFER_LMA))))
412     {
413       WARN("svm: EFER invalid %llx\n", vmcb_s->state_save_area.efer);
414       return -L4_err::EInval;
415     }
416
417   // EFER.SVME must be set
418   if (!(vmcb_s->state_save_area.efer & 0x1000))
419     {
420       WARN("svm: EFER invalid %llx\n", vmcb_s->state_save_area.efer);
421       return -L4_err::EInval;
422     }
423   // allow PAE in combination with NPT
424 #if 0
425   // CR4.PAE must be clear
426   if(vmcb_s->state_save_area.cr4 & 0x20)
427     return -L4_err::EInval;
428 #endif
429
430   // XXX:
431   // This generates a circular dep between thread<->task, this cries for a
432   // new abstraction...
433   if (!(ctxt->state() & Thread_fpu_owner))
434     {
435       if (!static_cast<Thread*>(ctxt)->switchin_fpu())
436         {
437           WARN("svm: switchin_fpu failed\n");
438           return -L4_err::EInval;
439         }
440     }
441
442 #if 0  //should never happen
443   host_cr0 = Cpu::get_cr0();
444   // the VMM does not currently own the fpu but wants to
445   // make it available for the guest. This may happen
446   // if it was descheduled between activating the fpu and
447   // executing the vm_run operation
448   if (!(vmcb_s->state_save_area.cr0 & 0x8) && (host_cr0 & 0x8))
449     {
450       WARN("svm: FPU TS\n");
451       return commit_result(-L4_err::EInval);
452     }
453 #endif
454
455   // increment our refcount, and drop it at the end automatically
456   Ref_ptr<Vm_svm> pin_myself(this);
457
458   // sanitize VMCB
459
460   orig_cr3  = vmcb_s->state_save_area.cr3;
461   orig_ncr3 = vmcb_s->control_area.n_cr3;
462
463   Vmcb *kernel_vmcb_s = s.kernel_vmcb();
464
465   copy_control_area(kernel_vmcb_s, vmcb_s);
466   copy_state_save_area(kernel_vmcb_s, vmcb_s);
467
468   if (EXPECT_FALSE(is_64bit() && !kernel_vmcb_s->np_enabled()
469                    && (kernel_vmcb_s->state_save_area.cr0 & CR0_PG)
470                    && !(kernel_vmcb_s->state_save_area.cr4 & CR4_PAE)))
471     {
472       WARN("svm: No 32bit shadow page-tables on AMD64, use PAE!\n");
473       return -L4_err::EInval;
474     }
475
476   // set MCE according to host
477   kernel_vmcb_s->state_save_area.cr4 |= Cpu::get_cr4() & CR4_MCE;
478
479   // allow w access to cr0, cr2, cr3
480   // allow r access to cr0, cr2, cr3, cr4
481   // to do: check if enabling PAE in cr4 needs to be controlled
482
483   // allow r/w access to dr[0-7]
484   kernel_vmcb_s->control_area.intercept_rd_drX |= 0xff00;
485   kernel_vmcb_s->control_area.intercept_wr_drX |= 0xff00;
486
487 #if 0
488   // intercept exception vectors 0-31
489   kernel_vmcb_s->control_area.intercept_exceptions = 0xffffffff;
490 #endif
491
492   // enable iopm and msrpm
493   kernel_vmcb_s->control_area.intercept_instruction0 |= 0x18000000;
494   // intercept FERR_FREEZE and shutdown events
495   kernel_vmcb_s->control_area.intercept_instruction0 |= 0xc0000000;
496   // intercept INTR/NMI/SMI/INIT
497   kernel_vmcb_s->control_area.intercept_instruction0 |= 0xf;
498   // intercept INVD
499   kernel_vmcb_s->control_area.intercept_instruction0 |= (1 << 22);
500   // intercept HLT
501   kernel_vmcb_s->control_area.intercept_instruction0 |= (1 << 24);
502   // intercept task switch
503   kernel_vmcb_s->control_area.intercept_instruction0 |= (1 << 29);
504   // intercept shutdown
505   kernel_vmcb_s->control_area.intercept_instruction0 |= (1 << 31);
506   // intercept MONITOR/MWAIT
507   kernel_vmcb_s->control_area.intercept_instruction1 |= (1 << 10) | (1 << 11);
508
509   // intercept virtualization related instructions
510   //  vmrun interception is required by the hardware
511   kernel_vmcb_s->control_area.intercept_instruction1 |= 0xff;
512
513   Mword kernel_vmcb_pa = s.kernel_vmcb_pa();
514   Unsigned64 iopm_base_pa = s.iopm_base_pa();
515   Unsigned64 msrpm_base_pa = s.msrpm_base_pa();
516
517   kernel_vmcb_s->control_area.iopm_base_pa = iopm_base_pa;
518   kernel_vmcb_s->control_area.msrpm_base_pa = msrpm_base_pa;
519
520   configure_asid(vmcb_s, kernel_vmcb_s);
521
522   // 7:0 V_TPR, 8 V_IRQ, 15:9 reserved SBZ,
523   // 19:16 V_INTR_PRIO, 20 V_IGN_TPR, 23:21 reserved SBZ
524   // 24 V_INTR_MASKING 31:25 reserved SBZ
525   // 39:32 V_INTR_VECTOR, 63:40 reserved SBZ
526 #if 0
527   kernel_vmcb_s->control_area.interrupt_ctl = 0x10f0000;
528 #endif
529   // enable IRQ masking virtualization
530   kernel_vmcb_s->control_area.interrupt_ctl |= 0x01000000;
531
532 #if 0
533   // 0 INTERRUPT_SHADOW, 31:1 reserved SBZ
534   // 63:32 reserved SBZ
535   kernel_vmcb_s->control_area.interrupt_shadow = 0;
536 #endif
537
538   kernel_vmcb_s->control_area.exitcode = 0;
539   kernel_vmcb_s->control_area.exitinfo1 = 0;
540   kernel_vmcb_s->control_area.exitinfo2 = 0;
541   kernel_vmcb_s->control_area.exitintinfo = 0;
542
543 #if 0
544   // 0/1 NP_ENABLE, 31:1 reserved SBZ
545   kernel_vmcb_s->control_area.np_enable = 1;
546
547   // 31 VALID, EVENTINJ
548   kernel_vmcb_s->control_area.eventinj = 0;
549 #endif
550
551   // N_CR3
552   kernel_vmcb_s->control_area.n_cr3 = vm_cr3;
553
554   if (!kernel_vmcb_s->np_enabled())
555     {
556       // to do: check that the vmtask has the
557       // VM property set, i.e. does not contain mappings
558       // to the fiasco kernel regions or runs with PL 3
559
560       // printf("nested paging disabled, use n_cr3 as cr3\n");
561       kernel_vmcb_s->state_save_area.cr3 = vm_cr3;
562
563       // intercept accesses to cr0, cr3 and cr4
564       kernel_vmcb_s->control_area.intercept_rd_crX = 0xfff9;
565       kernel_vmcb_s->control_area.intercept_wr_crX = 0xfff9;
566     }
567
568 #if 0
569   kernel_vmcb_s->control_area.lbr_virtualization_enable = 0;
570 #endif
571
572
573   // to do:
574   // - initialize VM_HSAVE_PA (done)
575   // - supply trusted msrpm_base_pa and iopm_base_pa (done)
576   // - save host state not covered by VMRUN/VMEXIT (ldt, some segments etc) (done)
577   // - disable interupts (done)
578   // - trigger interecepted device and timer interrupts (done, not necessary)
579   // - check host CR0.TS (floating point registers) (done)
580
581   Unsigned64 sysenter_cs, sysenter_eip, sysenter_esp;
582   Unsigned32 fs, gs;
583   Unsigned16 tr, ldtr;
584   //Unsigned32 cr4;
585
586   sysenter_cs = Cpu::rdmsr(MSR_SYSENTER_CS);
587   sysenter_eip = Cpu::rdmsr(MSR_SYSENTER_EIP);
588   sysenter_esp = Cpu::rdmsr(MSR_SYSENTER_ESP);
589
590   fs = Cpu::get_fs();
591   gs = Cpu::get_gs();
592   tr = Cpu::get_tr();
593   ldtr = Cpu::get_ldt();
594
595   Gdt_entry tr_entry;
596
597   tr_entry = (*Cpu::cpus.cpu(current_cpu()).get_gdt())[tr / 8];
598
599 #if 0
600   // to do: check if the nested page table walker looks
601   // into the TLB. if so, global pages have to be disabled in
602   // the host
603   cr4 = Cpu::get_cr4();
604
605   if (cr4 & CR4_PGE)
606     // disable support for global pages as the vm task has
607     // a divergent upper memory region from the regular tasks
608     Cpu::set_cr4(cr4 & ~CR4_PGE);
609 #endif
610
611   resume_vm_svm(kernel_vmcb_pa, vcpu);
612
613
614 #if 0
615   if (cr4 & CR4_PGE)
616     Cpu::set_cr4(cr4);
617 #endif
618
619   Cpu::wrmsr(sysenter_cs, MSR_SYSENTER_CS);
620   Cpu::wrmsr(sysenter_eip, MSR_SYSENTER_EIP);
621   Cpu::wrmsr(sysenter_esp, MSR_SYSENTER_ESP);
622
623   Cpu::set_ldt(ldtr);
624   Cpu::set_fs(fs);
625   Cpu::set_gs(gs);
626
627   // clear busy flag
628   Gdt_entry tss_entry;
629
630   tss_entry = (*Cpu::cpus.cpu(current_cpu()).get_gdt())[tr / 8];
631   tss_entry.access &= 0xfd;
632   (*Cpu::cpus.cpu(current_cpu()).get_gdt())[tr / 8] = tss_entry;
633
634   Cpu::set_tr(tr); // TODO move under stgi in asm
635
636   copy_state_save_area(vmcb_s, kernel_vmcb_s);
637   copy_control_area_back(vmcb_s, kernel_vmcb_s);
638
639   if (!(vmcb_s->np_enabled()))
640     vmcb_s->state_save_area.cr3 = orig_cr3;
641
642   vmcb_s->control_area.n_cr3 = orig_ncr3;
643
644   LOG_TRACE("VM-SVM", "svm", current(), __fmt_vm_svm_exit,
645             Log_vm_svm_exit *l = tbe->payload<Log_vm_svm_exit>();
646             l->exitcode = vmcb_s->control_area.exitcode;
647             l->exitinfo1 = vmcb_s->control_area.exitinfo1;
648             l->exitinfo2 = vmcb_s->control_area.exitinfo2;
649             l->rip       = vmcb_s->state_save_area.rip;
650            );
651
652   // check for IRQ exit
653   if (kernel_vmcb_s->control_area.exitcode == 0x60)
654     return 1;
655
656   vcpu->state &= ~(Vcpu_state::F_traps | Vcpu_state::F_user_mode);
657   return 0;
658 }
659
660 PUBLIC
661 int
662 Vm_svm::resume_vcpu(Context *ctxt, Vcpu_state *vcpu, bool user_mode)
663 {
664   (void)user_mode;
665   assert_kdb (user_mode);
666
667   if (EXPECT_FALSE(!(ctxt->state(true) & Thread_ext_vcpu_enabled)))
668     return -L4_err::EInval;
669
670   Vmcb *vmcb_s = reinterpret_cast<Vmcb*>(reinterpret_cast<char *>(vcpu) + 0x400);
671   for (;;)
672     {
673       // in the case of disabled IRQs and a pending IRQ directly simulate an
674       // external interrupt intercept
675       if (   !(vcpu->_saved_state & Vcpu_state::F_irqs)
676           && (vcpu->sticky_flags & Vcpu_state::Sf_irq_pending))
677         {
678           vmcb_s->control_area.exitcode = 0x60;
679           return 1; // return 1 to indicate pending IRQs (IPCs)
680         }
681
682       int r = do_resume_vcpu(ctxt, vcpu, vmcb_s);
683
684       // test for error or non-IRQ exit resason
685       if (r <= 0)
686         return r;
687
688       // check for IRQ exits and allow to handle the IRQ
689       if (r == 1)
690         Proc::preemption_point();
691
692       // Check if the current context got a message delivered.
693       // This is done by testing for a valid continuation.
694       // When a continuation is set we have to directly
695       // leave the kernel to not overwrite the vcpu-regs
696       // with bogus state.
697       Thread *t = nonull_static_cast<Thread*>(ctxt);
698       if (t->exception_triggered())
699         t->fast_return_to_user(vcpu->_entry_ip, vcpu->_entry_sp, t->vcpu_state().usr().get());
700     }
701 }
702
703
704
705 // ------------------------------------------------------------------------
706 IMPLEMENTATION [svm && debug]:
707
708 IMPLEMENT
709 unsigned
710 Vm_svm::log_fmt_svm(Tb_entry *e, int max, char *buf)
711 {
712   Log_vm_svm_exit *l = e->payload<Log_vm_svm_exit>();
713   return snprintf(buf, max, "ec=%lx ei1=%08lx ei2=%08lx rip=%08lx",
714                   l->exitcode, l->exitinfo1, l->exitinfo2, l->rip);
715 }