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" 77d36db35SAvi Kivity 87d36db35SAvi Kivity static int g_fail; 97d36db35SAvi Kivity static int g_tests; 107d36db35SAvi Kivity 117d36db35SAvi Kivity static void report(const char *msg, int pass) 127d36db35SAvi Kivity { 137d36db35SAvi Kivity ++g_tests; 147d36db35SAvi Kivity printf("%s: %s\n", msg, (pass ? "PASS" : "FAIL")); 157d36db35SAvi Kivity if (!pass) 167d36db35SAvi Kivity ++g_fail; 177d36db35SAvi Kivity } 187d36db35SAvi Kivity 197d36db35SAvi Kivity static void test_lapic_existence(void) 207d36db35SAvi Kivity { 217d36db35SAvi Kivity u32 lvr; 227d36db35SAvi Kivity 237d36db35SAvi Kivity lvr = apic_read(APIC_LVR); 247d36db35SAvi Kivity printf("apic version: %x\n", lvr); 257d36db35SAvi Kivity report("apic existence", (u16)lvr == 0x14); 267d36db35SAvi Kivity } 277d36db35SAvi Kivity 28d423ca36SLiu, Jinsong #define TSC_DEADLINE_TIMER_MODE (2 << 17) 29d423ca36SLiu, Jinsong #define TSC_DEADLINE_TIMER_VECTOR 0xef 30d423ca36SLiu, Jinsong #define MSR_IA32_TSC 0x00000010 31d423ca36SLiu, Jinsong #define MSR_IA32_TSCDEADLINE 0x000006e0 32d423ca36SLiu, Jinsong 33d423ca36SLiu, Jinsong static int tdt_count; 34d423ca36SLiu, Jinsong 35d423ca36SLiu, Jinsong static void tsc_deadline_timer_isr(isr_regs_t *regs) 36d423ca36SLiu, Jinsong { 37d423ca36SLiu, Jinsong ++tdt_count; 38d423ca36SLiu, Jinsong } 39d423ca36SLiu, Jinsong 40d423ca36SLiu, Jinsong static void start_tsc_deadline_timer(void) 41d423ca36SLiu, Jinsong { 42d423ca36SLiu, Jinsong handle_irq(TSC_DEADLINE_TIMER_VECTOR, tsc_deadline_timer_isr); 43d423ca36SLiu, Jinsong irq_enable(); 44d423ca36SLiu, Jinsong 45d423ca36SLiu, Jinsong wrmsr(MSR_IA32_TSCDEADLINE, rdmsr(MSR_IA32_TSC)); 46d423ca36SLiu, Jinsong asm volatile ("nop"); 47d423ca36SLiu, Jinsong report("tsc deadline timer", tdt_count == 1); 48d423ca36SLiu, Jinsong } 49d423ca36SLiu, Jinsong 50d423ca36SLiu, Jinsong static int enable_tsc_deadline_timer(void) 51d423ca36SLiu, Jinsong { 52d423ca36SLiu, Jinsong uint32_t lvtt; 53d423ca36SLiu, Jinsong 54d423ca36SLiu, Jinsong if (cpuid(1).c & (1 << 24)) { 55d423ca36SLiu, Jinsong lvtt = TSC_DEADLINE_TIMER_MODE | TSC_DEADLINE_TIMER_VECTOR; 56d423ca36SLiu, Jinsong apic_write(APIC_LVTT, lvtt); 57d423ca36SLiu, Jinsong start_tsc_deadline_timer(); 58d423ca36SLiu, Jinsong return 1; 59d423ca36SLiu, Jinsong } else { 60d423ca36SLiu, Jinsong return 0; 61d423ca36SLiu, Jinsong } 62d423ca36SLiu, Jinsong } 63d423ca36SLiu, Jinsong 64d423ca36SLiu, Jinsong static void test_tsc_deadline_timer(void) 65d423ca36SLiu, Jinsong { 66d423ca36SLiu, Jinsong if(enable_tsc_deadline_timer()) { 67d423ca36SLiu, Jinsong printf("tsc deadline timer enabled\n"); 68d423ca36SLiu, Jinsong } else { 69d423ca36SLiu, Jinsong printf("tsc deadline timer not detected\n"); 70d423ca36SLiu, Jinsong } 71d423ca36SLiu, Jinsong } 72d423ca36SLiu, Jinsong 737d36db35SAvi Kivity #define MSR_APIC_BASE 0x0000001b 747d36db35SAvi Kivity 757d36db35SAvi Kivity void test_enable_x2apic(void) 767d36db35SAvi Kivity { 777d36db35SAvi Kivity if (enable_x2apic()) { 787d36db35SAvi Kivity printf("x2apic enabled\n"); 797d36db35SAvi Kivity } else { 807d36db35SAvi Kivity printf("x2apic not detected\n"); 817d36db35SAvi Kivity } 827d36db35SAvi Kivity } 837d36db35SAvi Kivity 847d36db35SAvi Kivity static void eoi(void) 857d36db35SAvi Kivity { 867d36db35SAvi Kivity apic_write(APIC_EOI, 0); 877d36db35SAvi Kivity } 887d36db35SAvi Kivity 897d36db35SAvi Kivity static int ipi_count; 907d36db35SAvi Kivity 917d36db35SAvi Kivity static void self_ipi_isr(isr_regs_t *regs) 927d36db35SAvi Kivity { 937d36db35SAvi Kivity ++ipi_count; 947d36db35SAvi Kivity eoi(); 957d36db35SAvi Kivity } 967d36db35SAvi Kivity 977d36db35SAvi Kivity static void test_self_ipi(void) 987d36db35SAvi Kivity { 997d36db35SAvi Kivity int vec = 0xf1; 1007d36db35SAvi Kivity 101d51bd17eSGleb Natapov handle_irq(vec, self_ipi_isr); 1027d36db35SAvi Kivity irq_enable(); 1037d36db35SAvi Kivity apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec, 1047d36db35SAvi Kivity 0); 1057d36db35SAvi Kivity asm volatile ("nop"); 1067d36db35SAvi Kivity report("self ipi", ipi_count == 1); 1077d36db35SAvi Kivity } 1087d36db35SAvi Kivity 1097d36db35SAvi Kivity static void set_ioapic_redir(unsigned line, unsigned vec) 1107d36db35SAvi Kivity { 1117d36db35SAvi Kivity ioapic_redir_entry_t e = { 1127d36db35SAvi Kivity .vector = vec, 1137d36db35SAvi Kivity .delivery_mode = 0, 1147d36db35SAvi Kivity .trig_mode = 0, 1157d36db35SAvi Kivity }; 1167d36db35SAvi Kivity 1177d36db35SAvi Kivity ioapic_write_redir(line, e); 1187d36db35SAvi Kivity } 1197d36db35SAvi Kivity 1207d36db35SAvi Kivity static void set_irq_line(unsigned line, int val) 1217d36db35SAvi Kivity { 1227d36db35SAvi Kivity asm volatile("out %0, %1" : : "a"((u8)val), "d"((u16)(0x2000 + line))); 1237d36db35SAvi Kivity } 1247d36db35SAvi Kivity 1257d36db35SAvi Kivity static void toggle_irq_line(unsigned line) 1267d36db35SAvi Kivity { 1277d36db35SAvi Kivity set_irq_line(line, 1); 1287d36db35SAvi Kivity set_irq_line(line, 0); 1297d36db35SAvi Kivity } 1307d36db35SAvi Kivity 1317d36db35SAvi Kivity static int g_isr_77; 1327d36db35SAvi Kivity 1337d36db35SAvi Kivity static void ioapic_isr_77(isr_regs_t *regs) 1347d36db35SAvi Kivity { 1357d36db35SAvi Kivity ++g_isr_77; 1367d36db35SAvi Kivity eoi(); 1377d36db35SAvi Kivity } 1387d36db35SAvi Kivity 1397d36db35SAvi Kivity static void test_ioapic_intr(void) 1407d36db35SAvi Kivity { 141d51bd17eSGleb Natapov handle_irq(0x77, ioapic_isr_77); 142*598f9fc4SJoerg Roedel set_ioapic_redir(0x0e, 0x77); 143*598f9fc4SJoerg Roedel toggle_irq_line(0x0e); 1447d36db35SAvi Kivity asm volatile ("nop"); 1457d36db35SAvi Kivity report("ioapic interrupt", g_isr_77 == 1); 1467d36db35SAvi Kivity } 1477d36db35SAvi Kivity 1487d36db35SAvi Kivity static int g_78, g_66, g_66_after_78; 1497d36db35SAvi Kivity static ulong g_66_rip, g_78_rip; 1507d36db35SAvi Kivity 1517d36db35SAvi Kivity static void ioapic_isr_78(isr_regs_t *regs) 1527d36db35SAvi Kivity { 1537d36db35SAvi Kivity ++g_78; 1547d36db35SAvi Kivity g_78_rip = regs->rip; 1557d36db35SAvi Kivity eoi(); 1567d36db35SAvi Kivity } 1577d36db35SAvi Kivity 1587d36db35SAvi Kivity static void ioapic_isr_66(isr_regs_t *regs) 1597d36db35SAvi Kivity { 1607d36db35SAvi Kivity ++g_66; 1617d36db35SAvi Kivity if (g_78) 1627d36db35SAvi Kivity ++g_66_after_78; 1637d36db35SAvi Kivity g_66_rip = regs->rip; 1647d36db35SAvi Kivity eoi(); 1657d36db35SAvi Kivity } 1667d36db35SAvi Kivity 1677d36db35SAvi Kivity static void test_ioapic_simultaneous(void) 1687d36db35SAvi Kivity { 169d51bd17eSGleb Natapov handle_irq(0x78, ioapic_isr_78); 170d51bd17eSGleb Natapov handle_irq(0x66, ioapic_isr_66); 171*598f9fc4SJoerg Roedel set_ioapic_redir(0x0e, 0x78); 172*598f9fc4SJoerg Roedel set_ioapic_redir(0x0f, 0x66); 1737d36db35SAvi Kivity irq_disable(); 174*598f9fc4SJoerg Roedel toggle_irq_line(0x0f); 175*598f9fc4SJoerg Roedel toggle_irq_line(0x0e); 1767d36db35SAvi Kivity irq_enable(); 1777d36db35SAvi Kivity asm volatile ("nop"); 1787d36db35SAvi Kivity report("ioapic simultaneous interrupt", 1797d36db35SAvi Kivity g_66 && g_78 && g_66_after_78 && g_66_rip == g_78_rip); 1807d36db35SAvi Kivity } 1817d36db35SAvi Kivity 182f2d2b7c7SAvi Kivity volatile int nmi_counter_private, nmi_counter, nmi_hlt_counter, sti_loop_active; 183f2d2b7c7SAvi Kivity 184f2d2b7c7SAvi Kivity void sti_nop(char *p) 185f2d2b7c7SAvi Kivity { 186f2d2b7c7SAvi Kivity asm volatile ( 187f2d2b7c7SAvi Kivity ".globl post_sti \n\t" 188f2d2b7c7SAvi Kivity "sti \n" 189f2d2b7c7SAvi Kivity /* 190f2d2b7c7SAvi Kivity * vmx won't exit on external interrupt if blocked-by-sti, 191f2d2b7c7SAvi Kivity * so give it a reason to exit by accessing an unmapped page. 192f2d2b7c7SAvi Kivity */ 193f2d2b7c7SAvi Kivity "post_sti: testb $0, %0 \n\t" 194f2d2b7c7SAvi Kivity "nop \n\t" 195f2d2b7c7SAvi Kivity "cli" 196f2d2b7c7SAvi Kivity : : "m"(*p) 197f2d2b7c7SAvi Kivity ); 198f2d2b7c7SAvi Kivity nmi_counter = nmi_counter_private; 199f2d2b7c7SAvi Kivity } 200f2d2b7c7SAvi Kivity 201f2d2b7c7SAvi Kivity static void sti_loop(void *ignore) 202f2d2b7c7SAvi Kivity { 203f2d2b7c7SAvi Kivity unsigned k = 0; 204f2d2b7c7SAvi Kivity 205f2d2b7c7SAvi Kivity while (sti_loop_active) { 206f2d2b7c7SAvi Kivity sti_nop((char *)(ulong)((k++ * 4096) % (128 * 1024 * 1024))); 207f2d2b7c7SAvi Kivity } 208f2d2b7c7SAvi Kivity } 209f2d2b7c7SAvi Kivity 210f2d2b7c7SAvi Kivity static void nmi_handler(isr_regs_t *regs) 211f2d2b7c7SAvi Kivity { 212f2d2b7c7SAvi Kivity extern void post_sti(void); 213f2d2b7c7SAvi Kivity ++nmi_counter_private; 214f2d2b7c7SAvi Kivity nmi_hlt_counter += regs->rip == (ulong)post_sti; 215f2d2b7c7SAvi Kivity } 216f2d2b7c7SAvi Kivity 217f2d2b7c7SAvi Kivity static void update_cr3(void *cr3) 218f2d2b7c7SAvi Kivity { 219f2d2b7c7SAvi Kivity write_cr3((ulong)cr3); 220f2d2b7c7SAvi Kivity } 221f2d2b7c7SAvi Kivity 222f2d2b7c7SAvi Kivity static void test_sti_nmi(void) 223f2d2b7c7SAvi Kivity { 224f2d2b7c7SAvi Kivity unsigned old_counter; 225f2d2b7c7SAvi Kivity 226f2d2b7c7SAvi Kivity if (cpu_count() < 2) { 227f2d2b7c7SAvi Kivity return; 228f2d2b7c7SAvi Kivity } 229f2d2b7c7SAvi Kivity 230d51bd17eSGleb Natapov handle_irq(2, nmi_handler); 231f2d2b7c7SAvi Kivity on_cpu(1, update_cr3, (void *)read_cr3()); 232f2d2b7c7SAvi Kivity 233f2d2b7c7SAvi Kivity sti_loop_active = 1; 234f2d2b7c7SAvi Kivity on_cpu_async(1, sti_loop, 0); 235f2d2b7c7SAvi Kivity while (nmi_counter < 30000) { 236f2d2b7c7SAvi Kivity old_counter = nmi_counter; 237f2d2b7c7SAvi Kivity apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 1); 238f2d2b7c7SAvi Kivity while (nmi_counter == old_counter) { 239f2d2b7c7SAvi Kivity ; 240f2d2b7c7SAvi Kivity } 241f2d2b7c7SAvi Kivity } 242f2d2b7c7SAvi Kivity sti_loop_active = 0; 243f2d2b7c7SAvi Kivity report("nmi-after-sti", nmi_hlt_counter == 0); 244f2d2b7c7SAvi Kivity } 245f2d2b7c7SAvi Kivity 246173e7eacSAvi Kivity static volatile bool nmi_done, nmi_flushed; 247173e7eacSAvi Kivity static volatile int nmi_received; 248173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr1, cpu1_nmi_ctr1; 249173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr2, cpu1_nmi_ctr2; 250173e7eacSAvi Kivity 251173e7eacSAvi Kivity static void multiple_nmi_handler(isr_regs_t *regs) 252173e7eacSAvi Kivity { 253173e7eacSAvi Kivity ++nmi_received; 254173e7eacSAvi Kivity } 255173e7eacSAvi Kivity 256173e7eacSAvi Kivity static void kick_me_nmi(void *blah) 257173e7eacSAvi Kivity { 258173e7eacSAvi Kivity while (!nmi_done) { 259173e7eacSAvi Kivity ++cpu1_nmi_ctr1; 260173e7eacSAvi Kivity while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1 && !nmi_done) { 261173e7eacSAvi Kivity pause(); 262173e7eacSAvi Kivity } 263173e7eacSAvi Kivity if (nmi_done) { 264173e7eacSAvi Kivity return; 265173e7eacSAvi Kivity } 266173e7eacSAvi Kivity apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0); 267173e7eacSAvi Kivity /* make sure the NMI has arrived by sending an IPI after it */ 268173e7eacSAvi Kivity apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT 269173e7eacSAvi Kivity | 0x44, 0); 270173e7eacSAvi Kivity ++cpu1_nmi_ctr2; 271173e7eacSAvi Kivity while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2 && !nmi_done) { 272173e7eacSAvi Kivity pause(); 273173e7eacSAvi Kivity } 274173e7eacSAvi Kivity } 275173e7eacSAvi Kivity } 276173e7eacSAvi Kivity 277173e7eacSAvi Kivity static void flush_nmi(isr_regs_t *regs) 278173e7eacSAvi Kivity { 279173e7eacSAvi Kivity nmi_flushed = true; 280173e7eacSAvi Kivity apic_write(APIC_EOI, 0); 281173e7eacSAvi Kivity } 282173e7eacSAvi Kivity 283173e7eacSAvi Kivity static void test_multiple_nmi(void) 284173e7eacSAvi Kivity { 285173e7eacSAvi Kivity int i; 286173e7eacSAvi Kivity bool ok = true; 287173e7eacSAvi Kivity 288173e7eacSAvi Kivity if (cpu_count() < 2) { 289173e7eacSAvi Kivity return; 290173e7eacSAvi Kivity } 291173e7eacSAvi Kivity 292173e7eacSAvi Kivity sti(); 293173e7eacSAvi Kivity handle_irq(2, multiple_nmi_handler); 294173e7eacSAvi Kivity handle_irq(0x44, flush_nmi); 295173e7eacSAvi Kivity on_cpu_async(1, kick_me_nmi, 0); 296173e7eacSAvi Kivity for (i = 0; i < 1000000; ++i) { 297173e7eacSAvi Kivity nmi_flushed = false; 298173e7eacSAvi Kivity nmi_received = 0; 299173e7eacSAvi Kivity ++cpu0_nmi_ctr1; 300173e7eacSAvi Kivity while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1) { 301173e7eacSAvi Kivity pause(); 302173e7eacSAvi Kivity } 303173e7eacSAvi Kivity apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0); 304173e7eacSAvi Kivity while (!nmi_flushed) { 305173e7eacSAvi Kivity pause(); 306173e7eacSAvi Kivity } 307173e7eacSAvi Kivity if (nmi_received != 2) { 308173e7eacSAvi Kivity ok = false; 309173e7eacSAvi Kivity break; 310173e7eacSAvi Kivity } 311173e7eacSAvi Kivity ++cpu0_nmi_ctr2; 312173e7eacSAvi Kivity while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2) { 313173e7eacSAvi Kivity pause(); 314173e7eacSAvi Kivity } 315173e7eacSAvi Kivity } 316173e7eacSAvi Kivity nmi_done = true; 317173e7eacSAvi Kivity report("multiple nmi", ok); 318173e7eacSAvi Kivity } 319173e7eacSAvi Kivity 3207d36db35SAvi Kivity int main() 3217d36db35SAvi Kivity { 3227d36db35SAvi Kivity setup_vm(); 323f2d2b7c7SAvi Kivity smp_init(); 324d51bd17eSGleb Natapov setup_idt(); 3257d36db35SAvi Kivity 3267d36db35SAvi Kivity test_lapic_existence(); 3277d36db35SAvi Kivity 3287d36db35SAvi Kivity mask_pic_interrupts(); 3297d36db35SAvi Kivity enable_apic(); 3307d36db35SAvi Kivity test_enable_x2apic(); 3317d36db35SAvi Kivity 3327d36db35SAvi Kivity test_self_ipi(); 3337d36db35SAvi Kivity 3347d36db35SAvi Kivity test_ioapic_intr(); 3357d36db35SAvi Kivity test_ioapic_simultaneous(); 336f2d2b7c7SAvi Kivity test_sti_nmi(); 337173e7eacSAvi Kivity test_multiple_nmi(); 3387d36db35SAvi Kivity 339d423ca36SLiu, Jinsong test_tsc_deadline_timer(); 340d423ca36SLiu, Jinsong 3417d36db35SAvi Kivity printf("\nsummary: %d tests, %d failures\n", g_tests, g_fail); 3427d36db35SAvi Kivity 3437d36db35SAvi Kivity return g_fail != 0; 3447d36db35SAvi Kivity } 345