vmm(4): send all port io emulation to userland
authordv <dv@openbsd.org>
Thu, 1 Sep 2022 22:01:40 +0000 (22:01 +0000)
committerdv <dv@openbsd.org>
Thu, 1 Sep 2022 22:01:40 +0000 (22:01 +0000)
Simplify things by sending any io exits from IN/OUT instructions
to userland instead of trying to emulate anything in the kernel.
vmm was sending most pertinent exits to vmd anyways, so this
functionally changes little.

An added benefit is this solves an issue reported by tb@ where i386
OpenBSD guests would probe for a pc keyboard repeatedly and cause
excessive vm exits. (The emulation in vmm was not properly handling
these port reads.)

While here, make the assignment of the VEI_DIR_{IN,OUT} enum values
not assume the underlying integer the compiler may assign.

ok mlarkin@

sys/arch/amd64/amd64/vmm.c
sys/arch/amd64/include/vmmvar.h
usr.sbin/vmd/i8253.h
usr.sbin/vmd/i8259.h
usr.sbin/vmd/vm.c

index 9ee961c..a51c792 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vmm.c,v 1.320 2022/08/30 17:09:21 dv Exp $    */
+/*     $OpenBSD: vmm.c,v 1.321 2022/09/01 22:01:40 dv Exp $    */
 /*
  * Copyright (c) 2014 Mike Larkin <mlarkin@openbsd.org>
  *
@@ -5408,7 +5408,8 @@ svm_handle_exit(struct vcpu *vcpu)
                update_rip = 1;
                break;
        case SVM_VMEXIT_IOIO:
-               ret = svm_handle_inout(vcpu);
+               if (svm_handle_inout(vcpu) == 0)
+                       ret = EAGAIN;
                update_rip = 1;
                break;
        case SVM_VMEXIT_HLT:
@@ -5492,7 +5493,8 @@ vmx_handle_exit(struct vcpu *vcpu)
                update_rip = 1;
                break;
        case VMX_EXIT_IO:
-               ret = vmx_handle_inout(vcpu);
+               if (vmx_handle_inout(vcpu) == 0)
+                       ret = EAGAIN;
                update_rip = 1;
                break;
        case VMX_EXIT_EXTINT:
@@ -6034,22 +6036,17 @@ vmm_get_guest_cpu_mode(struct vcpu *vcpu)
  *
  * Exit handler for IN/OUT instructions.
  *
- * The vmm can handle certain IN/OUTS without exiting to vmd, but most of these
- * will be passed to vmd for completion.
- *
  * Parameters:
  *  vcpu: The VCPU where the IN/OUT instruction occurred
  *
  * Return values:
  *  0: if successful
  *  EINVAL: an invalid IN/OUT instruction was encountered
- *  EAGAIN: return to vmd - more processing needed in userland
  */
 int
 svm_handle_inout(struct vcpu *vcpu)
 {
        uint64_t insn_length, exit_qual;
-       int ret;
        struct vmcb *vmcb = (struct vmcb *)vcpu->vc_control_va;
 
        insn_length = vmcb->v_exitinfo2 - vmcb->v_rip;
@@ -6062,7 +6059,10 @@ svm_handle_inout(struct vcpu *vcpu)
        exit_qual = vmcb->v_exitinfo1;
 
        /* Bit 0 - direction */
-       vcpu->vc_exit.vei.vei_dir = (exit_qual & 0x1);
+       if (exit_qual & 0x1)
+               vcpu->vc_exit.vei.vei_dir = VEI_DIR_IN;
+       else
+               vcpu->vc_exit.vei.vei_dir = VEI_DIR_OUT;
        /* Bit 2 - string instruction? */
        vcpu->vc_exit.vei.vei_string = (exit_qual & 0x4) >> 2;
        /* Bit 3 - REP prefix? */
@@ -6083,52 +6083,7 @@ svm_handle_inout(struct vcpu *vcpu)
 
        vcpu->vc_gueststate.vg_rip += insn_length;
 
-       /*
-        * The following ports usually belong to devices owned by vmd.
-        * Return EAGAIN to signal help needed from userspace (vmd).
-        * Return 0 to indicate we don't care about this port.
-        *
-        * XXX something better than a hardcoded list here, maybe
-        * configure via vmd via the device list in vm create params?
-        */
-       switch (vcpu->vc_exit.vei.vei_port) {
-       case IO_ICU1 ... IO_ICU1 + 1:
-       case 0x40 ... 0x43:
-       case PCKBC_AUX:
-       case IO_RTC ... IO_RTC + 1:
-       case IO_ICU2 ... IO_ICU2 + 1:
-       case 0x3f8 ... 0x3ff:
-       case ELCR0 ... ELCR1:
-       case 0x500 ... 0x511:
-       case 0x514:
-       case 0x518:
-       case 0xcf8:
-       case 0xcfc ... 0xcff:
-       case VMM_PCI_IO_BAR_BASE ... VMM_PCI_IO_BAR_END:
-               ret = EAGAIN;
-               break;
-       default:
-               /* Read from unsupported ports returns FFs */
-               if (vcpu->vc_exit.vei.vei_dir == 1) {
-                       switch(vcpu->vc_exit.vei.vei_size) {
-                       case 1:
-                               vcpu->vc_gueststate.vg_rax |= 0xFF;
-                               vmcb->v_rax |= 0xFF;
-                               break;
-                       case 2:
-                               vcpu->vc_gueststate.vg_rax |= 0xFFFF;
-                               vmcb->v_rax |= 0xFFFF;
-                               break;
-                       case 4:
-                               vcpu->vc_gueststate.vg_rax |= 0xFFFFFFFF;
-                               vmcb->v_rax |= 0xFFFFFFFF;
-                               break;
-                       }
-               }
-               ret = 0;
-       }
-
-       return (ret);
+       return (0);
 }
 
 /*
@@ -6136,14 +6091,17 @@ svm_handle_inout(struct vcpu *vcpu)
  *
  * Exit handler for IN/OUT instructions.
  *
- * The vmm can handle certain IN/OUTS without exiting to vmd, but most of these
- * will be passed to vmd for completion.
+ * Parameters:
+ *  vcpu: The VCPU where the IN/OUT instruction occurred
+ *
+ * Return values:
+ *  0: if successful
+ *  EINVAL: invalid IN/OUT instruction or vmread failures occurred
  */
 int
 vmx_handle_inout(struct vcpu *vcpu)
 {
        uint64_t insn_length, exit_qual;
-       int ret;
 
        if (vmread(VMCS_INSTRUCTION_LENGTH, &insn_length)) {
                printf("%s: can't obtain instruction length\n", __func__);
@@ -6164,7 +6122,10 @@ vmx_handle_inout(struct vcpu *vcpu)
        /* Bits 0:2 - size of exit */
        vcpu->vc_exit.vei.vei_size = (exit_qual & 0x7) + 1;
        /* Bit 3 - direction */
-       vcpu->vc_exit.vei.vei_dir = (exit_qual & 0x8) >> 3;
+       if ((exit_qual & 0x8) >> 3)
+               vcpu->vc_exit.vei.vei_dir = VEI_DIR_IN;
+       else
+               vcpu->vc_exit.vei.vei_dir = VEI_DIR_OUT;
        /* Bit 4 - string instruction? */
        vcpu->vc_exit.vei.vei_string = (exit_qual & 0x10) >> 4;
        /* Bit 5 - REP prefix? */
@@ -6178,44 +6139,7 @@ vmx_handle_inout(struct vcpu *vcpu)
 
        vcpu->vc_gueststate.vg_rip += insn_length;
 
-       /*
-        * The following ports usually belong to devices owned by vmd.
-        * Return EAGAIN to signal help needed from userspace (vmd).
-        * Return 0 to indicate we don't care about this port.
-        *
-        * XXX something better than a hardcoded list here, maybe
-        * configure via vmd via the device list in vm create params?
-        */
-       switch (vcpu->vc_exit.vei.vei_port) {
-       case IO_ICU1 ... IO_ICU1 + 1:
-       case 0x40 ... 0x43:
-       case PCKBC_AUX:
-       case IO_RTC ... IO_RTC + 1:
-       case IO_ICU2 ... IO_ICU2 + 1:
-       case 0x3f8 ... 0x3ff:
-       case ELCR0 ... ELCR1:
-       case 0x500 ... 0x511:
-       case 0x514:
-       case 0x518:
-       case 0xcf8:
-       case 0xcfc ... 0xcff:
-       case VMM_PCI_IO_BAR_BASE ... VMM_PCI_IO_BAR_END:
-               ret = EAGAIN;
-               break;
-       default:
-               /* Read from unsupported ports returns FFs */
-               if (vcpu->vc_exit.vei.vei_dir == VEI_DIR_IN) {
-                       if (vcpu->vc_exit.vei.vei_size == 4)
-                               vcpu->vc_gueststate.vg_rax |= 0xFFFFFFFF;
-                       else if (vcpu->vc_exit.vei.vei_size == 2)
-                               vcpu->vc_gueststate.vg_rax |= 0xFFFF;
-                       else if (vcpu->vc_exit.vei.vei_size == 1)
-                               vcpu->vc_gueststate.vg_rax |= 0xFF;
-               }
-               ret = 0;
-       }
-
-       return (ret);
+       return (0);
 }
 
 /*
index a7f69eb..6a5296d 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vmmvar.h,v 1.80 2022/08/30 17:09:21 dv Exp $  */
+/*     $OpenBSD: vmmvar.h,v 1.81 2022/09/01 22:01:40 dv Exp $  */
 /*
  * Copyright (c) 2014 Mike Larkin <mlarkin@openbsd.org>
  *
@@ -339,13 +339,6 @@ enum {
        VMM_CPU_MODE_UNKNOWN,
 };
 
-/*
- * Port definitions not found elsewhere
- */
-#define PCKBC_AUX      0x61
-#define ELCR0          0x4D0
-#define ELCR1          0x4D1
-
 /*
  * vm exit data
  *  vm_exit_inout              : describes an IN/OUT exit
index e72fbe4..569c1a9 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: i8253.h,v 1.9 2018/04/26 17:10:10 mlarkin Exp $ */
+/* $OpenBSD: i8253.h,v 1.10 2022/09/01 22:01:40 dv Exp $ */
 /*
  * Copyright (c) 2016 Mike Larkin <mlarkin@openbsd.org>
  *
@@ -26,6 +26,7 @@
 #define TIMER_RB_C0    0x2     /* read back channel 0 */
 #define TIMER_RB_C1    0x4     /* read back channel 1 */
 #define TIMER_RB_C2    0x8     /* read back channel 1 */
+#define PCKBC_AUX      0x61    /* PC keyboard aux port for i8253 misc access */
 
 /* i8253 registers */
 struct i8253_channel {
index ce8a22d..f4b7598 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: i8259.h,v 1.4 2018/04/27 12:15:10 mlarkin Exp $ */
+/* $OpenBSD: i8259.h,v 1.5 2022/09/01 22:01:40 dv Exp $ */
 /*
  * Copyright (c) 2016 Mike Larkin <mlarkin@openbsd.org>
  *
 
 #include <machine/vmmvar.h>
 
-#define MASTER 0
-#define SLAVE 1
+#define MASTER                 0
+#define SLAVE                  1
+
+#define ELCR0                  0x4D0
+#define ELCR1                  0x4D1
 
 #define ICW1_ICW4              (0x1 << 0)
 #define ICW1_SNGL              (0x1 << 1)
index 209814e..8efbdaa 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vm.c,v 1.72 2022/08/30 17:09:21 dv Exp $      */
+/*     $OpenBSD: vm.c,v 1.73 2022/09/01 22:01:40 dv Exp $      */
 
 /*
  * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
@@ -1636,7 +1636,7 @@ vcpu_exit_inout(struct vm_run_params *vrp)
        if (ioports_map[vei->vei.vei_port] != NULL)
                intr = ioports_map[vei->vei.vei_port](vrp);
        else if (vei->vei.vei_dir == VEI_DIR_IN)
-                       set_return_data(vei, 0xFFFFFFFF);
+               set_return_data(vei, 0xFFFFFFFF);
 
        if (intr != 0xFF)
                vcpu_assert_pic_irq(vrp->vrp_vm_id, vrp->vrp_vcpu_id, intr);