put_page_and_type(page);
}
-void viridian_start_apic_assist(struct vcpu *v, int vector)
+void viridian_apic_assist_set(struct vcpu *v)
{
uint32_t *va = v->arch.hvm_vcpu.viridian.vp_assist.va;
if ( !va )
return;
- if ( vector < 0x10 )
- return;
-
/*
* If there is already an assist pending then something has gone
* wrong and the VM will most likely hang so force a crash now
* to make the problem clear.
*/
- if ( v->arch.hvm_vcpu.viridian.vp_assist.vector )
+ if ( v->arch.hvm_vcpu.viridian.vp_assist.pending )
domain_crash(v->domain);
- v->arch.hvm_vcpu.viridian.vp_assist.vector = vector;
+ v->arch.hvm_vcpu.viridian.vp_assist.pending = true;
*va |= 1u;
}
-int viridian_complete_apic_assist(struct vcpu *v)
+bool viridian_apic_assist_completed(struct vcpu *v)
{
uint32_t *va = v->arch.hvm_vcpu.viridian.vp_assist.va;
- int vector;
if ( !va )
- return 0;
+ return false;
- if ( *va & 1u )
- return 0; /* Interrupt not yet processed by the guest. */
-
- vector = v->arch.hvm_vcpu.viridian.vp_assist.vector;
- v->arch.hvm_vcpu.viridian.vp_assist.vector = 0;
+ if ( v->arch.hvm_vcpu.viridian.vp_assist.pending &&
+ !(*va & 1u) )
+ {
+ /* An EOI has been avoided */
+ v->arch.hvm_vcpu.viridian.vp_assist.pending = false;
+ return true;
+ }
- return vector;
+ return false;
}
-void viridian_abort_apic_assist(struct vcpu *v)
+void viridian_apic_assist_clear(struct vcpu *v)
{
uint32_t *va = v->arch.hvm_vcpu.viridian.vp_assist.va;
return;
*va &= ~1u;
- v->arch.hvm_vcpu.viridian.vp_assist.vector = 0;
+ v->arch.hvm_vcpu.viridian.vp_assist.pending = false;
}
static void update_reference_tsc(struct domain *d, bool_t initialize)
for_each_vcpu( d, v ) {
struct hvm_viridian_vcpu_context ctxt = {
.vp_assist_msr = v->arch.hvm_vcpu.viridian.vp_assist.msr.raw,
- .vp_assist_vector = v->arch.hvm_vcpu.viridian.vp_assist.vector,
+ .vp_assist_pending = v->arch.hvm_vcpu.viridian.vp_assist.pending,
};
if ( hvm_save_entry(VIRIDIAN_VCPU, v->vcpu_id, h, &ctxt) != 0 )
!v->arch.hvm_vcpu.viridian.vp_assist.va )
initialize_vp_assist(v);
- v->arch.hvm_vcpu.viridian.vp_assist.vector = ctxt.vp_assist_vector;
+ v->arch.hvm_vcpu.viridian.vp_assist.pending = !!ctxt.vp_assist_pending;
return 0;
}
void vlapic_EOI_set(struct vlapic *vlapic)
{
- int vector = vlapic_find_highest_isr(vlapic);
+ struct vcpu *v = vlapic_vcpu(vlapic);
+ /*
+ * If APIC assist was set then an EOI may have been avoided and
+ * hence this EOI actually relates to a lower priority vector.
+ * Thus it is necessary to first emulate the EOI for the higher
+ * priority vector and then recurse to handle the lower priority
+ * vector.
+ */
+ bool missed_eoi = viridian_apic_assist_completed(v);
+ int vector;
+
+ again:
+ vector = vlapic_find_highest_isr(vlapic);
/* Some EOI writes may not have a matching to an in-service interrupt. */
if ( vector == -1 )
return;
+ /*
+ * If APIC assist was set but the guest chose to EOI anyway then
+ * we need to clean up state.
+ * NOTE: It is harmless to call viridian_apic_assist_clear() on a
+ * recursion, even though it is not necessary.
+ */
+ if ( !missed_eoi )
+ viridian_apic_assist_clear(v);
+
vlapic_clear_vector(vector, &vlapic->regs->data[APIC_ISR]);
if ( hvm_funcs.handle_eoi )
hvm_funcs.handle_eoi(vector);
vlapic_handle_EOI(vlapic, vector);
+
+ if ( missed_eoi )
+ {
+ missed_eoi = false;
+ goto again;
+ }
}
void vlapic_handle_EOI(struct vlapic *vlapic, u8 vector)
int vlapic_has_pending_irq(struct vcpu *v)
{
struct vlapic *vlapic = vcpu_vlapic(v);
- int irr, vector, isr;
+ int irr, isr;
if ( !vlapic_enabled(vlapic) )
return -1;
!nestedhvm_vcpu_in_guestmode(v) )
return irr;
+ isr = vlapic_find_highest_isr(vlapic);
+
/*
- * If APIC assist was used then there may have been no EOI so
- * we need to clear the requisite bit from the ISR here, before
- * comparing with the IRR.
+ * If APIC assist was set then an EOI may have been avoided.
+ * If so, we need to emulate the EOI here before comparing ISR
+ * with IRR.
*/
- vector = viridian_complete_apic_assist(v);
- if ( vector )
- vlapic_clear_vector(vector, &vlapic->regs->data[APIC_ISR]);
-
- isr = vlapic_find_highest_isr(vlapic);
- if ( isr == -1 )
- return irr;
+ if ( viridian_apic_assist_completed(v) )
+ {
+ vlapic_EOI_set(vlapic);
+ isr = vlapic_find_highest_isr(vlapic);
+ }
/*
- * A vector is pending in the ISR so, regardless of whether the new
- * vector in the IRR is lower or higher in priority, any pending
- * APIC assist must be aborted to ensure an EOI.
+ * The specification says that if APIC assist is set and a
+ * subsequent interrupt of lower priority occurs then APIC assist
+ * needs to be cleared.
*/
- viridian_abort_apic_assist(v);
+ if ( isr >= 0 &&
+ (irr & 0xf0) <= (isr & 0xf0) )
+ {
+ viridian_apic_assist_clear(v);
+ return -1;
+ }
- return ((isr & 0xf0) < (irr & 0xf0)) ? irr : -1;
+ return irr;
}
int vlapic_ack_pending_irq(struct vcpu *v, int vector, bool_t force_ack)
goto done;
isr = vlapic_find_highest_isr(vlapic);
- if ( isr == -1 )
+ if ( isr == -1 && vector > 0x10 )
{
/*
- * This vector is edge triggered and no other vectors are pending
- * in the ISR so we can use APIC assist to avoid exiting for EOI.
+ * This vector is edge triggered, not in the legacy range, and no
+ * lower priority vectors are pending in the ISR. Thus we can set
+ * APIC assist to avoid exiting for EOI.
*/
- viridian_start_apic_assist(v, vector);
+ viridian_apic_assist_set(v);
}
done: