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