xref: /qemu/hw/intc/i8259.c (revision d1eebf4e3df8882d7be6377e1251123b6c16b9b0)
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/hw.h"
25 #include "hw/i386/pc.h"
26 #include "hw/isa/isa.h"
27 #include "monitor/monitor.h"
28 #include "qemu/timer.h"
29 #include "hw/isa/i8259_internal.h"
30 
31 /* debug PIC */
32 //#define DEBUG_PIC
33 
34 #ifdef DEBUG_PIC
35 #define DPRINTF(fmt, ...)                                       \
36     do { printf("pic: " fmt , ## __VA_ARGS__); } while (0)
37 #else
38 #define DPRINTF(fmt, ...)
39 #endif
40 
41 //#define DEBUG_IRQ_LATENCY
42 //#define DEBUG_IRQ_COUNT
43 
44 #define TYPE_I8259 "isa-i8259"
45 
46 #if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT)
47 static int irq_level[16];
48 #endif
49 #ifdef DEBUG_IRQ_COUNT
50 static uint64_t irq_count[16];
51 #endif
52 #ifdef DEBUG_IRQ_LATENCY
53 static int64_t irq_time[16];
54 #endif
55 DeviceState *isa_pic;
56 static PICCommonState *slave_pic;
57 
58 /* return the highest priority found in mask (highest = smallest
59    number). Return 8 if no irq */
60 static int get_priority(PICCommonState *s, int mask)
61 {
62     int priority;
63 
64     if (mask == 0) {
65         return 8;
66     }
67     priority = 0;
68     while ((mask & (1 << ((priority + s->priority_add) & 7))) == 0) {
69         priority++;
70     }
71     return priority;
72 }
73 
74 /* return the pic wanted interrupt. return -1 if none */
75 static int pic_get_irq(PICCommonState *s)
76 {
77     int mask, cur_priority, priority;
78 
79     mask = s->irr & ~s->imr;
80     priority = get_priority(s, mask);
81     if (priority == 8) {
82         return -1;
83     }
84     /* compute current priority. If special fully nested mode on the
85        master, the IRQ coming from the slave is not taken into account
86        for the priority computation. */
87     mask = s->isr;
88     if (s->special_mask) {
89         mask &= ~s->imr;
90     }
91     if (s->special_fully_nested_mode && s->master) {
92         mask &= ~(1 << 2);
93     }
94     cur_priority = get_priority(s, mask);
95     if (priority < cur_priority) {
96         /* higher priority found: an irq should be generated */
97         return (priority + s->priority_add) & 7;
98     } else {
99         return -1;
100     }
101 }
102 
103 /* Update INT output. Must be called every time the output may have changed. */
104 static void pic_update_irq(PICCommonState *s)
105 {
106     int irq;
107 
108     irq = pic_get_irq(s);
109     if (irq >= 0) {
110         DPRINTF("pic%d: imr=%x irr=%x padd=%d\n",
111                 s->master ? 0 : 1, s->imr, s->irr, s->priority_add);
112         qemu_irq_raise(s->int_out[0]);
113     } else {
114         qemu_irq_lower(s->int_out[0]);
115     }
116 }
117 
118 /* set irq level. If an edge is detected, then the IRR is set to 1 */
119 static void pic_set_irq(void *opaque, int irq, int level)
120 {
121     PICCommonState *s = opaque;
122     int mask = 1 << irq;
123 
124 #if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT) || \
125     defined(DEBUG_IRQ_LATENCY)
126     int irq_index = s->master ? irq : irq + 8;
127 #endif
128 #if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT)
129     if (level != irq_level[irq_index]) {
130         DPRINTF("pic_set_irq: irq=%d level=%d\n", irq_index, level);
131         irq_level[irq_index] = level;
132 #ifdef DEBUG_IRQ_COUNT
133         if (level == 1) {
134             irq_count[irq_index]++;
135         }
136 #endif
137     }
138 #endif
139 #ifdef DEBUG_IRQ_LATENCY
140     if (level) {
141         irq_time[irq_index] = qemu_get_clock_ns(vm_clock);
142     }
143 #endif
144 
145     if (s->elcr & mask) {
146         /* level triggered */
147         if (level) {
148             s->irr |= mask;
149             s->last_irr |= mask;
150         } else {
151             s->irr &= ~mask;
152             s->last_irr &= ~mask;
153         }
154     } else {
155         /* edge triggered */
156         if (level) {
157             if ((s->last_irr & mask) == 0) {
158                 s->irr |= mask;
159             }
160             s->last_irr |= mask;
161         } else {
162             s->last_irr &= ~mask;
163         }
164     }
165     pic_update_irq(s);
166 }
167 
168 /* acknowledge interrupt 'irq' */
169 static void pic_intack(PICCommonState *s, int irq)
170 {
171     if (s->auto_eoi) {
172         if (s->rotate_on_auto_eoi) {
173             s->priority_add = (irq + 1) & 7;
174         }
175     } else {
176         s->isr |= (1 << irq);
177     }
178     /* We don't clear a level sensitive interrupt here */
179     if (!(s->elcr & (1 << irq))) {
180         s->irr &= ~(1 << irq);
181     }
182     pic_update_irq(s);
183 }
184 
185 int pic_read_irq(DeviceState *d)
186 {
187     PICCommonState *s = PIC_COMMON(d);
188     int irq, irq2, intno;
189 
190     irq = pic_get_irq(s);
191     if (irq >= 0) {
192         if (irq == 2) {
193             irq2 = pic_get_irq(slave_pic);
194             if (irq2 >= 0) {
195                 pic_intack(slave_pic, irq2);
196             } else {
197                 /* spurious IRQ on slave controller */
198                 irq2 = 7;
199             }
200             intno = slave_pic->irq_base + irq2;
201         } else {
202             intno = s->irq_base + irq;
203         }
204         pic_intack(s, irq);
205     } else {
206         /* spurious IRQ on host controller */
207         irq = 7;
208         intno = s->irq_base + irq;
209     }
210 
211 #if defined(DEBUG_PIC) || defined(DEBUG_IRQ_LATENCY)
212     if (irq == 2) {
213         irq = irq2 + 8;
214     }
215 #endif
216 #ifdef DEBUG_IRQ_LATENCY
217     printf("IRQ%d latency=%0.3fus\n",
218            irq,
219            (double)(qemu_get_clock_ns(vm_clock) -
220                     irq_time[irq]) * 1000000.0 / get_ticks_per_sec());
221 #endif
222     DPRINTF("pic_interrupt: irq=%d\n", irq);
223     return intno;
224 }
225 
226 static void pic_init_reset(PICCommonState *s)
227 {
228     pic_reset_common(s);
229     pic_update_irq(s);
230 }
231 
232 static void pic_reset(DeviceState *dev)
233 {
234     PICCommonState *s = PIC_COMMON(dev);
235 
236     s->elcr = 0;
237     pic_init_reset(s);
238 }
239 
240 static void pic_ioport_write(void *opaque, hwaddr addr64,
241                              uint64_t val64, unsigned size)
242 {
243     PICCommonState *s = opaque;
244     uint32_t addr = addr64;
245     uint32_t val = val64;
246     int priority, cmd, irq;
247 
248     DPRINTF("write: addr=0x%02x val=0x%02x\n", addr, val);
249     if (addr == 0) {
250         if (val & 0x10) {
251             pic_init_reset(s);
252             s->init_state = 1;
253             s->init4 = val & 1;
254             s->single_mode = val & 2;
255             if (val & 0x08) {
256                 hw_error("level sensitive irq not supported");
257             }
258         } else if (val & 0x08) {
259             if (val & 0x04) {
260                 s->poll = 1;
261             }
262             if (val & 0x02) {
263                 s->read_reg_select = val & 1;
264             }
265             if (val & 0x40) {
266                 s->special_mask = (val >> 5) & 1;
267             }
268         } else {
269             cmd = val >> 5;
270             switch (cmd) {
271             case 0:
272             case 4:
273                 s->rotate_on_auto_eoi = cmd >> 2;
274                 break;
275             case 1: /* end of interrupt */
276             case 5:
277                 priority = get_priority(s, s->isr);
278                 if (priority != 8) {
279                     irq = (priority + s->priority_add) & 7;
280                     s->isr &= ~(1 << irq);
281                     if (cmd == 5) {
282                         s->priority_add = (irq + 1) & 7;
283                     }
284                     pic_update_irq(s);
285                 }
286                 break;
287             case 3:
288                 irq = val & 7;
289                 s->isr &= ~(1 << irq);
290                 pic_update_irq(s);
291                 break;
292             case 6:
293                 s->priority_add = (val + 1) & 7;
294                 pic_update_irq(s);
295                 break;
296             case 7:
297                 irq = val & 7;
298                 s->isr &= ~(1 << irq);
299                 s->priority_add = (irq + 1) & 7;
300                 pic_update_irq(s);
301                 break;
302             default:
303                 /* no operation */
304                 break;
305             }
306         }
307     } else {
308         switch (s->init_state) {
309         case 0:
310             /* normal mode */
311             s->imr = val;
312             pic_update_irq(s);
313             break;
314         case 1:
315             s->irq_base = val & 0xf8;
316             s->init_state = s->single_mode ? (s->init4 ? 3 : 0) : 2;
317             break;
318         case 2:
319             if (s->init4) {
320                 s->init_state = 3;
321             } else {
322                 s->init_state = 0;
323             }
324             break;
325         case 3:
326             s->special_fully_nested_mode = (val >> 4) & 1;
327             s->auto_eoi = (val >> 1) & 1;
328             s->init_state = 0;
329             break;
330         }
331     }
332 }
333 
334 static uint64_t pic_ioport_read(void *opaque, hwaddr addr,
335                                 unsigned size)
336 {
337     PICCommonState *s = opaque;
338     int ret;
339 
340     if (s->poll) {
341         ret = pic_get_irq(s);
342         if (ret >= 0) {
343             pic_intack(s, ret);
344             ret |= 0x80;
345         } else {
346             ret = 0;
347         }
348         s->poll = 0;
349     } else {
350         if (addr == 0) {
351             if (s->read_reg_select) {
352                 ret = s->isr;
353             } else {
354                 ret = s->irr;
355             }
356         } else {
357             ret = s->imr;
358         }
359     }
360     DPRINTF("read: addr=0x%02x val=0x%02x\n", addr, ret);
361     return ret;
362 }
363 
364 int pic_get_output(DeviceState *d)
365 {
366     PICCommonState *s = PIC_COMMON(d);
367 
368     return (pic_get_irq(s) >= 0);
369 }
370 
371 static void elcr_ioport_write(void *opaque, hwaddr addr,
372                               uint64_t val, unsigned size)
373 {
374     PICCommonState *s = opaque;
375     s->elcr = val & s->elcr_mask;
376 }
377 
378 static uint64_t elcr_ioport_read(void *opaque, hwaddr addr,
379                                  unsigned size)
380 {
381     PICCommonState *s = opaque;
382     return s->elcr;
383 }
384 
385 static const MemoryRegionOps pic_base_ioport_ops = {
386     .read = pic_ioport_read,
387     .write = pic_ioport_write,
388     .impl = {
389         .min_access_size = 1,
390         .max_access_size = 1,
391     },
392 };
393 
394 static const MemoryRegionOps pic_elcr_ioport_ops = {
395     .read = elcr_ioport_read,
396     .write = elcr_ioport_write,
397     .impl = {
398         .min_access_size = 1,
399         .max_access_size = 1,
400     },
401 };
402 
403 static void pic_init(PICCommonState *s)
404 {
405     DeviceState *dev = DEVICE(s);
406 
407     memory_region_init_io(&s->base_io, &pic_base_ioport_ops, s, "pic", 2);
408     memory_region_init_io(&s->elcr_io, &pic_elcr_ioport_ops, s, "elcr", 1);
409 
410     qdev_init_gpio_out(dev, s->int_out, ARRAY_SIZE(s->int_out));
411     qdev_init_gpio_in(dev, pic_set_irq, 8);
412 }
413 
414 void pic_info(Monitor *mon, const QDict *qdict)
415 {
416     int i;
417     PICCommonState *s;
418 
419     if (!isa_pic) {
420         return;
421     }
422     for (i = 0; i < 2; i++) {
423         s = i == 0 ? PIC_COMMON(isa_pic) : slave_pic;
424         monitor_printf(mon, "pic%d: irr=%02x imr=%02x isr=%02x hprio=%d "
425                        "irq_base=%02x rr_sel=%d elcr=%02x fnm=%d\n",
426                        i, s->irr, s->imr, s->isr, s->priority_add,
427                        s->irq_base, s->read_reg_select, s->elcr,
428                        s->special_fully_nested_mode);
429     }
430 }
431 
432 void irq_info(Monitor *mon, const QDict *qdict)
433 {
434 #ifndef DEBUG_IRQ_COUNT
435     monitor_printf(mon, "irq statistic code not compiled.\n");
436 #else
437     int i;
438     int64_t count;
439 
440     monitor_printf(mon, "IRQ statistics:\n");
441     for (i = 0; i < 16; i++) {
442         count = irq_count[i];
443         if (count > 0) {
444             monitor_printf(mon, "%2d: %" PRId64 "\n", i, count);
445         }
446     }
447 #endif
448 }
449 
450 qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq)
451 {
452     qemu_irq *irq_set;
453     DeviceState *dev;
454     ISADevice *isadev;
455     int i;
456 
457     irq_set = g_malloc(ISA_NUM_IRQS * sizeof(qemu_irq));
458 
459     isadev = i8259_init_chip(TYPE_I8259, bus, true);
460     dev = DEVICE(isadev);
461 
462     qdev_connect_gpio_out(dev, 0, parent_irq);
463     for (i = 0 ; i < 8; i++) {
464         irq_set[i] = qdev_get_gpio_in(dev, i);
465     }
466 
467     isa_pic = dev;
468 
469     isadev = i8259_init_chip(TYPE_I8259, bus, false);
470     dev = DEVICE(isadev);
471 
472     qdev_connect_gpio_out(dev, 0, irq_set[2]);
473     for (i = 0 ; i < 8; i++) {
474         irq_set[i + 8] = qdev_get_gpio_in(dev, i);
475     }
476 
477     slave_pic = PIC_COMMON(dev);
478 
479     return irq_set;
480 }
481 
482 static void i8259_class_init(ObjectClass *klass, void *data)
483 {
484     PICCommonClass *k = PIC_COMMON_CLASS(klass);
485     DeviceClass *dc = DEVICE_CLASS(klass);
486 
487     k->init = pic_init;
488     dc->reset = pic_reset;
489 }
490 
491 static const TypeInfo i8259_info = {
492     .name       = TYPE_I8259,
493     .instance_size = sizeof(PICCommonState),
494     .parent     = TYPE_PIC_COMMON,
495     .class_init = i8259_class_init,
496 };
497 
498 static void pic_register_types(void)
499 {
500     type_register_static(&i8259_info);
501 }
502 
503 type_init(pic_register_types)
504