vmmcp: this should be the complete list of registers we need to autoload
authorRonald G. Minnich <rminnich@gmail.com>
Tue, 16 Jun 2015 05:23:03 +0000 (22:23 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Mon, 22 Jun 2015 23:34:00 +0000 (16:34 -0700)
If we set FS_BASE and GS_BASE on autoload, vmmcpkernel causes
a vmware machine check. Why?

For LSTAR:
You have to disable intercept but also for this one do autoload.

If you don't disable intercept then the guest can not read or set it.
If you don't set autoload and the guest sets it, then this non-virtualized
register will probably kill your kernel.

Signed-off-by: Ronald G. Minnich <rminnich@gmail.com>
kern/arch/x86/vmm/intel/vmx.c
tests/vmm/vmmcpkernel.c

index 25e2825..421a168 100644 (file)
@@ -1,3 +1,4 @@
+//#define DEBUG
 /**
  *  vmx.c - The Intel VT-x driver for Dune
  *
@@ -176,6 +177,13 @@ int x86_ept_pte_fix_ups = 0;
 struct vmx_capability vmx_capability;
 struct vmcs_config vmcs_config;
 
+static int autoloaded_msrs[] = {
+       MSR_KERNEL_GS_BASE,
+       MSR_LSTAR,
+       MSR_STAR,
+       MSR_SFMASK,
+};
+
 void ept_flush(uint64_t eptp)
 {
        ept_sync_context(eptp);
@@ -983,18 +991,63 @@ static void __vmx_disable_intercept_for_msr(unsigned long *msr_bitmap, uint32_t
        }
 }
 
+static void vcpu_print_autoloads(struct vmx_vcpu *vcpu)
+{
+       struct vmx_msr_entry *e;
+       int sz = sizeof(autoloaded_msrs) / sizeof(*autoloaded_msrs);
+       printk("Host Autoloads:\n-------------------\n");
+       for (int i = 0; i < sz; i++) {
+               e = &vcpu->msr_autoload.host[i];
+               printk("\tMSR 0x%08x: %p\n", e->index, e->value);
+       }
+       printk("Guest Autoloads:\n-------------------\n");
+       for (int i = 0; i < sz; i++) {
+               e = &vcpu->msr_autoload.guest[i];
+               printk("\tMSR 0x%08x %p\n", e->index, e->value);
+       }
+}
+
+static void dumpmsrs(void)
+{
+       int i;
+       int set[] = {
+               MSR_LSTAR,
+               MSR_FS_BASE,
+               MSR_GS_BASE,
+               MSR_KERNEL_GS_BASE,
+               MSR_SFMASK,
+               MSR_IA32_PEBS_ENABLE
+       };
+       for(i = 0; i < ARRAY_SIZE(set); i++) {
+               printk("%p: %p\n", set[i], read_msr(set[i]));
+       }
+       printk("core id %d\n", hw_core_id());
+}
+
+/* Notes on autoloading.  We can't autoload FS_BASE or GS_BASE, according to the
+ * manual, but that's because they are automatically saved and restored when all
+ * of the other architectural registers are saved and restored, such as cs, ds,
+ * es, and other fun things. (See 24.4.1).  We need to make sure we don't
+ * accidentally intercept them too, since they are magically autloaded..
+ *
+ * We'll need to be careful of any MSR we neither autoload nor intercept
+ * whenever we vmenter/vmexit, and we intercept by default.
+ *
+ * Other MSRs, such as MSR_IA32_PEBS_ENABLE only work on certain architectures
+ * only work on certain architectures. */
 static void setup_msr(struct vmx_vcpu *vcpu)
 {
-       int set[] = { MSR_LSTAR };
        struct vmx_msr_entry *e;
-       int sz = sizeof(set) / sizeof(*set);
+       int sz = sizeof(autoloaded_msrs) / sizeof(*autoloaded_msrs);
        int i;
 
-       //BUILD_BUG_ON(sz > NR_AUTOLOAD_MSRS);
+       static_assert((sizeof(autoloaded_msrs) / sizeof(*autoloaded_msrs)) <=
+                     NR_AUTOLOAD_MSRS);
 
        vcpu->msr_autoload.nr = sz;
 
-       /* XXX enable only MSRs in set */
+       /* Since PADDR(msr_bitmap) is non-zero, and the bitmap is all 0xff, we now
+        * intercept all MSRs */
        vmcs_write64(MSR_BITMAP, PADDR(msr_bitmap));
 
        vmcs_write32(VM_EXIT_MSR_STORE_COUNT, vcpu->msr_autoload.nr);
@@ -1009,13 +1062,13 @@ static void setup_msr(struct vmx_vcpu *vcpu)
                uint64_t val;
 
                e = &vcpu->msr_autoload.host[i];
-               e->index = set[i];
+               e->index = autoloaded_msrs[i];
                __vmx_disable_intercept_for_msr(msr_bitmap, e->index);
                rdmsrl(e->index, val);
                e->value = val;
 
                e = &vcpu->msr_autoload.guest[i];
-               e->index = set[i];
+               e->index = autoloaded_msrs[i];
                e->value = 0xDEADBEEF;
        }
 }
@@ -1354,7 +1407,6 @@ int vmx_launch(uint64_t rip, uint64_t rsp, uint64_t cr3)
 {
        int ret;
        struct vmx_vcpu *vcpu;
-       int i = 0;
        int errors = 0;
 
        printd("RUNNING: %s: rip %p rsp %p cr3 %p \n",
@@ -1366,6 +1418,10 @@ int vmx_launch(uint64_t rip, uint64_t rsp, uint64_t cr3)
                return -ENOMEM;
        }
 
+       /* We need to prep the host's autoload region for our current core.  Right
+        * now, the only autoloaded MSR that varies at runtime (in this case per
+        * core is the KERN_GS_BASE). */
+       rdmsrl(MSR_KERNEL_GS_BASE, vcpu->msr_autoload.host[0].value);
        /* if cr3 is set, means 'set everything', else means 'start where you left off' */
        if (cr3) {
                vmx_get_cpu(vcpu);
@@ -1633,9 +1689,9 @@ int intel_vmm_init(void)
        /* FIXME: do we need APIC virtualization (flexpriority?) */
 
        memset(msr_bitmap, 0xff, PAGE_SIZE);
+       /* These are the only MSRs that are not autoloaded and not intercepted */
        __vmx_disable_intercept_for_msr(msr_bitmap, MSR_FS_BASE);
        __vmx_disable_intercept_for_msr(msr_bitmap, MSR_GS_BASE);
-       __vmx_disable_intercept_for_msr(msr_bitmap, MSR_KERN_GS_BASE);
 
        if ((ret = ept_init())) {
                printk("EPT init failed, %d\n", ret);
index ae44eac..31165dc 100644 (file)
@@ -21,10 +21,15 @@ int mcp = 1;
 
 uint8_t _kernel[64*1048576];
 
-static void *fail(void*arg)
+void *fail(void*arg)
 {
 
-       __asm__ __volatile__("vmcall\n");
+       __asm__ __volatile__(
+                       "MOVl $0xc0000082, %ecx\nrdmsr\naddl $1, %eax\nwrmsr\n"
+                       "MOVl $0xc0000100, %ecx\nrdmsr\naddl $1, %eax\nwrmsr\n"
+                       "MOVl $0xc0000101, %ecx\nrdmsr\naddl $1, %eax\nwrmsr\n"
+                       "MOVl $0xc0000102, %ecx\nrdmsr\naddl $1, %eax\nwrmsr\n"
+                       "mov $0x30, %rdi\nvmcall\nhlt\n");
        *mmap_blob = 1337;
        if (mcp)
        while (V(&shared, int) < 31) {
@@ -151,7 +156,8 @@ int main(int argc, char **argv)
        uint64_t entry = kernbase + (uint64_t) fail;
        uint8_t *kernel = (void *)(16*1048576);
        uint8_t program[] = {0x0f, 0x1, 0xc1, 0xeb, 0xfe};
-       memmove(kernel, program, sizeof(program));
+       printf("memmove(%p, %p, %d\n", kernel, fail, 4096);
+       memmove(kernel, fail, 4096);
        entry = (uint64_t)kernel;
        printf("kernbase for pml4 is 0x%llx and entry is %llx\n", kernbase, entry);
        printf("p512 %p p512[0] is 0x%lx p1 %p p1[0] is 0x%x\n", p512, p512[0], p1, p1[0]);