xref: /kvm-unit-tests/x86/apic.c (revision cd5f2fb4ad641c51fe0f1a85264dc3f6ede6e131)
17d36db35SAvi Kivity #include "libcflat.h"
27d36db35SAvi Kivity #include "apic.h"
37d36db35SAvi Kivity #include "vm.h"
4f2d2b7c7SAvi Kivity #include "smp.h"
5e7c37968SGleb Natapov #include "desc.h"
6110f0d93SGleb Natapov #include "isr.h"
722c7d929SJan Kiszka #include "msr.h"
89931b88cSRadim Krčmář #include "atomic.h"
903b1e457SNadav Amit #include "fwcfg.h"
107d36db35SAvi Kivity 
11e38858bcSJim Mattson #define MAX_TPR			0xf
12e38858bcSJim Mattson 
is_apic_hw_enabled(void)130d329639SSean Christopherson static bool is_apic_hw_enabled(void)
140d329639SSean Christopherson {
150d329639SSean Christopherson 	return rdmsr(MSR_IA32_APICBASE) & APIC_EN;
160d329639SSean Christopherson }
170d329639SSean Christopherson 
is_apic_sw_enabled(void)181eb8551aSSean Christopherson static bool is_apic_sw_enabled(void)
191eb8551aSSean Christopherson {
201eb8551aSSean Christopherson 	return apic_read(APIC_SPIV) & APIC_SPIV_APIC_ENABLED;
211eb8551aSSean Christopherson }
221eb8551aSSean Christopherson 
is_x2apic_enabled(void)230d329639SSean Christopherson static bool is_x2apic_enabled(void)
240d329639SSean Christopherson {
250d329639SSean Christopherson 	return (rdmsr(MSR_IA32_APICBASE) & (APIC_EN | APIC_EXTD)) == (APIC_EN | APIC_EXTD);
260d329639SSean Christopherson }
270d329639SSean Christopherson 
is_xapic_enabled(void)280d329639SSean Christopherson static bool is_xapic_enabled(void)
290d329639SSean Christopherson {
300d329639SSean Christopherson 	return (rdmsr(MSR_IA32_APICBASE) & (APIC_EN | APIC_EXTD)) == APIC_EN;
310d329639SSean Christopherson }
320d329639SSean Christopherson 
test_lapic_existence(void)337d36db35SAvi Kivity static void test_lapic_existence(void)
347d36db35SAvi Kivity {
353ee24f29SNadav Amit 	u8 version;
367d36db35SAvi Kivity 
373ee24f29SNadav Amit 	version = (u8)apic_read(APIC_LVR);
383ee24f29SNadav Amit 	printf("apic version: %x\n", version);
39a299895bSThomas Huth 	report(version >= 0x10 && version <= 0x15, "apic existence");
407d36db35SAvi Kivity }
417d36db35SAvi Kivity 
42d423ca36SLiu, Jinsong #define TSC_DEADLINE_TIMER_VECTOR 0xef
439931b88cSRadim Krčmář #define BROADCAST_VECTOR 0xcf
44d423ca36SLiu, Jinsong 
45d423ca36SLiu, Jinsong static int tdt_count;
46d423ca36SLiu, Jinsong 
tsc_deadline_timer_isr(isr_regs_t * regs)47d423ca36SLiu, Jinsong static void tsc_deadline_timer_isr(isr_regs_t *regs)
48d423ca36SLiu, Jinsong {
49d423ca36SLiu, Jinsong 	++tdt_count;
500b04ed06SPeter Xu 	eoi();
51d423ca36SLiu, Jinsong }
52d423ca36SLiu, Jinsong 
__test_tsc_deadline_timer(void)5332b9603cSRadim Krčmář static void __test_tsc_deadline_timer(void)
54d423ca36SLiu, Jinsong {
55d423ca36SLiu, Jinsong 	handle_irq(TSC_DEADLINE_TIMER_VECTOR, tsc_deadline_timer_isr);
56d423ca36SLiu, Jinsong 
57d423ca36SLiu, Jinsong 	wrmsr(MSR_IA32_TSCDEADLINE, rdmsr(MSR_IA32_TSC));
58d423ca36SLiu, Jinsong 	asm volatile ("nop");
59a299895bSThomas Huth 	report(tdt_count == 1, "tsc deadline timer");
60a299895bSThomas Huth 	report(rdmsr(MSR_IA32_TSCDEADLINE) == 0, "tsc deadline timer clearing");
61d423ca36SLiu, Jinsong }
62d423ca36SLiu, Jinsong 
enable_tsc_deadline_timer(void)63d423ca36SLiu, Jinsong static int enable_tsc_deadline_timer(void)
64d423ca36SLiu, Jinsong {
65d423ca36SLiu, Jinsong 	uint32_t lvtt;
66d423ca36SLiu, Jinsong 
67badc98caSKrish Sadhukhan 	if (this_cpu_has(X86_FEATURE_TSC_DEADLINE_TIMER)) {
689111ccabSRadim Krčmář 		lvtt = APIC_LVT_TIMER_TSCDEADLINE | TSC_DEADLINE_TIMER_VECTOR;
69d423ca36SLiu, Jinsong 		apic_write(APIC_LVTT, lvtt);
70d423ca36SLiu, Jinsong 		return 1;
71d423ca36SLiu, Jinsong 	} else {
72d423ca36SLiu, Jinsong 		return 0;
73d423ca36SLiu, Jinsong 	}
74d423ca36SLiu, Jinsong }
75d423ca36SLiu, Jinsong 
test_tsc_deadline_timer(void)76d423ca36SLiu, Jinsong static void test_tsc_deadline_timer(void)
77d423ca36SLiu, Jinsong {
7827cc2e49SSean Christopherson 	if(enable_tsc_deadline_timer())
7932b9603cSRadim Krčmář 		__test_tsc_deadline_timer();
8027cc2e49SSean Christopherson 	else
8132b9603cSRadim Krčmář 		report_skip("tsc deadline timer not detected");
82d423ca36SLiu, Jinsong }
83d423ca36SLiu, Jinsong 
do_write_apicbase(void * data)8422c7d929SJan Kiszka static void do_write_apicbase(void *data)
8522c7d929SJan Kiszka {
8622c7d929SJan Kiszka 	wrmsr(MSR_IA32_APICBASE, *(u64 *)data);
8703f37ef2SPaolo Bonzini }
887d36db35SAvi Kivity 
test_write_apicbase_exception(u64 data)8949f5ad9eSPaolo Bonzini static bool test_write_apicbase_exception(u64 data)
9049f5ad9eSPaolo Bonzini {
9149f5ad9eSPaolo Bonzini 	return test_for_exception(GP_VECTOR, do_write_apicbase, &data);
9249f5ad9eSPaolo Bonzini }
9349f5ad9eSPaolo Bonzini 
test_enable_x2apic(void)94db4898e8SThomas Huth static void test_enable_x2apic(void)
957d36db35SAvi Kivity {
966dc73196SSean Christopherson 	u64 apicbase = rdmsr(MSR_IA32_APICBASE);
972092999cSSean Christopherson 
987d36db35SAvi Kivity 	if (enable_x2apic()) {
997d36db35SAvi Kivity 		printf("x2apic enabled\n");
10022c7d929SJan Kiszka 
1016dc73196SSean Christopherson 		apicbase &= ~(APIC_EN | APIC_EXTD);
102a299895bSThomas Huth 		report(test_write_apicbase_exception(apicbase | APIC_EXTD),
103a299895bSThomas Huth 			"x2apic enabled to invalid state");
104a299895bSThomas Huth 		report(test_write_apicbase_exception(apicbase | APIC_EN),
105a299895bSThomas Huth 			"x2apic enabled to apic enabled");
10622c7d929SJan Kiszka 
107a299895bSThomas Huth 		report(!test_write_apicbase_exception(apicbase | 0),
108a299895bSThomas Huth 			"x2apic enabled to disabled state");
109a299895bSThomas Huth 		report(test_write_apicbase_exception(apicbase | APIC_EXTD),
110a299895bSThomas Huth 			"disabled to invalid state");
111a299895bSThomas Huth 		report(test_write_apicbase_exception(apicbase | APIC_EN | APIC_EXTD),
112a299895bSThomas Huth 			"disabled to x2apic enabled");
11322c7d929SJan Kiszka 
114a299895bSThomas Huth 		report(!test_write_apicbase_exception(apicbase | APIC_EN),
115a299895bSThomas Huth 			"apic disabled to apic enabled");
116a299895bSThomas Huth 		report(test_write_apicbase_exception(apicbase | APIC_EXTD),
117a299895bSThomas Huth 			"apic enabled to invalid state");
1187d36db35SAvi Kivity 	} else {
1197d36db35SAvi Kivity 		printf("x2apic not detected\n");
12022c7d929SJan Kiszka 
121a299895bSThomas Huth 		report(test_write_apicbase_exception(APIC_EN | APIC_EXTD),
122a299895bSThomas Huth 		       "enable unsupported x2apic");
1237d36db35SAvi Kivity 	}
1247d36db35SAvi Kivity }
1257d36db35SAvi Kivity 
verify_disabled_apic_mmio(void)126e38858bcSJim Mattson static void verify_disabled_apic_mmio(void)
127e38858bcSJim Mattson {
128e38858bcSJim Mattson 	volatile u32 *lvr = (volatile u32 *)(APIC_DEFAULT_PHYS_BASE + APIC_LVR);
129e38858bcSJim Mattson 	volatile u32 *tpr = (volatile u32 *)(APIC_DEFAULT_PHYS_BASE + APIC_TASKPRI);
130e38858bcSJim Mattson 	u32 cr8 = read_cr8();
131e38858bcSJim Mattson 
132e38858bcSJim Mattson 	memset((void *)APIC_DEFAULT_PHYS_BASE, 0xff, PAGE_SIZE);
133a299895bSThomas Huth 	report(*lvr == ~0, "*0xfee00030: %x", *lvr);
134a299895bSThomas Huth 	report(read_cr8() == cr8, "CR8: %lx", read_cr8());
135e38858bcSJim Mattson 	write_cr8(cr8 ^ MAX_TPR);
136a299895bSThomas Huth 	report(read_cr8() == (cr8 ^ MAX_TPR), "CR8: %lx", read_cr8());
137a299895bSThomas Huth 	report(*tpr == ~0, "*0xfee00080: %x", *tpr);
138e38858bcSJim Mattson 	write_cr8(cr8);
139e38858bcSJim Mattson }
140e38858bcSJim Mattson 
test_apic_disable(void)141c3ccca3fSJim Mattson static void test_apic_disable(void)
142c3ccca3fSJim Mattson {
143e38858bcSJim Mattson 	volatile u32 *lvr = (volatile u32 *)(APIC_DEFAULT_PHYS_BASE + APIC_LVR);
144e38858bcSJim Mattson 	volatile u32 *tpr = (volatile u32 *)(APIC_DEFAULT_PHYS_BASE + APIC_TASKPRI);
145e38858bcSJim Mattson 	u32 apic_version = apic_read(APIC_LVR);
146e38858bcSJim Mattson 	u32 cr8 = read_cr8();
147c3ccca3fSJim Mattson 
148c3ccca3fSJim Mattson 	report_prefix_push("apic_disable");
149c3ccca3fSJim Mattson 
150e38858bcSJim Mattson 	disable_apic();
1510d329639SSean Christopherson 	report(!is_apic_hw_enabled(), "Local apic disabled");
152a299895bSThomas Huth 	report(!this_cpu_has(X86_FEATURE_APIC),
153a299895bSThomas Huth 	       "CPUID.1H:EDX.APIC[bit 9] is clear");
154e38858bcSJim Mattson 	verify_disabled_apic_mmio();
155c3ccca3fSJim Mattson 
156e38858bcSJim Mattson 	reset_apic();
1570d329639SSean Christopherson 	report(is_xapic_enabled(), "Local apic enabled in xAPIC mode");
158a299895bSThomas Huth 	report(this_cpu_has(X86_FEATURE_APIC), "CPUID.1H:EDX.APIC[bit 9] is set");
159a299895bSThomas Huth 	report(*lvr == apic_version, "*0xfee00030: %x", *lvr);
160a299895bSThomas Huth 	report(*tpr == cr8, "*0xfee00080: %x", *tpr);
161e38858bcSJim Mattson 	write_cr8(cr8 ^ MAX_TPR);
162a299895bSThomas Huth 	report(*tpr == (cr8 ^ MAX_TPR) << 4, "*0xfee00080: %x", *tpr);
163e38858bcSJim Mattson 	write_cr8(cr8);
164c3ccca3fSJim Mattson 
165e38858bcSJim Mattson 	if (enable_x2apic()) {
1660d329639SSean Christopherson 		report(is_x2apic_enabled(), "Local apic enabled in x2APIC mode");
167a299895bSThomas Huth 		report(this_cpu_has(X86_FEATURE_APIC),
168a299895bSThomas Huth 		       "CPUID.1H:EDX.APIC[bit 9] is set");
169e38858bcSJim Mattson 		verify_disabled_apic_mmio();
170e38858bcSJim Mattson 	}
171c3ccca3fSJim Mattson 	report_prefix_pop();
172c3ccca3fSJim Mattson }
173c3ccca3fSJim Mattson 
174615a8838SNadav Amit #define ALTERNATE_APIC_BASE	0xfed40000
1759b6bdb3fSJan Kiszka 
test_apicbase(void)1769b6bdb3fSJan Kiszka static void test_apicbase(void)
1779b6bdb3fSJan Kiszka {
1789b6bdb3fSJan Kiszka 	u64 orig_apicbase = rdmsr(MSR_IA32_APICBASE);
1799b6bdb3fSJan Kiszka 	u32 lvr = apic_read(APIC_LVR);
1809b6bdb3fSJan Kiszka 	u64 value;
1819b6bdb3fSJan Kiszka 
1829b6bdb3fSJan Kiszka 	wrmsr(MSR_IA32_APICBASE, orig_apicbase & ~(APIC_EN | APIC_EXTD));
1839b6bdb3fSJan Kiszka 	wrmsr(MSR_IA32_APICBASE, ALTERNATE_APIC_BASE | APIC_BSP | APIC_EN);
1849b6bdb3fSJan Kiszka 
1855bba1769SAndrew Jones 	report_prefix_push("apicbase");
1865bba1769SAndrew Jones 
187a299895bSThomas Huth 	report(*(volatile u32 *)(ALTERNATE_APIC_BASE + APIC_LVR) == lvr,
188a299895bSThomas Huth 	       "relocate apic");
1899b6bdb3fSJan Kiszka 
190772befb7SEduardo Habkost 	value = orig_apicbase | (1UL << cpuid_maxphyaddr());
191a299895bSThomas Huth 	report(test_for_exception(GP_VECTOR, do_write_apicbase, &value),
192a299895bSThomas Huth 	       "reserved physaddr bits");
1939b6bdb3fSJan Kiszka 
1949b6bdb3fSJan Kiszka 	value = orig_apicbase | 1;
195a299895bSThomas Huth 	report(test_for_exception(GP_VECTOR, do_write_apicbase, &value),
196a299895bSThomas Huth 	       "reserved low bits");
1979b6bdb3fSJan Kiszka 
1986dc73196SSean Christopherson 	/* Restore the APIC address, the "reset" helpers leave it as is. */
1999b6bdb3fSJan Kiszka 	wrmsr(MSR_IA32_APICBASE, orig_apicbase);
2005bba1769SAndrew Jones 
2015bba1769SAndrew Jones 	report_prefix_pop();
2029b6bdb3fSJan Kiszka }
2039b6bdb3fSJan Kiszka 
do_write_apic_id(void * id)204a222b5e2SRadim Krčmář static void do_write_apic_id(void *id)
205a222b5e2SRadim Krčmář {
206a222b5e2SRadim Krčmář 	apic_write(APIC_ID, *(u32 *)id);
207a222b5e2SRadim Krčmář }
208a222b5e2SRadim Krčmář 
__test_apic_id(void * unused)209a222b5e2SRadim Krčmář static void __test_apic_id(void * unused)
210a222b5e2SRadim Krčmář {
211a222b5e2SRadim Krčmář 	u32 id, newid;
212a222b5e2SRadim Krčmář 	u8  initial_xapic_id = cpuid(1).b >> 24;
213a222b5e2SRadim Krčmář 	u32 initial_x2apic_id = cpuid(0xb).d;
2140d329639SSean Christopherson 	bool x2apic_mode = is_x2apic_enabled();
215a222b5e2SRadim Krčmář 
216a222b5e2SRadim Krčmář 	if (x2apic_mode)
217a222b5e2SRadim Krčmář 		reset_apic();
218a222b5e2SRadim Krčmář 
219a222b5e2SRadim Krčmář 	id = apic_id();
220a299895bSThomas Huth 	report(initial_xapic_id == id, "xapic id matches cpuid");
221a222b5e2SRadim Krčmář 
222a222b5e2SRadim Krčmář 	newid = (id + 1) << 24;
223a299895bSThomas Huth 	report(!test_for_exception(GP_VECTOR, do_write_apic_id, &newid) &&
224a299895bSThomas Huth 	       (id == apic_id() || id + 1 == apic_id()),
225a299895bSThomas Huth 	       "writeable xapic id");
226a222b5e2SRadim Krčmář 
227a222b5e2SRadim Krčmář 	if (!enable_x2apic())
228a222b5e2SRadim Krčmář 		goto out;
229a222b5e2SRadim Krčmář 
230a299895bSThomas Huth 	report(test_for_exception(GP_VECTOR, do_write_apic_id, &newid),
231a299895bSThomas Huth 	       "non-writeable x2apic id");
232a299895bSThomas Huth 	report(initial_xapic_id == (apic_id() & 0xff), "sane x2apic id");
233a222b5e2SRadim Krčmář 
234a222b5e2SRadim Krčmář 	/* old QEMUs do not set initial x2APIC ID */
235a299895bSThomas Huth 	report(initial_xapic_id == (initial_x2apic_id & 0xff) &&
236a299895bSThomas Huth 	       initial_x2apic_id == apic_id(),
237a299895bSThomas Huth 	       "x2apic id matches cpuid");
238a222b5e2SRadim Krčmář 
239a222b5e2SRadim Krčmář out:
240a222b5e2SRadim Krčmář 	reset_apic();
241a222b5e2SRadim Krčmář 
242a299895bSThomas Huth 	report(initial_xapic_id == apic_id(), "correct xapic id after reset");
243a222b5e2SRadim Krčmář 
244a222b5e2SRadim Krčmář 	/* old KVMs do not reset xAPIC ID */
245a222b5e2SRadim Krčmář 	if (id != apic_id())
246a222b5e2SRadim Krčmář 		apic_write(APIC_ID, id << 24);
247a222b5e2SRadim Krčmář 
248a222b5e2SRadim Krčmář 	if (x2apic_mode)
249a222b5e2SRadim Krčmář 		enable_x2apic();
250a222b5e2SRadim Krčmář }
251a222b5e2SRadim Krčmář 
test_apic_id(void)252a222b5e2SRadim Krčmář static void test_apic_id(void)
253a222b5e2SRadim Krčmář {
254a222b5e2SRadim Krčmář 	if (cpu_count() < 2)
255a222b5e2SRadim Krčmář 		return;
256a222b5e2SRadim Krčmář 
257a222b5e2SRadim Krčmář 	on_cpu(1, __test_apic_id, NULL);
258a222b5e2SRadim Krčmář }
259a222b5e2SRadim Krčmář 
260d8f62a83SSean Christopherson static atomic_t ipi_count;
2617d36db35SAvi Kivity 
handle_ipi(isr_regs_t * regs)262d8f62a83SSean Christopherson static void handle_ipi(isr_regs_t *regs)
2637d36db35SAvi Kivity {
264d8f62a83SSean Christopherson 	atomic_inc(&ipi_count);
2657d36db35SAvi Kivity 	eoi();
2667d36db35SAvi Kivity }
2677d36db35SAvi Kivity 
__test_self_ipi(void)268685d5f62SRicardo Koller static void __test_self_ipi(void)
2697d36db35SAvi Kivity {
2706c0999f4SNadav Amit 	u64 start = rdtsc();
2717d36db35SAvi Kivity 	int vec = 0xf1;
2727d36db35SAvi Kivity 
273d8f62a83SSean Christopherson 	handle_irq(vec, handle_ipi);
2747d36db35SAvi Kivity 	apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec,
27518a34cceSNadav Amit 		       id_map[0]);
2766c0999f4SNadav Amit 
2776c0999f4SNadav Amit 	do {
2786c0999f4SNadav Amit 		pause();
279d8f62a83SSean Christopherson 	} while (rdtsc() - start < 1000000000 && atomic_read(&ipi_count) == 0);
280685d5f62SRicardo Koller }
2816c0999f4SNadav Amit 
test_self_ipi_xapic(void)282685d5f62SRicardo Koller static void test_self_ipi_xapic(void)
283685d5f62SRicardo Koller {
284685d5f62SRicardo Koller 	report_prefix_push("self_ipi_xapic");
285685d5f62SRicardo Koller 
286685d5f62SRicardo Koller 	/* Reset to xAPIC mode. */
287685d5f62SRicardo Koller 	reset_apic();
2880d329639SSean Christopherson 	report(is_xapic_enabled(), "Local apic enabled in xAPIC mode");
289685d5f62SRicardo Koller 
290d8f62a83SSean Christopherson 	atomic_set(&ipi_count, 0);
291685d5f62SRicardo Koller 	__test_self_ipi();
292d8f62a83SSean Christopherson 	report(atomic_read(&ipi_count) == 1, "self ipi");
293685d5f62SRicardo Koller 
294685d5f62SRicardo Koller 	report_prefix_pop();
295685d5f62SRicardo Koller }
296685d5f62SRicardo Koller 
test_self_ipi_x2apic(void)297685d5f62SRicardo Koller static void test_self_ipi_x2apic(void)
298685d5f62SRicardo Koller {
299685d5f62SRicardo Koller 	report_prefix_push("self_ipi_x2apic");
300685d5f62SRicardo Koller 
301685d5f62SRicardo Koller 	if (enable_x2apic()) {
3020d329639SSean Christopherson 		report(is_x2apic_enabled(), "Local apic enabled in x2APIC mode");
303685d5f62SRicardo Koller 
304d8f62a83SSean Christopherson 		atomic_set(&ipi_count, 0);
305685d5f62SRicardo Koller 		__test_self_ipi();
306d8f62a83SSean Christopherson 		report(atomic_read(&ipi_count) == 1, "self ipi");
307685d5f62SRicardo Koller 	} else {
308685d5f62SRicardo Koller 		report_skip("x2apic not detected");
309685d5f62SRicardo Koller 	}
310685d5f62SRicardo Koller 
311685d5f62SRicardo Koller 	report_prefix_pop();
3127d36db35SAvi Kivity }
3137d36db35SAvi Kivity 
314f2d2b7c7SAvi Kivity volatile int nmi_counter_private, nmi_counter, nmi_hlt_counter, sti_loop_active;
315f2d2b7c7SAvi Kivity 
test_sti_nop(char * p)316*e4007e62SMaxim Levitsky static void test_sti_nop(char *p)
317f2d2b7c7SAvi Kivity {
318f2d2b7c7SAvi Kivity 	asm volatile (
319f2d2b7c7SAvi Kivity 		  ".globl post_sti \n\t"
320f2d2b7c7SAvi Kivity 		  "sti \n"
321f2d2b7c7SAvi Kivity 		  /*
322f2d2b7c7SAvi Kivity 		   * vmx won't exit on external interrupt if blocked-by-sti,
323f2d2b7c7SAvi Kivity 		   * so give it a reason to exit by accessing an unmapped page.
324f2d2b7c7SAvi Kivity 		   */
325f2d2b7c7SAvi Kivity 		  "post_sti: testb $0, %0 \n\t"
326f2d2b7c7SAvi Kivity 		  "nop \n\t"
327f2d2b7c7SAvi Kivity 		  "cli"
328f2d2b7c7SAvi Kivity 		  : : "m"(*p)
329f2d2b7c7SAvi Kivity 		  );
330f2d2b7c7SAvi Kivity 	nmi_counter = nmi_counter_private;
331f2d2b7c7SAvi Kivity }
332f2d2b7c7SAvi Kivity 
sti_loop(void * ignore)333f2d2b7c7SAvi Kivity static void sti_loop(void *ignore)
334f2d2b7c7SAvi Kivity {
335f2d2b7c7SAvi Kivity 	unsigned k = 0;
336f2d2b7c7SAvi Kivity 
33727cc2e49SSean Christopherson 	while (sti_loop_active)
338*e4007e62SMaxim Levitsky 		test_sti_nop((char *)(ulong)((k++ * 4096) % (128 * 1024 * 1024)));
339f2d2b7c7SAvi Kivity }
340f2d2b7c7SAvi Kivity 
nmi_handler(isr_regs_t * regs)341f2d2b7c7SAvi Kivity static void nmi_handler(isr_regs_t *regs)
342f2d2b7c7SAvi Kivity {
343f2d2b7c7SAvi Kivity 	extern void post_sti(void);
344f2d2b7c7SAvi Kivity 	++nmi_counter_private;
345f2d2b7c7SAvi Kivity 	nmi_hlt_counter += regs->rip == (ulong)post_sti;
346f2d2b7c7SAvi Kivity }
347f2d2b7c7SAvi Kivity 
test_sti_nmi(void)348f2d2b7c7SAvi Kivity static void test_sti_nmi(void)
349f2d2b7c7SAvi Kivity {
350f2d2b7c7SAvi Kivity 	unsigned old_counter;
351f2d2b7c7SAvi Kivity 
35227cc2e49SSean Christopherson 	if (cpu_count() < 2)
353f2d2b7c7SAvi Kivity 		return;
354f2d2b7c7SAvi Kivity 
355d51bd17eSGleb Natapov 	handle_irq(2, nmi_handler);
356f2d2b7c7SAvi Kivity 	on_cpu(1, update_cr3, (void *)read_cr3());
357f2d2b7c7SAvi Kivity 
358f2d2b7c7SAvi Kivity 	sti_loop_active = 1;
359f2d2b7c7SAvi Kivity 	on_cpu_async(1, sti_loop, 0);
360f2d2b7c7SAvi Kivity 	while (nmi_counter < 30000) {
361f2d2b7c7SAvi Kivity 		old_counter = nmi_counter;
36218a34cceSNadav Amit 		apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, id_map[1]);
36327cc2e49SSean Christopherson 		while (nmi_counter == old_counter)
364f2d2b7c7SAvi Kivity 			;
365f2d2b7c7SAvi Kivity 	}
366f2d2b7c7SAvi Kivity 	sti_loop_active = 0;
367a299895bSThomas Huth 	report(nmi_hlt_counter == 0, "nmi-after-sti");
368f2d2b7c7SAvi Kivity }
369f2d2b7c7SAvi Kivity 
370173e7eacSAvi Kivity static volatile bool nmi_done, nmi_flushed;
371173e7eacSAvi Kivity static volatile int nmi_received;
372173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr1, cpu1_nmi_ctr1;
373173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr2, cpu1_nmi_ctr2;
374173e7eacSAvi Kivity 
multiple_nmi_handler(isr_regs_t * regs)375173e7eacSAvi Kivity static void multiple_nmi_handler(isr_regs_t *regs)
376173e7eacSAvi Kivity {
377173e7eacSAvi Kivity 	++nmi_received;
378173e7eacSAvi Kivity }
379173e7eacSAvi Kivity 
kick_me_nmi(void * blah)380173e7eacSAvi Kivity static void kick_me_nmi(void *blah)
381173e7eacSAvi Kivity {
382173e7eacSAvi Kivity 	while (!nmi_done) {
383173e7eacSAvi Kivity 		++cpu1_nmi_ctr1;
38427cc2e49SSean Christopherson 		while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1 && !nmi_done)
385173e7eacSAvi Kivity 			pause();
38627cc2e49SSean Christopherson 
38727cc2e49SSean Christopherson 		if (nmi_done)
388173e7eacSAvi Kivity 			return;
38927cc2e49SSean Christopherson 
39018a34cceSNadav Amit 		apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, id_map[0]);
391173e7eacSAvi Kivity 		/* make sure the NMI has arrived by sending an IPI after it */
392173e7eacSAvi Kivity 		apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT
39318a34cceSNadav Amit 				| 0x44, id_map[0]);
394173e7eacSAvi Kivity 		++cpu1_nmi_ctr2;
39527cc2e49SSean Christopherson 		while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2 && !nmi_done)
396173e7eacSAvi Kivity 			pause();
397173e7eacSAvi Kivity 	}
398173e7eacSAvi Kivity }
399173e7eacSAvi Kivity 
flush_nmi(isr_regs_t * regs)400173e7eacSAvi Kivity static void flush_nmi(isr_regs_t *regs)
401173e7eacSAvi Kivity {
402173e7eacSAvi Kivity 	nmi_flushed = true;
403173e7eacSAvi Kivity 	apic_write(APIC_EOI, 0);
404173e7eacSAvi Kivity }
405173e7eacSAvi Kivity 
test_multiple_nmi(void)406173e7eacSAvi Kivity static void test_multiple_nmi(void)
407173e7eacSAvi Kivity {
408173e7eacSAvi Kivity 	int i;
409173e7eacSAvi Kivity 	bool ok = true;
410173e7eacSAvi Kivity 
41127cc2e49SSean Christopherson 	if (cpu_count() < 2)
412173e7eacSAvi Kivity 		return;
413173e7eacSAvi Kivity 
414173e7eacSAvi Kivity 	sti();
415173e7eacSAvi Kivity 	handle_irq(2, multiple_nmi_handler);
416173e7eacSAvi Kivity 	handle_irq(0x44, flush_nmi);
417173e7eacSAvi Kivity 	on_cpu_async(1, kick_me_nmi, 0);
418a7f60697SPaolo Bonzini 	for (i = 0; i < 100000; ++i) {
419173e7eacSAvi Kivity 		nmi_flushed = false;
420173e7eacSAvi Kivity 		nmi_received = 0;
421173e7eacSAvi Kivity 		++cpu0_nmi_ctr1;
42227cc2e49SSean Christopherson 		while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1)
423173e7eacSAvi Kivity 			pause();
42427cc2e49SSean Christopherson 
42518a34cceSNadav Amit 		apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, id_map[0]);
42627cc2e49SSean Christopherson 		while (!nmi_flushed)
427173e7eacSAvi Kivity 			pause();
42827cc2e49SSean Christopherson 
429173e7eacSAvi Kivity 		if (nmi_received != 2) {
430173e7eacSAvi Kivity 			ok = false;
431173e7eacSAvi Kivity 			break;
432173e7eacSAvi Kivity 		}
43327cc2e49SSean Christopherson 
434173e7eacSAvi Kivity 		++cpu0_nmi_ctr2;
43527cc2e49SSean Christopherson 		while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2)
436173e7eacSAvi Kivity 			pause();
437173e7eacSAvi Kivity 	}
438173e7eacSAvi Kivity 	nmi_done = true;
439a299895bSThomas Huth 	report(ok, "multiple nmi");
440173e7eacSAvi Kivity }
441173e7eacSAvi Kivity 
pending_nmi_handler(isr_regs_t * regs)4429f23b246SSean Christopherson static void pending_nmi_handler(isr_regs_t *regs)
4439f23b246SSean Christopherson {
4449f23b246SSean Christopherson 	int i;
4459f23b246SSean Christopherson 
4469f23b246SSean Christopherson 	if (++nmi_received == 1) {
4479f23b246SSean Christopherson 		for (i = 0; i < 10; ++i)
4489f23b246SSean Christopherson 			apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI, 0);
4499f23b246SSean Christopherson 	}
4509f23b246SSean Christopherson }
4519f23b246SSean Christopherson 
test_pending_nmi(void)4529f23b246SSean Christopherson static void test_pending_nmi(void)
4539f23b246SSean Christopherson {
4549f23b246SSean Christopherson 	int i;
4559f23b246SSean Christopherson 
4569f23b246SSean Christopherson 	handle_irq(2, pending_nmi_handler);
4579f23b246SSean Christopherson 	for (i = 0; i < 100000; ++i) {
4589f23b246SSean Christopherson 		nmi_received = 0;
4599f23b246SSean Christopherson 
4609f23b246SSean Christopherson 		apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI, 0);
4619f23b246SSean Christopherson 		while (nmi_received < 2)
4629f23b246SSean Christopherson 			pause();
4639f23b246SSean Christopherson 
4649f23b246SSean Christopherson 		if (nmi_received != 2)
4659f23b246SSean Christopherson 			break;
4669f23b246SSean Christopherson 	}
467a299895bSThomas Huth 	report(nmi_received == 2, "pending nmi");
4689f23b246SSean Christopherson }
4699f23b246SSean Christopherson 
4709f815b29SPeter Xu static volatile int lvtt_counter = 0;
4719f815b29SPeter Xu 
lvtt_handler(isr_regs_t * regs)4729f815b29SPeter Xu static void lvtt_handler(isr_regs_t *regs)
4739f815b29SPeter Xu {
4749f815b29SPeter Xu 	lvtt_counter++;
4759f815b29SPeter Xu 	eoi();
4769f815b29SPeter Xu }
4779f815b29SPeter Xu 
test_apic_timer_one_shot(void)4789f815b29SPeter Xu static void test_apic_timer_one_shot(void)
4799f815b29SPeter Xu {
4809f815b29SPeter Xu 	uint64_t tsc1, tsc2;
4819f815b29SPeter Xu 	static const uint32_t interval = 0x10000;
4829f815b29SPeter Xu 
4839f815b29SPeter Xu #define APIC_LVT_TIMER_VECTOR    (0xee)
4849f815b29SPeter Xu 
4859f815b29SPeter Xu 	handle_irq(APIC_LVT_TIMER_VECTOR, lvtt_handler);
4869f815b29SPeter Xu 
4879f815b29SPeter Xu 	/* One shot mode */
4889111ccabSRadim Krčmář 	apic_write(APIC_LVTT, APIC_LVT_TIMER_ONESHOT |
4899f815b29SPeter Xu 		   APIC_LVT_TIMER_VECTOR);
4909f815b29SPeter Xu 	/* Divider == 1 */
4919f815b29SPeter Xu 	apic_write(APIC_TDCR, 0x0000000b);
4929f815b29SPeter Xu 
4939f815b29SPeter Xu 	tsc1 = rdtsc();
4949f815b29SPeter Xu 	/* Set "Initial Counter Register", which starts the timer */
4959f815b29SPeter Xu 	apic_write(APIC_TMICT, interval);
4969f815b29SPeter Xu 	while (!lvtt_counter);
4979f815b29SPeter Xu 	tsc2 = rdtsc();
4989f815b29SPeter Xu 
4999f815b29SPeter Xu 	/*
5009f815b29SPeter Xu 	 * For LVT Timer clock, SDM vol 3 10.5.4 says it should be
5019f815b29SPeter Xu 	 * derived from processor's bus clock (IIUC which is the same
5029f815b29SPeter Xu 	 * as TSC), however QEMU seems to be using nanosecond. In all
5039f815b29SPeter Xu 	 * cases, the following should satisfy on all modern
5049f815b29SPeter Xu 	 * processors.
5059f815b29SPeter Xu 	 */
506a299895bSThomas Huth 	report((lvtt_counter == 1) && (tsc2 - tsc1 >= interval),
507a299895bSThomas Huth 	       "APIC LVT timer one shot");
5089f815b29SPeter Xu }
5099f815b29SPeter Xu 
5109931b88cSRadim Krčmář static atomic_t broadcast_counter;
5119931b88cSRadim Krčmář 
broadcast_handler(isr_regs_t * regs)5129931b88cSRadim Krčmář static void broadcast_handler(isr_regs_t *regs)
5139931b88cSRadim Krčmář {
5149931b88cSRadim Krčmář 	atomic_inc(&broadcast_counter);
5159931b88cSRadim Krčmář 	eoi();
5169931b88cSRadim Krčmář }
5179931b88cSRadim Krčmář 
broadcast_received(unsigned ncpus)5189931b88cSRadim Krčmář static bool broadcast_received(unsigned ncpus)
5199931b88cSRadim Krčmář {
5209931b88cSRadim Krčmář 	unsigned counter;
5219931b88cSRadim Krčmář 	u64 start = rdtsc();
5229931b88cSRadim Krčmář 
5239931b88cSRadim Krčmář 	do {
5249931b88cSRadim Krčmář 		counter = atomic_read(&broadcast_counter);
5259931b88cSRadim Krčmář 		if (counter >= ncpus)
5269931b88cSRadim Krčmář 			break;
5279931b88cSRadim Krčmář 		pause();
5289931b88cSRadim Krčmář 	} while (rdtsc() - start < 1000000000);
5299931b88cSRadim Krčmář 
5309931b88cSRadim Krčmář 	atomic_set(&broadcast_counter, 0);
5319931b88cSRadim Krčmář 
5329931b88cSRadim Krčmář 	return counter == ncpus;
5339931b88cSRadim Krčmář }
5349931b88cSRadim Krčmář 
test_physical_broadcast(void)5359931b88cSRadim Krčmář static void test_physical_broadcast(void)
5369931b88cSRadim Krčmář {
5379931b88cSRadim Krčmář 	unsigned ncpus = cpu_count();
5389931b88cSRadim Krčmář 	unsigned long cr3 = read_cr3();
5399931b88cSRadim Krčmář 	u32 broadcast_address = enable_x2apic() ? 0xffffffff : 0xff;
5409931b88cSRadim Krčmář 
5419931b88cSRadim Krčmář 	handle_irq(BROADCAST_VECTOR, broadcast_handler);
5429931b88cSRadim Krčmář 	for (int c = 1; c < ncpus; c++)
5439931b88cSRadim Krčmář 		on_cpu(c, update_cr3, (void *)cr3);
5449931b88cSRadim Krčmář 
5459931b88cSRadim Krčmář 	printf("starting broadcast (%s)\n", enable_x2apic() ? "x2apic" : "xapic");
5469931b88cSRadim Krčmář 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT |
5479931b88cSRadim Krčmář 		       BROADCAST_VECTOR, broadcast_address);
548a299895bSThomas Huth 	report(broadcast_received(ncpus), "APIC physical broadcast address");
5499931b88cSRadim Krčmář 
5509931b88cSRadim Krčmář 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT |
5519931b88cSRadim Krčmář 		       BROADCAST_VECTOR | APIC_DEST_ALLINC, 0);
552a299895bSThomas Huth 	report(broadcast_received(ncpus), "APIC physical broadcast shorthand");
5539931b88cSRadim Krčmář }
5549931b88cSRadim Krčmář 
wait_until_tmcct_common(uint32_t initial_count,bool stop_when_half,bool should_wrap_around)5550eac5394SEvgeny Yakovlev static void wait_until_tmcct_common(uint32_t initial_count, bool stop_when_half, bool should_wrap_around)
556d9b2b283SWanpeng Li {
557d9b2b283SWanpeng Li 	uint32_t tmcct = apic_read(APIC_TMCCT);
558d9b2b283SWanpeng Li 
559d9b2b283SWanpeng Li 	if (tmcct) {
560d9b2b283SWanpeng Li 		while (tmcct > (initial_count / 2))
561d9b2b283SWanpeng Li 			tmcct = apic_read(APIC_TMCCT);
562d9b2b283SWanpeng Li 
563d9b2b283SWanpeng Li 		if ( stop_when_half )
564d9b2b283SWanpeng Li 			return;
565d9b2b283SWanpeng Li 
566d9b2b283SWanpeng Li 		/* Wait until the counter reach 0 or wrap-around */
567d9b2b283SWanpeng Li 		while ( tmcct <= (initial_count / 2) && tmcct > 0 )
568d9b2b283SWanpeng Li 			tmcct = apic_read(APIC_TMCCT);
5690eac5394SEvgeny Yakovlev 
5700eac5394SEvgeny Yakovlev 		/* Wait specifically for wrap around to skip 0 TMCCR if we were asked to */
5710eac5394SEvgeny Yakovlev 		while (should_wrap_around && !tmcct)
5720eac5394SEvgeny Yakovlev 			tmcct = apic_read(APIC_TMCCT);
573d9b2b283SWanpeng Li 	}
574d9b2b283SWanpeng Li }
575d9b2b283SWanpeng Li 
wait_until_tmcct_is_zero(uint32_t initial_count,bool stop_when_half)5760eac5394SEvgeny Yakovlev static void wait_until_tmcct_is_zero(uint32_t initial_count, bool stop_when_half)
5770eac5394SEvgeny Yakovlev {
5780eac5394SEvgeny Yakovlev 	return wait_until_tmcct_common(initial_count, stop_when_half, false);
5790eac5394SEvgeny Yakovlev }
5800eac5394SEvgeny Yakovlev 
wait_until_tmcct_wrap_around(uint32_t initial_count,bool stop_when_half)5810eac5394SEvgeny Yakovlev static void wait_until_tmcct_wrap_around(uint32_t initial_count, bool stop_when_half)
5820eac5394SEvgeny Yakovlev {
5830eac5394SEvgeny Yakovlev 	return wait_until_tmcct_common(initial_count, stop_when_half, true);
5840eac5394SEvgeny Yakovlev }
5850eac5394SEvgeny Yakovlev 
apic_change_mode(unsigned long new_mode)586d9b2b283SWanpeng Li static inline void apic_change_mode(unsigned long new_mode)
587d9b2b283SWanpeng Li {
588d9b2b283SWanpeng Li 	uint32_t lvtt;
589d9b2b283SWanpeng Li 
590d9b2b283SWanpeng Li 	lvtt = apic_read(APIC_LVTT);
591d9b2b283SWanpeng Li 	apic_write(APIC_LVTT, (lvtt & ~APIC_LVT_TIMER_MASK) | new_mode);
592d9b2b283SWanpeng Li }
593d9b2b283SWanpeng Li 
test_apic_change_mode(void)5947db17e21SThomas Huth static void test_apic_change_mode(void)
595d9b2b283SWanpeng Li {
596d9b2b283SWanpeng Li 	uint32_t tmict = 0x999999;
597d9b2b283SWanpeng Li 
598d9b2b283SWanpeng Li 	printf("starting apic change mode\n");
599d9b2b283SWanpeng Li 
600d9b2b283SWanpeng Li 	apic_write(APIC_TMICT, tmict);
601d9b2b283SWanpeng Li 
602d9b2b283SWanpeng Li 	apic_change_mode(APIC_LVT_TIMER_PERIODIC);
603d9b2b283SWanpeng Li 
604a299895bSThomas Huth 	report(apic_read(APIC_TMICT) == tmict, "TMICT value reset");
605d9b2b283SWanpeng Li 
606d9b2b283SWanpeng Li 	/* Testing one-shot */
607d9b2b283SWanpeng Li 	apic_change_mode(APIC_LVT_TIMER_ONESHOT);
608d9b2b283SWanpeng Li 	apic_write(APIC_TMICT, tmict);
609a299895bSThomas Huth 	report(apic_read(APIC_TMCCT), "TMCCT should have a non-zero value");
610d9b2b283SWanpeng Li 
611d9b2b283SWanpeng Li 	wait_until_tmcct_is_zero(tmict, false);
612a299895bSThomas Huth 	report(!apic_read(APIC_TMCCT), "TMCCT should have reached 0");
613d9b2b283SWanpeng Li 
614d9b2b283SWanpeng Li 	/*
615d9b2b283SWanpeng Li 	 * Write TMICT before changing mode from one-shot to periodic TMCCT should
616d9b2b283SWanpeng Li 	 * be reset to TMICT periodicly
617d9b2b283SWanpeng Li 	 */
618d9b2b283SWanpeng Li 	apic_write(APIC_TMICT, tmict);
619d9b2b283SWanpeng Li 	wait_until_tmcct_is_zero(tmict, true);
620d9b2b283SWanpeng Li 	apic_change_mode(APIC_LVT_TIMER_PERIODIC);
621a299895bSThomas Huth 	report(apic_read(APIC_TMCCT), "TMCCT should have a non-zero value");
622d9b2b283SWanpeng Li 
623d9b2b283SWanpeng Li 	/*
624d9b2b283SWanpeng Li 	 * After the change of mode, the counter should not be reset and continue
625d9b2b283SWanpeng Li 	 * counting down from where it was
626d9b2b283SWanpeng Li 	 */
627a299895bSThomas Huth 	report(apic_read(APIC_TMCCT) < (tmict / 2),
628a299895bSThomas Huth 	       "TMCCT should not be reset to TMICT value");
6290eac5394SEvgeny Yakovlev 	/*
6300eac5394SEvgeny Yakovlev 	 * Specifically wait for timer wrap around and skip 0.
6310eac5394SEvgeny Yakovlev 	 * Under KVM lapic there is a possibility that a small amount of consecutive
6320eac5394SEvgeny Yakovlev 	 * TMCCR reads return 0 while hrtimer is reset in an async callback
6330eac5394SEvgeny Yakovlev 	 */
6340eac5394SEvgeny Yakovlev 	wait_until_tmcct_wrap_around(tmict, false);
635a299895bSThomas Huth 	report(apic_read(APIC_TMCCT) > (tmict / 2),
636a299895bSThomas Huth 	       "TMCCT should be reset to the initial-count");
637d9b2b283SWanpeng Li 
638d9b2b283SWanpeng Li 	wait_until_tmcct_is_zero(tmict, true);
639d9b2b283SWanpeng Li 	/*
640d9b2b283SWanpeng Li 	 * Keep the same TMICT and change timer mode to one-shot
641d9b2b283SWanpeng Li 	 * TMCCT should be > 0 and count-down to 0
642d9b2b283SWanpeng Li 	 */
643d9b2b283SWanpeng Li 	apic_change_mode(APIC_LVT_TIMER_ONESHOT);
644a299895bSThomas Huth 	report(apic_read(APIC_TMCCT) < (tmict / 2),
645a299895bSThomas Huth 	       "TMCCT should not be reset to init");
646d9b2b283SWanpeng Li 	wait_until_tmcct_is_zero(tmict, false);
647a299895bSThomas Huth 	report(!apic_read(APIC_TMCCT), "TMCCT should have reach zero");
648d9b2b283SWanpeng Li 
649d9b2b283SWanpeng Li 	/* now tmcct == 0 and tmict != 0 */
650d9b2b283SWanpeng Li 	apic_change_mode(APIC_LVT_TIMER_PERIODIC);
651a299895bSThomas Huth 	report(!apic_read(APIC_TMCCT), "TMCCT should stay at zero");
652d9b2b283SWanpeng Li }
653d9b2b283SWanpeng Li 
654de8d3fccSWanpeng Li #define KVM_HC_SEND_IPI 10
655de8d3fccSWanpeng Li 
test_pv_ipi(void)656de8d3fccSWanpeng Li static void test_pv_ipi(void)
657de8d3fccSWanpeng Li {
658de8d3fccSWanpeng Li 	int ret;
659de8d3fccSWanpeng Li 	unsigned long a0 = 0xFFFFFFFF, a1 = 0, a2 = 0xFFFFFFFF, a3 = 0x0;
660de8d3fccSWanpeng Li 
6616dc73196SSean Christopherson 	if (!test_device_enabled())
6626dc73196SSean Christopherson 		return;
6636dc73196SSean Christopherson 
664de8d3fccSWanpeng Li 	asm volatile("vmcall" : "=a"(ret) :"a"(KVM_HC_SEND_IPI), "b"(a0), "c"(a1), "d"(a2), "S"(a3));
665a299895bSThomas Huth 	report(!ret, "PV IPIs testing");
666de8d3fccSWanpeng Li }
667de8d3fccSWanpeng Li 
668d8f62a83SSean Christopherson #define APIC_LDR_CLUSTER_FLAG	BIT(31)
669d8f62a83SSean Christopherson 
set_ldr(void * __ldr)670d8f62a83SSean Christopherson static void set_ldr(void *__ldr)
671d8f62a83SSean Christopherson {
672d8f62a83SSean Christopherson 	u32 ldr = (unsigned long)__ldr;
673d8f62a83SSean Christopherson 
674d8f62a83SSean Christopherson 	if (ldr & APIC_LDR_CLUSTER_FLAG)
675d8f62a83SSean Christopherson 		apic_write(APIC_DFR, APIC_DFR_CLUSTER);
676d8f62a83SSean Christopherson 	else
677d8f62a83SSean Christopherson 		apic_write(APIC_DFR, APIC_DFR_FLAT);
678d8f62a83SSean Christopherson 
679d8f62a83SSean Christopherson 	apic_write(APIC_LDR, ldr << 24);
680d8f62a83SSean Christopherson }
681d8f62a83SSean Christopherson 
test_fixed_ipi(u32 dest_mode,u8 dest,u8 vector,int nr_ipis_expected,const char * mode_name)682d8f62a83SSean Christopherson static int test_fixed_ipi(u32 dest_mode, u8 dest, u8 vector,
683d8f62a83SSean Christopherson 			  int nr_ipis_expected, const char *mode_name)
684d8f62a83SSean Christopherson {
685d8f62a83SSean Christopherson 	u64 start = rdtsc();
686d8f62a83SSean Christopherson 	int got;
687d8f62a83SSean Christopherson 
688d8f62a83SSean Christopherson 	atomic_set(&ipi_count, 0);
689d8f62a83SSean Christopherson 
690d8f62a83SSean Christopherson 	/*
691d8f62a83SSean Christopherson 	 * Wait for vCPU1 to get back into HLT, i.e. into the host so that
692d8f62a83SSean Christopherson 	 * KVM must handle incomplete AVIC IPIs.
693d8f62a83SSean Christopherson 	 */
694d8f62a83SSean Christopherson 	do {
695d8f62a83SSean Christopherson 		pause();
696d8f62a83SSean Christopherson 	} while (rdtsc() - start < 1000000);
697d8f62a83SSean Christopherson 
698d8f62a83SSean Christopherson 	start = rdtsc();
699d8f62a83SSean Christopherson 
700d8f62a83SSean Christopherson 	apic_icr_write(dest_mode | APIC_DM_FIXED | vector, dest);
701d8f62a83SSean Christopherson 
702d8f62a83SSean Christopherson 	do {
703d8f62a83SSean Christopherson 		pause();
704d8f62a83SSean Christopherson 	} while (rdtsc() - start < 1000000000 &&
705d8f62a83SSean Christopherson 		 atomic_read(&ipi_count) != nr_ipis_expected);
706d8f62a83SSean Christopherson 
707d8f62a83SSean Christopherson 	/* Only report failures to cut down on the spam. */
708d8f62a83SSean Christopherson 	got = atomic_read(&ipi_count);
709d8f62a83SSean Christopherson 	if (got != nr_ipis_expected)
710d8f62a83SSean Christopherson 		report_fail("Want %d IPI(s) using %s mode, dest = %x, got %d IPI(s)",
711d8f62a83SSean Christopherson 			    nr_ipis_expected, mode_name, dest, got);
712d8f62a83SSean Christopherson 	atomic_set(&ipi_count, 0);
713d8f62a83SSean Christopherson 
714d8f62a83SSean Christopherson 	return got == nr_ipis_expected ? 0 : 1;
715d8f62a83SSean Christopherson }
716d8f62a83SSean Christopherson 
test_logical_ipi_single_target(u8 logical_id,bool cluster,u8 dest,u8 vector)717d8f62a83SSean Christopherson static int test_logical_ipi_single_target(u8 logical_id, bool cluster, u8 dest,
718d8f62a83SSean Christopherson 					  u8 vector)
719d8f62a83SSean Christopherson {
720d8f62a83SSean Christopherson 	/* Disallow broadcast, there are at least 2 vCPUs. */
721d8f62a83SSean Christopherson 	if (dest == 0xff)
722d8f62a83SSean Christopherson 		return 0;
723d8f62a83SSean Christopherson 
724d8f62a83SSean Christopherson 	set_ldr((void *)0);
725d8f62a83SSean Christopherson 	on_cpu(1, set_ldr,
726d8f62a83SSean Christopherson 	       (void *)((u32)logical_id | (cluster ? APIC_LDR_CLUSTER_FLAG : 0)));
727d8f62a83SSean Christopherson 	return test_fixed_ipi(APIC_DEST_LOGICAL, dest, vector, 1,
728d8f62a83SSean Christopherson 			      cluster ? "logical cluster" : "logical flat");
729d8f62a83SSean Christopherson }
730d8f62a83SSean Christopherson 
test_logical_ipi_multi_target(u8 vcpu0_logical_id,u8 vcpu1_logical_id,bool cluster,u8 dest,u8 vector)731d8f62a83SSean Christopherson static int test_logical_ipi_multi_target(u8 vcpu0_logical_id, u8 vcpu1_logical_id,
732d8f62a83SSean Christopherson 					 bool cluster, u8 dest, u8 vector)
733d8f62a83SSean Christopherson {
734d8f62a83SSean Christopherson 	/* Allow broadcast unless there are more than 2 vCPUs. */
735d8f62a83SSean Christopherson 	if (dest == 0xff && cpu_count() > 2)
736d8f62a83SSean Christopherson 		return 0;
737d8f62a83SSean Christopherson 
738d8f62a83SSean Christopherson 	set_ldr((void *)((u32)vcpu0_logical_id | (cluster ? APIC_LDR_CLUSTER_FLAG : 0)));
739d8f62a83SSean Christopherson 	on_cpu(1, set_ldr,
740d8f62a83SSean Christopherson 	       (void *)((u32)vcpu1_logical_id | (cluster ? APIC_LDR_CLUSTER_FLAG : 0)));
741d8f62a83SSean Christopherson 	return test_fixed_ipi(APIC_DEST_LOGICAL, dest, vector, 2,
742d8f62a83SSean Christopherson 			      cluster ? "logical cluster" : "logical flat");
743d8f62a83SSean Christopherson }
744d8f62a83SSean Christopherson 
test_logical_ipi_xapic(void)745d8f62a83SSean Christopherson static void test_logical_ipi_xapic(void)
746d8f62a83SSean Christopherson {
747d8f62a83SSean Christopherson 	int c, i, j, k, f;
748d8f62a83SSean Christopherson 	u8 vector = 0xf1;
749d8f62a83SSean Christopherson 
750d8f62a83SSean Christopherson 	if (cpu_count() < 2)
751d8f62a83SSean Christopherson 		return;
752d8f62a83SSean Christopherson 
753d8f62a83SSean Christopherson 	/*
754d8f62a83SSean Christopherson 	 * All vCPUs must be in xAPIC mode, i.e. simply resetting this vCPUs
755d8f62a83SSean Christopherson 	 * APIC is not sufficient.
756d8f62a83SSean Christopherson 	 */
757d8f62a83SSean Christopherson 	if (is_x2apic_enabled())
758d8f62a83SSean Christopherson 		return;
759d8f62a83SSean Christopherson 
760d8f62a83SSean Christopherson 	handle_irq(vector, handle_ipi);
761d8f62a83SSean Christopherson 
762d8f62a83SSean Christopherson 	/* Flat mode.  8 bits for logical IDs (one per bit). */
763d8f62a83SSean Christopherson 	f = 0;
764d8f62a83SSean Christopherson 	for (i = 0; i < 8; i++) {
765d8f62a83SSean Christopherson 		/*
766d8f62a83SSean Christopherson 		 * Test all possible destination values.  Non-existent targets
767d8f62a83SSean Christopherson 		 * should be ignored.  vCPU is always targeted, i.e. should get
768d8f62a83SSean Christopherson 		 * an IPI.
769d8f62a83SSean Christopherson 		 */
770d8f62a83SSean Christopherson 		for (k = 0; k < 0xff; k++) {
771d8f62a83SSean Christopherson 			/*
772d8f62a83SSean Christopherson 			 * Skip values that overlap the actual target the
773d8f62a83SSean Christopherson 			 * resulting combination will be covered by other
774d8f62a83SSean Christopherson 			 * numbers in the sequence.
775d8f62a83SSean Christopherson 			 */
776d8f62a83SSean Christopherson 			if (BIT(i) & k)
777d8f62a83SSean Christopherson 				continue;
778d8f62a83SSean Christopherson 
779d8f62a83SSean Christopherson 			f += test_logical_ipi_single_target(BIT(i), false,
780d8f62a83SSean Christopherson 							    BIT(i) | k, vector);
781d8f62a83SSean Christopherson 		}
782d8f62a83SSean Christopherson 	}
783d8f62a83SSean Christopherson 	report(!f, "IPI to single target using logical flat mode");
784d8f62a83SSean Christopherson 
785d8f62a83SSean Christopherson 	/* Cluster mode.  4 bits for the cluster, 4 bits for logical IDs. */
786d8f62a83SSean Christopherson 	f = 0;
787d8f62a83SSean Christopherson 	for (c = 0; c < 0xf; c++) {
788d8f62a83SSean Christopherson 		for (i = 0; i < 4; i++) {
789d8f62a83SSean Christopherson 			/* Same as above, just fewer bits... */
790d8f62a83SSean Christopherson 			for (k = 0; k < 0x10; k++) {
791d8f62a83SSean Christopherson 				if (BIT(i) & k)
792d8f62a83SSean Christopherson 					continue;
793d8f62a83SSean Christopherson 
794d8f62a83SSean Christopherson 				test_logical_ipi_single_target(c << 4 | BIT(i), true,
795d8f62a83SSean Christopherson 							       c << 4 | BIT(i) | k, vector);
796d8f62a83SSean Christopherson 			}
797d8f62a83SSean Christopherson 		}
798d8f62a83SSean Christopherson 	}
799d8f62a83SSean Christopherson 	report(!f, "IPI to single target using logical cluster mode");
800d8f62a83SSean Christopherson 
801d8f62a83SSean Christopherson 	/* And now do it all over again targeting both vCPU0 and vCPU1. */
802d8f62a83SSean Christopherson 	f = 0;
803d8f62a83SSean Christopherson 	for (i = 0; i < 8 && !f; i++) {
804d8f62a83SSean Christopherson 		for (j = 0; j < 8 && !f; j++) {
805d8f62a83SSean Christopherson 			if (i == j)
806d8f62a83SSean Christopherson 				continue;
807d8f62a83SSean Christopherson 
808d8f62a83SSean Christopherson 			for (k = 0; k < 0x100 && !f; k++) {
809d8f62a83SSean Christopherson 				if ((BIT(i) | BIT(j)) & k)
810d8f62a83SSean Christopherson 					continue;
811d8f62a83SSean Christopherson 
812d8f62a83SSean Christopherson 				f += test_logical_ipi_multi_target(BIT(i), BIT(j), false,
813d8f62a83SSean Christopherson 								   BIT(i) | BIT(j) | k, vector);
814d8f62a83SSean Christopherson 				if (f)
815d8f62a83SSean Christopherson 					break;
816d8f62a83SSean Christopherson 				f += test_logical_ipi_multi_target(BIT(i) | BIT(j),
817d8f62a83SSean Christopherson 								   BIT(i) | BIT(j), false,
818d8f62a83SSean Christopherson 								   BIT(i) | BIT(j) | k, vector);
819d8f62a83SSean Christopherson 			}
820d8f62a83SSean Christopherson 		}
821d8f62a83SSean Christopherson 	}
822d8f62a83SSean Christopherson 	report(!f, "IPI to multiple targets using logical flat mode");
823d8f62a83SSean Christopherson 
824d8f62a83SSean Christopherson 	f = 0;
825d8f62a83SSean Christopherson 	for (c = 0; c < 0xf && !f; c++) {
826d8f62a83SSean Christopherson 		for (i = 0; i < 4 && !f; i++) {
827d8f62a83SSean Christopherson 			for (j = 0; j < 4 && !f; j++) {
828d8f62a83SSean Christopherson 				if (i == j)
829d8f62a83SSean Christopherson 					continue;
830d8f62a83SSean Christopherson 
831d8f62a83SSean Christopherson 				for (k = 0; k < 0x10 && !f; k++) {
832d8f62a83SSean Christopherson 					if ((BIT(i) | BIT(j)) & k)
833d8f62a83SSean Christopherson 						continue;
834d8f62a83SSean Christopherson 
835d8f62a83SSean Christopherson 					f += test_logical_ipi_multi_target(c << 4 | BIT(i),
836d8f62a83SSean Christopherson 									   c << 4 | BIT(j), true,
837d8f62a83SSean Christopherson 									   c << 4 | BIT(i) | BIT(j) | k, vector);
838d8f62a83SSean Christopherson 					if (f)
839d8f62a83SSean Christopherson 						break;
840d8f62a83SSean Christopherson 					f += test_logical_ipi_multi_target(c << 4 | BIT(i) | BIT(j),
841d8f62a83SSean Christopherson 									   c << 4 | BIT(i) | BIT(j), true,
842d8f62a83SSean Christopherson 									   c << 4 | BIT(i) | BIT(j) | k, vector);
843d8f62a83SSean Christopherson 				}
844d8f62a83SSean Christopherson 			}
845d8f62a83SSean Christopherson 		}
846d8f62a83SSean Christopherson 	}
847d8f62a83SSean Christopherson 	report(!f, "IPI to multiple targets using logical cluster mode");
848d8f62a83SSean Christopherson }
849d8f62a83SSean Christopherson 
set_xapic_physical_id(void * apic_id)850baf248c5SSean Christopherson static void set_xapic_physical_id(void *apic_id)
851baf248c5SSean Christopherson {
852baf248c5SSean Christopherson 	apic_write(APIC_ID, (unsigned long)apic_id << 24);
853baf248c5SSean Christopherson }
854baf248c5SSean Christopherson 
handle_aliased_ipi(isr_regs_t * regs)855baf248c5SSean Christopherson static void handle_aliased_ipi(isr_regs_t *regs)
856baf248c5SSean Christopherson {
857baf248c5SSean Christopherson 	u32 apic_id = apic_read(APIC_ID) >> 24;
858baf248c5SSean Christopherson 
859baf248c5SSean Christopherson 	if (apic_id == 0xff)
860baf248c5SSean Christopherson 		apic_id = smp_id();
861baf248c5SSean Christopherson 	else
862baf248c5SSean Christopherson 		apic_id++;
863baf248c5SSean Christopherson 	apic_write(APIC_ID, (unsigned long)apic_id << 24);
864baf248c5SSean Christopherson 
865baf248c5SSean Christopherson 	/*
866baf248c5SSean Christopherson 	 * Handle the IPI after updating the APIC ID, as the IPI count acts as
867baf248c5SSean Christopherson 	 * synchronization barrier before vCPU0 sends the next IPI.
868baf248c5SSean Christopherson 	 */
869baf248c5SSean Christopherson 	handle_ipi(regs);
870baf248c5SSean Christopherson }
871baf248c5SSean Christopherson 
test_aliased_xapic_physical_ipi(void)872baf248c5SSean Christopherson static void test_aliased_xapic_physical_ipi(void)
873baf248c5SSean Christopherson {
874baf248c5SSean Christopherson 	u8 vector = 0xf1;
875baf248c5SSean Christopherson 	int i, f;
876baf248c5SSean Christopherson 
877baf248c5SSean Christopherson 	if (cpu_count() < 2)
878baf248c5SSean Christopherson 		return;
879baf248c5SSean Christopherson 
880baf248c5SSean Christopherson 	/*
881baf248c5SSean Christopherson 	 * All vCPUs must be in xAPIC mode, i.e. simply resetting this vCPUs
882baf248c5SSean Christopherson 	 * APIC is not sufficient.
883baf248c5SSean Christopherson 	 */
884baf248c5SSean Christopherson 	if (is_x2apic_enabled())
885baf248c5SSean Christopherson 		return;
886baf248c5SSean Christopherson 
887baf248c5SSean Christopherson 	/*
888baf248c5SSean Christopherson 	 * By default, KVM doesn't follow the x86 APIC architecture for aliased
889baf248c5SSean Christopherson 	 * APIC IDs if userspace has enabled KVM_X2APIC_API_USE_32BIT_IDS.
890baf248c5SSean Christopherson 	 * If x2APIC is supported, assume the userspace VMM has enabled 32-bit
891baf248c5SSean Christopherson 	 * IDs and thus activated KVM's quirk.  Delete this code to run the
892baf248c5SSean Christopherson 	 * aliasing test on x2APIC CPUs, e.g. to run it on bare metal.
893baf248c5SSean Christopherson 	 */
894baf248c5SSean Christopherson 	if (this_cpu_has(X86_FEATURE_X2APIC))
895baf248c5SSean Christopherson 		return;
896baf248c5SSean Christopherson 
897baf248c5SSean Christopherson 	handle_irq(vector, handle_aliased_ipi);
898baf248c5SSean Christopherson 
899baf248c5SSean Christopherson 	/*
900baf248c5SSean Christopherson 	 * Set both vCPU0 and vCPU1's APIC IDs to 0, then start the chain
901baf248c5SSean Christopherson 	 * reaction of IPIs from APIC ID 0..255.  Each vCPU will increment its
902baf248c5SSean Christopherson 	 * APIC ID in the handler, and then "reset" to its original ID (using
903baf248c5SSean Christopherson 	 * smp_id()) after the last IPI.  Using on_cpu() to set vCPU1's ID
904baf248c5SSean Christopherson 	 * after this point won't work due to on_cpu() using physical mode.
905baf248c5SSean Christopherson 	 */
906baf248c5SSean Christopherson 	on_cpu(1, set_xapic_physical_id, (void *)0ul);
907baf248c5SSean Christopherson 	set_xapic_physical_id((void *)0ul);
908baf248c5SSean Christopherson 
909baf248c5SSean Christopherson 	f = 0;
910baf248c5SSean Christopherson 	for (i = 0; i < 0x100; i++)
911baf248c5SSean Christopherson 		f += test_fixed_ipi(APIC_DEST_PHYSICAL, i, vector, 2, "physical");
912baf248c5SSean Christopherson 
913baf248c5SSean Christopherson 	report(!f, "IPI to aliased xAPIC physical IDs");
914baf248c5SSean Christopherson }
915baf248c5SSean Christopherson 
9166dc73196SSean Christopherson typedef void (*apic_test_fn)(void);
9176dc73196SSean Christopherson 
main(void)9187db17e21SThomas Huth int main(void)
9197d36db35SAvi Kivity {
9206dc73196SSean Christopherson 	bool is_x2apic = is_x2apic_enabled();
9216dc73196SSean Christopherson 	u32 spiv = apic_read(APIC_SPIV);
9226dc73196SSean Christopherson 	int i;
9236dc73196SSean Christopherson 
9246dc73196SSean Christopherson 	const apic_test_fn tests[] = {
9256dc73196SSean Christopherson 		test_lapic_existence,
9266dc73196SSean Christopherson 
9276dc73196SSean Christopherson 		test_apic_disable,
9286dc73196SSean Christopherson 		test_enable_x2apic,
9296dc73196SSean Christopherson 
9306dc73196SSean Christopherson 		test_self_ipi_xapic,
9316dc73196SSean Christopherson 		test_self_ipi_x2apic,
9326dc73196SSean Christopherson 		test_physical_broadcast,
933d8f62a83SSean Christopherson 		test_logical_ipi_xapic,
9346dc73196SSean Christopherson 
9356dc73196SSean Christopherson 		test_pv_ipi,
9366dc73196SSean Christopherson 
9376dc73196SSean Christopherson 		test_sti_nmi,
9386dc73196SSean Christopherson 		test_multiple_nmi,
9396dc73196SSean Christopherson 		test_pending_nmi,
9406dc73196SSean Christopherson 
9416dc73196SSean Christopherson 		test_apic_timer_one_shot,
9426dc73196SSean Christopherson 		test_apic_change_mode,
9436dc73196SSean Christopherson 		test_tsc_deadline_timer,
94475ac2899SSean Christopherson 
94575ac2899SSean Christopherson 		/*
94675ac2899SSean Christopherson 		 * KVM may disable APICv if the APIC ID and/or APIC_BASE is
94775ac2899SSean Christopherson 		 * modified, keep these tests at the end so that the test as a
94875ac2899SSean Christopherson 		 * whole provides coverage for APICv (when it's enabled).
94975ac2899SSean Christopherson 		 */
95075ac2899SSean Christopherson 		test_apic_id,
95175ac2899SSean Christopherson 		test_apicbase,
952baf248c5SSean Christopherson 		test_aliased_xapic_physical_ipi,
9536dc73196SSean Christopherson 	};
9546dc73196SSean Christopherson 
9551eb8551aSSean Christopherson 	assert_msg(is_apic_hw_enabled() && is_apic_sw_enabled(),
9561eb8551aSSean Christopherson 		   "APIC should be fully enabled by startup code.");
9571eb8551aSSean Christopherson 
9587d36db35SAvi Kivity 	setup_vm();
9597d36db35SAvi Kivity 
9607d36db35SAvi Kivity 	mask_pic_interrupts();
961787f0aebSMaxim Levitsky 	sti();
9627d36db35SAvi Kivity 
9636dc73196SSean Christopherson 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
9646dc73196SSean Christopherson 		tests[i]();
9657d36db35SAvi Kivity 
9666dc73196SSean Christopherson 		if (is_x2apic)
9676dc73196SSean Christopherson 			enable_x2apic();
9686dc73196SSean Christopherson 		else
9696dc73196SSean Christopherson 			reset_apic();
9707d36db35SAvi Kivity 
9716dc73196SSean Christopherson 		apic_write(APIC_SPIV, spiv);
9726dc73196SSean Christopherson 	}
973d423ca36SLiu, Jinsong 
974f3cdd159SJan Kiszka 	return report_summary();
9757d36db35SAvi Kivity }
976