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" 97d36db35SAvi Kivity 10e38858bcSJim Mattson #define MAX_TPR 0xf 11e38858bcSJim Mattson 127d36db35SAvi Kivity static void test_lapic_existence(void) 137d36db35SAvi Kivity { 143ee24f29SNadav Amit u8 version; 157d36db35SAvi Kivity 163ee24f29SNadav Amit version = (u8)apic_read(APIC_LVR); 173ee24f29SNadav Amit printf("apic version: %x\n", version); 183ee24f29SNadav Amit report("apic existence", version >= 0x10 && version <= 0x15); 197d36db35SAvi Kivity } 207d36db35SAvi Kivity 21d423ca36SLiu, Jinsong #define TSC_DEADLINE_TIMER_VECTOR 0xef 229931b88cSRadim Krčmář #define BROADCAST_VECTOR 0xcf 23d423ca36SLiu, Jinsong 24d423ca36SLiu, Jinsong static int tdt_count; 25d423ca36SLiu, Jinsong 26d423ca36SLiu, Jinsong static void tsc_deadline_timer_isr(isr_regs_t *regs) 27d423ca36SLiu, Jinsong { 28d423ca36SLiu, Jinsong ++tdt_count; 290b04ed06SPeter Xu eoi(); 30d423ca36SLiu, Jinsong } 31d423ca36SLiu, Jinsong 3232b9603cSRadim Krčmář static void __test_tsc_deadline_timer(void) 33d423ca36SLiu, Jinsong { 34d423ca36SLiu, Jinsong handle_irq(TSC_DEADLINE_TIMER_VECTOR, tsc_deadline_timer_isr); 35d423ca36SLiu, Jinsong irq_enable(); 36d423ca36SLiu, Jinsong 37d423ca36SLiu, Jinsong wrmsr(MSR_IA32_TSCDEADLINE, rdmsr(MSR_IA32_TSC)); 38d423ca36SLiu, Jinsong asm volatile ("nop"); 39d423ca36SLiu, Jinsong report("tsc deadline timer", tdt_count == 1); 40f8833144SNadav Amit report("tsc deadline timer clearing", rdmsr(MSR_IA32_TSCDEADLINE) == 0); 41d423ca36SLiu, Jinsong } 42d423ca36SLiu, Jinsong 43d423ca36SLiu, Jinsong static int enable_tsc_deadline_timer(void) 44d423ca36SLiu, Jinsong { 45d423ca36SLiu, Jinsong uint32_t lvtt; 46d423ca36SLiu, Jinsong 47*badc98caSKrish Sadhukhan if (this_cpu_has(X86_FEATURE_TSC_DEADLINE_TIMER)) { 489111ccabSRadim Krčmář lvtt = APIC_LVT_TIMER_TSCDEADLINE | TSC_DEADLINE_TIMER_VECTOR; 49d423ca36SLiu, Jinsong apic_write(APIC_LVTT, lvtt); 50d423ca36SLiu, Jinsong return 1; 51d423ca36SLiu, Jinsong } else { 52d423ca36SLiu, Jinsong return 0; 53d423ca36SLiu, Jinsong } 54d423ca36SLiu, Jinsong } 55d423ca36SLiu, Jinsong 56d423ca36SLiu, Jinsong static void test_tsc_deadline_timer(void) 57d423ca36SLiu, Jinsong { 58d423ca36SLiu, Jinsong if(enable_tsc_deadline_timer()) { 5932b9603cSRadim Krčmář __test_tsc_deadline_timer(); 60d423ca36SLiu, Jinsong } else { 6132b9603cSRadim Krčmář report_skip("tsc deadline timer not detected"); 62d423ca36SLiu, Jinsong } 63d423ca36SLiu, Jinsong } 64d423ca36SLiu, Jinsong 6522c7d929SJan Kiszka static void do_write_apicbase(void *data) 6622c7d929SJan Kiszka { 6722c7d929SJan Kiszka wrmsr(MSR_IA32_APICBASE, *(u64 *)data); 6803f37ef2SPaolo Bonzini } 697d36db35SAvi Kivity 7049f5ad9eSPaolo Bonzini static bool test_write_apicbase_exception(u64 data) 7149f5ad9eSPaolo Bonzini { 7249f5ad9eSPaolo Bonzini return test_for_exception(GP_VECTOR, do_write_apicbase, &data); 7349f5ad9eSPaolo Bonzini } 7449f5ad9eSPaolo Bonzini 75db4898e8SThomas Huth static void test_enable_x2apic(void) 767d36db35SAvi Kivity { 772092999cSSean Christopherson u64 orig_apicbase = rdmsr(MSR_IA32_APICBASE); 782092999cSSean Christopherson u64 apicbase; 792092999cSSean Christopherson 807d36db35SAvi Kivity if (enable_x2apic()) { 817d36db35SAvi Kivity printf("x2apic enabled\n"); 8222c7d929SJan Kiszka 832092999cSSean Christopherson apicbase = orig_apicbase & ~(APIC_EN | APIC_EXTD); 8422c7d929SJan Kiszka report("x2apic enabled to invalid state", 852092999cSSean Christopherson test_write_apicbase_exception(apicbase | APIC_EXTD)); 8622c7d929SJan Kiszka report("x2apic enabled to apic enabled", 872092999cSSean Christopherson test_write_apicbase_exception(apicbase | APIC_EN)); 8822c7d929SJan Kiszka 8949f5ad9eSPaolo Bonzini report("x2apic enabled to disabled state", 902092999cSSean Christopherson !test_write_apicbase_exception(apicbase | 0)); 9122c7d929SJan Kiszka report("disabled to invalid state", 922092999cSSean Christopherson test_write_apicbase_exception(apicbase | APIC_EXTD)); 9322c7d929SJan Kiszka report("disabled to x2apic enabled", 942092999cSSean Christopherson test_write_apicbase_exception(apicbase | APIC_EN | APIC_EXTD)); 9522c7d929SJan Kiszka 962092999cSSean Christopherson report("apic disabled to apic enabled", 972092999cSSean Christopherson !test_write_apicbase_exception(apicbase | APIC_EN)); 9822c7d929SJan Kiszka report("apic enabled to invalid state", 992092999cSSean Christopherson test_write_apicbase_exception(apicbase | APIC_EXTD)); 10022c7d929SJan Kiszka 1012092999cSSean Christopherson if (orig_apicbase & APIC_EXTD) 1022092999cSSean Christopherson enable_x2apic(); 1032092999cSSean Christopherson else 1042092999cSSean Christopherson reset_apic(); 1052092999cSSean Christopherson 1062092999cSSean Christopherson /* 1072092999cSSean Christopherson * Disabling the APIC resets various APIC registers, restore them to 1082092999cSSean Christopherson * their desired values. 1092092999cSSean Christopherson */ 11022c7d929SJan Kiszka apic_write(APIC_SPIV, 0x1ff); 1117d36db35SAvi Kivity } else { 1127d36db35SAvi Kivity printf("x2apic not detected\n"); 11322c7d929SJan Kiszka 11422c7d929SJan Kiszka report("enable unsupported x2apic", 11549f5ad9eSPaolo Bonzini test_write_apicbase_exception(APIC_EN | APIC_EXTD)); 1167d36db35SAvi Kivity } 1177d36db35SAvi Kivity } 1187d36db35SAvi Kivity 119e38858bcSJim Mattson static void verify_disabled_apic_mmio(void) 120e38858bcSJim Mattson { 121e38858bcSJim Mattson volatile u32 *lvr = (volatile u32 *)(APIC_DEFAULT_PHYS_BASE + APIC_LVR); 122e38858bcSJim Mattson volatile u32 *tpr = (volatile u32 *)(APIC_DEFAULT_PHYS_BASE + APIC_TASKPRI); 123e38858bcSJim Mattson u32 cr8 = read_cr8(); 124e38858bcSJim Mattson 125e38858bcSJim Mattson memset((void *)APIC_DEFAULT_PHYS_BASE, 0xff, PAGE_SIZE); 126e38858bcSJim Mattson report("*0xfee00030: %x", *lvr == ~0, *lvr); 127e38858bcSJim Mattson report("CR8: %lx", read_cr8() == cr8, read_cr8()); 128e38858bcSJim Mattson write_cr8(cr8 ^ MAX_TPR); 129e38858bcSJim Mattson report("CR8: %lx", read_cr8() == (cr8 ^ MAX_TPR), read_cr8()); 130e38858bcSJim Mattson report("*0xfee00080: %x", *tpr == ~0, *tpr); 131e38858bcSJim Mattson write_cr8(cr8); 132e38858bcSJim Mattson } 133e38858bcSJim Mattson 134c3ccca3fSJim Mattson static void test_apic_disable(void) 135c3ccca3fSJim 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); 138c3ccca3fSJim Mattson u64 orig_apicbase = rdmsr(MSR_IA32_APICBASE); 139e38858bcSJim Mattson u32 apic_version = apic_read(APIC_LVR); 140e38858bcSJim Mattson u32 cr8 = read_cr8(); 141c3ccca3fSJim Mattson 142c3ccca3fSJim Mattson report_prefix_push("apic_disable"); 143e38858bcSJim Mattson assert_msg(orig_apicbase & APIC_EN, "APIC not enabled."); 144c3ccca3fSJim Mattson 145e38858bcSJim Mattson disable_apic(); 146c3ccca3fSJim Mattson report("Local apic disabled", !(rdmsr(MSR_IA32_APICBASE) & APIC_EN)); 147*badc98caSKrish Sadhukhan report("CPUID.1H:EDX.APIC[bit 9] is clear", !this_cpu_has(X86_FEATURE_APIC)); 148e38858bcSJim Mattson verify_disabled_apic_mmio(); 149c3ccca3fSJim Mattson 150e38858bcSJim Mattson reset_apic(); 151e38858bcSJim Mattson report("Local apic enabled in xAPIC mode", 152e38858bcSJim Mattson (rdmsr(MSR_IA32_APICBASE) & (APIC_EN | APIC_EXTD)) == APIC_EN); 153*badc98caSKrish Sadhukhan report("CPUID.1H:EDX.APIC[bit 9] is set", this_cpu_has(X86_FEATURE_APIC)); 154e38858bcSJim Mattson report("*0xfee00030: %x", *lvr == apic_version, *lvr); 155e38858bcSJim Mattson report("*0xfee00080: %x", *tpr == cr8, *tpr); 156e38858bcSJim Mattson write_cr8(cr8 ^ MAX_TPR); 157e38858bcSJim Mattson report("*0xfee00080: %x", *tpr == (cr8 ^ MAX_TPR) << 4, *tpr); 158e38858bcSJim Mattson write_cr8(cr8); 159c3ccca3fSJim Mattson 160e38858bcSJim Mattson if (enable_x2apic()) { 161e38858bcSJim Mattson apic_write(APIC_SPIV, 0x1ff); 162e38858bcSJim Mattson report("Local apic enabled in x2APIC mode", 163e38858bcSJim Mattson (rdmsr(MSR_IA32_APICBASE) & (APIC_EN | APIC_EXTD)) == 164e38858bcSJim Mattson (APIC_EN | APIC_EXTD)); 165*badc98caSKrish Sadhukhan report("CPUID.1H:EDX.APIC[bit 9] is set", this_cpu_has(X86_FEATURE_APIC)); 166e38858bcSJim Mattson verify_disabled_apic_mmio(); 167e38858bcSJim Mattson if (!(orig_apicbase & APIC_EXTD)) 168e38858bcSJim Mattson reset_apic(); 169e38858bcSJim Mattson } 170c3ccca3fSJim Mattson report_prefix_pop(); 171c3ccca3fSJim Mattson } 172c3ccca3fSJim Mattson 173615a8838SNadav Amit #define ALTERNATE_APIC_BASE 0xfed40000 1749b6bdb3fSJan Kiszka 1759b6bdb3fSJan Kiszka static void test_apicbase(void) 1769b6bdb3fSJan Kiszka { 1779b6bdb3fSJan Kiszka u64 orig_apicbase = rdmsr(MSR_IA32_APICBASE); 1789b6bdb3fSJan Kiszka u32 lvr = apic_read(APIC_LVR); 1799b6bdb3fSJan Kiszka u64 value; 1809b6bdb3fSJan Kiszka 1819b6bdb3fSJan Kiszka wrmsr(MSR_IA32_APICBASE, orig_apicbase & ~(APIC_EN | APIC_EXTD)); 1829b6bdb3fSJan Kiszka wrmsr(MSR_IA32_APICBASE, ALTERNATE_APIC_BASE | APIC_BSP | APIC_EN); 1839b6bdb3fSJan Kiszka 1845bba1769SAndrew Jones report_prefix_push("apicbase"); 1855bba1769SAndrew Jones 1869b6bdb3fSJan Kiszka report("relocate apic", 1879b6bdb3fSJan Kiszka *(volatile u32 *)(ALTERNATE_APIC_BASE + APIC_LVR) == lvr); 1889b6bdb3fSJan Kiszka 189772befb7SEduardo Habkost value = orig_apicbase | (1UL << cpuid_maxphyaddr()); 1905bba1769SAndrew Jones report("reserved physaddr bits", 1919b6bdb3fSJan Kiszka test_for_exception(GP_VECTOR, do_write_apicbase, &value)); 1929b6bdb3fSJan Kiszka 1939b6bdb3fSJan Kiszka value = orig_apicbase | 1; 1945bba1769SAndrew Jones report("reserved low bits", 1959b6bdb3fSJan Kiszka test_for_exception(GP_VECTOR, do_write_apicbase, &value)); 1969b6bdb3fSJan Kiszka 1979b6bdb3fSJan Kiszka wrmsr(MSR_IA32_APICBASE, orig_apicbase); 1989b6bdb3fSJan Kiszka apic_write(APIC_SPIV, 0x1ff); 1995bba1769SAndrew Jones 2005bba1769SAndrew Jones report_prefix_pop(); 2019b6bdb3fSJan Kiszka } 2029b6bdb3fSJan Kiszka 203a222b5e2SRadim Krčmář static void do_write_apic_id(void *id) 204a222b5e2SRadim Krčmář { 205a222b5e2SRadim Krčmář apic_write(APIC_ID, *(u32 *)id); 206a222b5e2SRadim Krčmář } 207a222b5e2SRadim Krčmář 208a222b5e2SRadim Krčmář static void __test_apic_id(void * unused) 209a222b5e2SRadim Krčmář { 210a222b5e2SRadim Krčmář u32 id, newid; 211a222b5e2SRadim Krčmář u8 initial_xapic_id = cpuid(1).b >> 24; 212a222b5e2SRadim Krčmář u32 initial_x2apic_id = cpuid(0xb).d; 213a222b5e2SRadim Krčmář bool x2apic_mode = rdmsr(MSR_IA32_APICBASE) & APIC_EXTD; 214a222b5e2SRadim Krčmář 215a222b5e2SRadim Krčmář if (x2apic_mode) 216a222b5e2SRadim Krčmář reset_apic(); 217a222b5e2SRadim Krčmář 218a222b5e2SRadim Krčmář id = apic_id(); 219a222b5e2SRadim Krčmář report("xapic id matches cpuid", initial_xapic_id == id); 220a222b5e2SRadim Krčmář 221a222b5e2SRadim Krčmář newid = (id + 1) << 24; 222a222b5e2SRadim Krčmář report("writeable xapic id", 223a222b5e2SRadim Krčmář !test_for_exception(GP_VECTOR, do_write_apic_id, &newid) && 224af5f21beSNadav Amit (id == apic_id() || id + 1 == apic_id())); 225a222b5e2SRadim Krčmář 226a222b5e2SRadim Krčmář if (!enable_x2apic()) 227a222b5e2SRadim Krčmář goto out; 228a222b5e2SRadim Krčmář 229a222b5e2SRadim Krčmář report("non-writeable x2apic id", 230a222b5e2SRadim Krčmář test_for_exception(GP_VECTOR, do_write_apic_id, &newid)); 231a222b5e2SRadim Krčmář report("sane x2apic id", initial_xapic_id == (apic_id() & 0xff)); 232a222b5e2SRadim Krčmář 233a222b5e2SRadim Krčmář /* old QEMUs do not set initial x2APIC ID */ 234a222b5e2SRadim Krčmář report("x2apic id matches cpuid", 235a222b5e2SRadim Krčmář initial_xapic_id == (initial_x2apic_id & 0xff) && 236a222b5e2SRadim Krčmář initial_x2apic_id == apic_id()); 237a222b5e2SRadim Krčmář 238a222b5e2SRadim Krčmář out: 239a222b5e2SRadim Krčmář reset_apic(); 240a222b5e2SRadim Krčmář 241a222b5e2SRadim Krčmář report("correct xapic id after reset", initial_xapic_id == apic_id()); 242a222b5e2SRadim Krčmář 243a222b5e2SRadim Krčmář /* old KVMs do not reset xAPIC ID */ 244a222b5e2SRadim Krčmář if (id != apic_id()) 245a222b5e2SRadim Krčmář apic_write(APIC_ID, id << 24); 246a222b5e2SRadim Krčmář 247a222b5e2SRadim Krčmář if (x2apic_mode) 248a222b5e2SRadim Krčmář enable_x2apic(); 249a222b5e2SRadim Krčmář } 250a222b5e2SRadim Krčmář 251a222b5e2SRadim Krčmář static void test_apic_id(void) 252a222b5e2SRadim Krčmář { 253a222b5e2SRadim Krčmář if (cpu_count() < 2) 254a222b5e2SRadim Krčmář return; 255a222b5e2SRadim Krčmář 256a222b5e2SRadim Krčmář on_cpu(1, __test_apic_id, NULL); 257a222b5e2SRadim Krčmář } 258a222b5e2SRadim Krčmář 2597d36db35SAvi Kivity static int ipi_count; 2607d36db35SAvi Kivity 2617d36db35SAvi Kivity static void self_ipi_isr(isr_regs_t *regs) 2627d36db35SAvi Kivity { 2637d36db35SAvi Kivity ++ipi_count; 2647d36db35SAvi Kivity eoi(); 2657d36db35SAvi Kivity } 2667d36db35SAvi Kivity 2677d36db35SAvi Kivity static void test_self_ipi(void) 2687d36db35SAvi Kivity { 2696c0999f4SNadav Amit u64 start = rdtsc(); 2707d36db35SAvi Kivity int vec = 0xf1; 2717d36db35SAvi Kivity 272d51bd17eSGleb Natapov handle_irq(vec, self_ipi_isr); 2737d36db35SAvi Kivity irq_enable(); 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(); 2796c0999f4SNadav Amit } while (rdtsc() - start < 1000000000 && ipi_count == 0); 2806c0999f4SNadav Amit 2817d36db35SAvi Kivity report("self ipi", ipi_count == 1); 2827d36db35SAvi Kivity } 2837d36db35SAvi Kivity 284f2d2b7c7SAvi Kivity volatile int nmi_counter_private, nmi_counter, nmi_hlt_counter, sti_loop_active; 285f2d2b7c7SAvi Kivity 286db4898e8SThomas Huth static void sti_nop(char *p) 287f2d2b7c7SAvi Kivity { 288f2d2b7c7SAvi Kivity asm volatile ( 289f2d2b7c7SAvi Kivity ".globl post_sti \n\t" 290f2d2b7c7SAvi Kivity "sti \n" 291f2d2b7c7SAvi Kivity /* 292f2d2b7c7SAvi Kivity * vmx won't exit on external interrupt if blocked-by-sti, 293f2d2b7c7SAvi Kivity * so give it a reason to exit by accessing an unmapped page. 294f2d2b7c7SAvi Kivity */ 295f2d2b7c7SAvi Kivity "post_sti: testb $0, %0 \n\t" 296f2d2b7c7SAvi Kivity "nop \n\t" 297f2d2b7c7SAvi Kivity "cli" 298f2d2b7c7SAvi Kivity : : "m"(*p) 299f2d2b7c7SAvi Kivity ); 300f2d2b7c7SAvi Kivity nmi_counter = nmi_counter_private; 301f2d2b7c7SAvi Kivity } 302f2d2b7c7SAvi Kivity 303f2d2b7c7SAvi Kivity static void sti_loop(void *ignore) 304f2d2b7c7SAvi Kivity { 305f2d2b7c7SAvi Kivity unsigned k = 0; 306f2d2b7c7SAvi Kivity 307f2d2b7c7SAvi Kivity while (sti_loop_active) { 308f2d2b7c7SAvi Kivity sti_nop((char *)(ulong)((k++ * 4096) % (128 * 1024 * 1024))); 309f2d2b7c7SAvi Kivity } 310f2d2b7c7SAvi Kivity } 311f2d2b7c7SAvi Kivity 312f2d2b7c7SAvi Kivity static void nmi_handler(isr_regs_t *regs) 313f2d2b7c7SAvi Kivity { 314f2d2b7c7SAvi Kivity extern void post_sti(void); 315f2d2b7c7SAvi Kivity ++nmi_counter_private; 316f2d2b7c7SAvi Kivity nmi_hlt_counter += regs->rip == (ulong)post_sti; 317f2d2b7c7SAvi Kivity } 318f2d2b7c7SAvi Kivity 319f2d2b7c7SAvi Kivity static void update_cr3(void *cr3) 320f2d2b7c7SAvi Kivity { 321f2d2b7c7SAvi Kivity write_cr3((ulong)cr3); 322f2d2b7c7SAvi Kivity } 323f2d2b7c7SAvi Kivity 324f2d2b7c7SAvi Kivity static void test_sti_nmi(void) 325f2d2b7c7SAvi Kivity { 326f2d2b7c7SAvi Kivity unsigned old_counter; 327f2d2b7c7SAvi Kivity 328f2d2b7c7SAvi Kivity if (cpu_count() < 2) { 329f2d2b7c7SAvi Kivity return; 330f2d2b7c7SAvi Kivity } 331f2d2b7c7SAvi Kivity 332d51bd17eSGleb Natapov handle_irq(2, nmi_handler); 333f2d2b7c7SAvi Kivity on_cpu(1, update_cr3, (void *)read_cr3()); 334f2d2b7c7SAvi Kivity 335f2d2b7c7SAvi Kivity sti_loop_active = 1; 336f2d2b7c7SAvi Kivity on_cpu_async(1, sti_loop, 0); 337f2d2b7c7SAvi Kivity while (nmi_counter < 30000) { 338f2d2b7c7SAvi Kivity old_counter = nmi_counter; 33918a34cceSNadav Amit apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, id_map[1]); 340f2d2b7c7SAvi Kivity while (nmi_counter == old_counter) { 341f2d2b7c7SAvi Kivity ; 342f2d2b7c7SAvi Kivity } 343f2d2b7c7SAvi Kivity } 344f2d2b7c7SAvi Kivity sti_loop_active = 0; 345f2d2b7c7SAvi Kivity report("nmi-after-sti", nmi_hlt_counter == 0); 346f2d2b7c7SAvi Kivity } 347f2d2b7c7SAvi Kivity 348173e7eacSAvi Kivity static volatile bool nmi_done, nmi_flushed; 349173e7eacSAvi Kivity static volatile int nmi_received; 350173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr1, cpu1_nmi_ctr1; 351173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr2, cpu1_nmi_ctr2; 352173e7eacSAvi Kivity 353173e7eacSAvi Kivity static void multiple_nmi_handler(isr_regs_t *regs) 354173e7eacSAvi Kivity { 355173e7eacSAvi Kivity ++nmi_received; 356173e7eacSAvi Kivity } 357173e7eacSAvi Kivity 358173e7eacSAvi Kivity static void kick_me_nmi(void *blah) 359173e7eacSAvi Kivity { 360173e7eacSAvi Kivity while (!nmi_done) { 361173e7eacSAvi Kivity ++cpu1_nmi_ctr1; 362173e7eacSAvi Kivity while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1 && !nmi_done) { 363173e7eacSAvi Kivity pause(); 364173e7eacSAvi Kivity } 365173e7eacSAvi Kivity if (nmi_done) { 366173e7eacSAvi Kivity return; 367173e7eacSAvi Kivity } 36818a34cceSNadav Amit apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, id_map[0]); 369173e7eacSAvi Kivity /* make sure the NMI has arrived by sending an IPI after it */ 370173e7eacSAvi Kivity apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT 37118a34cceSNadav Amit | 0x44, id_map[0]); 372173e7eacSAvi Kivity ++cpu1_nmi_ctr2; 373173e7eacSAvi Kivity while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2 && !nmi_done) { 374173e7eacSAvi Kivity pause(); 375173e7eacSAvi Kivity } 376173e7eacSAvi Kivity } 377173e7eacSAvi Kivity } 378173e7eacSAvi Kivity 379173e7eacSAvi Kivity static void flush_nmi(isr_regs_t *regs) 380173e7eacSAvi Kivity { 381173e7eacSAvi Kivity nmi_flushed = true; 382173e7eacSAvi Kivity apic_write(APIC_EOI, 0); 383173e7eacSAvi Kivity } 384173e7eacSAvi Kivity 385173e7eacSAvi Kivity static void test_multiple_nmi(void) 386173e7eacSAvi Kivity { 387173e7eacSAvi Kivity int i; 388173e7eacSAvi Kivity bool ok = true; 389173e7eacSAvi Kivity 390173e7eacSAvi Kivity if (cpu_count() < 2) { 391173e7eacSAvi Kivity return; 392173e7eacSAvi Kivity } 393173e7eacSAvi Kivity 394173e7eacSAvi Kivity sti(); 395173e7eacSAvi Kivity handle_irq(2, multiple_nmi_handler); 396173e7eacSAvi Kivity handle_irq(0x44, flush_nmi); 397173e7eacSAvi Kivity on_cpu_async(1, kick_me_nmi, 0); 398173e7eacSAvi Kivity for (i = 0; i < 1000000; ++i) { 399173e7eacSAvi Kivity nmi_flushed = false; 400173e7eacSAvi Kivity nmi_received = 0; 401173e7eacSAvi Kivity ++cpu0_nmi_ctr1; 402173e7eacSAvi Kivity while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1) { 403173e7eacSAvi Kivity pause(); 404173e7eacSAvi Kivity } 40518a34cceSNadav Amit apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, id_map[0]); 406173e7eacSAvi Kivity while (!nmi_flushed) { 407173e7eacSAvi Kivity pause(); 408173e7eacSAvi Kivity } 409173e7eacSAvi Kivity if (nmi_received != 2) { 410173e7eacSAvi Kivity ok = false; 411173e7eacSAvi Kivity break; 412173e7eacSAvi Kivity } 413173e7eacSAvi Kivity ++cpu0_nmi_ctr2; 414173e7eacSAvi Kivity while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2) { 415173e7eacSAvi Kivity pause(); 416173e7eacSAvi Kivity } 417173e7eacSAvi Kivity } 418173e7eacSAvi Kivity nmi_done = true; 419173e7eacSAvi Kivity report("multiple nmi", ok); 420173e7eacSAvi Kivity } 421173e7eacSAvi Kivity 4229f23b246SSean Christopherson static void pending_nmi_handler(isr_regs_t *regs) 4239f23b246SSean Christopherson { 4249f23b246SSean Christopherson int i; 4259f23b246SSean Christopherson 4269f23b246SSean Christopherson if (++nmi_received == 1) { 4279f23b246SSean Christopherson for (i = 0; i < 10; ++i) 4289f23b246SSean Christopherson apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI, 0); 4299f23b246SSean Christopherson } 4309f23b246SSean Christopherson } 4319f23b246SSean Christopherson 4329f23b246SSean Christopherson static void test_pending_nmi(void) 4339f23b246SSean Christopherson { 4349f23b246SSean Christopherson int i; 4359f23b246SSean Christopherson 4369f23b246SSean Christopherson handle_irq(2, pending_nmi_handler); 4379f23b246SSean Christopherson for (i = 0; i < 100000; ++i) { 4389f23b246SSean Christopherson nmi_received = 0; 4399f23b246SSean Christopherson 4409f23b246SSean Christopherson apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI, 0); 4419f23b246SSean Christopherson while (nmi_received < 2) 4429f23b246SSean Christopherson pause(); 4439f23b246SSean Christopherson 4449f23b246SSean Christopherson if (nmi_received != 2) 4459f23b246SSean Christopherson break; 4469f23b246SSean Christopherson } 4479f23b246SSean Christopherson report("pending nmi", nmi_received == 2); 4489f23b246SSean Christopherson } 4499f23b246SSean Christopherson 4509f815b29SPeter Xu static volatile int lvtt_counter = 0; 4519f815b29SPeter Xu 4529f815b29SPeter Xu static void lvtt_handler(isr_regs_t *regs) 4539f815b29SPeter Xu { 4549f815b29SPeter Xu lvtt_counter++; 4559f815b29SPeter Xu eoi(); 4569f815b29SPeter Xu } 4579f815b29SPeter Xu 4589f815b29SPeter Xu static void test_apic_timer_one_shot(void) 4599f815b29SPeter Xu { 4609f815b29SPeter Xu uint64_t tsc1, tsc2; 4619f815b29SPeter Xu static const uint32_t interval = 0x10000; 4629f815b29SPeter Xu 4639f815b29SPeter Xu #define APIC_LVT_TIMER_VECTOR (0xee) 4649f815b29SPeter Xu 4659f815b29SPeter Xu handle_irq(APIC_LVT_TIMER_VECTOR, lvtt_handler); 4669f815b29SPeter Xu irq_enable(); 4679f815b29SPeter Xu 4689f815b29SPeter Xu /* One shot mode */ 4699111ccabSRadim Krčmář apic_write(APIC_LVTT, APIC_LVT_TIMER_ONESHOT | 4709f815b29SPeter Xu APIC_LVT_TIMER_VECTOR); 4719f815b29SPeter Xu /* Divider == 1 */ 4729f815b29SPeter Xu apic_write(APIC_TDCR, 0x0000000b); 4739f815b29SPeter Xu 4749f815b29SPeter Xu tsc1 = rdtsc(); 4759f815b29SPeter Xu /* Set "Initial Counter Register", which starts the timer */ 4769f815b29SPeter Xu apic_write(APIC_TMICT, interval); 4779f815b29SPeter Xu while (!lvtt_counter); 4789f815b29SPeter Xu tsc2 = rdtsc(); 4799f815b29SPeter Xu 4809f815b29SPeter Xu /* 4819f815b29SPeter Xu * For LVT Timer clock, SDM vol 3 10.5.4 says it should be 4829f815b29SPeter Xu * derived from processor's bus clock (IIUC which is the same 4839f815b29SPeter Xu * as TSC), however QEMU seems to be using nanosecond. In all 4849f815b29SPeter Xu * cases, the following should satisfy on all modern 4859f815b29SPeter Xu * processors. 4869f815b29SPeter Xu */ 4879f815b29SPeter Xu report("APIC LVT timer one shot", (lvtt_counter == 1) && 4889f815b29SPeter Xu (tsc2 - tsc1 >= interval)); 4899f815b29SPeter Xu } 4909f815b29SPeter Xu 4919931b88cSRadim Krčmář static atomic_t broadcast_counter; 4929931b88cSRadim Krčmář 4939931b88cSRadim Krčmář static void broadcast_handler(isr_regs_t *regs) 4949931b88cSRadim Krčmář { 4959931b88cSRadim Krčmář atomic_inc(&broadcast_counter); 4969931b88cSRadim Krčmář eoi(); 4979931b88cSRadim Krčmář } 4989931b88cSRadim Krčmář 4999931b88cSRadim Krčmář static bool broadcast_received(unsigned ncpus) 5009931b88cSRadim Krčmář { 5019931b88cSRadim Krčmář unsigned counter; 5029931b88cSRadim Krčmář u64 start = rdtsc(); 5039931b88cSRadim Krčmář 5049931b88cSRadim Krčmář do { 5059931b88cSRadim Krčmář counter = atomic_read(&broadcast_counter); 5069931b88cSRadim Krčmář if (counter >= ncpus) 5079931b88cSRadim Krčmář break; 5089931b88cSRadim Krčmář pause(); 5099931b88cSRadim Krčmář } while (rdtsc() - start < 1000000000); 5109931b88cSRadim Krčmář 5119931b88cSRadim Krčmář atomic_set(&broadcast_counter, 0); 5129931b88cSRadim Krčmář 5139931b88cSRadim Krčmář return counter == ncpus; 5149931b88cSRadim Krčmář } 5159931b88cSRadim Krčmář 5169931b88cSRadim Krčmář static void test_physical_broadcast(void) 5179931b88cSRadim Krčmář { 5189931b88cSRadim Krčmář unsigned ncpus = cpu_count(); 5199931b88cSRadim Krčmář unsigned long cr3 = read_cr3(); 5209931b88cSRadim Krčmář u32 broadcast_address = enable_x2apic() ? 0xffffffff : 0xff; 5219931b88cSRadim Krčmář 5229931b88cSRadim Krčmář handle_irq(BROADCAST_VECTOR, broadcast_handler); 5239931b88cSRadim Krčmář for (int c = 1; c < ncpus; c++) 5249931b88cSRadim Krčmář on_cpu(c, update_cr3, (void *)cr3); 5259931b88cSRadim Krčmář 5269931b88cSRadim Krčmář printf("starting broadcast (%s)\n", enable_x2apic() ? "x2apic" : "xapic"); 5279931b88cSRadim Krčmář apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT | 5289931b88cSRadim Krčmář BROADCAST_VECTOR, broadcast_address); 5299931b88cSRadim Krčmář report("APIC physical broadcast address", broadcast_received(ncpus)); 5309931b88cSRadim Krčmář 5319931b88cSRadim Krčmář apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT | 5329931b88cSRadim Krčmář BROADCAST_VECTOR | APIC_DEST_ALLINC, 0); 5339931b88cSRadim Krčmář report("APIC physical broadcast shorthand", broadcast_received(ncpus)); 5349931b88cSRadim Krčmář } 5359931b88cSRadim Krčmář 5360eac5394SEvgeny Yakovlev static void wait_until_tmcct_common(uint32_t initial_count, bool stop_when_half, bool should_wrap_around) 537d9b2b283SWanpeng Li { 538d9b2b283SWanpeng Li uint32_t tmcct = apic_read(APIC_TMCCT); 539d9b2b283SWanpeng Li 540d9b2b283SWanpeng Li if (tmcct) { 541d9b2b283SWanpeng Li while (tmcct > (initial_count / 2)) 542d9b2b283SWanpeng Li tmcct = apic_read(APIC_TMCCT); 543d9b2b283SWanpeng Li 544d9b2b283SWanpeng Li if ( stop_when_half ) 545d9b2b283SWanpeng Li return; 546d9b2b283SWanpeng Li 547d9b2b283SWanpeng Li /* Wait until the counter reach 0 or wrap-around */ 548d9b2b283SWanpeng Li while ( tmcct <= (initial_count / 2) && tmcct > 0 ) 549d9b2b283SWanpeng Li tmcct = apic_read(APIC_TMCCT); 5500eac5394SEvgeny Yakovlev 5510eac5394SEvgeny Yakovlev /* Wait specifically for wrap around to skip 0 TMCCR if we were asked to */ 5520eac5394SEvgeny Yakovlev while (should_wrap_around && !tmcct) 5530eac5394SEvgeny Yakovlev tmcct = apic_read(APIC_TMCCT); 554d9b2b283SWanpeng Li } 555d9b2b283SWanpeng Li } 556d9b2b283SWanpeng Li 5570eac5394SEvgeny Yakovlev static void wait_until_tmcct_is_zero(uint32_t initial_count, bool stop_when_half) 5580eac5394SEvgeny Yakovlev { 5590eac5394SEvgeny Yakovlev return wait_until_tmcct_common(initial_count, stop_when_half, false); 5600eac5394SEvgeny Yakovlev } 5610eac5394SEvgeny Yakovlev 5620eac5394SEvgeny Yakovlev static void wait_until_tmcct_wrap_around(uint32_t initial_count, bool stop_when_half) 5630eac5394SEvgeny Yakovlev { 5640eac5394SEvgeny Yakovlev return wait_until_tmcct_common(initial_count, stop_when_half, true); 5650eac5394SEvgeny Yakovlev } 5660eac5394SEvgeny Yakovlev 567d9b2b283SWanpeng Li static inline void apic_change_mode(unsigned long new_mode) 568d9b2b283SWanpeng Li { 569d9b2b283SWanpeng Li uint32_t lvtt; 570d9b2b283SWanpeng Li 571d9b2b283SWanpeng Li lvtt = apic_read(APIC_LVTT); 572d9b2b283SWanpeng Li apic_write(APIC_LVTT, (lvtt & ~APIC_LVT_TIMER_MASK) | new_mode); 573d9b2b283SWanpeng Li } 574d9b2b283SWanpeng Li 5757db17e21SThomas Huth static void test_apic_change_mode(void) 576d9b2b283SWanpeng Li { 577d9b2b283SWanpeng Li uint32_t tmict = 0x999999; 578d9b2b283SWanpeng Li 579d9b2b283SWanpeng Li printf("starting apic change mode\n"); 580d9b2b283SWanpeng Li 581d9b2b283SWanpeng Li apic_write(APIC_TMICT, tmict); 582d9b2b283SWanpeng Li 583d9b2b283SWanpeng Li apic_change_mode(APIC_LVT_TIMER_PERIODIC); 584d9b2b283SWanpeng Li 585d9b2b283SWanpeng Li report("TMICT value reset", apic_read(APIC_TMICT) == tmict); 586d9b2b283SWanpeng Li 587d9b2b283SWanpeng Li /* Testing one-shot */ 588d9b2b283SWanpeng Li apic_change_mode(APIC_LVT_TIMER_ONESHOT); 589d9b2b283SWanpeng Li apic_write(APIC_TMICT, tmict); 590d9b2b283SWanpeng Li report("TMCCT should have a non-zero value", apic_read(APIC_TMCCT)); 591d9b2b283SWanpeng Li 592d9b2b283SWanpeng Li wait_until_tmcct_is_zero(tmict, false); 593d9b2b283SWanpeng Li report("TMCCT should have reached 0", !apic_read(APIC_TMCCT)); 594d9b2b283SWanpeng Li 595d9b2b283SWanpeng Li /* 596d9b2b283SWanpeng Li * Write TMICT before changing mode from one-shot to periodic TMCCT should 597d9b2b283SWanpeng Li * be reset to TMICT periodicly 598d9b2b283SWanpeng Li */ 599d9b2b283SWanpeng Li apic_write(APIC_TMICT, tmict); 600d9b2b283SWanpeng Li wait_until_tmcct_is_zero(tmict, true); 601d9b2b283SWanpeng Li apic_change_mode(APIC_LVT_TIMER_PERIODIC); 602d9b2b283SWanpeng Li report("TMCCT should have a non-zero value", apic_read(APIC_TMCCT)); 603d9b2b283SWanpeng Li 604d9b2b283SWanpeng Li /* 605d9b2b283SWanpeng Li * After the change of mode, the counter should not be reset and continue 606d9b2b283SWanpeng Li * counting down from where it was 607d9b2b283SWanpeng Li */ 608d9b2b283SWanpeng Li report("TMCCT should not be reset to TMICT value", apic_read(APIC_TMCCT) < (tmict / 2)); 6090eac5394SEvgeny Yakovlev /* 6100eac5394SEvgeny Yakovlev * Specifically wait for timer wrap around and skip 0. 6110eac5394SEvgeny Yakovlev * Under KVM lapic there is a possibility that a small amount of consecutive 6120eac5394SEvgeny Yakovlev * TMCCR reads return 0 while hrtimer is reset in an async callback 6130eac5394SEvgeny Yakovlev */ 6140eac5394SEvgeny Yakovlev wait_until_tmcct_wrap_around(tmict, false); 615d9b2b283SWanpeng Li report("TMCCT should be reset to the initial-count", apic_read(APIC_TMCCT) > (tmict / 2)); 616d9b2b283SWanpeng Li 617d9b2b283SWanpeng Li wait_until_tmcct_is_zero(tmict, true); 618d9b2b283SWanpeng Li /* 619d9b2b283SWanpeng Li * Keep the same TMICT and change timer mode to one-shot 620d9b2b283SWanpeng Li * TMCCT should be > 0 and count-down to 0 621d9b2b283SWanpeng Li */ 622d9b2b283SWanpeng Li apic_change_mode(APIC_LVT_TIMER_ONESHOT); 623d9b2b283SWanpeng Li report("TMCCT should not be reset to init", apic_read(APIC_TMCCT) < (tmict / 2)); 624d9b2b283SWanpeng Li wait_until_tmcct_is_zero(tmict, false); 625d9b2b283SWanpeng Li report("TMCCT should have reach zero", !apic_read(APIC_TMCCT)); 626d9b2b283SWanpeng Li 627d9b2b283SWanpeng Li /* now tmcct == 0 and tmict != 0 */ 628d9b2b283SWanpeng Li apic_change_mode(APIC_LVT_TIMER_PERIODIC); 629d9b2b283SWanpeng Li report("TMCCT should stay at zero", !apic_read(APIC_TMCCT)); 630d9b2b283SWanpeng Li } 631d9b2b283SWanpeng Li 632de8d3fccSWanpeng Li #define KVM_HC_SEND_IPI 10 633de8d3fccSWanpeng Li 634de8d3fccSWanpeng Li static void test_pv_ipi(void) 635de8d3fccSWanpeng Li { 636de8d3fccSWanpeng Li int ret; 637de8d3fccSWanpeng Li unsigned long a0 = 0xFFFFFFFF, a1 = 0, a2 = 0xFFFFFFFF, a3 = 0x0; 638de8d3fccSWanpeng Li 639de8d3fccSWanpeng Li asm volatile("vmcall" : "=a"(ret) :"a"(KVM_HC_SEND_IPI), "b"(a0), "c"(a1), "d"(a2), "S"(a3)); 640de8d3fccSWanpeng Li report("PV IPIs testing", !ret); 641de8d3fccSWanpeng Li } 642de8d3fccSWanpeng Li 6437db17e21SThomas Huth int main(void) 6447d36db35SAvi Kivity { 6457d36db35SAvi Kivity setup_vm(); 646f2d2b7c7SAvi Kivity smp_init(); 6477d36db35SAvi Kivity 6487d36db35SAvi Kivity test_lapic_existence(); 6497d36db35SAvi Kivity 6507d36db35SAvi Kivity mask_pic_interrupts(); 651a222b5e2SRadim Krčmář test_apic_id(); 652c3ccca3fSJim Mattson test_apic_disable(); 6537d36db35SAvi Kivity test_enable_x2apic(); 6549b6bdb3fSJan Kiszka test_apicbase(); 6557d36db35SAvi Kivity 6567d36db35SAvi Kivity test_self_ipi(); 6579931b88cSRadim Krčmář test_physical_broadcast(); 658de8d3fccSWanpeng Li test_pv_ipi(); 6597d36db35SAvi Kivity 660f2d2b7c7SAvi Kivity test_sti_nmi(); 661173e7eacSAvi Kivity test_multiple_nmi(); 6629f23b246SSean Christopherson test_pending_nmi(); 6637d36db35SAvi Kivity 6649f815b29SPeter Xu test_apic_timer_one_shot(); 665d9b2b283SWanpeng Li test_apic_change_mode(); 666d423ca36SLiu, Jinsong test_tsc_deadline_timer(); 667d423ca36SLiu, Jinsong 668f3cdd159SJan Kiszka return report_summary(); 6697d36db35SAvi Kivity } 670