diff options
author | Radim Krčmář <rkrcmar@redhat.com> | 2015-02-12 13:41:31 -0500 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2015-04-08 04:46:59 -0400 |
commit | 03d2249ea60818e97475ac529aa183cf130935bb (patch) | |
tree | 80e19d06b6a255f108434bbd362142ebe69a2292 /arch/x86/kvm | |
parent | 19456060315cedc5595a47007f886369871dfbc5 (diff) |
KVM: x86: use MDA for interrupt matching
In mixed modes, we musn't deliver xAPIC IPIs like x2APIC and vice versa.
Instead of preserving the information in apic_send_ipi(), we regain it
by converting all destinations into correct MDA in the slow path.
This allows easier reasoning about subsequent matching.
Our kvm_apic_broadcast() had an interesting design decision: it didn't
consider IOxAPIC 0xff as broadcast in x2APIC mode ...
everything worked because IOxAPIC can't set that in physical mode and
logical mode considered it as a message for first 8 VCPUs.
This patch interprets IOxAPIC 0xff as x2APIC broadcast.
Signed-off-by: Radim Krčmář <rkrcmar@redhat.com>
Message-Id: <1423766494-26150-2-git-send-email-rkrcmar@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'arch/x86/kvm')
-rw-r--r-- | arch/x86/kvm/lapic.c | 40 |
1 files changed, 33 insertions, 7 deletions
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 44f7b9afbedb..69569744745d 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c | |||
@@ -588,15 +588,23 @@ static void apic_set_tpr(struct kvm_lapic *apic, u32 tpr) | |||
588 | apic_update_ppr(apic); | 588 | apic_update_ppr(apic); |
589 | } | 589 | } |
590 | 590 | ||
591 | static bool kvm_apic_broadcast(struct kvm_lapic *apic, u32 dest) | 591 | static bool kvm_apic_broadcast(struct kvm_lapic *apic, u32 mda) |
592 | { | 592 | { |
593 | return dest == (apic_x2apic_mode(apic) ? | 593 | if (apic_x2apic_mode(apic)) |
594 | X2APIC_BROADCAST : APIC_BROADCAST); | 594 | return mda == X2APIC_BROADCAST; |
595 | |||
596 | return GET_APIC_DEST_FIELD(mda) == APIC_BROADCAST; | ||
595 | } | 597 | } |
596 | 598 | ||
597 | static bool kvm_apic_match_physical_addr(struct kvm_lapic *apic, u32 dest) | 599 | static bool kvm_apic_match_physical_addr(struct kvm_lapic *apic, u32 mda) |
598 | { | 600 | { |
599 | return kvm_apic_id(apic) == dest || kvm_apic_broadcast(apic, dest); | 601 | if (kvm_apic_broadcast(apic, mda)) |
602 | return true; | ||
603 | |||
604 | if (apic_x2apic_mode(apic)) | ||
605 | return mda == kvm_apic_id(apic); | ||
606 | |||
607 | return mda == SET_APIC_DEST_FIELD(kvm_apic_id(apic)); | ||
600 | } | 608 | } |
601 | 609 | ||
602 | static bool kvm_apic_match_logical_addr(struct kvm_lapic *apic, u32 mda) | 610 | static bool kvm_apic_match_logical_addr(struct kvm_lapic *apic, u32 mda) |
@@ -613,6 +621,7 @@ static bool kvm_apic_match_logical_addr(struct kvm_lapic *apic, u32 mda) | |||
613 | && (logical_id & mda & 0xffff) != 0; | 621 | && (logical_id & mda & 0xffff) != 0; |
614 | 622 | ||
615 | logical_id = GET_APIC_LOGICAL_ID(logical_id); | 623 | logical_id = GET_APIC_LOGICAL_ID(logical_id); |
624 | mda = GET_APIC_DEST_FIELD(mda); | ||
616 | 625 | ||
617 | switch (kvm_apic_get_reg(apic, APIC_DFR)) { | 626 | switch (kvm_apic_get_reg(apic, APIC_DFR)) { |
618 | case APIC_DFR_FLAT: | 627 | case APIC_DFR_FLAT: |
@@ -627,10 +636,27 @@ static bool kvm_apic_match_logical_addr(struct kvm_lapic *apic, u32 mda) | |||
627 | } | 636 | } |
628 | } | 637 | } |
629 | 638 | ||
639 | /* KVM APIC implementation has two quirks | ||
640 | * - dest always begins at 0 while xAPIC MDA has offset 24, | ||
641 | * - IOxAPIC messages have to be delivered (directly) to x2APIC. | ||
642 | */ | ||
643 | static u32 kvm_apic_mda(unsigned int dest_id, struct kvm_lapic *source, | ||
644 | struct kvm_lapic *target) | ||
645 | { | ||
646 | bool ipi = source != NULL; | ||
647 | bool x2apic_mda = apic_x2apic_mode(ipi ? source : target); | ||
648 | |||
649 | if (!ipi && dest_id == APIC_BROADCAST && x2apic_mda) | ||
650 | return X2APIC_BROADCAST; | ||
651 | |||
652 | return x2apic_mda ? dest_id : SET_APIC_DEST_FIELD(dest_id); | ||
653 | } | ||
654 | |||
630 | bool kvm_apic_match_dest(struct kvm_vcpu *vcpu, struct kvm_lapic *source, | 655 | bool kvm_apic_match_dest(struct kvm_vcpu *vcpu, struct kvm_lapic *source, |
631 | int short_hand, unsigned int dest, int dest_mode) | 656 | int short_hand, unsigned int dest, int dest_mode) |
632 | { | 657 | { |
633 | struct kvm_lapic *target = vcpu->arch.apic; | 658 | struct kvm_lapic *target = vcpu->arch.apic; |
659 | u32 mda = kvm_apic_mda(dest, source, target); | ||
634 | 660 | ||
635 | apic_debug("target %p, source %p, dest 0x%x, " | 661 | apic_debug("target %p, source %p, dest 0x%x, " |
636 | "dest_mode 0x%x, short_hand 0x%x\n", | 662 | "dest_mode 0x%x, short_hand 0x%x\n", |
@@ -640,9 +666,9 @@ bool kvm_apic_match_dest(struct kvm_vcpu *vcpu, struct kvm_lapic *source, | |||
640 | switch (short_hand) { | 666 | switch (short_hand) { |
641 | case APIC_DEST_NOSHORT: | 667 | case APIC_DEST_NOSHORT: |
642 | if (dest_mode == APIC_DEST_PHYSICAL) | 668 | if (dest_mode == APIC_DEST_PHYSICAL) |
643 | return kvm_apic_match_physical_addr(target, dest); | 669 | return kvm_apic_match_physical_addr(target, mda); |
644 | else | 670 | else |
645 | return kvm_apic_match_logical_addr(target, dest); | 671 | return kvm_apic_match_logical_addr(target, mda); |
646 | case APIC_DEST_SELF: | 672 | case APIC_DEST_SELF: |
647 | return target == source; | 673 | return target == source; |
648 | case APIC_DEST_ALLINC: | 674 | case APIC_DEST_ALLINC: |