|
1 /* |
|
2 * QEMU 8259 interrupt controller emulation |
|
3 * |
|
4 * Copyright (c) 2003-2004 Fabrice Bellard |
|
5 * |
|
6 * Permission is hereby granted, free of charge, to any person obtaining a copy |
|
7 * of this software and associated documentation files (the "Software"), to deal |
|
8 * in the Software without restriction, including without limitation the rights |
|
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
10 * copies of the Software, and to permit persons to whom the Software is |
|
11 * furnished to do so, subject to the following conditions: |
|
12 * |
|
13 * The above copyright notice and this permission notice shall be included in |
|
14 * all copies or substantial portions of the Software. |
|
15 * |
|
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|
22 * THE SOFTWARE. |
|
23 */ |
|
24 #include "hw.h" |
|
25 #include "pc.h" |
|
26 #include "isa.h" |
|
27 #include "console.h" |
|
28 |
|
29 /* debug PIC */ |
|
30 //#define DEBUG_PIC |
|
31 |
|
32 //#define DEBUG_IRQ_LATENCY |
|
33 //#define DEBUG_IRQ_COUNT |
|
34 |
|
35 typedef struct PicState { |
|
36 uint8_t last_irr; /* edge detection */ |
|
37 uint8_t irr; /* interrupt request register */ |
|
38 uint8_t imr; /* interrupt mask register */ |
|
39 uint8_t isr; /* interrupt service register */ |
|
40 uint8_t priority_add; /* highest irq priority */ |
|
41 uint8_t irq_base; |
|
42 uint8_t read_reg_select; |
|
43 uint8_t poll; |
|
44 uint8_t special_mask; |
|
45 uint8_t init_state; |
|
46 uint8_t auto_eoi; |
|
47 uint8_t rotate_on_auto_eoi; |
|
48 uint8_t special_fully_nested_mode; |
|
49 uint8_t init4; /* true if 4 byte init */ |
|
50 uint8_t single_mode; /* true if slave pic is not initialized */ |
|
51 uint8_t elcr; /* PIIX edge/trigger selection*/ |
|
52 uint8_t elcr_mask; |
|
53 PicState2 *pics_state; |
|
54 } PicState; |
|
55 |
|
56 struct PicState2 { |
|
57 /* 0 is master pic, 1 is slave pic */ |
|
58 /* XXX: better separation between the two pics */ |
|
59 PicState pics[2]; |
|
60 qemu_irq parent_irq; |
|
61 void *irq_request_opaque; |
|
62 /* IOAPIC callback support */ |
|
63 SetIRQFunc *alt_irq_func; |
|
64 void *alt_irq_opaque; |
|
65 }; |
|
66 |
|
67 #if defined(DEBUG_PIC) || defined (DEBUG_IRQ_COUNT) |
|
68 static int irq_level[16]; |
|
69 #endif |
|
70 #ifdef DEBUG_IRQ_COUNT |
|
71 static uint64_t irq_count[16]; |
|
72 #endif |
|
73 |
|
74 /* set irq level. If an edge is detected, then the IRR is set to 1 */ |
|
75 static inline void pic_set_irq1(PicState *s, int irq, int level) |
|
76 { |
|
77 int mask; |
|
78 mask = 1 << irq; |
|
79 if (s->elcr & mask) { |
|
80 /* level triggered */ |
|
81 if (level) { |
|
82 s->irr |= mask; |
|
83 s->last_irr |= mask; |
|
84 } else { |
|
85 s->irr &= ~mask; |
|
86 s->last_irr &= ~mask; |
|
87 } |
|
88 } else { |
|
89 /* edge triggered */ |
|
90 if (level) { |
|
91 if ((s->last_irr & mask) == 0) |
|
92 s->irr |= mask; |
|
93 s->last_irr |= mask; |
|
94 } else { |
|
95 s->last_irr &= ~mask; |
|
96 } |
|
97 } |
|
98 } |
|
99 |
|
100 /* return the highest priority found in mask (highest = smallest |
|
101 number). Return 8 if no irq */ |
|
102 static inline int get_priority(PicState *s, int mask) |
|
103 { |
|
104 int priority; |
|
105 if (mask == 0) |
|
106 return 8; |
|
107 priority = 0; |
|
108 while ((mask & (1 << ((priority + s->priority_add) & 7))) == 0) |
|
109 priority++; |
|
110 return priority; |
|
111 } |
|
112 |
|
113 /* return the pic wanted interrupt. return -1 if none */ |
|
114 static int pic_get_irq(PicState *s) |
|
115 { |
|
116 int mask, cur_priority, priority; |
|
117 |
|
118 mask = s->irr & ~s->imr; |
|
119 priority = get_priority(s, mask); |
|
120 if (priority == 8) |
|
121 return -1; |
|
122 /* compute current priority. If special fully nested mode on the |
|
123 master, the IRQ coming from the slave is not taken into account |
|
124 for the priority computation. */ |
|
125 mask = s->isr; |
|
126 if (s->special_mask) |
|
127 mask &= ~s->imr; |
|
128 if (s->special_fully_nested_mode && s == &s->pics_state->pics[0]) |
|
129 mask &= ~(1 << 2); |
|
130 cur_priority = get_priority(s, mask); |
|
131 if (priority < cur_priority) { |
|
132 /* higher priority found: an irq should be generated */ |
|
133 return (priority + s->priority_add) & 7; |
|
134 } else { |
|
135 return -1; |
|
136 } |
|
137 } |
|
138 |
|
139 /* raise irq to CPU if necessary. must be called every time the active |
|
140 irq may change */ |
|
141 /* XXX: should not export it, but it is needed for an APIC kludge */ |
|
142 void pic_update_irq(PicState2 *s) |
|
143 { |
|
144 int irq2, irq; |
|
145 |
|
146 /* first look at slave pic */ |
|
147 irq2 = pic_get_irq(&s->pics[1]); |
|
148 if (irq2 >= 0) { |
|
149 /* if irq request by slave pic, signal master PIC */ |
|
150 pic_set_irq1(&s->pics[0], 2, 1); |
|
151 pic_set_irq1(&s->pics[0], 2, 0); |
|
152 } |
|
153 /* look at requested irq */ |
|
154 irq = pic_get_irq(&s->pics[0]); |
|
155 if (irq >= 0) { |
|
156 #if defined(DEBUG_PIC) |
|
157 { |
|
158 int i; |
|
159 for(i = 0; i < 2; i++) { |
|
160 printf("pic%d: imr=%x irr=%x padd=%d\n", |
|
161 i, s->pics[i].imr, s->pics[i].irr, |
|
162 s->pics[i].priority_add); |
|
163 |
|
164 } |
|
165 } |
|
166 printf("pic: cpu_interrupt\n"); |
|
167 #endif |
|
168 qemu_irq_raise(s->parent_irq); |
|
169 } |
|
170 |
|
171 /* all targets should do this rather than acking the IRQ in the cpu */ |
|
172 #if defined(TARGET_MIPS) || defined(TARGET_PPC) |
|
173 else { |
|
174 qemu_irq_lower(s->parent_irq); |
|
175 } |
|
176 #endif |
|
177 } |
|
178 |
|
179 #ifdef DEBUG_IRQ_LATENCY |
|
180 int64_t irq_time[16]; |
|
181 #endif |
|
182 |
|
183 static void i8259_set_irq(void *opaque, int irq, int level) |
|
184 { |
|
185 PicState2 *s = opaque; |
|
186 |
|
187 #if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT) |
|
188 if (level != irq_level[irq]) { |
|
189 #if defined(DEBUG_PIC) |
|
190 printf("i8259_set_irq: irq=%d level=%d\n", irq, level); |
|
191 #endif |
|
192 irq_level[irq] = level; |
|
193 #ifdef DEBUG_IRQ_COUNT |
|
194 if (level == 1) |
|
195 irq_count[irq]++; |
|
196 #endif |
|
197 } |
|
198 #endif |
|
199 #ifdef DEBUG_IRQ_LATENCY |
|
200 if (level) { |
|
201 irq_time[irq] = qemu_get_clock(vm_clock); |
|
202 } |
|
203 #endif |
|
204 pic_set_irq1(&s->pics[irq >> 3], irq & 7, level); |
|
205 /* used for IOAPIC irqs */ |
|
206 if (s->alt_irq_func) |
|
207 s->alt_irq_func(s->alt_irq_opaque, irq, level); |
|
208 pic_update_irq(s); |
|
209 } |
|
210 |
|
211 /* acknowledge interrupt 'irq' */ |
|
212 static inline void pic_intack(PicState *s, int irq) |
|
213 { |
|
214 if (s->auto_eoi) { |
|
215 if (s->rotate_on_auto_eoi) |
|
216 s->priority_add = (irq + 1) & 7; |
|
217 } else { |
|
218 s->isr |= (1 << irq); |
|
219 } |
|
220 /* We don't clear a level sensitive interrupt here */ |
|
221 if (!(s->elcr & (1 << irq))) |
|
222 s->irr &= ~(1 << irq); |
|
223 } |
|
224 |
|
225 int pic_read_irq(PicState2 *s) |
|
226 { |
|
227 int irq, irq2, intno; |
|
228 |
|
229 irq = pic_get_irq(&s->pics[0]); |
|
230 if (irq >= 0) { |
|
231 pic_intack(&s->pics[0], irq); |
|
232 if (irq == 2) { |
|
233 irq2 = pic_get_irq(&s->pics[1]); |
|
234 if (irq2 >= 0) { |
|
235 pic_intack(&s->pics[1], irq2); |
|
236 } else { |
|
237 /* spurious IRQ on slave controller */ |
|
238 irq2 = 7; |
|
239 } |
|
240 intno = s->pics[1].irq_base + irq2; |
|
241 irq = irq2 + 8; |
|
242 } else { |
|
243 intno = s->pics[0].irq_base + irq; |
|
244 } |
|
245 } else { |
|
246 /* spurious IRQ on host controller */ |
|
247 irq = 7; |
|
248 intno = s->pics[0].irq_base + irq; |
|
249 } |
|
250 pic_update_irq(s); |
|
251 |
|
252 #ifdef DEBUG_IRQ_LATENCY |
|
253 printf("IRQ%d latency=%0.3fus\n", |
|
254 irq, |
|
255 (double)(qemu_get_clock(vm_clock) - irq_time[irq]) * 1000000.0 / ticks_per_sec); |
|
256 #endif |
|
257 #if defined(DEBUG_PIC) |
|
258 printf("pic_interrupt: irq=%d\n", irq); |
|
259 #endif |
|
260 return intno; |
|
261 } |
|
262 |
|
263 static void pic_reset(void *opaque) |
|
264 { |
|
265 PicState *s = opaque; |
|
266 |
|
267 s->last_irr = 0; |
|
268 s->irr = 0; |
|
269 s->imr = 0; |
|
270 s->isr = 0; |
|
271 s->priority_add = 0; |
|
272 s->irq_base = 0; |
|
273 s->read_reg_select = 0; |
|
274 s->poll = 0; |
|
275 s->special_mask = 0; |
|
276 s->init_state = 0; |
|
277 s->auto_eoi = 0; |
|
278 s->rotate_on_auto_eoi = 0; |
|
279 s->special_fully_nested_mode = 0; |
|
280 s->init4 = 0; |
|
281 s->single_mode = 0; |
|
282 /* Note: ELCR is not reset */ |
|
283 } |
|
284 |
|
285 static void pic_ioport_write(void *opaque, uint32_t addr, uint32_t val) |
|
286 { |
|
287 PicState *s = opaque; |
|
288 int priority, cmd, irq; |
|
289 |
|
290 #ifdef DEBUG_PIC |
|
291 printf("pic_write: addr=0x%02x val=0x%02x\n", addr, val); |
|
292 #endif |
|
293 addr &= 1; |
|
294 if (addr == 0) { |
|
295 if (val & 0x10) { |
|
296 /* init */ |
|
297 pic_reset(s); |
|
298 /* deassert a pending interrupt */ |
|
299 qemu_irq_lower(s->pics_state->parent_irq); |
|
300 s->init_state = 1; |
|
301 s->init4 = val & 1; |
|
302 s->single_mode = val & 2; |
|
303 if (val & 0x08) |
|
304 hw_error("level sensitive irq not supported"); |
|
305 } else if (val & 0x08) { |
|
306 if (val & 0x04) |
|
307 s->poll = 1; |
|
308 if (val & 0x02) |
|
309 s->read_reg_select = val & 1; |
|
310 if (val & 0x40) |
|
311 s->special_mask = (val >> 5) & 1; |
|
312 } else { |
|
313 cmd = val >> 5; |
|
314 switch(cmd) { |
|
315 case 0: |
|
316 case 4: |
|
317 s->rotate_on_auto_eoi = cmd >> 2; |
|
318 break; |
|
319 case 1: /* end of interrupt */ |
|
320 case 5: |
|
321 priority = get_priority(s, s->isr); |
|
322 if (priority != 8) { |
|
323 irq = (priority + s->priority_add) & 7; |
|
324 s->isr &= ~(1 << irq); |
|
325 if (cmd == 5) |
|
326 s->priority_add = (irq + 1) & 7; |
|
327 pic_update_irq(s->pics_state); |
|
328 } |
|
329 break; |
|
330 case 3: |
|
331 irq = val & 7; |
|
332 s->isr &= ~(1 << irq); |
|
333 pic_update_irq(s->pics_state); |
|
334 break; |
|
335 case 6: |
|
336 s->priority_add = (val + 1) & 7; |
|
337 pic_update_irq(s->pics_state); |
|
338 break; |
|
339 case 7: |
|
340 irq = val & 7; |
|
341 s->isr &= ~(1 << irq); |
|
342 s->priority_add = (irq + 1) & 7; |
|
343 pic_update_irq(s->pics_state); |
|
344 break; |
|
345 default: |
|
346 /* no operation */ |
|
347 break; |
|
348 } |
|
349 } |
|
350 } else { |
|
351 switch(s->init_state) { |
|
352 case 0: |
|
353 /* normal mode */ |
|
354 s->imr = val; |
|
355 pic_update_irq(s->pics_state); |
|
356 break; |
|
357 case 1: |
|
358 s->irq_base = val & 0xf8; |
|
359 s->init_state = s->single_mode ? (s->init4 ? 3 : 0) : 2; |
|
360 break; |
|
361 case 2: |
|
362 if (s->init4) { |
|
363 s->init_state = 3; |
|
364 } else { |
|
365 s->init_state = 0; |
|
366 } |
|
367 break; |
|
368 case 3: |
|
369 s->special_fully_nested_mode = (val >> 4) & 1; |
|
370 s->auto_eoi = (val >> 1) & 1; |
|
371 s->init_state = 0; |
|
372 break; |
|
373 } |
|
374 } |
|
375 } |
|
376 |
|
377 static uint32_t pic_poll_read (PicState *s, uint32_t addr1) |
|
378 { |
|
379 int ret; |
|
380 |
|
381 ret = pic_get_irq(s); |
|
382 if (ret >= 0) { |
|
383 if (addr1 >> 7) { |
|
384 s->pics_state->pics[0].isr &= ~(1 << 2); |
|
385 s->pics_state->pics[0].irr &= ~(1 << 2); |
|
386 } |
|
387 s->irr &= ~(1 << ret); |
|
388 s->isr &= ~(1 << ret); |
|
389 if (addr1 >> 7 || ret != 2) |
|
390 pic_update_irq(s->pics_state); |
|
391 } else { |
|
392 ret = 0x07; |
|
393 pic_update_irq(s->pics_state); |
|
394 } |
|
395 |
|
396 return ret; |
|
397 } |
|
398 |
|
399 static uint32_t pic_ioport_read(void *opaque, uint32_t addr1) |
|
400 { |
|
401 PicState *s = opaque; |
|
402 unsigned int addr; |
|
403 int ret; |
|
404 |
|
405 addr = addr1; |
|
406 addr &= 1; |
|
407 if (s->poll) { |
|
408 ret = pic_poll_read(s, addr1); |
|
409 s->poll = 0; |
|
410 } else { |
|
411 if (addr == 0) { |
|
412 if (s->read_reg_select) |
|
413 ret = s->isr; |
|
414 else |
|
415 ret = s->irr; |
|
416 } else { |
|
417 ret = s->imr; |
|
418 } |
|
419 } |
|
420 #ifdef DEBUG_PIC |
|
421 printf("pic_read: addr=0x%02x val=0x%02x\n", addr1, ret); |
|
422 #endif |
|
423 return ret; |
|
424 } |
|
425 |
|
426 /* memory mapped interrupt status */ |
|
427 /* XXX: may be the same than pic_read_irq() */ |
|
428 uint32_t pic_intack_read(PicState2 *s) |
|
429 { |
|
430 int ret; |
|
431 |
|
432 ret = pic_poll_read(&s->pics[0], 0x00); |
|
433 if (ret == 2) |
|
434 ret = pic_poll_read(&s->pics[1], 0x80) + 8; |
|
435 /* Prepare for ISR read */ |
|
436 s->pics[0].read_reg_select = 1; |
|
437 |
|
438 return ret; |
|
439 } |
|
440 |
|
441 static void elcr_ioport_write(void *opaque, uint32_t addr, uint32_t val) |
|
442 { |
|
443 PicState *s = opaque; |
|
444 s->elcr = val & s->elcr_mask; |
|
445 } |
|
446 |
|
447 static uint32_t elcr_ioport_read(void *opaque, uint32_t addr1) |
|
448 { |
|
449 PicState *s = opaque; |
|
450 return s->elcr; |
|
451 } |
|
452 |
|
453 static void pic_save(QEMUFile *f, void *opaque) |
|
454 { |
|
455 PicState *s = opaque; |
|
456 |
|
457 qemu_put_8s(f, &s->last_irr); |
|
458 qemu_put_8s(f, &s->irr); |
|
459 qemu_put_8s(f, &s->imr); |
|
460 qemu_put_8s(f, &s->isr); |
|
461 qemu_put_8s(f, &s->priority_add); |
|
462 qemu_put_8s(f, &s->irq_base); |
|
463 qemu_put_8s(f, &s->read_reg_select); |
|
464 qemu_put_8s(f, &s->poll); |
|
465 qemu_put_8s(f, &s->special_mask); |
|
466 qemu_put_8s(f, &s->init_state); |
|
467 qemu_put_8s(f, &s->auto_eoi); |
|
468 qemu_put_8s(f, &s->rotate_on_auto_eoi); |
|
469 qemu_put_8s(f, &s->special_fully_nested_mode); |
|
470 qemu_put_8s(f, &s->init4); |
|
471 qemu_put_8s(f, &s->single_mode); |
|
472 qemu_put_8s(f, &s->elcr); |
|
473 } |
|
474 |
|
475 static int pic_load(QEMUFile *f, void *opaque, int version_id) |
|
476 { |
|
477 PicState *s = opaque; |
|
478 |
|
479 if (version_id != 1) |
|
480 return -EINVAL; |
|
481 |
|
482 qemu_get_8s(f, &s->last_irr); |
|
483 qemu_get_8s(f, &s->irr); |
|
484 qemu_get_8s(f, &s->imr); |
|
485 qemu_get_8s(f, &s->isr); |
|
486 qemu_get_8s(f, &s->priority_add); |
|
487 qemu_get_8s(f, &s->irq_base); |
|
488 qemu_get_8s(f, &s->read_reg_select); |
|
489 qemu_get_8s(f, &s->poll); |
|
490 qemu_get_8s(f, &s->special_mask); |
|
491 qemu_get_8s(f, &s->init_state); |
|
492 qemu_get_8s(f, &s->auto_eoi); |
|
493 qemu_get_8s(f, &s->rotate_on_auto_eoi); |
|
494 qemu_get_8s(f, &s->special_fully_nested_mode); |
|
495 qemu_get_8s(f, &s->init4); |
|
496 qemu_get_8s(f, &s->single_mode); |
|
497 qemu_get_8s(f, &s->elcr); |
|
498 return 0; |
|
499 } |
|
500 |
|
501 /* XXX: add generic master/slave system */ |
|
502 static void pic_init1(int io_addr, int elcr_addr, PicState *s) |
|
503 { |
|
504 register_ioport_write(io_addr, 2, 1, pic_ioport_write, s); |
|
505 register_ioport_read(io_addr, 2, 1, pic_ioport_read, s); |
|
506 if (elcr_addr >= 0) { |
|
507 register_ioport_write(elcr_addr, 1, 1, elcr_ioport_write, s); |
|
508 register_ioport_read(elcr_addr, 1, 1, elcr_ioport_read, s); |
|
509 } |
|
510 register_savevm("i8259", io_addr, 1, pic_save, pic_load, s); |
|
511 qemu_register_reset(pic_reset, s); |
|
512 } |
|
513 |
|
514 void pic_info(void) |
|
515 { |
|
516 int i; |
|
517 PicState *s; |
|
518 |
|
519 if (!isa_pic) |
|
520 return; |
|
521 |
|
522 for(i=0;i<2;i++) { |
|
523 s = &isa_pic->pics[i]; |
|
524 term_printf("pic%d: irr=%02x imr=%02x isr=%02x hprio=%d irq_base=%02x rr_sel=%d elcr=%02x fnm=%d\n", |
|
525 i, s->irr, s->imr, s->isr, s->priority_add, |
|
526 s->irq_base, s->read_reg_select, s->elcr, |
|
527 s->special_fully_nested_mode); |
|
528 } |
|
529 } |
|
530 |
|
531 void irq_info(void) |
|
532 { |
|
533 #ifndef DEBUG_IRQ_COUNT |
|
534 term_printf("irq statistic code not compiled.\n"); |
|
535 #else |
|
536 int i; |
|
537 int64_t count; |
|
538 |
|
539 term_printf("IRQ statistics:\n"); |
|
540 for (i = 0; i < 16; i++) { |
|
541 count = irq_count[i]; |
|
542 if (count > 0) |
|
543 term_printf("%2d: %" PRId64 "\n", i, count); |
|
544 } |
|
545 #endif |
|
546 } |
|
547 |
|
548 qemu_irq *i8259_init(qemu_irq parent_irq) |
|
549 { |
|
550 PicState2 *s; |
|
551 |
|
552 s = qemu_mallocz(sizeof(PicState2)); |
|
553 if (!s) |
|
554 return NULL; |
|
555 pic_init1(0x20, 0x4d0, &s->pics[0]); |
|
556 pic_init1(0xa0, 0x4d1, &s->pics[1]); |
|
557 s->pics[0].elcr_mask = 0xf8; |
|
558 s->pics[1].elcr_mask = 0xde; |
|
559 s->parent_irq = parent_irq; |
|
560 s->pics[0].pics_state = s; |
|
561 s->pics[1].pics_state = s; |
|
562 isa_pic = s; |
|
563 return qemu_allocate_irqs(i8259_set_irq, s, 16); |
|
564 } |
|
565 |
|
566 void pic_set_alt_irq_func(PicState2 *s, SetIRQFunc *alt_irq_func, |
|
567 void *alt_irq_opaque) |
|
568 { |
|
569 s->alt_irq_func = alt_irq_func; |
|
570 s->alt_irq_opaque = alt_irq_opaque; |
|
571 } |