From a5b38b5171ea46e13612a3471a7cc735db6d6f72 Mon Sep 17 00:00:00 2001 From: aurel32 Date: Sun, 13 Apr 2008 16:08:30 +0000 Subject: [PATCH] x86: Rework local IRQ delivery for APICs (Jan Kiszka) git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@4207 c046a42c-6fe2-441c-8c8c-71466251a162 --- hw/apic.c | 63 +++++++++++++++++++++++++++++++++++++++++++-------------------- hw/pc.c | 15 +++++++++++---- hw/pc.h | 3 +++ 3 files changed, 57 insertions(+), 24 deletions(-) diff --git a/hw/apic.c b/hw/apic.c index a6964e0a65..a1ebf21c56 100644 --- a/hw/apic.c +++ b/hw/apic.c @@ -166,6 +166,37 @@ static inline void reset_bit(uint32_t *tab, int index) tab[i] &= ~mask; } +void apic_local_deliver(CPUState *env, int vector) +{ + APICState *s = env->apic_state; + uint32_t lvt = s->lvt[vector]; + int trigger_mode; + + if (lvt & APIC_LVT_MASKED) + return; + + switch ((lvt >> 8) & 7) { + case APIC_DM_SMI: + cpu_interrupt(env, CPU_INTERRUPT_SMI); + break; + + case APIC_DM_NMI: + cpu_interrupt(env, CPU_INTERRUPT_NMI); + break; + + case APIC_DM_EXTINT: + cpu_interrupt(env, CPU_INTERRUPT_HARD); + break; + + case APIC_DM_FIXED: + trigger_mode = APIC_TRIGGER_EDGE; + if ((vector == APIC_LVT_LINT0 || vector == APIC_LVT_LINT1) && + (lvt & APIC_LVT_LEVEL_TRIGGER)) + trigger_mode = APIC_TRIGGER_LEVEL; + apic_set_irq(s, lvt & 0xff, trigger_mode); + } +} + #define foreach_apic(apic, deliver_bitmask, code) \ {\ int __i, __j, __mask;\ @@ -502,10 +533,8 @@ int apic_accept_pic_intr(CPUState *env) lvt0 = s->lvt[APIC_LVT_LINT0]; - if (s->id == 0 && - ((s->apicbase & MSR_IA32_APICBASE_ENABLE) == 0 || - ((lvt0 & APIC_LVT_MASKED) == 0 && - ((lvt0 >> 8) & 0x7) == APIC_DM_EXTINT))) + if ((s->apicbase & MSR_IA32_APICBASE_ENABLE) == 0 || + (lvt0 & APIC_LVT_MASKED) == 0) return 1; return 0; @@ -556,9 +585,7 @@ static void apic_timer(void *opaque) { APICState *s = opaque; - if (!(s->lvt[APIC_LVT_TIMER] & APIC_LVT_MASKED)) { - apic_set_irq(s, s->lvt[APIC_LVT_TIMER] & 0xff, APIC_TRIGGER_EDGE); - } + apic_local_deliver(s->cpu_env, APIC_LVT_TIMER); apic_timer_update(s, s->next_time); } @@ -821,12 +848,14 @@ static void apic_reset(void *opaque) APICState *s = opaque; apic_init_ipi(s); - /* - * LINT0 delivery mode is set to ExtInt at initialization time - * typically by BIOS, so PIC interrupt can be delivered to the - * processor when local APIC is enabled. - */ - s->lvt[APIC_LVT_LINT0] = 0x700; + if (s->id == 0) { + /* + * LINT0 delivery mode on CPU #0 is set to ExtInt at initialization + * time typically by BIOS, so PIC interrupt can be delivered to the + * processor when local APIC is enabled. + */ + s->lvt[APIC_LVT_LINT0] = 0x700; + } } static CPUReadMemoryFunc *apic_mem_read[3] = { @@ -851,19 +880,13 @@ int apic_init(CPUState *env) if (!s) return -1; env->apic_state = s; - apic_init_ipi(s); s->id = last_apic_id++; env->cpuid_apic_id = s->id; s->cpu_env = env; s->apicbase = 0xfee00000 | (s->id ? 0 : MSR_IA32_APICBASE_BSP) | MSR_IA32_APICBASE_ENABLE; - /* - * LINT0 delivery mode is set to ExtInt at initialization time - * typically by BIOS, so PIC interrupt can be delivered to the - * processor when local APIC is enabled. - */ - s->lvt[APIC_LVT_LINT0] = 0x700; + apic_reset(s); /* XXX: mapping more APICs at the same memory location */ if (apic_io_memory == 0) { diff --git a/hw/pc.c b/hw/pc.c index e126f923d0..44a021bc23 100644 --- a/hw/pc.c +++ b/hw/pc.c @@ -113,9 +113,16 @@ int cpu_get_pic_interrupt(CPUState *env) static void pic_irq_request(void *opaque, int irq, int level) { - CPUState *env = opaque; - if (level && apic_accept_pic_intr(env)) - cpu_interrupt(env, CPU_INTERRUPT_HARD); + CPUState *env = first_cpu; + + if (!level) + return; + + while (env) { + if (apic_accept_pic_intr(env)) + apic_local_deliver(env, APIC_LINT0); + env = env->next_cpu; + } } /* PC cmos mappings */ @@ -845,7 +852,7 @@ static void pc_init1(int ram_size, int vga_ram_size, if (linux_boot) load_linux(kernel_filename, initrd_filename, kernel_cmdline); - cpu_irq = qemu_allocate_irqs(pic_irq_request, first_cpu, 1); + cpu_irq = qemu_allocate_irqs(pic_irq_request, NULL, 1); i8259 = i8259_init(cpu_irq[0]); ferr_irq = i8259[13]; diff --git a/hw/pc.h b/hw/pc.h index 862659999b..97adc6ef84 100644 --- a/hw/pc.h +++ b/hw/pc.h @@ -39,8 +39,11 @@ void irq_info(void); /* APIC */ typedef struct IOAPICState IOAPICState; +#define APIC_LINT0 3 + int apic_init(CPUState *env); int apic_accept_pic_intr(CPUState *env); +void apic_local_deliver(CPUState *env, int vector); int apic_get_interrupt(CPUState *env); IOAPICState *ioapic_init(void); void ioapic_set_irq(void *opaque, int vector, int level); -- 2.11.0