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 #include "msr.h" 8 9 static void test_lapic_existence(void) 10 { 11 u32 lvr; 12 13 lvr = apic_read(APIC_LVR); 14 printf("apic version: %x\n", lvr); 15 report("apic existence", (u16)lvr == 0x14); 16 } 17 18 #define TSC_DEADLINE_TIMER_MODE (2 << 17) 19 #define TSC_DEADLINE_TIMER_VECTOR 0xef 20 #define MSR_IA32_TSC 0x00000010 21 #define MSR_IA32_TSCDEADLINE 0x000006e0 22 23 static int tdt_count; 24 25 static void tsc_deadline_timer_isr(isr_regs_t *regs) 26 { 27 ++tdt_count; 28 } 29 30 static void __test_tsc_deadline_timer(void) 31 { 32 handle_irq(TSC_DEADLINE_TIMER_VECTOR, tsc_deadline_timer_isr); 33 irq_enable(); 34 35 wrmsr(MSR_IA32_TSCDEADLINE, rdmsr(MSR_IA32_TSC)); 36 asm volatile ("nop"); 37 report("tsc deadline timer", tdt_count == 1); 38 report("tsc deadline timer clearing", rdmsr(MSR_IA32_TSCDEADLINE) == 0); 39 } 40 41 static int enable_tsc_deadline_timer(void) 42 { 43 uint32_t lvtt; 44 45 if (cpuid(1).c & (1 << 24)) { 46 lvtt = TSC_DEADLINE_TIMER_MODE | TSC_DEADLINE_TIMER_VECTOR; 47 apic_write(APIC_LVTT, lvtt); 48 return 1; 49 } else { 50 return 0; 51 } 52 } 53 54 static void test_tsc_deadline_timer(void) 55 { 56 if(enable_tsc_deadline_timer()) { 57 __test_tsc_deadline_timer(); 58 } else { 59 report_skip("tsc deadline timer not detected"); 60 } 61 } 62 63 static void do_write_apicbase(void *data) 64 { 65 wrmsr(MSR_IA32_APICBASE, *(u64 *)data); 66 } 67 68 void test_enable_x2apic(void) 69 { 70 u64 invalid_state = APIC_DEFAULT_PHYS_BASE | APIC_BSP | APIC_EXTD; 71 u64 apic_enabled = APIC_DEFAULT_PHYS_BASE | APIC_BSP | APIC_EN; 72 u64 x2apic_enabled = 73 APIC_DEFAULT_PHYS_BASE | APIC_BSP | APIC_EN | APIC_EXTD; 74 75 if (enable_x2apic()) { 76 printf("x2apic enabled\n"); 77 78 report("x2apic enabled to invalid state", 79 test_for_exception(GP_VECTOR, do_write_apicbase, 80 &invalid_state)); 81 report("x2apic enabled to apic enabled", 82 test_for_exception(GP_VECTOR, do_write_apicbase, 83 &apic_enabled)); 84 85 wrmsr(MSR_IA32_APICBASE, APIC_DEFAULT_PHYS_BASE | APIC_BSP); 86 report("disabled to invalid state", 87 test_for_exception(GP_VECTOR, do_write_apicbase, 88 &invalid_state)); 89 report("disabled to x2apic enabled", 90 test_for_exception(GP_VECTOR, do_write_apicbase, 91 &x2apic_enabled)); 92 93 wrmsr(MSR_IA32_APICBASE, apic_enabled); 94 report("apic enabled to invalid state", 95 test_for_exception(GP_VECTOR, do_write_apicbase, 96 &invalid_state)); 97 98 wrmsr(MSR_IA32_APICBASE, x2apic_enabled); 99 apic_write(APIC_SPIV, 0x1ff); 100 } else { 101 printf("x2apic not detected\n"); 102 103 report("enable unsupported x2apic", 104 test_for_exception(GP_VECTOR, do_write_apicbase, 105 &x2apic_enabled)); 106 } 107 } 108 109 #define ALTERNATE_APIC_BASE 0x42000000 110 111 static void test_apicbase(void) 112 { 113 u64 orig_apicbase = rdmsr(MSR_IA32_APICBASE); 114 u32 lvr = apic_read(APIC_LVR); 115 u64 value; 116 117 wrmsr(MSR_IA32_APICBASE, orig_apicbase & ~(APIC_EN | APIC_EXTD)); 118 wrmsr(MSR_IA32_APICBASE, ALTERNATE_APIC_BASE | APIC_BSP | APIC_EN); 119 120 report_prefix_push("apicbase"); 121 122 report("relocate apic", 123 *(volatile u32 *)(ALTERNATE_APIC_BASE + APIC_LVR) == lvr); 124 125 value = orig_apicbase | (1UL << cpuid_maxphyaddr()); 126 report("reserved physaddr bits", 127 test_for_exception(GP_VECTOR, do_write_apicbase, &value)); 128 129 value = orig_apicbase | 1; 130 report("reserved low bits", 131 test_for_exception(GP_VECTOR, do_write_apicbase, &value)); 132 133 wrmsr(MSR_IA32_APICBASE, orig_apicbase); 134 apic_write(APIC_SPIV, 0x1ff); 135 136 report_prefix_pop(); 137 } 138 139 static int ipi_count; 140 141 static void self_ipi_isr(isr_regs_t *regs) 142 { 143 ++ipi_count; 144 eoi(); 145 } 146 147 static void test_self_ipi(void) 148 { 149 int vec = 0xf1; 150 151 handle_irq(vec, self_ipi_isr); 152 irq_enable(); 153 apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec, 154 0); 155 asm volatile ("nop"); 156 report("self ipi", ipi_count == 1); 157 } 158 159 volatile int nmi_counter_private, nmi_counter, nmi_hlt_counter, sti_loop_active; 160 161 void sti_nop(char *p) 162 { 163 asm volatile ( 164 ".globl post_sti \n\t" 165 "sti \n" 166 /* 167 * vmx won't exit on external interrupt if blocked-by-sti, 168 * so give it a reason to exit by accessing an unmapped page. 169 */ 170 "post_sti: testb $0, %0 \n\t" 171 "nop \n\t" 172 "cli" 173 : : "m"(*p) 174 ); 175 nmi_counter = nmi_counter_private; 176 } 177 178 static void sti_loop(void *ignore) 179 { 180 unsigned k = 0; 181 182 while (sti_loop_active) { 183 sti_nop((char *)(ulong)((k++ * 4096) % (128 * 1024 * 1024))); 184 } 185 } 186 187 static void nmi_handler(isr_regs_t *regs) 188 { 189 extern void post_sti(void); 190 ++nmi_counter_private; 191 nmi_hlt_counter += regs->rip == (ulong)post_sti; 192 } 193 194 static void update_cr3(void *cr3) 195 { 196 write_cr3((ulong)cr3); 197 } 198 199 static void test_sti_nmi(void) 200 { 201 unsigned old_counter; 202 203 if (cpu_count() < 2) { 204 return; 205 } 206 207 handle_irq(2, nmi_handler); 208 on_cpu(1, update_cr3, (void *)read_cr3()); 209 210 sti_loop_active = 1; 211 on_cpu_async(1, sti_loop, 0); 212 while (nmi_counter < 30000) { 213 old_counter = nmi_counter; 214 apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 1); 215 while (nmi_counter == old_counter) { 216 ; 217 } 218 } 219 sti_loop_active = 0; 220 report("nmi-after-sti", nmi_hlt_counter == 0); 221 } 222 223 static volatile bool nmi_done, nmi_flushed; 224 static volatile int nmi_received; 225 static volatile int cpu0_nmi_ctr1, cpu1_nmi_ctr1; 226 static volatile int cpu0_nmi_ctr2, cpu1_nmi_ctr2; 227 228 static void multiple_nmi_handler(isr_regs_t *regs) 229 { 230 ++nmi_received; 231 } 232 233 static void kick_me_nmi(void *blah) 234 { 235 while (!nmi_done) { 236 ++cpu1_nmi_ctr1; 237 while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1 && !nmi_done) { 238 pause(); 239 } 240 if (nmi_done) { 241 return; 242 } 243 apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0); 244 /* make sure the NMI has arrived by sending an IPI after it */ 245 apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT 246 | 0x44, 0); 247 ++cpu1_nmi_ctr2; 248 while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2 && !nmi_done) { 249 pause(); 250 } 251 } 252 } 253 254 static void flush_nmi(isr_regs_t *regs) 255 { 256 nmi_flushed = true; 257 apic_write(APIC_EOI, 0); 258 } 259 260 static void test_multiple_nmi(void) 261 { 262 int i; 263 bool ok = true; 264 265 if (cpu_count() < 2) { 266 return; 267 } 268 269 sti(); 270 handle_irq(2, multiple_nmi_handler); 271 handle_irq(0x44, flush_nmi); 272 on_cpu_async(1, kick_me_nmi, 0); 273 for (i = 0; i < 1000000; ++i) { 274 nmi_flushed = false; 275 nmi_received = 0; 276 ++cpu0_nmi_ctr1; 277 while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1) { 278 pause(); 279 } 280 apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0); 281 while (!nmi_flushed) { 282 pause(); 283 } 284 if (nmi_received != 2) { 285 ok = false; 286 break; 287 } 288 ++cpu0_nmi_ctr2; 289 while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2) { 290 pause(); 291 } 292 } 293 nmi_done = true; 294 report("multiple nmi", ok); 295 } 296 297 int main() 298 { 299 setup_vm(); 300 smp_init(); 301 setup_idt(); 302 303 test_lapic_existence(); 304 305 mask_pic_interrupts(); 306 test_enable_x2apic(); 307 test_apicbase(); 308 309 test_self_ipi(); 310 311 test_sti_nmi(); 312 test_multiple_nmi(); 313 314 test_tsc_deadline_timer(); 315 316 return report_summary(); 317 } 318