xref: /kvm-unit-tests/x86/apic.c (revision e38858bce3bd3283b61ac7b32cf414ef6dcec3ae)
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 
10*e38858bcSJim Mattson #define MAX_TPR			0xf
11*e38858bcSJim 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 
707d36db35SAvi Kivity void test_enable_x2apic(void)
717d36db35SAvi Kivity {
7222c7d929SJan Kiszka     u64 invalid_state = APIC_DEFAULT_PHYS_BASE | APIC_BSP | APIC_EXTD;
7322c7d929SJan Kiszka     u64 apic_enabled = APIC_DEFAULT_PHYS_BASE | APIC_BSP | APIC_EN;
7422c7d929SJan Kiszka     u64 x2apic_enabled =
7522c7d929SJan Kiszka         APIC_DEFAULT_PHYS_BASE | APIC_BSP | APIC_EN | APIC_EXTD;
7622c7d929SJan Kiszka 
777d36db35SAvi Kivity     if (enable_x2apic()) {
787d36db35SAvi Kivity         printf("x2apic enabled\n");
7922c7d929SJan Kiszka 
8022c7d929SJan Kiszka         report("x2apic enabled to invalid state",
8122c7d929SJan Kiszka                test_for_exception(GP_VECTOR, do_write_apicbase,
8222c7d929SJan Kiszka                                   &invalid_state));
8322c7d929SJan Kiszka         report("x2apic enabled to apic enabled",
8422c7d929SJan Kiszka                test_for_exception(GP_VECTOR, do_write_apicbase,
8522c7d929SJan Kiszka                                   &apic_enabled));
8622c7d929SJan Kiszka 
8722c7d929SJan Kiszka         wrmsr(MSR_IA32_APICBASE, APIC_DEFAULT_PHYS_BASE | APIC_BSP);
8822c7d929SJan Kiszka         report("disabled to invalid state",
8922c7d929SJan Kiszka                test_for_exception(GP_VECTOR, do_write_apicbase,
9022c7d929SJan Kiszka                                   &invalid_state));
9122c7d929SJan Kiszka         report("disabled to x2apic enabled",
9222c7d929SJan Kiszka                test_for_exception(GP_VECTOR, do_write_apicbase,
9322c7d929SJan Kiszka                                   &x2apic_enabled));
9422c7d929SJan Kiszka 
9522c7d929SJan Kiszka         wrmsr(MSR_IA32_APICBASE, apic_enabled);
9622c7d929SJan Kiszka         report("apic enabled to invalid state",
9722c7d929SJan Kiszka                test_for_exception(GP_VECTOR, do_write_apicbase,
9822c7d929SJan Kiszka                                   &invalid_state));
9922c7d929SJan Kiszka 
10022c7d929SJan Kiszka         wrmsr(MSR_IA32_APICBASE, x2apic_enabled);
10122c7d929SJan Kiszka         apic_write(APIC_SPIV, 0x1ff);
1027d36db35SAvi Kivity     } else {
1037d36db35SAvi Kivity         printf("x2apic not detected\n");
10422c7d929SJan Kiszka 
10522c7d929SJan Kiszka         report("enable unsupported x2apic",
10622c7d929SJan Kiszka                test_for_exception(GP_VECTOR, do_write_apicbase,
10722c7d929SJan Kiszka                                   &x2apic_enabled));
1087d36db35SAvi Kivity     }
1097d36db35SAvi Kivity }
1107d36db35SAvi Kivity 
111*e38858bcSJim Mattson static void verify_disabled_apic_mmio(void)
112*e38858bcSJim Mattson {
113*e38858bcSJim Mattson     volatile u32 *lvr = (volatile u32 *)(APIC_DEFAULT_PHYS_BASE + APIC_LVR);
114*e38858bcSJim Mattson     volatile u32 *tpr = (volatile u32 *)(APIC_DEFAULT_PHYS_BASE + APIC_TASKPRI);
115*e38858bcSJim Mattson     u32 cr8 = read_cr8();
116*e38858bcSJim Mattson 
117*e38858bcSJim Mattson     memset((void *)APIC_DEFAULT_PHYS_BASE, 0xff, PAGE_SIZE);
118*e38858bcSJim Mattson     report("*0xfee00030: %x", *lvr == ~0, *lvr);
119*e38858bcSJim Mattson     report("CR8: %lx", read_cr8() == cr8, read_cr8());
120*e38858bcSJim Mattson     write_cr8(cr8 ^ MAX_TPR);
121*e38858bcSJim Mattson     report("CR8: %lx", read_cr8() == (cr8 ^ MAX_TPR), read_cr8());
122*e38858bcSJim Mattson     report("*0xfee00080: %x", *tpr == ~0, *tpr);
123*e38858bcSJim Mattson     write_cr8(cr8);
124*e38858bcSJim Mattson }
125*e38858bcSJim Mattson 
126c3ccca3fSJim Mattson static void test_apic_disable(void)
127c3ccca3fSJim Mattson {
128*e38858bcSJim Mattson     volatile u32 *lvr = (volatile u32 *)(APIC_DEFAULT_PHYS_BASE + APIC_LVR);
129*e38858bcSJim Mattson     volatile u32 *tpr = (volatile u32 *)(APIC_DEFAULT_PHYS_BASE + APIC_TASKPRI);
130c3ccca3fSJim Mattson     u64 orig_apicbase = rdmsr(MSR_IA32_APICBASE);
131*e38858bcSJim Mattson     u32 apic_version = apic_read(APIC_LVR);
132*e38858bcSJim Mattson     u32 cr8 = read_cr8();
133c3ccca3fSJim Mattson 
134c3ccca3fSJim Mattson     report_prefix_push("apic_disable");
135*e38858bcSJim Mattson     assert_msg(orig_apicbase & APIC_EN, "APIC not enabled.");
136c3ccca3fSJim Mattson 
137*e38858bcSJim Mattson     disable_apic();
138c3ccca3fSJim Mattson     report("Local apic disabled", !(rdmsr(MSR_IA32_APICBASE) & APIC_EN));
139c3ccca3fSJim Mattson     report("CPUID.1H:EDX.APIC[bit 9] is clear", !(cpuid(1).d & (1 << 9)));
140*e38858bcSJim Mattson     verify_disabled_apic_mmio();
141c3ccca3fSJim Mattson 
142*e38858bcSJim Mattson     reset_apic();
143c3ccca3fSJim Mattson     apic_write(APIC_SPIV, 0x1ff);
144*e38858bcSJim Mattson     report("Local apic enabled in xAPIC mode",
145*e38858bcSJim Mattson 	   (rdmsr(MSR_IA32_APICBASE) & (APIC_EN | APIC_EXTD)) == APIC_EN);
146c3ccca3fSJim Mattson     report("CPUID.1H:EDX.APIC[bit 9] is set", cpuid(1).d & (1 << 9));
147*e38858bcSJim Mattson     report("*0xfee00030: %x", *lvr == apic_version, *lvr);
148*e38858bcSJim Mattson     report("*0xfee00080: %x", *tpr == cr8, *tpr);
149*e38858bcSJim Mattson     write_cr8(cr8 ^ MAX_TPR);
150*e38858bcSJim Mattson     report("*0xfee00080: %x", *tpr == (cr8 ^ MAX_TPR) << 4, *tpr);
151*e38858bcSJim Mattson     write_cr8(cr8);
152c3ccca3fSJim Mattson 
153*e38858bcSJim Mattson     if (enable_x2apic()) {
154*e38858bcSJim Mattson 	apic_write(APIC_SPIV, 0x1ff);
155*e38858bcSJim Mattson 	report("Local apic enabled in x2APIC mode",
156*e38858bcSJim Mattson 	   (rdmsr(MSR_IA32_APICBASE) & (APIC_EN | APIC_EXTD)) ==
157*e38858bcSJim Mattson 	   (APIC_EN | APIC_EXTD));
158*e38858bcSJim Mattson 	report("CPUID.1H:EDX.APIC[bit 9] is set", cpuid(1).d & (1 << 9));
159*e38858bcSJim Mattson 	verify_disabled_apic_mmio();
160*e38858bcSJim Mattson 	if (!(orig_apicbase & APIC_EXTD))
161*e38858bcSJim Mattson 	    reset_apic();
162*e38858bcSJim Mattson     }
163c3ccca3fSJim Mattson     report_prefix_pop();
164c3ccca3fSJim Mattson }
165c3ccca3fSJim Mattson 
1669b6bdb3fSJan Kiszka #define ALTERNATE_APIC_BASE	0x42000000
1679b6bdb3fSJan Kiszka 
1689b6bdb3fSJan Kiszka static void test_apicbase(void)
1699b6bdb3fSJan Kiszka {
1709b6bdb3fSJan Kiszka     u64 orig_apicbase = rdmsr(MSR_IA32_APICBASE);
1719b6bdb3fSJan Kiszka     u32 lvr = apic_read(APIC_LVR);
1729b6bdb3fSJan Kiszka     u64 value;
1739b6bdb3fSJan Kiszka 
1749b6bdb3fSJan Kiszka     wrmsr(MSR_IA32_APICBASE, orig_apicbase & ~(APIC_EN | APIC_EXTD));
1759b6bdb3fSJan Kiszka     wrmsr(MSR_IA32_APICBASE, ALTERNATE_APIC_BASE | APIC_BSP | APIC_EN);
1769b6bdb3fSJan Kiszka 
1775bba1769SAndrew Jones     report_prefix_push("apicbase");
1785bba1769SAndrew Jones 
1799b6bdb3fSJan Kiszka     report("relocate apic",
1809b6bdb3fSJan Kiszka            *(volatile u32 *)(ALTERNATE_APIC_BASE + APIC_LVR) == lvr);
1819b6bdb3fSJan Kiszka 
182772befb7SEduardo Habkost     value = orig_apicbase | (1UL << cpuid_maxphyaddr());
1835bba1769SAndrew Jones     report("reserved physaddr bits",
1849b6bdb3fSJan Kiszka            test_for_exception(GP_VECTOR, do_write_apicbase, &value));
1859b6bdb3fSJan Kiszka 
1869b6bdb3fSJan Kiszka     value = orig_apicbase | 1;
1875bba1769SAndrew Jones     report("reserved low bits",
1889b6bdb3fSJan Kiszka            test_for_exception(GP_VECTOR, do_write_apicbase, &value));
1899b6bdb3fSJan Kiszka 
1909b6bdb3fSJan Kiszka     wrmsr(MSR_IA32_APICBASE, orig_apicbase);
1919b6bdb3fSJan Kiszka     apic_write(APIC_SPIV, 0x1ff);
1925bba1769SAndrew Jones 
1935bba1769SAndrew Jones     report_prefix_pop();
1949b6bdb3fSJan Kiszka }
1959b6bdb3fSJan Kiszka 
196a222b5e2SRadim Krčmář static void do_write_apic_id(void *id)
197a222b5e2SRadim Krčmář {
198a222b5e2SRadim Krčmář     apic_write(APIC_ID, *(u32 *)id);
199a222b5e2SRadim Krčmář }
200a222b5e2SRadim Krčmář 
201a222b5e2SRadim Krčmář static void __test_apic_id(void * unused)
202a222b5e2SRadim Krčmář {
203a222b5e2SRadim Krčmář     u32 id, newid;
204a222b5e2SRadim Krčmář     u8  initial_xapic_id = cpuid(1).b >> 24;
205a222b5e2SRadim Krčmář     u32 initial_x2apic_id = cpuid(0xb).d;
206a222b5e2SRadim Krčmář     bool x2apic_mode = rdmsr(MSR_IA32_APICBASE) & APIC_EXTD;
207a222b5e2SRadim Krčmář 
208a222b5e2SRadim Krčmář     if (x2apic_mode)
209a222b5e2SRadim Krčmář         reset_apic();
210a222b5e2SRadim Krčmář 
211a222b5e2SRadim Krčmář     id = apic_id();
212a222b5e2SRadim Krčmář     report("xapic id matches cpuid", initial_xapic_id == id);
213a222b5e2SRadim Krčmář 
214a222b5e2SRadim Krčmář     newid = (id + 1) << 24;
215a222b5e2SRadim Krčmář     report("writeable xapic id",
216a222b5e2SRadim Krčmář             !test_for_exception(GP_VECTOR, do_write_apic_id, &newid) &&
217a222b5e2SRadim Krčmář             id + 1 == apic_id());
218a222b5e2SRadim Krčmář 
219a222b5e2SRadim Krčmář     if (!enable_x2apic())
220a222b5e2SRadim Krčmář         goto out;
221a222b5e2SRadim Krčmář 
222a222b5e2SRadim Krčmář     report("non-writeable x2apic id",
223a222b5e2SRadim Krčmář             test_for_exception(GP_VECTOR, do_write_apic_id, &newid));
224a222b5e2SRadim Krčmář     report("sane x2apic id", initial_xapic_id == (apic_id() & 0xff));
225a222b5e2SRadim Krčmář 
226a222b5e2SRadim Krčmář     /* old QEMUs do not set initial x2APIC ID */
227a222b5e2SRadim Krčmář     report("x2apic id matches cpuid",
228a222b5e2SRadim Krčmář            initial_xapic_id == (initial_x2apic_id & 0xff) &&
229a222b5e2SRadim Krčmář            initial_x2apic_id == apic_id());
230a222b5e2SRadim Krčmář 
231a222b5e2SRadim Krčmář out:
232a222b5e2SRadim Krčmář     reset_apic();
233a222b5e2SRadim Krčmář 
234a222b5e2SRadim Krčmář     report("correct xapic id after reset", initial_xapic_id == apic_id());
235a222b5e2SRadim Krčmář 
236a222b5e2SRadim Krčmář     /* old KVMs do not reset xAPIC ID */
237a222b5e2SRadim Krčmář     if (id != apic_id())
238a222b5e2SRadim Krčmář         apic_write(APIC_ID, id << 24);
239a222b5e2SRadim Krčmář 
240a222b5e2SRadim Krčmář     if (x2apic_mode)
241a222b5e2SRadim Krčmář         enable_x2apic();
242a222b5e2SRadim Krčmář }
243a222b5e2SRadim Krčmář 
244a222b5e2SRadim Krčmář static void test_apic_id(void)
245a222b5e2SRadim Krčmář {
246a222b5e2SRadim Krčmář     if (cpu_count() < 2)
247a222b5e2SRadim Krčmář         return;
248a222b5e2SRadim Krčmář 
249a222b5e2SRadim Krčmář     on_cpu(1, __test_apic_id, NULL);
250a222b5e2SRadim Krčmář }
251a222b5e2SRadim Krčmář 
2527d36db35SAvi Kivity static int ipi_count;
2537d36db35SAvi Kivity 
2547d36db35SAvi Kivity static void self_ipi_isr(isr_regs_t *regs)
2557d36db35SAvi Kivity {
2567d36db35SAvi Kivity     ++ipi_count;
2577d36db35SAvi Kivity     eoi();
2587d36db35SAvi Kivity }
2597d36db35SAvi Kivity 
2607d36db35SAvi Kivity static void test_self_ipi(void)
2617d36db35SAvi Kivity {
2627d36db35SAvi Kivity     int vec = 0xf1;
2637d36db35SAvi Kivity 
264d51bd17eSGleb Natapov     handle_irq(vec, self_ipi_isr);
2657d36db35SAvi Kivity     irq_enable();
2667d36db35SAvi Kivity     apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec,
2677d36db35SAvi Kivity                    0);
2687d36db35SAvi Kivity     asm volatile ("nop");
2697d36db35SAvi Kivity     report("self ipi", ipi_count == 1);
2707d36db35SAvi Kivity }
2717d36db35SAvi Kivity 
272f2d2b7c7SAvi Kivity volatile int nmi_counter_private, nmi_counter, nmi_hlt_counter, sti_loop_active;
273f2d2b7c7SAvi Kivity 
274f2d2b7c7SAvi Kivity void sti_nop(char *p)
275f2d2b7c7SAvi Kivity {
276f2d2b7c7SAvi Kivity     asm volatile (
277f2d2b7c7SAvi Kivity 		  ".globl post_sti \n\t"
278f2d2b7c7SAvi Kivity 		  "sti \n"
279f2d2b7c7SAvi Kivity 		  /*
280f2d2b7c7SAvi Kivity 		   * vmx won't exit on external interrupt if blocked-by-sti,
281f2d2b7c7SAvi Kivity 		   * so give it a reason to exit by accessing an unmapped page.
282f2d2b7c7SAvi Kivity 		   */
283f2d2b7c7SAvi Kivity 		  "post_sti: testb $0, %0 \n\t"
284f2d2b7c7SAvi Kivity 		  "nop \n\t"
285f2d2b7c7SAvi Kivity 		  "cli"
286f2d2b7c7SAvi Kivity 		  : : "m"(*p)
287f2d2b7c7SAvi Kivity 		  );
288f2d2b7c7SAvi Kivity     nmi_counter = nmi_counter_private;
289f2d2b7c7SAvi Kivity }
290f2d2b7c7SAvi Kivity 
291f2d2b7c7SAvi Kivity static void sti_loop(void *ignore)
292f2d2b7c7SAvi Kivity {
293f2d2b7c7SAvi Kivity     unsigned k = 0;
294f2d2b7c7SAvi Kivity 
295f2d2b7c7SAvi Kivity     while (sti_loop_active) {
296f2d2b7c7SAvi Kivity 	sti_nop((char *)(ulong)((k++ * 4096) % (128 * 1024 * 1024)));
297f2d2b7c7SAvi Kivity     }
298f2d2b7c7SAvi Kivity }
299f2d2b7c7SAvi Kivity 
300f2d2b7c7SAvi Kivity static void nmi_handler(isr_regs_t *regs)
301f2d2b7c7SAvi Kivity {
302f2d2b7c7SAvi Kivity     extern void post_sti(void);
303f2d2b7c7SAvi Kivity     ++nmi_counter_private;
304f2d2b7c7SAvi Kivity     nmi_hlt_counter += regs->rip == (ulong)post_sti;
305f2d2b7c7SAvi Kivity }
306f2d2b7c7SAvi Kivity 
307f2d2b7c7SAvi Kivity static void update_cr3(void *cr3)
308f2d2b7c7SAvi Kivity {
309f2d2b7c7SAvi Kivity     write_cr3((ulong)cr3);
310f2d2b7c7SAvi Kivity }
311f2d2b7c7SAvi Kivity 
312f2d2b7c7SAvi Kivity static void test_sti_nmi(void)
313f2d2b7c7SAvi Kivity {
314f2d2b7c7SAvi Kivity     unsigned old_counter;
315f2d2b7c7SAvi Kivity 
316f2d2b7c7SAvi Kivity     if (cpu_count() < 2) {
317f2d2b7c7SAvi Kivity 	return;
318f2d2b7c7SAvi Kivity     }
319f2d2b7c7SAvi Kivity 
320d51bd17eSGleb Natapov     handle_irq(2, nmi_handler);
321f2d2b7c7SAvi Kivity     on_cpu(1, update_cr3, (void *)read_cr3());
322f2d2b7c7SAvi Kivity 
323f2d2b7c7SAvi Kivity     sti_loop_active = 1;
324f2d2b7c7SAvi Kivity     on_cpu_async(1, sti_loop, 0);
325f2d2b7c7SAvi Kivity     while (nmi_counter < 30000) {
326f2d2b7c7SAvi Kivity 	old_counter = nmi_counter;
327f2d2b7c7SAvi Kivity 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 1);
328f2d2b7c7SAvi Kivity 	while (nmi_counter == old_counter) {
329f2d2b7c7SAvi Kivity 	    ;
330f2d2b7c7SAvi Kivity 	}
331f2d2b7c7SAvi Kivity     }
332f2d2b7c7SAvi Kivity     sti_loop_active = 0;
333f2d2b7c7SAvi Kivity     report("nmi-after-sti", nmi_hlt_counter == 0);
334f2d2b7c7SAvi Kivity }
335f2d2b7c7SAvi Kivity 
336173e7eacSAvi Kivity static volatile bool nmi_done, nmi_flushed;
337173e7eacSAvi Kivity static volatile int nmi_received;
338173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr1, cpu1_nmi_ctr1;
339173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr2, cpu1_nmi_ctr2;
340173e7eacSAvi Kivity 
341173e7eacSAvi Kivity static void multiple_nmi_handler(isr_regs_t *regs)
342173e7eacSAvi Kivity {
343173e7eacSAvi Kivity     ++nmi_received;
344173e7eacSAvi Kivity }
345173e7eacSAvi Kivity 
346173e7eacSAvi Kivity static void kick_me_nmi(void *blah)
347173e7eacSAvi Kivity {
348173e7eacSAvi Kivity     while (!nmi_done) {
349173e7eacSAvi Kivity 	++cpu1_nmi_ctr1;
350173e7eacSAvi Kivity 	while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1 && !nmi_done) {
351173e7eacSAvi Kivity 	    pause();
352173e7eacSAvi Kivity 	}
353173e7eacSAvi Kivity 	if (nmi_done) {
354173e7eacSAvi Kivity 	    return;
355173e7eacSAvi Kivity 	}
356173e7eacSAvi Kivity 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
357173e7eacSAvi Kivity 	/* make sure the NMI has arrived by sending an IPI after it */
358173e7eacSAvi Kivity 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT
359173e7eacSAvi Kivity 		       | 0x44, 0);
360173e7eacSAvi Kivity 	++cpu1_nmi_ctr2;
361173e7eacSAvi Kivity 	while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2 && !nmi_done) {
362173e7eacSAvi Kivity 	    pause();
363173e7eacSAvi Kivity 	}
364173e7eacSAvi Kivity     }
365173e7eacSAvi Kivity }
366173e7eacSAvi Kivity 
367173e7eacSAvi Kivity static void flush_nmi(isr_regs_t *regs)
368173e7eacSAvi Kivity {
369173e7eacSAvi Kivity     nmi_flushed = true;
370173e7eacSAvi Kivity     apic_write(APIC_EOI, 0);
371173e7eacSAvi Kivity }
372173e7eacSAvi Kivity 
373173e7eacSAvi Kivity static void test_multiple_nmi(void)
374173e7eacSAvi Kivity {
375173e7eacSAvi Kivity     int i;
376173e7eacSAvi Kivity     bool ok = true;
377173e7eacSAvi Kivity 
378173e7eacSAvi Kivity     if (cpu_count() < 2) {
379173e7eacSAvi Kivity 	return;
380173e7eacSAvi Kivity     }
381173e7eacSAvi Kivity 
382173e7eacSAvi Kivity     sti();
383173e7eacSAvi Kivity     handle_irq(2, multiple_nmi_handler);
384173e7eacSAvi Kivity     handle_irq(0x44, flush_nmi);
385173e7eacSAvi Kivity     on_cpu_async(1, kick_me_nmi, 0);
386173e7eacSAvi Kivity     for (i = 0; i < 1000000; ++i) {
387173e7eacSAvi Kivity 	nmi_flushed = false;
388173e7eacSAvi Kivity 	nmi_received = 0;
389173e7eacSAvi Kivity 	++cpu0_nmi_ctr1;
390173e7eacSAvi Kivity 	while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1) {
391173e7eacSAvi Kivity 	    pause();
392173e7eacSAvi Kivity 	}
393173e7eacSAvi Kivity 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
394173e7eacSAvi Kivity 	while (!nmi_flushed) {
395173e7eacSAvi Kivity 	    pause();
396173e7eacSAvi Kivity 	}
397173e7eacSAvi Kivity 	if (nmi_received != 2) {
398173e7eacSAvi Kivity 	    ok = false;
399173e7eacSAvi Kivity 	    break;
400173e7eacSAvi Kivity 	}
401173e7eacSAvi Kivity 	++cpu0_nmi_ctr2;
402173e7eacSAvi Kivity 	while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2) {
403173e7eacSAvi Kivity 	    pause();
404173e7eacSAvi Kivity 	}
405173e7eacSAvi Kivity     }
406173e7eacSAvi Kivity     nmi_done = true;
407173e7eacSAvi Kivity     report("multiple nmi", ok);
408173e7eacSAvi Kivity }
409173e7eacSAvi Kivity 
4109f815b29SPeter Xu static volatile int lvtt_counter = 0;
4119f815b29SPeter Xu 
4129f815b29SPeter Xu static void lvtt_handler(isr_regs_t *regs)
4139f815b29SPeter Xu {
4149f815b29SPeter Xu     lvtt_counter++;
4159f815b29SPeter Xu     eoi();
4169f815b29SPeter Xu }
4179f815b29SPeter Xu 
4189f815b29SPeter Xu static void test_apic_timer_one_shot(void)
4199f815b29SPeter Xu {
4209f815b29SPeter Xu     uint64_t tsc1, tsc2;
4219f815b29SPeter Xu     static const uint32_t interval = 0x10000;
4229f815b29SPeter Xu 
4239f815b29SPeter Xu #define APIC_LVT_TIMER_VECTOR    (0xee)
4249f815b29SPeter Xu 
4259f815b29SPeter Xu     handle_irq(APIC_LVT_TIMER_VECTOR, lvtt_handler);
4269f815b29SPeter Xu     irq_enable();
4279f815b29SPeter Xu 
4289f815b29SPeter Xu     /* One shot mode */
4299111ccabSRadim Krčmář     apic_write(APIC_LVTT, APIC_LVT_TIMER_ONESHOT |
4309f815b29SPeter Xu                APIC_LVT_TIMER_VECTOR);
4319f815b29SPeter Xu     /* Divider == 1 */
4329f815b29SPeter Xu     apic_write(APIC_TDCR, 0x0000000b);
4339f815b29SPeter Xu 
4349f815b29SPeter Xu     tsc1 = rdtsc();
4359f815b29SPeter Xu     /* Set "Initial Counter Register", which starts the timer */
4369f815b29SPeter Xu     apic_write(APIC_TMICT, interval);
4379f815b29SPeter Xu     while (!lvtt_counter);
4389f815b29SPeter Xu     tsc2 = rdtsc();
4399f815b29SPeter Xu 
4409f815b29SPeter Xu     /*
4419f815b29SPeter Xu      * For LVT Timer clock, SDM vol 3 10.5.4 says it should be
4429f815b29SPeter Xu      * derived from processor's bus clock (IIUC which is the same
4439f815b29SPeter Xu      * as TSC), however QEMU seems to be using nanosecond. In all
4449f815b29SPeter Xu      * cases, the following should satisfy on all modern
4459f815b29SPeter Xu      * processors.
4469f815b29SPeter Xu      */
4479f815b29SPeter Xu     report("APIC LVT timer one shot", (lvtt_counter == 1) &&
4489f815b29SPeter Xu            (tsc2 - tsc1 >= interval));
4499f815b29SPeter Xu }
4509f815b29SPeter Xu 
4519931b88cSRadim Krčmář static atomic_t broadcast_counter;
4529931b88cSRadim Krčmář 
4539931b88cSRadim Krčmář static void broadcast_handler(isr_regs_t *regs)
4549931b88cSRadim Krčmář {
4559931b88cSRadim Krčmář 	atomic_inc(&broadcast_counter);
4569931b88cSRadim Krčmář 	eoi();
4579931b88cSRadim Krčmář }
4589931b88cSRadim Krčmář 
4599931b88cSRadim Krčmář static bool broadcast_received(unsigned ncpus)
4609931b88cSRadim Krčmář {
4619931b88cSRadim Krčmář 	unsigned counter;
4629931b88cSRadim Krčmář 	u64 start = rdtsc();
4639931b88cSRadim Krčmář 
4649931b88cSRadim Krčmář 	do {
4659931b88cSRadim Krčmář 		counter = atomic_read(&broadcast_counter);
4669931b88cSRadim Krčmář 		if (counter >= ncpus)
4679931b88cSRadim Krčmář 			break;
4689931b88cSRadim Krčmář 		pause();
4699931b88cSRadim Krčmář 	} while (rdtsc() - start < 1000000000);
4709931b88cSRadim Krčmář 
4719931b88cSRadim Krčmář 	atomic_set(&broadcast_counter, 0);
4729931b88cSRadim Krčmář 
4739931b88cSRadim Krčmář 	return counter == ncpus;
4749931b88cSRadim Krčmář }
4759931b88cSRadim Krčmář 
4769931b88cSRadim Krčmář static void test_physical_broadcast(void)
4779931b88cSRadim Krčmář {
4789931b88cSRadim Krčmář 	unsigned ncpus = cpu_count();
4799931b88cSRadim Krčmář 	unsigned long cr3 = read_cr3();
4809931b88cSRadim Krčmář 	u32 broadcast_address = enable_x2apic() ? 0xffffffff : 0xff;
4819931b88cSRadim Krčmář 
4829931b88cSRadim Krčmář 	handle_irq(BROADCAST_VECTOR, broadcast_handler);
4839931b88cSRadim Krčmář 	for (int c = 1; c < ncpus; c++)
4849931b88cSRadim Krčmář 		on_cpu(c, update_cr3, (void *)cr3);
4859931b88cSRadim Krčmář 
4869931b88cSRadim Krčmář 	printf("starting broadcast (%s)\n", enable_x2apic() ? "x2apic" : "xapic");
4879931b88cSRadim Krčmář 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT |
4889931b88cSRadim Krčmář 			BROADCAST_VECTOR, broadcast_address);
4899931b88cSRadim Krčmář 	report("APIC physical broadcast address", broadcast_received(ncpus));
4909931b88cSRadim Krčmář 
4919931b88cSRadim Krčmář 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT |
4929931b88cSRadim Krčmář 			BROADCAST_VECTOR | APIC_DEST_ALLINC, 0);
4939931b88cSRadim Krčmář 	report("APIC physical broadcast shorthand", broadcast_received(ncpus));
4949931b88cSRadim Krčmář }
4959931b88cSRadim Krčmář 
496d9b2b283SWanpeng Li void wait_until_tmcct_is_zero(uint32_t initial_count, bool stop_when_half)
497d9b2b283SWanpeng Li {
498d9b2b283SWanpeng Li 	uint32_t tmcct = apic_read(APIC_TMCCT);
499d9b2b283SWanpeng Li 
500d9b2b283SWanpeng Li 	if (tmcct) {
501d9b2b283SWanpeng Li 		while (tmcct > (initial_count / 2))
502d9b2b283SWanpeng Li 			tmcct = apic_read(APIC_TMCCT);
503d9b2b283SWanpeng Li 
504d9b2b283SWanpeng Li 		if ( stop_when_half )
505d9b2b283SWanpeng Li 			return;
506d9b2b283SWanpeng Li 
507d9b2b283SWanpeng Li 		/* Wait until the counter reach 0 or wrap-around */
508d9b2b283SWanpeng Li 		while ( tmcct <= (initial_count / 2) && tmcct > 0 )
509d9b2b283SWanpeng Li 			tmcct = apic_read(APIC_TMCCT);
510d9b2b283SWanpeng Li 	}
511d9b2b283SWanpeng Li }
512d9b2b283SWanpeng Li 
513d9b2b283SWanpeng Li static inline void apic_change_mode(unsigned long new_mode)
514d9b2b283SWanpeng Li {
515d9b2b283SWanpeng Li 	uint32_t lvtt;
516d9b2b283SWanpeng Li 
517d9b2b283SWanpeng Li 	lvtt = apic_read(APIC_LVTT);
518d9b2b283SWanpeng Li 	apic_write(APIC_LVTT, (lvtt & ~APIC_LVT_TIMER_MASK) | new_mode);
519d9b2b283SWanpeng Li }
520d9b2b283SWanpeng Li 
521d9b2b283SWanpeng Li static void test_apic_change_mode()
522d9b2b283SWanpeng Li {
523d9b2b283SWanpeng Li 	uint32_t tmict = 0x999999;
524d9b2b283SWanpeng Li 
525d9b2b283SWanpeng Li 	printf("starting apic change mode\n");
526d9b2b283SWanpeng Li 
527d9b2b283SWanpeng Li 	apic_write(APIC_TMICT, tmict);
528d9b2b283SWanpeng Li 
529d9b2b283SWanpeng Li 	apic_change_mode(APIC_LVT_TIMER_PERIODIC);
530d9b2b283SWanpeng Li 
531d9b2b283SWanpeng Li 	report("TMICT value reset", apic_read(APIC_TMICT) == tmict);
532d9b2b283SWanpeng Li 
533d9b2b283SWanpeng Li 	/* Testing one-shot */
534d9b2b283SWanpeng Li 	apic_change_mode(APIC_LVT_TIMER_ONESHOT);
535d9b2b283SWanpeng Li 	apic_write(APIC_TMICT, tmict);
536d9b2b283SWanpeng Li 	report("TMCCT should have a non-zero value", apic_read(APIC_TMCCT));
537d9b2b283SWanpeng Li 
538d9b2b283SWanpeng Li 	wait_until_tmcct_is_zero(tmict, false);
539d9b2b283SWanpeng Li 	report("TMCCT should have reached 0", !apic_read(APIC_TMCCT));
540d9b2b283SWanpeng Li 
541d9b2b283SWanpeng Li 	/*
542d9b2b283SWanpeng Li 	 * Write TMICT before changing mode from one-shot to periodic TMCCT should
543d9b2b283SWanpeng Li 	 * be reset to TMICT periodicly
544d9b2b283SWanpeng Li 	 */
545d9b2b283SWanpeng Li 	apic_write(APIC_TMICT, tmict);
546d9b2b283SWanpeng Li 	wait_until_tmcct_is_zero(tmict, true);
547d9b2b283SWanpeng Li 	apic_change_mode(APIC_LVT_TIMER_PERIODIC);
548d9b2b283SWanpeng Li 	report("TMCCT should have a non-zero value", apic_read(APIC_TMCCT));
549d9b2b283SWanpeng Li 
550d9b2b283SWanpeng Li 	/*
551d9b2b283SWanpeng Li 	 * After the change of mode, the counter should not be reset and continue
552d9b2b283SWanpeng Li 	 * counting down from where it was
553d9b2b283SWanpeng Li 	 */
554d9b2b283SWanpeng Li 	report("TMCCT should not be reset to TMICT value", apic_read(APIC_TMCCT) < (tmict / 2));
555d9b2b283SWanpeng Li 	wait_until_tmcct_is_zero(tmict, false);
556d9b2b283SWanpeng Li 	report("TMCCT should be reset to the initial-count", apic_read(APIC_TMCCT) > (tmict / 2));
557d9b2b283SWanpeng Li 
558d9b2b283SWanpeng Li 	wait_until_tmcct_is_zero(tmict, true);
559d9b2b283SWanpeng Li 	/*
560d9b2b283SWanpeng Li 	 * Keep the same TMICT and change timer mode to one-shot
561d9b2b283SWanpeng Li 	 * TMCCT should be > 0 and count-down to 0
562d9b2b283SWanpeng Li 	 */
563d9b2b283SWanpeng Li 	apic_change_mode(APIC_LVT_TIMER_ONESHOT);
564d9b2b283SWanpeng Li 	report("TMCCT should not be reset to init", apic_read(APIC_TMCCT) < (tmict / 2));
565d9b2b283SWanpeng Li 	wait_until_tmcct_is_zero(tmict, false);
566d9b2b283SWanpeng Li 	report("TMCCT should have reach zero", !apic_read(APIC_TMCCT));
567d9b2b283SWanpeng Li 
568d9b2b283SWanpeng Li 	/* now tmcct == 0 and tmict != 0 */
569d9b2b283SWanpeng Li 	apic_change_mode(APIC_LVT_TIMER_PERIODIC);
570d9b2b283SWanpeng Li 	report("TMCCT should stay at zero", !apic_read(APIC_TMCCT));
571d9b2b283SWanpeng Li }
572d9b2b283SWanpeng Li 
5737d36db35SAvi Kivity int main()
5747d36db35SAvi Kivity {
5757d36db35SAvi Kivity     setup_vm();
576f2d2b7c7SAvi Kivity     smp_init();
5777d36db35SAvi Kivity 
5787d36db35SAvi Kivity     test_lapic_existence();
5797d36db35SAvi Kivity 
5807d36db35SAvi Kivity     mask_pic_interrupts();
581a222b5e2SRadim Krčmář     test_apic_id();
582c3ccca3fSJim Mattson     test_apic_disable();
5837d36db35SAvi Kivity     test_enable_x2apic();
5849b6bdb3fSJan Kiszka     test_apicbase();
5857d36db35SAvi Kivity 
5867d36db35SAvi Kivity     test_self_ipi();
5879931b88cSRadim Krčmář     test_physical_broadcast();
5887d36db35SAvi Kivity 
589f2d2b7c7SAvi Kivity     test_sti_nmi();
590173e7eacSAvi Kivity     test_multiple_nmi();
5917d36db35SAvi Kivity 
5929f815b29SPeter Xu     test_apic_timer_one_shot();
593d9b2b283SWanpeng Li     test_apic_change_mode();
594d423ca36SLiu, Jinsong     test_tsc_deadline_timer();
595d423ca36SLiu, Jinsong 
596f3cdd159SJan Kiszka     return report_summary();
5977d36db35SAvi Kivity }
598