Commit a5b38b5171ea46e13612a3471a7cc735db6d6f72
1 parent
e2eb9d3e
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
Showing
3 changed files
with
57 additions
and
24 deletions
hw/apic.c
@@ -166,6 +166,37 @@ static inline void reset_bit(uint32_t *tab, int index) | @@ -166,6 +166,37 @@ static inline void reset_bit(uint32_t *tab, int index) | ||
166 | tab[i] &= ~mask; | 166 | tab[i] &= ~mask; |
167 | } | 167 | } |
168 | 168 | ||
169 | +void apic_local_deliver(CPUState *env, int vector) | ||
170 | +{ | ||
171 | + APICState *s = env->apic_state; | ||
172 | + uint32_t lvt = s->lvt[vector]; | ||
173 | + int trigger_mode; | ||
174 | + | ||
175 | + if (lvt & APIC_LVT_MASKED) | ||
176 | + return; | ||
177 | + | ||
178 | + switch ((lvt >> 8) & 7) { | ||
179 | + case APIC_DM_SMI: | ||
180 | + cpu_interrupt(env, CPU_INTERRUPT_SMI); | ||
181 | + break; | ||
182 | + | ||
183 | + case APIC_DM_NMI: | ||
184 | + cpu_interrupt(env, CPU_INTERRUPT_NMI); | ||
185 | + break; | ||
186 | + | ||
187 | + case APIC_DM_EXTINT: | ||
188 | + cpu_interrupt(env, CPU_INTERRUPT_HARD); | ||
189 | + break; | ||
190 | + | ||
191 | + case APIC_DM_FIXED: | ||
192 | + trigger_mode = APIC_TRIGGER_EDGE; | ||
193 | + if ((vector == APIC_LVT_LINT0 || vector == APIC_LVT_LINT1) && | ||
194 | + (lvt & APIC_LVT_LEVEL_TRIGGER)) | ||
195 | + trigger_mode = APIC_TRIGGER_LEVEL; | ||
196 | + apic_set_irq(s, lvt & 0xff, trigger_mode); | ||
197 | + } | ||
198 | +} | ||
199 | + | ||
169 | #define foreach_apic(apic, deliver_bitmask, code) \ | 200 | #define foreach_apic(apic, deliver_bitmask, code) \ |
170 | {\ | 201 | {\ |
171 | int __i, __j, __mask;\ | 202 | int __i, __j, __mask;\ |
@@ -502,10 +533,8 @@ int apic_accept_pic_intr(CPUState *env) | @@ -502,10 +533,8 @@ int apic_accept_pic_intr(CPUState *env) | ||
502 | 533 | ||
503 | lvt0 = s->lvt[APIC_LVT_LINT0]; | 534 | lvt0 = s->lvt[APIC_LVT_LINT0]; |
504 | 535 | ||
505 | - if (s->id == 0 && | ||
506 | - ((s->apicbase & MSR_IA32_APICBASE_ENABLE) == 0 || | ||
507 | - ((lvt0 & APIC_LVT_MASKED) == 0 && | ||
508 | - ((lvt0 >> 8) & 0x7) == APIC_DM_EXTINT))) | 536 | + if ((s->apicbase & MSR_IA32_APICBASE_ENABLE) == 0 || |
537 | + (lvt0 & APIC_LVT_MASKED) == 0) | ||
509 | return 1; | 538 | return 1; |
510 | 539 | ||
511 | return 0; | 540 | return 0; |
@@ -556,9 +585,7 @@ static void apic_timer(void *opaque) | @@ -556,9 +585,7 @@ static void apic_timer(void *opaque) | ||
556 | { | 585 | { |
557 | APICState *s = opaque; | 586 | APICState *s = opaque; |
558 | 587 | ||
559 | - if (!(s->lvt[APIC_LVT_TIMER] & APIC_LVT_MASKED)) { | ||
560 | - apic_set_irq(s, s->lvt[APIC_LVT_TIMER] & 0xff, APIC_TRIGGER_EDGE); | ||
561 | - } | 588 | + apic_local_deliver(s->cpu_env, APIC_LVT_TIMER); |
562 | apic_timer_update(s, s->next_time); | 589 | apic_timer_update(s, s->next_time); |
563 | } | 590 | } |
564 | 591 | ||
@@ -821,12 +848,14 @@ static void apic_reset(void *opaque) | @@ -821,12 +848,14 @@ static void apic_reset(void *opaque) | ||
821 | APICState *s = opaque; | 848 | APICState *s = opaque; |
822 | apic_init_ipi(s); | 849 | apic_init_ipi(s); |
823 | 850 | ||
824 | - /* | ||
825 | - * LINT0 delivery mode is set to ExtInt at initialization time | ||
826 | - * typically by BIOS, so PIC interrupt can be delivered to the | ||
827 | - * processor when local APIC is enabled. | ||
828 | - */ | ||
829 | - s->lvt[APIC_LVT_LINT0] = 0x700; | 851 | + if (s->id == 0) { |
852 | + /* | ||
853 | + * LINT0 delivery mode on CPU #0 is set to ExtInt at initialization | ||
854 | + * time typically by BIOS, so PIC interrupt can be delivered to the | ||
855 | + * processor when local APIC is enabled. | ||
856 | + */ | ||
857 | + s->lvt[APIC_LVT_LINT0] = 0x700; | ||
858 | + } | ||
830 | } | 859 | } |
831 | 860 | ||
832 | static CPUReadMemoryFunc *apic_mem_read[3] = { | 861 | static CPUReadMemoryFunc *apic_mem_read[3] = { |
@@ -851,19 +880,13 @@ int apic_init(CPUState *env) | @@ -851,19 +880,13 @@ int apic_init(CPUState *env) | ||
851 | if (!s) | 880 | if (!s) |
852 | return -1; | 881 | return -1; |
853 | env->apic_state = s; | 882 | env->apic_state = s; |
854 | - apic_init_ipi(s); | ||
855 | s->id = last_apic_id++; | 883 | s->id = last_apic_id++; |
856 | env->cpuid_apic_id = s->id; | 884 | env->cpuid_apic_id = s->id; |
857 | s->cpu_env = env; | 885 | s->cpu_env = env; |
858 | s->apicbase = 0xfee00000 | | 886 | s->apicbase = 0xfee00000 | |
859 | (s->id ? 0 : MSR_IA32_APICBASE_BSP) | MSR_IA32_APICBASE_ENABLE; | 887 | (s->id ? 0 : MSR_IA32_APICBASE_BSP) | MSR_IA32_APICBASE_ENABLE; |
860 | 888 | ||
861 | - /* | ||
862 | - * LINT0 delivery mode is set to ExtInt at initialization time | ||
863 | - * typically by BIOS, so PIC interrupt can be delivered to the | ||
864 | - * processor when local APIC is enabled. | ||
865 | - */ | ||
866 | - s->lvt[APIC_LVT_LINT0] = 0x700; | 889 | + apic_reset(s); |
867 | 890 | ||
868 | /* XXX: mapping more APICs at the same memory location */ | 891 | /* XXX: mapping more APICs at the same memory location */ |
869 | if (apic_io_memory == 0) { | 892 | if (apic_io_memory == 0) { |
hw/pc.c
@@ -113,9 +113,16 @@ int cpu_get_pic_interrupt(CPUState *env) | @@ -113,9 +113,16 @@ int cpu_get_pic_interrupt(CPUState *env) | ||
113 | 113 | ||
114 | static void pic_irq_request(void *opaque, int irq, int level) | 114 | static void pic_irq_request(void *opaque, int irq, int level) |
115 | { | 115 | { |
116 | - CPUState *env = opaque; | ||
117 | - if (level && apic_accept_pic_intr(env)) | ||
118 | - cpu_interrupt(env, CPU_INTERRUPT_HARD); | 116 | + CPUState *env = first_cpu; |
117 | + | ||
118 | + if (!level) | ||
119 | + return; | ||
120 | + | ||
121 | + while (env) { | ||
122 | + if (apic_accept_pic_intr(env)) | ||
123 | + apic_local_deliver(env, APIC_LINT0); | ||
124 | + env = env->next_cpu; | ||
125 | + } | ||
119 | } | 126 | } |
120 | 127 | ||
121 | /* PC cmos mappings */ | 128 | /* PC cmos mappings */ |
@@ -845,7 +852,7 @@ static void pc_init1(int ram_size, int vga_ram_size, | @@ -845,7 +852,7 @@ static void pc_init1(int ram_size, int vga_ram_size, | ||
845 | if (linux_boot) | 852 | if (linux_boot) |
846 | load_linux(kernel_filename, initrd_filename, kernel_cmdline); | 853 | load_linux(kernel_filename, initrd_filename, kernel_cmdline); |
847 | 854 | ||
848 | - cpu_irq = qemu_allocate_irqs(pic_irq_request, first_cpu, 1); | 855 | + cpu_irq = qemu_allocate_irqs(pic_irq_request, NULL, 1); |
849 | i8259 = i8259_init(cpu_irq[0]); | 856 | i8259 = i8259_init(cpu_irq[0]); |
850 | ferr_irq = i8259[13]; | 857 | ferr_irq = i8259[13]; |
851 | 858 |
hw/pc.h
@@ -39,8 +39,11 @@ void irq_info(void); | @@ -39,8 +39,11 @@ void irq_info(void); | ||
39 | /* APIC */ | 39 | /* APIC */ |
40 | typedef struct IOAPICState IOAPICState; | 40 | typedef struct IOAPICState IOAPICState; |
41 | 41 | ||
42 | +#define APIC_LINT0 3 | ||
43 | + | ||
42 | int apic_init(CPUState *env); | 44 | int apic_init(CPUState *env); |
43 | int apic_accept_pic_intr(CPUState *env); | 45 | int apic_accept_pic_intr(CPUState *env); |
46 | +void apic_local_deliver(CPUState *env, int vector); | ||
44 | int apic_get_interrupt(CPUState *env); | 47 | int apic_get_interrupt(CPUState *env); |
45 | IOAPICState *ioapic_init(void); | 48 | IOAPICState *ioapic_init(void); |
46 | void ioapic_set_irq(void *opaque, int vector, int level); | 49 | void ioapic_set_irq(void *opaque, int vector, int level); |