xref: /kvm-unit-tests/x86/apic.c (revision 0eac5394af6b481328857d43acb5d8e628b0dbac)
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"
97d36db35SAvi Kivity 
10e38858bcSJim Mattson #define MAX_TPR			0xf
11e38858bcSJim Mattson 
127d36db35SAvi Kivity static void test_lapic_existence(void)
137d36db35SAvi Kivity {
147d36db35SAvi Kivity     u32 lvr;
157d36db35SAvi Kivity 
167d36db35SAvi Kivity     lvr = apic_read(APIC_LVR);
177d36db35SAvi Kivity     printf("apic version: %x\n", lvr);
187d36db35SAvi Kivity     report("apic existence", (u16)lvr == 0x14);
197d36db35SAvi Kivity }
207d36db35SAvi Kivity 
21d423ca36SLiu, Jinsong #define TSC_DEADLINE_TIMER_VECTOR 0xef
229931b88cSRadim Krčmář #define BROADCAST_VECTOR 0xcf
23d423ca36SLiu, Jinsong 
24d423ca36SLiu, Jinsong static int tdt_count;
25d423ca36SLiu, Jinsong 
26d423ca36SLiu, Jinsong static void tsc_deadline_timer_isr(isr_regs_t *regs)
27d423ca36SLiu, Jinsong {
28d423ca36SLiu, Jinsong     ++tdt_count;
290b04ed06SPeter Xu     eoi();
30d423ca36SLiu, Jinsong }
31d423ca36SLiu, Jinsong 
3232b9603cSRadim Krčmář static void __test_tsc_deadline_timer(void)
33d423ca36SLiu, Jinsong {
34d423ca36SLiu, Jinsong     handle_irq(TSC_DEADLINE_TIMER_VECTOR, tsc_deadline_timer_isr);
35d423ca36SLiu, Jinsong     irq_enable();
36d423ca36SLiu, Jinsong 
37d423ca36SLiu, Jinsong     wrmsr(MSR_IA32_TSCDEADLINE, rdmsr(MSR_IA32_TSC));
38d423ca36SLiu, Jinsong     asm volatile ("nop");
39d423ca36SLiu, Jinsong     report("tsc deadline timer", tdt_count == 1);
40f8833144SNadav Amit     report("tsc deadline timer clearing", rdmsr(MSR_IA32_TSCDEADLINE) == 0);
41d423ca36SLiu, Jinsong }
42d423ca36SLiu, Jinsong 
43d423ca36SLiu, Jinsong static int enable_tsc_deadline_timer(void)
44d423ca36SLiu, Jinsong {
45d423ca36SLiu, Jinsong     uint32_t lvtt;
46d423ca36SLiu, Jinsong 
47d423ca36SLiu, Jinsong     if (cpuid(1).c & (1 << 24)) {
489111ccabSRadim Krčmář         lvtt = APIC_LVT_TIMER_TSCDEADLINE | TSC_DEADLINE_TIMER_VECTOR;
49d423ca36SLiu, Jinsong         apic_write(APIC_LVTT, lvtt);
50d423ca36SLiu, Jinsong         return 1;
51d423ca36SLiu, Jinsong     } else {
52d423ca36SLiu, Jinsong         return 0;
53d423ca36SLiu, Jinsong     }
54d423ca36SLiu, Jinsong }
55d423ca36SLiu, Jinsong 
56d423ca36SLiu, Jinsong static void test_tsc_deadline_timer(void)
57d423ca36SLiu, Jinsong {
58d423ca36SLiu, Jinsong     if(enable_tsc_deadline_timer()) {
5932b9603cSRadim Krčmář         __test_tsc_deadline_timer();
60d423ca36SLiu, Jinsong     } else {
6132b9603cSRadim Krčmář         report_skip("tsc deadline timer not detected");
62d423ca36SLiu, Jinsong     }
63d423ca36SLiu, Jinsong }
64d423ca36SLiu, Jinsong 
6522c7d929SJan Kiszka static void do_write_apicbase(void *data)
6622c7d929SJan Kiszka {
6722c7d929SJan Kiszka     wrmsr(MSR_IA32_APICBASE, *(u64 *)data);
6803f37ef2SPaolo Bonzini }
697d36db35SAvi Kivity 
7049f5ad9eSPaolo Bonzini static bool test_write_apicbase_exception(u64 data)
7149f5ad9eSPaolo Bonzini {
7249f5ad9eSPaolo Bonzini     data |= APIC_DEFAULT_PHYS_BASE | APIC_BSP;
7349f5ad9eSPaolo Bonzini     return test_for_exception(GP_VECTOR, do_write_apicbase, &data);
7449f5ad9eSPaolo Bonzini }
7549f5ad9eSPaolo Bonzini 
76db4898e8SThomas Huth static void test_enable_x2apic(void)
777d36db35SAvi Kivity {
787d36db35SAvi Kivity     if (enable_x2apic()) {
797d36db35SAvi Kivity         printf("x2apic enabled\n");
8022c7d929SJan Kiszka 
8122c7d929SJan Kiszka         report("x2apic enabled to invalid state",
8249f5ad9eSPaolo Bonzini                test_write_apicbase_exception(APIC_EXTD));
8322c7d929SJan Kiszka         report("x2apic enabled to apic enabled",
8449f5ad9eSPaolo Bonzini                test_write_apicbase_exception(APIC_EN));
8522c7d929SJan Kiszka 
8649f5ad9eSPaolo Bonzini         report("x2apic enabled to disabled state",
8749f5ad9eSPaolo Bonzini                !test_write_apicbase_exception(0));
8822c7d929SJan Kiszka         report("disabled to invalid state",
8949f5ad9eSPaolo Bonzini                test_write_apicbase_exception(APIC_EXTD));
9022c7d929SJan Kiszka         report("disabled to x2apic enabled",
9149f5ad9eSPaolo Bonzini                test_write_apicbase_exception(APIC_EN | APIC_EXTD));
9222c7d929SJan Kiszka 
9349f5ad9eSPaolo Bonzini         wrmsr(MSR_IA32_APICBASE, APIC_EN);
9422c7d929SJan Kiszka         report("apic enabled to invalid state",
9549f5ad9eSPaolo Bonzini                test_write_apicbase_exception(APIC_EXTD));
9622c7d929SJan Kiszka 
9749f5ad9eSPaolo Bonzini         wrmsr(MSR_IA32_APICBASE, APIC_EN | APIC_EXTD);
9822c7d929SJan Kiszka         apic_write(APIC_SPIV, 0x1ff);
997d36db35SAvi Kivity     } else {
1007d36db35SAvi Kivity         printf("x2apic not detected\n");
10122c7d929SJan Kiszka 
10222c7d929SJan Kiszka         report("enable unsupported x2apic",
10349f5ad9eSPaolo Bonzini                test_write_apicbase_exception(APIC_EN | APIC_EXTD));
1047d36db35SAvi Kivity     }
1057d36db35SAvi Kivity }
1067d36db35SAvi Kivity 
107e38858bcSJim Mattson static void verify_disabled_apic_mmio(void)
108e38858bcSJim Mattson {
109e38858bcSJim Mattson     volatile u32 *lvr = (volatile u32 *)(APIC_DEFAULT_PHYS_BASE + APIC_LVR);
110e38858bcSJim Mattson     volatile u32 *tpr = (volatile u32 *)(APIC_DEFAULT_PHYS_BASE + APIC_TASKPRI);
111e38858bcSJim Mattson     u32 cr8 = read_cr8();
112e38858bcSJim Mattson 
113e38858bcSJim Mattson     memset((void *)APIC_DEFAULT_PHYS_BASE, 0xff, PAGE_SIZE);
114e38858bcSJim Mattson     report("*0xfee00030: %x", *lvr == ~0, *lvr);
115e38858bcSJim Mattson     report("CR8: %lx", read_cr8() == cr8, read_cr8());
116e38858bcSJim Mattson     write_cr8(cr8 ^ MAX_TPR);
117e38858bcSJim Mattson     report("CR8: %lx", read_cr8() == (cr8 ^ MAX_TPR), read_cr8());
118e38858bcSJim Mattson     report("*0xfee00080: %x", *tpr == ~0, *tpr);
119e38858bcSJim Mattson     write_cr8(cr8);
120e38858bcSJim Mattson }
121e38858bcSJim Mattson 
122c3ccca3fSJim Mattson static void test_apic_disable(void)
123c3ccca3fSJim Mattson {
124e38858bcSJim Mattson     volatile u32 *lvr = (volatile u32 *)(APIC_DEFAULT_PHYS_BASE + APIC_LVR);
125e38858bcSJim Mattson     volatile u32 *tpr = (volatile u32 *)(APIC_DEFAULT_PHYS_BASE + APIC_TASKPRI);
126c3ccca3fSJim Mattson     u64 orig_apicbase = rdmsr(MSR_IA32_APICBASE);
127e38858bcSJim Mattson     u32 apic_version = apic_read(APIC_LVR);
128e38858bcSJim Mattson     u32 cr8 = read_cr8();
129c3ccca3fSJim Mattson 
130c3ccca3fSJim Mattson     report_prefix_push("apic_disable");
131e38858bcSJim Mattson     assert_msg(orig_apicbase & APIC_EN, "APIC not enabled.");
132c3ccca3fSJim Mattson 
133e38858bcSJim Mattson     disable_apic();
134c3ccca3fSJim Mattson     report("Local apic disabled", !(rdmsr(MSR_IA32_APICBASE) & APIC_EN));
135c3ccca3fSJim Mattson     report("CPUID.1H:EDX.APIC[bit 9] is clear", !(cpuid(1).d & (1 << 9)));
136e38858bcSJim Mattson     verify_disabled_apic_mmio();
137c3ccca3fSJim Mattson 
138e38858bcSJim Mattson     reset_apic();
139c3ccca3fSJim Mattson     apic_write(APIC_SPIV, 0x1ff);
140e38858bcSJim Mattson     report("Local apic enabled in xAPIC mode",
141e38858bcSJim Mattson 	   (rdmsr(MSR_IA32_APICBASE) & (APIC_EN | APIC_EXTD)) == APIC_EN);
142c3ccca3fSJim Mattson     report("CPUID.1H:EDX.APIC[bit 9] is set", cpuid(1).d & (1 << 9));
143e38858bcSJim Mattson     report("*0xfee00030: %x", *lvr == apic_version, *lvr);
144e38858bcSJim Mattson     report("*0xfee00080: %x", *tpr == cr8, *tpr);
145e38858bcSJim Mattson     write_cr8(cr8 ^ MAX_TPR);
146e38858bcSJim Mattson     report("*0xfee00080: %x", *tpr == (cr8 ^ MAX_TPR) << 4, *tpr);
147e38858bcSJim Mattson     write_cr8(cr8);
148c3ccca3fSJim Mattson 
149e38858bcSJim Mattson     if (enable_x2apic()) {
150e38858bcSJim Mattson 	apic_write(APIC_SPIV, 0x1ff);
151e38858bcSJim Mattson 	report("Local apic enabled in x2APIC mode",
152e38858bcSJim Mattson 	   (rdmsr(MSR_IA32_APICBASE) & (APIC_EN | APIC_EXTD)) ==
153e38858bcSJim Mattson 	   (APIC_EN | APIC_EXTD));
154e38858bcSJim Mattson 	report("CPUID.1H:EDX.APIC[bit 9] is set", cpuid(1).d & (1 << 9));
155e38858bcSJim Mattson 	verify_disabled_apic_mmio();
156e38858bcSJim Mattson 	if (!(orig_apicbase & APIC_EXTD))
157e38858bcSJim Mattson 	    reset_apic();
158e38858bcSJim Mattson     }
159c3ccca3fSJim Mattson     report_prefix_pop();
160c3ccca3fSJim Mattson }
161c3ccca3fSJim Mattson 
1629b6bdb3fSJan Kiszka #define ALTERNATE_APIC_BASE	0x42000000
1639b6bdb3fSJan Kiszka 
1649b6bdb3fSJan Kiszka static void test_apicbase(void)
1659b6bdb3fSJan Kiszka {
1669b6bdb3fSJan Kiszka     u64 orig_apicbase = rdmsr(MSR_IA32_APICBASE);
1679b6bdb3fSJan Kiszka     u32 lvr = apic_read(APIC_LVR);
1689b6bdb3fSJan Kiszka     u64 value;
1699b6bdb3fSJan Kiszka 
1709b6bdb3fSJan Kiszka     wrmsr(MSR_IA32_APICBASE, orig_apicbase & ~(APIC_EN | APIC_EXTD));
1719b6bdb3fSJan Kiszka     wrmsr(MSR_IA32_APICBASE, ALTERNATE_APIC_BASE | APIC_BSP | APIC_EN);
1729b6bdb3fSJan Kiszka 
1735bba1769SAndrew Jones     report_prefix_push("apicbase");
1745bba1769SAndrew Jones 
1759b6bdb3fSJan Kiszka     report("relocate apic",
1769b6bdb3fSJan Kiszka            *(volatile u32 *)(ALTERNATE_APIC_BASE + APIC_LVR) == lvr);
1779b6bdb3fSJan Kiszka 
178772befb7SEduardo Habkost     value = orig_apicbase | (1UL << cpuid_maxphyaddr());
1795bba1769SAndrew Jones     report("reserved physaddr bits",
1809b6bdb3fSJan Kiszka            test_for_exception(GP_VECTOR, do_write_apicbase, &value));
1819b6bdb3fSJan Kiszka 
1829b6bdb3fSJan Kiszka     value = orig_apicbase | 1;
1835bba1769SAndrew Jones     report("reserved low bits",
1849b6bdb3fSJan Kiszka            test_for_exception(GP_VECTOR, do_write_apicbase, &value));
1859b6bdb3fSJan Kiszka 
1869b6bdb3fSJan Kiszka     wrmsr(MSR_IA32_APICBASE, orig_apicbase);
1879b6bdb3fSJan Kiszka     apic_write(APIC_SPIV, 0x1ff);
1885bba1769SAndrew Jones 
1895bba1769SAndrew Jones     report_prefix_pop();
1909b6bdb3fSJan Kiszka }
1919b6bdb3fSJan Kiszka 
192a222b5e2SRadim Krčmář static void do_write_apic_id(void *id)
193a222b5e2SRadim Krčmář {
194a222b5e2SRadim Krčmář     apic_write(APIC_ID, *(u32 *)id);
195a222b5e2SRadim Krčmář }
196a222b5e2SRadim Krčmář 
197a222b5e2SRadim Krčmář static void __test_apic_id(void * unused)
198a222b5e2SRadim Krčmář {
199a222b5e2SRadim Krčmář     u32 id, newid;
200a222b5e2SRadim Krčmář     u8  initial_xapic_id = cpuid(1).b >> 24;
201a222b5e2SRadim Krčmář     u32 initial_x2apic_id = cpuid(0xb).d;
202a222b5e2SRadim Krčmář     bool x2apic_mode = rdmsr(MSR_IA32_APICBASE) & APIC_EXTD;
203a222b5e2SRadim Krčmář 
204a222b5e2SRadim Krčmář     if (x2apic_mode)
205a222b5e2SRadim Krčmář         reset_apic();
206a222b5e2SRadim Krčmář 
207a222b5e2SRadim Krčmář     id = apic_id();
208a222b5e2SRadim Krčmář     report("xapic id matches cpuid", initial_xapic_id == id);
209a222b5e2SRadim Krčmář 
210a222b5e2SRadim Krčmář     newid = (id + 1) << 24;
211a222b5e2SRadim Krčmář     report("writeable xapic id",
212a222b5e2SRadim Krčmář             !test_for_exception(GP_VECTOR, do_write_apic_id, &newid) &&
213a222b5e2SRadim Krčmář             id + 1 == apic_id());
214a222b5e2SRadim Krčmář 
215a222b5e2SRadim Krčmář     if (!enable_x2apic())
216a222b5e2SRadim Krčmář         goto out;
217a222b5e2SRadim Krčmář 
218a222b5e2SRadim Krčmář     report("non-writeable x2apic id",
219a222b5e2SRadim Krčmář             test_for_exception(GP_VECTOR, do_write_apic_id, &newid));
220a222b5e2SRadim Krčmář     report("sane x2apic id", initial_xapic_id == (apic_id() & 0xff));
221a222b5e2SRadim Krčmář 
222a222b5e2SRadim Krčmář     /* old QEMUs do not set initial x2APIC ID */
223a222b5e2SRadim Krčmář     report("x2apic id matches cpuid",
224a222b5e2SRadim Krčmář            initial_xapic_id == (initial_x2apic_id & 0xff) &&
225a222b5e2SRadim Krčmář            initial_x2apic_id == apic_id());
226a222b5e2SRadim Krčmář 
227a222b5e2SRadim Krčmář out:
228a222b5e2SRadim Krčmář     reset_apic();
229a222b5e2SRadim Krčmář 
230a222b5e2SRadim Krčmář     report("correct xapic id after reset", initial_xapic_id == apic_id());
231a222b5e2SRadim Krčmář 
232a222b5e2SRadim Krčmář     /* old KVMs do not reset xAPIC ID */
233a222b5e2SRadim Krčmář     if (id != apic_id())
234a222b5e2SRadim Krčmář         apic_write(APIC_ID, id << 24);
235a222b5e2SRadim Krčmář 
236a222b5e2SRadim Krčmář     if (x2apic_mode)
237a222b5e2SRadim Krčmář         enable_x2apic();
238a222b5e2SRadim Krčmář }
239a222b5e2SRadim Krčmář 
240a222b5e2SRadim Krčmář static void test_apic_id(void)
241a222b5e2SRadim Krčmář {
242a222b5e2SRadim Krčmář     if (cpu_count() < 2)
243a222b5e2SRadim Krčmář         return;
244a222b5e2SRadim Krčmář 
245a222b5e2SRadim Krčmář     on_cpu(1, __test_apic_id, NULL);
246a222b5e2SRadim Krčmář }
247a222b5e2SRadim Krčmář 
2487d36db35SAvi Kivity static int ipi_count;
2497d36db35SAvi Kivity 
2507d36db35SAvi Kivity static void self_ipi_isr(isr_regs_t *regs)
2517d36db35SAvi Kivity {
2527d36db35SAvi Kivity     ++ipi_count;
2537d36db35SAvi Kivity     eoi();
2547d36db35SAvi Kivity }
2557d36db35SAvi Kivity 
2567d36db35SAvi Kivity static void test_self_ipi(void)
2577d36db35SAvi Kivity {
2587d36db35SAvi Kivity     int vec = 0xf1;
2597d36db35SAvi Kivity 
260d51bd17eSGleb Natapov     handle_irq(vec, self_ipi_isr);
2617d36db35SAvi Kivity     irq_enable();
2627d36db35SAvi Kivity     apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec,
2637d36db35SAvi Kivity                    0);
2647d36db35SAvi Kivity     asm volatile ("nop");
2657d36db35SAvi Kivity     report("self ipi", ipi_count == 1);
2667d36db35SAvi Kivity }
2677d36db35SAvi Kivity 
268f2d2b7c7SAvi Kivity volatile int nmi_counter_private, nmi_counter, nmi_hlt_counter, sti_loop_active;
269f2d2b7c7SAvi Kivity 
270db4898e8SThomas Huth static void sti_nop(char *p)
271f2d2b7c7SAvi Kivity {
272f2d2b7c7SAvi Kivity     asm volatile (
273f2d2b7c7SAvi Kivity 		  ".globl post_sti \n\t"
274f2d2b7c7SAvi Kivity 		  "sti \n"
275f2d2b7c7SAvi Kivity 		  /*
276f2d2b7c7SAvi Kivity 		   * vmx won't exit on external interrupt if blocked-by-sti,
277f2d2b7c7SAvi Kivity 		   * so give it a reason to exit by accessing an unmapped page.
278f2d2b7c7SAvi Kivity 		   */
279f2d2b7c7SAvi Kivity 		  "post_sti: testb $0, %0 \n\t"
280f2d2b7c7SAvi Kivity 		  "nop \n\t"
281f2d2b7c7SAvi Kivity 		  "cli"
282f2d2b7c7SAvi Kivity 		  : : "m"(*p)
283f2d2b7c7SAvi Kivity 		  );
284f2d2b7c7SAvi Kivity     nmi_counter = nmi_counter_private;
285f2d2b7c7SAvi Kivity }
286f2d2b7c7SAvi Kivity 
287f2d2b7c7SAvi Kivity static void sti_loop(void *ignore)
288f2d2b7c7SAvi Kivity {
289f2d2b7c7SAvi Kivity     unsigned k = 0;
290f2d2b7c7SAvi Kivity 
291f2d2b7c7SAvi Kivity     while (sti_loop_active) {
292f2d2b7c7SAvi Kivity 	sti_nop((char *)(ulong)((k++ * 4096) % (128 * 1024 * 1024)));
293f2d2b7c7SAvi Kivity     }
294f2d2b7c7SAvi Kivity }
295f2d2b7c7SAvi Kivity 
296f2d2b7c7SAvi Kivity static void nmi_handler(isr_regs_t *regs)
297f2d2b7c7SAvi Kivity {
298f2d2b7c7SAvi Kivity     extern void post_sti(void);
299f2d2b7c7SAvi Kivity     ++nmi_counter_private;
300f2d2b7c7SAvi Kivity     nmi_hlt_counter += regs->rip == (ulong)post_sti;
301f2d2b7c7SAvi Kivity }
302f2d2b7c7SAvi Kivity 
303f2d2b7c7SAvi Kivity static void update_cr3(void *cr3)
304f2d2b7c7SAvi Kivity {
305f2d2b7c7SAvi Kivity     write_cr3((ulong)cr3);
306f2d2b7c7SAvi Kivity }
307f2d2b7c7SAvi Kivity 
308f2d2b7c7SAvi Kivity static void test_sti_nmi(void)
309f2d2b7c7SAvi Kivity {
310f2d2b7c7SAvi Kivity     unsigned old_counter;
311f2d2b7c7SAvi Kivity 
312f2d2b7c7SAvi Kivity     if (cpu_count() < 2) {
313f2d2b7c7SAvi Kivity 	return;
314f2d2b7c7SAvi Kivity     }
315f2d2b7c7SAvi Kivity 
316d51bd17eSGleb Natapov     handle_irq(2, nmi_handler);
317f2d2b7c7SAvi Kivity     on_cpu(1, update_cr3, (void *)read_cr3());
318f2d2b7c7SAvi Kivity 
319f2d2b7c7SAvi Kivity     sti_loop_active = 1;
320f2d2b7c7SAvi Kivity     on_cpu_async(1, sti_loop, 0);
321f2d2b7c7SAvi Kivity     while (nmi_counter < 30000) {
322f2d2b7c7SAvi Kivity 	old_counter = nmi_counter;
323f2d2b7c7SAvi Kivity 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 1);
324f2d2b7c7SAvi Kivity 	while (nmi_counter == old_counter) {
325f2d2b7c7SAvi Kivity 	    ;
326f2d2b7c7SAvi Kivity 	}
327f2d2b7c7SAvi Kivity     }
328f2d2b7c7SAvi Kivity     sti_loop_active = 0;
329f2d2b7c7SAvi Kivity     report("nmi-after-sti", nmi_hlt_counter == 0);
330f2d2b7c7SAvi Kivity }
331f2d2b7c7SAvi Kivity 
332173e7eacSAvi Kivity static volatile bool nmi_done, nmi_flushed;
333173e7eacSAvi Kivity static volatile int nmi_received;
334173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr1, cpu1_nmi_ctr1;
335173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr2, cpu1_nmi_ctr2;
336173e7eacSAvi Kivity 
337173e7eacSAvi Kivity static void multiple_nmi_handler(isr_regs_t *regs)
338173e7eacSAvi Kivity {
339173e7eacSAvi Kivity     ++nmi_received;
340173e7eacSAvi Kivity }
341173e7eacSAvi Kivity 
342173e7eacSAvi Kivity static void kick_me_nmi(void *blah)
343173e7eacSAvi Kivity {
344173e7eacSAvi Kivity     while (!nmi_done) {
345173e7eacSAvi Kivity 	++cpu1_nmi_ctr1;
346173e7eacSAvi Kivity 	while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1 && !nmi_done) {
347173e7eacSAvi Kivity 	    pause();
348173e7eacSAvi Kivity 	}
349173e7eacSAvi Kivity 	if (nmi_done) {
350173e7eacSAvi Kivity 	    return;
351173e7eacSAvi Kivity 	}
352173e7eacSAvi Kivity 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
353173e7eacSAvi Kivity 	/* make sure the NMI has arrived by sending an IPI after it */
354173e7eacSAvi Kivity 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT
355173e7eacSAvi Kivity 		       | 0x44, 0);
356173e7eacSAvi Kivity 	++cpu1_nmi_ctr2;
357173e7eacSAvi Kivity 	while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2 && !nmi_done) {
358173e7eacSAvi Kivity 	    pause();
359173e7eacSAvi Kivity 	}
360173e7eacSAvi Kivity     }
361173e7eacSAvi Kivity }
362173e7eacSAvi Kivity 
363173e7eacSAvi Kivity static void flush_nmi(isr_regs_t *regs)
364173e7eacSAvi Kivity {
365173e7eacSAvi Kivity     nmi_flushed = true;
366173e7eacSAvi Kivity     apic_write(APIC_EOI, 0);
367173e7eacSAvi Kivity }
368173e7eacSAvi Kivity 
369173e7eacSAvi Kivity static void test_multiple_nmi(void)
370173e7eacSAvi Kivity {
371173e7eacSAvi Kivity     int i;
372173e7eacSAvi Kivity     bool ok = true;
373173e7eacSAvi Kivity 
374173e7eacSAvi Kivity     if (cpu_count() < 2) {
375173e7eacSAvi Kivity 	return;
376173e7eacSAvi Kivity     }
377173e7eacSAvi Kivity 
378173e7eacSAvi Kivity     sti();
379173e7eacSAvi Kivity     handle_irq(2, multiple_nmi_handler);
380173e7eacSAvi Kivity     handle_irq(0x44, flush_nmi);
381173e7eacSAvi Kivity     on_cpu_async(1, kick_me_nmi, 0);
382173e7eacSAvi Kivity     for (i = 0; i < 1000000; ++i) {
383173e7eacSAvi Kivity 	nmi_flushed = false;
384173e7eacSAvi Kivity 	nmi_received = 0;
385173e7eacSAvi Kivity 	++cpu0_nmi_ctr1;
386173e7eacSAvi Kivity 	while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1) {
387173e7eacSAvi Kivity 	    pause();
388173e7eacSAvi Kivity 	}
389173e7eacSAvi Kivity 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
390173e7eacSAvi Kivity 	while (!nmi_flushed) {
391173e7eacSAvi Kivity 	    pause();
392173e7eacSAvi Kivity 	}
393173e7eacSAvi Kivity 	if (nmi_received != 2) {
394173e7eacSAvi Kivity 	    ok = false;
395173e7eacSAvi Kivity 	    break;
396173e7eacSAvi Kivity 	}
397173e7eacSAvi Kivity 	++cpu0_nmi_ctr2;
398173e7eacSAvi Kivity 	while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2) {
399173e7eacSAvi Kivity 	    pause();
400173e7eacSAvi Kivity 	}
401173e7eacSAvi Kivity     }
402173e7eacSAvi Kivity     nmi_done = true;
403173e7eacSAvi Kivity     report("multiple nmi", ok);
404173e7eacSAvi Kivity }
405173e7eacSAvi Kivity 
4069f815b29SPeter Xu static volatile int lvtt_counter = 0;
4079f815b29SPeter Xu 
4089f815b29SPeter Xu static void lvtt_handler(isr_regs_t *regs)
4099f815b29SPeter Xu {
4109f815b29SPeter Xu     lvtt_counter++;
4119f815b29SPeter Xu     eoi();
4129f815b29SPeter Xu }
4139f815b29SPeter Xu 
4149f815b29SPeter Xu static void test_apic_timer_one_shot(void)
4159f815b29SPeter Xu {
4169f815b29SPeter Xu     uint64_t tsc1, tsc2;
4179f815b29SPeter Xu     static const uint32_t interval = 0x10000;
4189f815b29SPeter Xu 
4199f815b29SPeter Xu #define APIC_LVT_TIMER_VECTOR    (0xee)
4209f815b29SPeter Xu 
4219f815b29SPeter Xu     handle_irq(APIC_LVT_TIMER_VECTOR, lvtt_handler);
4229f815b29SPeter Xu     irq_enable();
4239f815b29SPeter Xu 
4249f815b29SPeter Xu     /* One shot mode */
4259111ccabSRadim Krčmář     apic_write(APIC_LVTT, APIC_LVT_TIMER_ONESHOT |
4269f815b29SPeter Xu                APIC_LVT_TIMER_VECTOR);
4279f815b29SPeter Xu     /* Divider == 1 */
4289f815b29SPeter Xu     apic_write(APIC_TDCR, 0x0000000b);
4299f815b29SPeter Xu 
4309f815b29SPeter Xu     tsc1 = rdtsc();
4319f815b29SPeter Xu     /* Set "Initial Counter Register", which starts the timer */
4329f815b29SPeter Xu     apic_write(APIC_TMICT, interval);
4339f815b29SPeter Xu     while (!lvtt_counter);
4349f815b29SPeter Xu     tsc2 = rdtsc();
4359f815b29SPeter Xu 
4369f815b29SPeter Xu     /*
4379f815b29SPeter Xu      * For LVT Timer clock, SDM vol 3 10.5.4 says it should be
4389f815b29SPeter Xu      * derived from processor's bus clock (IIUC which is the same
4399f815b29SPeter Xu      * as TSC), however QEMU seems to be using nanosecond. In all
4409f815b29SPeter Xu      * cases, the following should satisfy on all modern
4419f815b29SPeter Xu      * processors.
4429f815b29SPeter Xu      */
4439f815b29SPeter Xu     report("APIC LVT timer one shot", (lvtt_counter == 1) &&
4449f815b29SPeter Xu            (tsc2 - tsc1 >= interval));
4459f815b29SPeter Xu }
4469f815b29SPeter Xu 
4479931b88cSRadim Krčmář static atomic_t broadcast_counter;
4489931b88cSRadim Krčmář 
4499931b88cSRadim Krčmář static void broadcast_handler(isr_regs_t *regs)
4509931b88cSRadim Krčmář {
4519931b88cSRadim Krčmář 	atomic_inc(&broadcast_counter);
4529931b88cSRadim Krčmář 	eoi();
4539931b88cSRadim Krčmář }
4549931b88cSRadim Krčmář 
4559931b88cSRadim Krčmář static bool broadcast_received(unsigned ncpus)
4569931b88cSRadim Krčmář {
4579931b88cSRadim Krčmář 	unsigned counter;
4589931b88cSRadim Krčmář 	u64 start = rdtsc();
4599931b88cSRadim Krčmář 
4609931b88cSRadim Krčmář 	do {
4619931b88cSRadim Krčmář 		counter = atomic_read(&broadcast_counter);
4629931b88cSRadim Krčmář 		if (counter >= ncpus)
4639931b88cSRadim Krčmář 			break;
4649931b88cSRadim Krčmář 		pause();
4659931b88cSRadim Krčmář 	} while (rdtsc() - start < 1000000000);
4669931b88cSRadim Krčmář 
4679931b88cSRadim Krčmář 	atomic_set(&broadcast_counter, 0);
4689931b88cSRadim Krčmář 
4699931b88cSRadim Krčmář 	return counter == ncpus;
4709931b88cSRadim Krčmář }
4719931b88cSRadim Krčmář 
4729931b88cSRadim Krčmář static void test_physical_broadcast(void)
4739931b88cSRadim Krčmář {
4749931b88cSRadim Krčmář 	unsigned ncpus = cpu_count();
4759931b88cSRadim Krčmář 	unsigned long cr3 = read_cr3();
4769931b88cSRadim Krčmář 	u32 broadcast_address = enable_x2apic() ? 0xffffffff : 0xff;
4779931b88cSRadim Krčmář 
4789931b88cSRadim Krčmář 	handle_irq(BROADCAST_VECTOR, broadcast_handler);
4799931b88cSRadim Krčmář 	for (int c = 1; c < ncpus; c++)
4809931b88cSRadim Krčmář 		on_cpu(c, update_cr3, (void *)cr3);
4819931b88cSRadim Krčmář 
4829931b88cSRadim Krčmář 	printf("starting broadcast (%s)\n", enable_x2apic() ? "x2apic" : "xapic");
4839931b88cSRadim Krčmář 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT |
4849931b88cSRadim Krčmář 			BROADCAST_VECTOR, broadcast_address);
4859931b88cSRadim Krčmář 	report("APIC physical broadcast address", broadcast_received(ncpus));
4869931b88cSRadim Krčmář 
4879931b88cSRadim Krčmář 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT |
4889931b88cSRadim Krčmář 			BROADCAST_VECTOR | APIC_DEST_ALLINC, 0);
4899931b88cSRadim Krčmář 	report("APIC physical broadcast shorthand", broadcast_received(ncpus));
4909931b88cSRadim Krčmář }
4919931b88cSRadim Krčmář 
492*0eac5394SEvgeny Yakovlev static void wait_until_tmcct_common(uint32_t initial_count, bool stop_when_half, bool should_wrap_around)
493d9b2b283SWanpeng Li {
494d9b2b283SWanpeng Li 	uint32_t tmcct = apic_read(APIC_TMCCT);
495d9b2b283SWanpeng Li 
496d9b2b283SWanpeng Li 	if (tmcct) {
497d9b2b283SWanpeng Li 		while (tmcct > (initial_count / 2))
498d9b2b283SWanpeng Li 			tmcct = apic_read(APIC_TMCCT);
499d9b2b283SWanpeng Li 
500d9b2b283SWanpeng Li 		if ( stop_when_half )
501d9b2b283SWanpeng Li 			return;
502d9b2b283SWanpeng Li 
503d9b2b283SWanpeng Li 		/* Wait until the counter reach 0 or wrap-around */
504d9b2b283SWanpeng Li 		while ( tmcct <= (initial_count / 2) && tmcct > 0 )
505d9b2b283SWanpeng Li 			tmcct = apic_read(APIC_TMCCT);
506*0eac5394SEvgeny Yakovlev 
507*0eac5394SEvgeny Yakovlev 		/* Wait specifically for wrap around to skip 0 TMCCR if we were asked to */
508*0eac5394SEvgeny Yakovlev 		while (should_wrap_around && !tmcct)
509*0eac5394SEvgeny Yakovlev 			tmcct = apic_read(APIC_TMCCT);
510d9b2b283SWanpeng Li 	}
511d9b2b283SWanpeng Li }
512d9b2b283SWanpeng Li 
513*0eac5394SEvgeny Yakovlev static void wait_until_tmcct_is_zero(uint32_t initial_count, bool stop_when_half)
514*0eac5394SEvgeny Yakovlev {
515*0eac5394SEvgeny Yakovlev 	return wait_until_tmcct_common(initial_count, stop_when_half, false);
516*0eac5394SEvgeny Yakovlev }
517*0eac5394SEvgeny Yakovlev 
518*0eac5394SEvgeny Yakovlev static void wait_until_tmcct_wrap_around(uint32_t initial_count, bool stop_when_half)
519*0eac5394SEvgeny Yakovlev {
520*0eac5394SEvgeny Yakovlev 	return wait_until_tmcct_common(initial_count, stop_when_half, true);
521*0eac5394SEvgeny Yakovlev }
522*0eac5394SEvgeny Yakovlev 
523d9b2b283SWanpeng Li static inline void apic_change_mode(unsigned long new_mode)
524d9b2b283SWanpeng Li {
525d9b2b283SWanpeng Li 	uint32_t lvtt;
526d9b2b283SWanpeng Li 
527d9b2b283SWanpeng Li 	lvtt = apic_read(APIC_LVTT);
528d9b2b283SWanpeng Li 	apic_write(APIC_LVTT, (lvtt & ~APIC_LVT_TIMER_MASK) | new_mode);
529d9b2b283SWanpeng Li }
530d9b2b283SWanpeng Li 
5317db17e21SThomas Huth static void test_apic_change_mode(void)
532d9b2b283SWanpeng Li {
533d9b2b283SWanpeng Li 	uint32_t tmict = 0x999999;
534d9b2b283SWanpeng Li 
535d9b2b283SWanpeng Li 	printf("starting apic change mode\n");
536d9b2b283SWanpeng Li 
537d9b2b283SWanpeng Li 	apic_write(APIC_TMICT, tmict);
538d9b2b283SWanpeng Li 
539d9b2b283SWanpeng Li 	apic_change_mode(APIC_LVT_TIMER_PERIODIC);
540d9b2b283SWanpeng Li 
541d9b2b283SWanpeng Li 	report("TMICT value reset", apic_read(APIC_TMICT) == tmict);
542d9b2b283SWanpeng Li 
543d9b2b283SWanpeng Li 	/* Testing one-shot */
544d9b2b283SWanpeng Li 	apic_change_mode(APIC_LVT_TIMER_ONESHOT);
545d9b2b283SWanpeng Li 	apic_write(APIC_TMICT, tmict);
546d9b2b283SWanpeng Li 	report("TMCCT should have a non-zero value", apic_read(APIC_TMCCT));
547d9b2b283SWanpeng Li 
548d9b2b283SWanpeng Li 	wait_until_tmcct_is_zero(tmict, false);
549d9b2b283SWanpeng Li 	report("TMCCT should have reached 0", !apic_read(APIC_TMCCT));
550d9b2b283SWanpeng Li 
551d9b2b283SWanpeng Li 	/*
552d9b2b283SWanpeng Li 	 * Write TMICT before changing mode from one-shot to periodic TMCCT should
553d9b2b283SWanpeng Li 	 * be reset to TMICT periodicly
554d9b2b283SWanpeng Li 	 */
555d9b2b283SWanpeng Li 	apic_write(APIC_TMICT, tmict);
556d9b2b283SWanpeng Li 	wait_until_tmcct_is_zero(tmict, true);
557d9b2b283SWanpeng Li 	apic_change_mode(APIC_LVT_TIMER_PERIODIC);
558d9b2b283SWanpeng Li 	report("TMCCT should have a non-zero value", apic_read(APIC_TMCCT));
559d9b2b283SWanpeng Li 
560d9b2b283SWanpeng Li 	/*
561d9b2b283SWanpeng Li 	 * After the change of mode, the counter should not be reset and continue
562d9b2b283SWanpeng Li 	 * counting down from where it was
563d9b2b283SWanpeng Li 	 */
564d9b2b283SWanpeng Li 	report("TMCCT should not be reset to TMICT value", apic_read(APIC_TMCCT) < (tmict / 2));
565*0eac5394SEvgeny Yakovlev 	/*
566*0eac5394SEvgeny Yakovlev 	 * Specifically wait for timer wrap around and skip 0.
567*0eac5394SEvgeny Yakovlev 	 * Under KVM lapic there is a possibility that a small amount of consecutive
568*0eac5394SEvgeny Yakovlev 	 * TMCCR reads return 0 while hrtimer is reset in an async callback
569*0eac5394SEvgeny Yakovlev 	 */
570*0eac5394SEvgeny Yakovlev 	wait_until_tmcct_wrap_around(tmict, false);
571d9b2b283SWanpeng Li 	report("TMCCT should be reset to the initial-count", apic_read(APIC_TMCCT) > (tmict / 2));
572d9b2b283SWanpeng Li 
573d9b2b283SWanpeng Li 	wait_until_tmcct_is_zero(tmict, true);
574d9b2b283SWanpeng Li 	/*
575d9b2b283SWanpeng Li 	 * Keep the same TMICT and change timer mode to one-shot
576d9b2b283SWanpeng Li 	 * TMCCT should be > 0 and count-down to 0
577d9b2b283SWanpeng Li 	 */
578d9b2b283SWanpeng Li 	apic_change_mode(APIC_LVT_TIMER_ONESHOT);
579d9b2b283SWanpeng Li 	report("TMCCT should not be reset to init", apic_read(APIC_TMCCT) < (tmict / 2));
580d9b2b283SWanpeng Li 	wait_until_tmcct_is_zero(tmict, false);
581d9b2b283SWanpeng Li 	report("TMCCT should have reach zero", !apic_read(APIC_TMCCT));
582d9b2b283SWanpeng Li 
583d9b2b283SWanpeng Li 	/* now tmcct == 0 and tmict != 0 */
584d9b2b283SWanpeng Li 	apic_change_mode(APIC_LVT_TIMER_PERIODIC);
585d9b2b283SWanpeng Li 	report("TMCCT should stay at zero", !apic_read(APIC_TMCCT));
586d9b2b283SWanpeng Li }
587d9b2b283SWanpeng Li 
588de8d3fccSWanpeng Li #define KVM_HC_SEND_IPI 10
589de8d3fccSWanpeng Li 
590de8d3fccSWanpeng Li static void test_pv_ipi(void)
591de8d3fccSWanpeng Li {
592de8d3fccSWanpeng Li     int ret;
593de8d3fccSWanpeng Li     unsigned long a0 = 0xFFFFFFFF, a1 = 0, a2 = 0xFFFFFFFF, a3 = 0x0;
594de8d3fccSWanpeng Li 
595de8d3fccSWanpeng Li     asm volatile("vmcall" : "=a"(ret) :"a"(KVM_HC_SEND_IPI), "b"(a0), "c"(a1), "d"(a2), "S"(a3));
596de8d3fccSWanpeng Li     report("PV IPIs testing", !ret);
597de8d3fccSWanpeng Li }
598de8d3fccSWanpeng Li 
5997db17e21SThomas Huth int main(void)
6007d36db35SAvi Kivity {
6017d36db35SAvi Kivity     setup_vm();
602f2d2b7c7SAvi Kivity     smp_init();
6037d36db35SAvi Kivity 
6047d36db35SAvi Kivity     test_lapic_existence();
6057d36db35SAvi Kivity 
6067d36db35SAvi Kivity     mask_pic_interrupts();
607a222b5e2SRadim Krčmář     test_apic_id();
608c3ccca3fSJim Mattson     test_apic_disable();
6097d36db35SAvi Kivity     test_enable_x2apic();
6109b6bdb3fSJan Kiszka     test_apicbase();
6117d36db35SAvi Kivity 
6127d36db35SAvi Kivity     test_self_ipi();
6139931b88cSRadim Krčmář     test_physical_broadcast();
614de8d3fccSWanpeng Li     test_pv_ipi();
6157d36db35SAvi Kivity 
616f2d2b7c7SAvi Kivity     test_sti_nmi();
617173e7eacSAvi Kivity     test_multiple_nmi();
6187d36db35SAvi Kivity 
6199f815b29SPeter Xu     test_apic_timer_one_shot();
620d9b2b283SWanpeng Li     test_apic_change_mode();
621d423ca36SLiu, Jinsong     test_tsc_deadline_timer();
622d423ca36SLiu, Jinsong 
623f3cdd159SJan Kiszka     return report_summary();
6247d36db35SAvi Kivity }
625