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 #define EDGE_TRIGGERED 0 9 #define LEVEL_TRIGGERED 1 10 11 static void set_ioapic_redir(unsigned line, unsigned vec, unsigned trig_mode) 12 { 13 ioapic_redir_entry_t e = { 14 .vector = vec, 15 .delivery_mode = 0, 16 .trig_mode = trig_mode, 17 }; 18 19 ioapic_write_redir(line, e); 20 } 21 22 static void set_irq_line(unsigned line, int val) 23 { 24 asm volatile("out %0, %1" : : "a"((u8)val), "d"((u16)(0x2000 + line))); 25 } 26 27 static void toggle_irq_line(unsigned line) 28 { 29 set_irq_line(line, 1); 30 set_irq_line(line, 0); 31 } 32 33 static void ioapic_reg_version(void) 34 { 35 u8 version_offset; 36 uint32_t data_read, data_write; 37 38 version_offset = 0x01; 39 data_read = ioapic_read_reg(version_offset); 40 data_write = data_read ^ 0xffffffff; 41 42 ioapic_write_reg(version_offset, data_write); 43 report("version register read only test", 44 data_read == ioapic_read_reg(version_offset)); 45 } 46 47 static void ioapic_reg_id(void) 48 { 49 u8 id_offset; 50 uint32_t data_read, data_write, diff; 51 52 id_offset = 0x0; 53 data_read = ioapic_read_reg(id_offset); 54 data_write = data_read ^ 0xffffffff; 55 56 ioapic_write_reg(id_offset, data_write); 57 58 diff = data_read ^ ioapic_read_reg(id_offset); 59 report("id register only bits [24:27] writable", 60 diff == 0x0f000000); 61 } 62 63 static void ioapic_arbitration_id(void) 64 { 65 u8 id_offset, arb_offset; 66 uint32_t write; 67 68 id_offset = 0x0; 69 arb_offset = 0x2; 70 write = 0x0f000000; 71 72 ioapic_write_reg(id_offset, write); 73 report("arbitration register set by id", 74 ioapic_read_reg(arb_offset) == write); 75 76 ioapic_write_reg(arb_offset, 0x0); 77 report("arbtration register read only", 78 ioapic_read_reg(arb_offset) == write); 79 } 80 81 static volatile int g_isr_76; 82 83 static void ioapic_isr_76(isr_regs_t *regs) 84 { 85 ++g_isr_76; 86 eoi(); 87 } 88 89 static void test_ioapic_edge_intr(void) 90 { 91 handle_irq(0x76, ioapic_isr_76); 92 set_ioapic_redir(0x0e, 0x76, EDGE_TRIGGERED); 93 toggle_irq_line(0x0e); 94 asm volatile ("nop"); 95 report("edge triggered intr", g_isr_76 == 1); 96 } 97 98 static volatile int g_isr_77; 99 100 static void ioapic_isr_77(isr_regs_t *regs) 101 { 102 ++g_isr_77; 103 set_irq_line(0x0e, 0); 104 eoi(); 105 } 106 107 static void test_ioapic_level_intr(void) 108 { 109 handle_irq(0x77, ioapic_isr_77); 110 set_ioapic_redir(0x0e, 0x77, LEVEL_TRIGGERED); 111 set_irq_line(0x0e, 1); 112 asm volatile ("nop"); 113 report("level triggered intr", g_isr_77 == 1); 114 } 115 116 static int g_78, g_66, g_66_after_78; 117 static ulong g_66_rip, g_78_rip; 118 119 static void ioapic_isr_78(isr_regs_t *regs) 120 { 121 ++g_78; 122 g_78_rip = regs->rip; 123 eoi(); 124 } 125 126 static void ioapic_isr_66(isr_regs_t *regs) 127 { 128 ++g_66; 129 if (g_78) 130 ++g_66_after_78; 131 g_66_rip = regs->rip; 132 eoi(); 133 } 134 135 static void test_ioapic_simultaneous(void) 136 { 137 handle_irq(0x78, ioapic_isr_78); 138 handle_irq(0x66, ioapic_isr_66); 139 set_ioapic_redir(0x0e, 0x78, EDGE_TRIGGERED); 140 set_ioapic_redir(0x0f, 0x66, EDGE_TRIGGERED); 141 irq_disable(); 142 toggle_irq_line(0x0f); 143 toggle_irq_line(0x0e); 144 irq_enable(); 145 asm volatile ("nop"); 146 report("ioapic simultaneous edge interrupts", 147 g_66 && g_78 && g_66_after_78 && g_66_rip == g_78_rip); 148 } 149 150 static int g_isr_98; 151 152 static void ioapic_isr_98(isr_regs_t *regs) 153 { 154 ++g_isr_98; 155 if (g_isr_98 == 1) { 156 set_irq_line(0x0e, 0); 157 set_irq_line(0x0e, 1); 158 } 159 set_irq_line(0x0e, 0); 160 eoi(); 161 } 162 163 static void test_ioapic_level_coalesce(void) 164 { 165 handle_irq(0x98, ioapic_isr_98); 166 set_ioapic_redir(0x0e, 0x98, LEVEL_TRIGGERED); 167 set_irq_line(0x0e, 1); 168 asm volatile ("nop"); 169 report("coalesce simultaneous level interrupts", g_isr_98 == 1); 170 } 171 172 static int g_isr_99; 173 174 static void ioapic_isr_99(isr_regs_t *regs) 175 { 176 ++g_isr_99; 177 set_irq_line(0x0e, 0); 178 eoi(); 179 } 180 181 static void test_ioapic_level_sequential(void) 182 { 183 handle_irq(0x99, ioapic_isr_99); 184 set_ioapic_redir(0x0e, 0x99, LEVEL_TRIGGERED); 185 set_irq_line(0x0e, 1); 186 set_irq_line(0x0e, 1); 187 asm volatile ("nop"); 188 report("sequential level interrupts", g_isr_99 == 2); 189 } 190 191 static volatile int g_isr_9a; 192 193 static void ioapic_isr_9a(isr_regs_t *regs) 194 { 195 ++g_isr_9a; 196 if (g_isr_9a == 2) 197 set_irq_line(0x0e, 0); 198 eoi(); 199 } 200 201 static void test_ioapic_level_retrigger(void) 202 { 203 int i; 204 205 handle_irq(0x9a, ioapic_isr_9a); 206 set_ioapic_redir(0x0e, 0x9a, LEVEL_TRIGGERED); 207 208 asm volatile ("cli"); 209 set_irq_line(0x0e, 1); 210 211 for (i = 0; i < 10; i++) { 212 if (g_isr_9a == 2) 213 break; 214 215 asm volatile ("sti; hlt; cli"); 216 } 217 218 asm volatile ("sti"); 219 220 report("retriggered level interrupts without masking", g_isr_9a == 2); 221 } 222 223 static volatile int g_isr_81; 224 225 static void ioapic_isr_81(isr_regs_t *regs) 226 { 227 ++g_isr_81; 228 set_irq_line(0x0e, 0); 229 eoi(); 230 } 231 232 static void test_ioapic_edge_mask(void) 233 { 234 handle_irq(0x81, ioapic_isr_81); 235 set_ioapic_redir(0x0e, 0x81, EDGE_TRIGGERED); 236 237 set_mask(0x0e, true); 238 set_irq_line(0x0e, 1); 239 set_irq_line(0x0e, 0); 240 241 asm volatile ("nop"); 242 report("masked level interrupt", g_isr_81 == 0); 243 244 set_mask(0x0e, false); 245 set_irq_line(0x0e, 1); 246 247 asm volatile ("nop"); 248 report("unmasked level interrupt", g_isr_81 == 1); 249 } 250 251 static volatile int g_isr_82; 252 253 static void ioapic_isr_82(isr_regs_t *regs) 254 { 255 ++g_isr_82; 256 set_irq_line(0x0e, 0); 257 eoi(); 258 } 259 260 static void test_ioapic_level_mask(void) 261 { 262 handle_irq(0x82, ioapic_isr_82); 263 set_ioapic_redir(0x0e, 0x82, LEVEL_TRIGGERED); 264 265 set_mask(0x0e, true); 266 set_irq_line(0x0e, 1); 267 268 asm volatile ("nop"); 269 report("masked level interrupt", g_isr_82 == 0); 270 271 set_mask(0x0e, false); 272 273 asm volatile ("nop"); 274 report("unmasked level interrupt", g_isr_82 == 1); 275 } 276 277 static volatile int g_isr_83; 278 279 static void ioapic_isr_83(isr_regs_t *regs) 280 { 281 ++g_isr_83; 282 set_mask(0x0e, true); 283 eoi(); 284 } 285 286 static void test_ioapic_level_retrigger_mask(void) 287 { 288 handle_irq(0x83, ioapic_isr_83); 289 set_ioapic_redir(0x0e, 0x83, LEVEL_TRIGGERED); 290 291 set_irq_line(0x0e, 1); 292 asm volatile ("nop"); 293 set_mask(0x0e, false); 294 asm volatile ("nop"); 295 report("retriggered level interrupts with mask", g_isr_83 == 2); 296 297 set_irq_line(0x0e, 0); 298 set_mask(0x0e, false); 299 } 300 301 302 int main(void) 303 { 304 setup_vm(); 305 smp_init(); 306 setup_idt(); 307 308 mask_pic_interrupts(); 309 enable_apic(); 310 311 irq_enable(); 312 313 ioapic_reg_version(); 314 ioapic_reg_id(); 315 ioapic_arbitration_id(); 316 317 test_ioapic_edge_intr(); 318 test_ioapic_level_intr(); 319 test_ioapic_simultaneous(); 320 test_ioapic_level_coalesce(); 321 test_ioapic_level_sequential(); 322 test_ioapic_level_retrigger(); 323 324 test_ioapic_edge_mask(); 325 test_ioapic_level_mask(); 326 test_ioapic_level_retrigger_mask(); 327 328 return report_summary(); 329 } 330