2 * This file is part of the Palacios Virtual Machine Monitor developed
3 * by the V3VEE Project with funding from the United States National
4 * Science Foundation and the Department of Energy.
6 * The V3VEE Project is a joint project between Northwestern University
7 * and the University of New Mexico. You can find out more at
10 * Copyright (c) 2008, Jack Lange <jarusl@cs.northwestern.edu>
11 * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org>
12 * All rights reserved.
14 * Author: Jack Lange <jarusl@cs.northwestern.edu>
16 * This is free software. You are permitted to use,
17 * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
20 static int PanicUnhandledVMExit(struct VM *vm)
22 PrintInfo("Panicking due to VMExit with reason %u\n", vm->vmcs.exitInfoFields.reason);
23 PrintTrace("Panicking due to VMExit with reason %u\n", vm->vmcs.exitInfoFields.reason);
24 PrintTrace_VMCS_ALL();
25 PrintTrace_VMX_Regs(&(vm->registers));
34 static int HandleVMPrintsAndPanics(struct VM *vm, uint_t port, uint_t data)
36 if (port==VMXASSIST_INFO_PORT &&
37 (vm->state == VM_VMXASSIST_STARTUP ||
38 vm->state == VM_VMXASSIST_V8086_BIOS ||
39 vm->state == VM_VMXASSIST_V8086)) {
40 // Communication channel from VMXAssist
41 PrintTrace("VMXASSIST Output Port\n");
42 PrintDebug("%c",data&0xff);
46 if ((port==ROMBIOS_PANIC_PORT ||
47 port==ROMBIOS_PANIC_PORT2 ||
48 port==ROMBIOS_DEBUG_PORT ||
49 port==ROMBIOS_INFO_PORT) &&
50 (vm->state==VM_VMXASSIST_V8086_BIOS)) {
51 // rombios is communicating
52 PrintTrace("ROMBIOS Output Port\n");
53 // PrintDebug("%c",data&0xff);
57 if (port==BOOT_STATE_CARD_PORT && vm->state==VM_VMXASSIST_V8086_BIOS) {
58 // rombios is sending something to the display card
59 PrintTrace("Hex Display: 0x%x\n",data&0xff);
65 static int HandleInOutExit(struct VM *vm)
69 struct VMCSExitInfoFields *exitinfo = &(vm->vmcs.exitInfoFields);
70 struct VMExitIOQual * qual = (struct VMExitIOQual *)&(vm->vmcs.exitInfoFields.qualification);
71 struct VMXRegs *regs = &(vm->registers);
73 address=GetLinearIP(vm);
75 PrintTrace("Handling Input/Output Instruction Exit\n");
77 PrintTrace_VMX_Regs(regs);
79 PrintTrace("Qualifications=0x%x\n", exitinfo->qualification);
80 PrintTrace("Reason=0x%x\n", exitinfo->reason);
81 PrintTrace("IO Port: 0x%x (%d)\n", qual->port, qual->port);
82 PrintTrace("Instruction Info=%x\n", exitinfo->instrInfo);
83 PrintTrace("%x : %s %s %s instruction of length %d for %d bytes from/to port 0x%x\n",
85 qual->dir == 0 ? "output" : "input",
86 qual->string ==0 ? "nonstring" : "STRING",
87 qual->REP == 0 ? "with no rep" : "WITH REP",
88 exitinfo->instrLength,
89 qual->accessSize==0 ? 1 : qual->accessSize==1 ? 2 : 4,
92 if ((qual->port == PIC_MASTER_CMD_ISR_PORT) ||
93 (qual->port == PIC_MASTER_IMR_PORT) ||
94 (qual->port == PIC_SLAVE_CMD_ISR_PORT) ||
95 (qual->port == PIC_SLAVE_IMR_PORT)) {
96 PrintTrace( "PIC Access\n");
100 if ((qual->dir == 1) && (qual->REP == 0) && (qual->string == 0)) {
101 char byte = In_Byte(qual->port);
103 vm->vmcs.guestStateArea.rip += exitinfo->instrLength;
104 regs->eax = (regs->eax & 0xffffff00) | byte;
105 PrintTrace("Returning 0x%x in eax\n", (regs->eax));
108 if (qual->dir==0 && qual->REP==0 && qual->string==0) {
109 // See if we need to handle the outb as a signal or
111 if (HandleVMPrintsAndPanics(vm,qual->port,regs->eax)) {
113 // If not, just go ahead and do the outb
114 Out_Byte(qual->port,regs->eax);
115 PrintTrace("Wrote 0x%x to port\n",(regs->eax));
117 vm->vmcs.guestStateArea.rip += exitinfo->instrLength;
124 static int HandleExternalIRQExit(struct VM *vm)
126 struct VMCSExitInfoFields * exitinfo = &(vm->vmcs.exitInfoFields);
127 struct VMExitIntInfo * intInfo = (struct VMExitIntInfo *)&(vm->vmcs.exitInfoFields.intInfo);
129 PrintTrace("External Interrupt captured\n");
130 PrintTrace("IntInfo: %x\n", exitinfo->intInfo);
133 if (!intInfo->valid) {
134 // interrupts are off, but this interrupt is not acknoledged (still pending)
135 // so we turn on interrupts to deliver appropriately in the
137 PrintTrace("External Interrupt is invald. Turning Interrupts back on\n");
142 // At this point, interrupts are off and the interrupt has been
143 // acknowledged. We will now handle the interrupt ourselves
144 // and turn interrupts back on in the host
146 PrintTrace("type: %d\n", intInfo->type);
147 PrintTrace("number: %d\n", intInfo->nr);
149 PrintTrace("Interrupt %d occuring now and handled by HandleExternalIRQExit\n",intInfo->nr);
151 switch (intInfo->type) {
152 case 0: { // ext. IRQ
153 // In the following, we construct an "int x" instruction
154 // where x is the specific interrupt number that is raised
155 // then we execute that instruciton
156 // because we are in host context, that means it is delivered as normal
157 // through the host IDT
159 ((char*)(&&ext_int_seq_start))[1] = intInfo->nr;
161 PrintTrace("Interrupt instruction setup done %x\n", *((ushort_t *)(&&ext_int_seq_start)));
169 PrintTrace("Type: NMI\n");
171 case 3: // hw exception
172 PrintTrace("Type: HW Exception\n");
174 case 4: // sw exception
175 PrintTrace("Type: SW Exception\n");
178 PrintTrace("Invalid Interrupt Type\n");
182 if (intInfo->valid && intInfo->errorCode) {
183 PrintTrace("IntError: %x\n", exitinfo->intErrorCode);
197 static int HandleExceptionOrNMI(struct VM *vm)
199 struct Instruction inst;
207 uint_t selectorindex=0;
209 PrintTrace("Exception or NMI occurred\n");
211 num=vm->vmcs.exitInfoFields.intInfo & 0xff;
212 type=(vm->vmcs.exitInfoFields.intInfo & 0x700)>>8;
213 errorvalid=(vm->vmcs.exitInfoFields.intInfo & 0x800)>>11;
215 error=vm->vmcs.exitInfoFields.intErrorCode;
219 selectorindex=(error>>3)&0xffff;
222 PrintTrace("Exception %d now - handled by HandleExceptionOrNMI\n",num);
224 PrintTrace("Exception Number %u : %s\n", num, exception_names[num]);
225 PrintTrace("Exception Type %u : %s\n", type, exception_type_names[type]);
228 PrintTrace("External\n");
230 PrintTrace("%s - Selector Index is %u\n", idt ? "IDT" : ti ? "LDT" : "GDT", selectorindex);
234 DecodeCurrentInstruction(vm,&inst);
236 if (inst.type==VM_MOV_TO_CR0) {
237 PrintTrace("MOV TO CR0, oldvalue=0x%x, newvalue=0x%x\n",inst.input2, inst.input1);
238 if ((inst.input2 & CR0_PE) && !(inst.input1 & CR0_PE) && vm->state==VM_VMXASSIST_STARTUP) {
239 // This is VMXAssist signalling for us to turn on V8086 mode and
240 // jump into the bios
241 PrintTrace("VMXAssist is signaling us for switch to V8086 mode and jump to 0xf000:fff0\n");
242 SetupV8086ModeForBoot(vm);
245 PrintTrace("Instruction is a write to CR0, but we don't understand it so we'll just exec it\n");
250 PrintTrace("Trying to execute the faulting instruction in VMM context now\n");
251 ExecFaultingInstructionInVMM(vm);
255 //PanicUnhandledVMExit(vmcs,regs);
264 int Do_VMM(struct VMXRegs regs)
267 ullong_t vmcs_ptr = 0;
268 uint_t vmcs_ptr_low = 0;
270 uint_t vmx_abort = 0;
274 PrintTrace("Vm Exit\n");
275 ret = VMCS_STORE(&vmcs_ptr);
276 vmcs_ptr &= 0xffffffff;
277 vmcs_ptr_low += vmcs_ptr;
282 PrintTrace("ret=%d\n", ret);
283 PrintTrace("Revision: %x\n", *(uint_t *)(vmcs_ptr_low));
284 vmx_abort = *(uint_t*)(((char *)vmcs_ptr_low)+4);
286 struct VM *vm = FindVM();
288 if (vmx_abort != 0) {
289 PrintTrace("VM ABORTED w/ code: %x\n", vmx_abort);
293 vm->registers = regs;
295 if (CopyOutVMCSData(&(vm->vmcs)) != 0) {
296 PrintTrace("Could not copy out VMCS\n");
301 PrintTrace("Guest esp: 0x%x (%u)\n", vm->vmcs.guestStateArea.rsp, vm->vmcs.guestStateArea.rsp);
303 PrintTrace("VM Exit for reason: %d (%x)\n",
304 vm->vmcs.exitInfoFields.reason & 0x00000fff,
305 vm->vmcs.exitInfoFields.reason);
307 if (vm->vmcs.exitInfoFields.reason & (0x1<<29) ) {
308 PrintTrace("VM Exit is from VMX root operation. Panicking\n");
312 if (vm->vmcs.exitInfoFields.reason & (0x1<<31) ) {
313 PrintTrace("VM Exit is due to a VM entry failure. Shouldn't happen here. Panicking\n");
314 PrintTrace_VMCSData(&(vm->vmcs));
318 switch (vm->vmcs.exitInfoFields.reason) {
319 case VM_EXIT_REASON_INFO_EXCEPTION_OR_NMI:
320 ret = HandleExceptionOrNMI(vm);
322 case VM_EXIT_REASON_EXTERNAL_INTR:
323 ret = HandleExternalIRQExit(vm);
325 case VM_EXIT_REASON_TRIPLE_FAULT:
326 ret = PanicUnhandledVMExit(vm);
328 case VM_EXIT_REASON_INIT_SIGNAL:
329 ret = PanicUnhandledVMExit(vm);
331 case VM_EXIT_REASON_STARTUP_IPI:
332 ret = PanicUnhandledVMExit(vm);
334 case VM_EXIT_REASON_IO_SMI:
335 ret = PanicUnhandledVMExit(vm);
337 case VM_EXIT_REASON_OTHER_SMI:
338 ret = PanicUnhandledVMExit(vm);
340 case VM_EXIT_REASON_INTR_WINDOW:
341 ret = PanicUnhandledVMExit(vm);
343 case VM_EXIT_REASON_NMI_WINDOW:
344 ret = PanicUnhandledVMExit(vm);
346 case VM_EXIT_REASON_TASK_SWITCH:
347 ret = PanicUnhandledVMExit(vm);
349 case VM_EXIT_REASON_CPUID:
350 ret = PanicUnhandledVMExit(vm);
352 case VM_EXIT_REASON_INVD:
353 ret = PanicUnhandledVMExit(vm);
355 case VM_EXIT_REASON_INVLPG:
356 ret = PanicUnhandledVMExit(vm);
358 case VM_EXIT_REASON_RDPMC:
359 ret = PanicUnhandledVMExit(vm);
361 case VM_EXIT_REASON_RDTSC:
362 ret = PanicUnhandledVMExit(vm);
364 case VM_EXIT_REASON_RSM:
365 ret = PanicUnhandledVMExit(vm);
367 case VM_EXIT_REASON_VMCALL:
368 ret = PanicUnhandledVMExit(vm);
370 case VM_EXIT_REASON_VMCLEAR:
371 ret = PanicUnhandledVMExit(vm);
373 case VM_EXIT_REASON_VMLAUNCH:
374 ret = PanicUnhandledVMExit(vm);
376 case VM_EXIT_REASON_VMPTRLD:
377 ret = PanicUnhandledVMExit(vm);
379 case VM_EXIT_REASON_VMPTRST:
380 ret = PanicUnhandledVMExit(vm);
382 case VM_EXIT_REASON_VMREAD:
383 ret = PanicUnhandledVMExit(vm);
385 case VM_EXIT_REASON_VMRESUME:
386 ret = PanicUnhandledVMExit(vm);
388 case VM_EXIT_REASON_VMWRITE:
389 ret = PanicUnhandledVMExit(vm);
391 case VM_EXIT_REASON_VMXOFF:
392 ret = PanicUnhandledVMExit(vm);
394 case VM_EXIT_REASON_VMXON:
395 ret = PanicUnhandledVMExit(vm);
397 case VM_EXIT_REASON_CR_REG_ACCESSES:
398 ret = PanicUnhandledVMExit(vm);
400 case VM_EXIT_REASON_MOV_DR:
401 ret = PanicUnhandledVMExit(vm);
403 case VM_EXIT_REASON_IO_INSTR:
404 ret = HandleInOutExit(vm);
406 case VM_EXIT_REASON_RDMSR:
407 ret = PanicUnhandledVMExit(vm);
409 case VM_EXIT_REASON_WRMSR:
410 ret = PanicUnhandledVMExit(vm);
412 case VM_EXIT_REASON_ENTRY_FAIL_INVALID_GUEST_STATE:
413 ret = PanicUnhandledVMExit(vm);
415 case VM_EXIT_REASON_ENTRY_FAIL_MSR_LOAD:
416 ret = PanicUnhandledVMExit(vm);
418 case VM_EXIT_REASON_MWAIT:
419 ret = PanicUnhandledVMExit(vm);
421 case VM_EXIT_REASON_MONITOR:
422 ret = PanicUnhandledVMExit(vm);
424 case VM_EXIT_REASON_PAUSE:
425 ret = PanicUnhandledVMExit(vm);
427 case VM_EXIT_REASON_ENTRY_FAILURE_MACHINE_CHECK:
428 ret = PanicUnhandledVMExit(vm);
430 case VM_EXIT_REASON_TPR_BELOW_THRESHOLD:
431 ret = PanicUnhandledVMExit(vm);
434 ret = PanicUnhandledVMExit(vm);
439 regs = vm->registers;
440 CopyInVMCSData(&(vm->vmcs));
444 VMCS_CLEAR(vmcs_ptr);
448 PrintTrace("Returning from Do_VMM: %d\n", ret);
458 // simply execute the instruction that is faulting and return
459 static int ExecFaultingInstructionInVMM(struct VM *vm)
461 uint_t address = GetLinearIP(vm);
462 myregs = (uint_t)&(vm->registers);
465 PrintTrace("About the execute faulting instruction!\n");
466 PrintTrace("Instruction is:\n");
467 PrintTraceMemDump((void*)(address),vm->vmcs.exitInfoFields.instrLength);
470 PrintTrace("The template code is:\n");
471 PrintTraceMemDump(&&template_code,TEMPLATE_CODE_LEN);
473 // clone the template code
474 //memcpy(&&template_code,code,MAX_CODE);
476 // clean up the nop field
477 memset(&&template_code+INSTR_OFFSET_START,*((uchar_t *)(&&template_code+0)),NOP_SEQ_LEN);
478 // overwrite the nops with the faulting instruction
479 memcpy(&&template_code+INSTR_OFFSET_START, (void*)(address),vm->vmcs.exitInfoFields.instrLength);
481 PrintTrace("Finished modifying the template code, which now is:\n");
482 PrintTraceMemDump(&&template_code,TEMPLATE_CODE_LEN);
484 PrintTrace("Now entering modified template code\n");
488 // Template code stores current registers,
489 // restores registers, has a landing pad of noops
490 // that will be modified, restores current regs, and then returns
492 // Note that this currently ignores cr0, cr3, cr4, dr7, rsp, rip, and rflags
493 // it also blythly assumes it can exec the instruction in protected mode
495 __asm__ __volatile__ ("nop\n" // for cloning purposes (1 byte)
496 "pusha\n" // push our current regs onto the current stack (1 byte)
497 "movl %0, %%eax\n" // Get oldesp location (5 bytes)
498 "movl %%esp, (%%eax)\n" // store the current stack pointer in oldesp (2 bytes)
499 "movl %1, %%eax\n" // Get regs location (5 bytes)
500 "movl (%%eax), %%esp\n" // point esp at regs (2 bytes)
501 "popa\n" // now we have the VM registers restored (1 byte)
502 "nop\n" // now we execute the actual instruction (1 byte x 10)
503 "nop\n" // now we execute the actual instruction
504 "nop\n" // now we execute the actual instruction
505 "nop\n" // now we execute the actual instruction
506 "nop\n" // now we execute the actual instruction
507 "nop\n" // now we execute the actual instruction
508 "nop\n" // now we execute the actual instruction
509 "nop\n" // now we execute the actual instruction
510 "nop\n" // now we execute the actual instruction
511 "nop\n" // now we execute the actual instruction
512 // need to copy back to the VM registers!
513 "movl %0, %%eax\n" // recapture oldesp location (5 bytes)
514 "movl (%%eax), %%esp\n" // now we'll get our esp back from oldesp (2 bytes)
515 "popa\n" // and restore our GP regs and we're done (1 byte)
520 PrintTrace("Survived executing the faulting instruction and returning.\n");
522 vm->vmcs.guestStateArea.rip += vm->vmcs.exitInfoFields.instrLength;