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