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 130d329639SSean Christopherson static bool is_apic_hw_enabled(void) 140d329639SSean Christopherson { 150d329639SSean Christopherson return rdmsr(MSR_IA32_APICBASE) & APIC_EN; 160d329639SSean Christopherson } 170d329639SSean Christopherson 181eb8551aSSean Christopherson static bool is_apic_sw_enabled(void) 191eb8551aSSean Christopherson { 201eb8551aSSean Christopherson return apic_read(APIC_SPIV) & APIC_SPIV_APIC_ENABLED; 211eb8551aSSean Christopherson } 221eb8551aSSean Christopherson 230d329639SSean Christopherson static bool is_x2apic_enabled(void) 240d329639SSean Christopherson { 250d329639SSean Christopherson return (rdmsr(MSR_IA32_APICBASE) & (APIC_EN | APIC_EXTD)) == (APIC_EN | APIC_EXTD); 260d329639SSean Christopherson } 270d329639SSean Christopherson 280d329639SSean Christopherson static bool is_xapic_enabled(void) 290d329639SSean Christopherson { 300d329639SSean Christopherson return (rdmsr(MSR_IA32_APICBASE) & (APIC_EN | APIC_EXTD)) == APIC_EN; 310d329639SSean Christopherson } 320d329639SSean Christopherson 337d36db35SAvi Kivity static void test_lapic_existence(void) 347d36db35SAvi Kivity { 353ee24f29SNadav Amit u8 version; 367d36db35SAvi Kivity 373ee24f29SNadav Amit version = (u8)apic_read(APIC_LVR); 383ee24f29SNadav Amit printf("apic version: %x\n", version); 39a299895bSThomas Huth report(version >= 0x10 && version <= 0x15, "apic existence"); 407d36db35SAvi Kivity } 417d36db35SAvi Kivity 42d423ca36SLiu, Jinsong #define TSC_DEADLINE_TIMER_VECTOR 0xef 439931b88cSRadim Krčmář #define BROADCAST_VECTOR 0xcf 44d423ca36SLiu, Jinsong 45d423ca36SLiu, Jinsong static int tdt_count; 46d423ca36SLiu, Jinsong 47d423ca36SLiu, Jinsong static void tsc_deadline_timer_isr(isr_regs_t *regs) 48d423ca36SLiu, Jinsong { 49d423ca36SLiu, Jinsong ++tdt_count; 500b04ed06SPeter Xu eoi(); 51d423ca36SLiu, Jinsong } 52d423ca36SLiu, Jinsong 5332b9603cSRadim Krčmář static void __test_tsc_deadline_timer(void) 54d423ca36SLiu, Jinsong { 55d423ca36SLiu, Jinsong handle_irq(TSC_DEADLINE_TIMER_VECTOR, tsc_deadline_timer_isr); 56d423ca36SLiu, Jinsong 57d423ca36SLiu, Jinsong wrmsr(MSR_IA32_TSCDEADLINE, rdmsr(MSR_IA32_TSC)); 58d423ca36SLiu, Jinsong asm volatile ("nop"); 59a299895bSThomas Huth report(tdt_count == 1, "tsc deadline timer"); 60a299895bSThomas Huth report(rdmsr(MSR_IA32_TSCDEADLINE) == 0, "tsc deadline timer clearing"); 61d423ca36SLiu, Jinsong } 62d423ca36SLiu, Jinsong 63d423ca36SLiu, Jinsong static int enable_tsc_deadline_timer(void) 64d423ca36SLiu, Jinsong { 65d423ca36SLiu, Jinsong uint32_t lvtt; 66d423ca36SLiu, Jinsong 67badc98caSKrish Sadhukhan if (this_cpu_has(X86_FEATURE_TSC_DEADLINE_TIMER)) { 689111ccabSRadim Krčmář lvtt = APIC_LVT_TIMER_TSCDEADLINE | TSC_DEADLINE_TIMER_VECTOR; 69d423ca36SLiu, Jinsong apic_write(APIC_LVTT, lvtt); 70d423ca36SLiu, Jinsong return 1; 71d423ca36SLiu, Jinsong } else { 72d423ca36SLiu, Jinsong return 0; 73d423ca36SLiu, Jinsong } 74d423ca36SLiu, Jinsong } 75d423ca36SLiu, Jinsong 76d423ca36SLiu, Jinsong static void test_tsc_deadline_timer(void) 77d423ca36SLiu, Jinsong { 7827cc2e49SSean Christopherson if(enable_tsc_deadline_timer()) 7932b9603cSRadim Krčmář __test_tsc_deadline_timer(); 8027cc2e49SSean Christopherson else 8132b9603cSRadim Krčmář report_skip("tsc deadline timer not detected"); 82d423ca36SLiu, Jinsong } 83d423ca36SLiu, Jinsong 8422c7d929SJan Kiszka static void do_write_apicbase(void *data) 8522c7d929SJan Kiszka { 8622c7d929SJan Kiszka wrmsr(MSR_IA32_APICBASE, *(u64 *)data); 8703f37ef2SPaolo Bonzini } 887d36db35SAvi Kivity 8949f5ad9eSPaolo Bonzini static bool test_write_apicbase_exception(u64 data) 9049f5ad9eSPaolo Bonzini { 9149f5ad9eSPaolo Bonzini return test_for_exception(GP_VECTOR, do_write_apicbase, &data); 9249f5ad9eSPaolo Bonzini } 9349f5ad9eSPaolo Bonzini 94db4898e8SThomas Huth static void test_enable_x2apic(void) 957d36db35SAvi Kivity { 966dc73196SSean Christopherson u64 apicbase = rdmsr(MSR_IA32_APICBASE); 972092999cSSean Christopherson 987d36db35SAvi Kivity if (enable_x2apic()) { 997d36db35SAvi Kivity printf("x2apic enabled\n"); 10022c7d929SJan Kiszka 1016dc73196SSean Christopherson apicbase &= ~(APIC_EN | APIC_EXTD); 102a299895bSThomas Huth report(test_write_apicbase_exception(apicbase | APIC_EXTD), 103a299895bSThomas Huth "x2apic enabled to invalid state"); 104a299895bSThomas Huth report(test_write_apicbase_exception(apicbase | APIC_EN), 105a299895bSThomas Huth "x2apic enabled to apic enabled"); 10622c7d929SJan Kiszka 107a299895bSThomas Huth report(!test_write_apicbase_exception(apicbase | 0), 108a299895bSThomas Huth "x2apic enabled to disabled state"); 109a299895bSThomas Huth report(test_write_apicbase_exception(apicbase | APIC_EXTD), 110a299895bSThomas Huth "disabled to invalid state"); 111a299895bSThomas Huth report(test_write_apicbase_exception(apicbase | APIC_EN | APIC_EXTD), 112a299895bSThomas Huth "disabled to x2apic enabled"); 11322c7d929SJan Kiszka 114a299895bSThomas Huth report(!test_write_apicbase_exception(apicbase | APIC_EN), 115a299895bSThomas Huth "apic disabled to apic enabled"); 116a299895bSThomas Huth report(test_write_apicbase_exception(apicbase | APIC_EXTD), 117a299895bSThomas Huth "apic enabled to invalid state"); 1187d36db35SAvi Kivity } else { 1197d36db35SAvi Kivity printf("x2apic not detected\n"); 12022c7d929SJan Kiszka 121a299895bSThomas Huth report(test_write_apicbase_exception(APIC_EN | APIC_EXTD), 122a299895bSThomas Huth "enable unsupported x2apic"); 1237d36db35SAvi Kivity } 1247d36db35SAvi Kivity } 1257d36db35SAvi Kivity 126e38858bcSJim Mattson static void verify_disabled_apic_mmio(void) 127e38858bcSJim Mattson { 128e38858bcSJim Mattson volatile u32 *lvr = (volatile u32 *)(APIC_DEFAULT_PHYS_BASE + APIC_LVR); 129e38858bcSJim Mattson volatile u32 *tpr = (volatile u32 *)(APIC_DEFAULT_PHYS_BASE + APIC_TASKPRI); 130e38858bcSJim Mattson u32 cr8 = read_cr8(); 131e38858bcSJim Mattson 132e38858bcSJim Mattson memset((void *)APIC_DEFAULT_PHYS_BASE, 0xff, PAGE_SIZE); 133a299895bSThomas Huth report(*lvr == ~0, "*0xfee00030: %x", *lvr); 134a299895bSThomas Huth report(read_cr8() == cr8, "CR8: %lx", read_cr8()); 135e38858bcSJim Mattson write_cr8(cr8 ^ MAX_TPR); 136a299895bSThomas Huth report(read_cr8() == (cr8 ^ MAX_TPR), "CR8: %lx", read_cr8()); 137a299895bSThomas Huth report(*tpr == ~0, "*0xfee00080: %x", *tpr); 138e38858bcSJim Mattson write_cr8(cr8); 139e38858bcSJim Mattson } 140e38858bcSJim Mattson 141c3ccca3fSJim Mattson static void test_apic_disable(void) 142c3ccca3fSJim Mattson { 143e38858bcSJim Mattson volatile u32 *lvr = (volatile u32 *)(APIC_DEFAULT_PHYS_BASE + APIC_LVR); 144e38858bcSJim Mattson volatile u32 *tpr = (volatile u32 *)(APIC_DEFAULT_PHYS_BASE + APIC_TASKPRI); 145e38858bcSJim Mattson u32 apic_version = apic_read(APIC_LVR); 146e38858bcSJim Mattson u32 cr8 = read_cr8(); 147c3ccca3fSJim Mattson 148c3ccca3fSJim Mattson report_prefix_push("apic_disable"); 149c3ccca3fSJim Mattson 150e38858bcSJim Mattson disable_apic(); 1510d329639SSean Christopherson report(!is_apic_hw_enabled(), "Local apic disabled"); 152a299895bSThomas Huth report(!this_cpu_has(X86_FEATURE_APIC), 153a299895bSThomas Huth "CPUID.1H:EDX.APIC[bit 9] is clear"); 154e38858bcSJim Mattson verify_disabled_apic_mmio(); 155c3ccca3fSJim Mattson 156e38858bcSJim Mattson reset_apic(); 1570d329639SSean Christopherson report(is_xapic_enabled(), "Local apic enabled in xAPIC mode"); 158a299895bSThomas Huth report(this_cpu_has(X86_FEATURE_APIC), "CPUID.1H:EDX.APIC[bit 9] is set"); 159a299895bSThomas Huth report(*lvr == apic_version, "*0xfee00030: %x", *lvr); 160a299895bSThomas Huth report(*tpr == cr8, "*0xfee00080: %x", *tpr); 161e38858bcSJim Mattson write_cr8(cr8 ^ MAX_TPR); 162a299895bSThomas Huth report(*tpr == (cr8 ^ MAX_TPR) << 4, "*0xfee00080: %x", *tpr); 163e38858bcSJim Mattson write_cr8(cr8); 164c3ccca3fSJim Mattson 165e38858bcSJim Mattson if (enable_x2apic()) { 1660d329639SSean Christopherson report(is_x2apic_enabled(), "Local apic enabled in x2APIC mode"); 167a299895bSThomas Huth report(this_cpu_has(X86_FEATURE_APIC), 168a299895bSThomas Huth "CPUID.1H:EDX.APIC[bit 9] is set"); 169e38858bcSJim Mattson verify_disabled_apic_mmio(); 170e38858bcSJim Mattson } 171c3ccca3fSJim Mattson report_prefix_pop(); 172c3ccca3fSJim Mattson } 173c3ccca3fSJim Mattson 174615a8838SNadav Amit #define ALTERNATE_APIC_BASE 0xfed40000 1759b6bdb3fSJan Kiszka 1769b6bdb3fSJan Kiszka static void test_apicbase(void) 1779b6bdb3fSJan Kiszka { 1789b6bdb3fSJan Kiszka u64 orig_apicbase = rdmsr(MSR_IA32_APICBASE); 1799b6bdb3fSJan Kiszka u32 lvr = apic_read(APIC_LVR); 1809b6bdb3fSJan Kiszka u64 value; 1819b6bdb3fSJan Kiszka 1829b6bdb3fSJan Kiszka wrmsr(MSR_IA32_APICBASE, orig_apicbase & ~(APIC_EN | APIC_EXTD)); 1839b6bdb3fSJan Kiszka wrmsr(MSR_IA32_APICBASE, ALTERNATE_APIC_BASE | APIC_BSP | APIC_EN); 1849b6bdb3fSJan Kiszka 1855bba1769SAndrew Jones report_prefix_push("apicbase"); 1865bba1769SAndrew Jones 187a299895bSThomas Huth report(*(volatile u32 *)(ALTERNATE_APIC_BASE + APIC_LVR) == lvr, 188a299895bSThomas Huth "relocate apic"); 1899b6bdb3fSJan Kiszka 190772befb7SEduardo Habkost value = orig_apicbase | (1UL << cpuid_maxphyaddr()); 191a299895bSThomas Huth report(test_for_exception(GP_VECTOR, do_write_apicbase, &value), 192a299895bSThomas Huth "reserved physaddr bits"); 1939b6bdb3fSJan Kiszka 1949b6bdb3fSJan Kiszka value = orig_apicbase | 1; 195a299895bSThomas Huth report(test_for_exception(GP_VECTOR, do_write_apicbase, &value), 196a299895bSThomas Huth "reserved low bits"); 1979b6bdb3fSJan Kiszka 1986dc73196SSean Christopherson /* Restore the APIC address, the "reset" helpers leave it as is. */ 1999b6bdb3fSJan Kiszka wrmsr(MSR_IA32_APICBASE, orig_apicbase); 2005bba1769SAndrew Jones 2015bba1769SAndrew Jones report_prefix_pop(); 2029b6bdb3fSJan Kiszka } 2039b6bdb3fSJan Kiszka 204a222b5e2SRadim Krčmář static void do_write_apic_id(void *id) 205a222b5e2SRadim Krčmář { 206a222b5e2SRadim Krčmář apic_write(APIC_ID, *(u32 *)id); 207a222b5e2SRadim Krčmář } 208a222b5e2SRadim Krčmář 209a222b5e2SRadim Krčmář static void __test_apic_id(void * unused) 210a222b5e2SRadim Krčmář { 211a222b5e2SRadim Krčmář u32 id, newid; 212a222b5e2SRadim Krčmář u8 initial_xapic_id = cpuid(1).b >> 24; 213a222b5e2SRadim Krčmář u32 initial_x2apic_id = cpuid(0xb).d; 2140d329639SSean Christopherson bool x2apic_mode = is_x2apic_enabled(); 215a222b5e2SRadim Krčmář 216a222b5e2SRadim Krčmář if (x2apic_mode) 217a222b5e2SRadim Krčmář reset_apic(); 218a222b5e2SRadim Krčmář 219a222b5e2SRadim Krčmář id = apic_id(); 220a299895bSThomas Huth report(initial_xapic_id == id, "xapic id matches cpuid"); 221a222b5e2SRadim Krčmář 222a222b5e2SRadim Krčmář newid = (id + 1) << 24; 223a299895bSThomas Huth report(!test_for_exception(GP_VECTOR, do_write_apic_id, &newid) && 224a299895bSThomas Huth (id == apic_id() || id + 1 == apic_id()), 225a299895bSThomas Huth "writeable xapic id"); 226a222b5e2SRadim Krčmář 227a222b5e2SRadim Krčmář if (!enable_x2apic()) 228a222b5e2SRadim Krčmář goto out; 229a222b5e2SRadim Krčmář 230a299895bSThomas Huth report(test_for_exception(GP_VECTOR, do_write_apic_id, &newid), 231a299895bSThomas Huth "non-writeable x2apic id"); 232a299895bSThomas Huth report(initial_xapic_id == (apic_id() & 0xff), "sane x2apic id"); 233a222b5e2SRadim Krčmář 234a222b5e2SRadim Krčmář /* old QEMUs do not set initial x2APIC ID */ 235a299895bSThomas Huth report(initial_xapic_id == (initial_x2apic_id & 0xff) && 236a299895bSThomas Huth initial_x2apic_id == apic_id(), 237a299895bSThomas Huth "x2apic id matches cpuid"); 238a222b5e2SRadim Krčmář 239a222b5e2SRadim Krčmář out: 240a222b5e2SRadim Krčmář reset_apic(); 241a222b5e2SRadim Krčmář 242a299895bSThomas Huth report(initial_xapic_id == apic_id(), "correct xapic id after reset"); 243a222b5e2SRadim Krčmář 244a222b5e2SRadim Krčmář /* old KVMs do not reset xAPIC ID */ 245a222b5e2SRadim Krčmář if (id != apic_id()) 246a222b5e2SRadim Krčmář apic_write(APIC_ID, id << 24); 247a222b5e2SRadim Krčmář 248a222b5e2SRadim Krčmář if (x2apic_mode) 249a222b5e2SRadim Krčmář enable_x2apic(); 250a222b5e2SRadim Krčmář } 251a222b5e2SRadim Krčmář 252a222b5e2SRadim Krčmář static void test_apic_id(void) 253a222b5e2SRadim Krčmář { 254a222b5e2SRadim Krčmář if (cpu_count() < 2) 255a222b5e2SRadim Krčmář return; 256a222b5e2SRadim Krčmář 257a222b5e2SRadim Krčmář on_cpu(1, __test_apic_id, NULL); 258a222b5e2SRadim Krčmář } 259a222b5e2SRadim Krčmář 260d8f62a83SSean Christopherson static atomic_t ipi_count; 2617d36db35SAvi Kivity 262d8f62a83SSean Christopherson static void handle_ipi(isr_regs_t *regs) 2637d36db35SAvi Kivity { 264d8f62a83SSean Christopherson atomic_inc(&ipi_count); 2657d36db35SAvi Kivity eoi(); 2667d36db35SAvi Kivity } 2677d36db35SAvi Kivity 268685d5f62SRicardo Koller static void __test_self_ipi(void) 2697d36db35SAvi Kivity { 2706c0999f4SNadav Amit u64 start = rdtsc(); 2717d36db35SAvi Kivity int vec = 0xf1; 2727d36db35SAvi Kivity 273d8f62a83SSean Christopherson handle_irq(vec, handle_ipi); 2747d36db35SAvi Kivity apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec, 27518a34cceSNadav Amit id_map[0]); 2766c0999f4SNadav Amit 2776c0999f4SNadav Amit do { 2786c0999f4SNadav Amit pause(); 279d8f62a83SSean Christopherson } while (rdtsc() - start < 1000000000 && atomic_read(&ipi_count) == 0); 280685d5f62SRicardo Koller } 2816c0999f4SNadav Amit 282685d5f62SRicardo Koller static void test_self_ipi_xapic(void) 283685d5f62SRicardo Koller { 284685d5f62SRicardo Koller report_prefix_push("self_ipi_xapic"); 285685d5f62SRicardo Koller 286685d5f62SRicardo Koller /* Reset to xAPIC mode. */ 287685d5f62SRicardo Koller reset_apic(); 2880d329639SSean Christopherson report(is_xapic_enabled(), "Local apic enabled in xAPIC mode"); 289685d5f62SRicardo Koller 290d8f62a83SSean Christopherson atomic_set(&ipi_count, 0); 291685d5f62SRicardo Koller __test_self_ipi(); 292d8f62a83SSean Christopherson report(atomic_read(&ipi_count) == 1, "self ipi"); 293685d5f62SRicardo Koller 294685d5f62SRicardo Koller report_prefix_pop(); 295685d5f62SRicardo Koller } 296685d5f62SRicardo Koller 297685d5f62SRicardo Koller static void test_self_ipi_x2apic(void) 298685d5f62SRicardo Koller { 299685d5f62SRicardo Koller report_prefix_push("self_ipi_x2apic"); 300685d5f62SRicardo Koller 301685d5f62SRicardo Koller if (enable_x2apic()) { 3020d329639SSean Christopherson report(is_x2apic_enabled(), "Local apic enabled in x2APIC mode"); 303685d5f62SRicardo Koller 304d8f62a83SSean Christopherson atomic_set(&ipi_count, 0); 305685d5f62SRicardo Koller __test_self_ipi(); 306d8f62a83SSean Christopherson report(atomic_read(&ipi_count) == 1, "self ipi"); 307685d5f62SRicardo Koller } else { 308685d5f62SRicardo Koller report_skip("x2apic not detected"); 309685d5f62SRicardo Koller } 310685d5f62SRicardo Koller 311685d5f62SRicardo Koller report_prefix_pop(); 3127d36db35SAvi Kivity } 3137d36db35SAvi Kivity 314f2d2b7c7SAvi Kivity volatile int nmi_counter_private, nmi_counter, nmi_hlt_counter, sti_loop_active; 315f2d2b7c7SAvi Kivity 316db4898e8SThomas Huth static void sti_nop(char *p) 317f2d2b7c7SAvi Kivity { 318f2d2b7c7SAvi Kivity asm volatile ( 319f2d2b7c7SAvi Kivity ".globl post_sti \n\t" 320f2d2b7c7SAvi Kivity "sti \n" 321f2d2b7c7SAvi Kivity /* 322f2d2b7c7SAvi Kivity * vmx won't exit on external interrupt if blocked-by-sti, 323f2d2b7c7SAvi Kivity * so give it a reason to exit by accessing an unmapped page. 324f2d2b7c7SAvi Kivity */ 325f2d2b7c7SAvi Kivity "post_sti: testb $0, %0 \n\t" 326f2d2b7c7SAvi Kivity "nop \n\t" 327f2d2b7c7SAvi Kivity "cli" 328f2d2b7c7SAvi Kivity : : "m"(*p) 329f2d2b7c7SAvi Kivity ); 330f2d2b7c7SAvi Kivity nmi_counter = nmi_counter_private; 331f2d2b7c7SAvi Kivity } 332f2d2b7c7SAvi Kivity 333f2d2b7c7SAvi Kivity static void sti_loop(void *ignore) 334f2d2b7c7SAvi Kivity { 335f2d2b7c7SAvi Kivity unsigned k = 0; 336f2d2b7c7SAvi Kivity 33727cc2e49SSean Christopherson while (sti_loop_active) 338f2d2b7c7SAvi Kivity sti_nop((char *)(ulong)((k++ * 4096) % (128 * 1024 * 1024))); 339f2d2b7c7SAvi Kivity } 340f2d2b7c7SAvi Kivity 341f2d2b7c7SAvi Kivity static void nmi_handler(isr_regs_t *regs) 342f2d2b7c7SAvi Kivity { 343f2d2b7c7SAvi Kivity extern void post_sti(void); 344f2d2b7c7SAvi Kivity ++nmi_counter_private; 345f2d2b7c7SAvi Kivity nmi_hlt_counter += regs->rip == (ulong)post_sti; 346f2d2b7c7SAvi Kivity } 347f2d2b7c7SAvi Kivity 348f2d2b7c7SAvi Kivity static void test_sti_nmi(void) 349f2d2b7c7SAvi Kivity { 350f2d2b7c7SAvi Kivity unsigned old_counter; 351f2d2b7c7SAvi Kivity 35227cc2e49SSean Christopherson if (cpu_count() < 2) 353f2d2b7c7SAvi Kivity return; 354f2d2b7c7SAvi Kivity 355d51bd17eSGleb Natapov handle_irq(2, nmi_handler); 356f2d2b7c7SAvi Kivity on_cpu(1, update_cr3, (void *)read_cr3()); 357f2d2b7c7SAvi Kivity 358f2d2b7c7SAvi Kivity sti_loop_active = 1; 359f2d2b7c7SAvi Kivity on_cpu_async(1, sti_loop, 0); 360f2d2b7c7SAvi Kivity while (nmi_counter < 30000) { 361f2d2b7c7SAvi Kivity old_counter = nmi_counter; 36218a34cceSNadav Amit apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, id_map[1]); 36327cc2e49SSean Christopherson while (nmi_counter == old_counter) 364f2d2b7c7SAvi Kivity ; 365f2d2b7c7SAvi Kivity } 366f2d2b7c7SAvi Kivity sti_loop_active = 0; 367a299895bSThomas Huth report(nmi_hlt_counter == 0, "nmi-after-sti"); 368f2d2b7c7SAvi Kivity } 369f2d2b7c7SAvi Kivity 370173e7eacSAvi Kivity static volatile bool nmi_done, nmi_flushed; 371173e7eacSAvi Kivity static volatile int nmi_received; 372173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr1, cpu1_nmi_ctr1; 373173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr2, cpu1_nmi_ctr2; 374173e7eacSAvi Kivity 375173e7eacSAvi Kivity static void multiple_nmi_handler(isr_regs_t *regs) 376173e7eacSAvi Kivity { 377173e7eacSAvi Kivity ++nmi_received; 378173e7eacSAvi Kivity } 379173e7eacSAvi Kivity 380173e7eacSAvi Kivity static void kick_me_nmi(void *blah) 381173e7eacSAvi Kivity { 382173e7eacSAvi Kivity while (!nmi_done) { 383173e7eacSAvi Kivity ++cpu1_nmi_ctr1; 38427cc2e49SSean Christopherson while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1 && !nmi_done) 385173e7eacSAvi Kivity pause(); 38627cc2e49SSean Christopherson 38727cc2e49SSean Christopherson if (nmi_done) 388173e7eacSAvi Kivity return; 38927cc2e49SSean Christopherson 39018a34cceSNadav Amit apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, id_map[0]); 391173e7eacSAvi Kivity /* make sure the NMI has arrived by sending an IPI after it */ 392173e7eacSAvi Kivity apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT 39318a34cceSNadav Amit | 0x44, id_map[0]); 394173e7eacSAvi Kivity ++cpu1_nmi_ctr2; 39527cc2e49SSean Christopherson while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2 && !nmi_done) 396173e7eacSAvi Kivity pause(); 397173e7eacSAvi Kivity } 398173e7eacSAvi Kivity } 399173e7eacSAvi Kivity 400173e7eacSAvi Kivity static void flush_nmi(isr_regs_t *regs) 401173e7eacSAvi Kivity { 402173e7eacSAvi Kivity nmi_flushed = true; 403173e7eacSAvi Kivity apic_write(APIC_EOI, 0); 404173e7eacSAvi Kivity } 405173e7eacSAvi Kivity 406173e7eacSAvi Kivity static void test_multiple_nmi(void) 407173e7eacSAvi Kivity { 408173e7eacSAvi Kivity int i; 409173e7eacSAvi Kivity bool ok = true; 410173e7eacSAvi Kivity 41127cc2e49SSean Christopherson if (cpu_count() < 2) 412173e7eacSAvi Kivity return; 413173e7eacSAvi Kivity 414173e7eacSAvi Kivity sti(); 415173e7eacSAvi Kivity handle_irq(2, multiple_nmi_handler); 416173e7eacSAvi Kivity handle_irq(0x44, flush_nmi); 417173e7eacSAvi Kivity on_cpu_async(1, kick_me_nmi, 0); 418a7f60697SPaolo Bonzini for (i = 0; i < 100000; ++i) { 419173e7eacSAvi Kivity nmi_flushed = false; 420173e7eacSAvi Kivity nmi_received = 0; 421173e7eacSAvi Kivity ++cpu0_nmi_ctr1; 42227cc2e49SSean Christopherson while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1) 423173e7eacSAvi Kivity pause(); 42427cc2e49SSean Christopherson 42518a34cceSNadav Amit apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, id_map[0]); 42627cc2e49SSean Christopherson while (!nmi_flushed) 427173e7eacSAvi Kivity pause(); 42827cc2e49SSean Christopherson 429173e7eacSAvi Kivity if (nmi_received != 2) { 430173e7eacSAvi Kivity ok = false; 431173e7eacSAvi Kivity break; 432173e7eacSAvi Kivity } 43327cc2e49SSean Christopherson 434173e7eacSAvi Kivity ++cpu0_nmi_ctr2; 43527cc2e49SSean Christopherson while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2) 436173e7eacSAvi Kivity pause(); 437173e7eacSAvi Kivity } 438173e7eacSAvi Kivity nmi_done = true; 439a299895bSThomas Huth report(ok, "multiple nmi"); 440173e7eacSAvi Kivity } 441173e7eacSAvi Kivity 4429f23b246SSean Christopherson static void pending_nmi_handler(isr_regs_t *regs) 4439f23b246SSean Christopherson { 4449f23b246SSean Christopherson int i; 4459f23b246SSean Christopherson 4469f23b246SSean Christopherson if (++nmi_received == 1) { 4479f23b246SSean Christopherson for (i = 0; i < 10; ++i) 4489f23b246SSean Christopherson apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI, 0); 4499f23b246SSean Christopherson } 4509f23b246SSean Christopherson } 4519f23b246SSean Christopherson 4529f23b246SSean Christopherson static void test_pending_nmi(void) 4539f23b246SSean Christopherson { 4549f23b246SSean Christopherson int i; 4559f23b246SSean Christopherson 4569f23b246SSean Christopherson handle_irq(2, pending_nmi_handler); 4579f23b246SSean Christopherson for (i = 0; i < 100000; ++i) { 4589f23b246SSean Christopherson nmi_received = 0; 4599f23b246SSean Christopherson 4609f23b246SSean Christopherson apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI, 0); 4619f23b246SSean Christopherson while (nmi_received < 2) 4629f23b246SSean Christopherson pause(); 4639f23b246SSean Christopherson 4649f23b246SSean Christopherson if (nmi_received != 2) 4659f23b246SSean Christopherson break; 4669f23b246SSean Christopherson } 467a299895bSThomas Huth report(nmi_received == 2, "pending nmi"); 4689f23b246SSean Christopherson } 4699f23b246SSean Christopherson 4709f815b29SPeter Xu static volatile int lvtt_counter = 0; 4719f815b29SPeter Xu 4729f815b29SPeter Xu static void lvtt_handler(isr_regs_t *regs) 4739f815b29SPeter Xu { 4749f815b29SPeter Xu lvtt_counter++; 4759f815b29SPeter Xu eoi(); 4769f815b29SPeter Xu } 4779f815b29SPeter Xu 4789f815b29SPeter Xu static void test_apic_timer_one_shot(void) 4799f815b29SPeter Xu { 4809f815b29SPeter Xu uint64_t tsc1, tsc2; 4819f815b29SPeter Xu static const uint32_t interval = 0x10000; 4829f815b29SPeter Xu 4839f815b29SPeter Xu #define APIC_LVT_TIMER_VECTOR (0xee) 4849f815b29SPeter Xu 4859f815b29SPeter Xu handle_irq(APIC_LVT_TIMER_VECTOR, lvtt_handler); 4869f815b29SPeter Xu 4879f815b29SPeter Xu /* One shot mode */ 4889111ccabSRadim Krčmář apic_write(APIC_LVTT, APIC_LVT_TIMER_ONESHOT | 4899f815b29SPeter Xu APIC_LVT_TIMER_VECTOR); 4909f815b29SPeter Xu /* Divider == 1 */ 4919f815b29SPeter Xu apic_write(APIC_TDCR, 0x0000000b); 4929f815b29SPeter Xu 4939f815b29SPeter Xu tsc1 = rdtsc(); 4949f815b29SPeter Xu /* Set "Initial Counter Register", which starts the timer */ 4959f815b29SPeter Xu apic_write(APIC_TMICT, interval); 4969f815b29SPeter Xu while (!lvtt_counter); 4979f815b29SPeter Xu tsc2 = rdtsc(); 4989f815b29SPeter Xu 4999f815b29SPeter Xu /* 5009f815b29SPeter Xu * For LVT Timer clock, SDM vol 3 10.5.4 says it should be 5019f815b29SPeter Xu * derived from processor's bus clock (IIUC which is the same 5029f815b29SPeter Xu * as TSC), however QEMU seems to be using nanosecond. In all 5039f815b29SPeter Xu * cases, the following should satisfy on all modern 5049f815b29SPeter Xu * processors. 5059f815b29SPeter Xu */ 506a299895bSThomas Huth report((lvtt_counter == 1) && (tsc2 - tsc1 >= interval), 507a299895bSThomas Huth "APIC LVT timer one shot"); 5089f815b29SPeter Xu } 5099f815b29SPeter Xu 5109931b88cSRadim Krčmář static atomic_t broadcast_counter; 5119931b88cSRadim Krčmář 5129931b88cSRadim Krčmář static void broadcast_handler(isr_regs_t *regs) 5139931b88cSRadim Krčmář { 5149931b88cSRadim Krčmář atomic_inc(&broadcast_counter); 5159931b88cSRadim Krčmář eoi(); 5169931b88cSRadim Krčmář } 5179931b88cSRadim Krčmář 5189931b88cSRadim Krčmář static bool broadcast_received(unsigned ncpus) 5199931b88cSRadim Krčmář { 5209931b88cSRadim Krčmář unsigned counter; 5219931b88cSRadim Krčmář u64 start = rdtsc(); 5229931b88cSRadim Krčmář 5239931b88cSRadim Krčmář do { 5249931b88cSRadim Krčmář counter = atomic_read(&broadcast_counter); 5259931b88cSRadim Krčmář if (counter >= ncpus) 5269931b88cSRadim Krčmář break; 5279931b88cSRadim Krčmář pause(); 5289931b88cSRadim Krčmář } while (rdtsc() - start < 1000000000); 5299931b88cSRadim Krčmář 5309931b88cSRadim Krčmář atomic_set(&broadcast_counter, 0); 5319931b88cSRadim Krčmář 5329931b88cSRadim Krčmář return counter == ncpus; 5339931b88cSRadim Krčmář } 5349931b88cSRadim Krčmář 5359931b88cSRadim Krčmář static void test_physical_broadcast(void) 5369931b88cSRadim Krčmář { 5379931b88cSRadim Krčmář unsigned ncpus = cpu_count(); 5389931b88cSRadim Krčmář unsigned long cr3 = read_cr3(); 5399931b88cSRadim Krčmář u32 broadcast_address = enable_x2apic() ? 0xffffffff : 0xff; 5409931b88cSRadim Krčmář 5419931b88cSRadim Krčmář handle_irq(BROADCAST_VECTOR, broadcast_handler); 5429931b88cSRadim Krčmář for (int c = 1; c < ncpus; c++) 5439931b88cSRadim Krčmář on_cpu(c, update_cr3, (void *)cr3); 5449931b88cSRadim Krčmář 5459931b88cSRadim Krčmář printf("starting broadcast (%s)\n", enable_x2apic() ? "x2apic" : "xapic"); 5469931b88cSRadim Krčmář apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT | 5479931b88cSRadim Krčmář BROADCAST_VECTOR, broadcast_address); 548a299895bSThomas Huth report(broadcast_received(ncpus), "APIC physical broadcast address"); 5499931b88cSRadim Krčmář 5509931b88cSRadim Krčmář apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT | 5519931b88cSRadim Krčmář BROADCAST_VECTOR | APIC_DEST_ALLINC, 0); 552a299895bSThomas Huth report(broadcast_received(ncpus), "APIC physical broadcast shorthand"); 5539931b88cSRadim Krčmář } 5549931b88cSRadim Krčmář 5550eac5394SEvgeny Yakovlev static void wait_until_tmcct_common(uint32_t initial_count, bool stop_when_half, bool should_wrap_around) 556d9b2b283SWanpeng Li { 557d9b2b283SWanpeng Li uint32_t tmcct = apic_read(APIC_TMCCT); 558d9b2b283SWanpeng Li 559d9b2b283SWanpeng Li if (tmcct) { 560d9b2b283SWanpeng Li while (tmcct > (initial_count / 2)) 561d9b2b283SWanpeng Li tmcct = apic_read(APIC_TMCCT); 562d9b2b283SWanpeng Li 563d9b2b283SWanpeng Li if ( stop_when_half ) 564d9b2b283SWanpeng Li return; 565d9b2b283SWanpeng Li 566d9b2b283SWanpeng Li /* Wait until the counter reach 0 or wrap-around */ 567d9b2b283SWanpeng Li while ( tmcct <= (initial_count / 2) && tmcct > 0 ) 568d9b2b283SWanpeng Li tmcct = apic_read(APIC_TMCCT); 5690eac5394SEvgeny Yakovlev 5700eac5394SEvgeny Yakovlev /* Wait specifically for wrap around to skip 0 TMCCR if we were asked to */ 5710eac5394SEvgeny Yakovlev while (should_wrap_around && !tmcct) 5720eac5394SEvgeny Yakovlev tmcct = apic_read(APIC_TMCCT); 573d9b2b283SWanpeng Li } 574d9b2b283SWanpeng Li } 575d9b2b283SWanpeng Li 5760eac5394SEvgeny Yakovlev static void wait_until_tmcct_is_zero(uint32_t initial_count, bool stop_when_half) 5770eac5394SEvgeny Yakovlev { 5780eac5394SEvgeny Yakovlev return wait_until_tmcct_common(initial_count, stop_when_half, false); 5790eac5394SEvgeny Yakovlev } 5800eac5394SEvgeny Yakovlev 5810eac5394SEvgeny Yakovlev static void wait_until_tmcct_wrap_around(uint32_t initial_count, bool stop_when_half) 5820eac5394SEvgeny Yakovlev { 5830eac5394SEvgeny Yakovlev return wait_until_tmcct_common(initial_count, stop_when_half, true); 5840eac5394SEvgeny Yakovlev } 5850eac5394SEvgeny Yakovlev 586d9b2b283SWanpeng Li static inline void apic_change_mode(unsigned long new_mode) 587d9b2b283SWanpeng Li { 588d9b2b283SWanpeng Li uint32_t lvtt; 589d9b2b283SWanpeng Li 590d9b2b283SWanpeng Li lvtt = apic_read(APIC_LVTT); 591d9b2b283SWanpeng Li apic_write(APIC_LVTT, (lvtt & ~APIC_LVT_TIMER_MASK) | new_mode); 592d9b2b283SWanpeng Li } 593d9b2b283SWanpeng Li 5947db17e21SThomas Huth static void test_apic_change_mode(void) 595d9b2b283SWanpeng Li { 596d9b2b283SWanpeng Li uint32_t tmict = 0x999999; 597d9b2b283SWanpeng Li 598d9b2b283SWanpeng Li printf("starting apic change mode\n"); 599d9b2b283SWanpeng Li 600d9b2b283SWanpeng Li apic_write(APIC_TMICT, tmict); 601d9b2b283SWanpeng Li 602d9b2b283SWanpeng Li apic_change_mode(APIC_LVT_TIMER_PERIODIC); 603d9b2b283SWanpeng Li 604a299895bSThomas Huth report(apic_read(APIC_TMICT) == tmict, "TMICT value reset"); 605d9b2b283SWanpeng Li 606d9b2b283SWanpeng Li /* Testing one-shot */ 607d9b2b283SWanpeng Li apic_change_mode(APIC_LVT_TIMER_ONESHOT); 608d9b2b283SWanpeng Li apic_write(APIC_TMICT, tmict); 609a299895bSThomas Huth report(apic_read(APIC_TMCCT), "TMCCT should have a non-zero value"); 610d9b2b283SWanpeng Li 611d9b2b283SWanpeng Li wait_until_tmcct_is_zero(tmict, false); 612a299895bSThomas Huth report(!apic_read(APIC_TMCCT), "TMCCT should have reached 0"); 613d9b2b283SWanpeng Li 614d9b2b283SWanpeng Li /* 615d9b2b283SWanpeng Li * Write TMICT before changing mode from one-shot to periodic TMCCT should 616d9b2b283SWanpeng Li * be reset to TMICT periodicly 617d9b2b283SWanpeng Li */ 618d9b2b283SWanpeng Li apic_write(APIC_TMICT, tmict); 619d9b2b283SWanpeng Li wait_until_tmcct_is_zero(tmict, true); 620d9b2b283SWanpeng Li apic_change_mode(APIC_LVT_TIMER_PERIODIC); 621a299895bSThomas Huth report(apic_read(APIC_TMCCT), "TMCCT should have a non-zero value"); 622d9b2b283SWanpeng Li 623d9b2b283SWanpeng Li /* 624d9b2b283SWanpeng Li * After the change of mode, the counter should not be reset and continue 625d9b2b283SWanpeng Li * counting down from where it was 626d9b2b283SWanpeng Li */ 627a299895bSThomas Huth report(apic_read(APIC_TMCCT) < (tmict / 2), 628a299895bSThomas Huth "TMCCT should not be reset to TMICT value"); 6290eac5394SEvgeny Yakovlev /* 6300eac5394SEvgeny Yakovlev * Specifically wait for timer wrap around and skip 0. 6310eac5394SEvgeny Yakovlev * Under KVM lapic there is a possibility that a small amount of consecutive 6320eac5394SEvgeny Yakovlev * TMCCR reads return 0 while hrtimer is reset in an async callback 6330eac5394SEvgeny Yakovlev */ 6340eac5394SEvgeny Yakovlev wait_until_tmcct_wrap_around(tmict, false); 635a299895bSThomas Huth report(apic_read(APIC_TMCCT) > (tmict / 2), 636a299895bSThomas Huth "TMCCT should be reset to the initial-count"); 637d9b2b283SWanpeng Li 638d9b2b283SWanpeng Li wait_until_tmcct_is_zero(tmict, true); 639d9b2b283SWanpeng Li /* 640d9b2b283SWanpeng Li * Keep the same TMICT and change timer mode to one-shot 641d9b2b283SWanpeng Li * TMCCT should be > 0 and count-down to 0 642d9b2b283SWanpeng Li */ 643d9b2b283SWanpeng Li apic_change_mode(APIC_LVT_TIMER_ONESHOT); 644a299895bSThomas Huth report(apic_read(APIC_TMCCT) < (tmict / 2), 645a299895bSThomas Huth "TMCCT should not be reset to init"); 646d9b2b283SWanpeng Li wait_until_tmcct_is_zero(tmict, false); 647a299895bSThomas Huth report(!apic_read(APIC_TMCCT), "TMCCT should have reach zero"); 648d9b2b283SWanpeng Li 649d9b2b283SWanpeng Li /* now tmcct == 0 and tmict != 0 */ 650d9b2b283SWanpeng Li apic_change_mode(APIC_LVT_TIMER_PERIODIC); 651a299895bSThomas Huth report(!apic_read(APIC_TMCCT), "TMCCT should stay at zero"); 652d9b2b283SWanpeng Li } 653d9b2b283SWanpeng Li 654de8d3fccSWanpeng Li #define KVM_HC_SEND_IPI 10 655de8d3fccSWanpeng Li 656de8d3fccSWanpeng Li static void test_pv_ipi(void) 657de8d3fccSWanpeng Li { 658de8d3fccSWanpeng Li int ret; 659de8d3fccSWanpeng Li unsigned long a0 = 0xFFFFFFFF, a1 = 0, a2 = 0xFFFFFFFF, a3 = 0x0; 660de8d3fccSWanpeng Li 6616dc73196SSean Christopherson if (!test_device_enabled()) 6626dc73196SSean Christopherson return; 6636dc73196SSean Christopherson 664de8d3fccSWanpeng Li asm volatile("vmcall" : "=a"(ret) :"a"(KVM_HC_SEND_IPI), "b"(a0), "c"(a1), "d"(a2), "S"(a3)); 665a299895bSThomas Huth report(!ret, "PV IPIs testing"); 666de8d3fccSWanpeng Li } 667de8d3fccSWanpeng Li 668d8f62a83SSean Christopherson #define APIC_LDR_CLUSTER_FLAG BIT(31) 669d8f62a83SSean Christopherson 670d8f62a83SSean Christopherson static void set_ldr(void *__ldr) 671d8f62a83SSean Christopherson { 672d8f62a83SSean Christopherson u32 ldr = (unsigned long)__ldr; 673d8f62a83SSean Christopherson 674d8f62a83SSean Christopherson if (ldr & APIC_LDR_CLUSTER_FLAG) 675d8f62a83SSean Christopherson apic_write(APIC_DFR, APIC_DFR_CLUSTER); 676d8f62a83SSean Christopherson else 677d8f62a83SSean Christopherson apic_write(APIC_DFR, APIC_DFR_FLAT); 678d8f62a83SSean Christopherson 679d8f62a83SSean Christopherson apic_write(APIC_LDR, ldr << 24); 680d8f62a83SSean Christopherson } 681d8f62a83SSean Christopherson 682d8f62a83SSean Christopherson static int test_fixed_ipi(u32 dest_mode, u8 dest, u8 vector, 683d8f62a83SSean Christopherson int nr_ipis_expected, const char *mode_name) 684d8f62a83SSean Christopherson { 685d8f62a83SSean Christopherson u64 start = rdtsc(); 686d8f62a83SSean Christopherson int got; 687d8f62a83SSean Christopherson 688d8f62a83SSean Christopherson atomic_set(&ipi_count, 0); 689d8f62a83SSean Christopherson 690d8f62a83SSean Christopherson /* 691d8f62a83SSean Christopherson * Wait for vCPU1 to get back into HLT, i.e. into the host so that 692d8f62a83SSean Christopherson * KVM must handle incomplete AVIC IPIs. 693d8f62a83SSean Christopherson */ 694d8f62a83SSean Christopherson do { 695d8f62a83SSean Christopherson pause(); 696d8f62a83SSean Christopherson } while (rdtsc() - start < 1000000); 697d8f62a83SSean Christopherson 698d8f62a83SSean Christopherson start = rdtsc(); 699d8f62a83SSean Christopherson 700d8f62a83SSean Christopherson apic_icr_write(dest_mode | APIC_DM_FIXED | vector, dest); 701d8f62a83SSean Christopherson 702d8f62a83SSean Christopherson do { 703d8f62a83SSean Christopherson pause(); 704d8f62a83SSean Christopherson } while (rdtsc() - start < 1000000000 && 705d8f62a83SSean Christopherson atomic_read(&ipi_count) != nr_ipis_expected); 706d8f62a83SSean Christopherson 707d8f62a83SSean Christopherson /* Only report failures to cut down on the spam. */ 708d8f62a83SSean Christopherson got = atomic_read(&ipi_count); 709d8f62a83SSean Christopherson if (got != nr_ipis_expected) 710d8f62a83SSean Christopherson report_fail("Want %d IPI(s) using %s mode, dest = %x, got %d IPI(s)", 711d8f62a83SSean Christopherson nr_ipis_expected, mode_name, dest, got); 712d8f62a83SSean Christopherson atomic_set(&ipi_count, 0); 713d8f62a83SSean Christopherson 714d8f62a83SSean Christopherson return got == nr_ipis_expected ? 0 : 1; 715d8f62a83SSean Christopherson } 716d8f62a83SSean Christopherson 717d8f62a83SSean Christopherson static int test_logical_ipi_single_target(u8 logical_id, bool cluster, u8 dest, 718d8f62a83SSean Christopherson u8 vector) 719d8f62a83SSean Christopherson { 720d8f62a83SSean Christopherson /* Disallow broadcast, there are at least 2 vCPUs. */ 721d8f62a83SSean Christopherson if (dest == 0xff) 722d8f62a83SSean Christopherson return 0; 723d8f62a83SSean Christopherson 724d8f62a83SSean Christopherson set_ldr((void *)0); 725d8f62a83SSean Christopherson on_cpu(1, set_ldr, 726d8f62a83SSean Christopherson (void *)((u32)logical_id | (cluster ? APIC_LDR_CLUSTER_FLAG : 0))); 727d8f62a83SSean Christopherson return test_fixed_ipi(APIC_DEST_LOGICAL, dest, vector, 1, 728d8f62a83SSean Christopherson cluster ? "logical cluster" : "logical flat"); 729d8f62a83SSean Christopherson } 730d8f62a83SSean Christopherson 731d8f62a83SSean Christopherson static int test_logical_ipi_multi_target(u8 vcpu0_logical_id, u8 vcpu1_logical_id, 732d8f62a83SSean Christopherson bool cluster, u8 dest, u8 vector) 733d8f62a83SSean Christopherson { 734d8f62a83SSean Christopherson /* Allow broadcast unless there are more than 2 vCPUs. */ 735d8f62a83SSean Christopherson if (dest == 0xff && cpu_count() > 2) 736d8f62a83SSean Christopherson return 0; 737d8f62a83SSean Christopherson 738d8f62a83SSean Christopherson set_ldr((void *)((u32)vcpu0_logical_id | (cluster ? APIC_LDR_CLUSTER_FLAG : 0))); 739d8f62a83SSean Christopherson on_cpu(1, set_ldr, 740d8f62a83SSean Christopherson (void *)((u32)vcpu1_logical_id | (cluster ? APIC_LDR_CLUSTER_FLAG : 0))); 741d8f62a83SSean Christopherson return test_fixed_ipi(APIC_DEST_LOGICAL, dest, vector, 2, 742d8f62a83SSean Christopherson cluster ? "logical cluster" : "logical flat"); 743d8f62a83SSean Christopherson } 744d8f62a83SSean Christopherson 745d8f62a83SSean Christopherson static void test_logical_ipi_xapic(void) 746d8f62a83SSean Christopherson { 747d8f62a83SSean Christopherson int c, i, j, k, f; 748d8f62a83SSean Christopherson u8 vector = 0xf1; 749d8f62a83SSean Christopherson 750d8f62a83SSean Christopherson if (cpu_count() < 2) 751d8f62a83SSean Christopherson return; 752d8f62a83SSean Christopherson 753d8f62a83SSean Christopherson /* 754d8f62a83SSean Christopherson * All vCPUs must be in xAPIC mode, i.e. simply resetting this vCPUs 755d8f62a83SSean Christopherson * APIC is not sufficient. 756d8f62a83SSean Christopherson */ 757d8f62a83SSean Christopherson if (is_x2apic_enabled()) 758d8f62a83SSean Christopherson return; 759d8f62a83SSean Christopherson 760d8f62a83SSean Christopherson handle_irq(vector, handle_ipi); 761d8f62a83SSean Christopherson 762d8f62a83SSean Christopherson /* Flat mode. 8 bits for logical IDs (one per bit). */ 763d8f62a83SSean Christopherson f = 0; 764d8f62a83SSean Christopherson for (i = 0; i < 8; i++) { 765d8f62a83SSean Christopherson /* 766d8f62a83SSean Christopherson * Test all possible destination values. Non-existent targets 767d8f62a83SSean Christopherson * should be ignored. vCPU is always targeted, i.e. should get 768d8f62a83SSean Christopherson * an IPI. 769d8f62a83SSean Christopherson */ 770d8f62a83SSean Christopherson for (k = 0; k < 0xff; k++) { 771d8f62a83SSean Christopherson /* 772d8f62a83SSean Christopherson * Skip values that overlap the actual target the 773d8f62a83SSean Christopherson * resulting combination will be covered by other 774d8f62a83SSean Christopherson * numbers in the sequence. 775d8f62a83SSean Christopherson */ 776d8f62a83SSean Christopherson if (BIT(i) & k) 777d8f62a83SSean Christopherson continue; 778d8f62a83SSean Christopherson 779d8f62a83SSean Christopherson f += test_logical_ipi_single_target(BIT(i), false, 780d8f62a83SSean Christopherson BIT(i) | k, vector); 781d8f62a83SSean Christopherson } 782d8f62a83SSean Christopherson } 783d8f62a83SSean Christopherson report(!f, "IPI to single target using logical flat mode"); 784d8f62a83SSean Christopherson 785d8f62a83SSean Christopherson /* Cluster mode. 4 bits for the cluster, 4 bits for logical IDs. */ 786d8f62a83SSean Christopherson f = 0; 787d8f62a83SSean Christopherson for (c = 0; c < 0xf; c++) { 788d8f62a83SSean Christopherson for (i = 0; i < 4; i++) { 789d8f62a83SSean Christopherson /* Same as above, just fewer bits... */ 790d8f62a83SSean Christopherson for (k = 0; k < 0x10; k++) { 791d8f62a83SSean Christopherson if (BIT(i) & k) 792d8f62a83SSean Christopherson continue; 793d8f62a83SSean Christopherson 794d8f62a83SSean Christopherson test_logical_ipi_single_target(c << 4 | BIT(i), true, 795d8f62a83SSean Christopherson c << 4 | BIT(i) | k, vector); 796d8f62a83SSean Christopherson } 797d8f62a83SSean Christopherson } 798d8f62a83SSean Christopherson } 799d8f62a83SSean Christopherson report(!f, "IPI to single target using logical cluster mode"); 800d8f62a83SSean Christopherson 801d8f62a83SSean Christopherson /* And now do it all over again targeting both vCPU0 and vCPU1. */ 802d8f62a83SSean Christopherson f = 0; 803d8f62a83SSean Christopherson for (i = 0; i < 8 && !f; i++) { 804d8f62a83SSean Christopherson for (j = 0; j < 8 && !f; j++) { 805d8f62a83SSean Christopherson if (i == j) 806d8f62a83SSean Christopherson continue; 807d8f62a83SSean Christopherson 808d8f62a83SSean Christopherson for (k = 0; k < 0x100 && !f; k++) { 809d8f62a83SSean Christopherson if ((BIT(i) | BIT(j)) & k) 810d8f62a83SSean Christopherson continue; 811d8f62a83SSean Christopherson 812d8f62a83SSean Christopherson f += test_logical_ipi_multi_target(BIT(i), BIT(j), false, 813d8f62a83SSean Christopherson BIT(i) | BIT(j) | k, vector); 814d8f62a83SSean Christopherson if (f) 815d8f62a83SSean Christopherson break; 816d8f62a83SSean Christopherson f += test_logical_ipi_multi_target(BIT(i) | BIT(j), 817d8f62a83SSean Christopherson BIT(i) | BIT(j), false, 818d8f62a83SSean Christopherson BIT(i) | BIT(j) | k, vector); 819d8f62a83SSean Christopherson } 820d8f62a83SSean Christopherson } 821d8f62a83SSean Christopherson } 822d8f62a83SSean Christopherson report(!f, "IPI to multiple targets using logical flat mode"); 823d8f62a83SSean Christopherson 824d8f62a83SSean Christopherson f = 0; 825d8f62a83SSean Christopherson for (c = 0; c < 0xf && !f; c++) { 826d8f62a83SSean Christopherson for (i = 0; i < 4 && !f; i++) { 827d8f62a83SSean Christopherson for (j = 0; j < 4 && !f; j++) { 828d8f62a83SSean Christopherson if (i == j) 829d8f62a83SSean Christopherson continue; 830d8f62a83SSean Christopherson 831d8f62a83SSean Christopherson for (k = 0; k < 0x10 && !f; k++) { 832d8f62a83SSean Christopherson if ((BIT(i) | BIT(j)) & k) 833d8f62a83SSean Christopherson continue; 834d8f62a83SSean Christopherson 835d8f62a83SSean Christopherson f += test_logical_ipi_multi_target(c << 4 | BIT(i), 836d8f62a83SSean Christopherson c << 4 | BIT(j), true, 837d8f62a83SSean Christopherson c << 4 | BIT(i) | BIT(j) | k, vector); 838d8f62a83SSean Christopherson if (f) 839d8f62a83SSean Christopherson break; 840d8f62a83SSean Christopherson f += test_logical_ipi_multi_target(c << 4 | BIT(i) | BIT(j), 841d8f62a83SSean Christopherson c << 4 | BIT(i) | BIT(j), true, 842d8f62a83SSean Christopherson c << 4 | BIT(i) | BIT(j) | k, vector); 843d8f62a83SSean Christopherson } 844d8f62a83SSean Christopherson } 845d8f62a83SSean Christopherson } 846d8f62a83SSean Christopherson } 847d8f62a83SSean Christopherson report(!f, "IPI to multiple targets using logical cluster mode"); 848d8f62a83SSean Christopherson } 849d8f62a83SSean Christopherson 850*baf248c5SSean Christopherson static void set_xapic_physical_id(void *apic_id) 851*baf248c5SSean Christopherson { 852*baf248c5SSean Christopherson apic_write(APIC_ID, (unsigned long)apic_id << 24); 853*baf248c5SSean Christopherson } 854*baf248c5SSean Christopherson 855*baf248c5SSean Christopherson static void handle_aliased_ipi(isr_regs_t *regs) 856*baf248c5SSean Christopherson { 857*baf248c5SSean Christopherson u32 apic_id = apic_read(APIC_ID) >> 24; 858*baf248c5SSean Christopherson 859*baf248c5SSean Christopherson if (apic_id == 0xff) 860*baf248c5SSean Christopherson apic_id = smp_id(); 861*baf248c5SSean Christopherson else 862*baf248c5SSean Christopherson apic_id++; 863*baf248c5SSean Christopherson apic_write(APIC_ID, (unsigned long)apic_id << 24); 864*baf248c5SSean Christopherson 865*baf248c5SSean Christopherson /* 866*baf248c5SSean Christopherson * Handle the IPI after updating the APIC ID, as the IPI count acts as 867*baf248c5SSean Christopherson * synchronization barrier before vCPU0 sends the next IPI. 868*baf248c5SSean Christopherson */ 869*baf248c5SSean Christopherson handle_ipi(regs); 870*baf248c5SSean Christopherson } 871*baf248c5SSean Christopherson 872*baf248c5SSean Christopherson static void test_aliased_xapic_physical_ipi(void) 873*baf248c5SSean Christopherson { 874*baf248c5SSean Christopherson u8 vector = 0xf1; 875*baf248c5SSean Christopherson int i, f; 876*baf248c5SSean Christopherson 877*baf248c5SSean Christopherson if (cpu_count() < 2) 878*baf248c5SSean Christopherson return; 879*baf248c5SSean Christopherson 880*baf248c5SSean Christopherson /* 881*baf248c5SSean Christopherson * All vCPUs must be in xAPIC mode, i.e. simply resetting this vCPUs 882*baf248c5SSean Christopherson * APIC is not sufficient. 883*baf248c5SSean Christopherson */ 884*baf248c5SSean Christopherson if (is_x2apic_enabled()) 885*baf248c5SSean Christopherson return; 886*baf248c5SSean Christopherson 887*baf248c5SSean Christopherson /* 888*baf248c5SSean Christopherson * By default, KVM doesn't follow the x86 APIC architecture for aliased 889*baf248c5SSean Christopherson * APIC IDs if userspace has enabled KVM_X2APIC_API_USE_32BIT_IDS. 890*baf248c5SSean Christopherson * If x2APIC is supported, assume the userspace VMM has enabled 32-bit 891*baf248c5SSean Christopherson * IDs and thus activated KVM's quirk. Delete this code to run the 892*baf248c5SSean Christopherson * aliasing test on x2APIC CPUs, e.g. to run it on bare metal. 893*baf248c5SSean Christopherson */ 894*baf248c5SSean Christopherson if (this_cpu_has(X86_FEATURE_X2APIC)) 895*baf248c5SSean Christopherson return; 896*baf248c5SSean Christopherson 897*baf248c5SSean Christopherson handle_irq(vector, handle_aliased_ipi); 898*baf248c5SSean Christopherson 899*baf248c5SSean Christopherson /* 900*baf248c5SSean Christopherson * Set both vCPU0 and vCPU1's APIC IDs to 0, then start the chain 901*baf248c5SSean Christopherson * reaction of IPIs from APIC ID 0..255. Each vCPU will increment its 902*baf248c5SSean Christopherson * APIC ID in the handler, and then "reset" to its original ID (using 903*baf248c5SSean Christopherson * smp_id()) after the last IPI. Using on_cpu() to set vCPU1's ID 904*baf248c5SSean Christopherson * after this point won't work due to on_cpu() using physical mode. 905*baf248c5SSean Christopherson */ 906*baf248c5SSean Christopherson on_cpu(1, set_xapic_physical_id, (void *)0ul); 907*baf248c5SSean Christopherson set_xapic_physical_id((void *)0ul); 908*baf248c5SSean Christopherson 909*baf248c5SSean Christopherson f = 0; 910*baf248c5SSean Christopherson for (i = 0; i < 0x100; i++) 911*baf248c5SSean Christopherson f += test_fixed_ipi(APIC_DEST_PHYSICAL, i, vector, 2, "physical"); 912*baf248c5SSean Christopherson 913*baf248c5SSean Christopherson report(!f, "IPI to aliased xAPIC physical IDs"); 914*baf248c5SSean Christopherson } 915*baf248c5SSean Christopherson 9166dc73196SSean Christopherson typedef void (*apic_test_fn)(void); 9176dc73196SSean Christopherson 9187db17e21SThomas Huth int main(void) 9197d36db35SAvi Kivity { 9206dc73196SSean Christopherson bool is_x2apic = is_x2apic_enabled(); 9216dc73196SSean Christopherson u32 spiv = apic_read(APIC_SPIV); 9226dc73196SSean Christopherson int i; 9236dc73196SSean Christopherson 9246dc73196SSean Christopherson const apic_test_fn tests[] = { 9256dc73196SSean Christopherson test_lapic_existence, 9266dc73196SSean Christopherson 9276dc73196SSean Christopherson test_apic_disable, 9286dc73196SSean Christopherson test_enable_x2apic, 9296dc73196SSean Christopherson 9306dc73196SSean Christopherson test_self_ipi_xapic, 9316dc73196SSean Christopherson test_self_ipi_x2apic, 9326dc73196SSean Christopherson test_physical_broadcast, 933d8f62a83SSean Christopherson test_logical_ipi_xapic, 9346dc73196SSean Christopherson 9356dc73196SSean Christopherson test_pv_ipi, 9366dc73196SSean Christopherson 9376dc73196SSean Christopherson test_sti_nmi, 9386dc73196SSean Christopherson test_multiple_nmi, 9396dc73196SSean Christopherson test_pending_nmi, 9406dc73196SSean Christopherson 9416dc73196SSean Christopherson test_apic_timer_one_shot, 9426dc73196SSean Christopherson test_apic_change_mode, 9436dc73196SSean Christopherson test_tsc_deadline_timer, 94475ac2899SSean Christopherson 94575ac2899SSean Christopherson /* 94675ac2899SSean Christopherson * KVM may disable APICv if the APIC ID and/or APIC_BASE is 94775ac2899SSean Christopherson * modified, keep these tests at the end so that the test as a 94875ac2899SSean Christopherson * whole provides coverage for APICv (when it's enabled). 94975ac2899SSean Christopherson */ 95075ac2899SSean Christopherson test_apic_id, 95175ac2899SSean Christopherson test_apicbase, 952*baf248c5SSean Christopherson test_aliased_xapic_physical_ipi, 9536dc73196SSean Christopherson }; 9546dc73196SSean Christopherson 9551eb8551aSSean Christopherson assert_msg(is_apic_hw_enabled() && is_apic_sw_enabled(), 9561eb8551aSSean Christopherson "APIC should be fully enabled by startup code."); 9571eb8551aSSean Christopherson 9587d36db35SAvi Kivity setup_vm(); 9597d36db35SAvi Kivity 9607d36db35SAvi Kivity mask_pic_interrupts(); 961fbfa64f2SSean Christopherson irq_enable(); 9627d36db35SAvi Kivity 9636dc73196SSean Christopherson for (i = 0; i < ARRAY_SIZE(tests); i++) { 9646dc73196SSean Christopherson tests[i](); 9657d36db35SAvi Kivity 9666dc73196SSean Christopherson if (is_x2apic) 9676dc73196SSean Christopherson enable_x2apic(); 9686dc73196SSean Christopherson else 9696dc73196SSean Christopherson reset_apic(); 9707d36db35SAvi Kivity 9716dc73196SSean Christopherson apic_write(APIC_SPIV, spiv); 9726dc73196SSean Christopherson } 973d423ca36SLiu, Jinsong 974f3cdd159SJan Kiszka return report_summary(); 9757d36db35SAvi Kivity } 976