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