1 #include "libcflat.h" 2 #include "apic.h" 3 #include "vm.h" 4 5 typedef struct { 6 unsigned short offset0; 7 unsigned short selector; 8 unsigned short ist : 3; 9 unsigned short : 5; 10 unsigned short type : 4; 11 unsigned short : 1; 12 unsigned short dpl : 2; 13 unsigned short p : 1; 14 unsigned short offset1; 15 #ifdef __x86_64__ 16 unsigned offset2; 17 unsigned reserved; 18 #endif 19 } idt_entry_t; 20 21 typedef struct { 22 ulong regs[sizeof(ulong)*2]; 23 ulong func; 24 ulong rip; 25 ulong cs; 26 ulong rflags; 27 } isr_regs_t; 28 29 #ifdef __x86_64__ 30 # define R "r" 31 #else 32 # define R "e" 33 #endif 34 35 extern char isr_entry_point[]; 36 37 asm ( 38 "isr_entry_point: \n" 39 #ifdef __x86_64__ 40 "push %r15 \n\t" 41 "push %r14 \n\t" 42 "push %r13 \n\t" 43 "push %r12 \n\t" 44 "push %r11 \n\t" 45 "push %r10 \n\t" 46 "push %r9 \n\t" 47 "push %r8 \n\t" 48 #endif 49 "push %"R "di \n\t" 50 "push %"R "si \n\t" 51 "push %"R "bp \n\t" 52 "push %"R "sp \n\t" 53 "push %"R "bx \n\t" 54 "push %"R "dx \n\t" 55 "push %"R "cx \n\t" 56 "push %"R "ax \n\t" 57 #ifdef __x86_64__ 58 "mov %rsp, %rdi \n\t" 59 "callq *8*16(%rsp) \n\t" 60 #else 61 "push %esp \n\t" 62 "calll *4+4*8(%esp) \n\t" 63 "add $4, %esp \n\t" 64 #endif 65 "pop %"R "ax \n\t" 66 "pop %"R "cx \n\t" 67 "pop %"R "dx \n\t" 68 "pop %"R "bx \n\t" 69 "pop %"R "bp \n\t" 70 "pop %"R "bp \n\t" 71 "pop %"R "si \n\t" 72 "pop %"R "di \n\t" 73 #ifdef __x86_64__ 74 "pop %r8 \n\t" 75 "pop %r9 \n\t" 76 "pop %r10 \n\t" 77 "pop %r11 \n\t" 78 "pop %r12 \n\t" 79 "pop %r13 \n\t" 80 "pop %r14 \n\t" 81 "pop %r15 \n\t" 82 #endif 83 #ifdef __x86_64__ 84 "add $8, %rsp \n\t" 85 "iretq \n\t" 86 #else 87 "add $4, %esp \n\t" 88 "iretl \n\t" 89 #endif 90 ); 91 92 static idt_entry_t idt[256]; 93 94 static int g_fail; 95 static int g_tests; 96 97 static void outb(unsigned char data, unsigned short port) 98 { 99 asm volatile ("out %0, %1" : : "a"(data), "d"(port)); 100 } 101 102 static void report(const char *msg, int pass) 103 { 104 ++g_tests; 105 printf("%s: %s\n", msg, (pass ? "PASS" : "FAIL")); 106 if (!pass) 107 ++g_fail; 108 } 109 110 static void test_lapic_existence(void) 111 { 112 u32 lvr; 113 114 lvr = apic_read(APIC_LVR); 115 printf("apic version: %x\n", lvr); 116 report("apic existence", (u16)lvr == 0x14); 117 } 118 119 #define MSR_APIC_BASE 0x0000001b 120 121 void test_enable_x2apic(void) 122 { 123 if (enable_x2apic()) { 124 printf("x2apic enabled\n"); 125 } else { 126 printf("x2apic not detected\n"); 127 } 128 } 129 130 static void init_idt(void) 131 { 132 struct { 133 u16 limit; 134 ulong idt; 135 } __attribute__((packed)) idt_ptr = { 136 sizeof(idt_entry_t) * 256 - 1, 137 (ulong)&idt, 138 }; 139 140 asm volatile("lidt %0" : : "m"(idt_ptr)); 141 } 142 143 static void set_idt_entry(unsigned vec, void (*func)(isr_regs_t *regs)) 144 { 145 u8 *thunk = vmalloc(50); 146 ulong ptr = (ulong)thunk; 147 idt_entry_t ent = { 148 .offset0 = ptr, 149 .selector = read_cs(), 150 .ist = 0, 151 .type = 14, 152 .dpl = 0, 153 .p = 1, 154 .offset1 = ptr >> 16, 155 #ifdef __x86_64__ 156 .offset2 = ptr >> 32, 157 #endif 158 }; 159 #ifdef __x86_64__ 160 /* sub $8, %rsp */ 161 *thunk++ = 0x48; *thunk++ = 0x83; *thunk++ = 0xec; *thunk++ = 0x08; 162 /* mov $func_low, %(rsp) */ 163 *thunk++ = 0xc7; *thunk++ = 0x04; *thunk++ = 0x24; 164 *(u32 *)thunk = (ulong)func; thunk += 4; 165 /* mov $func_high, %(rsp+4) */ 166 *thunk++ = 0xc7; *thunk++ = 0x44; *thunk++ = 0x24; *thunk++ = 0x04; 167 *(u32 *)thunk = (ulong)func >> 32; thunk += 4; 168 /* jmp isr_entry_point */ 169 *thunk ++ = 0xe9; 170 *(u32 *)thunk = (ulong)isr_entry_point - (ulong)(thunk + 4); 171 #else 172 /* push $func */ 173 *thunk++ = 0x68; 174 *(u32 *)thunk = (ulong)func; 175 /* jmp isr_entry_point */ 176 *thunk ++ = 0xe9; 177 *(u32 *)thunk = (ulong)isr_entry_point - (ulong)(thunk + 4); 178 #endif 179 idt[vec] = ent; 180 } 181 182 static void irq_disable(void) 183 { 184 asm volatile("cli"); 185 } 186 187 static void irq_enable(void) 188 { 189 asm volatile("sti"); 190 } 191 192 static void eoi(void) 193 { 194 apic_write(APIC_EOI, 0); 195 } 196 197 static int ipi_count; 198 199 static void self_ipi_isr(isr_regs_t *regs) 200 { 201 ++ipi_count; 202 eoi(); 203 } 204 205 static void test_self_ipi(void) 206 { 207 int vec = 0xf1; 208 209 set_idt_entry(vec, self_ipi_isr); 210 irq_enable(); 211 apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec, 212 0); 213 asm volatile ("nop"); 214 report("self ipi", ipi_count == 1); 215 } 216 217 static void set_ioapic_redir(unsigned line, unsigned vec) 218 { 219 ioapic_redir_entry_t e = { 220 .vector = vec, 221 .delivery_mode = 0, 222 .trig_mode = 0, 223 }; 224 225 ioapic_write_redir(line, e); 226 } 227 228 static void set_irq_line(unsigned line, int val) 229 { 230 asm volatile("out %0, %1" : : "a"((u8)val), "d"((u16)(0x2000 + line))); 231 } 232 233 static void toggle_irq_line(unsigned line) 234 { 235 set_irq_line(line, 1); 236 set_irq_line(line, 0); 237 } 238 239 static int g_isr_77; 240 241 static void ioapic_isr_77(isr_regs_t *regs) 242 { 243 ++g_isr_77; 244 eoi(); 245 } 246 247 static void test_ioapic_intr(void) 248 { 249 set_idt_entry(0x77, ioapic_isr_77); 250 set_ioapic_redir(0x10, 0x77); 251 toggle_irq_line(0x10); 252 asm volatile ("nop"); 253 report("ioapic interrupt", g_isr_77 == 1); 254 } 255 256 static int g_78, g_66, g_66_after_78; 257 static ulong g_66_rip, g_78_rip; 258 259 static void ioapic_isr_78(isr_regs_t *regs) 260 { 261 ++g_78; 262 g_78_rip = regs->rip; 263 eoi(); 264 } 265 266 static void ioapic_isr_66(isr_regs_t *regs) 267 { 268 ++g_66; 269 if (g_78) 270 ++g_66_after_78; 271 g_66_rip = regs->rip; 272 eoi(); 273 } 274 275 static void test_ioapic_simultaneous(void) 276 { 277 set_idt_entry(0x78, ioapic_isr_78); 278 set_idt_entry(0x66, ioapic_isr_66); 279 set_ioapic_redir(0x10, 0x78); 280 set_ioapic_redir(0x11, 0x66); 281 irq_disable(); 282 toggle_irq_line(0x11); 283 toggle_irq_line(0x10); 284 irq_enable(); 285 asm volatile ("nop"); 286 report("ioapic simultaneous interrupt", 287 g_66 && g_78 && g_66_after_78 && g_66_rip == g_78_rip); 288 } 289 290 int main() 291 { 292 setup_vm(); 293 294 test_lapic_existence(); 295 296 mask_pic_interrupts(); 297 enable_apic(); 298 test_enable_x2apic(); 299 init_idt(); 300 301 test_self_ipi(); 302 303 test_ioapic_intr(); 304 test_ioapic_simultaneous(); 305 306 printf("\nsummary: %d tests, %d failures\n", g_tests, g_fail); 307 308 return g_fail != 0; 309 } 310