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