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); |