]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/examples/sys/vmtest/vmxtest.c
update
[l4.git] / l4 / pkg / examples / sys / vmtest / vmxtest.c
1 /*
2  * Author(s): Adam Lackorzynski
3  *            Alexander Warg
4  *            Matthias Lange <mlange@sec.t-labs.tu-berlin.de>
5  *
6  * (c) 2008-2012 Technische Universität Dresden
7  * This file is part of TUD:OS and distributed under the terms of the
8  * GNU General Public License 2.
9  * Please see the COPYING-GPL-2 file for details.
10  */
11
12 #include <l4/sys/factory.h>
13 #include <l4/sys/task.h>
14 #include <l4/sys/kdebug.h>
15 #include <l4/sys/vm.h>
16 #include <l4/sys/err.h>
17 #include <l4/sys/vcpu.h>
18 #include <l4/sys/thread.h>
19
20 #include <l4/util/util.h>
21 #include <l4/util/cpu.h>
22 #include <l4/util/kip.h>
23 #include <l4/re/env.h>
24 #include <l4/re/c/util/cap_alloc.h>
25 #include <l4/vcpu/vcpu.h>
26
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30
31 #include "vmcs.h"
32
33 enum { STACKSIZE = 8 << 10 };
34
35 static char stack[STACKSIZE];
36 static char hdl_stack[STACKSIZE];
37
38 static unsigned long idt[32 * 2] __attribute__((aligned(4096)));
39 static unsigned long gdt[32 * 2] __attribute__((aligned(4096)));
40 static l4_umword_t old_rip;
41 static l4_vcpu_state_t *vcpu;
42 static l4_addr_t *vmcs_s;
43 static l4_umword_t test_end;
44 static l4_cap_idx_t vm_task;
45
46 L4_INLINE
47 void vmwrite(void *vmcs, unsigned field, unsigned long long val)
48 { l4_vm_vmx_write(vmcs, field, val); }
49
50 __attribute__((aligned(4096))) static void test_func(void)
51 {
52   unsigned long dummy;
53   asm volatile("2:  nop               \n"
54                "    nop \n"
55                "    addl %%edx,%%eax  \n"
56                "    ud2               \n"
57                "    addl %%edx,%%eax  \n"
58                "    int3              \n"
59                "3:  nop               \n"
60                "    nop               \n"
61                "    addl %%edx,%%eax  \n"
62                "    int3              \n"
63                "    nop               \n"
64                "    nop               \n"
65                "    movl %%eax, %%ecx \n"
66                "    addl %%edx, %%ecx \n"
67                "4:                    \n"
68                "    addl %%edx,%%eax  \n"
69                "    ud2               \n"
70                : "=r" (dummy)
71                );
72
73   asm volatile("1:  int3        \n"
74                "    ud2         \n"
75                "    jmp 1b      \n");
76 }
77
78 static void init_vmcs(void *vmcs)
79 {
80   vmwrite(vmcs, VMX_GUEST_CS_SEL, 0x8);
81   vmwrite(vmcs, VMX_GUEST_CS_ACCESS_RIGHTS, 0xd09b);
82   vmwrite(vmcs, VMX_GUEST_CS_LIMIT, 0xffffffff);
83   vmwrite(vmcs, VMX_GUEST_CS_BASE, 0);
84
85   vmwrite(vmcs, VMX_GUEST_SS_SEL, 0x10);
86   vmwrite(vmcs, VMX_GUEST_SS_ACCESS_RIGHTS, 0xc093);
87   vmwrite(vmcs, VMX_GUEST_SS_LIMIT, 0xffffffff);
88   vmwrite(vmcs, VMX_GUEST_SS_BASE, 0);
89
90   vmwrite(vmcs, VMX_GUEST_DS_SEL, 0x20);
91   vmwrite(vmcs, VMX_GUEST_DS_ACCESS_RIGHTS, 0xc0f3);
92   vmwrite(vmcs, VMX_GUEST_DS_LIMIT, 0xffffffff);
93   vmwrite(vmcs, VMX_GUEST_DS_BASE, 0);
94
95   vmwrite(vmcs, VMX_GUEST_ES_SEL, 0x0);
96   vmwrite(vmcs, VMX_GUEST_ES_ACCESS_RIGHTS, 0x14003);
97   vmwrite(vmcs, VMX_GUEST_ES_LIMIT, 0xffffffff);
98   vmwrite(vmcs, VMX_GUEST_ES_BASE, 0);
99
100   vmwrite(vmcs, VMX_GUEST_FS_SEL, 0x0);
101   vmwrite(vmcs, VMX_GUEST_FS_ACCESS_RIGHTS, 0x1c0f3);
102   vmwrite(vmcs, VMX_GUEST_FS_LIMIT, 0xffffffff);
103   vmwrite(vmcs, VMX_GUEST_FS_BASE, 0);
104
105   vmwrite(vmcs, VMX_GUEST_GS_SEL, 0x0);
106   vmwrite(vmcs, VMX_GUEST_GS_ACCESS_RIGHTS, 0x1c0f3);
107   vmwrite(vmcs, VMX_GUEST_GS_LIMIT, 0xffffffff);
108   vmwrite(vmcs, VMX_GUEST_GS_BASE, 0);
109
110   vmwrite(vmcs, VMX_GUEST_GDTR_LIMIT, 0x3f);
111   vmwrite(vmcs, VMX_GUEST_GDTR_BASE, (l4_umword_t)gdt);
112
113   vmwrite(vmcs, VMX_GUEST_LDTR_SEL, 0x0);
114   vmwrite(vmcs, VMX_GUEST_LDTR_ACCESS_RIGHTS, 0x10000);
115   vmwrite(vmcs, VMX_GUEST_LDTR_LIMIT, 0);
116   vmwrite(vmcs, VMX_GUEST_LDTR_BASE, 0);
117
118   vmwrite(vmcs, VMX_GUEST_IDTR_LIMIT, 0xff);
119   vmwrite(vmcs, VMX_GUEST_IDTR_BASE, (l4_umword_t)idt);
120
121   vmwrite(vmcs, VMX_GUEST_TR_SEL, 0x28);
122   vmwrite(vmcs, VMX_GUEST_TR_ACCESS_RIGHTS, 0x108b);
123   vmwrite(vmcs, VMX_GUEST_TR_LIMIT, 67);
124   vmwrite(vmcs, VMX_GUEST_TR_BASE, 0);
125
126   vmwrite(vmcs_s, VMX_GUEST_CR0, 0x0001003b);
127 }
128
129 static int check_vmx(void)
130 {
131   l4_umword_t ax, bx, cx, dx;
132
133   if (!l4util_cpu_has_cpuid())
134     return 1;
135
136   l4util_cpu_cpuid(0x1, &ax, &bx, &cx, &dx);
137
138   if (!(cx & (1 << 5)))
139     {
140       printf("CPU does not support VMX.\n");
141       return 1;
142     }
143
144   return 0;
145 }
146
147 static void handle_vmexit(void)
148 {
149   static unsigned int exitcnt;
150
151   ++exitcnt;
152
153   l4_msgtag_t tag;
154   l4_uint32_t interrupt_info;
155
156   printf("iteration=%d, rip=0x%lx -> 0x%lx\n",
157          exitcnt, old_rip,
158          l4_vm_vmx_read_nat(vmcs_s, VMX_GUEST_RIP));
159
160   l4_uint32_t exit_reason = l4_vm_vmx_read_32(vmcs_s, VMX_EXIT_REASON);
161   if ((exit_reason & (1 << 31)))
162     printf("VM entry failure, reason %d\n", (exit_reason & 0xffff));
163   else
164     {
165       switch (exit_reason)
166         {
167         case 0:
168           printf("Exception or NMI at guest ip 0x%lx, checking interrupt info\n",
169                  l4_vm_vmx_read_nat(vmcs_s, VMX_GUEST_RIP));
170           interrupt_info = l4_vm_vmx_read_32(vmcs_s, VMX_EXIT_INTERRUPT_INFO);
171           // check valid bit
172           if (!(interrupt_info & (1 << 31)))
173             printf("Interrupt info not valid\n");
174
175           printf("interrupt vector=%d, type=%d, error code valid=%d\n",
176                  (interrupt_info & 0xFF), ((interrupt_info & 0x700) >> 8),
177                  ((interrupt_info & 0x800) >> 11));
178
179           if ((interrupt_info & (1 << 11))) // interrupt error code valid?
180             printf("interrupt error=0x%x\n",
181                    l4_vm_vmx_read_32(vmcs_s, VMX_EXIT_INTERRUPT_ERROR));
182
183           printf("cr0=%lx\n", l4_vm_vmx_read_nat(vmcs_s, VMX_GUEST_CR0));
184           printf("eax: %lx ebx: %lx esi: %lx\n",
185                  vcpu->r.ax, vcpu->r.bx, vcpu->r.si);
186
187           if (((interrupt_info & 0x700) >> 8) == 3 &&
188               (interrupt_info & 0xff) == 14)
189             {
190               l4_umword_t fault_addr
191                 = l4_vm_vmx_read_nat(vmcs_s, VMX_EXIT_QUALIFICATION);
192               printf("detected pagefault @ %lx\n", fault_addr);
193               tag = l4_task_map(vm_task, L4RE_THIS_TASK_CAP,
194                                 l4_fpage(fault_addr & L4_PAGEMASK, L4_PAGESHIFT,
195                                          L4_FPAGE_RW),
196                                 l4_map_control(fault_addr, 0, L4_MAP_ITEM_MAP));
197               if (l4_error(tag))
198                 printf("Error mapping page\n");
199               break;
200             }
201
202           // increment rip to continue
203           l4_umword_t l = l4_vm_vmx_read_32(vmcs_s,
204                                             VMX_EXIT_INSTRUCTION_LENGTH);
205           l4_umword_t ip = l4_vm_vmx_read_nat(vmcs_s, VMX_GUEST_RIP);
206           printf("insn length: %lx new rip=%lx\n", l, ip);
207           vmwrite(vmcs_s, VMX_GUEST_RIP, ip + l);
208           break;
209         case 1:
210           printf("External interrupt\n");
211           break;
212         case 48: // EPT violation
213           printf("EPT violation\n");
214           l4_umword_t q = l4_vm_vmx_read_nat(vmcs_s, VMX_EXIT_QUALIFICATION);
215           printf("  exit qualifiction: %lx\n", q);
216           printf("  guest phys = %llx,  guest linear: %lx\n",
217                  l4_vm_vmx_read_64(vmcs_s, 0x2400), l4_vm_vmx_read_nat(vmcs_s, 0x640a));
218           printf("  guest cr0 = %lx\n",
219                  l4_vm_vmx_read_nat(vmcs_s, VMX_GUEST_CR0));
220
221             {
222               l4_umword_t fault_addr = l4_vm_vmx_read_64(vmcs_s, 0x2400);
223               printf("detected pagefault @ %lx\n", fault_addr);
224               tag = l4_task_map(vm_task, L4RE_THIS_TASK_CAP,
225                                 l4_fpage(fault_addr & L4_PAGEMASK,
226                                          L4_PAGESHIFT, L4_FPAGE_RWX),
227                                 l4_map_control(fault_addr, 0, L4_MAP_ITEM_MAP));
228               if (l4_error(tag))
229                 printf("Error mapping page\n");
230             }
231           break;
232         default:
233           printf("Exit reason %d\n", exit_reason);
234           break;
235         }
236     }
237 }
238
239 static void vm_resume(void)
240 {
241   int r;
242   l4_msgtag_t tag;
243
244   tag = l4_thread_vcpu_resume_commit(L4_INVALID_CAP,
245                                      l4_thread_vcpu_resume_start());
246   r = l4_error(tag);
247   if (r)
248     printf("vm_resume failed: %s (%d)\n", l4sys_errtostr(r), r);
249
250   handle_vmexit();
251   old_rip = l4_vm_vmx_read_nat(vmcs_s, VMX_GUEST_RIP);
252
253   if (old_rip <= test_end)
254     vm_resume();
255 }
256
257 static void handler(void)
258 {
259   printf("Received interrupt %d\n", (int)vcpu->i.label);
260   vm_resume();
261 }
262
263
264 static l4_vcpu_state_t *get_state_mem(l4_addr_t *extstate)
265 {
266   static int done;
267   long r;
268   l4_msgtag_t tag;
269   static l4_addr_t ext_state;
270
271   if (done)
272     {
273       *extstate = ext_state;
274       return vcpu;
275     }
276
277   r = l4vcpu_ext_alloc(&vcpu, &ext_state, L4_BASE_TASK_CAP, l4re_env()->rm);
278   if (r)
279     {
280       printf("Getting state mem failed: %ld\n", r);
281       exit(1);
282     }
283
284   vcpu->state = L4_VCPU_F_FPU_ENABLED;
285   vcpu->saved_state = L4_VCPU_F_USER_MODE | L4_VCPU_F_FPU_ENABLED | L4_VCPU_F_IRQ;
286
287   vcpu->entry_ip = (l4_umword_t)handler;
288   l4_umword_t *stack = (l4_umword_t *)(hdl_stack + STACKSIZE);
289   --stack;
290   *stack = 0;
291   vcpu->entry_sp = (l4_umword_t)stack;
292
293   tag = l4_thread_vcpu_control_ext(L4_INVALID_CAP, (l4_addr_t)vcpu);
294   r = l4_error(tag);
295   if (r)
296     {
297       printf("Could not enable ext vCPU: %ld\n", r);
298       exit(1);
299     }
300
301   done = 1;
302
303   *extstate = ext_state;
304
305   for (unsigned i = 0x480; i < 0x48d; ++i)
306     printf("VMX: CAP MSR[%3x]: %llx\n", i, l4_vm_vmx_get_caps(vcpu, i));
307
308   for (unsigned i = 0x481; i < 0x485; ++i)
309     printf("VMX: CAP MSR[%3x]: default1: %x\n", i, l4_vm_vmx_get_caps_default1(vcpu, i));
310
311   return vcpu;
312 }
313
314 static void run_test(int ept_available)
315 {
316   l4_umword_t eflags;
317   l4_msgtag_t tag;
318   vm_task = l4re_util_cap_alloc();
319   l4_umword_t ip, marker;
320   l4_addr_t vmcs;
321
322   get_state_mem(&vmcs);
323
324   printf("run test, ept_available=%d\n", ept_available);
325
326   if (l4_is_invalid_cap(vm_task))
327     {
328       printf("No more caps.\n");
329       return;
330     }
331
332   tag = l4_factory_create_vm(l4re_env()->factory, vm_task);
333   if (l4_error(tag))
334   {
335     printf("Failed to create new task\n");
336     exit(1);
337   }
338
339   vmcs_s = (l4_addr_t *)vmcs;
340
341   vcpu->user_task = vm_task;
342
343   // init registers
344   vcpu->r.dx = 1;
345   vcpu->r.cx = 2;
346   vcpu->r.bx = 3;
347   vcpu->r.bp = 4;
348   vcpu->r.si = 5;
349   vcpu->r.di = 6;
350
351 #if 1
352   asm volatile("    jmp 1f;           \n"
353                "2:  nop               \n"
354                "    nop               \n"
355                "    nop               \n"
356                                  "    addl %%edx,%%eax  \n"
357 //               "    ud2               \n"
358                                  "    addl %%edx,%%eax  \n"
359                "    int3              \n"
360                "3:  nop               \n"
361                "    nop               \n"
362                                  "    addl %%edx,%%eax  \n"
363                "    int3              \n"
364                "    nop               \n"
365                "    nop               \n"
366                "    movl %%eax, %%ecx \n"
367                "    addl %%edx, %%ecx \n"
368                "4:                    \n"
369                "    movl $1, %%eax    \n"
370                                  "    addl %%edx,%%eax  \n"
371   //             "    ud2               \n"
372                "1:  mov $2b, %0      \n"
373                "    mov $3b, %1      \n"
374                "    mov $4b, %2      \n"
375                : "=r" (ip), "=r" (marker), "=r" (test_end));
376 #else
377    asm volatile("    jmp 2f;           \n"
378                 "1:                    \n"
379                 "    nop               \n"
380                 "    nop               \n"
381                 "    movl $1, %1       \n"
382                 "    jmp 3f;           \n"
383                 "2:                    \n"
384                 "    movl $1b, %0      \n"
385                 "    movl $0, %1       \n"
386                 "3:                    \n"
387               :"=r"(ip), "=r"(marker));
388
389
390   if (marker) {
391      test_func();
392   }
393 #endif
394   if (0)
395     test_func();
396
397   printf("ip=%lx\n", ip);
398
399   init_vmcs((void *)vmcs);
400
401   asm volatile("pushf     \n"
402                "pop %0   \n"
403                : "=r" (eflags));
404
405   // clear interrupt
406   eflags = (eflags & 0xfffffdff);
407   eflags &= ~(0x1 << 17);
408
409   vmwrite(vmcs_s, VMX_GUEST_RSP, (l4_umword_t)stack + STACKSIZE);
410   vmwrite(vmcs_s, VMX_GUEST_RFLAGS, eflags);
411   vmwrite(vmcs_s, VMX_GUEST_RIP, ip);
412   vmwrite(vmcs_s, VMX_GUEST_CR0, 0x0001003b);
413   vmwrite(vmcs_s, VMX_GUEST_CR4, 0x2690);
414   vmwrite(vmcs_s, VMX_GUEST_DR7, 0x300);
415   vmwrite(vmcs_s, VMX_VMCS_LINK_PTR, 0xffffffffffffffffULL);
416
417   vmwrite(vmcs_s, VMX_EXCEPTION_BITMAP, 0xffffffff);
418   vmwrite(vmcs_s, VMX_PF_ERROR_CODE_MATCH, 0);
419
420   unsigned ofs;
421   for (ofs = 0; ofs < STACKSIZE; ofs += L4_PAGESIZE)
422     {
423       stack[ofs] = 0;
424       unsigned char c = stack[ofs];
425       unsigned dummy;
426
427       asm volatile("nop" : "=a"(dummy) : "0" (c));
428
429       tag = l4_task_map(vm_task, L4RE_THIS_TASK_CAP,
430                         l4_fpage((((l4_umword_t)(stack)) + ofs) & L4_PAGEMASK,
431                                  L4_PAGESHIFT, L4_FPAGE_RWX),
432                         l4_map_control(((l4_umword_t)stack) +  ofs, 0,
433                                        L4_MAP_ITEM_MAP));
434     }
435
436   tag = l4_task_map(vm_task, L4RE_THIS_TASK_CAP,
437                     l4_fpage(ip & L4_PAGEMASK, L4_PAGESHIFT, L4_FPAGE_RWX),
438                     l4_map_control(ip, 0, L4_MAP_ITEM_MAP));
439
440   idt[26] = 0x80000; // #13 general protection fault
441   idt[27] = 0x8e00;
442
443   idt[28] = 0x80000; // #14 page fault
444   idt[29] = 0x8e00;
445
446   // code segment 0x08
447   gdt[2] = 0xffff;
448   gdt[3] = 0xcf9b00;
449
450   // stack segment 0x10
451   gdt[4] = 0xffff;
452   gdt[5] = 0xcf9300;
453
454   // data segment 0x20
455   gdt[8] = 0xffff;
456   gdt[9] = 0xcff300;
457
458   // tss 0x28
459   gdt[10] = 0x67;
460   gdt[11] = 0x8b00;
461
462   unsigned idt0 = (unsigned long)idt;
463   tag = l4_task_map(vm_task, L4RE_THIS_TASK_CAP,
464                     l4_fpage(idt0 & L4_PAGEMASK, L4_PAGESHIFT, L4_FPAGE_RW),
465                     l4_map_control(idt0, 0, L4_MAP_ITEM_MAP));
466
467   unsigned gdt0 = (unsigned long)gdt;
468   tag = l4_task_map(vm_task, L4RE_THIS_TASK_CAP,
469                     l4_fpage(gdt0 & L4_PAGEMASK, L4_PAGESHIFT, L4_FPAGE_RW),
470                     l4_map_control(gdt0, 0, L4_MAP_ITEM_MAP));
471
472   vm_resume();
473
474   printf("eax=%lx, edx=%lx, ecx=%lx\n", vcpu->r.ax, vcpu->r.dx, vcpu->r.cx);
475   printf("run vm stop, status=%s\n", ((vcpu->r.ax == 2) && (vcpu->r.cx == 4)) ? "success" : "failure");
476 }
477
478 __attribute__((aligned(4096))) int main(void)
479 {
480   printf("VMX testing\n");
481
482   if (check_vmx())
483     {
484       printf("No VT CPU. Bye.\n");
485       return 1;
486     }
487
488   l4_touch_rw(stack, sizeof(stack));
489   l4_touch_rw(hdl_stack, sizeof(hdl_stack));
490
491   run_test(1);
492
493   printf("VM test exited\n");
494
495   l4_sleep_forever();
496
497   return 0;
498 }