From c6f7803f060fdfc8bc66ce5b18b875f0012c8154 Mon Sep 17 00:00:00 2001 From: Victor Sun Date: Wed, 25 Dec 2019 16:26:11 +0800 Subject: [PATCH] HV: restore lapic state and apic id upon INIT Per SDM 10.12.5.1 vol.3, local APIC should keep LAPIC state after receiving INIT. The local APIC ID register should also be preserved. Tracked-On: #4267 Signed-off-by: Victor Sun Reviewed-by: Jason Chen CJ Acked-by: Eddie Dong --- hypervisor/arch/x86/guest/vcpu.c | 4 +-- hypervisor/arch/x86/guest/vlapic.c | 34 ++++++++++++++++------ hypervisor/include/arch/x86/guest/vlapic.h | 3 +- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/hypervisor/arch/x86/guest/vcpu.c b/hypervisor/arch/x86/guest/vcpu.c index 29e3a48f4..544f36634 100644 --- a/hypervisor/arch/x86/guest/vcpu.c +++ b/hypervisor/arch/x86/guest/vcpu.c @@ -194,7 +194,7 @@ void vcpu_reset_eoi_exit_bitmaps(struct acrn_vcpu *vcpu) } /* As a vcpu reset internal API, DO NOT touch any vcpu state transition in this function. */ -static void vcpu_reset_internal(struct acrn_vcpu *vcpu, __unused enum reset_mode mode) +static void vcpu_reset_internal(struct acrn_vcpu *vcpu, enum reset_mode mode) { int32_t i; struct acrn_vlapic *vlapic; @@ -217,7 +217,7 @@ static void vcpu_reset_internal(struct acrn_vcpu *vcpu, __unused enum reset_mode vcpu->thread_obj.notify_mode = SCHED_NOTIFY_IPI; vlapic = vcpu_vlapic(vcpu); - vlapic_reset(vlapic, apicv_ops); + vlapic_reset(vlapic, apicv_ops, mode); reset_vcpu_regs(vcpu); } diff --git a/hypervisor/arch/x86/guest/vlapic.c b/hypervisor/arch/x86/guest/vlapic.c index e1fca5e82..824c36784 100644 --- a/hypervisor/arch/x86/guest/vlapic.c +++ b/hypervisor/arch/x86/guest/vlapic.c @@ -55,8 +55,8 @@ static inline uint32_t prio(uint32_t x) #define VLAPIC_VERSION (16U) #define APICBASE_BSP 0x00000100UL -#define APICBASE_X2APIC 0x00000400U -#define APICBASE_XAPIC 0x00000800U +#define APICBASE_X2APIC 0x00000400UL +#define APICBASE_XAPIC 0x00000800UL #define APICBASE_LAPIC_MODE (APICBASE_XAPIC | APICBASE_X2APIC) #define APICBASE_ENABLED 0x00000800UL #define LOGICAL_ID_MASK 0xFU @@ -1643,24 +1643,39 @@ static int32_t vlapic_write(struct acrn_vlapic *vlapic, uint32_t offset, uint64_ * @pre vlapic != NULL && ops != NULL */ void -vlapic_reset(struct acrn_vlapic *vlapic, const struct acrn_apicv_ops *ops) +vlapic_reset(struct acrn_vlapic *vlapic, const struct acrn_apicv_ops *ops, enum reset_mode mode) { struct lapic_regs *lapic; + uint64_t preserved_lapic_mode = vlapic->msr_apicbase & APICBASE_LAPIC_MODE; + uint32_t preserved_apic_id = vlapic->apic_page.id.v; - /* - * Upon reset, vlapic is set to xAPIC mode. - */ - vlapic->msr_apicbase = DEFAULT_APIC_BASE | APICBASE_ENABLED; + vlapic->msr_apicbase = DEFAULT_APIC_BASE; if (vlapic->vcpu->vcpu_id == BOOT_CPU_ID) { vlapic->msr_apicbase |= APICBASE_BSP; } + if (mode == INIT_RESET) { + if ((preserved_lapic_mode & APICBASE_ENABLED) != 0U ) { + /* Per SDM 10.12.5.1 vol.3, need to preserve lapic mode after INIT */ + vlapic->msr_apicbase |= preserved_lapic_mode; + } + } else { + /* Upon reset, vlapic is set to xAPIC mode. */ + vlapic->msr_apicbase |= APICBASE_XAPIC; + } lapic = &(vlapic->apic_page); (void)memset((void *)lapic, 0U, sizeof(struct lapic_regs)); (void)memset((void *)&(vlapic->pir_desc), 0U, sizeof(vlapic->pir_desc)); - lapic->id.v = vlapic_build_id(vlapic); + if (mode == INIT_RESET) { + if ((preserved_lapic_mode & APICBASE_ENABLED) != 0U ) { + /* the local APIC ID register should be preserved in XAPIC or X2APIC mode */ + lapic->id.v = preserved_apic_id; + } + } else { + lapic->id.v = vlapic_build_id(vlapic); + } lapic->version.v = VLAPIC_VERSION; lapic->version.v |= (VLAPIC_MAXLVT_INDEX << MAXLVTSHIFT); lapic->dfr.v = 0xffffffffU; @@ -1779,7 +1794,8 @@ int32_t vlapic_set_apicbase(struct acrn_vlapic *vlapic, uint64_t new) if ((new & APICBASE_LAPIC_MODE) == (APICBASE_XAPIC | APICBASE_X2APIC)) { if (is_lapic_pt_configured(vcpu->vm)) { - vlapic_reset(vlapic, &ptapic_ops); + /* vlapic need to be reset to make sure it is in correct state */ + vlapic_reset(vlapic, &ptapic_ops, SOFTWARE_RESET); } vlapic->msr_apicbase = new; vlapic_build_x2apic_id(vlapic); diff --git a/hypervisor/include/arch/x86/guest/vlapic.h b/hypervisor/include/arch/x86/guest/vlapic.h index f271a4e0f..cd12d656b 100644 --- a/hypervisor/include/arch/x86/guest/vlapic.h +++ b/hypervisor/include/arch/x86/guest/vlapic.h @@ -106,6 +106,7 @@ struct acrn_apicv_ops { bool (*x2apic_write_msr_may_valid)(uint32_t offset); }; +enum reset_mode; extern const struct acrn_apicv_ops *apicv_ops; void vlapic_set_apicv_ops(void); @@ -194,7 +195,7 @@ void vlapic_free(struct acrn_vcpu *vcpu); * @pre vlapic->vcpu->vcpu_id < MAX_VCPUS_PER_VM */ void vlapic_init(struct acrn_vlapic *vlapic); -void vlapic_reset(struct acrn_vlapic *vlapic, const struct acrn_apicv_ops *ops); +void vlapic_reset(struct acrn_vlapic *vlapic, const struct acrn_apicv_ops *ops, enum reset_mode mode); void vlapic_restore(struct acrn_vlapic *vlapic, const struct lapic_regs *regs); uint64_t vlapic_apicv_get_apic_access_addr(void); uint64_t vlapic_apicv_get_apic_page_addr(struct acrn_vlapic *vlapic);