1 #include "libcflat.h" 2 #include "apic.h" 3 #include "vm.h" 4 #include "smp.h" 5 #include "desc.h" 6 #include "isr.h" 7 8 static int g_fail; 9 static int g_tests; 10 11 static void report(const char *msg, int pass) 12 { 13 ++g_tests; 14 printf("%s: %s\n", msg, (pass ? "PASS" : "FAIL")); 15 if (!pass) 16 ++g_fail; 17 } 18 19 static void test_lapic_existence(void) 20 { 21 u32 lvr; 22 23 lvr = apic_read(APIC_LVR); 24 printf("apic version: %x\n", lvr); 25 report("apic existence", (u16)lvr == 0x14); 26 } 27 28 #define MSR_APIC_BASE 0x0000001b 29 30 void test_enable_x2apic(void) 31 { 32 if (enable_x2apic()) { 33 printf("x2apic enabled\n"); 34 } else { 35 printf("x2apic not detected\n"); 36 } 37 } 38 39 static void eoi(void) 40 { 41 apic_write(APIC_EOI, 0); 42 } 43 44 static int ipi_count; 45 46 static void self_ipi_isr(isr_regs_t *regs) 47 { 48 ++ipi_count; 49 eoi(); 50 } 51 52 static void test_self_ipi(void) 53 { 54 int vec = 0xf1; 55 56 handle_irq(vec, self_ipi_isr); 57 irq_enable(); 58 apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec, 59 0); 60 asm volatile ("nop"); 61 report("self ipi", ipi_count == 1); 62 } 63 64 static void set_ioapic_redir(unsigned line, unsigned vec) 65 { 66 ioapic_redir_entry_t e = { 67 .vector = vec, 68 .delivery_mode = 0, 69 .trig_mode = 0, 70 }; 71 72 ioapic_write_redir(line, e); 73 } 74 75 static void set_irq_line(unsigned line, int val) 76 { 77 asm volatile("out %0, %1" : : "a"((u8)val), "d"((u16)(0x2000 + line))); 78 } 79 80 static void toggle_irq_line(unsigned line) 81 { 82 set_irq_line(line, 1); 83 set_irq_line(line, 0); 84 } 85 86 static int g_isr_77; 87 88 static void ioapic_isr_77(isr_regs_t *regs) 89 { 90 ++g_isr_77; 91 eoi(); 92 } 93 94 static void test_ioapic_intr(void) 95 { 96 handle_irq(0x77, ioapic_isr_77); 97 set_ioapic_redir(0x10, 0x77); 98 toggle_irq_line(0x10); 99 asm volatile ("nop"); 100 report("ioapic interrupt", g_isr_77 == 1); 101 } 102 103 static int g_78, g_66, g_66_after_78; 104 static ulong g_66_rip, g_78_rip; 105 106 static void ioapic_isr_78(isr_regs_t *regs) 107 { 108 ++g_78; 109 g_78_rip = regs->rip; 110 eoi(); 111 } 112 113 static void ioapic_isr_66(isr_regs_t *regs) 114 { 115 ++g_66; 116 if (g_78) 117 ++g_66_after_78; 118 g_66_rip = regs->rip; 119 eoi(); 120 } 121 122 static void test_ioapic_simultaneous(void) 123 { 124 handle_irq(0x78, ioapic_isr_78); 125 handle_irq(0x66, ioapic_isr_66); 126 set_ioapic_redir(0x10, 0x78); 127 set_ioapic_redir(0x11, 0x66); 128 irq_disable(); 129 toggle_irq_line(0x11); 130 toggle_irq_line(0x10); 131 irq_enable(); 132 asm volatile ("nop"); 133 report("ioapic simultaneous interrupt", 134 g_66 && g_78 && g_66_after_78 && g_66_rip == g_78_rip); 135 } 136 137 volatile int nmi_counter_private, nmi_counter, nmi_hlt_counter, sti_loop_active; 138 139 void sti_nop(char *p) 140 { 141 asm volatile ( 142 ".globl post_sti \n\t" 143 "sti \n" 144 /* 145 * vmx won't exit on external interrupt if blocked-by-sti, 146 * so give it a reason to exit by accessing an unmapped page. 147 */ 148 "post_sti: testb $0, %0 \n\t" 149 "nop \n\t" 150 "cli" 151 : : "m"(*p) 152 ); 153 nmi_counter = nmi_counter_private; 154 } 155 156 static void sti_loop(void *ignore) 157 { 158 unsigned k = 0; 159 160 while (sti_loop_active) { 161 sti_nop((char *)(ulong)((k++ * 4096) % (128 * 1024 * 1024))); 162 } 163 } 164 165 static void nmi_handler(isr_regs_t *regs) 166 { 167 extern void post_sti(void); 168 ++nmi_counter_private; 169 nmi_hlt_counter += regs->rip == (ulong)post_sti; 170 } 171 172 static void update_cr3(void *cr3) 173 { 174 write_cr3((ulong)cr3); 175 } 176 177 static void test_sti_nmi(void) 178 { 179 unsigned old_counter; 180 181 if (cpu_count() < 2) { 182 return; 183 } 184 185 handle_irq(2, nmi_handler); 186 on_cpu(1, update_cr3, (void *)read_cr3()); 187 188 sti_loop_active = 1; 189 on_cpu_async(1, sti_loop, 0); 190 while (nmi_counter < 30000) { 191 old_counter = nmi_counter; 192 apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 1); 193 while (nmi_counter == old_counter) { 194 ; 195 } 196 } 197 sti_loop_active = 0; 198 report("nmi-after-sti", nmi_hlt_counter == 0); 199 } 200 201 int main() 202 { 203 setup_vm(); 204 smp_init(); 205 setup_idt(); 206 207 test_lapic_existence(); 208 209 mask_pic_interrupts(); 210 enable_apic(); 211 test_enable_x2apic(); 212 213 test_self_ipi(); 214 215 test_ioapic_intr(); 216 test_ioapic_simultaneous(); 217 test_sti_nmi(); 218 219 printf("\nsummary: %d tests, %d failures\n", g_tests, g_fail); 220 221 return g_fail != 0; 222 } 223