From: Jan Kiszka <jan.kis...@siemens.com> Make use of the new IRQ message and report delivery results from the sink to the source. As a by-product, this also adds de-coalescing support to the PIC.
Signed-off-by: Jan Kiszka <jan.kis...@siemens.com> --- hw/apic.c | 64 +++++++++++++++++++---------------------- hw/apic.h | 9 ++---- hw/i8259.c | 16 ++++++++++- hw/ioapic.c | 20 ++++++++++--- hw/mc146818rtc.c | 83 ++++++++++++++++++++++++++++++++++------------------- hw/pc.c | 29 ++++++++++++++++-- 6 files changed, 141 insertions(+), 80 deletions(-) diff --git a/hw/apic.c b/hw/apic.c index 7fbd79b..f9587d1 100644 --- a/hw/apic.c +++ b/hw/apic.c @@ -123,10 +123,8 @@ typedef struct APICState { static int apic_io_memory; static APICState *local_apics[MAX_APICS + 1]; static int last_apic_idx = 0; -static int apic_irq_delivered; - -static void apic_set_irq(APICState *s, int vector_num, int trigger_mode); +static int apic_set_irq(APICState *s, int vector_num, int trigger_mode); static void apic_update_irq(APICState *s); static void apic_get_delivery_bitmask(uint32_t *deliver_bitmask, uint8_t dest, uint8_t dest_mode); @@ -239,12 +237,12 @@ void apic_deliver_pic_intr(CPUState *env, int level) }\ } -static void apic_bus_deliver(const uint32_t *deliver_bitmask, - uint8_t delivery_mode, - uint8_t vector_num, uint8_t polarity, - uint8_t trigger_mode) +static int apic_bus_deliver(const uint32_t *deliver_bitmask, + uint8_t delivery_mode, uint8_t vector_num, + uint8_t polarity, uint8_t trigger_mode) { APICState *apic_iter; + int ret; switch (delivery_mode) { case APIC_DM_LOWPRI: @@ -261,11 +259,12 @@ static void apic_bus_deliver(const uint32_t *deliver_bitmask, if (d >= 0) { apic_iter = local_apics[d]; if (apic_iter) { - apic_set_irq(apic_iter, vector_num, trigger_mode); + return apic_set_irq(apic_iter, vector_num, + trigger_mode); } } } - return; + return QEMU_IRQ_MASKED; case APIC_DM_FIXED: break; @@ -273,34 +272,42 @@ static void apic_bus_deliver(const uint32_t *deliver_bitmask, case APIC_DM_SMI: foreach_apic(apic_iter, deliver_bitmask, cpu_interrupt(apic_iter->cpu_env, CPU_INTERRUPT_SMI) ); - return; + return QEMU_IRQ_DELIVERED; case APIC_DM_NMI: foreach_apic(apic_iter, deliver_bitmask, cpu_interrupt(apic_iter->cpu_env, CPU_INTERRUPT_NMI) ); - return; + return QEMU_IRQ_DELIVERED; case APIC_DM_INIT: /* normal INIT IPI sent to processors */ foreach_apic(apic_iter, deliver_bitmask, cpu_interrupt(apic_iter->cpu_env, CPU_INTERRUPT_INIT) ); - return; + return QEMU_IRQ_DELIVERED; case APIC_DM_EXTINT: /* handled in I/O APIC code */ break; default: - return; + return QEMU_IRQ_MASKED; } + ret = QEMU_IRQ_MASKED; foreach_apic(apic_iter, deliver_bitmask, - apic_set_irq(apic_iter, vector_num, trigger_mode) ); + if (ret == QEMU_IRQ_MASKED) + ret = QEMU_IRQ_COALESCED; + if (apic_set_irq(apic_iter, vector_num, + trigger_mode) == QEMU_IRQ_DELIVERED) { + ret = QEMU_IRQ_DELIVERED; + } + ); + return ret; } -void apic_deliver_irq(uint8_t dest, uint8_t dest_mode, - uint8_t delivery_mode, uint8_t vector_num, - uint8_t polarity, uint8_t trigger_mode) +int apic_deliver_irq(uint8_t dest, uint8_t dest_mode, + uint8_t delivery_mode, uint8_t vector_num, + uint8_t polarity, uint8_t trigger_mode) { uint32_t deliver_bitmask[MAX_APIC_WORDS]; @@ -308,8 +315,8 @@ void apic_deliver_irq(uint8_t dest, uint8_t dest_mode, " polarity %d trigger_mode %d\n", __func__, dest, dest_mode, delivery_mode, vector_num, polarity, trigger_mode); apic_get_delivery_bitmask(deliver_bitmask, dest, dest_mode); - apic_bus_deliver(deliver_bitmask, delivery_mode, vector_num, polarity, - trigger_mode); + return apic_bus_deliver(deliver_bitmask, delivery_mode, vector_num, + polarity, trigger_mode); } void cpu_set_apic_base(CPUState *env, uint64_t val) @@ -402,22 +409,10 @@ static void apic_update_irq(APICState *s) cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD); } -void apic_reset_irq_delivered(void) -{ - DPRINTF_C("%s: old coalescing %d\n", __func__, apic_irq_delivered); - apic_irq_delivered = 0; -} - -int apic_get_irq_delivered(void) -{ - DPRINTF_C("%s: returning coalescing %d\n", __func__, apic_irq_delivered); - return apic_irq_delivered; -} - -static void apic_set_irq(APICState *s, int vector_num, int trigger_mode) +static int apic_set_irq(APICState *s, int vector_num, int trigger_mode) { - apic_irq_delivered += !get_bit(s->irr, vector_num); - DPRINTF_C("%s: coalescing %d\n", __func__, apic_irq_delivered); + int ret = get_bit(s->irr, vector_num) ? QEMU_IRQ_COALESCED + : QEMU_IRQ_DELIVERED; set_bit(s->irr, vector_num); if (trigger_mode) @@ -425,6 +420,7 @@ static void apic_set_irq(APICState *s, int vector_num, int trigger_mode) else reset_bit(s->tmr, vector_num); apic_update_irq(s); + return ret; } static void apic_eoi(APICState *s) diff --git a/hw/apic.h b/hw/apic.h index e1954f4..738d98a 100644 --- a/hw/apic.h +++ b/hw/apic.h @@ -2,17 +2,14 @@ #define APIC_H typedef struct IOAPICState IOAPICState; -void apic_deliver_irq(uint8_t dest, uint8_t dest_mode, - uint8_t delivery_mode, - uint8_t vector_num, uint8_t polarity, - uint8_t trigger_mode); +int apic_deliver_irq(uint8_t dest, uint8_t dest_mode, + uint8_t delivery_mode, uint8_t vector_num, + uint8_t polarity, uint8_t trigger_mode); int apic_init(CPUState *env); int apic_accept_pic_intr(CPUState *env); void apic_deliver_pic_intr(CPUState *env, int level); int apic_get_interrupt(CPUState *env); qemu_irq *ioapic_init(void); -void apic_reset_irq_delivered(void); -int apic_get_irq_delivered(void); int cpu_is_bsp(CPUState *env); diff --git a/hw/i8259.c b/hw/i8259.c index f743ee8..09150c4 100644 --- a/hw/i8259.c +++ b/hw/i8259.c @@ -189,6 +189,9 @@ int64_t irq_time[16]; static void i8259_set_irq(qemu_irq irq, void *opaque, int n, int level) { PicState2 *s = opaque; + PicState *pic; + int result = QEMU_IRQ_DELIVERED; + int mask; #if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT) if (level != irq_level[n]) { @@ -205,8 +208,19 @@ static void i8259_set_irq(qemu_irq irq, void *opaque, int n, int level) irq_time[n] = qemu_get_clock(vm_clock); } #endif - pic_set_irq1(&s->pics[n >> 3], n & 7, level); + pic = &s->pics[n >> 3]; + n &= 7; + mask = 1 << n; + if (level) { + if (pic->imr & mask) { + result = QEMU_IRQ_MASKED; + } else if (pic->irr & mask) { + result = QEMU_IRQ_COALESCED; + } + } + pic_set_irq1(pic, n, level); pic_update_irq(s); + qemu_irq_fire_delivery_cb(irq, level, result); } /* acknowledge interrupt 'irq' */ diff --git a/hw/ioapic.c b/hw/ioapic.c index d818573..b54738f 100644 --- a/hw/ioapic.c +++ b/hw/ioapic.c @@ -58,7 +58,7 @@ struct IOAPICState { uint64_t ioredtbl[IOAPIC_NUM_PINS]; }; -static void ioapic_service(IOAPICState *s) +static int ioapic_service(IOAPICState *s) { uint8_t i; uint8_t trig_mode; @@ -69,12 +69,16 @@ static void ioapic_service(IOAPICState *s) uint8_t dest; uint8_t dest_mode; uint8_t polarity; + int ret = QEMU_IRQ_MASKED; for (i = 0; i < IOAPIC_NUM_PINS; i++) { mask = 1 << i; if (s->irr & mask) { entry = s->ioredtbl[i]; if (!(entry & IOAPIC_LVT_MASKED)) { + if (ret == QEMU_IRQ_MASKED) { + ret = QEMU_IRQ_COALESCED; + } trig_mode = ((entry >> 15) & 1); dest = entry >> 56; dest_mode = (entry >> 11) & 1; @@ -87,16 +91,21 @@ static void ioapic_service(IOAPICState *s) else vector = entry & 0xff; - apic_deliver_irq(dest, dest_mode, delivery_mode, - vector, polarity, trig_mode); + if (apic_deliver_irq(dest, dest_mode, + delivery_mode, vector, polarity, + trig_mode) == QEMU_IRQ_DELIVERED) { + ret = QEMU_IRQ_DELIVERED; + } } } } + return ret; } static void ioapic_set_irq(qemu_irq irq, void *opaque, int vector, int level) { IOAPICState *s = opaque; + int result = QEMU_IRQ_MASKED; /* ISA IRQs map to GSI 1-1 except for IRQ0 which maps * to GSI 2. GSI maps to ioapic 1-1. This is not @@ -114,7 +123,7 @@ static void ioapic_set_irq(qemu_irq irq, void *opaque, int vector, int level) /* level triggered */ if (level) { s->irr |= mask; - ioapic_service(s); + result = ioapic_service(s); } else { s->irr &= ~mask; } @@ -122,10 +131,11 @@ static void ioapic_set_irq(qemu_irq irq, void *opaque, int vector, int level) /* edge triggered */ if (level) { s->irr |= mask; - ioapic_service(s); + result = ioapic_service(s); } } } + qemu_irq_fire_delivery_cb(irq, level, result); } static uint32_t ioapic_mem_readl(void *opaque, target_phys_addr_t addr) diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c index c3e6a70..cbb98a4 100644 --- a/hw/mc146818rtc.c +++ b/hw/mc146818rtc.c @@ -25,7 +25,6 @@ #include "qemu-timer.h" #include "sysemu.h" #include "pc.h" -#include "apic.h" #include "isa.h" #include "hpet_emul.h" #include "mc146818rtc.h" @@ -101,7 +100,7 @@ typedef struct RTCState { QEMUTimer *second_timer2; } RTCState; -static void rtc_irq_raise(qemu_irq irq) +static void rtc_irq_raise(RTCState *s, IRQMsg *msg) { /* When HPET is operating in legacy mode, RTC interrupts are disabled * We block qemu_irq_raise, but not qemu_irq_lower, in case legacy @@ -109,9 +108,14 @@ static void rtc_irq_raise(qemu_irq irq) * be lowered in any case */ #if defined TARGET_I386 - if (!hpet_in_legacy_mode()) + if (hpet_in_legacy_mode()) { + if (msg) { + msg->delivery_cb(s->irq, s, -1, -1, QEMU_IRQ_MASKED); + } + return; + } #endif - qemu_irq_raise(irq); + qemu_irq_raise_msg(s->irq, msg); } static void rtc_set_time(RTCState *s); @@ -131,20 +135,41 @@ static void rtc_coalesced_timer_update(RTCState *s) } } +static void rtc_periodic_delivery_cb(qemu_irq irq, void *opaque, int n, + int level, int result) +{ + RTCState *s = opaque; + + if (result == QEMU_IRQ_COALESCED) { + s->irq_coalesced++; + rtc_coalesced_timer_update(s); + DPRINTF_C("cmos: coalesced irqs increased to %d\n", s->irq_coalesced); + } +} + +static void rtc_reinject_delivery_cb(qemu_irq irq, void *opaque, int n, + int level, int result) +{ + RTCState *s = opaque; + + if (result != QEMU_IRQ_COALESCED) { + s->irq_coalesced--; + DPRINTF_C("cmos: coalesced irqs decreased to %d\n", s->irq_coalesced); + } +} + static void rtc_coalesced_timer(void *opaque) { RTCState *s = opaque; + IRQMsg msg = { + .delivery_cb = rtc_reinject_delivery_cb, + .delivery_opaque = s, + }; if (s->irq_coalesced != 0) { - apic_reset_irq_delivered(); s->cmos_data[RTC_REG_C] |= 0xc0; DPRINTF_C("cmos: injecting from timer\n"); - rtc_irq_raise(s->irq); - if (apic_get_irq_delivered()) { - s->irq_coalesced--; - DPRINTF_C("cmos: coalesced irqs decreased to %d\n", - s->irq_coalesced); - } + rtc_irq_raise(s, &msg); } rtc_coalesced_timer_update(s); @@ -203,19 +228,18 @@ static void rtc_periodic_timer(void *opaque) s->cmos_data[RTC_REG_C] |= 0xc0; #ifdef TARGET_I386 if(rtc_td_hack) { - if (s->irq_reinject_on_ack_count >= RTC_REINJECT_ON_ACK_COUNT) - s->irq_reinject_on_ack_count = 0; - apic_reset_irq_delivered(); - rtc_irq_raise(s->irq); - if (!apic_get_irq_delivered()) { - s->irq_coalesced++; - rtc_coalesced_timer_update(s); - DPRINTF_C("cmos: coalesced irqs increased to %d\n", - s->irq_coalesced); + IRQMsg msg = { + .delivery_cb = rtc_periodic_delivery_cb, + .delivery_opaque = s, + }; + + if (s->irq_reinject_on_ack_count >= RTC_REINJECT_ON_ACK_COUNT) { + s->irq_reinject_on_ack_count = 0; } + rtc_irq_raise(s, &msg); } else #endif - rtc_irq_raise(s->irq); + rtc_irq_raise(s, NULL); } if (s->cmos_data[RTC_REG_B] & REG_B_SQWE) { /* Not square wave at all but we don't want 2048Hz interrupts! @@ -444,7 +468,7 @@ static void rtc_update_second2(void *opaque) s->cmos_data[RTC_HOURS_ALARM] == s->current_tm.tm_hour)) { s->cmos_data[RTC_REG_C] |= 0xa0; - rtc_irq_raise(s->irq); + rtc_irq_raise(s, NULL); } } @@ -452,7 +476,7 @@ static void rtc_update_second2(void *opaque) s->cmos_data[RTC_REG_C] |= REG_C_UF; if (s->cmos_data[RTC_REG_B] & REG_B_UIE) { s->cmos_data[RTC_REG_C] |= REG_C_IRQF; - rtc_irq_raise(s->irq); + rtc_irq_raise(s, NULL); } /* clear update in progress bit */ @@ -488,15 +512,14 @@ static uint32_t cmos_ioport_read(void *opaque, uint32_t addr) #ifdef TARGET_I386 if(s->irq_coalesced && s->irq_reinject_on_ack_count < RTC_REINJECT_ON_ACK_COUNT) { + IRQMsg msg = { + .delivery_cb = rtc_reinject_delivery_cb, + .delivery_opaque = s, + }; + s->irq_reinject_on_ack_count++; - apic_reset_irq_delivered(); DPRINTF_C("cmos: injecting on ack\n"); - qemu_irq_raise(s->irq); - if (apic_get_irq_delivered()) { - s->irq_coalesced--; - DPRINTF_C("cmos: coalesced irqs decreased to %d\n", - s->irq_coalesced); - } + qemu_irq_raise_msg(s->irq, &msg); break; } #endif diff --git a/hw/pc.c b/hw/pc.c index 20057ca..6129e59 100644 --- a/hw/pc.c +++ b/hw/pc.c @@ -77,16 +77,37 @@ struct e820_table { static struct e820_table e820_table; +static void isa_irq_delivery_cb(qemu_irq irq, void *opaque, int n, int level, + int result) +{ + int *result_ptr = opaque; + + *result_ptr = result; +} + void isa_irq_handler(qemu_irq irq, void *opaque, int n, int level) { - IsaIrqState *isa = (IsaIrqState *)opaque; + int result = QEMU_IRQ_MASKED; + IRQMsg msg = { + .delivery_cb = isa_irq_delivery_cb, + }; + IsaIrqState *isa = opaque; + int ioapic_result; DPRINTF("isa_irqs: %s irq %d\n", level? "raise" : "lower", n); if (n < 16) { - qemu_set_irq(isa->i8259[n], level); + msg.delivery_opaque = &result; + qemu_set_irq_msg(isa->i8259[n], level, &msg); + } + if (isa->ioapic) { + msg.delivery_opaque = &ioapic_result; + qemu_set_irq_msg(isa->ioapic[n], level, &msg); + if (ioapic_result == QEMU_IRQ_DELIVERED || + result == QEMU_IRQ_MASKED) { + result = ioapic_result; + } } - if (isa->ioapic) - qemu_set_irq(isa->ioapic[n], level); + qemu_irq_fire_delivery_cb(irq, level, result); }; static void ioport80_write(void *opaque, uint32_t addr, uint32_t data) -- 1.6.0.2