Convert run_once() to parlib_run_once() (XCC)
[akaros.git] / user / vmm / vmexit.c
index 173afa6..fa61c80 100644 (file)
@@ -64,6 +64,43 @@ static void sleep_til_irq(struct guest_thread *gth)
        uth_mutex_unlock(gth->halt_mtx);
 }
 
+enum {
+               CPUID_0B_LEVEL_SMT = 0,
+               CPUID_0B_LEVEL_CORE
+};
+
+static bool handle_cpuid(struct guest_thread *gth)
+{
+       struct vm_trapframe *vm_tf = gth_to_vmtf(gth);
+       struct virtual_machine *vm = gth_to_vm(gth);
+       uint32_t level = vm_tf->tf_rcx & 0x0F;
+
+       if (vm_tf->tf_rax != 0x0B)
+               return FALSE;
+
+       vm_tf->tf_rip += 2;
+       vm_tf->tf_rax = 0;
+       vm_tf->tf_rbx = 0;
+       vm_tf->tf_rcx = level;
+       vm_tf->tf_rdx = gth->gpc_id;
+       if (level == CPUID_0B_LEVEL_SMT) {
+               vm_tf->tf_rax = 0;
+               vm_tf->tf_rbx = 1;
+               vm_tf->tf_rcx |= ((level + 1) << 8);
+       }
+       if (level == CPUID_0B_LEVEL_CORE) {
+               uint32_t shift = LOG2_UP(vm->nr_gpcs);
+
+               if (shift > 0x1F)
+                       shift = 0x1F;
+               vm_tf->tf_rax = shift;
+               vm_tf->tf_rbx = vm->nr_gpcs;
+               vm_tf->tf_rcx |= ((level + 1) << 8);
+       }
+
+       return TRUE;
+}
+
 static bool handle_ept_fault(struct guest_thread *gth)
 {
        struct vm_trapframe *vm_tf = gth_to_vmtf(gth);
@@ -73,8 +110,19 @@ static bool handle_ept_fault(struct guest_thread *gth)
        int store, size;
        int advance;
 
-       if (decode(gth, &gpa, &regx, &regp, &store, &size, &advance))
+       int ret = decode(gth, &gpa, &regx, &regp, &store, &size, &advance);
+
+       if (ret < 0)
                return FALSE;
+       if (ret == VM_PAGE_FAULT) {
+               /* We were unable to translate RIP due to an ept fault */
+               vm_tf->tf_trap_inject = VM_TRAP_VALID
+                                     | VM_TRAP_ERROR_CODE
+                                     | VM_TRAP_HARDWARE
+                                     | HW_TRAP_PAGE_FAULT;
+               return TRUE;
+       }
+
        assert(size >= 0);
        /* TODO use helpers for some of these addr checks.  the fee/fec ones might
         * be wrong too. */
@@ -110,7 +158,7 @@ static bool handle_ept_fault(struct guest_thread *gth)
        return TRUE;
 }
 
-static bool handle_vmcall(struct guest_thread *gth)
+static bool handle_vmcall_printc(struct guest_thread *gth)
 {
        struct vm_trapframe *vm_tf = gth_to_vmtf(gth);
        uint8_t byte;
@@ -119,20 +167,96 @@ static bool handle_vmcall(struct guest_thread *gth)
        printf("%c", byte);
        if (byte == '\n')
                printf("%c", '%');
-       vm_tf->tf_rip += 3;
+       fflush(stdout);
        return TRUE;
 }
 
+static bool handle_vmcall_smpboot(struct guest_thread *gth)
+{
+       struct vm_trapframe *vm_tf = gth_to_vmtf(gth);
+       struct vm_trapframe *vm_tf_ap;
+       struct virtual_machine *vm = gth_to_vm(gth);
+       int cur_pcores = vm->up_gpcs;
+
+       /* Check if we're guest pcore 0. Only the BSP is allowed to start APs. */
+       if (vm_tf->tf_guest_pcoreid != 0) {
+               fprintf(stderr,
+                       "Only guest pcore 0 is allowed to start APs. core was %ld\n",
+                       vm_tf->tf_guest_pcoreid);
+               return FALSE;
+       }
+
+       /* Check if we've reached the maximum, if yes, blow out. */
+       if (vm->nr_gpcs == cur_pcores) {
+               fprintf(stderr,
+                       "guest tried to start up too many cores. max was %ld, current up %ld\n",
+                       vm->nr_gpcs, cur_pcores);
+               return FALSE;
+       }
+
+       /* Start up secondary core. */
+       vm_tf_ap = gth_to_vmtf(vm->gths[cur_pcores]);
+       /* We use the BSP's CR3 for now. This should be fine because they
+        * change it later anyway. */
+       vm_tf_ap->tf_cr3 = vm_tf->tf_cr3;
+
+       /* Starting RIP is passed in via rdi. */
+       vm_tf_ap->tf_rip = vm_tf->tf_rdi;
+
+       /* Starting RSP is passed in via rsi. */
+       vm_tf_ap->tf_rsp = vm_tf->tf_rsi;
+
+       vm->up_gpcs++;
+
+       start_guest_thread(vm->gths[cur_pcores]);
+
+       return TRUE;
+}
+
+static bool handle_vmcall(struct guest_thread *gth)
+{
+       struct vm_trapframe *vm_tf = gth_to_vmtf(gth);
+       bool retval = FALSE;
+
+       if (gth->vmcall)
+               return gth->vmcall(gth, vm_tf);
+
+       switch (vm_tf->tf_rax) {
+               case VMCALL_PRINTC:
+                       retval = handle_vmcall_printc(gth);
+                       break;
+               case VMCALL_SMPBOOT:
+                       retval = handle_vmcall_smpboot(gth);
+                       break;
+       }
+
+       if (retval)
+               vm_tf->tf_rip += 3;
+
+       return retval;
+}
+
 static bool handle_io(struct guest_thread *gth)
 {
-       return io(gth);
+       struct vm_trapframe *vm_tf = gth_to_vmtf(gth);
+       int ret = io(gth);
+
+       if (ret < 0)
+               return FALSE;
+       if (ret == VM_PAGE_FAULT) {
+               /* We were unable to translate RIP due to an ept fault */
+               vm_tf->tf_trap_inject = VM_TRAP_VALID
+                                     | VM_TRAP_ERROR_CODE
+                                     | VM_TRAP_HARDWARE
+                                     | HW_TRAP_PAGE_FAULT;
+       }
+       return TRUE;
 }
 
 static bool handle_msr(struct guest_thread *gth)
 {
        struct vm_trapframe *vm_tf = gth_to_vmtf(gth);
 
-       /* TODO: consider pushing the gth into msrio */
        if (msrio(gth, gth_to_gpci(gth), vm_tf->tf_exit_reason)) {
                /* Use event injection through vmctl to send a general protection fault
                 * vmctl.interrupt gets written to the VM-Entry Interruption-Information
@@ -167,6 +291,8 @@ static bool handle_halt(struct guest_thread *gth)
 {
        struct vm_trapframe *vm_tf = gth_to_vmtf(gth);
 
+       if (gth->halt_exit)
+               return FALSE;
        /* It's possible the guest disabled IRQs and halted, perhaps waiting on an
         * NMI or something.  If we need to support that, we can change this.  */
        sleep_til_irq(gth);
@@ -194,6 +320,8 @@ bool handle_vmexit(struct guest_thread *gth)
        struct vm_trapframe *vm_tf = gth_to_vmtf(gth);
 
        switch (vm_tf->tf_exit_reason) {
+       case EXIT_REASON_CPUID:
+               return handle_cpuid(gth);
        case EXIT_REASON_EPT_VIOLATION:
                return handle_ept_fault(gth);
        case EXIT_REASON_VMCALL:
@@ -214,7 +342,7 @@ bool handle_vmexit(struct guest_thread *gth)
                /* TODO: just ignore these? */
                return TRUE;
        default:
-               fprintf(stderr, "Don't know how to handle exit %d\n",
+               fprintf(stderr, "VMM library: don't know how to handle exit %d\n",
                        vm_tf->tf_exit_reason);
                fprintf(stderr, "RIP %p, shutdown 0x%x\n", vm_tf->tf_rip,
                        vm_tf->tf_exit_reason);