Current interrupt logic in Qemu unconditionally checks pending irqs on
PIC after checking local APIC, however, this is problematic.
        On x86 platform, PIC is usually connected to the LINT0 of local
APIC. In this way when local APIC is disabled, this pin behaves like
INTR. But when local APIC is enabled, its behavior can be determined by
LVT_LINT0: PIC should only deliver normal irq only when `external
interrupt' delivery mode is set.
        x86_64 Linux kernel uses PIT->PIC->LINT0 as NMI source when
performance counters are not available, but the logic described above
treats the NMI as normal interrupt which yields a 2x faster global timer
because an additional timer interrupt is injected on every tick. This
patch fixes this issue.

Thanks,
Qing

diff -uNra qemu-cvs/hw/apic.c qemu/hw/apic.c
--- qemu-cvs/hw/apic.c  2007-04-04 00:38:34.000000000 +0800
+++ qemu/hw/apic.c      2007-04-28 16:19:36.000000000 +0800
@@ -484,6 +484,20 @@
     return intno;
 }
 
+int apic_accept_pic_intr(CPUState *env)
+{
+    APICState *s = env->apic_state;
+    uint32_t lvt0 = s->lvt[APIC_LVT_LINT0];
+
+    if (env->cpu_index == 0 &&
+        ((s->apicbase & MSR_IA32_APICBASE_ENABLE) == 0 ||
+         ((lvt0 & APIC_LVT_MASKED) == 0 &&
+          ((lvt0 >> 8) & 0x7) == APIC_DM_EXTINT)))
+        return 1;
+
+    return 0;
+}
+
 static uint32_t apic_get_current_count(APICState *s)
 {
     int64_t d;
@@ -821,6 +835,13 @@
     s->apicbase = 0xfee00000 | 
         (s->id ? 0 : MSR_IA32_APICBASE_BSP) | MSR_IA32_APICBASE_ENABLE;
 
+    /*
+     * LINT0 delivery mode is set to ExtInt at initialization time, so
+     * PIC interrupt can be delivered to the processor when local APIC
+     * is enabled.
+     */
+    s->lvt[APIC_LVT_LINT0] = 0x700;
+
     /* XXX: mapping more APICs at the same memory location */
     if (apic_io_memory == 0) {
         /* NOTE: the APIC is directly connected to the CPU - it is not
diff -uNra qemu-cvs/hw/pc.c qemu/hw/pc.c
--- qemu-cvs/hw/pc.c    2007-04-08 02:14:41.000000000 +0800
+++ qemu/hw/pc.c        2007-04-28 16:19:36.000000000 +0800
@@ -98,6 +98,9 @@
         return intno;
     }
     /* read the irq from the PIC */
+    if (!apic_accept_pic_intr(env))
+        return -1;
+
     intno = pic_read_irq(isa_pic);
     return intno;
 }
diff -uNra qemu-cvs/vl.h qemu/vl.h
--- qemu-cvs/vl.h       2007-04-24 15:40:49.000000000 +0800
+++ qemu/vl.h   2007-04-28 16:19:36.000000000 +0800
@@ -1090,6 +1090,7 @@
 
 int apic_init(CPUState *env);
 int apic_get_interrupt(CPUState *env);
+int apic_accept_pic_intr(CPUState *env);
 IOAPICState *ioapic_init(void);
 void ioapic_set_irq(void *opaque, int vector, int level);

Attachment: qemu-pc-pic-apic-mutual-exclusive.patch
Description: qemu-pc-pic-apic-mutual-exclusive.patch

Reply via email to