diff options
author | Jan Kiszka <jan.kiszka@siemens.com> | 2011-02-03 22:54:11 +0100 |
---|---|---|
committer | Anthony Liguori <aliguori@us.ibm.com> | 2011-02-04 06:33:26 -0600 |
commit | 0280b571c1a153f8926612d8c8d7359242d596f5 (patch) | |
tree | 17ecae86aca385ebc79a209314ca61bc635f5456 /hw/ioapic.c | |
parent | 73eb4c04e9e8ea7f6eb83694cb0c43e38d882a7c (diff) |
ioapic: Implement EOI handling for level-triggered IRQs
Add the missing EOI broadcast from local APIC to the IOAPICs on
completion of level-triggered IRQs. This ensures that a still asserted
IRQ source properly re-triggers an APIC IRQ.
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
Diffstat (limited to 'hw/ioapic.c')
-rw-r--r-- | hw/ioapic.c | 43 |
1 files changed, 41 insertions, 2 deletions
diff --git a/hw/ioapic.c b/hw/ioapic.c index 210956860c..8bc31f7cdf 100644 --- a/hw/ioapic.c +++ b/hw/ioapic.c @@ -23,6 +23,7 @@ #include "hw.h" #include "pc.h" #include "apic.h" +#include "ioapic.h" #include "qemu-timer.h" #include "host-utils.h" #include "sysbus.h" @@ -36,7 +37,10 @@ #define DPRINTF(fmt, ...) #endif -#define IOAPIC_LVT_MASKED (1<<16) +#define MAX_IOAPICS 1 + +#define IOAPIC_LVT_MASKED (1 << 16) +#define IOAPIC_LVT_REMOTE_IRR (1 << 14) #define IOAPIC_TRIGGER_EDGE 0 #define IOAPIC_TRIGGER_LEVEL 1 @@ -61,6 +65,8 @@ struct IOAPICState { uint64_t ioredtbl[IOAPIC_NUM_PINS]; }; +static IOAPICState *ioapics[MAX_IOAPICS]; + static void ioapic_service(IOAPICState *s) { uint8_t i; @@ -83,8 +89,11 @@ static void ioapic_service(IOAPICState *s) dest_mode = (entry >> 11) & 1; delivery_mode = (entry >> 8) & 7; polarity = (entry >> 13) & 1; - if (trig_mode == IOAPIC_TRIGGER_EDGE) + if (trig_mode == IOAPIC_TRIGGER_EDGE) { s->irr &= ~mask; + } else { + s->ioredtbl[i] |= IOAPIC_LVT_REMOTE_IRR; + } if (delivery_mode == IOAPIC_DM_EXTINT) vector = pic_read_irq(isa_pic); else @@ -131,6 +140,29 @@ static void ioapic_set_irq(void *opaque, int vector, int level) } } +void ioapic_eoi_broadcast(int vector) +{ + IOAPICState *s; + uint64_t entry; + int i, n; + + for (i = 0; i < MAX_IOAPICS; i++) { + s = ioapics[i]; + if (!s) { + continue; + } + for (n = 0; n < IOAPIC_NUM_PINS; n++) { + entry = s->ioredtbl[n]; + if ((entry & IOAPIC_LVT_REMOTE_IRR) && (entry & 0xff) == vector) { + s->ioredtbl[n] = entry & ~IOAPIC_LVT_REMOTE_IRR; + if (!(entry & IOAPIC_LVT_MASKED) && (s->irr & (1 << n))) { + ioapic_service(s); + } + } + } + } +} + static uint32_t ioapic_mem_readl(void *opaque, target_phys_addr_t addr) { IOAPICState *s = opaque; @@ -240,6 +272,11 @@ static int ioapic_init1(SysBusDevice *dev) { IOAPICState *s = FROM_SYSBUS(IOAPICState, dev); int io_memory; + static int ioapic_no; + + if (ioapic_no >= MAX_IOAPICS) { + return -1; + } io_memory = cpu_register_io_memory(ioapic_mem_read, ioapic_mem_write, s, @@ -248,6 +285,8 @@ static int ioapic_init1(SysBusDevice *dev) qdev_init_gpio_in(&dev->qdev, ioapic_set_irq, IOAPIC_NUM_PINS); + ioapics[ioapic_no++] = s; + return 0; } |