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" 87d36db35SAvi Kivity 97d36db35SAvi Kivity static void test_lapic_existence(void) 107d36db35SAvi Kivity { 117d36db35SAvi Kivity u32 lvr; 127d36db35SAvi Kivity 137d36db35SAvi Kivity lvr = apic_read(APIC_LVR); 147d36db35SAvi Kivity printf("apic version: %x\n", lvr); 157d36db35SAvi Kivity report("apic existence", (u16)lvr == 0x14); 167d36db35SAvi Kivity } 177d36db35SAvi Kivity 18d423ca36SLiu, Jinsong #define TSC_DEADLINE_TIMER_MODE (2 << 17) 19d423ca36SLiu, Jinsong #define TSC_DEADLINE_TIMER_VECTOR 0xef 20d423ca36SLiu, Jinsong #define MSR_IA32_TSC 0x00000010 21d423ca36SLiu, Jinsong #define MSR_IA32_TSCDEADLINE 0x000006e0 22d423ca36SLiu, Jinsong 23d423ca36SLiu, Jinsong static int tdt_count; 24d423ca36SLiu, Jinsong 25d423ca36SLiu, Jinsong static void tsc_deadline_timer_isr(isr_regs_t *regs) 26d423ca36SLiu, Jinsong { 27d423ca36SLiu, Jinsong ++tdt_count; 28d423ca36SLiu, Jinsong } 29d423ca36SLiu, Jinsong 30*32b9603cSRadim Krčmář static void __test_tsc_deadline_timer(void) 31d423ca36SLiu, Jinsong { 32d423ca36SLiu, Jinsong handle_irq(TSC_DEADLINE_TIMER_VECTOR, tsc_deadline_timer_isr); 33d423ca36SLiu, Jinsong irq_enable(); 34d423ca36SLiu, Jinsong 35d423ca36SLiu, Jinsong wrmsr(MSR_IA32_TSCDEADLINE, rdmsr(MSR_IA32_TSC)); 36d423ca36SLiu, Jinsong asm volatile ("nop"); 37d423ca36SLiu, Jinsong report("tsc deadline timer", tdt_count == 1); 38f8833144SNadav Amit report("tsc deadline timer clearing", rdmsr(MSR_IA32_TSCDEADLINE) == 0); 39d423ca36SLiu, Jinsong } 40d423ca36SLiu, Jinsong 41d423ca36SLiu, Jinsong static int enable_tsc_deadline_timer(void) 42d423ca36SLiu, Jinsong { 43d423ca36SLiu, Jinsong uint32_t lvtt; 44d423ca36SLiu, Jinsong 45d423ca36SLiu, Jinsong if (cpuid(1).c & (1 << 24)) { 46d423ca36SLiu, Jinsong lvtt = TSC_DEADLINE_TIMER_MODE | TSC_DEADLINE_TIMER_VECTOR; 47d423ca36SLiu, Jinsong apic_write(APIC_LVTT, lvtt); 48d423ca36SLiu, Jinsong return 1; 49d423ca36SLiu, Jinsong } else { 50d423ca36SLiu, Jinsong return 0; 51d423ca36SLiu, Jinsong } 52d423ca36SLiu, Jinsong } 53d423ca36SLiu, Jinsong 54d423ca36SLiu, Jinsong static void test_tsc_deadline_timer(void) 55d423ca36SLiu, Jinsong { 56d423ca36SLiu, Jinsong if(enable_tsc_deadline_timer()) { 57*32b9603cSRadim Krčmář __test_tsc_deadline_timer(); 58d423ca36SLiu, Jinsong } else { 59*32b9603cSRadim Krčmář report_skip("tsc deadline timer not detected"); 60d423ca36SLiu, Jinsong } 61d423ca36SLiu, Jinsong } 62d423ca36SLiu, Jinsong 6322c7d929SJan Kiszka static void do_write_apicbase(void *data) 6422c7d929SJan Kiszka { 6522c7d929SJan Kiszka wrmsr(MSR_IA32_APICBASE, *(u64 *)data); 6603f37ef2SPaolo Bonzini } 677d36db35SAvi Kivity 687d36db35SAvi Kivity void test_enable_x2apic(void) 697d36db35SAvi Kivity { 7022c7d929SJan Kiszka u64 invalid_state = APIC_DEFAULT_PHYS_BASE | APIC_BSP | APIC_EXTD; 7122c7d929SJan Kiszka u64 apic_enabled = APIC_DEFAULT_PHYS_BASE | APIC_BSP | APIC_EN; 7222c7d929SJan Kiszka u64 x2apic_enabled = 7322c7d929SJan Kiszka APIC_DEFAULT_PHYS_BASE | APIC_BSP | APIC_EN | APIC_EXTD; 7422c7d929SJan Kiszka 757d36db35SAvi Kivity if (enable_x2apic()) { 767d36db35SAvi Kivity printf("x2apic enabled\n"); 7722c7d929SJan Kiszka 7822c7d929SJan Kiszka report("x2apic enabled to invalid state", 7922c7d929SJan Kiszka test_for_exception(GP_VECTOR, do_write_apicbase, 8022c7d929SJan Kiszka &invalid_state)); 8122c7d929SJan Kiszka report("x2apic enabled to apic enabled", 8222c7d929SJan Kiszka test_for_exception(GP_VECTOR, do_write_apicbase, 8322c7d929SJan Kiszka &apic_enabled)); 8422c7d929SJan Kiszka 8522c7d929SJan Kiszka wrmsr(MSR_IA32_APICBASE, APIC_DEFAULT_PHYS_BASE | APIC_BSP); 8622c7d929SJan Kiszka report("disabled to invalid state", 8722c7d929SJan Kiszka test_for_exception(GP_VECTOR, do_write_apicbase, 8822c7d929SJan Kiszka &invalid_state)); 8922c7d929SJan Kiszka report("disabled to x2apic enabled", 9022c7d929SJan Kiszka test_for_exception(GP_VECTOR, do_write_apicbase, 9122c7d929SJan Kiszka &x2apic_enabled)); 9222c7d929SJan Kiszka 9322c7d929SJan Kiszka wrmsr(MSR_IA32_APICBASE, apic_enabled); 9422c7d929SJan Kiszka report("apic enabled to invalid state", 9522c7d929SJan Kiszka test_for_exception(GP_VECTOR, do_write_apicbase, 9622c7d929SJan Kiszka &invalid_state)); 9722c7d929SJan Kiszka 9822c7d929SJan Kiszka wrmsr(MSR_IA32_APICBASE, x2apic_enabled); 9922c7d929SJan Kiszka apic_write(APIC_SPIV, 0x1ff); 1007d36db35SAvi Kivity } else { 1017d36db35SAvi Kivity printf("x2apic not detected\n"); 10222c7d929SJan Kiszka 10322c7d929SJan Kiszka report("enable unsupported x2apic", 10422c7d929SJan Kiszka test_for_exception(GP_VECTOR, do_write_apicbase, 10522c7d929SJan Kiszka &x2apic_enabled)); 1067d36db35SAvi Kivity } 1077d36db35SAvi Kivity } 1087d36db35SAvi Kivity 1099b6bdb3fSJan Kiszka #define ALTERNATE_APIC_BASE 0x42000000 1109b6bdb3fSJan Kiszka 1119b6bdb3fSJan Kiszka static void test_apicbase(void) 1129b6bdb3fSJan Kiszka { 1139b6bdb3fSJan Kiszka u64 orig_apicbase = rdmsr(MSR_IA32_APICBASE); 1149b6bdb3fSJan Kiszka u32 lvr = apic_read(APIC_LVR); 1159b6bdb3fSJan Kiszka u64 value; 1169b6bdb3fSJan Kiszka 1179b6bdb3fSJan Kiszka wrmsr(MSR_IA32_APICBASE, orig_apicbase & ~(APIC_EN | APIC_EXTD)); 1189b6bdb3fSJan Kiszka wrmsr(MSR_IA32_APICBASE, ALTERNATE_APIC_BASE | APIC_BSP | APIC_EN); 1199b6bdb3fSJan Kiszka 1205bba1769SAndrew Jones report_prefix_push("apicbase"); 1215bba1769SAndrew Jones 1229b6bdb3fSJan Kiszka report("relocate apic", 1239b6bdb3fSJan Kiszka *(volatile u32 *)(ALTERNATE_APIC_BASE + APIC_LVR) == lvr); 1249b6bdb3fSJan Kiszka 125772befb7SEduardo Habkost value = orig_apicbase | (1UL << cpuid_maxphyaddr()); 1265bba1769SAndrew Jones report("reserved physaddr bits", 1279b6bdb3fSJan Kiszka test_for_exception(GP_VECTOR, do_write_apicbase, &value)); 1289b6bdb3fSJan Kiszka 1299b6bdb3fSJan Kiszka value = orig_apicbase | 1; 1305bba1769SAndrew Jones report("reserved low bits", 1319b6bdb3fSJan Kiszka test_for_exception(GP_VECTOR, do_write_apicbase, &value)); 1329b6bdb3fSJan Kiszka 1339b6bdb3fSJan Kiszka wrmsr(MSR_IA32_APICBASE, orig_apicbase); 1349b6bdb3fSJan Kiszka apic_write(APIC_SPIV, 0x1ff); 1355bba1769SAndrew Jones 1365bba1769SAndrew Jones report_prefix_pop(); 1379b6bdb3fSJan Kiszka } 1389b6bdb3fSJan Kiszka 1397d36db35SAvi Kivity static int ipi_count; 1407d36db35SAvi Kivity 1417d36db35SAvi Kivity static void self_ipi_isr(isr_regs_t *regs) 1427d36db35SAvi Kivity { 1437d36db35SAvi Kivity ++ipi_count; 1447d36db35SAvi Kivity eoi(); 1457d36db35SAvi Kivity } 1467d36db35SAvi Kivity 1477d36db35SAvi Kivity static void test_self_ipi(void) 1487d36db35SAvi Kivity { 1497d36db35SAvi Kivity int vec = 0xf1; 1507d36db35SAvi Kivity 151d51bd17eSGleb Natapov handle_irq(vec, self_ipi_isr); 1527d36db35SAvi Kivity irq_enable(); 1537d36db35SAvi Kivity apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec, 1547d36db35SAvi Kivity 0); 1557d36db35SAvi Kivity asm volatile ("nop"); 1567d36db35SAvi Kivity report("self ipi", ipi_count == 1); 1577d36db35SAvi Kivity } 1587d36db35SAvi Kivity 159f2d2b7c7SAvi Kivity volatile int nmi_counter_private, nmi_counter, nmi_hlt_counter, sti_loop_active; 160f2d2b7c7SAvi Kivity 161f2d2b7c7SAvi Kivity void sti_nop(char *p) 162f2d2b7c7SAvi Kivity { 163f2d2b7c7SAvi Kivity asm volatile ( 164f2d2b7c7SAvi Kivity ".globl post_sti \n\t" 165f2d2b7c7SAvi Kivity "sti \n" 166f2d2b7c7SAvi Kivity /* 167f2d2b7c7SAvi Kivity * vmx won't exit on external interrupt if blocked-by-sti, 168f2d2b7c7SAvi Kivity * so give it a reason to exit by accessing an unmapped page. 169f2d2b7c7SAvi Kivity */ 170f2d2b7c7SAvi Kivity "post_sti: testb $0, %0 \n\t" 171f2d2b7c7SAvi Kivity "nop \n\t" 172f2d2b7c7SAvi Kivity "cli" 173f2d2b7c7SAvi Kivity : : "m"(*p) 174f2d2b7c7SAvi Kivity ); 175f2d2b7c7SAvi Kivity nmi_counter = nmi_counter_private; 176f2d2b7c7SAvi Kivity } 177f2d2b7c7SAvi Kivity 178f2d2b7c7SAvi Kivity static void sti_loop(void *ignore) 179f2d2b7c7SAvi Kivity { 180f2d2b7c7SAvi Kivity unsigned k = 0; 181f2d2b7c7SAvi Kivity 182f2d2b7c7SAvi Kivity while (sti_loop_active) { 183f2d2b7c7SAvi Kivity sti_nop((char *)(ulong)((k++ * 4096) % (128 * 1024 * 1024))); 184f2d2b7c7SAvi Kivity } 185f2d2b7c7SAvi Kivity } 186f2d2b7c7SAvi Kivity 187f2d2b7c7SAvi Kivity static void nmi_handler(isr_regs_t *regs) 188f2d2b7c7SAvi Kivity { 189f2d2b7c7SAvi Kivity extern void post_sti(void); 190f2d2b7c7SAvi Kivity ++nmi_counter_private; 191f2d2b7c7SAvi Kivity nmi_hlt_counter += regs->rip == (ulong)post_sti; 192f2d2b7c7SAvi Kivity } 193f2d2b7c7SAvi Kivity 194f2d2b7c7SAvi Kivity static void update_cr3(void *cr3) 195f2d2b7c7SAvi Kivity { 196f2d2b7c7SAvi Kivity write_cr3((ulong)cr3); 197f2d2b7c7SAvi Kivity } 198f2d2b7c7SAvi Kivity 199f2d2b7c7SAvi Kivity static void test_sti_nmi(void) 200f2d2b7c7SAvi Kivity { 201f2d2b7c7SAvi Kivity unsigned old_counter; 202f2d2b7c7SAvi Kivity 203f2d2b7c7SAvi Kivity if (cpu_count() < 2) { 204f2d2b7c7SAvi Kivity return; 205f2d2b7c7SAvi Kivity } 206f2d2b7c7SAvi Kivity 207d51bd17eSGleb Natapov handle_irq(2, nmi_handler); 208f2d2b7c7SAvi Kivity on_cpu(1, update_cr3, (void *)read_cr3()); 209f2d2b7c7SAvi Kivity 210f2d2b7c7SAvi Kivity sti_loop_active = 1; 211f2d2b7c7SAvi Kivity on_cpu_async(1, sti_loop, 0); 212f2d2b7c7SAvi Kivity while (nmi_counter < 30000) { 213f2d2b7c7SAvi Kivity old_counter = nmi_counter; 214f2d2b7c7SAvi Kivity apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 1); 215f2d2b7c7SAvi Kivity while (nmi_counter == old_counter) { 216f2d2b7c7SAvi Kivity ; 217f2d2b7c7SAvi Kivity } 218f2d2b7c7SAvi Kivity } 219f2d2b7c7SAvi Kivity sti_loop_active = 0; 220f2d2b7c7SAvi Kivity report("nmi-after-sti", nmi_hlt_counter == 0); 221f2d2b7c7SAvi Kivity } 222f2d2b7c7SAvi Kivity 223173e7eacSAvi Kivity static volatile bool nmi_done, nmi_flushed; 224173e7eacSAvi Kivity static volatile int nmi_received; 225173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr1, cpu1_nmi_ctr1; 226173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr2, cpu1_nmi_ctr2; 227173e7eacSAvi Kivity 228173e7eacSAvi Kivity static void multiple_nmi_handler(isr_regs_t *regs) 229173e7eacSAvi Kivity { 230173e7eacSAvi Kivity ++nmi_received; 231173e7eacSAvi Kivity } 232173e7eacSAvi Kivity 233173e7eacSAvi Kivity static void kick_me_nmi(void *blah) 234173e7eacSAvi Kivity { 235173e7eacSAvi Kivity while (!nmi_done) { 236173e7eacSAvi Kivity ++cpu1_nmi_ctr1; 237173e7eacSAvi Kivity while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1 && !nmi_done) { 238173e7eacSAvi Kivity pause(); 239173e7eacSAvi Kivity } 240173e7eacSAvi Kivity if (nmi_done) { 241173e7eacSAvi Kivity return; 242173e7eacSAvi Kivity } 243173e7eacSAvi Kivity apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0); 244173e7eacSAvi Kivity /* make sure the NMI has arrived by sending an IPI after it */ 245173e7eacSAvi Kivity apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT 246173e7eacSAvi Kivity | 0x44, 0); 247173e7eacSAvi Kivity ++cpu1_nmi_ctr2; 248173e7eacSAvi Kivity while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2 && !nmi_done) { 249173e7eacSAvi Kivity pause(); 250173e7eacSAvi Kivity } 251173e7eacSAvi Kivity } 252173e7eacSAvi Kivity } 253173e7eacSAvi Kivity 254173e7eacSAvi Kivity static void flush_nmi(isr_regs_t *regs) 255173e7eacSAvi Kivity { 256173e7eacSAvi Kivity nmi_flushed = true; 257173e7eacSAvi Kivity apic_write(APIC_EOI, 0); 258173e7eacSAvi Kivity } 259173e7eacSAvi Kivity 260173e7eacSAvi Kivity static void test_multiple_nmi(void) 261173e7eacSAvi Kivity { 262173e7eacSAvi Kivity int i; 263173e7eacSAvi Kivity bool ok = true; 264173e7eacSAvi Kivity 265173e7eacSAvi Kivity if (cpu_count() < 2) { 266173e7eacSAvi Kivity return; 267173e7eacSAvi Kivity } 268173e7eacSAvi Kivity 269173e7eacSAvi Kivity sti(); 270173e7eacSAvi Kivity handle_irq(2, multiple_nmi_handler); 271173e7eacSAvi Kivity handle_irq(0x44, flush_nmi); 272173e7eacSAvi Kivity on_cpu_async(1, kick_me_nmi, 0); 273173e7eacSAvi Kivity for (i = 0; i < 1000000; ++i) { 274173e7eacSAvi Kivity nmi_flushed = false; 275173e7eacSAvi Kivity nmi_received = 0; 276173e7eacSAvi Kivity ++cpu0_nmi_ctr1; 277173e7eacSAvi Kivity while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1) { 278173e7eacSAvi Kivity pause(); 279173e7eacSAvi Kivity } 280173e7eacSAvi Kivity apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0); 281173e7eacSAvi Kivity while (!nmi_flushed) { 282173e7eacSAvi Kivity pause(); 283173e7eacSAvi Kivity } 284173e7eacSAvi Kivity if (nmi_received != 2) { 285173e7eacSAvi Kivity ok = false; 286173e7eacSAvi Kivity break; 287173e7eacSAvi Kivity } 288173e7eacSAvi Kivity ++cpu0_nmi_ctr2; 289173e7eacSAvi Kivity while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2) { 290173e7eacSAvi Kivity pause(); 291173e7eacSAvi Kivity } 292173e7eacSAvi Kivity } 293173e7eacSAvi Kivity nmi_done = true; 294173e7eacSAvi Kivity report("multiple nmi", ok); 295173e7eacSAvi Kivity } 296173e7eacSAvi Kivity 2977d36db35SAvi Kivity int main() 2987d36db35SAvi Kivity { 2997d36db35SAvi Kivity setup_vm(); 300f2d2b7c7SAvi Kivity smp_init(); 301d51bd17eSGleb Natapov setup_idt(); 3027d36db35SAvi Kivity 3037d36db35SAvi Kivity test_lapic_existence(); 3047d36db35SAvi Kivity 3057d36db35SAvi Kivity mask_pic_interrupts(); 3067d36db35SAvi Kivity test_enable_x2apic(); 3079b6bdb3fSJan Kiszka test_apicbase(); 3087d36db35SAvi Kivity 3097d36db35SAvi Kivity test_self_ipi(); 3107d36db35SAvi Kivity 311f2d2b7c7SAvi Kivity test_sti_nmi(); 312173e7eacSAvi Kivity test_multiple_nmi(); 3137d36db35SAvi Kivity 314d423ca36SLiu, Jinsong test_tsc_deadline_timer(); 315d423ca36SLiu, Jinsong 316f3cdd159SJan Kiszka return report_summary(); 3177d36db35SAvi Kivity } 318