diff options
author | Gleb Natapov <gleb@redhat.com> | 2009-07-05 10:39:36 -0400 |
---|---|---|
committer | Avi Kivity <avi@redhat.com> | 2009-09-10 01:33:08 -0400 |
commit | 0105d1a526404cfc9779552a6198bfd0e5fc937a (patch) | |
tree | edf269da022574fe380b73473daef4b763b25672 /arch/x86/kvm/lapic.c | |
parent | fc61b800f9506ca47bf1439342a79847f2353562 (diff) |
KVM: x2apic interface to lapic
This patch implements MSR interface to local apic as defines by x2apic
Intel specification.
Signed-off-by: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Avi Kivity <avi@redhat.com>
Diffstat (limited to 'arch/x86/kvm/lapic.c')
-rw-r--r-- | arch/x86/kvm/lapic.c | 223 |
1 files changed, 165 insertions, 58 deletions
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 62ea2abfd961..683345a121b7 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c | |||
@@ -32,6 +32,7 @@ | |||
32 | #include <asm/current.h> | 32 | #include <asm/current.h> |
33 | #include <asm/apicdef.h> | 33 | #include <asm/apicdef.h> |
34 | #include <asm/atomic.h> | 34 | #include <asm/atomic.h> |
35 | #include <asm/apicdef.h> | ||
35 | #include "kvm_cache_regs.h" | 36 | #include "kvm_cache_regs.h" |
36 | #include "irq.h" | 37 | #include "irq.h" |
37 | #include "trace.h" | 38 | #include "trace.h" |
@@ -158,6 +159,11 @@ void kvm_apic_set_version(struct kvm_vcpu *vcpu) | |||
158 | apic_set_reg(apic, APIC_LVR, v); | 159 | apic_set_reg(apic, APIC_LVR, v); |
159 | } | 160 | } |
160 | 161 | ||
162 | static inline int apic_x2apic_mode(struct kvm_lapic *apic) | ||
163 | { | ||
164 | return apic->vcpu->arch.apic_base & X2APIC_ENABLE; | ||
165 | } | ||
166 | |||
161 | static unsigned int apic_lvt_mask[APIC_LVT_NUM] = { | 167 | static unsigned int apic_lvt_mask[APIC_LVT_NUM] = { |
162 | LVT_MASK | APIC_LVT_TIMER_PERIODIC, /* LVTT */ | 168 | LVT_MASK | APIC_LVT_TIMER_PERIODIC, /* LVTT */ |
163 | LVT_MASK | APIC_MODE_MASK, /* LVTTHMR */ | 169 | LVT_MASK | APIC_MODE_MASK, /* LVTTHMR */ |
@@ -284,7 +290,12 @@ int kvm_apic_match_physical_addr(struct kvm_lapic *apic, u16 dest) | |||
284 | int kvm_apic_match_logical_addr(struct kvm_lapic *apic, u8 mda) | 290 | int kvm_apic_match_logical_addr(struct kvm_lapic *apic, u8 mda) |
285 | { | 291 | { |
286 | int result = 0; | 292 | int result = 0; |
287 | u8 logical_id; | 293 | u32 logical_id; |
294 | |||
295 | if (apic_x2apic_mode(apic)) { | ||
296 | logical_id = apic_get_reg(apic, APIC_LDR); | ||
297 | return logical_id & mda; | ||
298 | } | ||
288 | 299 | ||
289 | logical_id = GET_APIC_LOGICAL_ID(apic_get_reg(apic, APIC_LDR)); | 300 | logical_id = GET_APIC_LOGICAL_ID(apic_get_reg(apic, APIC_LDR)); |
290 | 301 | ||
@@ -477,7 +488,10 @@ static void apic_send_ipi(struct kvm_lapic *apic) | |||
477 | irq.level = icr_low & APIC_INT_ASSERT; | 488 | irq.level = icr_low & APIC_INT_ASSERT; |
478 | irq.trig_mode = icr_low & APIC_INT_LEVELTRIG; | 489 | irq.trig_mode = icr_low & APIC_INT_LEVELTRIG; |
479 | irq.shorthand = icr_low & APIC_SHORT_MASK; | 490 | irq.shorthand = icr_low & APIC_SHORT_MASK; |
480 | irq.dest_id = GET_APIC_DEST_FIELD(icr_high); | 491 | if (apic_x2apic_mode(apic)) |
492 | irq.dest_id = icr_high; | ||
493 | else | ||
494 | irq.dest_id = GET_APIC_DEST_FIELD(icr_high); | ||
481 | 495 | ||
482 | apic_debug("icr_high 0x%x, icr_low 0x%x, " | 496 | apic_debug("icr_high 0x%x, icr_low 0x%x, " |
483 | "short_hand 0x%x, dest 0x%x, trig_mode 0x%x, level 0x%x, " | 497 | "short_hand 0x%x, dest 0x%x, trig_mode 0x%x, level 0x%x, " |
@@ -538,6 +552,12 @@ static u32 __apic_read(struct kvm_lapic *apic, unsigned int offset) | |||
538 | return 0; | 552 | return 0; |
539 | 553 | ||
540 | switch (offset) { | 554 | switch (offset) { |
555 | case APIC_ID: | ||
556 | if (apic_x2apic_mode(apic)) | ||
557 | val = kvm_apic_id(apic); | ||
558 | else | ||
559 | val = kvm_apic_id(apic) << 24; | ||
560 | break; | ||
541 | case APIC_ARBPRI: | 561 | case APIC_ARBPRI: |
542 | printk(KERN_WARNING "Access APIC ARBPRI register " | 562 | printk(KERN_WARNING "Access APIC ARBPRI register " |
543 | "which is for P6\n"); | 563 | "which is for P6\n"); |
@@ -564,28 +584,26 @@ static inline struct kvm_lapic *to_lapic(struct kvm_io_device *dev) | |||
564 | return container_of(dev, struct kvm_lapic, dev); | 584 | return container_of(dev, struct kvm_lapic, dev); |
565 | } | 585 | } |
566 | 586 | ||
567 | static int apic_mmio_in_range(struct kvm_lapic *apic, gpa_t addr) | 587 | static int apic_reg_read(struct kvm_lapic *apic, u32 offset, int len, |
568 | { | 588 | void *data) |
569 | return apic_hw_enabled(apic) && | ||
570 | addr >= apic->base_address && | ||
571 | addr < apic->base_address + LAPIC_MMIO_LENGTH; | ||
572 | } | ||
573 | |||
574 | static int apic_mmio_read(struct kvm_io_device *this, | ||
575 | gpa_t address, int len, void *data) | ||
576 | { | 589 | { |
577 | struct kvm_lapic *apic = to_lapic(this); | ||
578 | unsigned int offset = address - apic->base_address; | ||
579 | unsigned char alignment = offset & 0xf; | 590 | unsigned char alignment = offset & 0xf; |
580 | u32 result; | 591 | u32 result; |
581 | if (!apic_mmio_in_range(apic, address)) | 592 | /* this bitmask has a bit cleared for each reserver register */ |
582 | return -EOPNOTSUPP; | 593 | static const u64 rmask = 0x43ff01ffffffe70cULL; |
583 | 594 | ||
584 | if ((alignment + len) > 4) { | 595 | if ((alignment + len) > 4) { |
585 | printk(KERN_ERR "KVM_APIC_READ: alignment error %lx %d", | 596 | printk(KERN_ERR "KVM_APIC_READ: alignment error %x %d\n", |
586 | (unsigned long)address, len); | 597 | offset, len); |
587 | return 0; | 598 | return 1; |
588 | } | 599 | } |
600 | |||
601 | if (offset > 0x3f0 || !(rmask & (1ULL << (offset >> 4)))) { | ||
602 | printk(KERN_ERR "KVM_APIC_READ: read reserved register %x\n", | ||
603 | offset); | ||
604 | return 1; | ||
605 | } | ||
606 | |||
589 | result = __apic_read(apic, offset & ~0xf); | 607 | result = __apic_read(apic, offset & ~0xf); |
590 | 608 | ||
591 | trace_kvm_apic_read(offset, result); | 609 | trace_kvm_apic_read(offset, result); |
@@ -604,6 +622,27 @@ static int apic_mmio_read(struct kvm_io_device *this, | |||
604 | return 0; | 622 | return 0; |
605 | } | 623 | } |
606 | 624 | ||
625 | static int apic_mmio_in_range(struct kvm_lapic *apic, gpa_t addr) | ||
626 | { | ||
627 | return apic_hw_enabled(apic) && | ||
628 | addr >= apic->base_address && | ||
629 | addr < apic->base_address + LAPIC_MMIO_LENGTH; | ||
630 | } | ||
631 | |||
632 | static int apic_mmio_read(struct kvm_io_device *this, | ||
633 | gpa_t address, int len, void *data) | ||
634 | { | ||
635 | struct kvm_lapic *apic = to_lapic(this); | ||
636 | u32 offset = address - apic->base_address; | ||
637 | |||
638 | if (!apic_mmio_in_range(apic, address)) | ||
639 | return -EOPNOTSUPP; | ||
640 | |||
641 | apic_reg_read(apic, offset, len, data); | ||
642 | |||
643 | return 0; | ||
644 | } | ||
645 | |||
607 | static void update_divide_count(struct kvm_lapic *apic) | 646 | static void update_divide_count(struct kvm_lapic *apic) |
608 | { | 647 | { |
609 | u32 tmp1, tmp2, tdcr; | 648 | u32 tmp1, tmp2, tdcr; |
@@ -657,42 +696,18 @@ static void apic_manage_nmi_watchdog(struct kvm_lapic *apic, u32 lvt0_val) | |||
657 | apic->vcpu->kvm->arch.vapics_in_nmi_mode--; | 696 | apic->vcpu->kvm->arch.vapics_in_nmi_mode--; |
658 | } | 697 | } |
659 | 698 | ||
660 | static int apic_mmio_write(struct kvm_io_device *this, | 699 | static int apic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val) |
661 | gpa_t address, int len, const void *data) | ||
662 | { | 700 | { |
663 | struct kvm_lapic *apic = to_lapic(this); | 701 | int ret = 0; |
664 | unsigned int offset = address - apic->base_address; | ||
665 | unsigned char alignment = offset & 0xf; | ||
666 | u32 val; | ||
667 | if (!apic_mmio_in_range(apic, address)) | ||
668 | return -EOPNOTSUPP; | ||
669 | |||
670 | /* | ||
671 | * APIC register must be aligned on 128-bits boundary. | ||
672 | * 32/64/128 bits registers must be accessed thru 32 bits. | ||
673 | * Refer SDM 8.4.1 | ||
674 | */ | ||
675 | if (len != 4 || alignment) { | ||
676 | /* Don't shout loud, $infamous_os would cause only noise. */ | ||
677 | apic_debug("apic write: bad size=%d %lx\n", | ||
678 | len, (long)address); | ||
679 | return 0; | ||
680 | } | ||
681 | |||
682 | val = *(u32 *) data; | ||
683 | 702 | ||
684 | /* too common printing */ | 703 | trace_kvm_apic_write(reg, val); |
685 | if (offset != APIC_EOI) | ||
686 | apic_debug("%s: offset 0x%x with length 0x%x, and value is " | ||
687 | "0x%x\n", __func__, offset, len, val); | ||
688 | 704 | ||
689 | offset &= 0xff0; | 705 | switch (reg) { |
690 | |||
691 | trace_kvm_apic_write(offset, val); | ||
692 | |||
693 | switch (offset) { | ||
694 | case APIC_ID: /* Local APIC ID */ | 706 | case APIC_ID: /* Local APIC ID */ |
695 | apic_set_reg(apic, APIC_ID, val); | 707 | if (!apic_x2apic_mode(apic)) |
708 | apic_set_reg(apic, APIC_ID, val); | ||
709 | else | ||
710 | ret = 1; | ||
696 | break; | 711 | break; |
697 | 712 | ||
698 | case APIC_TASKPRI: | 713 | case APIC_TASKPRI: |
@@ -705,11 +720,17 @@ static int apic_mmio_write(struct kvm_io_device *this, | |||
705 | break; | 720 | break; |
706 | 721 | ||
707 | case APIC_LDR: | 722 | case APIC_LDR: |
708 | apic_set_reg(apic, APIC_LDR, val & APIC_LDR_MASK); | 723 | if (!apic_x2apic_mode(apic)) |
724 | apic_set_reg(apic, APIC_LDR, val & APIC_LDR_MASK); | ||
725 | else | ||
726 | ret = 1; | ||
709 | break; | 727 | break; |
710 | 728 | ||
711 | case APIC_DFR: | 729 | case APIC_DFR: |
712 | apic_set_reg(apic, APIC_DFR, val | 0x0FFFFFFF); | 730 | if (!apic_x2apic_mode(apic)) |
731 | apic_set_reg(apic, APIC_DFR, val | 0x0FFFFFFF); | ||
732 | else | ||
733 | ret = 1; | ||
713 | break; | 734 | break; |
714 | 735 | ||
715 | case APIC_SPIV: { | 736 | case APIC_SPIV: { |
@@ -739,7 +760,9 @@ static int apic_mmio_write(struct kvm_io_device *this, | |||
739 | break; | 760 | break; |
740 | 761 | ||
741 | case APIC_ICR2: | 762 | case APIC_ICR2: |
742 | apic_set_reg(apic, APIC_ICR2, val & 0xff000000); | 763 | if (!apic_x2apic_mode(apic)) |
764 | val &= 0xff000000; | ||
765 | apic_set_reg(apic, APIC_ICR2, val); | ||
743 | break; | 766 | break; |
744 | 767 | ||
745 | case APIC_LVT0: | 768 | case APIC_LVT0: |
@@ -753,8 +776,8 @@ static int apic_mmio_write(struct kvm_io_device *this, | |||
753 | if (!apic_sw_enabled(apic)) | 776 | if (!apic_sw_enabled(apic)) |
754 | val |= APIC_LVT_MASKED; | 777 | val |= APIC_LVT_MASKED; |
755 | 778 | ||
756 | val &= apic_lvt_mask[(offset - APIC_LVTT) >> 4]; | 779 | val &= apic_lvt_mask[(reg - APIC_LVTT) >> 4]; |
757 | apic_set_reg(apic, offset, val); | 780 | apic_set_reg(apic, reg, val); |
758 | 781 | ||
759 | break; | 782 | break; |
760 | 783 | ||
@@ -762,7 +785,7 @@ static int apic_mmio_write(struct kvm_io_device *this, | |||
762 | hrtimer_cancel(&apic->lapic_timer.timer); | 785 | hrtimer_cancel(&apic->lapic_timer.timer); |
763 | apic_set_reg(apic, APIC_TMICT, val); | 786 | apic_set_reg(apic, APIC_TMICT, val); |
764 | start_apic_timer(apic); | 787 | start_apic_timer(apic); |
765 | return 0; | 788 | break; |
766 | 789 | ||
767 | case APIC_TDCR: | 790 | case APIC_TDCR: |
768 | if (val & 4) | 791 | if (val & 4) |
@@ -771,11 +794,58 @@ static int apic_mmio_write(struct kvm_io_device *this, | |||
771 | update_divide_count(apic); | 794 | update_divide_count(apic); |
772 | break; | 795 | break; |
773 | 796 | ||
797 | case APIC_ESR: | ||
798 | if (apic_x2apic_mode(apic) && val != 0) { | ||
799 | printk(KERN_ERR "KVM_WRITE:ESR not zero %x\n", val); | ||
800 | ret = 1; | ||
801 | } | ||
802 | break; | ||
803 | |||
804 | case APIC_SELF_IPI: | ||
805 | if (apic_x2apic_mode(apic)) { | ||
806 | apic_reg_write(apic, APIC_ICR, 0x40000 | (val & 0xff)); | ||
807 | } else | ||
808 | ret = 1; | ||
809 | break; | ||
774 | default: | 810 | default: |
775 | apic_debug("Local APIC Write to read-only register %x\n", | 811 | ret = 1; |
776 | offset); | ||
777 | break; | 812 | break; |
778 | } | 813 | } |
814 | if (ret) | ||
815 | apic_debug("Local APIC Write to read-only register %x\n", reg); | ||
816 | return ret; | ||
817 | } | ||
818 | |||
819 | static int apic_mmio_write(struct kvm_io_device *this, | ||
820 | gpa_t address, int len, const void *data) | ||
821 | { | ||
822 | struct kvm_lapic *apic = to_lapic(this); | ||
823 | unsigned int offset = address - apic->base_address; | ||
824 | u32 val; | ||
825 | |||
826 | if (!apic_mmio_in_range(apic, address)) | ||
827 | return -EOPNOTSUPP; | ||
828 | |||
829 | /* | ||
830 | * APIC register must be aligned on 128-bits boundary. | ||
831 | * 32/64/128 bits registers must be accessed thru 32 bits. | ||
832 | * Refer SDM 8.4.1 | ||
833 | */ | ||
834 | if (len != 4 || (offset & 0xf)) { | ||
835 | /* Don't shout loud, $infamous_os would cause only noise. */ | ||
836 | apic_debug("apic write: bad size=%d %lx\n", len, (long)address); | ||
837 | return; | ||
838 | } | ||
839 | |||
840 | val = *(u32*)data; | ||
841 | |||
842 | /* too common printing */ | ||
843 | if (offset != APIC_EOI) | ||
844 | apic_debug("%s: offset 0x%x with length 0x%x, and value is " | ||
845 | "0x%x\n", __func__, offset, len, val); | ||
846 | |||
847 | apic_reg_write(apic, offset & 0xff0, val); | ||
848 | |||
779 | return 0; | 849 | return 0; |
780 | } | 850 | } |
781 | 851 | ||
@@ -834,6 +904,11 @@ void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value) | |||
834 | value &= ~MSR_IA32_APICBASE_BSP; | 904 | value &= ~MSR_IA32_APICBASE_BSP; |
835 | 905 | ||
836 | vcpu->arch.apic_base = value; | 906 | vcpu->arch.apic_base = value; |
907 | if (apic_x2apic_mode(apic)) { | ||
908 | u32 id = kvm_apic_id(apic); | ||
909 | u32 ldr = ((id & ~0xf) << 16) | (1 << (id & 0xf)); | ||
910 | apic_set_reg(apic, APIC_LDR, ldr); | ||
911 | } | ||
837 | apic->base_address = apic->vcpu->arch.apic_base & | 912 | apic->base_address = apic->vcpu->arch.apic_base & |
838 | MSR_IA32_APICBASE_BASE; | 913 | MSR_IA32_APICBASE_BASE; |
839 | 914 | ||
@@ -1130,3 +1205,35 @@ void kvm_lapic_set_vapic_addr(struct kvm_vcpu *vcpu, gpa_t vapic_addr) | |||
1130 | 1205 | ||
1131 | vcpu->arch.apic->vapic_addr = vapic_addr; | 1206 | vcpu->arch.apic->vapic_addr = vapic_addr; |
1132 | } | 1207 | } |
1208 | |||
1209 | int kvm_x2apic_msr_write(struct kvm_vcpu *vcpu, u32 msr, u64 data) | ||
1210 | { | ||
1211 | struct kvm_lapic *apic = vcpu->arch.apic; | ||
1212 | u32 reg = (msr - APIC_BASE_MSR) << 4; | ||
1213 | |||
1214 | if (!irqchip_in_kernel(vcpu->kvm) || !apic_x2apic_mode(apic)) | ||
1215 | return 1; | ||
1216 | |||
1217 | /* if this is ICR write vector before command */ | ||
1218 | if (msr == 0x830) | ||
1219 | apic_reg_write(apic, APIC_ICR2, (u32)(data >> 32)); | ||
1220 | return apic_reg_write(apic, reg, (u32)data); | ||
1221 | } | ||
1222 | |||
1223 | int kvm_x2apic_msr_read(struct kvm_vcpu *vcpu, u32 msr, u64 *data) | ||
1224 | { | ||
1225 | struct kvm_lapic *apic = vcpu->arch.apic; | ||
1226 | u32 reg = (msr - APIC_BASE_MSR) << 4, low, high = 0; | ||
1227 | |||
1228 | if (!irqchip_in_kernel(vcpu->kvm) || !apic_x2apic_mode(apic)) | ||
1229 | return 1; | ||
1230 | |||
1231 | if (apic_reg_read(apic, reg, 4, &low)) | ||
1232 | return 1; | ||
1233 | if (msr == 0x830) | ||
1234 | apic_reg_read(apic, APIC_ICR2, 4, &high); | ||
1235 | |||
1236 | *data = (((u64)high) << 32) | low; | ||
1237 | |||
1238 | return 0; | ||
1239 | } | ||