Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


addd vmx_handler files
[palacios.git] / palacios / src / palacios / vmx_handler.c
1 /* 
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.  
5  *
6  * The V3VEE Project is a joint project between Northwestern University
7  * and the University of New Mexico.  You can find out more at 
8  * http://www.v3vee.org
9  *
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.
13  *
14  * Author: Jack Lange <jarusl@cs.northwestern.edu>
15  *
16  * This is free software.  You are permitted to use,
17  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
18  */
19
20 static int PanicUnhandledVMExit(struct VM *vm)
21 {
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));
26   VMXPanic();
27   return 0;
28 }
29
30
31
32
33
34 static int HandleVMPrintsAndPanics(struct VM *vm, uint_t port, uint_t data)
35 {
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);
43     return 1;
44   } 
45
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);
54     return 1;
55   }
56
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);
60     return 1;
61   }
62   return 0;
63 }
64
65 static int HandleInOutExit(struct VM *vm)
66 {
67   uint_t address;
68
69   struct VMCSExitInfoFields *exitinfo = &(vm->vmcs.exitInfoFields);
70   struct VMExitIOQual * qual = (struct VMExitIOQual *)&(vm->vmcs.exitInfoFields.qualification);
71   struct VMXRegs *regs = &(vm->registers);
72
73   address=GetLinearIP(vm);
74
75   PrintTrace("Handling Input/Output Instruction Exit\n");
76
77   PrintTrace_VMX_Regs(regs);
78
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",
84                    address,
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,
90                    qual->port);
91
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");
97   }
98                   
99
100   if ((qual->dir == 1) && (qual->REP == 0) && (qual->string == 0)) { 
101     char byte = In_Byte(qual->port);
102
103     vm->vmcs.guestStateArea.rip += exitinfo->instrLength;
104     regs->eax = (regs->eax & 0xffffff00) | byte;
105     PrintTrace("Returning 0x%x in eax\n", (regs->eax));
106   }
107
108   if (qual->dir==0 && qual->REP==0 && qual->string==0) { 
109     // See if we need to handle the outb as a signal or
110     // print from the VM
111     if (HandleVMPrintsAndPanics(vm,qual->port,regs->eax)) {
112     } else {
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));
116     }
117     vm->vmcs.guestStateArea.rip += exitinfo->instrLength;
118   }
119
120   return 0;
121 }  
122
123
124 static int HandleExternalIRQExit(struct VM *vm)
125 {
126   struct VMCSExitInfoFields * exitinfo = &(vm->vmcs.exitInfoFields);
127   struct VMExitIntInfo * intInfo  = (struct VMExitIntInfo *)&(vm->vmcs.exitInfoFields.intInfo);
128
129   PrintTrace("External Interrupt captured\n");
130   PrintTrace("IntInfo: %x\n", exitinfo->intInfo);
131
132
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
136      // host
137     PrintTrace("External Interrupt is invald.  Turning Interrupts back on\n");
138     asm("sti");
139     return 0;
140   } 
141
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
145
146   PrintTrace("type: %d\n", intInfo->type);
147   PrintTrace("number: %d\n", intInfo->nr);
148
149   PrintTrace("Interrupt %d occuring now and handled by HandleExternalIRQExit\n",intInfo->nr);
150
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
158      
159      ((char*)(&&ext_int_seq_start))[1] = intInfo->nr;
160  
161      PrintTrace("Interrupt instruction setup done %x\n", *((ushort_t *)(&&ext_int_seq_start)));
162      
163 ext_int_seq_start:
164      asm("int $0");
165   }
166
167     break;
168   case 2: // NMI
169     PrintTrace("Type: NMI\n");
170     break;
171   case 3: // hw exception
172     PrintTrace("Type: HW Exception\n");
173     break;
174   case 4: // sw exception
175     PrintTrace("Type: SW Exception\n");
176     break;
177   default:
178     PrintTrace("Invalid Interrupt Type\n");
179     return -1;
180   }
181   
182   if (intInfo->valid && intInfo->errorCode) {
183     PrintTrace("IntError: %x\n", exitinfo->intErrorCode);
184   }
185
186
187   return 0;
188
189 }
190
191
192
193
194
195
196
197 static int HandleExceptionOrNMI(struct VM *vm)
198 {
199   struct Instruction inst;
200   uint_t num;
201   uint_t type;
202   uint_t errorvalid;
203   uint_t error;
204   uint_t ext=0;
205   uint_t idt=0;
206   uint_t ti=0;
207   uint_t selectorindex=0;
208
209   PrintTrace("Exception or NMI occurred\n");
210   
211   num=vm->vmcs.exitInfoFields.intInfo & 0xff;
212   type=(vm->vmcs.exitInfoFields.intInfo & 0x700)>>8;
213   errorvalid=(vm->vmcs.exitInfoFields.intInfo & 0x800)>>11;
214   if (errorvalid) { 
215     error=vm->vmcs.exitInfoFields.intErrorCode;
216     ext=error&0x1;
217     idt=(error&0x2)>>1;
218     ti=(error&0x4)>>2;
219     selectorindex=(error>>3)&0xffff;
220   }
221   
222   PrintTrace("Exception %d now - handled by HandleExceptionOrNMI\n",num);
223
224   PrintTrace("Exception Number %u : %s\n", num, exception_names[num]);
225   PrintTrace("Exception Type %u : %s\n", type, exception_type_names[type]);
226   if (errorvalid) { 
227     if (ext) { 
228       PrintTrace("External\n");
229     } else {
230       PrintTrace("%s - Selector Index is %u\n", idt ? "IDT" : ti ? "LDT" : "GDT", selectorindex);
231     }
232   }
233
234   DecodeCurrentInstruction(vm,&inst);
235
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);
243       goto leave;
244     } else {
245       PrintTrace("Instruction is a write to CR0, but we don't understand it so we'll just exec it\n");
246     } 
247   } 
248
249
250   PrintTrace("Trying to execute the faulting instruction in VMM context now\n");
251   ExecFaultingInstructionInVMM(vm);
252
253     leave:
254   //
255   //PanicUnhandledVMExit(vmcs,regs);
256   //VMXPanic();
257   return 0;
258 }
259
260
261
262
263
264 int Do_VMM(struct VMXRegs regs) 
265 {
266
267   ullong_t vmcs_ptr = 0;
268   uint_t vmcs_ptr_low = 0;
269   int ret = 0;
270   uint_t vmx_abort = 0;
271
272
273   
274   PrintTrace("Vm Exit\n");
275   ret = VMCS_STORE(&vmcs_ptr);
276   vmcs_ptr &= 0xffffffff;
277   vmcs_ptr_low +=  vmcs_ptr;
278
279
280
281
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);
285     
286   struct VM *vm = FindVM();
287
288   if (vmx_abort != 0) {
289     PrintTrace("VM ABORTED w/ code: %x\n", vmx_abort);
290     return -1;
291   }
292
293   vm->registers = regs;
294
295   if (CopyOutVMCSData(&(vm->vmcs)) != 0) {
296     PrintTrace("Could not copy out VMCS\n");
297     return -1;
298   }
299
300
301   PrintTrace("Guest esp: 0x%x (%u)\n", vm->vmcs.guestStateArea.rsp, vm->vmcs.guestStateArea.rsp);
302
303   PrintTrace("VM Exit for reason: %d (%x)\n", 
304               vm->vmcs.exitInfoFields.reason & 0x00000fff,
305               vm->vmcs.exitInfoFields.reason);  
306
307   if (vm->vmcs.exitInfoFields.reason & (0x1<<29) ) { 
308     PrintTrace("VM Exit is from VMX root operation.  Panicking\n");
309     VMXPanic();
310   }
311
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));
315     VMXPanic();
316   }
317
318   switch (vm->vmcs.exitInfoFields.reason) {
319   case VM_EXIT_REASON_INFO_EXCEPTION_OR_NMI:
320     ret = HandleExceptionOrNMI(vm);
321     break;
322   case VM_EXIT_REASON_EXTERNAL_INTR:
323     ret = HandleExternalIRQExit(vm);
324     break;
325   case VM_EXIT_REASON_TRIPLE_FAULT:
326     ret = PanicUnhandledVMExit(vm);
327     break;
328   case VM_EXIT_REASON_INIT_SIGNAL:
329     ret = PanicUnhandledVMExit(vm);
330     break;
331   case VM_EXIT_REASON_STARTUP_IPI:
332     ret = PanicUnhandledVMExit(vm);
333     break;
334   case VM_EXIT_REASON_IO_SMI:
335     ret = PanicUnhandledVMExit(vm);
336     break;
337   case VM_EXIT_REASON_OTHER_SMI:
338     ret = PanicUnhandledVMExit(vm);
339     break;
340   case VM_EXIT_REASON_INTR_WINDOW:
341     ret = PanicUnhandledVMExit(vm);
342     break;
343   case VM_EXIT_REASON_NMI_WINDOW:
344     ret = PanicUnhandledVMExit(vm);
345     break;
346   case VM_EXIT_REASON_TASK_SWITCH:
347     ret = PanicUnhandledVMExit(vm);
348     break;
349   case VM_EXIT_REASON_CPUID:
350     ret = PanicUnhandledVMExit(vm);
351     break;
352   case VM_EXIT_REASON_INVD:
353     ret = PanicUnhandledVMExit(vm);
354     break;
355   case VM_EXIT_REASON_INVLPG:
356     ret = PanicUnhandledVMExit(vm);
357     break;
358   case VM_EXIT_REASON_RDPMC:
359     ret = PanicUnhandledVMExit(vm);
360     break;
361   case VM_EXIT_REASON_RDTSC:
362     ret = PanicUnhandledVMExit(vm);
363     break;
364   case VM_EXIT_REASON_RSM:
365     ret = PanicUnhandledVMExit(vm);
366     break;
367   case VM_EXIT_REASON_VMCALL:
368     ret = PanicUnhandledVMExit(vm);
369     break;
370   case VM_EXIT_REASON_VMCLEAR:
371     ret = PanicUnhandledVMExit(vm);
372     break;
373   case VM_EXIT_REASON_VMLAUNCH:
374     ret = PanicUnhandledVMExit(vm);
375     break;
376   case VM_EXIT_REASON_VMPTRLD:
377     ret = PanicUnhandledVMExit(vm);
378     break;
379   case VM_EXIT_REASON_VMPTRST:
380     ret = PanicUnhandledVMExit(vm);
381     break;
382   case VM_EXIT_REASON_VMREAD:
383     ret = PanicUnhandledVMExit(vm);
384     break;
385   case VM_EXIT_REASON_VMRESUME:
386     ret = PanicUnhandledVMExit(vm);
387     break;
388   case VM_EXIT_REASON_VMWRITE:
389     ret = PanicUnhandledVMExit(vm);
390     break;
391   case VM_EXIT_REASON_VMXOFF:
392     ret = PanicUnhandledVMExit(vm);
393     break;
394   case VM_EXIT_REASON_VMXON:
395     ret = PanicUnhandledVMExit(vm);
396     break;
397   case VM_EXIT_REASON_CR_REG_ACCESSES:
398     ret = PanicUnhandledVMExit(vm);
399     break;
400   case VM_EXIT_REASON_MOV_DR:
401     ret = PanicUnhandledVMExit(vm);
402     break;
403   case VM_EXIT_REASON_IO_INSTR:
404     ret = HandleInOutExit(vm);
405     break;
406   case VM_EXIT_REASON_RDMSR:
407     ret = PanicUnhandledVMExit(vm);
408     break;
409   case VM_EXIT_REASON_WRMSR:
410     ret = PanicUnhandledVMExit(vm);
411     break;
412   case VM_EXIT_REASON_ENTRY_FAIL_INVALID_GUEST_STATE:
413     ret = PanicUnhandledVMExit(vm);
414     break;
415   case VM_EXIT_REASON_ENTRY_FAIL_MSR_LOAD:
416     ret = PanicUnhandledVMExit(vm);
417     break;
418   case VM_EXIT_REASON_MWAIT:
419     ret = PanicUnhandledVMExit(vm);
420     break;
421   case VM_EXIT_REASON_MONITOR:
422     ret = PanicUnhandledVMExit(vm);
423     break;
424   case VM_EXIT_REASON_PAUSE:
425     ret = PanicUnhandledVMExit(vm);
426     break;
427   case VM_EXIT_REASON_ENTRY_FAILURE_MACHINE_CHECK:
428     ret = PanicUnhandledVMExit(vm);
429     break;
430   case VM_EXIT_REASON_TPR_BELOW_THRESHOLD:
431     ret = PanicUnhandledVMExit(vm);
432     break;
433   default:
434     ret = PanicUnhandledVMExit(vm);
435     break;
436   }
437   
438   
439   regs = vm->registers;
440   CopyInVMCSData(&(vm->vmcs));
441
442   /*
443     {
444     VMCS_CLEAR(vmcs_ptr);
445     }
446   */
447
448   PrintTrace("Returning from Do_VMM: %d\n", ret);
449  
450   return ret;
451 }
452
453
454
455
456
457
458 // simply execute the instruction that is faulting and return
459 static int ExecFaultingInstructionInVMM(struct VM *vm)
460 {
461   uint_t address = GetLinearIP(vm);
462   myregs = (uint_t)&(vm->registers);
463   
464
465   PrintTrace("About the execute faulting instruction!\n");
466   PrintTrace("Instruction is:\n");
467   PrintTraceMemDump((void*)(address),vm->vmcs.exitInfoFields.instrLength);
468   
469
470   PrintTrace("The template code is:\n");
471   PrintTraceMemDump(&&template_code,TEMPLATE_CODE_LEN);
472
473   // clone the template code
474   //memcpy(&&template_code,code,MAX_CODE);
475   
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);
480   
481   PrintTrace("Finished modifying the template code, which now is:\n");
482   PrintTraceMemDump(&&template_code,TEMPLATE_CODE_LEN);
483
484   PrintTrace("Now entering modified template code\n");
485
486
487  template_code:
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
491   //
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
494   //
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)
516                         : "=m"(oldesp)
517                         : "m"(myregs)
518                         );
519   
520   PrintTrace("Survived executing the faulting instruction and returning.\n");
521
522   vm->vmcs.guestStateArea.rip += vm->vmcs.exitInfoFields.instrLength;
523
524   return 0;
525
526 }
527