#include <asm/hvm/hvm.h>
#include <asm/hvm/io.h>
#include <asm/hvm/support.h>
+#include <asm/hvm/vlapic.h>
#include <asm/hvm/svm/svm.h>
#include <asm/hvm/svm/intr.h>
#include <xen/event.h>
return 0;
}
+
+static void update_cr8_intercept(
+ struct vcpu *v, int intr_window_enabled)
+{
+ struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
+ struct vlapic *vlapic = vcpu_vlapic(v);
+ int max_irr;
+
+ vmcb->cr_intercepts &= ~CR_INTERCEPT_CR8_WRITE;
+
+ /*
+ * If ExtInts are masked then that dominates the TPR --- the 'interrupt
+ * window' has already been enabled in this case.
+ */
+ if ( intr_window_enabled )
+ return;
+
+ /* Is there an interrupt pending at the LAPIC? Nothing to do if not. */
+ if ( !vlapic_enabled(vlapic) ||
+ ((max_irr = vlapic_find_highest_irr(vlapic)) == -1) )
+ return;
+
+ /* Highest-priority pending interrupt is masked by the TPR? */
+ if ( (vmcb->vintr.fields.tpr & 0xf) >= (max_irr >> 4) )
+ vmcb->cr_intercepts |= CR_INTERCEPT_CR8_WRITE;
+}
asmlinkage void svm_intr_assist(void)
{
struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
int intr_type = APIC_DM_EXTINT;
int intr_vector = -1;
+ int intr_window_enabled = 0;
/*
* Previous Interrupt delivery caused this intercept?
vmcb->exitintinfo.bytes = 0;
HVMTRACE_1D(REINJ_VIRQ, v, intr_vector);
svm_inject_extint(v, intr_vector);
- return;
+ goto out;
}
/*
* external physical interrupt was pending when we executed VMRUN.
*/
if ( vmcb->vintr.fields.irq )
- return;
+ goto out;
/* Crank the handle on interrupt state and check for new interrrupts. */
pt_update_irq(v);
hvm_set_callback_irq_level();
if ( !cpu_has_pending_irq(v) )
- return;
+ goto out;
/*
* If the guest can't take an interrupt right now, create a 'fake'
vmcb->general1_intercepts |= GENERAL1_INTERCEPT_VINTR;
HVMTRACE_2D(INJ_VIRQ, v, 0x0, /*fake=*/ 1);
svm_inject_extint(v, 0x0); /* actual vector doesn't matter */
- return;
+ intr_window_enabled = 1;
+ goto out;
}
/* Okay, we can deliver the interrupt: grab it and update PIC state. */
svm_inject_extint(v, intr_vector);
pt_intr_post(v, intr_vector, intr_type);
+
+ out:
+ update_cr8_intercept(v, intr_window_enabled);
}
/*
struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
int inst_len, rc;
+ /*
+ * Before doing anything else, we need to sync up the VLAPIC's TPR with
+ * SVM's vTPR if CR8 writes are currently disabled. It's OK if the
+ * guest doesn't touch the CR8 (e.g. 32-bit Windows) because we update
+ * the vTPR on MMIO writes to the TPR
+ */
+ if ( !(vmcb->cr_intercepts & CR_INTERCEPT_CR8_WRITE) )
+ vlapic_set_reg(vcpu_vlapic(v), APIC_TASKPRI,
+ (vmcb->vintr.fields.tpr & 0x0F) << 4);
+
exit_reason = vmcb->exitcode;
HVMTRACE_2D(VMEXIT, v, vmcb->rip, exit_reason);
/* Intercept all debug-register writes. */
vmcb->dr_intercepts = ~0u;
- /* Intercept all control-register accesses, except to CR2. */
- vmcb->cr_intercepts = ~(CR_INTERCEPT_CR2_READ | CR_INTERCEPT_CR2_WRITE);
+ /*
+ * Intercept all control-register accesses except for CR2 reads/writes
+ * and CR8 reads (and actually CR8 writes, but that's a special case
+ * that's handled in svm/intr.c).
+ */
+ vmcb->cr_intercepts = ~(CR_INTERCEPT_CR2_READ |
+ CR_INTERCEPT_CR2_WRITE |
+ CR_INTERCEPT_CR8_READ);
/* I/O and MSR permission bitmaps. */
arch_svm->msrpm = alloc_xenheap_pages(get_order_from_bytes(MSRPM_SIZE));