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