xref: /kvm-unit-tests/x86/apic.c (revision a7f606970b42317f4b9e1f138bcbce85bf08b2c2)
17d36db35SAvi Kivity #include "libcflat.h"
27d36db35SAvi Kivity #include "apic.h"
37d36db35SAvi Kivity #include "vm.h"
4f2d2b7c7SAvi Kivity #include "smp.h"
5e7c37968SGleb Natapov #include "desc.h"
6110f0d93SGleb Natapov #include "isr.h"
722c7d929SJan Kiszka #include "msr.h"
89931b88cSRadim Krčmář #include "atomic.h"
903b1e457SNadav Amit #include "fwcfg.h"
107d36db35SAvi Kivity 
11e38858bcSJim Mattson #define MAX_TPR			0xf
12e38858bcSJim Mattson 
137d36db35SAvi Kivity static void test_lapic_existence(void)
147d36db35SAvi Kivity {
153ee24f29SNadav Amit     u8 version;
167d36db35SAvi Kivity 
173ee24f29SNadav Amit     version = (u8)apic_read(APIC_LVR);
183ee24f29SNadav Amit     printf("apic version: %x\n", version);
19a299895bSThomas Huth     report(version >= 0x10 && version <= 0x15, "apic existence");
207d36db35SAvi Kivity }
217d36db35SAvi Kivity 
22d423ca36SLiu, Jinsong #define TSC_DEADLINE_TIMER_VECTOR 0xef
239931b88cSRadim Krčmář #define BROADCAST_VECTOR 0xcf
24d423ca36SLiu, Jinsong 
25d423ca36SLiu, Jinsong static int tdt_count;
26d423ca36SLiu, Jinsong 
27d423ca36SLiu, Jinsong static void tsc_deadline_timer_isr(isr_regs_t *regs)
28d423ca36SLiu, Jinsong {
29d423ca36SLiu, Jinsong     ++tdt_count;
300b04ed06SPeter Xu     eoi();
31d423ca36SLiu, Jinsong }
32d423ca36SLiu, Jinsong 
3332b9603cSRadim Krčmář static void __test_tsc_deadline_timer(void)
34d423ca36SLiu, Jinsong {
35d423ca36SLiu, Jinsong     handle_irq(TSC_DEADLINE_TIMER_VECTOR, tsc_deadline_timer_isr);
36d423ca36SLiu, Jinsong     irq_enable();
37d423ca36SLiu, Jinsong 
38d423ca36SLiu, Jinsong     wrmsr(MSR_IA32_TSCDEADLINE, rdmsr(MSR_IA32_TSC));
39d423ca36SLiu, Jinsong     asm volatile ("nop");
40a299895bSThomas Huth     report(tdt_count == 1, "tsc deadline timer");
41a299895bSThomas Huth     report(rdmsr(MSR_IA32_TSCDEADLINE) == 0, "tsc deadline timer clearing");
42d423ca36SLiu, Jinsong }
43d423ca36SLiu, Jinsong 
44d423ca36SLiu, Jinsong static int enable_tsc_deadline_timer(void)
45d423ca36SLiu, Jinsong {
46d423ca36SLiu, Jinsong     uint32_t lvtt;
47d423ca36SLiu, Jinsong 
48badc98caSKrish Sadhukhan     if (this_cpu_has(X86_FEATURE_TSC_DEADLINE_TIMER)) {
499111ccabSRadim Krčmář         lvtt = APIC_LVT_TIMER_TSCDEADLINE | TSC_DEADLINE_TIMER_VECTOR;
50d423ca36SLiu, Jinsong         apic_write(APIC_LVTT, lvtt);
51d423ca36SLiu, Jinsong         return 1;
52d423ca36SLiu, Jinsong     } else {
53d423ca36SLiu, Jinsong         return 0;
54d423ca36SLiu, Jinsong     }
55d423ca36SLiu, Jinsong }
56d423ca36SLiu, Jinsong 
57d423ca36SLiu, Jinsong static void test_tsc_deadline_timer(void)
58d423ca36SLiu, Jinsong {
59d423ca36SLiu, Jinsong     if(enable_tsc_deadline_timer()) {
6032b9603cSRadim Krčmář         __test_tsc_deadline_timer();
61d423ca36SLiu, Jinsong     } else {
6232b9603cSRadim Krčmář         report_skip("tsc deadline timer not detected");
63d423ca36SLiu, Jinsong     }
64d423ca36SLiu, Jinsong }
65d423ca36SLiu, Jinsong 
6622c7d929SJan Kiszka static void do_write_apicbase(void *data)
6722c7d929SJan Kiszka {
6822c7d929SJan Kiszka     wrmsr(MSR_IA32_APICBASE, *(u64 *)data);
6903f37ef2SPaolo Bonzini }
707d36db35SAvi Kivity 
7149f5ad9eSPaolo Bonzini static bool test_write_apicbase_exception(u64 data)
7249f5ad9eSPaolo Bonzini {
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 {
782092999cSSean Christopherson     u64 orig_apicbase = rdmsr(MSR_IA32_APICBASE);
792092999cSSean Christopherson     u64 apicbase;
802092999cSSean Christopherson 
817d36db35SAvi Kivity     if (enable_x2apic()) {
827d36db35SAvi Kivity         printf("x2apic enabled\n");
8322c7d929SJan Kiszka 
842092999cSSean Christopherson         apicbase = orig_apicbase & ~(APIC_EN | APIC_EXTD);
85a299895bSThomas Huth         report(test_write_apicbase_exception(apicbase | APIC_EXTD),
86a299895bSThomas Huth                "x2apic enabled to invalid state");
87a299895bSThomas Huth         report(test_write_apicbase_exception(apicbase | APIC_EN),
88a299895bSThomas Huth                "x2apic enabled to apic enabled");
8922c7d929SJan Kiszka 
90a299895bSThomas Huth         report(!test_write_apicbase_exception(apicbase | 0),
91a299895bSThomas Huth                "x2apic enabled to disabled state");
92a299895bSThomas Huth         report(test_write_apicbase_exception(apicbase | APIC_EXTD),
93a299895bSThomas Huth                "disabled to invalid state");
94a299895bSThomas Huth         report(test_write_apicbase_exception(apicbase | APIC_EN | APIC_EXTD),
95a299895bSThomas Huth                "disabled to x2apic enabled");
9622c7d929SJan Kiszka 
97a299895bSThomas Huth         report(!test_write_apicbase_exception(apicbase | APIC_EN),
98a299895bSThomas Huth                "apic disabled to apic enabled");
99a299895bSThomas Huth         report(test_write_apicbase_exception(apicbase | APIC_EXTD),
100a299895bSThomas Huth                "apic enabled to invalid state");
10122c7d929SJan Kiszka 
1022092999cSSean Christopherson         if (orig_apicbase & APIC_EXTD)
1032092999cSSean Christopherson             enable_x2apic();
1042092999cSSean Christopherson         else
1052092999cSSean Christopherson             reset_apic();
1062092999cSSean Christopherson 
1072092999cSSean Christopherson         /*
1082092999cSSean Christopherson          * Disabling the APIC resets various APIC registers, restore them to
1092092999cSSean Christopherson          * their desired values.
1102092999cSSean Christopherson          */
11122c7d929SJan Kiszka         apic_write(APIC_SPIV, 0x1ff);
1127d36db35SAvi Kivity     } else {
1137d36db35SAvi Kivity         printf("x2apic not detected\n");
11422c7d929SJan Kiszka 
115a299895bSThomas Huth         report(test_write_apicbase_exception(APIC_EN | APIC_EXTD),
116a299895bSThomas Huth                "enable unsupported x2apic");
1177d36db35SAvi Kivity     }
1187d36db35SAvi Kivity }
1197d36db35SAvi Kivity 
120e38858bcSJim Mattson static void verify_disabled_apic_mmio(void)
121e38858bcSJim Mattson {
122e38858bcSJim Mattson     volatile u32 *lvr = (volatile u32 *)(APIC_DEFAULT_PHYS_BASE + APIC_LVR);
123e38858bcSJim Mattson     volatile u32 *tpr = (volatile u32 *)(APIC_DEFAULT_PHYS_BASE + APIC_TASKPRI);
124e38858bcSJim Mattson     u32 cr8 = read_cr8();
125e38858bcSJim Mattson 
126e38858bcSJim Mattson     memset((void *)APIC_DEFAULT_PHYS_BASE, 0xff, PAGE_SIZE);
127a299895bSThomas Huth     report(*lvr == ~0, "*0xfee00030: %x", *lvr);
128a299895bSThomas Huth     report(read_cr8() == cr8, "CR8: %lx", read_cr8());
129e38858bcSJim Mattson     write_cr8(cr8 ^ MAX_TPR);
130a299895bSThomas Huth     report(read_cr8() == (cr8 ^ MAX_TPR), "CR8: %lx", read_cr8());
131a299895bSThomas Huth     report(*tpr == ~0, "*0xfee00080: %x", *tpr);
132e38858bcSJim Mattson     write_cr8(cr8);
133e38858bcSJim Mattson }
134e38858bcSJim Mattson 
135c3ccca3fSJim Mattson static void test_apic_disable(void)
136c3ccca3fSJim Mattson {
137e38858bcSJim Mattson     volatile u32 *lvr = (volatile u32 *)(APIC_DEFAULT_PHYS_BASE + APIC_LVR);
138e38858bcSJim Mattson     volatile u32 *tpr = (volatile u32 *)(APIC_DEFAULT_PHYS_BASE + APIC_TASKPRI);
139c3ccca3fSJim Mattson     u64 orig_apicbase = rdmsr(MSR_IA32_APICBASE);
140e38858bcSJim Mattson     u32 apic_version = apic_read(APIC_LVR);
141e38858bcSJim Mattson     u32 cr8 = read_cr8();
142c3ccca3fSJim Mattson 
143c3ccca3fSJim Mattson     report_prefix_push("apic_disable");
144e38858bcSJim Mattson     assert_msg(orig_apicbase & APIC_EN, "APIC not enabled.");
145c3ccca3fSJim Mattson 
146e38858bcSJim Mattson     disable_apic();
147a299895bSThomas Huth     report(!(rdmsr(MSR_IA32_APICBASE) & APIC_EN), "Local apic disabled");
148a299895bSThomas Huth     report(!this_cpu_has(X86_FEATURE_APIC),
149a299895bSThomas Huth            "CPUID.1H:EDX.APIC[bit 9] is clear");
150e38858bcSJim Mattson     verify_disabled_apic_mmio();
151c3ccca3fSJim Mattson 
152e38858bcSJim Mattson     reset_apic();
153a299895bSThomas Huth     report((rdmsr(MSR_IA32_APICBASE) & (APIC_EN | APIC_EXTD)) == APIC_EN,
154a299895bSThomas Huth            "Local apic enabled in xAPIC mode");
155a299895bSThomas Huth     report(this_cpu_has(X86_FEATURE_APIC), "CPUID.1H:EDX.APIC[bit 9] is set");
156a299895bSThomas Huth     report(*lvr == apic_version, "*0xfee00030: %x", *lvr);
157a299895bSThomas Huth     report(*tpr == cr8, "*0xfee00080: %x", *tpr);
158e38858bcSJim Mattson     write_cr8(cr8 ^ MAX_TPR);
159a299895bSThomas Huth     report(*tpr == (cr8 ^ MAX_TPR) << 4, "*0xfee00080: %x", *tpr);
160e38858bcSJim Mattson     write_cr8(cr8);
161c3ccca3fSJim Mattson 
162e38858bcSJim Mattson     if (enable_x2apic()) {
163e38858bcSJim Mattson 	apic_write(APIC_SPIV, 0x1ff);
164a299895bSThomas Huth 	report((rdmsr(MSR_IA32_APICBASE) & (APIC_EN | APIC_EXTD)) == (APIC_EN | APIC_EXTD),
165a299895bSThomas Huth                "Local apic enabled in x2APIC mode");
166a299895bSThomas Huth 	report(this_cpu_has(X86_FEATURE_APIC),
167a299895bSThomas Huth                "CPUID.1H:EDX.APIC[bit 9] is set");
168e38858bcSJim Mattson 	verify_disabled_apic_mmio();
169e38858bcSJim Mattson 	if (!(orig_apicbase & APIC_EXTD))
170e38858bcSJim Mattson 	    reset_apic();
171e38858bcSJim Mattson     }
172c3ccca3fSJim Mattson     report_prefix_pop();
173c3ccca3fSJim Mattson }
174c3ccca3fSJim Mattson 
175615a8838SNadav Amit #define ALTERNATE_APIC_BASE	0xfed40000
1769b6bdb3fSJan Kiszka 
1779b6bdb3fSJan Kiszka static void test_apicbase(void)
1789b6bdb3fSJan Kiszka {
1799b6bdb3fSJan Kiszka     u64 orig_apicbase = rdmsr(MSR_IA32_APICBASE);
1809b6bdb3fSJan Kiszka     u32 lvr = apic_read(APIC_LVR);
1819b6bdb3fSJan Kiszka     u64 value;
1829b6bdb3fSJan Kiszka 
1839b6bdb3fSJan Kiszka     wrmsr(MSR_IA32_APICBASE, orig_apicbase & ~(APIC_EN | APIC_EXTD));
1849b6bdb3fSJan Kiszka     wrmsr(MSR_IA32_APICBASE, ALTERNATE_APIC_BASE | APIC_BSP | APIC_EN);
1859b6bdb3fSJan Kiszka 
1865bba1769SAndrew Jones     report_prefix_push("apicbase");
1875bba1769SAndrew Jones 
188a299895bSThomas Huth     report(*(volatile u32 *)(ALTERNATE_APIC_BASE + APIC_LVR) == lvr,
189a299895bSThomas Huth            "relocate apic");
1909b6bdb3fSJan Kiszka 
191772befb7SEduardo Habkost     value = orig_apicbase | (1UL << cpuid_maxphyaddr());
192a299895bSThomas Huth     report(test_for_exception(GP_VECTOR, do_write_apicbase, &value),
193a299895bSThomas Huth            "reserved physaddr bits");
1949b6bdb3fSJan Kiszka 
1959b6bdb3fSJan Kiszka     value = orig_apicbase | 1;
196a299895bSThomas Huth     report(test_for_exception(GP_VECTOR, do_write_apicbase, &value),
197a299895bSThomas Huth            "reserved low bits");
1989b6bdb3fSJan Kiszka 
1999b6bdb3fSJan Kiszka     wrmsr(MSR_IA32_APICBASE, orig_apicbase);
2009b6bdb3fSJan Kiszka     apic_write(APIC_SPIV, 0x1ff);
2015bba1769SAndrew Jones 
2025bba1769SAndrew Jones     report_prefix_pop();
2039b6bdb3fSJan Kiszka }
2049b6bdb3fSJan Kiszka 
205a222b5e2SRadim Krčmář static void do_write_apic_id(void *id)
206a222b5e2SRadim Krčmář {
207a222b5e2SRadim Krčmář     apic_write(APIC_ID, *(u32 *)id);
208a222b5e2SRadim Krčmář }
209a222b5e2SRadim Krčmář 
210a222b5e2SRadim Krčmář static void __test_apic_id(void * unused)
211a222b5e2SRadim Krčmář {
212a222b5e2SRadim Krčmář     u32 id, newid;
213a222b5e2SRadim Krčmář     u8  initial_xapic_id = cpuid(1).b >> 24;
214a222b5e2SRadim Krčmář     u32 initial_x2apic_id = cpuid(0xb).d;
215a222b5e2SRadim Krčmář     bool x2apic_mode = rdmsr(MSR_IA32_APICBASE) & APIC_EXTD;
216a222b5e2SRadim Krčmář 
217a222b5e2SRadim Krčmář     if (x2apic_mode)
218a222b5e2SRadim Krčmář         reset_apic();
219a222b5e2SRadim Krčmář 
220a222b5e2SRadim Krčmář     id = apic_id();
221a299895bSThomas Huth     report(initial_xapic_id == id, "xapic id matches cpuid");
222a222b5e2SRadim Krčmář 
223a222b5e2SRadim Krčmář     newid = (id + 1) << 24;
224a299895bSThomas Huth     report(!test_for_exception(GP_VECTOR, do_write_apic_id, &newid) &&
225a299895bSThomas Huth            (id == apic_id() || id + 1 == apic_id()),
226a299895bSThomas Huth            "writeable xapic id");
227a222b5e2SRadim Krčmář 
228a222b5e2SRadim Krčmář     if (!enable_x2apic())
229a222b5e2SRadim Krčmář         goto out;
230a222b5e2SRadim Krčmář 
231a299895bSThomas Huth     report(test_for_exception(GP_VECTOR, do_write_apic_id, &newid),
232a299895bSThomas Huth            "non-writeable x2apic id");
233a299895bSThomas Huth     report(initial_xapic_id == (apic_id() & 0xff), "sane x2apic id");
234a222b5e2SRadim Krčmář 
235a222b5e2SRadim Krčmář     /* old QEMUs do not set initial x2APIC ID */
236a299895bSThomas Huth     report(initial_xapic_id == (initial_x2apic_id & 0xff) &&
237a299895bSThomas Huth            initial_x2apic_id == apic_id(),
238a299895bSThomas Huth            "x2apic id matches cpuid");
239a222b5e2SRadim Krčmář 
240a222b5e2SRadim Krčmář out:
241a222b5e2SRadim Krčmář     reset_apic();
242a222b5e2SRadim Krčmář 
243a299895bSThomas Huth     report(initial_xapic_id == apic_id(), "correct xapic id after reset");
244a222b5e2SRadim Krčmář 
245a222b5e2SRadim Krčmář     /* old KVMs do not reset xAPIC ID */
246a222b5e2SRadim Krčmář     if (id != apic_id())
247a222b5e2SRadim Krčmář         apic_write(APIC_ID, id << 24);
248a222b5e2SRadim Krčmář 
249a222b5e2SRadim Krčmář     if (x2apic_mode)
250a222b5e2SRadim Krčmář         enable_x2apic();
251a222b5e2SRadim Krčmář }
252a222b5e2SRadim Krčmář 
253a222b5e2SRadim Krčmář static void test_apic_id(void)
254a222b5e2SRadim Krčmář {
255a222b5e2SRadim Krčmář     if (cpu_count() < 2)
256a222b5e2SRadim Krčmář         return;
257a222b5e2SRadim Krčmář 
258a222b5e2SRadim Krčmář     on_cpu(1, __test_apic_id, NULL);
259a222b5e2SRadim Krčmář }
260a222b5e2SRadim Krčmář 
2617d36db35SAvi Kivity static int ipi_count;
2627d36db35SAvi Kivity 
2637d36db35SAvi Kivity static void self_ipi_isr(isr_regs_t *regs)
2647d36db35SAvi Kivity {
2657d36db35SAvi Kivity     ++ipi_count;
2667d36db35SAvi Kivity     eoi();
2677d36db35SAvi Kivity }
2687d36db35SAvi Kivity 
269685d5f62SRicardo Koller static void __test_self_ipi(void)
2707d36db35SAvi Kivity {
2716c0999f4SNadav Amit     u64 start = rdtsc();
2727d36db35SAvi Kivity     int vec = 0xf1;
2737d36db35SAvi Kivity 
274d51bd17eSGleb Natapov     handle_irq(vec, self_ipi_isr);
2757d36db35SAvi Kivity     irq_enable();
2767d36db35SAvi Kivity     apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec,
27718a34cceSNadav Amit                    id_map[0]);
2786c0999f4SNadav Amit 
2796c0999f4SNadav Amit     do {
2806c0999f4SNadav Amit         pause();
2816c0999f4SNadav Amit     } while (rdtsc() - start < 1000000000 && ipi_count == 0);
282685d5f62SRicardo Koller }
2836c0999f4SNadav Amit 
284685d5f62SRicardo Koller static void test_self_ipi_xapic(void)
285685d5f62SRicardo Koller {
286685d5f62SRicardo Koller     u64 orig_apicbase = rdmsr(MSR_IA32_APICBASE);
287685d5f62SRicardo Koller 
288685d5f62SRicardo Koller     report_prefix_push("self_ipi_xapic");
289685d5f62SRicardo Koller 
290685d5f62SRicardo Koller     /* Reset to xAPIC mode. */
291685d5f62SRicardo Koller     reset_apic();
292685d5f62SRicardo Koller     report((rdmsr(MSR_IA32_APICBASE) & (APIC_EN | APIC_EXTD)) == APIC_EN,
293685d5f62SRicardo Koller            "Local apic enabled in xAPIC mode");
294685d5f62SRicardo Koller 
295685d5f62SRicardo Koller     ipi_count = 0;
296685d5f62SRicardo Koller     __test_self_ipi();
297a299895bSThomas Huth     report(ipi_count == 1, "self ipi");
298685d5f62SRicardo Koller 
299685d5f62SRicardo Koller     /* Enable x2APIC mode if it was already enabled. */
300685d5f62SRicardo Koller     if (orig_apicbase & APIC_EXTD)
301685d5f62SRicardo Koller         enable_x2apic();
302685d5f62SRicardo Koller 
303685d5f62SRicardo Koller     report_prefix_pop();
304685d5f62SRicardo Koller }
305685d5f62SRicardo Koller 
306685d5f62SRicardo Koller static void test_self_ipi_x2apic(void)
307685d5f62SRicardo Koller {
308685d5f62SRicardo Koller     u64 orig_apicbase = rdmsr(MSR_IA32_APICBASE);
309685d5f62SRicardo Koller 
310685d5f62SRicardo Koller     report_prefix_push("self_ipi_x2apic");
311685d5f62SRicardo Koller 
312685d5f62SRicardo Koller     if (enable_x2apic()) {
313685d5f62SRicardo Koller         report((rdmsr(MSR_IA32_APICBASE) & (APIC_EN | APIC_EXTD)) ==
314685d5f62SRicardo Koller                (APIC_EN | APIC_EXTD),
315685d5f62SRicardo Koller                "Local apic enabled in x2APIC mode");
316685d5f62SRicardo Koller 
317685d5f62SRicardo Koller         ipi_count = 0;
318685d5f62SRicardo Koller         __test_self_ipi();
319685d5f62SRicardo Koller         report(ipi_count == 1, "self ipi");
320685d5f62SRicardo Koller 
321685d5f62SRicardo Koller         /* Reset to xAPIC mode unless x2APIC was already enabled. */
322685d5f62SRicardo Koller         if (!(orig_apicbase & APIC_EXTD))
323685d5f62SRicardo Koller             reset_apic();
324685d5f62SRicardo Koller     } else {
325685d5f62SRicardo Koller         report_skip("x2apic not detected");
326685d5f62SRicardo Koller     }
327685d5f62SRicardo Koller 
328685d5f62SRicardo Koller     report_prefix_pop();
3297d36db35SAvi Kivity }
3307d36db35SAvi Kivity 
331f2d2b7c7SAvi Kivity volatile int nmi_counter_private, nmi_counter, nmi_hlt_counter, sti_loop_active;
332f2d2b7c7SAvi Kivity 
333db4898e8SThomas Huth static void sti_nop(char *p)
334f2d2b7c7SAvi Kivity {
335f2d2b7c7SAvi Kivity     asm volatile (
336f2d2b7c7SAvi Kivity 		  ".globl post_sti \n\t"
337f2d2b7c7SAvi Kivity 		  "sti \n"
338f2d2b7c7SAvi Kivity 		  /*
339f2d2b7c7SAvi Kivity 		   * vmx won't exit on external interrupt if blocked-by-sti,
340f2d2b7c7SAvi Kivity 		   * so give it a reason to exit by accessing an unmapped page.
341f2d2b7c7SAvi Kivity 		   */
342f2d2b7c7SAvi Kivity 		  "post_sti: testb $0, %0 \n\t"
343f2d2b7c7SAvi Kivity 		  "nop \n\t"
344f2d2b7c7SAvi Kivity 		  "cli"
345f2d2b7c7SAvi Kivity 		  : : "m"(*p)
346f2d2b7c7SAvi Kivity 		  );
347f2d2b7c7SAvi Kivity     nmi_counter = nmi_counter_private;
348f2d2b7c7SAvi Kivity }
349f2d2b7c7SAvi Kivity 
350f2d2b7c7SAvi Kivity static void sti_loop(void *ignore)
351f2d2b7c7SAvi Kivity {
352f2d2b7c7SAvi Kivity     unsigned k = 0;
353f2d2b7c7SAvi Kivity 
354f2d2b7c7SAvi Kivity     while (sti_loop_active) {
355f2d2b7c7SAvi Kivity 	sti_nop((char *)(ulong)((k++ * 4096) % (128 * 1024 * 1024)));
356f2d2b7c7SAvi Kivity     }
357f2d2b7c7SAvi Kivity }
358f2d2b7c7SAvi Kivity 
359f2d2b7c7SAvi Kivity static void nmi_handler(isr_regs_t *regs)
360f2d2b7c7SAvi Kivity {
361f2d2b7c7SAvi Kivity     extern void post_sti(void);
362f2d2b7c7SAvi Kivity     ++nmi_counter_private;
363f2d2b7c7SAvi Kivity     nmi_hlt_counter += regs->rip == (ulong)post_sti;
364f2d2b7c7SAvi Kivity }
365f2d2b7c7SAvi Kivity 
366f2d2b7c7SAvi Kivity static void test_sti_nmi(void)
367f2d2b7c7SAvi Kivity {
368f2d2b7c7SAvi Kivity     unsigned old_counter;
369f2d2b7c7SAvi Kivity 
370f2d2b7c7SAvi Kivity     if (cpu_count() < 2) {
371f2d2b7c7SAvi Kivity 	return;
372f2d2b7c7SAvi Kivity     }
373f2d2b7c7SAvi Kivity 
374d51bd17eSGleb Natapov     handle_irq(2, nmi_handler);
375f2d2b7c7SAvi Kivity     on_cpu(1, update_cr3, (void *)read_cr3());
376f2d2b7c7SAvi Kivity 
377f2d2b7c7SAvi Kivity     sti_loop_active = 1;
378f2d2b7c7SAvi Kivity     on_cpu_async(1, sti_loop, 0);
379f2d2b7c7SAvi Kivity     while (nmi_counter < 30000) {
380f2d2b7c7SAvi Kivity 	old_counter = nmi_counter;
38118a34cceSNadav Amit 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, id_map[1]);
382f2d2b7c7SAvi Kivity 	while (nmi_counter == old_counter) {
383f2d2b7c7SAvi Kivity 	    ;
384f2d2b7c7SAvi Kivity 	}
385f2d2b7c7SAvi Kivity     }
386f2d2b7c7SAvi Kivity     sti_loop_active = 0;
387a299895bSThomas Huth     report(nmi_hlt_counter == 0, "nmi-after-sti");
388f2d2b7c7SAvi Kivity }
389f2d2b7c7SAvi Kivity 
390173e7eacSAvi Kivity static volatile bool nmi_done, nmi_flushed;
391173e7eacSAvi Kivity static volatile int nmi_received;
392173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr1, cpu1_nmi_ctr1;
393173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr2, cpu1_nmi_ctr2;
394173e7eacSAvi Kivity 
395173e7eacSAvi Kivity static void multiple_nmi_handler(isr_regs_t *regs)
396173e7eacSAvi Kivity {
397173e7eacSAvi Kivity     ++nmi_received;
398173e7eacSAvi Kivity }
399173e7eacSAvi Kivity 
400173e7eacSAvi Kivity static void kick_me_nmi(void *blah)
401173e7eacSAvi Kivity {
402173e7eacSAvi Kivity     while (!nmi_done) {
403173e7eacSAvi Kivity 	++cpu1_nmi_ctr1;
404173e7eacSAvi Kivity 	while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1 && !nmi_done) {
405173e7eacSAvi Kivity 	    pause();
406173e7eacSAvi Kivity 	}
407173e7eacSAvi Kivity 	if (nmi_done) {
408173e7eacSAvi Kivity 	    return;
409173e7eacSAvi Kivity 	}
41018a34cceSNadav Amit 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, id_map[0]);
411173e7eacSAvi Kivity 	/* make sure the NMI has arrived by sending an IPI after it */
412173e7eacSAvi Kivity 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT
41318a34cceSNadav Amit 		       | 0x44, id_map[0]);
414173e7eacSAvi Kivity 	++cpu1_nmi_ctr2;
415173e7eacSAvi Kivity 	while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2 && !nmi_done) {
416173e7eacSAvi Kivity 	    pause();
417173e7eacSAvi Kivity 	}
418173e7eacSAvi Kivity     }
419173e7eacSAvi Kivity }
420173e7eacSAvi Kivity 
421173e7eacSAvi Kivity static void flush_nmi(isr_regs_t *regs)
422173e7eacSAvi Kivity {
423173e7eacSAvi Kivity     nmi_flushed = true;
424173e7eacSAvi Kivity     apic_write(APIC_EOI, 0);
425173e7eacSAvi Kivity }
426173e7eacSAvi Kivity 
427173e7eacSAvi Kivity static void test_multiple_nmi(void)
428173e7eacSAvi Kivity {
429173e7eacSAvi Kivity     int i;
430173e7eacSAvi Kivity     bool ok = true;
431173e7eacSAvi Kivity 
432173e7eacSAvi Kivity     if (cpu_count() < 2) {
433173e7eacSAvi Kivity 	return;
434173e7eacSAvi Kivity     }
435173e7eacSAvi Kivity 
436173e7eacSAvi Kivity     sti();
437173e7eacSAvi Kivity     handle_irq(2, multiple_nmi_handler);
438173e7eacSAvi Kivity     handle_irq(0x44, flush_nmi);
439173e7eacSAvi Kivity     on_cpu_async(1, kick_me_nmi, 0);
440*a7f60697SPaolo Bonzini     for (i = 0; i < 100000; ++i) {
441173e7eacSAvi Kivity 	nmi_flushed = false;
442173e7eacSAvi Kivity 	nmi_received = 0;
443173e7eacSAvi Kivity 	++cpu0_nmi_ctr1;
444173e7eacSAvi Kivity 	while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1) {
445173e7eacSAvi Kivity 	    pause();
446173e7eacSAvi Kivity 	}
44718a34cceSNadav Amit 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, id_map[0]);
448173e7eacSAvi Kivity 	while (!nmi_flushed) {
449173e7eacSAvi Kivity 	    pause();
450173e7eacSAvi Kivity 	}
451173e7eacSAvi Kivity 	if (nmi_received != 2) {
452173e7eacSAvi Kivity 	    ok = false;
453173e7eacSAvi Kivity 	    break;
454173e7eacSAvi Kivity 	}
455173e7eacSAvi Kivity 	++cpu0_nmi_ctr2;
456173e7eacSAvi Kivity 	while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2) {
457173e7eacSAvi Kivity 	    pause();
458173e7eacSAvi Kivity 	}
459173e7eacSAvi Kivity     }
460173e7eacSAvi Kivity     nmi_done = true;
461a299895bSThomas Huth     report(ok, "multiple nmi");
462173e7eacSAvi Kivity }
463173e7eacSAvi Kivity 
4649f23b246SSean Christopherson static void pending_nmi_handler(isr_regs_t *regs)
4659f23b246SSean Christopherson {
4669f23b246SSean Christopherson     int i;
4679f23b246SSean Christopherson 
4689f23b246SSean Christopherson     if (++nmi_received == 1) {
4699f23b246SSean Christopherson         for (i = 0; i < 10; ++i)
4709f23b246SSean Christopherson             apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI, 0);
4719f23b246SSean Christopherson     }
4729f23b246SSean Christopherson }
4739f23b246SSean Christopherson 
4749f23b246SSean Christopherson static void test_pending_nmi(void)
4759f23b246SSean Christopherson {
4769f23b246SSean Christopherson     int i;
4779f23b246SSean Christopherson 
4789f23b246SSean Christopherson     handle_irq(2, pending_nmi_handler);
4799f23b246SSean Christopherson     for (i = 0; i < 100000; ++i) {
4809f23b246SSean Christopherson 	    nmi_received = 0;
4819f23b246SSean Christopherson 
4829f23b246SSean Christopherson         apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI, 0);
4839f23b246SSean Christopherson         while (nmi_received < 2)
4849f23b246SSean Christopherson             pause();
4859f23b246SSean Christopherson 
4869f23b246SSean Christopherson         if (nmi_received != 2)
4879f23b246SSean Christopherson             break;
4889f23b246SSean Christopherson     }
489a299895bSThomas Huth     report(nmi_received == 2, "pending nmi");
4909f23b246SSean Christopherson }
4919f23b246SSean Christopherson 
4929f815b29SPeter Xu static volatile int lvtt_counter = 0;
4939f815b29SPeter Xu 
4949f815b29SPeter Xu static void lvtt_handler(isr_regs_t *regs)
4959f815b29SPeter Xu {
4969f815b29SPeter Xu     lvtt_counter++;
4979f815b29SPeter Xu     eoi();
4989f815b29SPeter Xu }
4999f815b29SPeter Xu 
5009f815b29SPeter Xu static void test_apic_timer_one_shot(void)
5019f815b29SPeter Xu {
5029f815b29SPeter Xu     uint64_t tsc1, tsc2;
5039f815b29SPeter Xu     static const uint32_t interval = 0x10000;
5049f815b29SPeter Xu 
5059f815b29SPeter Xu #define APIC_LVT_TIMER_VECTOR    (0xee)
5069f815b29SPeter Xu 
5079f815b29SPeter Xu     handle_irq(APIC_LVT_TIMER_VECTOR, lvtt_handler);
5089f815b29SPeter Xu     irq_enable();
5099f815b29SPeter Xu 
5109f815b29SPeter Xu     /* One shot mode */
5119111ccabSRadim Krčmář     apic_write(APIC_LVTT, APIC_LVT_TIMER_ONESHOT |
5129f815b29SPeter Xu                APIC_LVT_TIMER_VECTOR);
5139f815b29SPeter Xu     /* Divider == 1 */
5149f815b29SPeter Xu     apic_write(APIC_TDCR, 0x0000000b);
5159f815b29SPeter Xu 
5169f815b29SPeter Xu     tsc1 = rdtsc();
5179f815b29SPeter Xu     /* Set "Initial Counter Register", which starts the timer */
5189f815b29SPeter Xu     apic_write(APIC_TMICT, interval);
5199f815b29SPeter Xu     while (!lvtt_counter);
5209f815b29SPeter Xu     tsc2 = rdtsc();
5219f815b29SPeter Xu 
5229f815b29SPeter Xu     /*
5239f815b29SPeter Xu      * For LVT Timer clock, SDM vol 3 10.5.4 says it should be
5249f815b29SPeter Xu      * derived from processor's bus clock (IIUC which is the same
5259f815b29SPeter Xu      * as TSC), however QEMU seems to be using nanosecond. In all
5269f815b29SPeter Xu      * cases, the following should satisfy on all modern
5279f815b29SPeter Xu      * processors.
5289f815b29SPeter Xu      */
529a299895bSThomas Huth     report((lvtt_counter == 1) && (tsc2 - tsc1 >= interval),
530a299895bSThomas Huth            "APIC LVT timer one shot");
5319f815b29SPeter Xu }
5329f815b29SPeter Xu 
5339931b88cSRadim Krčmář static atomic_t broadcast_counter;
5349931b88cSRadim Krčmář 
5359931b88cSRadim Krčmář static void broadcast_handler(isr_regs_t *regs)
5369931b88cSRadim Krčmář {
5379931b88cSRadim Krčmář 	atomic_inc(&broadcast_counter);
5389931b88cSRadim Krčmář 	eoi();
5399931b88cSRadim Krčmář }
5409931b88cSRadim Krčmář 
5419931b88cSRadim Krčmář static bool broadcast_received(unsigned ncpus)
5429931b88cSRadim Krčmář {
5439931b88cSRadim Krčmář 	unsigned counter;
5449931b88cSRadim Krčmář 	u64 start = rdtsc();
5459931b88cSRadim Krčmář 
5469931b88cSRadim Krčmář 	do {
5479931b88cSRadim Krčmář 		counter = atomic_read(&broadcast_counter);
5489931b88cSRadim Krčmář 		if (counter >= ncpus)
5499931b88cSRadim Krčmář 			break;
5509931b88cSRadim Krčmář 		pause();
5519931b88cSRadim Krčmář 	} while (rdtsc() - start < 1000000000);
5529931b88cSRadim Krčmář 
5539931b88cSRadim Krčmář 	atomic_set(&broadcast_counter, 0);
5549931b88cSRadim Krčmář 
5559931b88cSRadim Krčmář 	return counter == ncpus;
5569931b88cSRadim Krčmář }
5579931b88cSRadim Krčmář 
5589931b88cSRadim Krčmář static void test_physical_broadcast(void)
5599931b88cSRadim Krčmář {
5609931b88cSRadim Krčmář 	unsigned ncpus = cpu_count();
5619931b88cSRadim Krčmář 	unsigned long cr3 = read_cr3();
5629931b88cSRadim Krčmář 	u32 broadcast_address = enable_x2apic() ? 0xffffffff : 0xff;
5639931b88cSRadim Krčmář 
5649931b88cSRadim Krčmář 	handle_irq(BROADCAST_VECTOR, broadcast_handler);
5659931b88cSRadim Krčmář 	for (int c = 1; c < ncpus; c++)
5669931b88cSRadim Krčmář 		on_cpu(c, update_cr3, (void *)cr3);
5679931b88cSRadim Krčmář 
5689931b88cSRadim Krčmář 	printf("starting broadcast (%s)\n", enable_x2apic() ? "x2apic" : "xapic");
5699931b88cSRadim Krčmář 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT |
5709931b88cSRadim Krčmář 			BROADCAST_VECTOR, broadcast_address);
571a299895bSThomas Huth 	report(broadcast_received(ncpus), "APIC physical broadcast address");
5729931b88cSRadim Krčmář 
5739931b88cSRadim Krčmář 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT |
5749931b88cSRadim Krčmář 			BROADCAST_VECTOR | APIC_DEST_ALLINC, 0);
575a299895bSThomas Huth 	report(broadcast_received(ncpus), "APIC physical broadcast shorthand");
5769931b88cSRadim Krčmář }
5779931b88cSRadim Krčmář 
5780eac5394SEvgeny Yakovlev static void wait_until_tmcct_common(uint32_t initial_count, bool stop_when_half, bool should_wrap_around)
579d9b2b283SWanpeng Li {
580d9b2b283SWanpeng Li 	uint32_t tmcct = apic_read(APIC_TMCCT);
581d9b2b283SWanpeng Li 
582d9b2b283SWanpeng Li 	if (tmcct) {
583d9b2b283SWanpeng Li 		while (tmcct > (initial_count / 2))
584d9b2b283SWanpeng Li 			tmcct = apic_read(APIC_TMCCT);
585d9b2b283SWanpeng Li 
586d9b2b283SWanpeng Li 		if ( stop_when_half )
587d9b2b283SWanpeng Li 			return;
588d9b2b283SWanpeng Li 
589d9b2b283SWanpeng Li 		/* Wait until the counter reach 0 or wrap-around */
590d9b2b283SWanpeng Li 		while ( tmcct <= (initial_count / 2) && tmcct > 0 )
591d9b2b283SWanpeng Li 			tmcct = apic_read(APIC_TMCCT);
5920eac5394SEvgeny Yakovlev 
5930eac5394SEvgeny Yakovlev 		/* Wait specifically for wrap around to skip 0 TMCCR if we were asked to */
5940eac5394SEvgeny Yakovlev 		while (should_wrap_around && !tmcct)
5950eac5394SEvgeny Yakovlev 			tmcct = apic_read(APIC_TMCCT);
596d9b2b283SWanpeng Li 	}
597d9b2b283SWanpeng Li }
598d9b2b283SWanpeng Li 
5990eac5394SEvgeny Yakovlev static void wait_until_tmcct_is_zero(uint32_t initial_count, bool stop_when_half)
6000eac5394SEvgeny Yakovlev {
6010eac5394SEvgeny Yakovlev 	return wait_until_tmcct_common(initial_count, stop_when_half, false);
6020eac5394SEvgeny Yakovlev }
6030eac5394SEvgeny Yakovlev 
6040eac5394SEvgeny Yakovlev static void wait_until_tmcct_wrap_around(uint32_t initial_count, bool stop_when_half)
6050eac5394SEvgeny Yakovlev {
6060eac5394SEvgeny Yakovlev 	return wait_until_tmcct_common(initial_count, stop_when_half, true);
6070eac5394SEvgeny Yakovlev }
6080eac5394SEvgeny Yakovlev 
609d9b2b283SWanpeng Li static inline void apic_change_mode(unsigned long new_mode)
610d9b2b283SWanpeng Li {
611d9b2b283SWanpeng Li 	uint32_t lvtt;
612d9b2b283SWanpeng Li 
613d9b2b283SWanpeng Li 	lvtt = apic_read(APIC_LVTT);
614d9b2b283SWanpeng Li 	apic_write(APIC_LVTT, (lvtt & ~APIC_LVT_TIMER_MASK) | new_mode);
615d9b2b283SWanpeng Li }
616d9b2b283SWanpeng Li 
6177db17e21SThomas Huth static void test_apic_change_mode(void)
618d9b2b283SWanpeng Li {
619d9b2b283SWanpeng Li 	uint32_t tmict = 0x999999;
620d9b2b283SWanpeng Li 
621d9b2b283SWanpeng Li 	printf("starting apic change mode\n");
622d9b2b283SWanpeng Li 
623d9b2b283SWanpeng Li 	apic_write(APIC_TMICT, tmict);
624d9b2b283SWanpeng Li 
625d9b2b283SWanpeng Li 	apic_change_mode(APIC_LVT_TIMER_PERIODIC);
626d9b2b283SWanpeng Li 
627a299895bSThomas Huth 	report(apic_read(APIC_TMICT) == tmict, "TMICT value reset");
628d9b2b283SWanpeng Li 
629d9b2b283SWanpeng Li 	/* Testing one-shot */
630d9b2b283SWanpeng Li 	apic_change_mode(APIC_LVT_TIMER_ONESHOT);
631d9b2b283SWanpeng Li 	apic_write(APIC_TMICT, tmict);
632a299895bSThomas Huth 	report(apic_read(APIC_TMCCT), "TMCCT should have a non-zero value");
633d9b2b283SWanpeng Li 
634d9b2b283SWanpeng Li 	wait_until_tmcct_is_zero(tmict, false);
635a299895bSThomas Huth 	report(!apic_read(APIC_TMCCT), "TMCCT should have reached 0");
636d9b2b283SWanpeng Li 
637d9b2b283SWanpeng Li 	/*
638d9b2b283SWanpeng Li 	 * Write TMICT before changing mode from one-shot to periodic TMCCT should
639d9b2b283SWanpeng Li 	 * be reset to TMICT periodicly
640d9b2b283SWanpeng Li 	 */
641d9b2b283SWanpeng Li 	apic_write(APIC_TMICT, tmict);
642d9b2b283SWanpeng Li 	wait_until_tmcct_is_zero(tmict, true);
643d9b2b283SWanpeng Li 	apic_change_mode(APIC_LVT_TIMER_PERIODIC);
644a299895bSThomas Huth 	report(apic_read(APIC_TMCCT), "TMCCT should have a non-zero value");
645d9b2b283SWanpeng Li 
646d9b2b283SWanpeng Li 	/*
647d9b2b283SWanpeng Li 	 * After the change of mode, the counter should not be reset and continue
648d9b2b283SWanpeng Li 	 * counting down from where it was
649d9b2b283SWanpeng Li 	 */
650a299895bSThomas Huth 	report(apic_read(APIC_TMCCT) < (tmict / 2),
651a299895bSThomas Huth 	       "TMCCT should not be reset to TMICT value");
6520eac5394SEvgeny Yakovlev 	/*
6530eac5394SEvgeny Yakovlev 	 * Specifically wait for timer wrap around and skip 0.
6540eac5394SEvgeny Yakovlev 	 * Under KVM lapic there is a possibility that a small amount of consecutive
6550eac5394SEvgeny Yakovlev 	 * TMCCR reads return 0 while hrtimer is reset in an async callback
6560eac5394SEvgeny Yakovlev 	 */
6570eac5394SEvgeny Yakovlev 	wait_until_tmcct_wrap_around(tmict, false);
658a299895bSThomas Huth 	report(apic_read(APIC_TMCCT) > (tmict / 2),
659a299895bSThomas Huth 	       "TMCCT should be reset to the initial-count");
660d9b2b283SWanpeng Li 
661d9b2b283SWanpeng Li 	wait_until_tmcct_is_zero(tmict, true);
662d9b2b283SWanpeng Li 	/*
663d9b2b283SWanpeng Li 	 * Keep the same TMICT and change timer mode to one-shot
664d9b2b283SWanpeng Li 	 * TMCCT should be > 0 and count-down to 0
665d9b2b283SWanpeng Li 	 */
666d9b2b283SWanpeng Li 	apic_change_mode(APIC_LVT_TIMER_ONESHOT);
667a299895bSThomas Huth 	report(apic_read(APIC_TMCCT) < (tmict / 2),
668a299895bSThomas Huth 	       "TMCCT should not be reset to init");
669d9b2b283SWanpeng Li 	wait_until_tmcct_is_zero(tmict, false);
670a299895bSThomas Huth 	report(!apic_read(APIC_TMCCT), "TMCCT should have reach zero");
671d9b2b283SWanpeng Li 
672d9b2b283SWanpeng Li 	/* now tmcct == 0 and tmict != 0 */
673d9b2b283SWanpeng Li 	apic_change_mode(APIC_LVT_TIMER_PERIODIC);
674a299895bSThomas Huth 	report(!apic_read(APIC_TMCCT), "TMCCT should stay at zero");
675d9b2b283SWanpeng Li }
676d9b2b283SWanpeng Li 
677de8d3fccSWanpeng Li #define KVM_HC_SEND_IPI 10
678de8d3fccSWanpeng Li 
679de8d3fccSWanpeng Li static void test_pv_ipi(void)
680de8d3fccSWanpeng Li {
681de8d3fccSWanpeng Li     int ret;
682de8d3fccSWanpeng Li     unsigned long a0 = 0xFFFFFFFF, a1 = 0, a2 = 0xFFFFFFFF, a3 = 0x0;
683de8d3fccSWanpeng Li 
684de8d3fccSWanpeng Li     asm volatile("vmcall" : "=a"(ret) :"a"(KVM_HC_SEND_IPI), "b"(a0), "c"(a1), "d"(a2), "S"(a3));
685a299895bSThomas Huth     report(!ret, "PV IPIs testing");
686de8d3fccSWanpeng Li }
687de8d3fccSWanpeng Li 
6887db17e21SThomas Huth int main(void)
6897d36db35SAvi Kivity {
6907d36db35SAvi Kivity     setup_vm();
6917d36db35SAvi Kivity 
6927d36db35SAvi Kivity     test_lapic_existence();
6937d36db35SAvi Kivity 
6947d36db35SAvi Kivity     mask_pic_interrupts();
695a222b5e2SRadim Krčmář     test_apic_id();
696c3ccca3fSJim Mattson     test_apic_disable();
6977d36db35SAvi Kivity     test_enable_x2apic();
6989b6bdb3fSJan Kiszka     test_apicbase();
6997d36db35SAvi Kivity 
700685d5f62SRicardo Koller     test_self_ipi_xapic();
701685d5f62SRicardo Koller     test_self_ipi_x2apic();
7029931b88cSRadim Krčmář     test_physical_broadcast();
70303b1e457SNadav Amit     if (test_device_enabled())
704de8d3fccSWanpeng Li         test_pv_ipi();
7057d36db35SAvi Kivity 
706f2d2b7c7SAvi Kivity     test_sti_nmi();
707173e7eacSAvi Kivity     test_multiple_nmi();
7089f23b246SSean Christopherson     test_pending_nmi();
7097d36db35SAvi Kivity 
7109f815b29SPeter Xu     test_apic_timer_one_shot();
711d9b2b283SWanpeng Li     test_apic_change_mode();
712d423ca36SLiu, Jinsong     test_tsc_deadline_timer();
713d423ca36SLiu, Jinsong 
714f3cdd159SJan Kiszka     return report_summary();
7157d36db35SAvi Kivity }
716