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_81; 192 193 static void ioapic_isr_81(isr_regs_t *regs) 194 { 195 ++g_isr_81; 196 set_irq_line(0x0e, 0); 197 eoi(); 198 } 199 200 static void test_ioapic_edge_mask(void) 201 { 202 handle_irq(0x81, ioapic_isr_81); 203 set_ioapic_redir(0x0e, 0x81, EDGE_TRIGGERED); 204 205 set_mask(0x0e, true); 206 set_irq_line(0x0e, 1); 207 set_irq_line(0x0e, 0); 208 209 asm volatile ("nop"); 210 report("masked level interrupt", g_isr_81 == 0); 211 212 set_mask(0x0e, false); 213 set_irq_line(0x0e, 1); 214 215 asm volatile ("nop"); 216 report("unmasked level interrupt", g_isr_81 == 1); 217 } 218 219 static volatile int g_isr_82; 220 221 static void ioapic_isr_82(isr_regs_t *regs) 222 { 223 ++g_isr_82; 224 set_irq_line(0x0e, 0); 225 eoi(); 226 } 227 228 static void test_ioapic_level_mask(void) 229 { 230 handle_irq(0x82, ioapic_isr_82); 231 set_ioapic_redir(0x0e, 0x82, LEVEL_TRIGGERED); 232 233 set_mask(0x0e, true); 234 set_irq_line(0x0e, 1); 235 236 asm volatile ("nop"); 237 report("masked level interrupt", g_isr_82 == 0); 238 239 set_mask(0x0e, false); 240 241 asm volatile ("nop"); 242 report("unmasked level interrupt", g_isr_82 == 1); 243 } 244 245 246 int main(void) 247 { 248 setup_vm(); 249 smp_init(); 250 setup_idt(); 251 252 mask_pic_interrupts(); 253 enable_apic(); 254 255 irq_enable(); 256 257 ioapic_reg_version(); 258 ioapic_reg_id(); 259 ioapic_arbitration_id(); 260 261 test_ioapic_edge_intr(); 262 test_ioapic_level_intr(); 263 test_ioapic_simultaneous(); 264 test_ioapic_level_coalesce(); 265 test_ioapic_level_sequential(); 266 267 test_ioapic_edge_mask(); 268 test_ioapic_level_mask(); 269 270 return report_summary(); 271 } 272