FreeBSD/sys/amd64/vmm/amd/svm.c
/*
 * Inject event to virtual cpu.
 */
Inject event to virtual cpu.
Inject event to virtual cpu .
static void
svm_inj_interrupts(struct svm_softc *sc, int vcpu, struct vlapic *vlapic)
{
	struct vmcb_ctrl *ctrl;
	struct vmcb_state *state;
	struct svm_vcpu *vcpustate;
	uint8_t v_tpr;
	int vector, need_intr_window, pending_apic_vector;
	state = svm_get_vmcb_state(sc, vcpu);
	ctrl  = svm_get_vmcb_ctrl(sc, vcpu);
	vcpustate = svm_get_vcpu(sc, vcpu);
	need_intr_window = 0;
	pending_apic_vector = 0;
	if (vcpustate->nextrip != state->rip) {
		ctrl->intr_shadow = 0;
		VCPU_CTR2(sc->vm, vcpu, "Guest interrupt blocking "
		    "cleared due to rip change: %#lx/%#lx",
		    vcpustate->nextrip, state->rip);
	}
	svm_inj_intinfo(sc, vcpu);
	if (vm_nmi_pending(sc->vm, vcpu)) {
		if (nmi_blocked(sc, vcpu)) {
			VCPU_CTR0(sc->vm, vcpu, "Cannot inject NMI due "
			    "to NMI-blocking");
		} else if (ctrl->intr_shadow) {
			VCPU_CTR0(sc->vm, vcpu, "Cannot inject NMI due to "
			    "interrupt shadow");
			need_intr_window = 1;
			goto done;
		} else if (ctrl->eventinj & VMCB_EVENTINJ_VALID) {
			VCPU_CTR1(sc->vm, vcpu, "Cannot inject NMI due to "
			    "eventinj %#lx", ctrl->eventinj);
			ipi_cpu(curcpu, IPI_AST);	
		} else {
			vm_nmi_clear(sc->vm, vcpu);
			svm_eventinject(sc, vcpu, VMCB_EVENTINJ_TYPE_NMI,
			    IDT_NMI, 0, false);
			enable_nmi_blocking(sc, vcpu);
			VCPU_CTR0(sc->vm, vcpu, "Injecting vNMI");
		}
	}
	if (!vm_extint_pending(sc->vm, vcpu)) {
		if (vlapic_pending_intr(vlapic, &vector)) {
			KASSERT(vector >= 16 && vector <= 255,
			    ("invalid vector %d from local APIC", vector));
			pending_apic_vector = vector;
		}
		goto done;
	}
	vatpic_pending_intr(sc->vm, &vector);
	KASSERT(vector >= 0 && vector <= 255, ("invalid vector %d from INTR",
	    vector));
	if ((state->rflags & PSL_I) == 0) {
		VCPU_CTR2(sc->vm, vcpu, "Cannot inject vector %d due to "
		    "rflags %#lx", vector, state->rflags);
		need_intr_window = 1;
		goto done;
	}
	if (ctrl->intr_shadow) {
		VCPU_CTR1(sc->vm, vcpu, "Cannot inject vector %d due to "
		    "interrupt shadow", vector);
		need_intr_window = 1;
		goto done;
	}
	if (ctrl->eventinj & VMCB_EVENTINJ_VALID) {
		VCPU_CTR2(sc->vm, vcpu, "Cannot inject vector %d due to "
		    "eventinj %#lx", vector, ctrl->eventinj);
		need_intr_window = 1;
		goto done;
	}
	svm_eventinject(sc, vcpu, VMCB_EVENTINJ_TYPE_INTR, vector, 0, false);
	vm_extint_clear(sc->vm, vcpu);
	vatpic_intr_accepted(sc->vm, vector);
	need_intr_window = 1;
done:
	v_tpr = vlapic_get_cr8(vlapic);
	KASSERT(v_tpr <= 15, ("invalid v_tpr %#x", v_tpr));
	if (ctrl->v_tpr != v_tpr) {
		VCPU_CTR2(sc->vm, vcpu, "VMCB V_TPR changed from %#x to %#x",
		    ctrl->v_tpr, v_tpr);
		ctrl->v_tpr = v_tpr;
		svm_set_dirty(sc, vcpu, VMCB_CACHE_TPR);
	}
	if (pending_apic_vector) {
		KASSERT(!need_intr_window, ("intr_window exiting impossible"));
		VCPU_CTR1(sc->vm, vcpu, "Injecting vector %d using V_IRQ",
		    pending_apic_vector);
		ctrl->v_irq = 1;
		ctrl->v_ign_tpr = 0;
		ctrl->v_intr_vector = pending_apic_vector;
		ctrl->v_intr_prio = pending_apic_vector >> 4;
		svm_set_dirty(sc, vcpu, VMCB_CACHE_TPR);
	} else if (need_intr_window) {
		KASSERT((ctrl->eventinj & VMCB_EVENTINJ_VALID) != 0 ||
		    (state->rflags & PSL_I) == 0 || ctrl->intr_shadow,
		    ("Bogus intr_window_exiting: eventinj (%#lx), "
		    "intr_shadow (%u), rflags (%#lx)",
		    ctrl->eventinj, ctrl->intr_shadow, state->rflags));
		enable_intr_window_exiting(sc, vcpu);
	} else {
		disable_intr_window_exiting(sc, vcpu);
	}
}
static void svm_inj_interrupts ( struct svm_softc * sc , int vcpu , struct vlapic * vlapic ) { struct vmcb_ctrl * ctrl ; struct vmcb_state * state ; struct svm_vcpu * vcpustate ; uint8_t v_tpr ; int vector , need_intr_window , pending_apic_vector ; state = svm_get_vmcb_state ( sc , vcpu ) ; ctrl = svm_get_vmcb_ctrl ( sc , vcpu ) ; vcpustate = svm_get_vcpu ( sc , vcpu ) ; need_intr_window = 0 ; pending_apic_vector = 0 ; if ( vcpustate -> nextrip != state -> rip ) { ctrl -> intr_shadow = 0 ; VCPU_CTR2 ( sc -> vm , vcpu , " " " " , vcpustate -> nextrip , state -> rip ) ; } svm_inj_intinfo ( sc , vcpu ) ; if ( vm_nmi_pending ( sc -> vm , vcpu ) ) { if ( nmi_blocked ( sc , vcpu ) ) { VCPU_CTR0 ( sc -> vm , vcpu , " " " " ) ; } else if ( ctrl -> intr_shadow ) { VCPU_CTR0 ( sc -> vm , vcpu , " " " " ) ; need_intr_window = 1 ; goto done ; } else if ( ctrl -> eventinj & VMCB_EVENTINJ_VALID ) { VCPU_CTR1 ( sc -> vm , vcpu , " " " " , ctrl -> eventinj ) ; ipi_cpu ( curcpu , IPI_AST ) ; } else { vm_nmi_clear ( sc -> vm , vcpu ) ; svm_eventinject ( sc , vcpu , VMCB_EVENTINJ_TYPE_NMI , IDT_NMI , 0 , false ) ; enable_nmi_blocking ( sc , vcpu ) ; VCPU_CTR0 ( sc -> vm , vcpu , " " ) ; } } if ( ! vm_extint_pending ( sc -> vm , vcpu ) ) { if ( vlapic_pending_intr ( vlapic , & vector ) ) { KASSERT ( vector >= 16 && vector <= 255 , ( " " , vector ) ) ; pending_apic_vector = vector ; } goto done ; } vatpic_pending_intr ( sc -> vm , & vector ) ; KASSERT ( vector >= 0 && vector <= 255 , ( " " , vector ) ) ; if ( ( state -> rflags & PSL_I ) == 0 ) { VCPU_CTR2 ( sc -> vm , vcpu , " " " " , vector , state -> rflags ) ; need_intr_window = 1 ; goto done ; } if ( ctrl -> intr_shadow ) { VCPU_CTR1 ( sc -> vm , vcpu , " " " " , vector ) ; need_intr_window = 1 ; goto done ; } if ( ctrl -> eventinj & VMCB_EVENTINJ_VALID ) { VCPU_CTR2 ( sc -> vm , vcpu , " " " " , vector , ctrl -> eventinj ) ; need_intr_window = 1 ; goto done ; } svm_eventinject ( sc , vcpu , VMCB_EVENTINJ_TYPE_INTR , vector , 0 , false ) ; vm_extint_clear ( sc -> vm , vcpu ) ; vatpic_intr_accepted ( sc -> vm , vector ) ; need_intr_window = 1 ; done : v_tpr = vlapic_get_cr8 ( vlapic ) ; KASSERT ( v_tpr <= 15 , ( " " , v_tpr ) ) ; if ( ctrl -> v_tpr != v_tpr ) { VCPU_CTR2 ( sc -> vm , vcpu , " " , ctrl -> v_tpr , v_tpr ) ; ctrl -> v_tpr = v_tpr ; svm_set_dirty ( sc , vcpu , VMCB_CACHE_TPR ) ; } if ( pending_apic_vector ) { KASSERT ( ! need_intr_window , ( " " ) ) ; VCPU_CTR1 ( sc -> vm , vcpu , " " , pending_apic_vector ) ; ctrl -> v_irq = 1 ; ctrl -> v_ign_tpr = 0 ; ctrl -> v_intr_vector = pending_apic_vector ; ctrl -> v_intr_prio = pending_apic_vector >> 4 ; svm_set_dirty ( sc , vcpu , VMCB_CACHE_TPR ) ; } else if ( need_intr_window ) { KASSERT ( ( ctrl -> eventinj & VMCB_EVENTINJ_VALID ) != 0 || ( state -> rflags & PSL_I ) == 0 || ctrl -> intr_shadow , ( " " " " , ctrl -> eventinj , ctrl -> intr_shadow , state -> rflags ) ) ; enable_intr_window_exiting ( sc , vcpu ) ; } else { disable_intr_window_exiting ( sc , vcpu ) ; } }
Inject event to virtual cpu.
Inject event to virtual cpu .
/*
	 * Inject pending events or exceptions for this vcpu.
	 *
	 * An event might be pending because the previous #VMEXIT happened
	 * during event delivery (i.e. ctrl->exitintinfo).
	 *
	 * An event might also be pending because an exception was injected
	 * by the hypervisor (e.g. #PF during instruction emulation).
	 */ /* NMI event has priority over interrupts. */ /*
			 * Can't inject another NMI if the guest has not
			 * yet executed an "iret" after the last NMI.
			 */ /*
			 * Can't inject an NMI if the vcpu is in an intr_shadow.
			 */ /*
			 * If there is already an exception/interrupt pending
			 * then defer the NMI until after that.
			 */ /*
			 * Use self-IPI to trigger a VM-exit as soon as
			 * possible after the event injection is completed.
			 *
			 * This works only if the external interrupt exiting
			 * is at a lower priority than the event injection.
			 *
			 * Although not explicitly specified in APMv2 the
			 * relative priorities were verified empirically.
			 */ /* XXX vmm_ipinum? */ /* Inject NMI, vector number is not used */ /* virtual NMI blocking is now in effect */ /*
		 * APIC interrupts are delivered using the V_IRQ offload.
		 *
		 * The primary benefit is that the hypervisor doesn't need to
		 * deal with the various conditions that inhibit interrupts.
		 * It also means that TPR changes via CR8 will be handled
		 * without any hypervisor involvement.
		 *
		 * Note that the APIC vector must remain pending in the vIRR
		 * until it is confirmed that it was delivered to the guest.
		 * This can be confirmed based on the value of V_IRQ at the
		 * next #VMEXIT (1 = pending, 0 = delivered).
		 *
		 * Also note that it is possible that another higher priority
		 * vector can become pending before this vector is delivered
		 * to the guest. This is alright because vcpu_notify_event()
		 * will send an IPI and force the vcpu to trap back into the
		 * hypervisor. The higher priority vector will be injected on
		 * the next VMRUN.
		 */ /* Ask the legacy pic for a vector to inject */ /*
	 * If the guest has disabled interrupts or is in an interrupt shadow
	 * then we cannot inject the pending interrupt.
	 */ /*
	 * Legacy PIC interrupts are delivered via the event injection
	 * mechanism.
	 */ /*
	 * Force a VM-exit as soon as the vcpu is ready to accept another
	 * interrupt. This is done because the PIC might have another vector
	 * that it wants to inject. Also, if the APIC has a pending interrupt
	 * that was preempted by the ExtInt then it allows us to inject the
	 * APIC vector as soon as possible.
	 */ /*
	 * The guest can modify the TPR by writing to %CR8. In guest mode
	 * the processor reflects this write to V_TPR without hypervisor
	 * intervention.
	 *
	 * The guest can also modify the TPR by writing to it via the memory
	 * mapped APIC page. In this case, the write will be emulated by the
	 * hypervisor. For this reason V_TPR must be updated before every
	 * VMRUN.
	 */ /*
		 * If an APIC vector is being injected then interrupt window
		 * exiting is not possible on this VMRUN.
		 */ /*
		 * We use V_IRQ in conjunction with the VINTR intercept to
		 * trap into the hypervisor as soon as a virtual interrupt
		 * can be delivered.
		 *
		 * Since injected events are not subject to intercept checks
		 * we need to ensure that the V_IRQ is not actually going to
		 * be delivered on VM entry. The KASSERT below enforces this.
		 */