vm exit handler for xsetbv
[akaros.git] / kern / arch / x86 / trap.c
index 6cb72cb..7bde8b0 100644 (file)
@@ -367,13 +367,13 @@ static void trap_dispatch(struct hw_trapframe *hw_tf)
                         * the same).  See set_current_ctx() for more info. */
                        if (!in_kernel(hw_tf))
                                hw_tf = &pcpui->cur_ctx->tf.hw_tf;
-                       printd("bad opcode, eip: %p, next 3 bytes: %x %x %x\n", ip, 
-                              *(uint8_t*)(ip + 0), 
-                              *(uint8_t*)(ip + 1), 
-                              *(uint8_t*)(ip + 2)); 
+                       printd("bad opcode, eip: %p, next 3 bytes: %x %x %x\n", ip,
+                              *(uint8_t*)(ip + 0),
+                              *(uint8_t*)(ip + 1),
+                              *(uint8_t*)(ip + 2));
                        /* rdtscp: 0f 01 f9 */
-                       if (*(uint8_t*)(ip + 0) == 0x0f, 
-                           *(uint8_t*)(ip + 1) == 0x01, 
+                       if (*(uint8_t*)(ip + 0) == 0x0f,
+                           *(uint8_t*)(ip + 1) == 0x01,
                            *(uint8_t*)(ip + 2) == 0xf9) {
                                x86_fake_rdtscp(hw_tf);
                                pcpui->__lock_checking_enabled++;       /* for print debugging */
@@ -777,6 +777,37 @@ bool handle_vmexit_extirq(struct vm_trapframe *tf)
        return TRUE;
 }
 
+static bool handle_vmexit_xsetbv(struct vm_trapframe *tf)
+{
+       // The VM's requested-feature bitmap is represented by edx:eax
+       uint64_t vm_rfbm = (tf->tf_rdx << 32) | tf->tf_rax;
+
+       // If the VM tries to set xcr0 to a superset
+       // of Akaros's default value, kill the VM.
+
+       // Bit in vm_rfbm and x86_default_xcr0:        Ok. Requested and allowed.
+       // Bit in vm_rfbm but not x86_default_xcr0:    Bad! Requested, not allowed.
+       // Bit not in vm_rfbm but in x86_default_xcr0: Ok. Not requested.
+
+       // vm_rfbm & (~x86_default_xcr0) is nonzero if any bits
+       // are set in vm_rfbm but not x86_default_xcr0
+
+       if (vm_rfbm & (~x86_default_xcr0))
+               return FALSE;
+
+
+       // If attempting to use vm_rfbm for xsetbv
+       // causes a fault, we reflect to the VMM.
+       if (safe_lxcr0(vm_rfbm))
+               return FALSE;
+
+
+       // If no fault, advance the instruction pointer
+       // and return TRUE to make the VM resume.
+       tf->tf_rip += 3; // XSETBV is a 3-byte instruction
+       return TRUE;
+}
+
 static void vmexit_dispatch(struct vm_trapframe *tf)
 {
        bool handled = FALSE;
@@ -814,6 +845,9 @@ static void vmexit_dispatch(struct vm_trapframe *tf)
        case EXIT_REASON_EXTERNAL_INTERRUPT:
                handled = handle_vmexit_extirq(tf);
                break;
+       case EXIT_REASON_XSETBV:
+               handled = handle_vmexit_xsetbv(tf);
+               break;
        default:
                printd("Unhandled vmexit: reason 0x%x, exit qualification 0x%x\n",
                       tf->tf_exit_reason, tf->tf_exit_qual);