diff options
author | Mike Larkin <mlarkin@cvs.openbsd.org> | 2016-09-01 14:45:37 +0000 |
---|---|---|
committer | Mike Larkin <mlarkin@cvs.openbsd.org> | 2016-09-01 14:45:37 +0000 |
commit | f098197be7e2ae763aa7826b39512aa1751bde39 (patch) | |
tree | 3ea043c375d11a779cc98579b64a99dde4049e9c | |
parent | ddd24481f4f57b10829698508a474390983b7d04 (diff) |
Remove the clock hack, and properly handle interrupts generated from vmd(8)'s
forthcoming emulated interrupt controller.
ok stefan
-rw-r--r-- | sys/arch/amd64/amd64/vmm.c | 202 | ||||
-rw-r--r-- | sys/arch/amd64/include/vmmvar.h | 7 |
2 files changed, 123 insertions, 86 deletions
diff --git a/sys/arch/amd64/amd64/vmm.c b/sys/arch/amd64/amd64/vmm.c index 082547524ae..59220baa538 100644 --- a/sys/arch/amd64/amd64/vmm.c +++ b/sys/arch/amd64/amd64/vmm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vmm.c,v 1.71 2016/07/23 07:25:29 mlarkin Exp $ */ +/* $OpenBSD: vmm.c,v 1.72 2016/09/01 14:45:36 mlarkin Exp $ */ /* * Copyright (c) 2014 Mike Larkin <mlarkin@openbsd.org> * @@ -40,7 +40,7 @@ #include <dev/isa/isareg.h> -#ifdef VMM_DEBUG +#ifdef VMM_DEBUG int vmm_debug = 0; #define DPRINTF(x...) do { if (vmm_debug) printf(x); } while(0) #else @@ -119,8 +119,8 @@ int vcpu_init(struct vcpu *); int vcpu_init_vmx(struct vcpu *); int vcpu_init_svm(struct vcpu *); int vcpu_must_stop(struct vcpu *); -int vcpu_run_vmx(struct vcpu *, uint8_t, int16_t *); -int vcpu_run_svm(struct vcpu *, uint8_t); +int vcpu_run_vmx(struct vcpu *, struct vm_run_params *); +int vcpu_run_svm(struct vcpu *, struct vm_run_params *); void vcpu_deinit(struct vcpu *); void vcpu_deinit_vmx(struct vcpu *); void vcpu_deinit_svm(struct vcpu *); @@ -198,10 +198,6 @@ struct vmm_softc *vmm_softc; extern vaddr_t idt_vaddr; extern struct gate_descriptor *idt; -/* XXX Temporary hack for the PIT clock */ -#define CLOCK_BIAS 8192 -uint64_t vmmclk = 0; - /* Constants used in "CR access exit" */ #define CR_WRITE 0 #define CR_READ 1 @@ -481,7 +477,7 @@ vm_resetcpu(struct vm_resetcpu_params *vrp) * vm_intr_pending * * IOCTL handler routine for VMM_IOC_INTR messages, sent from vmd when an - * interrupt is pending and needs acknowledgment. + * interrupt is pending and needs acknowledgment * * Parameters: * vip: Describes the vm/vcpu for which the interrupt is pending @@ -535,9 +531,9 @@ vm_intr_pending(struct vm_intr_params *vip) * simply re-enter the guest. This "fast notification" is done only * as an optimization. */ - if (vcpu->vc_state == VCPU_STATE_RUNNING) { + if (vcpu->vc_state == VCPU_STATE_RUNNING && + vip->vip_intr == 1) x86_send_ipi(vcpu->vc_last_pcpu, X86_IPI_NOP); - } #endif /* MULTIPROCESSOR */ return (0); @@ -1395,15 +1391,6 @@ vcpu_reset_regs_vmx(struct vcpu *vcpu, struct vcpu_init_state *vis) goto exit; } - /* - * XXX - - * vg_rip gets special treatment here since we will rewrite - * it just before vmx_enter_guest, so it needs to match. - * we could just set vg_rip here and be done with (no vmwrite - * here) but that would require us to have proper resume - * handling (resume=1) in the exit handler, so for now we - * will just end up doing an extra vmwrite here. - */ vcpu->vc_gueststate.vg_rip = vis->vis_rip; if (vmwrite(VMCS_GUEST_IA32_RIP, vis->vis_rip)) { ret = EINVAL; @@ -2574,10 +2561,10 @@ vm_run(struct vm_run_params *vrp) /* Run the VCPU specified in vrp */ if (vcpu->vc_virt_mode == VMM_MODE_VMX || vcpu->vc_virt_mode == VMM_MODE_EPT) { - ret = vcpu_run_vmx(vcpu, vrp->vrp_continue, &vrp->vrp_injint); + ret = vcpu_run_vmx(vcpu, vrp); } else if (vcpu->vc_virt_mode == VMM_MODE_SVM || vcpu->vc_virt_mode == VMM_MODE_RVI) { - ret = vcpu_run_svm(vcpu, vrp->vrp_continue); + ret = vcpu_run_svm(vcpu, vrp); } /* @@ -2595,6 +2582,7 @@ vm_run(struct vm_run_params *vrp) } else if (ret == EAGAIN) { /* If we are exiting, populate exit data so vmd can help. */ vrp->vrp_exit_reason = vcpu->vc_gueststate.vg_exit_reason; + vrp->vrp_irqready = vcpu->vc_irqready; vcpu->vc_state = VCPU_STATE_STOPPED; if (copyout(&vcpu->vc_exit, vrp->vrp_exit, @@ -2640,11 +2628,7 @@ vcpu_must_stop(struct vcpu *vcpu) * * Parameters: * vcpu: The VCPU to run - * from_exit: 1 if returning directly from an exit to vmd during the - * previous run, or 0 if we exited last time without needing to - * exit to vmd. - * injint: Interrupt that should be injected during this run, or -1 if - * no interrupt should be injected. + * vrp: run parameters * * Return values: * 0: The run loop exited and no help is needed from vmd @@ -2652,7 +2636,7 @@ vcpu_must_stop(struct vcpu *vcpu) * EINVAL: an error occured */ int -vcpu_run_vmx(struct vcpu *vcpu, uint8_t from_exit, int16_t *injint) +vcpu_run_vmx(struct vcpu *vcpu, struct vm_run_params *vrp) { int ret = 0, resume, locked, exitinfo; struct region_descriptor gdt; @@ -2660,9 +2644,13 @@ vcpu_run_vmx(struct vcpu *vcpu, uint8_t from_exit, int16_t *injint) uint64_t exit_reason, cr3, vmcs_ptr; struct schedstate_percpu *spc; struct vmx_invvpid_descriptor vid; - uint64_t rflags, eii; + uint64_t eii, procbased; + uint8_t from_exit; + uint16_t irq; resume = 0; + from_exit = vrp->vrp_continue; + irq = vrp->vrp_irq; while (ret == 0) { if (!resume) { @@ -2709,7 +2697,9 @@ vcpu_run_vmx(struct vcpu *vcpu, uint8_t from_exit, int16_t *injint) /* * If we are returning from userspace (vmd) because we exited - * last time, fix up any needed vcpu state first. + * last time, fix up any needed vcpu state first. Which state + * needs to be fixed up depends on what vmd populated in the + * exit data structure. */ if (from_exit) { from_exit = 0; @@ -2720,6 +2710,8 @@ vcpu_run_vmx(struct vcpu *vcpu, uint8_t from_exit, int16_t *injint) break; case VMX_EXIT_HLT: break; + case VMX_EXIT_INT_WINDOW: + break; case VMX_EXIT_TRIPLE_FAULT: DPRINTF("%s: vm %d vcpu %d triple fault\n", __func__, vcpu->vc_parent->vm_id, @@ -2753,65 +2745,76 @@ vcpu_run_vmx(struct vcpu *vcpu, uint8_t from_exit, int16_t *injint) } } - /* - * XXX - clock hack. We don't track host clocks while not - * running inside a VM, and thus we lose many clocks while - * the OS is running other processes. For now, approximate - * when a clock should be injected by injecting one clock - * per CLOCK_BIAS exits. - * - * This should be changed to track host clocks to know if - * a clock tick was missed, and "catch up" clock interrupt - * injections later as needed. - * - * Note that checking injint here and not injecting the - * clock interrupt if injint is set also violates interrupt - * priority, until this hack is fixed. - */ - vmmclk++; - eii = 0xFFFFFFFFFFFFFFFFULL; - - if (vmmclk % CLOCK_BIAS == 0) - eii = 0x20; + /* Handle vmd(8) injected interrupts */ + /* XXX - 0x20 should be changed to PIC's vector base */ - if (*injint != -1) - eii = *injint + 0x20; + /* Is there an interrupt pending injection? */ + if (irq != 0xFFFF) { + if (!vcpu->vc_irqready) { + printf("vcpu_run_vmx: error - irq injected" + " while not ready\n"); + ret = EINVAL; + break; + } - if (eii != 0xFFFFFFFFFFFFFFFFULL) { - if (vmread(VMCS_GUEST_IA32_RFLAGS, &rflags)) { - printf("intr: can't read guest rflags\n"); - rflags = 0; + eii = (irq & 0xFF) + 0x20; + eii |= (1ULL << 31); /* Valid */ + eii |= (0ULL << 8); /* Hardware Interrupt */ + if (vmwrite(VMCS_ENTRY_INTERRUPTION_INFO, eii)) { + printf("vcpu_run_vmx: can't vector " + "interrupt to guest\n"); + ret = EINVAL; + break; } - if (rflags & PSL_I) { - eii |= (1ULL << 31); /* Valid */ - eii |= (0ULL << 8); /* Hardware Interrupt */ - if (vmwrite(VMCS_ENTRY_INTERRUPTION_INFO, eii)) { - printf("intr: can't vector clock " - "interrupt to guest\n"); + irq = 0xFFFF; + } else if (!vcpu->vc_intr) { + /* + * Disable window exiting + */ + if (vmread(VMCS_PROCBASED_CTLS, &procbased)) { + printf("vcpu_run_vmx: can't read" + "procbased ctls on exit\n"); + ret = EINVAL; + break; + } else { + procbased &= ~IA32_VMX_INTERRUPT_WINDOW_EXITING; + if (vmwrite(VMCS_PROCBASED_CTLS, procbased)) { + printf("vcpu_run_vmx: can't write" + " procbased ctls on exit\n"); + ret = EINVAL; + break; } - if (*injint != -1) - *injint = -1; } } - /* XXX end clock hack */ - /* Invalidate old TLB mappings */ vid.vid_vpid = vcpu->vc_parent->vm_id; vid.vid_addr = 0; invvpid(IA32_VMX_INVVPID_SINGLE_CTX_GLB, &vid); - /* Start / resume the VM / VCPU */ + /* Start / resume the VCPU */ KERNEL_ASSERT_LOCKED(); KERNEL_UNLOCK(); ret = vmx_enter_guest(&vcpu->vc_control_pa, &vcpu->vc_gueststate, resume); exit_reason = VM_EXIT_NONE; - if (ret == 0) + if (ret == 0) { + /* + * ret == 0 implies we entered the guest, and later + * exited for some valid reason + */ exitinfo = vmx_get_exit_info( &vcpu->vc_gueststate.vg_rip, &exit_reason); + if (vmread(VMCS_GUEST_IA32_RFLAGS, + &vcpu->vc_gueststate.vg_rflags)) { + printf("vcpu_run_vmx: can't read guest rflags" + " during exit\n"); + ret = EINVAL; + break; + } + } if (ret || exitinfo != VMX_EXIT_INFO_COMPLETE || exit_reason != VMX_EXIT_EXTINT) { @@ -2861,6 +2864,32 @@ vcpu_run_vmx(struct vcpu *vcpu, uint8_t from_exit, int16_t *injint) if (!locked) KERNEL_LOCK(); + if (vcpu->vc_gueststate.vg_rflags & PSL_I) + vcpu->vc_irqready = 1; + else + vcpu->vc_irqready = 0; + + /* + * If not ready for interrupts, but interrupts pending, + * enable interrupt window exiting. + */ + if (vcpu->vc_irqready == 0 && vcpu->vc_intr) { + if (vmread(VMCS_PROCBASED_CTLS, &procbased)) { + printf("vcpu_run_vmx: can't read" + " procbased ctls on intwin exit\n"); + ret = EINVAL; + break; + } + + procbased |= IA32_VMX_INTERRUPT_WINDOW_EXITING; + if (vmwrite(VMCS_PROCBASED_CTLS, procbased)) { + printf("vcpu_run_vmx: can't write" + " procbased ctls on intwin exit\n"); + ret = EINVAL; + break; + } + } + /* Exit to vmd if we are terminating or failed enter */ if (ret || vcpu_must_stop(vcpu)) break; @@ -2887,17 +2916,16 @@ vcpu_run_vmx(struct vcpu *vcpu, uint8_t from_exit, int16_t *injint) ret = EINVAL; } else { printf("vcpu_run_vmx: failed launch for unknown " - "reason\n"); + "reason %d\n", ret); ret = EINVAL; } - } /* * We are heading back to userspace (vmd), either because we need help - * handling an exit, or we failed in some way to enter the guest. - * Clear any current VMCS pointer as we may end up coming back on - * a different CPU. + * handling an exit, a guest interrupt is pending, or we failed in some + * way to enter the guest. Clear any current VMCS pointer as we may end + * up coming back on a different CPU. */ if (!vmptrst(&vmcs_ptr)) { if (vmcs_ptr != 0xFFFFFFFFFFFFFFFFULL) @@ -2935,7 +2963,6 @@ vmx_handle_intr(struct vcpu *vcpu) idte=&idt[vec]; handler = idte->gd_looffset + ((uint64_t)idte->gd_hioffset << 16); vmm_dispatch_intr(handler); - } /* @@ -2986,13 +3013,25 @@ vmx_get_exit_info(uint64_t *rip, uint64_t *exit_reason) int vmx_handle_exit(struct vcpu *vcpu) { - uint64_t exit_reason; + uint64_t exit_reason, rflags; int update_rip, ret = 0; update_rip = 0; exit_reason = vcpu->vc_gueststate.vg_exit_reason; + rflags = vcpu->vc_gueststate.vg_rflags; switch (exit_reason) { + case VMX_EXIT_INT_WINDOW: + if (!(rflags & PSL_I)) { + DPRINTF("vmx_handle_exit: impossible interrupt window" + " exit config\n"); + ret = EINVAL; + break; + } + + ret = EAGAIN; + update_rip = 0; + break; case VMX_EXIT_EPT_VIOLATION: ret = vmx_handle_np_fault(vcpu); break; @@ -3258,21 +3297,16 @@ vmx_handle_inout(struct vcpu *vcpu) * XXX handle not eax target */ switch (vcpu->vc_exit.vei.vei_port) { + case IO_ICU1 ... IO_ICU1 + 1: case 0x40 ... 0x43: + case IO_RTC ... IO_RTC + 1: + case IO_ICU2 ... IO_ICU2 + 1: case 0x3f8 ... 0x3ff: case 0xcf8: case 0xcfc: case VMM_PCI_IO_BAR_BASE ... VMM_PCI_IO_BAR_END: ret = EAGAIN; break; - case IO_RTC ... IO_RTC + 1: - /* We can directly read the RTC on behalf of the guest */ - if (vcpu->vc_exit.vei.vei_dir == 1) { - vcpu->vc_gueststate.vg_rax = - inb(vcpu->vc_exit.vei.vei_port); - } - ret = 0; - break; default: /* Read from unsupported ports returns FFs */ if (vcpu->vc_exit.vei.vei_dir == 1) @@ -3583,7 +3617,7 @@ vmx_handle_cpuid(struct vcpu *vcpu) * VMM main loop used to run a VCPU. */ int -vcpu_run_svm(struct vcpu *vcpu, uint8_t from_exit) +vcpu_run_svm(struct vcpu *vcpu, struct vm_run_params *vrp) { /* XXX removed due to rot */ return (0); diff --git a/sys/arch/amd64/include/vmmvar.h b/sys/arch/amd64/include/vmmvar.h index 1ed1f4dafaf..c4c8e3a0bdc 100644 --- a/sys/arch/amd64/include/vmmvar.h +++ b/sys/arch/amd64/include/vmmvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: vmmvar.h,v 1.15 2016/06/10 16:37:16 stefan Exp $ */ +/* $OpenBSD: vmmvar.h,v 1.16 2016/09/01 14:45:36 mlarkin Exp $ */ /* * Copyright (c) 2014 Mike Larkin <mlarkin@openbsd.org> * @@ -204,13 +204,14 @@ struct vm_run_params { uint32_t vrp_vm_id; uint32_t vrp_vcpu_id; uint8_t vrp_continue; /* Continuing from an exit */ - int16_t vrp_injint; /* Injected interrupt vector */ + uint16_t vrp_irq; /* IRQ to inject */ /* Input/output parameter to VMM_IOC_RUN */ union vm_exit *vrp_exit; /* updated exit data */ /* Output parameter from VMM_IOC_RUN */ uint16_t vrp_exit_reason; /* exit reason */ + uint8_t vrp_irqready; /* ready for IRQ on entry */ }; struct vm_info_result { @@ -340,6 +341,7 @@ struct vmx_gueststate uint64_t vg_cr2; /* 0x78 */ uint64_t vg_rip; /* 0x80 */ uint32_t vg_exit_reason; /* 0x88 */ + uint64_t vg_rflags; /* 0x90 */ }; /* @@ -375,6 +377,7 @@ struct vcpu { union vm_exit vc_exit; uint16_t vc_intr; + uint8_t vc_irqready; /* VMX only */ uint64_t vc_vmx_basic; |