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 enum { 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;
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;
47 void vmwrite(void *vmcs, unsigned field, unsigned long long val)
48 { l4_vm_vmx_write(vmcs, field, val); }
50 __attribute__((aligned(4096))) static void test_func(void)
53 asm volatile("2: nop \n"
55 " addl %%edx,%%eax \n"
57 " addl %%edx,%%eax \n"
61 " addl %%edx,%%eax \n"
65 " movl %%eax, %%ecx \n"
66 " addl %%edx, %%ecx \n"
68 " addl %%edx,%%eax \n"
73 asm volatile("1: int3 \n"
78 static void init_vmcs(void *vmcs)
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);
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);
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);
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);
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);
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);
110 vmwrite(vmcs, VMX_GUEST_GDTR_LIMIT, 0x3f);
111 vmwrite(vmcs, VMX_GUEST_GDTR_BASE, (l4_umword_t)gdt);
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);
118 vmwrite(vmcs, VMX_GUEST_IDTR_LIMIT, 0xff);
119 vmwrite(vmcs, VMX_GUEST_IDTR_BASE, (l4_umword_t)idt);
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);
126 vmwrite(vmcs_s, VMX_GUEST_CR0, 0x0001003b);
129 static int check_vmx(void)
131 l4_umword_t ax, bx, cx, dx;
133 if (!l4util_cpu_has_cpuid())
136 l4util_cpu_cpuid(0x1, &ax, &bx, &cx, &dx);
138 if (!(cx & (1 << 5)))
140 printf("CPU does not support VMX.\n");
147 static void handle_vmexit(void)
149 static unsigned int exitcnt;
154 l4_uint32_t interrupt_info;
156 printf("iteration=%d, rip=0x%lx -> 0x%lx\n",
158 l4_vm_vmx_read_nat(vmcs_s, VMX_GUEST_RIP));
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));
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);
172 if (!(interrupt_info & (1 << 31)))
173 printf("Interrupt info not valid\n");
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));
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));
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);
187 if (((interrupt_info & 0x700) >> 8) == 3 &&
188 (interrupt_info & 0xff) == 14)
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,
196 l4_map_control(fault_addr, 0, L4_MAP_ITEM_MAP));
198 printf("Error mapping page\n");
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);
210 printf("External interrupt\n");
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));
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));
229 printf("Error mapping page\n");
233 printf("Exit reason %d\n", exit_reason);
239 static void vm_resume(void)
244 tag = l4_thread_vcpu_resume_commit(L4_INVALID_CAP,
245 l4_thread_vcpu_resume_start());
248 printf("vm_resume failed: %s (%d)\n", l4sys_errtostr(r), r);
251 old_rip = l4_vm_vmx_read_nat(vmcs_s, VMX_GUEST_RIP);
253 if (old_rip <= test_end)
257 static void handler(void)
259 printf("Received interrupt %d\n", (int)vcpu->i.label);
264 static l4_vcpu_state_t *get_state_mem(l4_addr_t *extstate)
269 static l4_addr_t ext_state;
273 *extstate = ext_state;
277 r = l4vcpu_ext_alloc(&vcpu, &ext_state, L4_BASE_TASK_CAP, l4re_env()->rm);
280 printf("Getting state mem failed: %ld\n", r);
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;
287 vcpu->entry_ip = (l4_umword_t)handler;
288 l4_umword_t *stack = (l4_umword_t *)(hdl_stack + STACKSIZE);
291 vcpu->entry_sp = (l4_umword_t)stack;
293 tag = l4_thread_vcpu_control_ext(L4_INVALID_CAP, (l4_addr_t)vcpu);
297 printf("Could not enable ext vCPU: %ld\n", r);
303 *extstate = ext_state;
305 for (unsigned i = 0x480; i < 0x48d; ++i)
306 printf("VMX: CAP MSR[%3x]: %llx\n", i, l4_vm_vmx_get_caps(vcpu, i));
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));
314 static void run_test(int ept_available)
318 vm_task = l4re_util_cap_alloc();
319 l4_umword_t ip, marker;
322 get_state_mem(&vmcs);
324 printf("run test, ept_available=%d\n", ept_available);
326 if (l4_is_invalid_cap(vm_task))
328 printf("No more caps.\n");
332 tag = l4_factory_create_vm(l4re_env()->factory, vm_task);
335 printf("Failed to create new task\n");
339 vmcs_s = (l4_addr_t *)vmcs;
341 vcpu->user_task = vm_task;
352 asm volatile(" jmp 1f; \n"
356 " addl %%edx,%%eax \n"
358 " addl %%edx,%%eax \n"
362 " addl %%edx,%%eax \n"
366 " movl %%eax, %%ecx \n"
367 " addl %%edx, %%ecx \n"
370 " addl %%edx,%%eax \n"
375 : "=r" (ip), "=r" (marker), "=r" (test_end));
377 asm volatile(" jmp 2f; \n"
387 :"=r"(ip), "=r"(marker));
397 printf("ip=%lx\n", ip);
399 init_vmcs((void *)vmcs);
401 asm volatile("pushf \n"
406 eflags = (eflags & 0xfffffdff);
407 eflags &= ~(0x1 << 17);
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);
417 vmwrite(vmcs_s, VMX_EXCEPTION_BITMAP, 0xffffffff);
418 vmwrite(vmcs_s, VMX_PF_ERROR_CODE_MATCH, 0);
421 for (ofs = 0; ofs < STACKSIZE; ofs += L4_PAGESIZE)
424 unsigned char c = stack[ofs];
427 asm volatile("nop" : "=a"(dummy) : "0" (c));
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,
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));
440 idt[26] = 0x80000; // #13 general protection fault
443 idt[28] = 0x80000; // #14 page fault
450 // stack segment 0x10
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));
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));
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");
478 __attribute__((aligned(4096))) int main(void)
480 printf("VMX testing\n");
484 printf("No VT CPU. Bye.\n");
488 l4_touch_rw(stack, sizeof(stack));
489 l4_touch_rw(hdl_stack, sizeof(hdl_stack));
493 printf("VM test exited\n");