Skip to content

Commit 73b42dc

Browse files
committed
KVM: x86: Re-split x2APIC ICR into ICR+ICR2 for AMD (x2AVIC)
Re-introduce the "split" x2APIC ICR storage that KVM used prior to Intel's IPI virtualization support, but only for AMD. While not stated anywhere in the APM, despite stating the ICR is a single 64-bit register, AMD CPUs store the 64-bit ICR as two separate 32-bit values in ICR and ICR2. When IPI virtualization (IPIv on Intel, all AVIC flavors on AMD) is enabled, KVM needs to match CPU behavior as some ICR ICR writes will be handled by the CPU, not by KVM. Add a kvm_x86_ops knob to control the underlying format used by the CPU to store the x2APIC ICR, and tune it to AMD vs. Intel regardless of whether or not x2AVIC is enabled. If KVM is handling all ICR writes, the storage format for x2APIC mode doesn't matter, and having the behavior follow AMD versus Intel will provide better test coverage and ease debugging. Fixes: 4d1d794 ("KVM: SVM: Introduce logic to (de)activate x2AVIC mode") Cc: [email protected] Cc: Maxim Levitsky <[email protected]> Cc: Suravee Suthikulpanit <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Sean Christopherson <[email protected]>
1 parent d332343 commit 73b42dc

File tree

4 files changed

+36
-12
lines changed

4 files changed

+36
-12
lines changed

arch/x86/include/asm/kvm_host.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1727,6 +1727,8 @@ struct kvm_x86_ops {
17271727
void (*enable_nmi_window)(struct kvm_vcpu *vcpu);
17281728
void (*enable_irq_window)(struct kvm_vcpu *vcpu);
17291729
void (*update_cr8_intercept)(struct kvm_vcpu *vcpu, int tpr, int irr);
1730+
1731+
const bool x2apic_icr_is_split;
17301732
const unsigned long required_apicv_inhibits;
17311733
bool allow_apicv_in_x2apic_without_x2apic_virtualization;
17321734
void (*refresh_apicv_exec_ctrl)(struct kvm_vcpu *vcpu);

arch/x86/kvm/lapic.c

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2471,11 +2471,25 @@ int kvm_x2apic_icr_write(struct kvm_lapic *apic, u64 data)
24712471
data &= ~APIC_ICR_BUSY;
24722472

24732473
kvm_apic_send_ipi(apic, (u32)data, (u32)(data >> 32));
2474-
kvm_lapic_set_reg64(apic, APIC_ICR, data);
2474+
if (kvm_x86_ops.x2apic_icr_is_split) {
2475+
kvm_lapic_set_reg(apic, APIC_ICR, data);
2476+
kvm_lapic_set_reg(apic, APIC_ICR2, data >> 32);
2477+
} else {
2478+
kvm_lapic_set_reg64(apic, APIC_ICR, data);
2479+
}
24752480
trace_kvm_apic_write(APIC_ICR, data);
24762481
return 0;
24772482
}
24782483

2484+
static u64 kvm_x2apic_icr_read(struct kvm_lapic *apic)
2485+
{
2486+
if (kvm_x86_ops.x2apic_icr_is_split)
2487+
return (u64)kvm_lapic_get_reg(apic, APIC_ICR) |
2488+
(u64)kvm_lapic_get_reg(apic, APIC_ICR2) << 32;
2489+
2490+
return kvm_lapic_get_reg64(apic, APIC_ICR);
2491+
}
2492+
24792493
/* emulate APIC access in a trap manner */
24802494
void kvm_apic_write_nodecode(struct kvm_vcpu *vcpu, u32 offset)
24812495
{
@@ -2493,7 +2507,7 @@ void kvm_apic_write_nodecode(struct kvm_vcpu *vcpu, u32 offset)
24932507
* maybe-unecessary write, and both are in the noise anyways.
24942508
*/
24952509
if (apic_x2apic_mode(apic) && offset == APIC_ICR)
2496-
WARN_ON_ONCE(kvm_x2apic_icr_write(apic, kvm_lapic_get_reg64(apic, APIC_ICR)));
2510+
WARN_ON_ONCE(kvm_x2apic_icr_write(apic, kvm_x2apic_icr_read(apic)));
24972511
else
24982512
kvm_lapic_reg_write(apic, offset, kvm_lapic_get_reg(apic, offset));
24992513
}
@@ -3013,18 +3027,22 @@ static int kvm_apic_state_fixup(struct kvm_vcpu *vcpu,
30133027

30143028
/*
30153029
* In x2APIC mode, the LDR is fixed and based on the id. And
3016-
* ICR is internally a single 64-bit register, but needs to be
3017-
* split to ICR+ICR2 in userspace for backwards compatibility.
3030+
* if the ICR is _not_ split, ICR is internally a single 64-bit
3031+
* register, but needs to be split to ICR+ICR2 in userspace for
3032+
* backwards compatibility.
30183033
*/
3019-
if (set) {
3034+
if (set)
30203035
*ldr = kvm_apic_calc_x2apic_ldr(x2apic_id);
30213036

3022-
icr = __kvm_lapic_get_reg(s->regs, APIC_ICR) |
3023-
(u64)__kvm_lapic_get_reg(s->regs, APIC_ICR2) << 32;
3024-
__kvm_lapic_set_reg64(s->regs, APIC_ICR, icr);
3025-
} else {
3026-
icr = __kvm_lapic_get_reg64(s->regs, APIC_ICR);
3027-
__kvm_lapic_set_reg(s->regs, APIC_ICR2, icr >> 32);
3037+
if (!kvm_x86_ops.x2apic_icr_is_split) {
3038+
if (set) {
3039+
icr = __kvm_lapic_get_reg(s->regs, APIC_ICR) |
3040+
(u64)__kvm_lapic_get_reg(s->regs, APIC_ICR2) << 32;
3041+
__kvm_lapic_set_reg64(s->regs, APIC_ICR, icr);
3042+
} else {
3043+
icr = __kvm_lapic_get_reg64(s->regs, APIC_ICR);
3044+
__kvm_lapic_set_reg(s->regs, APIC_ICR2, icr >> 32);
3045+
}
30283046
}
30293047
}
30303048

@@ -3222,7 +3240,7 @@ static int kvm_lapic_msr_read(struct kvm_lapic *apic, u32 reg, u64 *data)
32223240
u32 low;
32233241

32243242
if (reg == APIC_ICR) {
3225-
*data = kvm_lapic_get_reg64(apic, APIC_ICR);
3243+
*data = kvm_x2apic_icr_read(apic);
32263244
return 0;
32273245
}
32283246

arch/x86/kvm/svm/svm.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5053,6 +5053,8 @@ static struct kvm_x86_ops svm_x86_ops __initdata = {
50535053
.enable_nmi_window = svm_enable_nmi_window,
50545054
.enable_irq_window = svm_enable_irq_window,
50555055
.update_cr8_intercept = svm_update_cr8_intercept,
5056+
5057+
.x2apic_icr_is_split = true,
50565058
.set_virtual_apic_mode = avic_refresh_virtual_apic_mode,
50575059
.refresh_apicv_exec_ctrl = avic_refresh_apicv_exec_ctrl,
50585060
.apicv_post_state_restore = avic_apicv_post_state_restore,

arch/x86/kvm/vmx/main.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ struct kvm_x86_ops vt_x86_ops __initdata = {
8989
.enable_nmi_window = vmx_enable_nmi_window,
9090
.enable_irq_window = vmx_enable_irq_window,
9191
.update_cr8_intercept = vmx_update_cr8_intercept,
92+
93+
.x2apic_icr_is_split = false,
9294
.set_virtual_apic_mode = vmx_set_virtual_apic_mode,
9395
.set_apic_access_page_addr = vmx_set_apic_access_page_addr,
9496
.refresh_apicv_exec_ctrl = vmx_refresh_apicv_exec_ctrl,

0 commit comments

Comments
 (0)