2 * Author(s): Adam Lackorzynski
4 * Matthias Lange <mlange@sec.t-labs.tu-berlin.de>
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.
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>
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>
33 #define STACKSIZE (8<<10)
35 static char stack[STACKSIZE];
36 static char hdl_stack[STACKSIZE];
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;
42 void test_func(void) __attribute__((aligned(4096)));
45 void handle_vmexit(void);
46 l4_vcpu_state_t *vcpu;
49 static l4_umword_t test_end;
50 static l4_cap_idx_t vm_task;
53 void vmwrite(void *vmcs, unsigned field, unsigned long long val)
55 void *ptr = l4_vm_vmx_field_ptr(vmcs, field);
57 switch(l4_vm_vmx_field_len(field))
60 *((l4_uint16_t *)(ptr)) = val;
63 *((l4_uint32_t *)(ptr)) = val;
66 *((l4_uint64_t *)(ptr)) = val;
71 static void init_vmcs(void *vmcs)
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);
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);
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);
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);
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);
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);
103 vmwrite(vmcs, VMX_GUEST_GDTR_LIMIT, 0x3f);
104 vmwrite(vmcs, VMX_GUEST_GDTR_BASE, (l4_umword_t)gdt);
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);
111 vmwrite(vmcs, VMX_GUEST_IDTR_LIMIT, 0xff);
112 vmwrite(vmcs, VMX_GUEST_IDTR_BASE, (l4_umword_t)idt);
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);
120 static int check_vmx(void)
122 l4_umword_t ax, bx, cx, dx;
124 if (!l4util_cpu_has_cpuid())
127 l4util_cpu_cpuid(0x1, &ax, &bx, &cx, &dx);
131 printf("CPU does not support VMX.\n");
138 static void handler(void)
140 printf("Received interrupt %d\n", (int)vcpu->i.label);
144 void handle_vmexit(void)
146 static unsigned int i = 0;
150 l4_uint32_t interrupt_info;
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)));
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));
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));
167 if (!(interrupt_info & (1<<31)))
168 printf("Interrupt info not valid\n");
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));
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)));
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);
181 if (((interrupt_info & 0x700)>>8) == 3 &&
182 (interrupt_info & 0xff) == 14)
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));
190 printf("Error mapping page\n");
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);
200 printf("External interrupt\n");
203 printf("Exit reason %d\n", exit_reason);
214 tag = l4_thread_vcpu_resume_commit(L4_INVALID_CAP, l4_thread_vcpu_resume_start());
217 printf("vm_resume failed: %s (%d)\n", l4sys_errtostr(r), r);
220 old_rip = *((l4_umword_t *)l4_vm_vmx_field_ptr(vmcs_s, VMX_GUEST_RIP));
222 if (old_rip <= test_end)
226 static l4_vcpu_state_t *get_state_mem(l4_addr_t *extstate)
231 static l4_addr_t ext_state;
235 *extstate = ext_state;
239 r = l4vcpu_ext_alloc(&vcpu, &ext_state, L4_BASE_TASK_CAP, l4re_env()->rm);
242 printf("Getting state mem failed: %ld\n", r);
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;
249 vcpu->entry_ip = (l4_umword_t)handler;
250 l4_umword_t *stack = (l4_umword_t *)(hdl_stack + STACKSIZE);
253 vcpu->entry_sp = (l4_umword_t)stack;
255 tag = l4_thread_vcpu_control_ext(L4_INVALID_CAP, (l4_addr_t)vcpu);
259 printf("Could not enable ext vCPU: %ld\n", r);
265 *extstate = ext_state;
270 static void run_test(int ept_available)
274 vm_task = l4re_util_cap_alloc();
275 l4_umword_t ip, marker;
278 get_state_mem(&vmcs);
280 printf("run test, ept_available=%d\n", ept_available);
282 if (l4_is_invalid_cap(vm_task))
284 printf("No more caps.\n");
288 tag = l4_factory_create_vm(l4re_env()->factory, vm_task);
291 printf("Failed to create new task\n");
295 vmcs_s = (l4_addr_t *)vmcs;
297 vcpu->user_task = vm_task;
308 asm volatile(" jmp 1f; \n"
312 " addl %%edx,%%eax \n"
314 " addl %%edx,%%eax \n"
318 " addl %%edx,%%eax \n"
322 " movl %%eax, %%ecx \n"
323 " addl %%edx, %%ecx \n"
326 " addl %%edx,%%eax \n"
331 : "=r" (ip), "=r" (marker), "=r" (test_end));
333 asm volatile(" jmp 2f; \n"
343 :"=r"(ip), "=r"(marker));
351 printf("ip=%lx\n", ip);
353 init_vmcs((void *)vmcs);
355 asm volatile("pushf \n"
360 eflags = (eflags & 0xfffffdff);
361 eflags &= ~(0x1 << 17);
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);
371 vmwrite(vmcs_s, VMX_EXCEPTION_BITMAP, 0xffffffff);
372 vmwrite(vmcs_s, VMX_PF_ERROR_CODE_MATCH, 0);
375 for (ofs = 0; ofs < STACKSIZE; ofs += L4_PAGESIZE)
378 unsigned char c = stack[ofs];
381 asm volatile("nop" : "=a"(dummy) : "0" (c));
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,
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));
394 idt[26] = 0x80000; // #13 general protection fault
397 idt[28] = 0x80000; // #14 page fault
404 // stack segment 0x10
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));
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));
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");
432 __attribute__((aligned(4096))) int main(void)
434 printf("VMX testing\n");
438 printf("No VT CPU. Bye.\n");
442 l4_touch_rw(stack, sizeof(stack));
443 l4_touch_rw(hdl_stack, sizeof(hdl_stack));
447 printf("VM test exited\n");
454 __attribute__((aligned(4096))) void test_func(void)
457 asm volatile("2: nop \n"
459 " addl %%edx,%%eax \n"
461 " addl %%edx,%%eax \n"
465 " addl %%edx,%%eax \n"
469 " movl %%eax, %%ecx \n"
470 " addl %%edx, %%ecx \n"
472 " addl %%edx,%%eax \n"
477 asm volatile("1: int3 \n"