xref: /kvm-unit-tests/x86/apic.c (revision 6c0999f45c240a89ac1c151f9d8b27b241e4046b)
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     return test_for_exception(GP_VECTOR, do_write_apicbase, &data);
7349f5ad9eSPaolo Bonzini }
7449f5ad9eSPaolo Bonzini 
75db4898e8SThomas Huth static void test_enable_x2apic(void)
767d36db35SAvi Kivity {
772092999cSSean Christopherson     u64 orig_apicbase = rdmsr(MSR_IA32_APICBASE);
782092999cSSean Christopherson     u64 apicbase;
792092999cSSean Christopherson 
807d36db35SAvi Kivity     if (enable_x2apic()) {
817d36db35SAvi Kivity         printf("x2apic enabled\n");
8222c7d929SJan Kiszka 
832092999cSSean Christopherson         apicbase = orig_apicbase & ~(APIC_EN | APIC_EXTD);
8422c7d929SJan Kiszka         report("x2apic enabled to invalid state",
852092999cSSean Christopherson                test_write_apicbase_exception(apicbase | APIC_EXTD));
8622c7d929SJan Kiszka         report("x2apic enabled to apic enabled",
872092999cSSean Christopherson                test_write_apicbase_exception(apicbase | APIC_EN));
8822c7d929SJan Kiszka 
8949f5ad9eSPaolo Bonzini         report("x2apic enabled to disabled state",
902092999cSSean Christopherson                !test_write_apicbase_exception(apicbase | 0));
9122c7d929SJan Kiszka         report("disabled to invalid state",
922092999cSSean Christopherson                test_write_apicbase_exception(apicbase | APIC_EXTD));
9322c7d929SJan Kiszka         report("disabled to x2apic enabled",
942092999cSSean Christopherson                test_write_apicbase_exception(apicbase | APIC_EN | APIC_EXTD));
9522c7d929SJan Kiszka 
962092999cSSean Christopherson         report("apic disabled to apic enabled",
972092999cSSean Christopherson                !test_write_apicbase_exception(apicbase | APIC_EN));
9822c7d929SJan Kiszka         report("apic enabled to invalid state",
992092999cSSean Christopherson                test_write_apicbase_exception(apicbase | APIC_EXTD));
10022c7d929SJan Kiszka 
1012092999cSSean Christopherson         if (orig_apicbase & APIC_EXTD)
1022092999cSSean Christopherson             enable_x2apic();
1032092999cSSean Christopherson         else
1042092999cSSean Christopherson             reset_apic();
1052092999cSSean Christopherson 
1062092999cSSean Christopherson         /*
1072092999cSSean Christopherson          * Disabling the APIC resets various APIC registers, restore them to
1082092999cSSean Christopherson          * their desired values.
1092092999cSSean Christopherson          */
11022c7d929SJan Kiszka         apic_write(APIC_SPIV, 0x1ff);
1117d36db35SAvi Kivity     } else {
1127d36db35SAvi Kivity         printf("x2apic not detected\n");
11322c7d929SJan Kiszka 
11422c7d929SJan Kiszka         report("enable unsupported x2apic",
11549f5ad9eSPaolo Bonzini                test_write_apicbase_exception(APIC_EN | APIC_EXTD));
1167d36db35SAvi Kivity     }
1177d36db35SAvi Kivity }
1187d36db35SAvi Kivity 
119e38858bcSJim Mattson static void verify_disabled_apic_mmio(void)
120e38858bcSJim Mattson {
121e38858bcSJim Mattson     volatile u32 *lvr = (volatile u32 *)(APIC_DEFAULT_PHYS_BASE + APIC_LVR);
122e38858bcSJim Mattson     volatile u32 *tpr = (volatile u32 *)(APIC_DEFAULT_PHYS_BASE + APIC_TASKPRI);
123e38858bcSJim Mattson     u32 cr8 = read_cr8();
124e38858bcSJim Mattson 
125e38858bcSJim Mattson     memset((void *)APIC_DEFAULT_PHYS_BASE, 0xff, PAGE_SIZE);
126e38858bcSJim Mattson     report("*0xfee00030: %x", *lvr == ~0, *lvr);
127e38858bcSJim Mattson     report("CR8: %lx", read_cr8() == cr8, read_cr8());
128e38858bcSJim Mattson     write_cr8(cr8 ^ MAX_TPR);
129e38858bcSJim Mattson     report("CR8: %lx", read_cr8() == (cr8 ^ MAX_TPR), read_cr8());
130e38858bcSJim Mattson     report("*0xfee00080: %x", *tpr == ~0, *tpr);
131e38858bcSJim Mattson     write_cr8(cr8);
132e38858bcSJim Mattson }
133e38858bcSJim Mattson 
134c3ccca3fSJim Mattson static void test_apic_disable(void)
135c3ccca3fSJim Mattson {
136e38858bcSJim Mattson     volatile u32 *lvr = (volatile u32 *)(APIC_DEFAULT_PHYS_BASE + APIC_LVR);
137e38858bcSJim Mattson     volatile u32 *tpr = (volatile u32 *)(APIC_DEFAULT_PHYS_BASE + APIC_TASKPRI);
138c3ccca3fSJim Mattson     u64 orig_apicbase = rdmsr(MSR_IA32_APICBASE);
139e38858bcSJim Mattson     u32 apic_version = apic_read(APIC_LVR);
140e38858bcSJim Mattson     u32 cr8 = read_cr8();
141c3ccca3fSJim Mattson 
142c3ccca3fSJim Mattson     report_prefix_push("apic_disable");
143e38858bcSJim Mattson     assert_msg(orig_apicbase & APIC_EN, "APIC not enabled.");
144c3ccca3fSJim Mattson 
145e38858bcSJim Mattson     disable_apic();
146c3ccca3fSJim Mattson     report("Local apic disabled", !(rdmsr(MSR_IA32_APICBASE) & APIC_EN));
147c3ccca3fSJim Mattson     report("CPUID.1H:EDX.APIC[bit 9] is clear", !(cpuid(1).d & (1 << 9)));
148e38858bcSJim Mattson     verify_disabled_apic_mmio();
149c3ccca3fSJim Mattson 
150e38858bcSJim Mattson     reset_apic();
151c3ccca3fSJim Mattson     apic_write(APIC_SPIV, 0x1ff);
152e38858bcSJim Mattson     report("Local apic enabled in xAPIC mode",
153e38858bcSJim Mattson 	   (rdmsr(MSR_IA32_APICBASE) & (APIC_EN | APIC_EXTD)) == APIC_EN);
154c3ccca3fSJim Mattson     report("CPUID.1H:EDX.APIC[bit 9] is set", cpuid(1).d & (1 << 9));
155e38858bcSJim Mattson     report("*0xfee00030: %x", *lvr == apic_version, *lvr);
156e38858bcSJim Mattson     report("*0xfee00080: %x", *tpr == cr8, *tpr);
157e38858bcSJim Mattson     write_cr8(cr8 ^ MAX_TPR);
158e38858bcSJim Mattson     report("*0xfee00080: %x", *tpr == (cr8 ^ MAX_TPR) << 4, *tpr);
159e38858bcSJim Mattson     write_cr8(cr8);
160c3ccca3fSJim Mattson 
161e38858bcSJim Mattson     if (enable_x2apic()) {
162e38858bcSJim Mattson 	apic_write(APIC_SPIV, 0x1ff);
163e38858bcSJim Mattson 	report("Local apic enabled in x2APIC mode",
164e38858bcSJim Mattson 	   (rdmsr(MSR_IA32_APICBASE) & (APIC_EN | APIC_EXTD)) ==
165e38858bcSJim Mattson 	   (APIC_EN | APIC_EXTD));
166e38858bcSJim Mattson 	report("CPUID.1H:EDX.APIC[bit 9] is set", cpuid(1).d & (1 << 9));
167e38858bcSJim Mattson 	verify_disabled_apic_mmio();
168e38858bcSJim Mattson 	if (!(orig_apicbase & APIC_EXTD))
169e38858bcSJim Mattson 	    reset_apic();
170e38858bcSJim Mattson     }
171c3ccca3fSJim Mattson     report_prefix_pop();
172c3ccca3fSJim Mattson }
173c3ccca3fSJim Mattson 
1749b6bdb3fSJan Kiszka #define ALTERNATE_APIC_BASE	0x42000000
1759b6bdb3fSJan Kiszka 
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 
1879b6bdb3fSJan Kiszka     report("relocate apic",
1889b6bdb3fSJan Kiszka            *(volatile u32 *)(ALTERNATE_APIC_BASE + APIC_LVR) == lvr);
1899b6bdb3fSJan Kiszka 
190772befb7SEduardo Habkost     value = orig_apicbase | (1UL << cpuid_maxphyaddr());
1915bba1769SAndrew Jones     report("reserved physaddr bits",
1929b6bdb3fSJan Kiszka            test_for_exception(GP_VECTOR, do_write_apicbase, &value));
1939b6bdb3fSJan Kiszka 
1949b6bdb3fSJan Kiszka     value = orig_apicbase | 1;
1955bba1769SAndrew Jones     report("reserved low bits",
1969b6bdb3fSJan Kiszka            test_for_exception(GP_VECTOR, do_write_apicbase, &value));
1979b6bdb3fSJan Kiszka 
1989b6bdb3fSJan Kiszka     wrmsr(MSR_IA32_APICBASE, orig_apicbase);
1999b6bdb3fSJan Kiszka     apic_write(APIC_SPIV, 0x1ff);
2005bba1769SAndrew Jones 
2015bba1769SAndrew Jones     report_prefix_pop();
2029b6bdb3fSJan Kiszka }
2039b6bdb3fSJan Kiszka 
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ář 
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;
214a222b5e2SRadim Krčmář     bool x2apic_mode = rdmsr(MSR_IA32_APICBASE) & APIC_EXTD;
215a222b5e2SRadim Krčmář 
216a222b5e2SRadim Krčmář     if (x2apic_mode)
217a222b5e2SRadim Krčmář         reset_apic();
218a222b5e2SRadim Krčmář 
219a222b5e2SRadim Krčmář     id = apic_id();
220a222b5e2SRadim Krčmář     report("xapic id matches cpuid", initial_xapic_id == id);
221a222b5e2SRadim Krčmář 
222a222b5e2SRadim Krčmář     newid = (id + 1) << 24;
223a222b5e2SRadim Krčmář     report("writeable xapic id",
224a222b5e2SRadim Krčmář             !test_for_exception(GP_VECTOR, do_write_apic_id, &newid) &&
225a222b5e2SRadim Krčmář             id + 1 == apic_id());
226a222b5e2SRadim Krčmář 
227a222b5e2SRadim Krčmář     if (!enable_x2apic())
228a222b5e2SRadim Krčmář         goto out;
229a222b5e2SRadim Krčmář 
230a222b5e2SRadim Krčmář     report("non-writeable x2apic id",
231a222b5e2SRadim Krčmář             test_for_exception(GP_VECTOR, do_write_apic_id, &newid));
232a222b5e2SRadim Krčmář     report("sane x2apic id", initial_xapic_id == (apic_id() & 0xff));
233a222b5e2SRadim Krčmář 
234a222b5e2SRadim Krčmář     /* old QEMUs do not set initial x2APIC ID */
235a222b5e2SRadim Krčmář     report("x2apic id matches cpuid",
236a222b5e2SRadim Krčmář            initial_xapic_id == (initial_x2apic_id & 0xff) &&
237a222b5e2SRadim Krčmář            initial_x2apic_id == apic_id());
238a222b5e2SRadim Krčmář 
239a222b5e2SRadim Krčmář out:
240a222b5e2SRadim Krčmář     reset_apic();
241a222b5e2SRadim Krčmář 
242a222b5e2SRadim Krčmář     report("correct xapic id after reset", initial_xapic_id == apic_id());
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ář 
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ář 
2607d36db35SAvi Kivity static int ipi_count;
2617d36db35SAvi Kivity 
2627d36db35SAvi Kivity static void self_ipi_isr(isr_regs_t *regs)
2637d36db35SAvi Kivity {
2647d36db35SAvi Kivity     ++ipi_count;
2657d36db35SAvi Kivity     eoi();
2667d36db35SAvi Kivity }
2677d36db35SAvi Kivity 
2687d36db35SAvi Kivity static void test_self_ipi(void)
2697d36db35SAvi Kivity {
270*6c0999f4SNadav Amit     u64 start = rdtsc();
2717d36db35SAvi Kivity     int vec = 0xf1;
2727d36db35SAvi Kivity 
273d51bd17eSGleb Natapov     handle_irq(vec, self_ipi_isr);
2747d36db35SAvi Kivity     irq_enable();
2757d36db35SAvi Kivity     apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec,
2767d36db35SAvi Kivity                    0);
277*6c0999f4SNadav Amit 
278*6c0999f4SNadav Amit     do {
279*6c0999f4SNadav Amit         pause();
280*6c0999f4SNadav Amit     } while (rdtsc() - start < 1000000000 && ipi_count == 0);
281*6c0999f4SNadav Amit 
2827d36db35SAvi Kivity     report("self ipi", ipi_count == 1);
2837d36db35SAvi Kivity }
2847d36db35SAvi Kivity 
285f2d2b7c7SAvi Kivity volatile int nmi_counter_private, nmi_counter, nmi_hlt_counter, sti_loop_active;
286f2d2b7c7SAvi Kivity 
287db4898e8SThomas Huth static void sti_nop(char *p)
288f2d2b7c7SAvi Kivity {
289f2d2b7c7SAvi Kivity     asm volatile (
290f2d2b7c7SAvi Kivity 		  ".globl post_sti \n\t"
291f2d2b7c7SAvi Kivity 		  "sti \n"
292f2d2b7c7SAvi Kivity 		  /*
293f2d2b7c7SAvi Kivity 		   * vmx won't exit on external interrupt if blocked-by-sti,
294f2d2b7c7SAvi Kivity 		   * so give it a reason to exit by accessing an unmapped page.
295f2d2b7c7SAvi Kivity 		   */
296f2d2b7c7SAvi Kivity 		  "post_sti: testb $0, %0 \n\t"
297f2d2b7c7SAvi Kivity 		  "nop \n\t"
298f2d2b7c7SAvi Kivity 		  "cli"
299f2d2b7c7SAvi Kivity 		  : : "m"(*p)
300f2d2b7c7SAvi Kivity 		  );
301f2d2b7c7SAvi Kivity     nmi_counter = nmi_counter_private;
302f2d2b7c7SAvi Kivity }
303f2d2b7c7SAvi Kivity 
304f2d2b7c7SAvi Kivity static void sti_loop(void *ignore)
305f2d2b7c7SAvi Kivity {
306f2d2b7c7SAvi Kivity     unsigned k = 0;
307f2d2b7c7SAvi Kivity 
308f2d2b7c7SAvi Kivity     while (sti_loop_active) {
309f2d2b7c7SAvi Kivity 	sti_nop((char *)(ulong)((k++ * 4096) % (128 * 1024 * 1024)));
310f2d2b7c7SAvi Kivity     }
311f2d2b7c7SAvi Kivity }
312f2d2b7c7SAvi Kivity 
313f2d2b7c7SAvi Kivity static void nmi_handler(isr_regs_t *regs)
314f2d2b7c7SAvi Kivity {
315f2d2b7c7SAvi Kivity     extern void post_sti(void);
316f2d2b7c7SAvi Kivity     ++nmi_counter_private;
317f2d2b7c7SAvi Kivity     nmi_hlt_counter += regs->rip == (ulong)post_sti;
318f2d2b7c7SAvi Kivity }
319f2d2b7c7SAvi Kivity 
320f2d2b7c7SAvi Kivity static void update_cr3(void *cr3)
321f2d2b7c7SAvi Kivity {
322f2d2b7c7SAvi Kivity     write_cr3((ulong)cr3);
323f2d2b7c7SAvi Kivity }
324f2d2b7c7SAvi Kivity 
325f2d2b7c7SAvi Kivity static void test_sti_nmi(void)
326f2d2b7c7SAvi Kivity {
327f2d2b7c7SAvi Kivity     unsigned old_counter;
328f2d2b7c7SAvi Kivity 
329f2d2b7c7SAvi Kivity     if (cpu_count() < 2) {
330f2d2b7c7SAvi Kivity 	return;
331f2d2b7c7SAvi Kivity     }
332f2d2b7c7SAvi Kivity 
333d51bd17eSGleb Natapov     handle_irq(2, nmi_handler);
334f2d2b7c7SAvi Kivity     on_cpu(1, update_cr3, (void *)read_cr3());
335f2d2b7c7SAvi Kivity 
336f2d2b7c7SAvi Kivity     sti_loop_active = 1;
337f2d2b7c7SAvi Kivity     on_cpu_async(1, sti_loop, 0);
338f2d2b7c7SAvi Kivity     while (nmi_counter < 30000) {
339f2d2b7c7SAvi Kivity 	old_counter = nmi_counter;
340f2d2b7c7SAvi Kivity 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 1);
341f2d2b7c7SAvi Kivity 	while (nmi_counter == old_counter) {
342f2d2b7c7SAvi Kivity 	    ;
343f2d2b7c7SAvi Kivity 	}
344f2d2b7c7SAvi Kivity     }
345f2d2b7c7SAvi Kivity     sti_loop_active = 0;
346f2d2b7c7SAvi Kivity     report("nmi-after-sti", nmi_hlt_counter == 0);
347f2d2b7c7SAvi Kivity }
348f2d2b7c7SAvi Kivity 
349173e7eacSAvi Kivity static volatile bool nmi_done, nmi_flushed;
350173e7eacSAvi Kivity static volatile int nmi_received;
351173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr1, cpu1_nmi_ctr1;
352173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr2, cpu1_nmi_ctr2;
353173e7eacSAvi Kivity 
354173e7eacSAvi Kivity static void multiple_nmi_handler(isr_regs_t *regs)
355173e7eacSAvi Kivity {
356173e7eacSAvi Kivity     ++nmi_received;
357173e7eacSAvi Kivity }
358173e7eacSAvi Kivity 
359173e7eacSAvi Kivity static void kick_me_nmi(void *blah)
360173e7eacSAvi Kivity {
361173e7eacSAvi Kivity     while (!nmi_done) {
362173e7eacSAvi Kivity 	++cpu1_nmi_ctr1;
363173e7eacSAvi Kivity 	while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1 && !nmi_done) {
364173e7eacSAvi Kivity 	    pause();
365173e7eacSAvi Kivity 	}
366173e7eacSAvi Kivity 	if (nmi_done) {
367173e7eacSAvi Kivity 	    return;
368173e7eacSAvi Kivity 	}
369173e7eacSAvi Kivity 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
370173e7eacSAvi Kivity 	/* make sure the NMI has arrived by sending an IPI after it */
371173e7eacSAvi Kivity 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT
372173e7eacSAvi Kivity 		       | 0x44, 0);
373173e7eacSAvi Kivity 	++cpu1_nmi_ctr2;
374173e7eacSAvi Kivity 	while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2 && !nmi_done) {
375173e7eacSAvi Kivity 	    pause();
376173e7eacSAvi Kivity 	}
377173e7eacSAvi Kivity     }
378173e7eacSAvi Kivity }
379173e7eacSAvi Kivity 
380173e7eacSAvi Kivity static void flush_nmi(isr_regs_t *regs)
381173e7eacSAvi Kivity {
382173e7eacSAvi Kivity     nmi_flushed = true;
383173e7eacSAvi Kivity     apic_write(APIC_EOI, 0);
384173e7eacSAvi Kivity }
385173e7eacSAvi Kivity 
386173e7eacSAvi Kivity static void test_multiple_nmi(void)
387173e7eacSAvi Kivity {
388173e7eacSAvi Kivity     int i;
389173e7eacSAvi Kivity     bool ok = true;
390173e7eacSAvi Kivity 
391173e7eacSAvi Kivity     if (cpu_count() < 2) {
392173e7eacSAvi Kivity 	return;
393173e7eacSAvi Kivity     }
394173e7eacSAvi Kivity 
395173e7eacSAvi Kivity     sti();
396173e7eacSAvi Kivity     handle_irq(2, multiple_nmi_handler);
397173e7eacSAvi Kivity     handle_irq(0x44, flush_nmi);
398173e7eacSAvi Kivity     on_cpu_async(1, kick_me_nmi, 0);
399173e7eacSAvi Kivity     for (i = 0; i < 1000000; ++i) {
400173e7eacSAvi Kivity 	nmi_flushed = false;
401173e7eacSAvi Kivity 	nmi_received = 0;
402173e7eacSAvi Kivity 	++cpu0_nmi_ctr1;
403173e7eacSAvi Kivity 	while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1) {
404173e7eacSAvi Kivity 	    pause();
405173e7eacSAvi Kivity 	}
406173e7eacSAvi Kivity 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
407173e7eacSAvi Kivity 	while (!nmi_flushed) {
408173e7eacSAvi Kivity 	    pause();
409173e7eacSAvi Kivity 	}
410173e7eacSAvi Kivity 	if (nmi_received != 2) {
411173e7eacSAvi Kivity 	    ok = false;
412173e7eacSAvi Kivity 	    break;
413173e7eacSAvi Kivity 	}
414173e7eacSAvi Kivity 	++cpu0_nmi_ctr2;
415173e7eacSAvi Kivity 	while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2) {
416173e7eacSAvi Kivity 	    pause();
417173e7eacSAvi Kivity 	}
418173e7eacSAvi Kivity     }
419173e7eacSAvi Kivity     nmi_done = true;
420173e7eacSAvi Kivity     report("multiple nmi", ok);
421173e7eacSAvi Kivity }
422173e7eacSAvi Kivity 
4239f815b29SPeter Xu static volatile int lvtt_counter = 0;
4249f815b29SPeter Xu 
4259f815b29SPeter Xu static void lvtt_handler(isr_regs_t *regs)
4269f815b29SPeter Xu {
4279f815b29SPeter Xu     lvtt_counter++;
4289f815b29SPeter Xu     eoi();
4299f815b29SPeter Xu }
4309f815b29SPeter Xu 
4319f815b29SPeter Xu static void test_apic_timer_one_shot(void)
4329f815b29SPeter Xu {
4339f815b29SPeter Xu     uint64_t tsc1, tsc2;
4349f815b29SPeter Xu     static const uint32_t interval = 0x10000;
4359f815b29SPeter Xu 
4369f815b29SPeter Xu #define APIC_LVT_TIMER_VECTOR    (0xee)
4379f815b29SPeter Xu 
4389f815b29SPeter Xu     handle_irq(APIC_LVT_TIMER_VECTOR, lvtt_handler);
4399f815b29SPeter Xu     irq_enable();
4409f815b29SPeter Xu 
4419f815b29SPeter Xu     /* One shot mode */
4429111ccabSRadim Krčmář     apic_write(APIC_LVTT, APIC_LVT_TIMER_ONESHOT |
4439f815b29SPeter Xu                APIC_LVT_TIMER_VECTOR);
4449f815b29SPeter Xu     /* Divider == 1 */
4459f815b29SPeter Xu     apic_write(APIC_TDCR, 0x0000000b);
4469f815b29SPeter Xu 
4479f815b29SPeter Xu     tsc1 = rdtsc();
4489f815b29SPeter Xu     /* Set "Initial Counter Register", which starts the timer */
4499f815b29SPeter Xu     apic_write(APIC_TMICT, interval);
4509f815b29SPeter Xu     while (!lvtt_counter);
4519f815b29SPeter Xu     tsc2 = rdtsc();
4529f815b29SPeter Xu 
4539f815b29SPeter Xu     /*
4549f815b29SPeter Xu      * For LVT Timer clock, SDM vol 3 10.5.4 says it should be
4559f815b29SPeter Xu      * derived from processor's bus clock (IIUC which is the same
4569f815b29SPeter Xu      * as TSC), however QEMU seems to be using nanosecond. In all
4579f815b29SPeter Xu      * cases, the following should satisfy on all modern
4589f815b29SPeter Xu      * processors.
4599f815b29SPeter Xu      */
4609f815b29SPeter Xu     report("APIC LVT timer one shot", (lvtt_counter == 1) &&
4619f815b29SPeter Xu            (tsc2 - tsc1 >= interval));
4629f815b29SPeter Xu }
4639f815b29SPeter Xu 
4649931b88cSRadim Krčmář static atomic_t broadcast_counter;
4659931b88cSRadim Krčmář 
4669931b88cSRadim Krčmář static void broadcast_handler(isr_regs_t *regs)
4679931b88cSRadim Krčmář {
4689931b88cSRadim Krčmář 	atomic_inc(&broadcast_counter);
4699931b88cSRadim Krčmář 	eoi();
4709931b88cSRadim Krčmář }
4719931b88cSRadim Krčmář 
4729931b88cSRadim Krčmář static bool broadcast_received(unsigned ncpus)
4739931b88cSRadim Krčmář {
4749931b88cSRadim Krčmář 	unsigned counter;
4759931b88cSRadim Krčmář 	u64 start = rdtsc();
4769931b88cSRadim Krčmář 
4779931b88cSRadim Krčmář 	do {
4789931b88cSRadim Krčmář 		counter = atomic_read(&broadcast_counter);
4799931b88cSRadim Krčmář 		if (counter >= ncpus)
4809931b88cSRadim Krčmář 			break;
4819931b88cSRadim Krčmář 		pause();
4829931b88cSRadim Krčmář 	} while (rdtsc() - start < 1000000000);
4839931b88cSRadim Krčmář 
4849931b88cSRadim Krčmář 	atomic_set(&broadcast_counter, 0);
4859931b88cSRadim Krčmář 
4869931b88cSRadim Krčmář 	return counter == ncpus;
4879931b88cSRadim Krčmář }
4889931b88cSRadim Krčmář 
4899931b88cSRadim Krčmář static void test_physical_broadcast(void)
4909931b88cSRadim Krčmář {
4919931b88cSRadim Krčmář 	unsigned ncpus = cpu_count();
4929931b88cSRadim Krčmář 	unsigned long cr3 = read_cr3();
4939931b88cSRadim Krčmář 	u32 broadcast_address = enable_x2apic() ? 0xffffffff : 0xff;
4949931b88cSRadim Krčmář 
4959931b88cSRadim Krčmář 	handle_irq(BROADCAST_VECTOR, broadcast_handler);
4969931b88cSRadim Krčmář 	for (int c = 1; c < ncpus; c++)
4979931b88cSRadim Krčmář 		on_cpu(c, update_cr3, (void *)cr3);
4989931b88cSRadim Krčmář 
4999931b88cSRadim Krčmář 	printf("starting broadcast (%s)\n", enable_x2apic() ? "x2apic" : "xapic");
5009931b88cSRadim Krčmář 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT |
5019931b88cSRadim Krčmář 			BROADCAST_VECTOR, broadcast_address);
5029931b88cSRadim Krčmář 	report("APIC physical broadcast address", broadcast_received(ncpus));
5039931b88cSRadim Krčmář 
5049931b88cSRadim Krčmář 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT |
5059931b88cSRadim Krčmář 			BROADCAST_VECTOR | APIC_DEST_ALLINC, 0);
5069931b88cSRadim Krčmář 	report("APIC physical broadcast shorthand", broadcast_received(ncpus));
5079931b88cSRadim Krčmář }
5089931b88cSRadim Krčmář 
5090eac5394SEvgeny Yakovlev static void wait_until_tmcct_common(uint32_t initial_count, bool stop_when_half, bool should_wrap_around)
510d9b2b283SWanpeng Li {
511d9b2b283SWanpeng Li 	uint32_t tmcct = apic_read(APIC_TMCCT);
512d9b2b283SWanpeng Li 
513d9b2b283SWanpeng Li 	if (tmcct) {
514d9b2b283SWanpeng Li 		while (tmcct > (initial_count / 2))
515d9b2b283SWanpeng Li 			tmcct = apic_read(APIC_TMCCT);
516d9b2b283SWanpeng Li 
517d9b2b283SWanpeng Li 		if ( stop_when_half )
518d9b2b283SWanpeng Li 			return;
519d9b2b283SWanpeng Li 
520d9b2b283SWanpeng Li 		/* Wait until the counter reach 0 or wrap-around */
521d9b2b283SWanpeng Li 		while ( tmcct <= (initial_count / 2) && tmcct > 0 )
522d9b2b283SWanpeng Li 			tmcct = apic_read(APIC_TMCCT);
5230eac5394SEvgeny Yakovlev 
5240eac5394SEvgeny Yakovlev 		/* Wait specifically for wrap around to skip 0 TMCCR if we were asked to */
5250eac5394SEvgeny Yakovlev 		while (should_wrap_around && !tmcct)
5260eac5394SEvgeny Yakovlev 			tmcct = apic_read(APIC_TMCCT);
527d9b2b283SWanpeng Li 	}
528d9b2b283SWanpeng Li }
529d9b2b283SWanpeng Li 
5300eac5394SEvgeny Yakovlev static void wait_until_tmcct_is_zero(uint32_t initial_count, bool stop_when_half)
5310eac5394SEvgeny Yakovlev {
5320eac5394SEvgeny Yakovlev 	return wait_until_tmcct_common(initial_count, stop_when_half, false);
5330eac5394SEvgeny Yakovlev }
5340eac5394SEvgeny Yakovlev 
5350eac5394SEvgeny Yakovlev static void wait_until_tmcct_wrap_around(uint32_t initial_count, bool stop_when_half)
5360eac5394SEvgeny Yakovlev {
5370eac5394SEvgeny Yakovlev 	return wait_until_tmcct_common(initial_count, stop_when_half, true);
5380eac5394SEvgeny Yakovlev }
5390eac5394SEvgeny Yakovlev 
540d9b2b283SWanpeng Li static inline void apic_change_mode(unsigned long new_mode)
541d9b2b283SWanpeng Li {
542d9b2b283SWanpeng Li 	uint32_t lvtt;
543d9b2b283SWanpeng Li 
544d9b2b283SWanpeng Li 	lvtt = apic_read(APIC_LVTT);
545d9b2b283SWanpeng Li 	apic_write(APIC_LVTT, (lvtt & ~APIC_LVT_TIMER_MASK) | new_mode);
546d9b2b283SWanpeng Li }
547d9b2b283SWanpeng Li 
5487db17e21SThomas Huth static void test_apic_change_mode(void)
549d9b2b283SWanpeng Li {
550d9b2b283SWanpeng Li 	uint32_t tmict = 0x999999;
551d9b2b283SWanpeng Li 
552d9b2b283SWanpeng Li 	printf("starting apic change mode\n");
553d9b2b283SWanpeng Li 
554d9b2b283SWanpeng Li 	apic_write(APIC_TMICT, tmict);
555d9b2b283SWanpeng Li 
556d9b2b283SWanpeng Li 	apic_change_mode(APIC_LVT_TIMER_PERIODIC);
557d9b2b283SWanpeng Li 
558d9b2b283SWanpeng Li 	report("TMICT value reset", apic_read(APIC_TMICT) == tmict);
559d9b2b283SWanpeng Li 
560d9b2b283SWanpeng Li 	/* Testing one-shot */
561d9b2b283SWanpeng Li 	apic_change_mode(APIC_LVT_TIMER_ONESHOT);
562d9b2b283SWanpeng Li 	apic_write(APIC_TMICT, tmict);
563d9b2b283SWanpeng Li 	report("TMCCT should have a non-zero value", apic_read(APIC_TMCCT));
564d9b2b283SWanpeng Li 
565d9b2b283SWanpeng Li 	wait_until_tmcct_is_zero(tmict, false);
566d9b2b283SWanpeng Li 	report("TMCCT should have reached 0", !apic_read(APIC_TMCCT));
567d9b2b283SWanpeng Li 
568d9b2b283SWanpeng Li 	/*
569d9b2b283SWanpeng Li 	 * Write TMICT before changing mode from one-shot to periodic TMCCT should
570d9b2b283SWanpeng Li 	 * be reset to TMICT periodicly
571d9b2b283SWanpeng Li 	 */
572d9b2b283SWanpeng Li 	apic_write(APIC_TMICT, tmict);
573d9b2b283SWanpeng Li 	wait_until_tmcct_is_zero(tmict, true);
574d9b2b283SWanpeng Li 	apic_change_mode(APIC_LVT_TIMER_PERIODIC);
575d9b2b283SWanpeng Li 	report("TMCCT should have a non-zero value", apic_read(APIC_TMCCT));
576d9b2b283SWanpeng Li 
577d9b2b283SWanpeng Li 	/*
578d9b2b283SWanpeng Li 	 * After the change of mode, the counter should not be reset and continue
579d9b2b283SWanpeng Li 	 * counting down from where it was
580d9b2b283SWanpeng Li 	 */
581d9b2b283SWanpeng Li 	report("TMCCT should not be reset to TMICT value", apic_read(APIC_TMCCT) < (tmict / 2));
5820eac5394SEvgeny Yakovlev 	/*
5830eac5394SEvgeny Yakovlev 	 * Specifically wait for timer wrap around and skip 0.
5840eac5394SEvgeny Yakovlev 	 * Under KVM lapic there is a possibility that a small amount of consecutive
5850eac5394SEvgeny Yakovlev 	 * TMCCR reads return 0 while hrtimer is reset in an async callback
5860eac5394SEvgeny Yakovlev 	 */
5870eac5394SEvgeny Yakovlev 	wait_until_tmcct_wrap_around(tmict, false);
588d9b2b283SWanpeng Li 	report("TMCCT should be reset to the initial-count", apic_read(APIC_TMCCT) > (tmict / 2));
589d9b2b283SWanpeng Li 
590d9b2b283SWanpeng Li 	wait_until_tmcct_is_zero(tmict, true);
591d9b2b283SWanpeng Li 	/*
592d9b2b283SWanpeng Li 	 * Keep the same TMICT and change timer mode to one-shot
593d9b2b283SWanpeng Li 	 * TMCCT should be > 0 and count-down to 0
594d9b2b283SWanpeng Li 	 */
595d9b2b283SWanpeng Li 	apic_change_mode(APIC_LVT_TIMER_ONESHOT);
596d9b2b283SWanpeng Li 	report("TMCCT should not be reset to init", apic_read(APIC_TMCCT) < (tmict / 2));
597d9b2b283SWanpeng Li 	wait_until_tmcct_is_zero(tmict, false);
598d9b2b283SWanpeng Li 	report("TMCCT should have reach zero", !apic_read(APIC_TMCCT));
599d9b2b283SWanpeng Li 
600d9b2b283SWanpeng Li 	/* now tmcct == 0 and tmict != 0 */
601d9b2b283SWanpeng Li 	apic_change_mode(APIC_LVT_TIMER_PERIODIC);
602d9b2b283SWanpeng Li 	report("TMCCT should stay at zero", !apic_read(APIC_TMCCT));
603d9b2b283SWanpeng Li }
604d9b2b283SWanpeng Li 
605de8d3fccSWanpeng Li #define KVM_HC_SEND_IPI 10
606de8d3fccSWanpeng Li 
607de8d3fccSWanpeng Li static void test_pv_ipi(void)
608de8d3fccSWanpeng Li {
609de8d3fccSWanpeng Li     int ret;
610de8d3fccSWanpeng Li     unsigned long a0 = 0xFFFFFFFF, a1 = 0, a2 = 0xFFFFFFFF, a3 = 0x0;
611de8d3fccSWanpeng Li 
612de8d3fccSWanpeng Li     asm volatile("vmcall" : "=a"(ret) :"a"(KVM_HC_SEND_IPI), "b"(a0), "c"(a1), "d"(a2), "S"(a3));
613de8d3fccSWanpeng Li     report("PV IPIs testing", !ret);
614de8d3fccSWanpeng Li }
615de8d3fccSWanpeng Li 
6167db17e21SThomas Huth int main(void)
6177d36db35SAvi Kivity {
6187d36db35SAvi Kivity     setup_vm();
619f2d2b7c7SAvi Kivity     smp_init();
6207d36db35SAvi Kivity 
6217d36db35SAvi Kivity     test_lapic_existence();
6227d36db35SAvi Kivity 
6237d36db35SAvi Kivity     mask_pic_interrupts();
624a222b5e2SRadim Krčmář     test_apic_id();
625c3ccca3fSJim Mattson     test_apic_disable();
6267d36db35SAvi Kivity     test_enable_x2apic();
6279b6bdb3fSJan Kiszka     test_apicbase();
6287d36db35SAvi Kivity 
6297d36db35SAvi Kivity     test_self_ipi();
6309931b88cSRadim Krčmář     test_physical_broadcast();
631de8d3fccSWanpeng Li     test_pv_ipi();
6327d36db35SAvi Kivity 
633f2d2b7c7SAvi Kivity     test_sti_nmi();
634173e7eacSAvi Kivity     test_multiple_nmi();
6357d36db35SAvi Kivity 
6369f815b29SPeter Xu     test_apic_timer_one_shot();
637d9b2b283SWanpeng Li     test_apic_change_mode();
638d423ca36SLiu, Jinsong     test_tsc_deadline_timer();
639d423ca36SLiu, Jinsong 
640f3cdd159SJan Kiszka     return report_summary();
6417d36db35SAvi Kivity }
642