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 287d36db35SAvi Kivity #define MSR_APIC_BASE 0x0000001b 297d36db35SAvi Kivity 307d36db35SAvi Kivity void test_enable_x2apic(void) 317d36db35SAvi Kivity { 327d36db35SAvi Kivity if (enable_x2apic()) { 337d36db35SAvi Kivity printf("x2apic enabled\n"); 347d36db35SAvi Kivity } else { 357d36db35SAvi Kivity printf("x2apic not detected\n"); 367d36db35SAvi Kivity } 377d36db35SAvi Kivity } 387d36db35SAvi Kivity 397d36db35SAvi Kivity static void eoi(void) 407d36db35SAvi Kivity { 417d36db35SAvi Kivity apic_write(APIC_EOI, 0); 427d36db35SAvi Kivity } 437d36db35SAvi Kivity 447d36db35SAvi Kivity static int ipi_count; 457d36db35SAvi Kivity 467d36db35SAvi Kivity static void self_ipi_isr(isr_regs_t *regs) 477d36db35SAvi Kivity { 487d36db35SAvi Kivity ++ipi_count; 497d36db35SAvi Kivity eoi(); 507d36db35SAvi Kivity } 517d36db35SAvi Kivity 527d36db35SAvi Kivity static void test_self_ipi(void) 537d36db35SAvi Kivity { 547d36db35SAvi Kivity int vec = 0xf1; 557d36db35SAvi Kivity 56d51bd17eSGleb Natapov handle_irq(vec, self_ipi_isr); 577d36db35SAvi Kivity irq_enable(); 587d36db35SAvi Kivity apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec, 597d36db35SAvi Kivity 0); 607d36db35SAvi Kivity asm volatile ("nop"); 617d36db35SAvi Kivity report("self ipi", ipi_count == 1); 627d36db35SAvi Kivity } 637d36db35SAvi Kivity 647d36db35SAvi Kivity static void set_ioapic_redir(unsigned line, unsigned vec) 657d36db35SAvi Kivity { 667d36db35SAvi Kivity ioapic_redir_entry_t e = { 677d36db35SAvi Kivity .vector = vec, 687d36db35SAvi Kivity .delivery_mode = 0, 697d36db35SAvi Kivity .trig_mode = 0, 707d36db35SAvi Kivity }; 717d36db35SAvi Kivity 727d36db35SAvi Kivity ioapic_write_redir(line, e); 737d36db35SAvi Kivity } 747d36db35SAvi Kivity 757d36db35SAvi Kivity static void set_irq_line(unsigned line, int val) 767d36db35SAvi Kivity { 777d36db35SAvi Kivity asm volatile("out %0, %1" : : "a"((u8)val), "d"((u16)(0x2000 + line))); 787d36db35SAvi Kivity } 797d36db35SAvi Kivity 807d36db35SAvi Kivity static void toggle_irq_line(unsigned line) 817d36db35SAvi Kivity { 827d36db35SAvi Kivity set_irq_line(line, 1); 837d36db35SAvi Kivity set_irq_line(line, 0); 847d36db35SAvi Kivity } 857d36db35SAvi Kivity 867d36db35SAvi Kivity static int g_isr_77; 877d36db35SAvi Kivity 887d36db35SAvi Kivity static void ioapic_isr_77(isr_regs_t *regs) 897d36db35SAvi Kivity { 907d36db35SAvi Kivity ++g_isr_77; 917d36db35SAvi Kivity eoi(); 927d36db35SAvi Kivity } 937d36db35SAvi Kivity 947d36db35SAvi Kivity static void test_ioapic_intr(void) 957d36db35SAvi Kivity { 96d51bd17eSGleb Natapov handle_irq(0x77, ioapic_isr_77); 977d36db35SAvi Kivity set_ioapic_redir(0x10, 0x77); 987d36db35SAvi Kivity toggle_irq_line(0x10); 997d36db35SAvi Kivity asm volatile ("nop"); 1007d36db35SAvi Kivity report("ioapic interrupt", g_isr_77 == 1); 1017d36db35SAvi Kivity } 1027d36db35SAvi Kivity 1037d36db35SAvi Kivity static int g_78, g_66, g_66_after_78; 1047d36db35SAvi Kivity static ulong g_66_rip, g_78_rip; 1057d36db35SAvi Kivity 1067d36db35SAvi Kivity static void ioapic_isr_78(isr_regs_t *regs) 1077d36db35SAvi Kivity { 1087d36db35SAvi Kivity ++g_78; 1097d36db35SAvi Kivity g_78_rip = regs->rip; 1107d36db35SAvi Kivity eoi(); 1117d36db35SAvi Kivity } 1127d36db35SAvi Kivity 1137d36db35SAvi Kivity static void ioapic_isr_66(isr_regs_t *regs) 1147d36db35SAvi Kivity { 1157d36db35SAvi Kivity ++g_66; 1167d36db35SAvi Kivity if (g_78) 1177d36db35SAvi Kivity ++g_66_after_78; 1187d36db35SAvi Kivity g_66_rip = regs->rip; 1197d36db35SAvi Kivity eoi(); 1207d36db35SAvi Kivity } 1217d36db35SAvi Kivity 1227d36db35SAvi Kivity static void test_ioapic_simultaneous(void) 1237d36db35SAvi Kivity { 124d51bd17eSGleb Natapov handle_irq(0x78, ioapic_isr_78); 125d51bd17eSGleb Natapov handle_irq(0x66, ioapic_isr_66); 1267d36db35SAvi Kivity set_ioapic_redir(0x10, 0x78); 1277d36db35SAvi Kivity set_ioapic_redir(0x11, 0x66); 1287d36db35SAvi Kivity irq_disable(); 1297d36db35SAvi Kivity toggle_irq_line(0x11); 1307d36db35SAvi Kivity toggle_irq_line(0x10); 1317d36db35SAvi Kivity irq_enable(); 1327d36db35SAvi Kivity asm volatile ("nop"); 1337d36db35SAvi Kivity report("ioapic simultaneous interrupt", 1347d36db35SAvi Kivity g_66 && g_78 && g_66_after_78 && g_66_rip == g_78_rip); 1357d36db35SAvi Kivity } 1367d36db35SAvi Kivity 137f2d2b7c7SAvi Kivity volatile int nmi_counter_private, nmi_counter, nmi_hlt_counter, sti_loop_active; 138f2d2b7c7SAvi Kivity 139f2d2b7c7SAvi Kivity void sti_nop(char *p) 140f2d2b7c7SAvi Kivity { 141f2d2b7c7SAvi Kivity asm volatile ( 142f2d2b7c7SAvi Kivity ".globl post_sti \n\t" 143f2d2b7c7SAvi Kivity "sti \n" 144f2d2b7c7SAvi Kivity /* 145f2d2b7c7SAvi Kivity * vmx won't exit on external interrupt if blocked-by-sti, 146f2d2b7c7SAvi Kivity * so give it a reason to exit by accessing an unmapped page. 147f2d2b7c7SAvi Kivity */ 148f2d2b7c7SAvi Kivity "post_sti: testb $0, %0 \n\t" 149f2d2b7c7SAvi Kivity "nop \n\t" 150f2d2b7c7SAvi Kivity "cli" 151f2d2b7c7SAvi Kivity : : "m"(*p) 152f2d2b7c7SAvi Kivity ); 153f2d2b7c7SAvi Kivity nmi_counter = nmi_counter_private; 154f2d2b7c7SAvi Kivity } 155f2d2b7c7SAvi Kivity 156f2d2b7c7SAvi Kivity static void sti_loop(void *ignore) 157f2d2b7c7SAvi Kivity { 158f2d2b7c7SAvi Kivity unsigned k = 0; 159f2d2b7c7SAvi Kivity 160f2d2b7c7SAvi Kivity while (sti_loop_active) { 161f2d2b7c7SAvi Kivity sti_nop((char *)(ulong)((k++ * 4096) % (128 * 1024 * 1024))); 162f2d2b7c7SAvi Kivity } 163f2d2b7c7SAvi Kivity } 164f2d2b7c7SAvi Kivity 165f2d2b7c7SAvi Kivity static void nmi_handler(isr_regs_t *regs) 166f2d2b7c7SAvi Kivity { 167f2d2b7c7SAvi Kivity extern void post_sti(void); 168f2d2b7c7SAvi Kivity ++nmi_counter_private; 169f2d2b7c7SAvi Kivity nmi_hlt_counter += regs->rip == (ulong)post_sti; 170f2d2b7c7SAvi Kivity } 171f2d2b7c7SAvi Kivity 172f2d2b7c7SAvi Kivity static void update_cr3(void *cr3) 173f2d2b7c7SAvi Kivity { 174f2d2b7c7SAvi Kivity write_cr3((ulong)cr3); 175f2d2b7c7SAvi Kivity } 176f2d2b7c7SAvi Kivity 177f2d2b7c7SAvi Kivity static void test_sti_nmi(void) 178f2d2b7c7SAvi Kivity { 179f2d2b7c7SAvi Kivity unsigned old_counter; 180f2d2b7c7SAvi Kivity 181f2d2b7c7SAvi Kivity if (cpu_count() < 2) { 182f2d2b7c7SAvi Kivity return; 183f2d2b7c7SAvi Kivity } 184f2d2b7c7SAvi Kivity 185d51bd17eSGleb Natapov handle_irq(2, nmi_handler); 186f2d2b7c7SAvi Kivity on_cpu(1, update_cr3, (void *)read_cr3()); 187f2d2b7c7SAvi Kivity 188f2d2b7c7SAvi Kivity sti_loop_active = 1; 189f2d2b7c7SAvi Kivity on_cpu_async(1, sti_loop, 0); 190f2d2b7c7SAvi Kivity while (nmi_counter < 30000) { 191f2d2b7c7SAvi Kivity old_counter = nmi_counter; 192f2d2b7c7SAvi Kivity apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 1); 193f2d2b7c7SAvi Kivity while (nmi_counter == old_counter) { 194f2d2b7c7SAvi Kivity ; 195f2d2b7c7SAvi Kivity } 196f2d2b7c7SAvi Kivity } 197f2d2b7c7SAvi Kivity sti_loop_active = 0; 198f2d2b7c7SAvi Kivity report("nmi-after-sti", nmi_hlt_counter == 0); 199f2d2b7c7SAvi Kivity } 200f2d2b7c7SAvi Kivity 201*173e7eacSAvi Kivity static volatile bool nmi_done, nmi_flushed; 202*173e7eacSAvi Kivity static volatile int nmi_received; 203*173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr1, cpu1_nmi_ctr1; 204*173e7eacSAvi Kivity static volatile int cpu0_nmi_ctr2, cpu1_nmi_ctr2; 205*173e7eacSAvi Kivity 206*173e7eacSAvi Kivity static void multiple_nmi_handler(isr_regs_t *regs) 207*173e7eacSAvi Kivity { 208*173e7eacSAvi Kivity ++nmi_received; 209*173e7eacSAvi Kivity } 210*173e7eacSAvi Kivity 211*173e7eacSAvi Kivity static void kick_me_nmi(void *blah) 212*173e7eacSAvi Kivity { 213*173e7eacSAvi Kivity while (!nmi_done) { 214*173e7eacSAvi Kivity ++cpu1_nmi_ctr1; 215*173e7eacSAvi Kivity while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1 && !nmi_done) { 216*173e7eacSAvi Kivity pause(); 217*173e7eacSAvi Kivity } 218*173e7eacSAvi Kivity if (nmi_done) { 219*173e7eacSAvi Kivity return; 220*173e7eacSAvi Kivity } 221*173e7eacSAvi Kivity apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0); 222*173e7eacSAvi Kivity /* make sure the NMI has arrived by sending an IPI after it */ 223*173e7eacSAvi Kivity apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT 224*173e7eacSAvi Kivity | 0x44, 0); 225*173e7eacSAvi Kivity ++cpu1_nmi_ctr2; 226*173e7eacSAvi Kivity while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2 && !nmi_done) { 227*173e7eacSAvi Kivity pause(); 228*173e7eacSAvi Kivity } 229*173e7eacSAvi Kivity } 230*173e7eacSAvi Kivity } 231*173e7eacSAvi Kivity 232*173e7eacSAvi Kivity static void flush_nmi(isr_regs_t *regs) 233*173e7eacSAvi Kivity { 234*173e7eacSAvi Kivity nmi_flushed = true; 235*173e7eacSAvi Kivity apic_write(APIC_EOI, 0); 236*173e7eacSAvi Kivity } 237*173e7eacSAvi Kivity 238*173e7eacSAvi Kivity static void test_multiple_nmi(void) 239*173e7eacSAvi Kivity { 240*173e7eacSAvi Kivity int i; 241*173e7eacSAvi Kivity bool ok = true; 242*173e7eacSAvi Kivity 243*173e7eacSAvi Kivity if (cpu_count() < 2) { 244*173e7eacSAvi Kivity return; 245*173e7eacSAvi Kivity } 246*173e7eacSAvi Kivity 247*173e7eacSAvi Kivity sti(); 248*173e7eacSAvi Kivity handle_irq(2, multiple_nmi_handler); 249*173e7eacSAvi Kivity handle_irq(0x44, flush_nmi); 250*173e7eacSAvi Kivity on_cpu_async(1, kick_me_nmi, 0); 251*173e7eacSAvi Kivity for (i = 0; i < 1000000; ++i) { 252*173e7eacSAvi Kivity nmi_flushed = false; 253*173e7eacSAvi Kivity nmi_received = 0; 254*173e7eacSAvi Kivity ++cpu0_nmi_ctr1; 255*173e7eacSAvi Kivity while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1) { 256*173e7eacSAvi Kivity pause(); 257*173e7eacSAvi Kivity } 258*173e7eacSAvi Kivity apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0); 259*173e7eacSAvi Kivity while (!nmi_flushed) { 260*173e7eacSAvi Kivity pause(); 261*173e7eacSAvi Kivity } 262*173e7eacSAvi Kivity if (nmi_received != 2) { 263*173e7eacSAvi Kivity ok = false; 264*173e7eacSAvi Kivity break; 265*173e7eacSAvi Kivity } 266*173e7eacSAvi Kivity ++cpu0_nmi_ctr2; 267*173e7eacSAvi Kivity while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2) { 268*173e7eacSAvi Kivity pause(); 269*173e7eacSAvi Kivity } 270*173e7eacSAvi Kivity } 271*173e7eacSAvi Kivity nmi_done = true; 272*173e7eacSAvi Kivity report("multiple nmi", ok); 273*173e7eacSAvi Kivity } 274*173e7eacSAvi Kivity 2757d36db35SAvi Kivity int main() 2767d36db35SAvi Kivity { 2777d36db35SAvi Kivity setup_vm(); 278f2d2b7c7SAvi Kivity smp_init(); 279d51bd17eSGleb Natapov setup_idt(); 2807d36db35SAvi Kivity 2817d36db35SAvi Kivity test_lapic_existence(); 2827d36db35SAvi Kivity 2837d36db35SAvi Kivity mask_pic_interrupts(); 2847d36db35SAvi Kivity enable_apic(); 2857d36db35SAvi Kivity test_enable_x2apic(); 2867d36db35SAvi Kivity 2877d36db35SAvi Kivity test_self_ipi(); 2887d36db35SAvi Kivity 2897d36db35SAvi Kivity test_ioapic_intr(); 2907d36db35SAvi Kivity test_ioapic_simultaneous(); 291f2d2b7c7SAvi Kivity test_sti_nmi(); 292*173e7eacSAvi Kivity test_multiple_nmi(); 2937d36db35SAvi Kivity 2947d36db35SAvi Kivity printf("\nsummary: %d tests, %d failures\n", g_tests, g_fail); 2957d36db35SAvi Kivity 2967d36db35SAvi Kivity return g_fail != 0; 2977d36db35SAvi Kivity } 298