xref: /kvm-unit-tests/x86/apic.c (revision 9111ccab0bb42d93d9f2b84c9089b5790e763056)
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"
87d36db35SAvi Kivity 
97d36db35SAvi Kivity static void test_lapic_existence(void)
107d36db35SAvi Kivity {
117d36db35SAvi Kivity     u32 lvr;
127d36db35SAvi Kivity 
137d36db35SAvi Kivity     lvr = apic_read(APIC_LVR);
147d36db35SAvi Kivity     printf("apic version: %x\n", lvr);
157d36db35SAvi Kivity     report("apic existence", (u16)lvr == 0x14);
167d36db35SAvi Kivity }
177d36db35SAvi Kivity 
18d423ca36SLiu, Jinsong #define TSC_DEADLINE_TIMER_VECTOR 0xef
19d423ca36SLiu, Jinsong 
20d423ca36SLiu, Jinsong static int tdt_count;
21d423ca36SLiu, Jinsong 
22d423ca36SLiu, Jinsong static void tsc_deadline_timer_isr(isr_regs_t *regs)
23d423ca36SLiu, Jinsong {
24d423ca36SLiu, Jinsong     ++tdt_count;
250b04ed06SPeter Xu     eoi();
26d423ca36SLiu, Jinsong }
27d423ca36SLiu, Jinsong 
2832b9603cSRadim Krčmář static void __test_tsc_deadline_timer(void)
29d423ca36SLiu, Jinsong {
30d423ca36SLiu, Jinsong     handle_irq(TSC_DEADLINE_TIMER_VECTOR, tsc_deadline_timer_isr);
31d423ca36SLiu, Jinsong     irq_enable();
32d423ca36SLiu, Jinsong 
33d423ca36SLiu, Jinsong     wrmsr(MSR_IA32_TSCDEADLINE, rdmsr(MSR_IA32_TSC));
34d423ca36SLiu, Jinsong     asm volatile ("nop");
35d423ca36SLiu, Jinsong     report("tsc deadline timer", tdt_count == 1);
36f8833144SNadav Amit     report("tsc deadline timer clearing", rdmsr(MSR_IA32_TSCDEADLINE) == 0);
37d423ca36SLiu, Jinsong }
38d423ca36SLiu, Jinsong 
39d423ca36SLiu, Jinsong static int enable_tsc_deadline_timer(void)
40d423ca36SLiu, Jinsong {
41d423ca36SLiu, Jinsong     uint32_t lvtt;
42d423ca36SLiu, Jinsong 
43d423ca36SLiu, Jinsong     if (cpuid(1).c & (1 << 24)) {
44*9111ccabSRadim Krčmář         lvtt = APIC_LVT_TIMER_TSCDEADLINE | TSC_DEADLINE_TIMER_VECTOR;
45d423ca36SLiu, Jinsong         apic_write(APIC_LVTT, lvtt);
46d423ca36SLiu, Jinsong         return 1;
47d423ca36SLiu, Jinsong     } else {
48d423ca36SLiu, Jinsong         return 0;
49d423ca36SLiu, Jinsong     }
50d423ca36SLiu, Jinsong }
51d423ca36SLiu, Jinsong 
52d423ca36SLiu, Jinsong static void test_tsc_deadline_timer(void)
53d423ca36SLiu, Jinsong {
54d423ca36SLiu, Jinsong     if(enable_tsc_deadline_timer()) {
5532b9603cSRadim Krčmář         __test_tsc_deadline_timer();
56d423ca36SLiu, Jinsong     } else {
5732b9603cSRadim Krčmář         report_skip("tsc deadline timer not detected");
58d423ca36SLiu, Jinsong     }
59d423ca36SLiu, Jinsong }
60d423ca36SLiu, Jinsong 
6122c7d929SJan Kiszka static void do_write_apicbase(void *data)
6222c7d929SJan Kiszka {
6322c7d929SJan Kiszka     wrmsr(MSR_IA32_APICBASE, *(u64 *)data);
6403f37ef2SPaolo Bonzini }
657d36db35SAvi Kivity 
667d36db35SAvi Kivity void test_enable_x2apic(void)
677d36db35SAvi Kivity {
6822c7d929SJan Kiszka     u64 invalid_state = APIC_DEFAULT_PHYS_BASE | APIC_BSP | APIC_EXTD;
6922c7d929SJan Kiszka     u64 apic_enabled = APIC_DEFAULT_PHYS_BASE | APIC_BSP | APIC_EN;
7022c7d929SJan Kiszka     u64 x2apic_enabled =
7122c7d929SJan Kiszka         APIC_DEFAULT_PHYS_BASE | APIC_BSP | APIC_EN | APIC_EXTD;
7222c7d929SJan Kiszka 
737d36db35SAvi Kivity     if (enable_x2apic()) {
747d36db35SAvi Kivity         printf("x2apic enabled\n");
7522c7d929SJan Kiszka 
7622c7d929SJan Kiszka         report("x2apic enabled to invalid state",
7722c7d929SJan Kiszka                test_for_exception(GP_VECTOR, do_write_apicbase,
7822c7d929SJan Kiszka                                   &invalid_state));
7922c7d929SJan Kiszka         report("x2apic enabled to apic enabled",
8022c7d929SJan Kiszka                test_for_exception(GP_VECTOR, do_write_apicbase,
8122c7d929SJan Kiszka                                   &apic_enabled));
8222c7d929SJan Kiszka 
8322c7d929SJan Kiszka         wrmsr(MSR_IA32_APICBASE, APIC_DEFAULT_PHYS_BASE | APIC_BSP);
8422c7d929SJan Kiszka         report("disabled to invalid state",
8522c7d929SJan Kiszka                test_for_exception(GP_VECTOR, do_write_apicbase,
8622c7d929SJan Kiszka                                   &invalid_state));
8722c7d929SJan Kiszka         report("disabled to x2apic enabled",
8822c7d929SJan Kiszka                test_for_exception(GP_VECTOR, do_write_apicbase,
8922c7d929SJan Kiszka                                   &x2apic_enabled));
9022c7d929SJan Kiszka 
9122c7d929SJan Kiszka         wrmsr(MSR_IA32_APICBASE, apic_enabled);
9222c7d929SJan Kiszka         report("apic enabled to invalid state",
9322c7d929SJan Kiszka                test_for_exception(GP_VECTOR, do_write_apicbase,
9422c7d929SJan Kiszka                                   &invalid_state));
9522c7d929SJan Kiszka 
9622c7d929SJan Kiszka         wrmsr(MSR_IA32_APICBASE, x2apic_enabled);
9722c7d929SJan Kiszka         apic_write(APIC_SPIV, 0x1ff);
987d36db35SAvi Kivity     } else {
997d36db35SAvi Kivity         printf("x2apic not detected\n");
10022c7d929SJan Kiszka 
10122c7d929SJan Kiszka         report("enable unsupported x2apic",
10222c7d929SJan Kiszka                test_for_exception(GP_VECTOR, do_write_apicbase,
10322c7d929SJan Kiszka                                   &x2apic_enabled));
1047d36db35SAvi Kivity     }
1057d36db35SAvi Kivity }
1067d36db35SAvi Kivity 
1079b6bdb3fSJan Kiszka #define ALTERNATE_APIC_BASE	0x42000000
1089b6bdb3fSJan Kiszka 
1099b6bdb3fSJan Kiszka static void test_apicbase(void)
1109b6bdb3fSJan Kiszka {
1119b6bdb3fSJan Kiszka     u64 orig_apicbase = rdmsr(MSR_IA32_APICBASE);
1129b6bdb3fSJan Kiszka     u32 lvr = apic_read(APIC_LVR);
1139b6bdb3fSJan Kiszka     u64 value;
1149b6bdb3fSJan Kiszka 
1159b6bdb3fSJan Kiszka     wrmsr(MSR_IA32_APICBASE, orig_apicbase & ~(APIC_EN | APIC_EXTD));
1169b6bdb3fSJan Kiszka     wrmsr(MSR_IA32_APICBASE, ALTERNATE_APIC_BASE | APIC_BSP | APIC_EN);
1179b6bdb3fSJan Kiszka 
1185bba1769SAndrew Jones     report_prefix_push("apicbase");
1195bba1769SAndrew Jones 
1209b6bdb3fSJan Kiszka     report("relocate apic",
1219b6bdb3fSJan Kiszka            *(volatile u32 *)(ALTERNATE_APIC_BASE + APIC_LVR) == lvr);
1229b6bdb3fSJan Kiszka 
123772befb7SEduardo Habkost     value = orig_apicbase | (1UL << cpuid_maxphyaddr());
1245bba1769SAndrew Jones     report("reserved physaddr bits",
1259b6bdb3fSJan Kiszka            test_for_exception(GP_VECTOR, do_write_apicbase, &value));
1269b6bdb3fSJan Kiszka 
1279b6bdb3fSJan Kiszka     value = orig_apicbase | 1;
1285bba1769SAndrew Jones     report("reserved low bits",
1299b6bdb3fSJan Kiszka            test_for_exception(GP_VECTOR, do_write_apicbase, &value));
1309b6bdb3fSJan Kiszka 
1319b6bdb3fSJan Kiszka     wrmsr(MSR_IA32_APICBASE, orig_apicbase);
1329b6bdb3fSJan Kiszka     apic_write(APIC_SPIV, 0x1ff);
1335bba1769SAndrew Jones 
1345bba1769SAndrew Jones     report_prefix_pop();
1359b6bdb3fSJan Kiszka }
1369b6bdb3fSJan Kiszka 
137a222b5e2SRadim Krčmář static void do_write_apic_id(void *id)
138a222b5e2SRadim Krčmář {
139a222b5e2SRadim Krčmář     apic_write(APIC_ID, *(u32 *)id);
140a222b5e2SRadim Krčmář }
141a222b5e2SRadim Krčmář 
142a222b5e2SRadim Krčmář static void __test_apic_id(void * unused)
143a222b5e2SRadim Krčmář {
144a222b5e2SRadim Krčmář     u32 id, newid;
145a222b5e2SRadim Krčmář     u8  initial_xapic_id = cpuid(1).b >> 24;
146a222b5e2SRadim Krčmář     u32 initial_x2apic_id = cpuid(0xb).d;
147a222b5e2SRadim Krčmář     bool x2apic_mode = rdmsr(MSR_IA32_APICBASE) & APIC_EXTD;
148a222b5e2SRadim Krčmář 
149a222b5e2SRadim Krčmář     if (x2apic_mode)
150a222b5e2SRadim Krčmář         reset_apic();
151a222b5e2SRadim Krčmář 
152a222b5e2SRadim Krčmář     id = apic_id();
153a222b5e2SRadim Krčmář     report("xapic id matches cpuid", initial_xapic_id == id);
154a222b5e2SRadim Krčmář 
155a222b5e2SRadim Krčmář     newid = (id + 1) << 24;
156a222b5e2SRadim Krčmář     report("writeable xapic id",
157a222b5e2SRadim Krčmář             !test_for_exception(GP_VECTOR, do_write_apic_id, &newid) &&
158a222b5e2SRadim Krčmář             id + 1 == apic_id());
159a222b5e2SRadim Krčmář 
160a222b5e2SRadim Krčmář     if (!enable_x2apic())
161a222b5e2SRadim Krčmář         goto out;
162a222b5e2SRadim Krčmář 
163a222b5e2SRadim Krčmář     report("non-writeable x2apic id",
164a222b5e2SRadim Krčmář             test_for_exception(GP_VECTOR, do_write_apic_id, &newid));
165a222b5e2SRadim Krčmář     report("sane x2apic id", initial_xapic_id == (apic_id() & 0xff));
166a222b5e2SRadim Krčmář 
167a222b5e2SRadim Krčmář     /* old QEMUs do not set initial x2APIC ID */
168a222b5e2SRadim Krčmář     report("x2apic id matches cpuid",
169a222b5e2SRadim Krčmář            initial_xapic_id == (initial_x2apic_id & 0xff) &&
170a222b5e2SRadim Krčmář            initial_x2apic_id == apic_id());
171a222b5e2SRadim Krčmář 
172a222b5e2SRadim Krčmář out:
173a222b5e2SRadim Krčmář     reset_apic();
174a222b5e2SRadim Krčmář 
175a222b5e2SRadim Krčmář     report("correct xapic id after reset", initial_xapic_id == apic_id());
176a222b5e2SRadim Krčmář 
177a222b5e2SRadim Krčmář     /* old KVMs do not reset xAPIC ID */
178a222b5e2SRadim Krčmář     if (id != apic_id())
179a222b5e2SRadim Krčmář         apic_write(APIC_ID, id << 24);
180a222b5e2SRadim Krčmář 
181a222b5e2SRadim Krčmář     if (x2apic_mode)
182a222b5e2SRadim Krčmář         enable_x2apic();
183a222b5e2SRadim Krčmář }
184a222b5e2SRadim Krčmář 
185a222b5e2SRadim Krčmář static void test_apic_id(void)
186a222b5e2SRadim Krčmář {
187a222b5e2SRadim Krčmář     if (cpu_count() < 2)
188a222b5e2SRadim Krčmář         return;
189a222b5e2SRadim Krčmář 
190a222b5e2SRadim Krčmář     on_cpu(1, __test_apic_id, NULL);
191a222b5e2SRadim Krčmář }
192a222b5e2SRadim Krčmář 
1937d36db35SAvi Kivity static int ipi_count;
1947d36db35SAvi Kivity 
1957d36db35SAvi Kivity static void self_ipi_isr(isr_regs_t *regs)
1967d36db35SAvi Kivity {
1977d36db35SAvi Kivity     ++ipi_count;
1987d36db35SAvi Kivity     eoi();
1997d36db35SAvi Kivity }
2007d36db35SAvi Kivity 
2017d36db35SAvi Kivity static void test_self_ipi(void)
2027d36db35SAvi Kivity {
2037d36db35SAvi Kivity     int vec = 0xf1;
2047d36db35SAvi Kivity 
205d51bd17eSGleb Natapov     handle_irq(vec, self_ipi_isr);
2067d36db35SAvi Kivity     irq_enable();
2077d36db35SAvi Kivity     apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec,
2087d36db35SAvi Kivity                    0);
2097d36db35SAvi Kivity     asm volatile ("nop");
2107d36db35SAvi Kivity     report("self ipi", ipi_count == 1);
2117d36db35SAvi Kivity }
2127d36db35SAvi Kivity 
213f2d2b7c7SAvi Kivity volatile int nmi_counter_private, nmi_counter, nmi_hlt_counter, sti_loop_active;
214f2d2b7c7SAvi Kivity 
215f2d2b7c7SAvi Kivity void sti_nop(char *p)
216f2d2b7c7SAvi Kivity {
217f2d2b7c7SAvi Kivity     asm volatile (
218f2d2b7c7SAvi Kivity 		  ".globl post_sti \n\t"
219f2d2b7c7SAvi Kivity 		  "sti \n"
220f2d2b7c7SAvi Kivity 		  /*
221f2d2b7c7SAvi Kivity 		   * vmx won't exit on external interrupt if blocked-by-sti,
222f2d2b7c7SAvi Kivity 		   * so give it a reason to exit by accessing an unmapped page.
223f2d2b7c7SAvi Kivity 		   */
224f2d2b7c7SAvi Kivity 		  "post_sti: testb $0, %0 \n\t"
225f2d2b7c7SAvi Kivity 		  "nop \n\t"
226f2d2b7c7SAvi Kivity 		  "cli"
227f2d2b7c7SAvi Kivity 		  : : "m"(*p)
228f2d2b7c7SAvi Kivity 		  );
229f2d2b7c7SAvi Kivity     nmi_counter = nmi_counter_private;
230f2d2b7c7SAvi Kivity }
231f2d2b7c7SAvi Kivity 
232f2d2b7c7SAvi Kivity static void sti_loop(void *ignore)
233f2d2b7c7SAvi Kivity {
234f2d2b7c7SAvi Kivity     unsigned k = 0;
235f2d2b7c7SAvi Kivity 
236f2d2b7c7SAvi Kivity     while (sti_loop_active) {
237f2d2b7c7SAvi Kivity 	sti_nop((char *)(ulong)((k++ * 4096) % (128 * 1024 * 1024)));
238f2d2b7c7SAvi Kivity     }
239f2d2b7c7SAvi Kivity }
240f2d2b7c7SAvi Kivity 
241f2d2b7c7SAvi Kivity static void nmi_handler(isr_regs_t *regs)
242f2d2b7c7SAvi Kivity {
243f2d2b7c7SAvi Kivity     extern void post_sti(void);
244f2d2b7c7SAvi Kivity     ++nmi_counter_private;
245f2d2b7c7SAvi Kivity     nmi_hlt_counter += regs->rip == (ulong)post_sti;
246f2d2b7c7SAvi Kivity }
247f2d2b7c7SAvi Kivity 
248f2d2b7c7SAvi Kivity static void update_cr3(void *cr3)
249f2d2b7c7SAvi Kivity {
250f2d2b7c7SAvi Kivity     write_cr3((ulong)cr3);
251f2d2b7c7SAvi Kivity }
252f2d2b7c7SAvi Kivity 
253f2d2b7c7SAvi Kivity static void test_sti_nmi(void)
254f2d2b7c7SAvi Kivity {
255f2d2b7c7SAvi Kivity     unsigned old_counter;
256f2d2b7c7SAvi Kivity 
257f2d2b7c7SAvi Kivity     if (cpu_count() < 2) {
258f2d2b7c7SAvi Kivity 	return;
259f2d2b7c7SAvi Kivity     }
260f2d2b7c7SAvi Kivity 
261d51bd17eSGleb Natapov     handle_irq(2, nmi_handler);
262f2d2b7c7SAvi Kivity     on_cpu(1, update_cr3, (void *)read_cr3());
263f2d2b7c7SAvi Kivity 
264f2d2b7c7SAvi Kivity     sti_loop_active = 1;
265f2d2b7c7SAvi Kivity     on_cpu_async(1, sti_loop, 0);
266f2d2b7c7SAvi Kivity     while (nmi_counter < 30000) {
267f2d2b7c7SAvi Kivity 	old_counter = nmi_counter;
268f2d2b7c7SAvi Kivity 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 1);
269f2d2b7c7SAvi Kivity 	while (nmi_counter == old_counter) {
270f2d2b7c7SAvi Kivity 	    ;
271f2d2b7c7SAvi Kivity 	}
272f2d2b7c7SAvi Kivity     }
273f2d2b7c7SAvi Kivity     sti_loop_active = 0;
274f2d2b7c7SAvi Kivity     report("nmi-after-sti", nmi_hlt_counter == 0);
275f2d2b7c7SAvi Kivity }
276f2d2b7c7SAvi Kivity 
277173e7eacSAvi Kivity static volatile bool nmi_done, nmi_flushed;
278173e7eacSAvi Kivity static volatile int nmi_received;
279173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr1, cpu1_nmi_ctr1;
280173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr2, cpu1_nmi_ctr2;
281173e7eacSAvi Kivity 
282173e7eacSAvi Kivity static void multiple_nmi_handler(isr_regs_t *regs)
283173e7eacSAvi Kivity {
284173e7eacSAvi Kivity     ++nmi_received;
285173e7eacSAvi Kivity }
286173e7eacSAvi Kivity 
287173e7eacSAvi Kivity static void kick_me_nmi(void *blah)
288173e7eacSAvi Kivity {
289173e7eacSAvi Kivity     while (!nmi_done) {
290173e7eacSAvi Kivity 	++cpu1_nmi_ctr1;
291173e7eacSAvi Kivity 	while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1 && !nmi_done) {
292173e7eacSAvi Kivity 	    pause();
293173e7eacSAvi Kivity 	}
294173e7eacSAvi Kivity 	if (nmi_done) {
295173e7eacSAvi Kivity 	    return;
296173e7eacSAvi Kivity 	}
297173e7eacSAvi Kivity 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
298173e7eacSAvi Kivity 	/* make sure the NMI has arrived by sending an IPI after it */
299173e7eacSAvi Kivity 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT
300173e7eacSAvi Kivity 		       | 0x44, 0);
301173e7eacSAvi Kivity 	++cpu1_nmi_ctr2;
302173e7eacSAvi Kivity 	while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2 && !nmi_done) {
303173e7eacSAvi Kivity 	    pause();
304173e7eacSAvi Kivity 	}
305173e7eacSAvi Kivity     }
306173e7eacSAvi Kivity }
307173e7eacSAvi Kivity 
308173e7eacSAvi Kivity static void flush_nmi(isr_regs_t *regs)
309173e7eacSAvi Kivity {
310173e7eacSAvi Kivity     nmi_flushed = true;
311173e7eacSAvi Kivity     apic_write(APIC_EOI, 0);
312173e7eacSAvi Kivity }
313173e7eacSAvi Kivity 
314173e7eacSAvi Kivity static void test_multiple_nmi(void)
315173e7eacSAvi Kivity {
316173e7eacSAvi Kivity     int i;
317173e7eacSAvi Kivity     bool ok = true;
318173e7eacSAvi Kivity 
319173e7eacSAvi Kivity     if (cpu_count() < 2) {
320173e7eacSAvi Kivity 	return;
321173e7eacSAvi Kivity     }
322173e7eacSAvi Kivity 
323173e7eacSAvi Kivity     sti();
324173e7eacSAvi Kivity     handle_irq(2, multiple_nmi_handler);
325173e7eacSAvi Kivity     handle_irq(0x44, flush_nmi);
326173e7eacSAvi Kivity     on_cpu_async(1, kick_me_nmi, 0);
327173e7eacSAvi Kivity     for (i = 0; i < 1000000; ++i) {
328173e7eacSAvi Kivity 	nmi_flushed = false;
329173e7eacSAvi Kivity 	nmi_received = 0;
330173e7eacSAvi Kivity 	++cpu0_nmi_ctr1;
331173e7eacSAvi Kivity 	while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1) {
332173e7eacSAvi Kivity 	    pause();
333173e7eacSAvi Kivity 	}
334173e7eacSAvi Kivity 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
335173e7eacSAvi Kivity 	while (!nmi_flushed) {
336173e7eacSAvi Kivity 	    pause();
337173e7eacSAvi Kivity 	}
338173e7eacSAvi Kivity 	if (nmi_received != 2) {
339173e7eacSAvi Kivity 	    ok = false;
340173e7eacSAvi Kivity 	    break;
341173e7eacSAvi Kivity 	}
342173e7eacSAvi Kivity 	++cpu0_nmi_ctr2;
343173e7eacSAvi Kivity 	while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2) {
344173e7eacSAvi Kivity 	    pause();
345173e7eacSAvi Kivity 	}
346173e7eacSAvi Kivity     }
347173e7eacSAvi Kivity     nmi_done = true;
348173e7eacSAvi Kivity     report("multiple nmi", ok);
349173e7eacSAvi Kivity }
350173e7eacSAvi Kivity 
3519f815b29SPeter Xu static volatile int lvtt_counter = 0;
3529f815b29SPeter Xu 
3539f815b29SPeter Xu static void lvtt_handler(isr_regs_t *regs)
3549f815b29SPeter Xu {
3559f815b29SPeter Xu     lvtt_counter++;
3569f815b29SPeter Xu     eoi();
3579f815b29SPeter Xu }
3589f815b29SPeter Xu 
3599f815b29SPeter Xu static void test_apic_timer_one_shot(void)
3609f815b29SPeter Xu {
3619f815b29SPeter Xu     uint64_t tsc1, tsc2;
3629f815b29SPeter Xu     static const uint32_t interval = 0x10000;
3639f815b29SPeter Xu 
3649f815b29SPeter Xu #define APIC_LVT_TIMER_VECTOR    (0xee)
3659f815b29SPeter Xu 
3669f815b29SPeter Xu     handle_irq(APIC_LVT_TIMER_VECTOR, lvtt_handler);
3679f815b29SPeter Xu     irq_enable();
3689f815b29SPeter Xu 
3699f815b29SPeter Xu     /* One shot mode */
370*9111ccabSRadim Krčmář     apic_write(APIC_LVTT, APIC_LVT_TIMER_ONESHOT |
3719f815b29SPeter Xu                APIC_LVT_TIMER_VECTOR);
3729f815b29SPeter Xu     /* Divider == 1 */
3739f815b29SPeter Xu     apic_write(APIC_TDCR, 0x0000000b);
3749f815b29SPeter Xu 
3759f815b29SPeter Xu     tsc1 = rdtsc();
3769f815b29SPeter Xu     /* Set "Initial Counter Register", which starts the timer */
3779f815b29SPeter Xu     apic_write(APIC_TMICT, interval);
3789f815b29SPeter Xu     while (!lvtt_counter);
3799f815b29SPeter Xu     tsc2 = rdtsc();
3809f815b29SPeter Xu 
3819f815b29SPeter Xu     /*
3829f815b29SPeter Xu      * For LVT Timer clock, SDM vol 3 10.5.4 says it should be
3839f815b29SPeter Xu      * derived from processor's bus clock (IIUC which is the same
3849f815b29SPeter Xu      * as TSC), however QEMU seems to be using nanosecond. In all
3859f815b29SPeter Xu      * cases, the following should satisfy on all modern
3869f815b29SPeter Xu      * processors.
3879f815b29SPeter Xu      */
3889f815b29SPeter Xu     report("APIC LVT timer one shot", (lvtt_counter == 1) &&
3899f815b29SPeter Xu            (tsc2 - tsc1 >= interval));
3909f815b29SPeter Xu }
3919f815b29SPeter Xu 
3927d36db35SAvi Kivity int main()
3937d36db35SAvi Kivity {
3947d36db35SAvi Kivity     setup_vm();
395f2d2b7c7SAvi Kivity     smp_init();
396d51bd17eSGleb Natapov     setup_idt();
3977d36db35SAvi Kivity 
3987d36db35SAvi Kivity     test_lapic_existence();
3997d36db35SAvi Kivity 
4007d36db35SAvi Kivity     mask_pic_interrupts();
401a222b5e2SRadim Krčmář     test_apic_id();
4027d36db35SAvi Kivity     test_enable_x2apic();
4039b6bdb3fSJan Kiszka     test_apicbase();
4047d36db35SAvi Kivity 
4057d36db35SAvi Kivity     test_self_ipi();
4067d36db35SAvi Kivity 
407f2d2b7c7SAvi Kivity     test_sti_nmi();
408173e7eacSAvi Kivity     test_multiple_nmi();
4097d36db35SAvi Kivity 
4109f815b29SPeter Xu     test_apic_timer_one_shot();
411d423ca36SLiu, Jinsong     test_tsc_deadline_timer();
412d423ca36SLiu, Jinsong 
413f3cdd159SJan Kiszka     return report_summary();
4147d36db35SAvi Kivity }
415