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 13*0d329639SSean Christopherson static bool is_apic_hw_enabled(void) 14*0d329639SSean Christopherson { 15*0d329639SSean Christopherson return rdmsr(MSR_IA32_APICBASE) & APIC_EN; 16*0d329639SSean Christopherson } 17*0d329639SSean Christopherson 18*0d329639SSean Christopherson static bool is_x2apic_enabled(void) 19*0d329639SSean Christopherson { 20*0d329639SSean Christopherson return (rdmsr(MSR_IA32_APICBASE) & (APIC_EN | APIC_EXTD)) == (APIC_EN | APIC_EXTD); 21*0d329639SSean Christopherson } 22*0d329639SSean Christopherson 23*0d329639SSean Christopherson static bool is_xapic_enabled(void) 24*0d329639SSean Christopherson { 25*0d329639SSean Christopherson return (rdmsr(MSR_IA32_APICBASE) & (APIC_EN | APIC_EXTD)) == APIC_EN; 26*0d329639SSean Christopherson } 27*0d329639SSean Christopherson 287d36db35SAvi Kivity static void test_lapic_existence(void) 297d36db35SAvi Kivity { 303ee24f29SNadav Amit u8 version; 317d36db35SAvi Kivity 323ee24f29SNadav Amit version = (u8)apic_read(APIC_LVR); 333ee24f29SNadav Amit printf("apic version: %x\n", version); 34a299895bSThomas Huth report(version >= 0x10 && version <= 0x15, "apic existence"); 357d36db35SAvi Kivity } 367d36db35SAvi Kivity 37d423ca36SLiu, Jinsong #define TSC_DEADLINE_TIMER_VECTOR 0xef 389931b88cSRadim Krčmář #define BROADCAST_VECTOR 0xcf 39d423ca36SLiu, Jinsong 40d423ca36SLiu, Jinsong static int tdt_count; 41d423ca36SLiu, Jinsong 42d423ca36SLiu, Jinsong static void tsc_deadline_timer_isr(isr_regs_t *regs) 43d423ca36SLiu, Jinsong { 44d423ca36SLiu, Jinsong ++tdt_count; 450b04ed06SPeter Xu eoi(); 46d423ca36SLiu, Jinsong } 47d423ca36SLiu, Jinsong 4832b9603cSRadim Krčmář static void __test_tsc_deadline_timer(void) 49d423ca36SLiu, Jinsong { 50d423ca36SLiu, Jinsong handle_irq(TSC_DEADLINE_TIMER_VECTOR, tsc_deadline_timer_isr); 51d423ca36SLiu, Jinsong irq_enable(); 52d423ca36SLiu, Jinsong 53d423ca36SLiu, Jinsong wrmsr(MSR_IA32_TSCDEADLINE, rdmsr(MSR_IA32_TSC)); 54d423ca36SLiu, Jinsong asm volatile ("nop"); 55a299895bSThomas Huth report(tdt_count == 1, "tsc deadline timer"); 56a299895bSThomas Huth report(rdmsr(MSR_IA32_TSCDEADLINE) == 0, "tsc deadline timer clearing"); 57d423ca36SLiu, Jinsong } 58d423ca36SLiu, Jinsong 59d423ca36SLiu, Jinsong static int enable_tsc_deadline_timer(void) 60d423ca36SLiu, Jinsong { 61d423ca36SLiu, Jinsong uint32_t lvtt; 62d423ca36SLiu, Jinsong 63badc98caSKrish Sadhukhan if (this_cpu_has(X86_FEATURE_TSC_DEADLINE_TIMER)) { 649111ccabSRadim Krčmář lvtt = APIC_LVT_TIMER_TSCDEADLINE | TSC_DEADLINE_TIMER_VECTOR; 65d423ca36SLiu, Jinsong apic_write(APIC_LVTT, lvtt); 66d423ca36SLiu, Jinsong return 1; 67d423ca36SLiu, Jinsong } else { 68d423ca36SLiu, Jinsong return 0; 69d423ca36SLiu, Jinsong } 70d423ca36SLiu, Jinsong } 71d423ca36SLiu, Jinsong 72d423ca36SLiu, Jinsong static void test_tsc_deadline_timer(void) 73d423ca36SLiu, Jinsong { 7427cc2e49SSean Christopherson if(enable_tsc_deadline_timer()) 7532b9603cSRadim Krčmář __test_tsc_deadline_timer(); 7627cc2e49SSean Christopherson else 7732b9603cSRadim Krčmář report_skip("tsc deadline timer not detected"); 78d423ca36SLiu, Jinsong } 79d423ca36SLiu, Jinsong 8022c7d929SJan Kiszka static void do_write_apicbase(void *data) 8122c7d929SJan Kiszka { 8222c7d929SJan Kiszka wrmsr(MSR_IA32_APICBASE, *(u64 *)data); 8303f37ef2SPaolo Bonzini } 847d36db35SAvi Kivity 8549f5ad9eSPaolo Bonzini static bool test_write_apicbase_exception(u64 data) 8649f5ad9eSPaolo Bonzini { 8749f5ad9eSPaolo Bonzini return test_for_exception(GP_VECTOR, do_write_apicbase, &data); 8849f5ad9eSPaolo Bonzini } 8949f5ad9eSPaolo Bonzini 90db4898e8SThomas Huth static void test_enable_x2apic(void) 917d36db35SAvi Kivity { 922092999cSSean Christopherson u64 orig_apicbase = rdmsr(MSR_IA32_APICBASE); 932092999cSSean Christopherson u64 apicbase; 942092999cSSean Christopherson 957d36db35SAvi Kivity if (enable_x2apic()) { 967d36db35SAvi Kivity printf("x2apic enabled\n"); 9722c7d929SJan Kiszka 982092999cSSean Christopherson apicbase = orig_apicbase & ~(APIC_EN | APIC_EXTD); 99a299895bSThomas Huth report(test_write_apicbase_exception(apicbase | APIC_EXTD), 100a299895bSThomas Huth "x2apic enabled to invalid state"); 101a299895bSThomas Huth report(test_write_apicbase_exception(apicbase | APIC_EN), 102a299895bSThomas Huth "x2apic enabled to apic enabled"); 10322c7d929SJan Kiszka 104a299895bSThomas Huth report(!test_write_apicbase_exception(apicbase | 0), 105a299895bSThomas Huth "x2apic enabled to disabled state"); 106a299895bSThomas Huth report(test_write_apicbase_exception(apicbase | APIC_EXTD), 107a299895bSThomas Huth "disabled to invalid state"); 108a299895bSThomas Huth report(test_write_apicbase_exception(apicbase | APIC_EN | APIC_EXTD), 109a299895bSThomas Huth "disabled to x2apic enabled"); 11022c7d929SJan Kiszka 111a299895bSThomas Huth report(!test_write_apicbase_exception(apicbase | APIC_EN), 112a299895bSThomas Huth "apic disabled to apic enabled"); 113a299895bSThomas Huth report(test_write_apicbase_exception(apicbase | APIC_EXTD), 114a299895bSThomas Huth "apic enabled to invalid state"); 11522c7d929SJan Kiszka 1162092999cSSean Christopherson if (orig_apicbase & APIC_EXTD) 1172092999cSSean Christopherson enable_x2apic(); 1182092999cSSean Christopherson else 1192092999cSSean Christopherson reset_apic(); 1202092999cSSean Christopherson 1212092999cSSean Christopherson /* 12227cc2e49SSean Christopherson * Disabling the APIC resets various APIC registers, restore 12327cc2e49SSean Christopherson * them to their desired values. 1242092999cSSean Christopherson */ 12522c7d929SJan Kiszka apic_write(APIC_SPIV, 0x1ff); 1267d36db35SAvi Kivity } else { 1277d36db35SAvi Kivity printf("x2apic not detected\n"); 12822c7d929SJan Kiszka 129a299895bSThomas Huth report(test_write_apicbase_exception(APIC_EN | APIC_EXTD), 130a299895bSThomas Huth "enable unsupported x2apic"); 1317d36db35SAvi Kivity } 1327d36db35SAvi Kivity } 1337d36db35SAvi Kivity 134e38858bcSJim Mattson static void verify_disabled_apic_mmio(void) 135e38858bcSJim 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); 138e38858bcSJim Mattson u32 cr8 = read_cr8(); 139e38858bcSJim Mattson 140e38858bcSJim Mattson memset((void *)APIC_DEFAULT_PHYS_BASE, 0xff, PAGE_SIZE); 141a299895bSThomas Huth report(*lvr == ~0, "*0xfee00030: %x", *lvr); 142a299895bSThomas Huth report(read_cr8() == cr8, "CR8: %lx", read_cr8()); 143e38858bcSJim Mattson write_cr8(cr8 ^ MAX_TPR); 144a299895bSThomas Huth report(read_cr8() == (cr8 ^ MAX_TPR), "CR8: %lx", read_cr8()); 145a299895bSThomas Huth report(*tpr == ~0, "*0xfee00080: %x", *tpr); 146e38858bcSJim Mattson write_cr8(cr8); 147e38858bcSJim Mattson } 148e38858bcSJim Mattson 149c3ccca3fSJim Mattson static void test_apic_disable(void) 150c3ccca3fSJim Mattson { 151e38858bcSJim Mattson volatile u32 *lvr = (volatile u32 *)(APIC_DEFAULT_PHYS_BASE + APIC_LVR); 152e38858bcSJim Mattson volatile u32 *tpr = (volatile u32 *)(APIC_DEFAULT_PHYS_BASE + APIC_TASKPRI); 153c3ccca3fSJim Mattson u64 orig_apicbase = rdmsr(MSR_IA32_APICBASE); 154e38858bcSJim Mattson u32 apic_version = apic_read(APIC_LVR); 155e38858bcSJim Mattson u32 cr8 = read_cr8(); 156c3ccca3fSJim Mattson 157c3ccca3fSJim Mattson report_prefix_push("apic_disable"); 158e38858bcSJim Mattson assert_msg(orig_apicbase & APIC_EN, "APIC not enabled."); 159c3ccca3fSJim Mattson 160e38858bcSJim Mattson disable_apic(); 161*0d329639SSean Christopherson report(!is_apic_hw_enabled(), "Local apic disabled"); 162a299895bSThomas Huth report(!this_cpu_has(X86_FEATURE_APIC), 163a299895bSThomas Huth "CPUID.1H:EDX.APIC[bit 9] is clear"); 164e38858bcSJim Mattson verify_disabled_apic_mmio(); 165c3ccca3fSJim Mattson 166e38858bcSJim Mattson reset_apic(); 167*0d329639SSean Christopherson report(is_xapic_enabled(), "Local apic enabled in xAPIC mode"); 168a299895bSThomas Huth report(this_cpu_has(X86_FEATURE_APIC), "CPUID.1H:EDX.APIC[bit 9] is set"); 169a299895bSThomas Huth report(*lvr == apic_version, "*0xfee00030: %x", *lvr); 170a299895bSThomas Huth report(*tpr == cr8, "*0xfee00080: %x", *tpr); 171e38858bcSJim Mattson write_cr8(cr8 ^ MAX_TPR); 172a299895bSThomas Huth report(*tpr == (cr8 ^ MAX_TPR) << 4, "*0xfee00080: %x", *tpr); 173e38858bcSJim Mattson write_cr8(cr8); 174c3ccca3fSJim Mattson 175e38858bcSJim Mattson if (enable_x2apic()) { 176e38858bcSJim Mattson apic_write(APIC_SPIV, 0x1ff); 177*0d329639SSean Christopherson report(is_x2apic_enabled(), "Local apic enabled in x2APIC mode"); 178a299895bSThomas Huth report(this_cpu_has(X86_FEATURE_APIC), 179a299895bSThomas Huth "CPUID.1H:EDX.APIC[bit 9] is set"); 180e38858bcSJim Mattson verify_disabled_apic_mmio(); 181e38858bcSJim Mattson if (!(orig_apicbase & APIC_EXTD)) 182e38858bcSJim Mattson reset_apic(); 183e38858bcSJim Mattson } 184c3ccca3fSJim Mattson report_prefix_pop(); 185c3ccca3fSJim Mattson } 186c3ccca3fSJim Mattson 187615a8838SNadav Amit #define ALTERNATE_APIC_BASE 0xfed40000 1889b6bdb3fSJan Kiszka 1899b6bdb3fSJan Kiszka static void test_apicbase(void) 1909b6bdb3fSJan Kiszka { 1919b6bdb3fSJan Kiszka u64 orig_apicbase = rdmsr(MSR_IA32_APICBASE); 1929b6bdb3fSJan Kiszka u32 lvr = apic_read(APIC_LVR); 1939b6bdb3fSJan Kiszka u64 value; 1949b6bdb3fSJan Kiszka 1959b6bdb3fSJan Kiszka wrmsr(MSR_IA32_APICBASE, orig_apicbase & ~(APIC_EN | APIC_EXTD)); 1969b6bdb3fSJan Kiszka wrmsr(MSR_IA32_APICBASE, ALTERNATE_APIC_BASE | APIC_BSP | APIC_EN); 1979b6bdb3fSJan Kiszka 1985bba1769SAndrew Jones report_prefix_push("apicbase"); 1995bba1769SAndrew Jones 200a299895bSThomas Huth report(*(volatile u32 *)(ALTERNATE_APIC_BASE + APIC_LVR) == lvr, 201a299895bSThomas Huth "relocate apic"); 2029b6bdb3fSJan Kiszka 203772befb7SEduardo Habkost value = orig_apicbase | (1UL << cpuid_maxphyaddr()); 204a299895bSThomas Huth report(test_for_exception(GP_VECTOR, do_write_apicbase, &value), 205a299895bSThomas Huth "reserved physaddr bits"); 2069b6bdb3fSJan Kiszka 2079b6bdb3fSJan Kiszka value = orig_apicbase | 1; 208a299895bSThomas Huth report(test_for_exception(GP_VECTOR, do_write_apicbase, &value), 209a299895bSThomas Huth "reserved low bits"); 2109b6bdb3fSJan Kiszka 2119b6bdb3fSJan Kiszka wrmsr(MSR_IA32_APICBASE, orig_apicbase); 2129b6bdb3fSJan Kiszka apic_write(APIC_SPIV, 0x1ff); 2135bba1769SAndrew Jones 2145bba1769SAndrew Jones report_prefix_pop(); 2159b6bdb3fSJan Kiszka } 2169b6bdb3fSJan Kiszka 217a222b5e2SRadim Krčmář static void do_write_apic_id(void *id) 218a222b5e2SRadim Krčmář { 219a222b5e2SRadim Krčmář apic_write(APIC_ID, *(u32 *)id); 220a222b5e2SRadim Krčmář } 221a222b5e2SRadim Krčmář 222a222b5e2SRadim Krčmář static void __test_apic_id(void * unused) 223a222b5e2SRadim Krčmář { 224a222b5e2SRadim Krčmář u32 id, newid; 225a222b5e2SRadim Krčmář u8 initial_xapic_id = cpuid(1).b >> 24; 226a222b5e2SRadim Krčmář u32 initial_x2apic_id = cpuid(0xb).d; 227*0d329639SSean Christopherson bool x2apic_mode = is_x2apic_enabled(); 228a222b5e2SRadim Krčmář 229a222b5e2SRadim Krčmář if (x2apic_mode) 230a222b5e2SRadim Krčmář reset_apic(); 231a222b5e2SRadim Krčmář 232a222b5e2SRadim Krčmář id = apic_id(); 233a299895bSThomas Huth report(initial_xapic_id == id, "xapic id matches cpuid"); 234a222b5e2SRadim Krčmář 235a222b5e2SRadim Krčmář newid = (id + 1) << 24; 236a299895bSThomas Huth report(!test_for_exception(GP_VECTOR, do_write_apic_id, &newid) && 237a299895bSThomas Huth (id == apic_id() || id + 1 == apic_id()), 238a299895bSThomas Huth "writeable xapic id"); 239a222b5e2SRadim Krčmář 240a222b5e2SRadim Krčmář if (!enable_x2apic()) 241a222b5e2SRadim Krčmář goto out; 242a222b5e2SRadim Krčmář 243a299895bSThomas Huth report(test_for_exception(GP_VECTOR, do_write_apic_id, &newid), 244a299895bSThomas Huth "non-writeable x2apic id"); 245a299895bSThomas Huth report(initial_xapic_id == (apic_id() & 0xff), "sane x2apic id"); 246a222b5e2SRadim Krčmář 247a222b5e2SRadim Krčmář /* old QEMUs do not set initial x2APIC ID */ 248a299895bSThomas Huth report(initial_xapic_id == (initial_x2apic_id & 0xff) && 249a299895bSThomas Huth initial_x2apic_id == apic_id(), 250a299895bSThomas Huth "x2apic id matches cpuid"); 251a222b5e2SRadim Krčmář 252a222b5e2SRadim Krčmář out: 253a222b5e2SRadim Krčmář reset_apic(); 254a222b5e2SRadim Krčmář 255a299895bSThomas Huth report(initial_xapic_id == apic_id(), "correct xapic id after reset"); 256a222b5e2SRadim Krčmář 257a222b5e2SRadim Krčmář /* old KVMs do not reset xAPIC ID */ 258a222b5e2SRadim Krčmář if (id != apic_id()) 259a222b5e2SRadim Krčmář apic_write(APIC_ID, id << 24); 260a222b5e2SRadim Krčmář 261a222b5e2SRadim Krčmář if (x2apic_mode) 262a222b5e2SRadim Krčmář enable_x2apic(); 263a222b5e2SRadim Krčmář } 264a222b5e2SRadim Krčmář 265a222b5e2SRadim Krčmář static void test_apic_id(void) 266a222b5e2SRadim Krčmář { 267a222b5e2SRadim Krčmář if (cpu_count() < 2) 268a222b5e2SRadim Krčmář return; 269a222b5e2SRadim Krčmář 270a222b5e2SRadim Krčmář on_cpu(1, __test_apic_id, NULL); 271a222b5e2SRadim Krčmář } 272a222b5e2SRadim Krčmář 2737d36db35SAvi Kivity static int ipi_count; 2747d36db35SAvi Kivity 2757d36db35SAvi Kivity static void self_ipi_isr(isr_regs_t *regs) 2767d36db35SAvi Kivity { 2777d36db35SAvi Kivity ++ipi_count; 2787d36db35SAvi Kivity eoi(); 2797d36db35SAvi Kivity } 2807d36db35SAvi Kivity 281685d5f62SRicardo Koller static void __test_self_ipi(void) 2827d36db35SAvi Kivity { 2836c0999f4SNadav Amit u64 start = rdtsc(); 2847d36db35SAvi Kivity int vec = 0xf1; 2857d36db35SAvi Kivity 286d51bd17eSGleb Natapov handle_irq(vec, self_ipi_isr); 2877d36db35SAvi Kivity irq_enable(); 2887d36db35SAvi Kivity apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec, 28918a34cceSNadav Amit id_map[0]); 2906c0999f4SNadav Amit 2916c0999f4SNadav Amit do { 2926c0999f4SNadav Amit pause(); 2936c0999f4SNadav Amit } while (rdtsc() - start < 1000000000 && ipi_count == 0); 294685d5f62SRicardo Koller } 2956c0999f4SNadav Amit 296685d5f62SRicardo Koller static void test_self_ipi_xapic(void) 297685d5f62SRicardo Koller { 298*0d329639SSean Christopherson u64 was_x2apic = is_x2apic_enabled(); 299685d5f62SRicardo Koller 300685d5f62SRicardo Koller report_prefix_push("self_ipi_xapic"); 301685d5f62SRicardo Koller 302685d5f62SRicardo Koller /* Reset to xAPIC mode. */ 303685d5f62SRicardo Koller reset_apic(); 304*0d329639SSean Christopherson report(is_xapic_enabled(), "Local apic enabled in xAPIC mode"); 305685d5f62SRicardo Koller 306685d5f62SRicardo Koller ipi_count = 0; 307685d5f62SRicardo Koller __test_self_ipi(); 308a299895bSThomas Huth report(ipi_count == 1, "self ipi"); 309685d5f62SRicardo Koller 310685d5f62SRicardo Koller /* Enable x2APIC mode if it was already enabled. */ 311*0d329639SSean Christopherson if (was_x2apic) 312685d5f62SRicardo Koller enable_x2apic(); 313685d5f62SRicardo Koller 314685d5f62SRicardo Koller report_prefix_pop(); 315685d5f62SRicardo Koller } 316685d5f62SRicardo Koller 317685d5f62SRicardo Koller static void test_self_ipi_x2apic(void) 318685d5f62SRicardo Koller { 319*0d329639SSean Christopherson u64 was_xapic = is_xapic_enabled(); 320685d5f62SRicardo Koller 321685d5f62SRicardo Koller report_prefix_push("self_ipi_x2apic"); 322685d5f62SRicardo Koller 323685d5f62SRicardo Koller if (enable_x2apic()) { 324*0d329639SSean Christopherson report(is_x2apic_enabled(), "Local apic enabled in x2APIC mode"); 325685d5f62SRicardo Koller 326685d5f62SRicardo Koller ipi_count = 0; 327685d5f62SRicardo Koller __test_self_ipi(); 328685d5f62SRicardo Koller report(ipi_count == 1, "self ipi"); 329685d5f62SRicardo Koller 330685d5f62SRicardo Koller /* Reset to xAPIC mode unless x2APIC was already enabled. */ 331*0d329639SSean Christopherson if (was_xapic) 332685d5f62SRicardo Koller reset_apic(); 333685d5f62SRicardo Koller } else { 334685d5f62SRicardo Koller report_skip("x2apic not detected"); 335685d5f62SRicardo Koller } 336685d5f62SRicardo Koller 337685d5f62SRicardo Koller report_prefix_pop(); 3387d36db35SAvi Kivity } 3397d36db35SAvi Kivity 340f2d2b7c7SAvi Kivity volatile int nmi_counter_private, nmi_counter, nmi_hlt_counter, sti_loop_active; 341f2d2b7c7SAvi Kivity 342db4898e8SThomas Huth static void sti_nop(char *p) 343f2d2b7c7SAvi Kivity { 344f2d2b7c7SAvi Kivity asm volatile ( 345f2d2b7c7SAvi Kivity ".globl post_sti \n\t" 346f2d2b7c7SAvi Kivity "sti \n" 347f2d2b7c7SAvi Kivity /* 348f2d2b7c7SAvi Kivity * vmx won't exit on external interrupt if blocked-by-sti, 349f2d2b7c7SAvi Kivity * so give it a reason to exit by accessing an unmapped page. 350f2d2b7c7SAvi Kivity */ 351f2d2b7c7SAvi Kivity "post_sti: testb $0, %0 \n\t" 352f2d2b7c7SAvi Kivity "nop \n\t" 353f2d2b7c7SAvi Kivity "cli" 354f2d2b7c7SAvi Kivity : : "m"(*p) 355f2d2b7c7SAvi Kivity ); 356f2d2b7c7SAvi Kivity nmi_counter = nmi_counter_private; 357f2d2b7c7SAvi Kivity } 358f2d2b7c7SAvi Kivity 359f2d2b7c7SAvi Kivity static void sti_loop(void *ignore) 360f2d2b7c7SAvi Kivity { 361f2d2b7c7SAvi Kivity unsigned k = 0; 362f2d2b7c7SAvi Kivity 36327cc2e49SSean Christopherson while (sti_loop_active) 364f2d2b7c7SAvi Kivity sti_nop((char *)(ulong)((k++ * 4096) % (128 * 1024 * 1024))); 365f2d2b7c7SAvi Kivity } 366f2d2b7c7SAvi Kivity 367f2d2b7c7SAvi Kivity static void nmi_handler(isr_regs_t *regs) 368f2d2b7c7SAvi Kivity { 369f2d2b7c7SAvi Kivity extern void post_sti(void); 370f2d2b7c7SAvi Kivity ++nmi_counter_private; 371f2d2b7c7SAvi Kivity nmi_hlt_counter += regs->rip == (ulong)post_sti; 372f2d2b7c7SAvi Kivity } 373f2d2b7c7SAvi Kivity 374f2d2b7c7SAvi Kivity static void test_sti_nmi(void) 375f2d2b7c7SAvi Kivity { 376f2d2b7c7SAvi Kivity unsigned old_counter; 377f2d2b7c7SAvi Kivity 37827cc2e49SSean Christopherson if (cpu_count() < 2) 379f2d2b7c7SAvi Kivity return; 380f2d2b7c7SAvi Kivity 381d51bd17eSGleb Natapov handle_irq(2, nmi_handler); 382f2d2b7c7SAvi Kivity on_cpu(1, update_cr3, (void *)read_cr3()); 383f2d2b7c7SAvi Kivity 384f2d2b7c7SAvi Kivity sti_loop_active = 1; 385f2d2b7c7SAvi Kivity on_cpu_async(1, sti_loop, 0); 386f2d2b7c7SAvi Kivity while (nmi_counter < 30000) { 387f2d2b7c7SAvi Kivity old_counter = nmi_counter; 38818a34cceSNadav Amit apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, id_map[1]); 38927cc2e49SSean Christopherson while (nmi_counter == old_counter) 390f2d2b7c7SAvi Kivity ; 391f2d2b7c7SAvi Kivity } 392f2d2b7c7SAvi Kivity sti_loop_active = 0; 393a299895bSThomas Huth report(nmi_hlt_counter == 0, "nmi-after-sti"); 394f2d2b7c7SAvi Kivity } 395f2d2b7c7SAvi Kivity 396173e7eacSAvi Kivity static volatile bool nmi_done, nmi_flushed; 397173e7eacSAvi Kivity static volatile int nmi_received; 398173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr1, cpu1_nmi_ctr1; 399173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr2, cpu1_nmi_ctr2; 400173e7eacSAvi Kivity 401173e7eacSAvi Kivity static void multiple_nmi_handler(isr_regs_t *regs) 402173e7eacSAvi Kivity { 403173e7eacSAvi Kivity ++nmi_received; 404173e7eacSAvi Kivity } 405173e7eacSAvi Kivity 406173e7eacSAvi Kivity static void kick_me_nmi(void *blah) 407173e7eacSAvi Kivity { 408173e7eacSAvi Kivity while (!nmi_done) { 409173e7eacSAvi Kivity ++cpu1_nmi_ctr1; 41027cc2e49SSean Christopherson while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1 && !nmi_done) 411173e7eacSAvi Kivity pause(); 41227cc2e49SSean Christopherson 41327cc2e49SSean Christopherson if (nmi_done) 414173e7eacSAvi Kivity return; 41527cc2e49SSean Christopherson 41618a34cceSNadav Amit apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, id_map[0]); 417173e7eacSAvi Kivity /* make sure the NMI has arrived by sending an IPI after it */ 418173e7eacSAvi Kivity apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT 41918a34cceSNadav Amit | 0x44, id_map[0]); 420173e7eacSAvi Kivity ++cpu1_nmi_ctr2; 42127cc2e49SSean Christopherson while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2 && !nmi_done) 422173e7eacSAvi Kivity pause(); 423173e7eacSAvi Kivity } 424173e7eacSAvi Kivity } 425173e7eacSAvi Kivity 426173e7eacSAvi Kivity static void flush_nmi(isr_regs_t *regs) 427173e7eacSAvi Kivity { 428173e7eacSAvi Kivity nmi_flushed = true; 429173e7eacSAvi Kivity apic_write(APIC_EOI, 0); 430173e7eacSAvi Kivity } 431173e7eacSAvi Kivity 432173e7eacSAvi Kivity static void test_multiple_nmi(void) 433173e7eacSAvi Kivity { 434173e7eacSAvi Kivity int i; 435173e7eacSAvi Kivity bool ok = true; 436173e7eacSAvi Kivity 43727cc2e49SSean Christopherson if (cpu_count() < 2) 438173e7eacSAvi Kivity return; 439173e7eacSAvi Kivity 440173e7eacSAvi Kivity sti(); 441173e7eacSAvi Kivity handle_irq(2, multiple_nmi_handler); 442173e7eacSAvi Kivity handle_irq(0x44, flush_nmi); 443173e7eacSAvi Kivity on_cpu_async(1, kick_me_nmi, 0); 444a7f60697SPaolo Bonzini for (i = 0; i < 100000; ++i) { 445173e7eacSAvi Kivity nmi_flushed = false; 446173e7eacSAvi Kivity nmi_received = 0; 447173e7eacSAvi Kivity ++cpu0_nmi_ctr1; 44827cc2e49SSean Christopherson while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1) 449173e7eacSAvi Kivity pause(); 45027cc2e49SSean Christopherson 45118a34cceSNadav Amit apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, id_map[0]); 45227cc2e49SSean Christopherson while (!nmi_flushed) 453173e7eacSAvi Kivity pause(); 45427cc2e49SSean Christopherson 455173e7eacSAvi Kivity if (nmi_received != 2) { 456173e7eacSAvi Kivity ok = false; 457173e7eacSAvi Kivity break; 458173e7eacSAvi Kivity } 45927cc2e49SSean Christopherson 460173e7eacSAvi Kivity ++cpu0_nmi_ctr2; 46127cc2e49SSean Christopherson while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2) 462173e7eacSAvi Kivity pause(); 463173e7eacSAvi Kivity } 464173e7eacSAvi Kivity nmi_done = true; 465a299895bSThomas Huth report(ok, "multiple nmi"); 466173e7eacSAvi Kivity } 467173e7eacSAvi Kivity 4689f23b246SSean Christopherson static void pending_nmi_handler(isr_regs_t *regs) 4699f23b246SSean Christopherson { 4709f23b246SSean Christopherson int i; 4719f23b246SSean Christopherson 4729f23b246SSean Christopherson if (++nmi_received == 1) { 4739f23b246SSean Christopherson for (i = 0; i < 10; ++i) 4749f23b246SSean Christopherson apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI, 0); 4759f23b246SSean Christopherson } 4769f23b246SSean Christopherson } 4779f23b246SSean Christopherson 4789f23b246SSean Christopherson static void test_pending_nmi(void) 4799f23b246SSean Christopherson { 4809f23b246SSean Christopherson int i; 4819f23b246SSean Christopherson 4829f23b246SSean Christopherson handle_irq(2, pending_nmi_handler); 4839f23b246SSean Christopherson for (i = 0; i < 100000; ++i) { 4849f23b246SSean Christopherson nmi_received = 0; 4859f23b246SSean Christopherson 4869f23b246SSean Christopherson apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI, 0); 4879f23b246SSean Christopherson while (nmi_received < 2) 4889f23b246SSean Christopherson pause(); 4899f23b246SSean Christopherson 4909f23b246SSean Christopherson if (nmi_received != 2) 4919f23b246SSean Christopherson break; 4929f23b246SSean Christopherson } 493a299895bSThomas Huth report(nmi_received == 2, "pending nmi"); 4949f23b246SSean Christopherson } 4959f23b246SSean Christopherson 4969f815b29SPeter Xu static volatile int lvtt_counter = 0; 4979f815b29SPeter Xu 4989f815b29SPeter Xu static void lvtt_handler(isr_regs_t *regs) 4999f815b29SPeter Xu { 5009f815b29SPeter Xu lvtt_counter++; 5019f815b29SPeter Xu eoi(); 5029f815b29SPeter Xu } 5039f815b29SPeter Xu 5049f815b29SPeter Xu static void test_apic_timer_one_shot(void) 5059f815b29SPeter Xu { 5069f815b29SPeter Xu uint64_t tsc1, tsc2; 5079f815b29SPeter Xu static const uint32_t interval = 0x10000; 5089f815b29SPeter Xu 5099f815b29SPeter Xu #define APIC_LVT_TIMER_VECTOR (0xee) 5109f815b29SPeter Xu 5119f815b29SPeter Xu handle_irq(APIC_LVT_TIMER_VECTOR, lvtt_handler); 5129f815b29SPeter Xu irq_enable(); 5139f815b29SPeter Xu 5149f815b29SPeter Xu /* One shot mode */ 5159111ccabSRadim Krčmář apic_write(APIC_LVTT, APIC_LVT_TIMER_ONESHOT | 5169f815b29SPeter Xu APIC_LVT_TIMER_VECTOR); 5179f815b29SPeter Xu /* Divider == 1 */ 5189f815b29SPeter Xu apic_write(APIC_TDCR, 0x0000000b); 5199f815b29SPeter Xu 5209f815b29SPeter Xu tsc1 = rdtsc(); 5219f815b29SPeter Xu /* Set "Initial Counter Register", which starts the timer */ 5229f815b29SPeter Xu apic_write(APIC_TMICT, interval); 5239f815b29SPeter Xu while (!lvtt_counter); 5249f815b29SPeter Xu tsc2 = rdtsc(); 5259f815b29SPeter Xu 5269f815b29SPeter Xu /* 5279f815b29SPeter Xu * For LVT Timer clock, SDM vol 3 10.5.4 says it should be 5289f815b29SPeter Xu * derived from processor's bus clock (IIUC which is the same 5299f815b29SPeter Xu * as TSC), however QEMU seems to be using nanosecond. In all 5309f815b29SPeter Xu * cases, the following should satisfy on all modern 5319f815b29SPeter Xu * processors. 5329f815b29SPeter Xu */ 533a299895bSThomas Huth report((lvtt_counter == 1) && (tsc2 - tsc1 >= interval), 534a299895bSThomas Huth "APIC LVT timer one shot"); 5359f815b29SPeter Xu } 5369f815b29SPeter Xu 5379931b88cSRadim Krčmář static atomic_t broadcast_counter; 5389931b88cSRadim Krčmář 5399931b88cSRadim Krčmář static void broadcast_handler(isr_regs_t *regs) 5409931b88cSRadim Krčmář { 5419931b88cSRadim Krčmář atomic_inc(&broadcast_counter); 5429931b88cSRadim Krčmář eoi(); 5439931b88cSRadim Krčmář } 5449931b88cSRadim Krčmář 5459931b88cSRadim Krčmář static bool broadcast_received(unsigned ncpus) 5469931b88cSRadim Krčmář { 5479931b88cSRadim Krčmář unsigned counter; 5489931b88cSRadim Krčmář u64 start = rdtsc(); 5499931b88cSRadim Krčmář 5509931b88cSRadim Krčmář do { 5519931b88cSRadim Krčmář counter = atomic_read(&broadcast_counter); 5529931b88cSRadim Krčmář if (counter >= ncpus) 5539931b88cSRadim Krčmář break; 5549931b88cSRadim Krčmář pause(); 5559931b88cSRadim Krčmář } while (rdtsc() - start < 1000000000); 5569931b88cSRadim Krčmář 5579931b88cSRadim Krčmář atomic_set(&broadcast_counter, 0); 5589931b88cSRadim Krčmář 5599931b88cSRadim Krčmář return counter == ncpus; 5609931b88cSRadim Krčmář } 5619931b88cSRadim Krčmář 5629931b88cSRadim Krčmář static void test_physical_broadcast(void) 5639931b88cSRadim Krčmář { 5649931b88cSRadim Krčmář unsigned ncpus = cpu_count(); 5659931b88cSRadim Krčmář unsigned long cr3 = read_cr3(); 5669931b88cSRadim Krčmář u32 broadcast_address = enable_x2apic() ? 0xffffffff : 0xff; 5679931b88cSRadim Krčmář 5689931b88cSRadim Krčmář handle_irq(BROADCAST_VECTOR, broadcast_handler); 5699931b88cSRadim Krčmář for (int c = 1; c < ncpus; c++) 5709931b88cSRadim Krčmář on_cpu(c, update_cr3, (void *)cr3); 5719931b88cSRadim Krčmář 5729931b88cSRadim Krčmář printf("starting broadcast (%s)\n", enable_x2apic() ? "x2apic" : "xapic"); 5739931b88cSRadim Krčmář apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT | 5749931b88cSRadim Krčmář BROADCAST_VECTOR, broadcast_address); 575a299895bSThomas Huth report(broadcast_received(ncpus), "APIC physical broadcast address"); 5769931b88cSRadim Krčmář 5779931b88cSRadim Krčmář apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT | 5789931b88cSRadim Krčmář BROADCAST_VECTOR | APIC_DEST_ALLINC, 0); 579a299895bSThomas Huth report(broadcast_received(ncpus), "APIC physical broadcast shorthand"); 5809931b88cSRadim Krčmář } 5819931b88cSRadim Krčmář 5820eac5394SEvgeny Yakovlev static void wait_until_tmcct_common(uint32_t initial_count, bool stop_when_half, bool should_wrap_around) 583d9b2b283SWanpeng Li { 584d9b2b283SWanpeng Li uint32_t tmcct = apic_read(APIC_TMCCT); 585d9b2b283SWanpeng Li 586d9b2b283SWanpeng Li if (tmcct) { 587d9b2b283SWanpeng Li while (tmcct > (initial_count / 2)) 588d9b2b283SWanpeng Li tmcct = apic_read(APIC_TMCCT); 589d9b2b283SWanpeng Li 590d9b2b283SWanpeng Li if ( stop_when_half ) 591d9b2b283SWanpeng Li return; 592d9b2b283SWanpeng Li 593d9b2b283SWanpeng Li /* Wait until the counter reach 0 or wrap-around */ 594d9b2b283SWanpeng Li while ( tmcct <= (initial_count / 2) && tmcct > 0 ) 595d9b2b283SWanpeng Li tmcct = apic_read(APIC_TMCCT); 5960eac5394SEvgeny Yakovlev 5970eac5394SEvgeny Yakovlev /* Wait specifically for wrap around to skip 0 TMCCR if we were asked to */ 5980eac5394SEvgeny Yakovlev while (should_wrap_around && !tmcct) 5990eac5394SEvgeny Yakovlev tmcct = apic_read(APIC_TMCCT); 600d9b2b283SWanpeng Li } 601d9b2b283SWanpeng Li } 602d9b2b283SWanpeng Li 6030eac5394SEvgeny Yakovlev static void wait_until_tmcct_is_zero(uint32_t initial_count, bool stop_when_half) 6040eac5394SEvgeny Yakovlev { 6050eac5394SEvgeny Yakovlev return wait_until_tmcct_common(initial_count, stop_when_half, false); 6060eac5394SEvgeny Yakovlev } 6070eac5394SEvgeny Yakovlev 6080eac5394SEvgeny Yakovlev static void wait_until_tmcct_wrap_around(uint32_t initial_count, bool stop_when_half) 6090eac5394SEvgeny Yakovlev { 6100eac5394SEvgeny Yakovlev return wait_until_tmcct_common(initial_count, stop_when_half, true); 6110eac5394SEvgeny Yakovlev } 6120eac5394SEvgeny Yakovlev 613d9b2b283SWanpeng Li static inline void apic_change_mode(unsigned long new_mode) 614d9b2b283SWanpeng Li { 615d9b2b283SWanpeng Li uint32_t lvtt; 616d9b2b283SWanpeng Li 617d9b2b283SWanpeng Li lvtt = apic_read(APIC_LVTT); 618d9b2b283SWanpeng Li apic_write(APIC_LVTT, (lvtt & ~APIC_LVT_TIMER_MASK) | new_mode); 619d9b2b283SWanpeng Li } 620d9b2b283SWanpeng Li 6217db17e21SThomas Huth static void test_apic_change_mode(void) 622d9b2b283SWanpeng Li { 623d9b2b283SWanpeng Li uint32_t tmict = 0x999999; 624d9b2b283SWanpeng Li 625d9b2b283SWanpeng Li printf("starting apic change mode\n"); 626d9b2b283SWanpeng Li 627d9b2b283SWanpeng Li apic_write(APIC_TMICT, tmict); 628d9b2b283SWanpeng Li 629d9b2b283SWanpeng Li apic_change_mode(APIC_LVT_TIMER_PERIODIC); 630d9b2b283SWanpeng Li 631a299895bSThomas Huth report(apic_read(APIC_TMICT) == tmict, "TMICT value reset"); 632d9b2b283SWanpeng Li 633d9b2b283SWanpeng Li /* Testing one-shot */ 634d9b2b283SWanpeng Li apic_change_mode(APIC_LVT_TIMER_ONESHOT); 635d9b2b283SWanpeng Li apic_write(APIC_TMICT, tmict); 636a299895bSThomas Huth report(apic_read(APIC_TMCCT), "TMCCT should have a non-zero value"); 637d9b2b283SWanpeng Li 638d9b2b283SWanpeng Li wait_until_tmcct_is_zero(tmict, false); 639a299895bSThomas Huth report(!apic_read(APIC_TMCCT), "TMCCT should have reached 0"); 640d9b2b283SWanpeng Li 641d9b2b283SWanpeng Li /* 642d9b2b283SWanpeng Li * Write TMICT before changing mode from one-shot to periodic TMCCT should 643d9b2b283SWanpeng Li * be reset to TMICT periodicly 644d9b2b283SWanpeng Li */ 645d9b2b283SWanpeng Li apic_write(APIC_TMICT, tmict); 646d9b2b283SWanpeng Li wait_until_tmcct_is_zero(tmict, true); 647d9b2b283SWanpeng Li apic_change_mode(APIC_LVT_TIMER_PERIODIC); 648a299895bSThomas Huth report(apic_read(APIC_TMCCT), "TMCCT should have a non-zero value"); 649d9b2b283SWanpeng Li 650d9b2b283SWanpeng Li /* 651d9b2b283SWanpeng Li * After the change of mode, the counter should not be reset and continue 652d9b2b283SWanpeng Li * counting down from where it was 653d9b2b283SWanpeng Li */ 654a299895bSThomas Huth report(apic_read(APIC_TMCCT) < (tmict / 2), 655a299895bSThomas Huth "TMCCT should not be reset to TMICT value"); 6560eac5394SEvgeny Yakovlev /* 6570eac5394SEvgeny Yakovlev * Specifically wait for timer wrap around and skip 0. 6580eac5394SEvgeny Yakovlev * Under KVM lapic there is a possibility that a small amount of consecutive 6590eac5394SEvgeny Yakovlev * TMCCR reads return 0 while hrtimer is reset in an async callback 6600eac5394SEvgeny Yakovlev */ 6610eac5394SEvgeny Yakovlev wait_until_tmcct_wrap_around(tmict, false); 662a299895bSThomas Huth report(apic_read(APIC_TMCCT) > (tmict / 2), 663a299895bSThomas Huth "TMCCT should be reset to the initial-count"); 664d9b2b283SWanpeng Li 665d9b2b283SWanpeng Li wait_until_tmcct_is_zero(tmict, true); 666d9b2b283SWanpeng Li /* 667d9b2b283SWanpeng Li * Keep the same TMICT and change timer mode to one-shot 668d9b2b283SWanpeng Li * TMCCT should be > 0 and count-down to 0 669d9b2b283SWanpeng Li */ 670d9b2b283SWanpeng Li apic_change_mode(APIC_LVT_TIMER_ONESHOT); 671a299895bSThomas Huth report(apic_read(APIC_TMCCT) < (tmict / 2), 672a299895bSThomas Huth "TMCCT should not be reset to init"); 673d9b2b283SWanpeng Li wait_until_tmcct_is_zero(tmict, false); 674a299895bSThomas Huth report(!apic_read(APIC_TMCCT), "TMCCT should have reach zero"); 675d9b2b283SWanpeng Li 676d9b2b283SWanpeng Li /* now tmcct == 0 and tmict != 0 */ 677d9b2b283SWanpeng Li apic_change_mode(APIC_LVT_TIMER_PERIODIC); 678a299895bSThomas Huth report(!apic_read(APIC_TMCCT), "TMCCT should stay at zero"); 679d9b2b283SWanpeng Li } 680d9b2b283SWanpeng Li 681de8d3fccSWanpeng Li #define KVM_HC_SEND_IPI 10 682de8d3fccSWanpeng Li 683de8d3fccSWanpeng Li static void test_pv_ipi(void) 684de8d3fccSWanpeng Li { 685de8d3fccSWanpeng Li int ret; 686de8d3fccSWanpeng Li unsigned long a0 = 0xFFFFFFFF, a1 = 0, a2 = 0xFFFFFFFF, a3 = 0x0; 687de8d3fccSWanpeng Li 688de8d3fccSWanpeng Li asm volatile("vmcall" : "=a"(ret) :"a"(KVM_HC_SEND_IPI), "b"(a0), "c"(a1), "d"(a2), "S"(a3)); 689a299895bSThomas Huth report(!ret, "PV IPIs testing"); 690de8d3fccSWanpeng Li } 691de8d3fccSWanpeng Li 6927db17e21SThomas Huth int main(void) 6937d36db35SAvi Kivity { 6947d36db35SAvi Kivity setup_vm(); 6957d36db35SAvi Kivity 6967d36db35SAvi Kivity test_lapic_existence(); 6977d36db35SAvi Kivity 6987d36db35SAvi Kivity mask_pic_interrupts(); 699a222b5e2SRadim Krčmář test_apic_id(); 700c3ccca3fSJim Mattson test_apic_disable(); 7017d36db35SAvi Kivity test_enable_x2apic(); 7029b6bdb3fSJan Kiszka test_apicbase(); 7037d36db35SAvi Kivity 704685d5f62SRicardo Koller test_self_ipi_xapic(); 705685d5f62SRicardo Koller test_self_ipi_x2apic(); 7069931b88cSRadim Krčmář test_physical_broadcast(); 70703b1e457SNadav Amit if (test_device_enabled()) 708de8d3fccSWanpeng Li test_pv_ipi(); 7097d36db35SAvi Kivity 710f2d2b7c7SAvi Kivity test_sti_nmi(); 711173e7eacSAvi Kivity test_multiple_nmi(); 7129f23b246SSean Christopherson test_pending_nmi(); 7137d36db35SAvi Kivity 7149f815b29SPeter Xu test_apic_timer_one_shot(); 715d9b2b283SWanpeng Li test_apic_change_mode(); 716d423ca36SLiu, Jinsong test_tsc_deadline_timer(); 717d423ca36SLiu, Jinsong 718f3cdd159SJan Kiszka return report_summary(); 7197d36db35SAvi Kivity } 720