Skip to content

Commit 0105d1a

Browse files
Gleb Natapovavikivity
authored andcommitted
KVM: x2apic interface to lapic
This patch implements MSR interface to local apic as defines by x2apic Intel specification. Signed-off-by: Gleb Natapov <[email protected]> Signed-off-by: Avi Kivity <[email protected]>
1 parent fc61b80 commit 0105d1a

File tree

3 files changed

+173
-59
lines changed

3 files changed

+173
-59
lines changed

arch/x86/kvm/lapic.c

Lines changed: 165 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include <asm/current.h>
3333
#include <asm/apicdef.h>
3434
#include <asm/atomic.h>
35+
#include <asm/apicdef.h>
3536
#include "kvm_cache_regs.h"
3637
#include "irq.h"
3738
#include "trace.h"
@@ -158,6 +159,11 @@ void kvm_apic_set_version(struct kvm_vcpu *vcpu)
158159
apic_set_reg(apic, APIC_LVR, v);
159160
}
160161

162+
static inline int apic_x2apic_mode(struct kvm_lapic *apic)
163+
{
164+
return apic->vcpu->arch.apic_base & X2APIC_ENABLE;
165+
}
166+
161167
static unsigned int apic_lvt_mask[APIC_LVT_NUM] = {
162168
LVT_MASK | APIC_LVT_TIMER_PERIODIC, /* LVTT */
163169
LVT_MASK | APIC_MODE_MASK, /* LVTTHMR */
@@ -284,7 +290,12 @@ int kvm_apic_match_physical_addr(struct kvm_lapic *apic, u16 dest)
284290
int kvm_apic_match_logical_addr(struct kvm_lapic *apic, u8 mda)
285291
{
286292
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+
}
288299

289300
logical_id = GET_APIC_LOGICAL_ID(apic_get_reg(apic, APIC_LDR));
290301

@@ -477,7 +488,10 @@ static void apic_send_ipi(struct kvm_lapic *apic)
477488
irq.level = icr_low & APIC_INT_ASSERT;
478489
irq.trig_mode = icr_low & APIC_INT_LEVELTRIG;
479490
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);
481495

482496
apic_debug("icr_high 0x%x, icr_low 0x%x, "
483497
"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)
538552
return 0;
539553

540554
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;
541561
case APIC_ARBPRI:
542562
printk(KERN_WARNING "Access APIC ARBPRI register "
543563
"which is for P6\n");
@@ -564,28 +584,26 @@ static inline struct kvm_lapic *to_lapic(struct kvm_io_device *dev)
564584
return container_of(dev, struct kvm_lapic, dev);
565585
}
566586

567-
static int apic_mmio_in_range(struct kvm_lapic *apic, gpa_t addr)
568-
{
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)
587+
static int apic_reg_read(struct kvm_lapic *apic, u32 offset, int len,
588+
void *data)
576589
{
577-
struct kvm_lapic *apic = to_lapic(this);
578-
unsigned int offset = address - apic->base_address;
579590
unsigned char alignment = offset & 0xf;
580591
u32 result;
581-
if (!apic_mmio_in_range(apic, address))
582-
return -EOPNOTSUPP;
592+
/* this bitmask has a bit cleared for each reserver register */
593+
static const u64 rmask = 0x43ff01ffffffe70cULL;
583594

584595
if ((alignment + len) > 4) {
585-
printk(KERN_ERR "KVM_APIC_READ: alignment error %lx %d",
586-
(unsigned long)address, len);
587-
return 0;
596+
printk(KERN_ERR "KVM_APIC_READ: alignment error %x %d\n",
597+
offset, len);
598+
return 1;
588599
}
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+
589607
result = __apic_read(apic, offset & ~0xf);
590608

591609
trace_kvm_apic_read(offset, result);
@@ -604,6 +622,27 @@ static int apic_mmio_read(struct kvm_io_device *this,
604622
return 0;
605623
}
606624

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+
607646
static void update_divide_count(struct kvm_lapic *apic)
608647
{
609648
u32 tmp1, tmp2, tdcr;
@@ -657,42 +696,18 @@ static void apic_manage_nmi_watchdog(struct kvm_lapic *apic, u32 lvt0_val)
657696
apic->vcpu->kvm->arch.vapics_in_nmi_mode--;
658697
}
659698

660-
static int apic_mmio_write(struct kvm_io_device *this,
661-
gpa_t address, int len, const void *data)
699+
static int apic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
662700
{
663-
struct kvm_lapic *apic = to_lapic(this);
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;
701+
int ret = 0;
683702

684-
/* too common printing */
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);
703+
trace_kvm_apic_write(reg, val);
688704

689-
offset &= 0xff0;
690-
691-
trace_kvm_apic_write(offset, val);
692-
693-
switch (offset) {
705+
switch (reg) {
694706
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;
696711
break;
697712

698713
case APIC_TASKPRI:
@@ -705,11 +720,17 @@ static int apic_mmio_write(struct kvm_io_device *this,
705720
break;
706721

707722
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;
709727
break;
710728

711729
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;
713734
break;
714735

715736
case APIC_SPIV: {
@@ -739,7 +760,9 @@ static int apic_mmio_write(struct kvm_io_device *this,
739760
break;
740761

741762
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);
743766
break;
744767

745768
case APIC_LVT0:
@@ -753,16 +776,16 @@ static int apic_mmio_write(struct kvm_io_device *this,
753776
if (!apic_sw_enabled(apic))
754777
val |= APIC_LVT_MASKED;
755778

756-
val &= apic_lvt_mask[(offset - APIC_LVTT) >> 4];
757-
apic_set_reg(apic, offset, val);
779+
val &= apic_lvt_mask[(reg - APIC_LVTT) >> 4];
780+
apic_set_reg(apic, reg, val);
758781

759782
break;
760783

761784
case APIC_TMICT:
762785
hrtimer_cancel(&apic->lapic_timer.timer);
763786
apic_set_reg(apic, APIC_TMICT, val);
764787
start_apic_timer(apic);
765-
return 0;
788+
break;
766789

767790
case APIC_TDCR:
768791
if (val & 4)
@@ -771,11 +794,58 @@ static int apic_mmio_write(struct kvm_io_device *this,
771794
update_divide_count(apic);
772795
break;
773796

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;
774810
default:
775-
apic_debug("Local APIC Write to read-only register %x\n",
776-
offset);
811+
ret = 1;
777812
break;
778813
}
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+
779849
return 0;
780850
}
781851

@@ -834,6 +904,11 @@ void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value)
834904
value &= ~MSR_IA32_APICBASE_BSP;
835905

836906
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+
}
837912
apic->base_address = apic->vcpu->arch.apic_base &
838913
MSR_IA32_APICBASE_BASE;
839914

@@ -1130,3 +1205,35 @@ void kvm_lapic_set_vapic_addr(struct kvm_vcpu *vcpu, gpa_t vapic_addr)
11301205

11311206
vcpu->arch.apic->vapic_addr = vapic_addr;
11321207
}
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+
}

arch/x86/kvm/lapic.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,6 @@ void kvm_lapic_set_vapic_addr(struct kvm_vcpu *vcpu, gpa_t vapic_addr);
4646
void kvm_lapic_sync_from_vapic(struct kvm_vcpu *vcpu);
4747
void kvm_lapic_sync_to_vapic(struct kvm_vcpu *vcpu);
4848

49+
int kvm_x2apic_msr_write(struct kvm_vcpu *vcpu, u32 msr, u64 data);
50+
int kvm_x2apic_msr_read(struct kvm_vcpu *vcpu, u32 msr, u64 *data);
4951
#endif

arch/x86/kvm/x86.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -867,6 +867,8 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data)
867867
case MSR_IA32_APICBASE:
868868
kvm_set_apic_base(vcpu, data);
869869
break;
870+
case APIC_BASE_MSR ... APIC_BASE_MSR + 0x3ff:
871+
return kvm_x2apic_msr_write(vcpu, msr, data);
870872
case MSR_IA32_MISC_ENABLE:
871873
vcpu->arch.ia32_misc_enable_msr = data;
872874
break;
@@ -1065,6 +1067,9 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
10651067
case MSR_IA32_APICBASE:
10661068
data = kvm_get_apic_base(vcpu);
10671069
break;
1070+
case APIC_BASE_MSR ... APIC_BASE_MSR + 0x3ff:
1071+
return kvm_x2apic_msr_read(vcpu, msr, pdata);
1072+
break;
10681073
case MSR_IA32_MISC_ENABLE:
10691074
data = vcpu->arch.ia32_misc_enable_msr;
10701075
break;
@@ -1469,7 +1474,7 @@ static void do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,
14691474
0 /* TM2 */ | F(SSSE3) | 0 /* CNXT-ID */ | 0 /* Reserved */ |
14701475
0 /* Reserved */ | F(CX16) | 0 /* xTPR Update, PDCM */ |
14711476
0 /* Reserved, DCA */ | F(XMM4_1) |
1472-
F(XMM4_2) | 0 /* x2APIC */ | F(MOVBE) | F(POPCNT) |
1477+
F(XMM4_2) | F(X2APIC) | F(MOVBE) | F(POPCNT) |
14731478
0 /* Reserved, XSAVE, OSXSAVE */;
14741479
/* cpuid 0x80000001.ecx */
14751480
const u32 kvm_supported_word6_x86_features =

0 commit comments

Comments
 (0)