From 33de4f94699c48eab363d0762965d68f5f40c087 Mon Sep 17 00:00:00 2001 From: dv Date: Thu, 1 Sep 2022 22:01:40 +0000 Subject: [PATCH] vmm(4): send all port io emulation to userland 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 | 118 ++++++-------------------------- sys/arch/amd64/include/vmmvar.h | 9 +-- usr.sbin/vmd/i8253.h | 3 +- usr.sbin/vmd/i8259.h | 9 ++- usr.sbin/vmd/vm.c | 4 +- 5 files changed, 32 insertions(+), 111 deletions(-) diff --git a/sys/arch/amd64/amd64/vmm.c b/sys/arch/amd64/amd64/vmm.c index 9ee961cb3dc..a51c79272a9 100644 --- a/sys/arch/amd64/amd64/vmm.c +++ b/sys/arch/amd64/amd64/vmm.c @@ -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 * @@ -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); } /* diff --git a/sys/arch/amd64/include/vmmvar.h b/sys/arch/amd64/include/vmmvar.h index a7f69eb77f1..6a5296d08e4 100644 --- a/sys/arch/amd64/include/vmmvar.h +++ b/sys/arch/amd64/include/vmmvar.h @@ -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 * @@ -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 diff --git a/usr.sbin/vmd/i8253.h b/usr.sbin/vmd/i8253.h index e72fbe40bac..569c1a93a6f 100644 --- a/usr.sbin/vmd/i8253.h +++ b/usr.sbin/vmd/i8253.h @@ -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 * @@ -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 { diff --git a/usr.sbin/vmd/i8259.h b/usr.sbin/vmd/i8259.h index ce8a22d5c57..f4b7598692c 100644 --- a/usr.sbin/vmd/i8259.h +++ b/usr.sbin/vmd/i8259.h @@ -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 * @@ -19,8 +19,11 @@ #include -#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) diff --git a/usr.sbin/vmd/vm.c b/usr.sbin/vmd/vm.c index 209814e4bda..8efbdaadb32 100644 --- a/usr.sbin/vmd/vm.c +++ b/usr.sbin/vmd/vm.c @@ -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 @@ -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); -- 2.20.1