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 | 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 | 200 | #define foreach_apic(apic, deliver_bitmask, code) \ |
170 | 201 | {\ |
171 | 202 | int __i, __j, __mask;\ |
... | ... | @@ -502,10 +533,8 @@ int apic_accept_pic_intr(CPUState *env) |
502 | 533 | |
503 | 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 | 538 | return 1; |
510 | 539 | |
511 | 540 | return 0; |
... | ... | @@ -556,9 +585,7 @@ static void apic_timer(void *opaque) |
556 | 585 | { |
557 | 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 | 589 | apic_timer_update(s, s->next_time); |
563 | 590 | } |
564 | 591 | |
... | ... | @@ -821,12 +848,14 @@ static void apic_reset(void *opaque) |
821 | 848 | APICState *s = opaque; |
822 | 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 | 861 | static CPUReadMemoryFunc *apic_mem_read[3] = { |
... | ... | @@ -851,19 +880,13 @@ int apic_init(CPUState *env) |
851 | 880 | if (!s) |
852 | 881 | return -1; |
853 | 882 | env->apic_state = s; |
854 | - apic_init_ipi(s); | |
855 | 883 | s->id = last_apic_id++; |
856 | 884 | env->cpuid_apic_id = s->id; |
857 | 885 | s->cpu_env = env; |
858 | 886 | s->apicbase = 0xfee00000 | |
859 | 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 | 891 | /* XXX: mapping more APICs at the same memory location */ |
869 | 892 | if (apic_io_memory == 0) { | ... | ... |
hw/pc.c
... | ... | @@ -113,9 +113,16 @@ int cpu_get_pic_interrupt(CPUState *env) |
113 | 113 | |
114 | 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 | 128 | /* PC cmos mappings */ |
... | ... | @@ -845,7 +852,7 @@ static void pc_init1(int ram_size, int vga_ram_size, |
845 | 852 | if (linux_boot) |
846 | 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 | 856 | i8259 = i8259_init(cpu_irq[0]); |
850 | 857 | ferr_irq = i8259[13]; |
851 | 858 | ... | ... |
hw/pc.h
... | ... | @@ -39,8 +39,11 @@ void irq_info(void); |
39 | 39 | /* APIC */ |
40 | 40 | typedef struct IOAPICState IOAPICState; |
41 | 41 | |
42 | +#define APIC_LINT0 3 | |
43 | + | |
42 | 44 | int apic_init(CPUState *env); |
43 | 45 | int apic_accept_pic_intr(CPUState *env); |
46 | +void apic_local_deliver(CPUState *env, int vector); | |
44 | 47 | int apic_get_interrupt(CPUState *env); |
45 | 48 | IOAPICState *ioapic_init(void); |
46 | 49 | void ioapic_set_irq(void *opaque, int vector, int level); | ... | ... |