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.


Modified boot and vmxassist to handle real/protected transition.
[palacios.git] / palacios / src / palacios / vmx_handler.c
index 2bcc394..f872aee 100644 (file)
 #include <palacios/vmcs.h>
 #include <palacios/vmx_lowlevel.h>
 #include <palacios/vmx_io.h>
+#include <palacios/vmx.h>
+#include <palacios/vmm_ctrl_regs.h>
+#include <palacios/vmm_lowlevel.h>
+#include <palacios/vmx_ctrl_regs.h>
+#include <palacios/vmx_assist.h>
 
 
 static int inline check_vmcs_write(vmcs_field_t field, addr_t val)
 {
     int ret = 0;
-    ret = vmcs_write(field,val);
+    ret = vmcs_write(field, val);
 
     if (ret != VMX_SUCCESS) {
         PrintError("VMWRITE error on %s!: %d\n", v3_vmcs_field_to_str(field), ret);
-        return 1;
     }
 
-    return 0;
+    return ret;
 }
 
 static int inline check_vmcs_read(vmcs_field_t field, void * val)
 {
     int ret = 0;
-    ret = vmcs_read(field,val);
+    ret = vmcs_read(field, val);
 
-    if(ret != VMX_SUCCESS) {
+    if (ret != VMX_SUCCESS) {
         PrintError("VMREAD error on %s!: %d\n", v3_vmcs_field_to_str(field), ret);
-        return ret;
     }
 
-    return 0;
+    return ret;
+}
+
+static void inline translate_access_to_v3_seg(struct vmcs_segment_access * access, 
+                                             struct v3_segment * v3_seg) {
+    v3_seg->type = access->type;
+    v3_seg->system = access->desc_type;
+    v3_seg->dpl = access->dpl;
+    v3_seg->present = access->present;
+    v3_seg->avail = access->avail;
+    v3_seg->long_mode = access->long_mode;
+    v3_seg->db = access->db;
+    v3_seg->granularity = access->granularity;
 }
 
-int v3_handle_vmx_exit(struct v3_gprs * gprs, struct guest_info * info)
+static int load_vmcs_guest_state(struct guest_info * info)
 {
-    uint32_t exit_reason;
-    ulong_t exit_qual;
 
-    check_vmcs_read(VMCS_EXIT_REASON, &exit_reason);
-    check_vmcs_read(VMCS_EXIT_QUAL, &exit_qual);
+    struct vmcs_segment_access access;
+    int ret = 0;
 
-    PrintDebug("VMX Exit taken, id-qual: %d-%ld\n", exit_reason, exit_qual);
+    // JRL: Add error checking
 
-    /* Update guest state */
+    memset(&access, 0, sizeof(access));
+
+    /* CS Segment */
+    check_vmcs_read(VMCS_GUEST_CS_BASE, &(info->segments.cs.base));
+    check_vmcs_read(VMCS_GUEST_CS_SELECTOR, &(info->segments.cs.selector));
+    check_vmcs_read(VMCS_GUEST_CS_LIMIT, &(info->segments.cs.limit));
+    check_vmcs_read(VMCS_GUEST_CS_ACCESS, &(access.value));
+
+    translate_access_to_v3_seg(&access, &(info->segments.cs));
+
+    /* SS Segment */
+    check_vmcs_read(VMCS_GUEST_SS_BASE, &(info->segments.ss.base));
+    check_vmcs_read(VMCS_GUEST_SS_SELECTOR, &(info->segments.ss.selector));
+    check_vmcs_read(VMCS_GUEST_SS_LIMIT, &(info->segments.ss.limit));
+    check_vmcs_read(VMCS_GUEST_SS_ACCESS, &(access.value));
+
+    translate_access_to_v3_seg(&access, &(info->segments.ss));
+
+    /* DS Segment */
+    check_vmcs_read(VMCS_GUEST_DS_BASE, &(info->segments.ds.base));
+    check_vmcs_read(VMCS_GUEST_DS_SELECTOR, &(info->segments.ds.selector));
+    check_vmcs_read(VMCS_GUEST_DS_LIMIT, &(info->segments.ds.limit));
+    check_vmcs_read(VMCS_GUEST_DS_ACCESS, &(access.value));
+
+    translate_access_to_v3_seg(&access, &(info->segments.ds));
+
+    /* ES Segment */
+    check_vmcs_read(VMCS_GUEST_ES_BASE, &(info->segments.es.base));
+    check_vmcs_read(VMCS_GUEST_ES_SELECTOR, &(info->segments.es.selector));
+    check_vmcs_read(VMCS_GUEST_ES_LIMIT, &(info->segments.es.limit));
+    check_vmcs_read(VMCS_GUEST_ES_ACCESS, &(access.value));
+
+    translate_access_to_v3_seg(&access, &(info->segments.es));
+
+    /* FS Segment */
+    check_vmcs_read(VMCS_GUEST_FS_BASE, &(info->segments.fs.base));
+    check_vmcs_read(VMCS_GUEST_FS_SELECTOR, &(info->segments.fs.selector));
+    check_vmcs_read(VMCS_GUEST_FS_LIMIT, &(info->segments.fs.limit));
+    check_vmcs_read(VMCS_GUEST_FS_ACCESS, &(access.value));
+
+    translate_access_to_v3_seg(&access, &(info->segments.fs));
+
+
+    /* GS Segment */
+    check_vmcs_read(VMCS_GUEST_GS_BASE, &(info->segments.gs.base));
+    check_vmcs_read(VMCS_GUEST_GS_SELECTOR, &(info->segments.gs.selector));
+    check_vmcs_read(VMCS_GUEST_GS_LIMIT, &(info->segments.gs.limit));
+    check_vmcs_read(VMCS_GUEST_GS_ACCESS, &(access.value));
+
+    translate_access_to_v3_seg(&access, &(info->segments.gs));
+
+    /* LDTR Segment */
+    check_vmcs_read(VMCS_GUEST_LDTR_BASE, &(info->segments.ldtr.base));
+    check_vmcs_read(VMCS_GUEST_LDTR_SELECTOR, &(info->segments.ldtr.selector));
+    check_vmcs_read(VMCS_GUEST_LDTR_LIMIT, &(info->segments.ldtr.limit));
+    check_vmcs_read(VMCS_GUEST_LDTR_ACCESS, &(access.value));
+
+    translate_access_to_v3_seg(&access, &(info->segments.ldtr));
+
+    /* TR Segment */
+    check_vmcs_read(VMCS_GUEST_TR_BASE, &(info->segments.tr.base));
+    check_vmcs_read(VMCS_GUEST_TR_SELECTOR, &(info->segments.tr.selector));
+    check_vmcs_read(VMCS_GUEST_TR_LIMIT, &(info->segments.tr.limit));
+    check_vmcs_read(VMCS_GUEST_TR_ACCESS, &(access.value));
+
+    translate_access_to_v3_seg(&access, &(info->segments.tr));
+
+    /* GDTR Segment */
+    check_vmcs_read(VMCS_GUEST_GDTR_BASE, &(info->segments.gdtr.base));
+    check_vmcs_read(VMCS_GUEST_GDTR_LIMIT, &(info->segments.gdtr.limit));
+    
+    /* IDTR Segment */
+    check_vmcs_read(VMCS_GUEST_IDTR_BASE, &(info->segments.idtr.base));
+    check_vmcs_read(VMCS_GUEST_IDTR_LIMIT, &(info->segments.idtr.limit));
+
+
+    /* 
+     *  Read the control state
+     */
     check_vmcs_read(VMCS_GUEST_RIP, &(info->rip));
     check_vmcs_read(VMCS_GUEST_RSP, &(info->vm_regs.rsp));
     check_vmcs_read(VMCS_GUEST_CR0, &(info->ctrl_regs.cr0));
+    check_vmcs_read(VMCS_CR0_READ_SHDW, &(info->shdw_pg_state.guest_cr0));
     check_vmcs_read(VMCS_GUEST_CR3, &(info->ctrl_regs.cr3));
     check_vmcs_read(VMCS_GUEST_CR4, &(info->ctrl_regs.cr4));
 
-    // read out segments
-
-    switch(exit_reason)
-    {
-        case VMEXIT_INFO_EXCEPTION_OR_NMI:
-        {
-            uint32_t int_info;
-            pf_error_t error_code;
-            check_vmcs_read(VMCS_EXIT_INT_INFO, &int_info);
-            check_vmcs_read(VMCS_EXIT_INT_ERR, &error_code);
-
-            if((uint8_t)int_info == 0x0e) {
-                PrintDebug("Page Fault at %p\n", (void*)exit_qual);
-                if(info->shdw_pg_mode == SHADOW_PAGING) {
-                    if(v3_handle_shadow_pagefault(info, (addr_t)exit_qual, error_code) == -1) {
-                        return -1;
-                    }
-                } else {
-                    PrintError("Page fault in unimplemented paging mode\n");
+    return ret;
+}
+
+
+#if 0
+static void setup_v8086_mode_for_boot(struct guest_info * info)
+{
+
+    ((struct vmx_data *)info->vmm_data)->state = VMXASSIST_V8086_BIOS;
+    struct rflags * flags = (struct rflags *)&(info->ctrl_regs.rflags);
+    flags->rsvd1 = 1;
+    flags->vm = 1;
+    flags->iopl = 3;
+
+    info->rip = 0xfff0;
+   
+    /* Zero the segment registers */
+    memset(&(info->segments), 0, sizeof(struct v3_segment)*6);
+
+
+    info->segments.cs.selector = 0xf000;
+    info->segments.cs.base = 0xf000 << 4;
+    info->segments.cs.limit = 0xffff;
+    info->segments.cs.type = 3;
+    info->segments.cs.system = 1;
+    info->segments.cs.dpl = 3;
+    info->segments.cs.present = 1;
+    info->segments.cs.granularity = 0;
+
+    int i;
+    
+    /* Set values for selectors ds through ss */
+    struct v3_segment * seg_ptr = (struct v3_segment *)&(info->segments);
+    for(i = 1; i < 6 ; i++) {
+        seg_ptr[i].selector = 0x0000;
+        seg_ptr[i].base = 0x00000;
+        seg_ptr[i].limit = 0xffff;
+        seg_ptr[i].type = 3;
+        seg_ptr[i].system = 1;
+        seg_ptr[i].dpl = 3;
+        seg_ptr[i].present = 1;
+        seg_ptr[i].granularity = 0;
+    }
+
+}
+
+#endif
+    
+static int inline handle_cr_access(struct guest_info * info, ulong_t exit_qual) {
+    struct vmexit_cr_qual * cr_qual = (struct vmexit_cr_qual *)&exit_qual;
+
+    PrintDebug("Control register: %d\n", cr_qual->access_type);
+
+    if (cr_qual->access_type < 2) {
+        v3_reg_t reg = 0;
+       
+       switch(cr_qual->gpr) {
+            case 0:
+                reg = info->vm_regs.rax;
+                break;
+            case 1:
+                reg = info->vm_regs.rcx;
+                break;
+            case 2:
+                reg = info->vm_regs.rdx;
+                break;
+            case 3:
+                reg = info->vm_regs.rbx;
+                break;
+            case 4:
+                reg = info->vm_regs.rsp;
+                break;
+            case 5:
+                reg = info->vm_regs.rbp;
+                break;
+            case 6:
+                reg = info->vm_regs.rsi;
+                break;
+            case 7:
+                reg = info->vm_regs.rdi;
+                break;
+            case 8:
+                reg = info->vm_regs.r8;
+                break;
+            case 9:
+                reg = info->vm_regs.r9;
+                break;
+            case 10:
+                reg = info->vm_regs.r10;
+                break;
+            case 11:
+                reg = info->vm_regs.r11;
+                break;
+            case 12:
+                reg = info->vm_regs.r11;
+                break;
+            case 13:
+                reg = info->vm_regs.r13;
+                break;
+            case 14:
+                reg = info->vm_regs.r14;
+                break;
+            case 15:
+                reg = info->vm_regs.r15;
+                break;
+        }
+
+        if (cr_qual->cr_id == 0) {
+            uint32_t instr_len;
+
+            vmcs_read(VMCS_EXIT_INSTR_LEN, &instr_len);
+
+            if ( ~reg & CR0_PE ) {
+
+                if (v3_vmxassist_ctx_switch(info) != 0) {
+                    PrintError("Unable to execute VMXASSIST context switch!\n");
                     return -1;
                 }
+
+                load_vmcs_guest_state(info);
+
+                ((struct vmx_data *)info->vmm_data)->state = VMXASSIST_ENABLED;
+
+                PrintDebug("Loading vmxassist at RIP: 0x%p\n", (void *)info->rip);
+                return 0;
+            } else if (v3_vmx_handle_cr0_write(info, reg) != 0) {
+               PrintError("Could not handle CR0 Write\n");
+                return -1;
             }
-            break;
+
+            load_vmcs_guest_state(info);
+
+            PrintDebug("Leaving VMXASSIST and entering protected mode at RIP: 0x%p\n", (void *)info->rip);
+
+            return 0;
         }
+    }
+
+    PrintError("Unhandled CR access\n");
+    return -1;
+}
+
+
+/* At this point the GPRs are already copied into the guest_info state */
+int v3_handle_vmx_exit(struct v3_gprs * gprs, struct guest_info * info) {
+    uint32_t exit_reason;
+    ulong_t exit_qual;
+
+    check_vmcs_read(VMCS_EXIT_REASON, &exit_reason);
+    check_vmcs_read(VMCS_EXIT_QUAL, &exit_qual);
+
+    // PrintDebug("VMX Exit taken, id-qual: %u-%lu\n", exit_reason, exit_qual);
+
+    /* Update guest state */
+    load_vmcs_guest_state(info);
+  
+    switch (exit_reason) {
+        case VMEXIT_INFO_EXCEPTION_OR_NMI: {
+           uint32_t int_info;
+           pf_error_t error_code;
+
+           check_vmcs_read(VMCS_EXIT_INT_INFO, &int_info);
+           check_vmcs_read(VMCS_EXIT_INT_ERR, &error_code);
+           
+           // JRL: Change "0x0e" to a macro value
+           if ((uint8_t)int_info == 0x0e) {
+               PrintDebug("Page Fault at %p\n", (void *)exit_qual);
+               
+               if (info->shdw_pg_mode == SHADOW_PAGING) {
+                   if (v3_handle_shadow_pagefault(info, (addr_t)exit_qual, error_code) == -1) {
+                       PrintError("Error handling shadow page fault\n");
+                       return -1;
+                   }
+               } else {
+                   PrintError("Page fault in unimplemented paging mode\n");
+                   return -1;
+               }
+           } else {
+               PrintDebug("Unknown exception: 0x%x\n", (uint8_t)int_info);
+               v3_print_GPRs(info);
+               return -1;
+           }
+           break;
+       }
+           
+        case VMEXIT_CPUID: {
+           int instr_len;
+
+           v3_cpuid(info->vm_regs.rax, (addr_t *)&(info->vm_regs.rax), (addr_t *)&(info->vm_regs.rbx), 
+                    (addr_t *)&(info->vm_regs.rcx), (addr_t *)&(info->vm_regs.rdx));
+
+           check_vmcs_read(VMCS_EXIT_INSTR_LEN, &instr_len);
+
+           info->rip += instr_len;
+           break;
+       }
+           
+        case VMEXIT_IO_INSTR: {
+           struct vmexit_io_qual * io_qual = (struct vmexit_io_qual *)&exit_qual;
+           
+           if (io_qual->dir == 0) {
+               if (io_qual->string) {
+                   if (v3_handle_vmx_io_outs(info) == -1) {
+                       PrintError("Error in outs IO handler\n");
+                       return -1;
+                   }
+               } else {
+                   if (v3_handle_vmx_io_out(info) == -1) {
+                       PrintError("Error in out IO handler\n");
+                       return -1;
+                   }
+               }
+           } else {
+               if (io_qual->string) {
+                   if(v3_handle_vmx_io_ins(info) == -1) {
+                       PrintError("Error in ins IO handler\n");
+                       return -1;
+                   }
+               } else {
+                   if (v3_handle_vmx_io_in(info) == -1) {
+                       PrintError("Error in in IO handler\n");
+                       return -1;
+                   }
+               }
+           }
+           break;
+       }
+           
+        case VMEXIT_CR_REG_ACCESSES:
+            if (handle_cr_access(info,exit_qual) != 0) {
+               PrintError("Error handling CR access\n");
+                return -1;
+           }
 
-        case VMEXIT_IO_INSTR: 
-        {
-            struct vmcs_io_qual * io_qual = (struct vmcs_io_qual *)&exit_qual;
-
-            if(io_qual->dir == 0) {
-                if(io_qual->string) {
-                    if(v3_handle_vmx_io_outs(info) == -1) {
-                        return -1;
-                    }
-                } else {
-                    if(v3_handle_vmx_io_out(info) == -1) {
-                        return -1;
-                    }
-                }
-            } else {
-                if(io_qual->string) {
-                    if(v3_handle_vmx_io_ins(info) == -1) {
-                        return -1;
-                    }
-                } else {
-                    if(v3_handle_vmx_io_in(info) == -1) {
-                        return -1;
-                    }
-                }
-            }
             break;
-        }
 
         default:
-            PrintError("Unhandled VMEXIT\n");
+            PrintError("Unhandled VMEXIT: %u (0x%x), %lu (0x%lx)\n", exit_reason, exit_reason, exit_qual, exit_qual);
             return -1;
     }
 
@@ -132,6 +407,7 @@ int v3_handle_vmx_exit(struct v3_gprs * gprs, struct guest_info * info)
     check_vmcs_write(VMCS_GUEST_RIP, info->rip);
     check_vmcs_write(VMCS_GUEST_RSP, info->vm_regs.rsp);
 
-    PrintDebug("Executing VMRESUME\n");
+    check_vmcs_write(VMCS_CR0_READ_SHDW, info->shdw_pg_state.guest_cr0);
+
     return 0;
 }