diff --git a/xen/arch/x86/hvm/viridian.c b/xen/arch/x86/hvm/viridian.c index f0fa59d7d5..70aab520bc 100644 --- a/xen/arch/x86/hvm/viridian.c +++ b/xen/arch/x86/hvm/viridian.c @@ -433,46 +433,44 @@ static void teardown_vp_assist(struct vcpu *v) 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. */ + 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; + } - vector = v->arch.hvm_vcpu.viridian.vp_assist.vector; - v->arch.hvm_vcpu.viridian.vp_assist.vector = 0; - - 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; @@ -480,7 +478,7 @@ void viridian_abort_apic_assist(struct vcpu *v) 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) @@ -1040,7 +1038,7 @@ static int viridian_save_vcpu_ctxt(struct domain *d, hvm_domain_context_t *h) 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 ) @@ -1075,7 +1073,7 @@ static int viridian_load_vcpu_ctxt(struct domain *d, hvm_domain_context_t *h) !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; } diff --git a/xen/arch/x86/hvm/vlapic.c b/xen/arch/x86/hvm/vlapic.c index 50f53bdaef..7387f91fe0 100644 --- a/xen/arch/x86/hvm/vlapic.c +++ b/xen/arch/x86/hvm/vlapic.c @@ -416,18 +416,45 @@ struct vlapic *vlapic_lowest_prio( 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) @@ -1239,7 +1266,7 @@ int vlapic_virtual_intr_delivery_enabled(void) 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; @@ -1252,27 +1279,32 @@ int vlapic_has_pending_irq(struct vcpu *v) !nestedhvm_vcpu_in_guestmode(v) ) return irr; - /* - * 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. - */ - 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; /* - * 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. + * 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. */ - viridian_abort_apic_assist(v); + if ( viridian_apic_assist_completed(v) ) + { + vlapic_EOI_set(vlapic); + isr = vlapic_find_highest_isr(vlapic); + } - return ((isr & 0xf0) < (irr & 0xf0)) ? irr : -1; + /* + * The specification says that if APIC assist is set and a + * subsequent interrupt of lower priority occurs then APIC assist + * needs to be cleared. + */ + if ( isr >= 0 && + (irr & 0xf0) <= (isr & 0xf0) ) + { + viridian_apic_assist_clear(v); + return -1; + } + + return irr; } int vlapic_ack_pending_irq(struct vcpu *v, int vector, bool_t force_ack) @@ -1290,13 +1322,14 @@ 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: diff --git a/xen/include/asm-x86/hvm/viridian.h b/xen/include/asm-x86/hvm/viridian.h index 30259e91b0..4cbd133720 100644 --- a/xen/include/asm-x86/hvm/viridian.h +++ b/xen/include/asm-x86/hvm/viridian.h @@ -24,7 +24,7 @@ struct viridian_vcpu struct { union viridian_vp_assist msr; void *va; - int vector; + bool pending; } vp_assist; uint64_t crash_param[5]; }; @@ -120,9 +120,9 @@ void viridian_time_ref_count_thaw(struct domain *d); void viridian_vcpu_deinit(struct vcpu *v); void viridian_domain_deinit(struct domain *d); -void viridian_start_apic_assist(struct vcpu *v, int vector); -int viridian_complete_apic_assist(struct vcpu *v); -void viridian_abort_apic_assist(struct vcpu *v); +void viridian_apic_assist_set(struct vcpu *v); +bool viridian_apic_assist_completed(struct vcpu *v); +void viridian_apic_assist_clear(struct vcpu *v); #endif /* __ASM_X86_HVM_VIRIDIAN_H__ */ diff --git a/xen/include/public/arch-x86/hvm/save.h b/xen/include/public/arch-x86/hvm/save.h index fd7bf3fb38..4691d4d4aa 100644 --- a/xen/include/public/arch-x86/hvm/save.h +++ b/xen/include/public/arch-x86/hvm/save.h @@ -600,7 +600,7 @@ DECLARE_HVM_SAVE_TYPE(VIRIDIAN_DOMAIN, 15, struct hvm_viridian_domain_context); struct hvm_viridian_vcpu_context { uint64_t vp_assist_msr; - uint8_t vp_assist_vector; + uint8_t vp_assist_pending; uint8_t _pad[7]; };