X-Git-Url: https://vcs.maemo.org/git/?a=blobdiff_plain;ds=sidebyside;f=hw%2Fapic.c;h=3a442bf8264b4f60b44003d1ef5979c6271097a1;hb=cd346349b45ef056f138a184f660b8c34c3213cc;hp=581aa7b67babd84896949641833d139af3ec001d;hpb=b1fc0348b1ddc935fca98bddc7ee1c8c64e91f0b;p=qemu diff --git a/hw/apic.c b/hw/apic.c index 581aa7b..3a442bf 100644 --- a/hw/apic.c +++ b/hw/apic.c @@ -1,6 +1,6 @@ /* * APIC support - * + * * Copyright (c) 2004-2005 Fabrice Bellard * * This library is free software; you can redistribute it and/or @@ -60,6 +60,9 @@ #define APIC_SV_ENABLE (1 << 8) +#define MAX_APICS 255 +#define MAX_APIC_WORDS 8 + typedef struct APICState { CPUState *cpu_env; uint32_t apicbase; @@ -81,8 +84,6 @@ typedef struct APICState { uint32_t initial_count; int64_t initial_count_load_time, next_time; QEMUTimer *timer; - - struct APICState *next_apic; } APICState; struct IOAPICState { @@ -94,14 +95,95 @@ struct IOAPICState { }; static int apic_io_memory; -static APICState *first_local_apic = NULL; +static APICState *local_apics[MAX_APICS + 1]; static int last_apic_id = 0; static void apic_init_ipi(APICState *s); static void apic_set_irq(APICState *s, int vector_num, int trigger_mode); static void apic_update_irq(APICState *s); -static void apic_bus_deliver(uint32_t deliver_bitmask, uint8_t delivery_mode, +/* Find first bit starting from msb. Return 0 if value = 0 */ +static int fls_bit(uint32_t value) +{ + unsigned int ret = 0; + +#if defined(HOST_I386) + __asm__ __volatile__ ("bsr %1, %0\n" : "+r" (ret) : "rm" (value)); + return ret; +#else + if (value > 0xffff) + value >>= 16, ret = 16; + if (value > 0xff) + value >>= 8, ret += 8; + if (value > 0xf) + value >>= 4, ret += 4; + if (value > 0x3) + value >>= 2, ret += 2; + return ret + (value >> 1); +#endif +} + +/* Find first bit starting from lsb. Return 0 if value = 0 */ +static int ffs_bit(uint32_t value) +{ + unsigned int ret = 0; + +#if defined(HOST_I386) + __asm__ __volatile__ ("bsf %1, %0\n" : "+r" (ret) : "rm" (value)); + return ret; +#else + if (!value) + return 0; + if (!(value & 0xffff)) + value >>= 16, ret = 16; + if (!(value & 0xff)) + value >>= 8, ret += 8; + if (!(value & 0xf)) + value >>= 4, ret += 4; + if (!(value & 0x3)) + value >>= 2, ret += 2; + if (!(value & 0x1)) + ret++; + return ret; +#endif +} + +static inline void set_bit(uint32_t *tab, int index) +{ + int i, mask; + i = index >> 5; + mask = 1 << (index & 0x1f); + tab[i] |= mask; +} + +static inline void reset_bit(uint32_t *tab, int index) +{ + int i, mask; + i = index >> 5; + mask = 1 << (index & 0x1f); + tab[i] &= ~mask; +} + +#define foreach_apic(apic, deliver_bitmask, code) \ +{\ + int __i, __j, __mask;\ + for(__i = 0; __i < MAX_APIC_WORDS; __i++) {\ + __mask = deliver_bitmask[__i];\ + if (__mask) {\ + for(__j = 0; __j < 32; __j++) {\ + if (__mask & (1 << __j)) {\ + apic = local_apics[__i * 32 + __j];\ + if (apic) {\ + code;\ + }\ + }\ + }\ + }\ + }\ +} + +static void apic_bus_deliver(const uint32_t *deliver_bitmask, + uint8_t delivery_mode, uint8_t vector_num, uint8_t polarity, uint8_t trigger_mode) { @@ -109,8 +191,26 @@ static void apic_bus_deliver(uint32_t deliver_bitmask, uint8_t delivery_mode, switch (delivery_mode) { case APIC_DM_LOWPRI: + /* XXX: search for focus processor, arbitration */ + { + int i, d; + d = -1; + for(i = 0; i < MAX_APIC_WORDS; i++) { + if (deliver_bitmask[i]) { + d = i * 32 + ffs_bit(deliver_bitmask[i]); + break; + } + } + if (d >= 0) { + apic_iter = local_apics[d]; + if (apic_iter) { + apic_set_irq(apic_iter, vector_num, trigger_mode); + } + } + } + return; + case APIC_DM_FIXED: - /* XXX: arbitration */ break; case APIC_DM_SMI: @@ -119,12 +219,10 @@ static void apic_bus_deliver(uint32_t deliver_bitmask, uint8_t delivery_mode, case APIC_DM_INIT: /* normal INIT IPI sent to processors */ - for (apic_iter = first_local_apic; apic_iter != NULL; - apic_iter = apic_iter->next_apic) { - apic_init_ipi(apic_iter); - } + foreach_apic(apic_iter, deliver_bitmask, + apic_init_ipi(apic_iter) ); return; - + case APIC_DM_EXTINT: /* handled in I/O APIC code */ break; @@ -133,20 +231,17 @@ static void apic_bus_deliver(uint32_t deliver_bitmask, uint8_t delivery_mode, return; } - for (apic_iter = first_local_apic; apic_iter != NULL; - apic_iter = apic_iter->next_apic) { - if (deliver_bitmask & (1 << apic_iter->id)) - apic_set_irq(apic_iter, vector_num, trigger_mode); - } + foreach_apic(apic_iter, deliver_bitmask, + apic_set_irq(apic_iter, vector_num, trigger_mode) ); } void cpu_set_apic_base(CPUState *env, uint64_t val) { APICState *s = env->apic_state; #ifdef DEBUG_APIC - printf("cpu_set_apic_base: %016llx\n", val); + printf("cpu_set_apic_base: %016" PRIx64 "\n", val); #endif - s->apicbase = (val & 0xfffff000) | + s->apicbase = (val & 0xfffff000) | (s->apicbase & (MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE)); /* if disabled, cannot be enabled again */ if (!(val & MSR_IA32_APICBASE_ENABLE)) { @@ -160,7 +255,7 @@ uint64_t cpu_get_apic_base(CPUState *env) { APICState *s = env->apic_state; #ifdef DEBUG_APIC - printf("cpu_get_apic_base: %016llx\n", (uint64_t)s->apicbase); + printf("cpu_get_apic_base: %016" PRIx64 "\n", (uint64_t)s->apicbase); #endif return s->apicbase; } @@ -178,42 +273,6 @@ uint8_t cpu_get_apic_tpr(CPUX86State *env) return s->tpr >> 4; } -int fls_bit(int value) -{ - unsigned int ret = 0; - -#ifdef HOST_I386 - __asm__ __volatile__ ("bsr %1, %0\n" : "+r" (ret) : "rm" (value)); - return ret; -#else - if (value > 0xffff) - value >>= 16, ret = 16; - if (value > 0xff) - value >>= 8, ret += 8; - if (value > 0xf) - value >>= 4, ret += 4; - if (value > 0x3) - value >>= 2, ret += 2; - return ret + (value >> 1); -#endif -} - -static inline void set_bit(uint32_t *tab, int index) -{ - int i, mask; - i = index >> 5; - mask = 1 << (index & 0x1f); - tab[i] |= mask; -} - -static inline void reset_bit(uint32_t *tab, int index) -{ - int i, mask; - i = index >> 5; - mask = 1 << (index & 0x1f); - tab[i] &= ~mask; -} - /* return -1 if no bit is set */ static int get_highest_priority_int(uint32_t *tab) { @@ -285,26 +344,37 @@ static void apic_eoi(APICState *s) apic_update_irq(s); } -static uint32_t apic_get_delivery_bitmask(uint8_t dest, uint8_t dest_mode) +static void apic_get_delivery_bitmask(uint32_t *deliver_bitmask, + uint8_t dest, uint8_t dest_mode) { - uint32_t mask = 0; APICState *apic_iter; + int i; if (dest_mode == 0) { - if (dest == 0xff) - mask = 0xff; - else - mask = 1 << dest; + if (dest == 0xff) { + memset(deliver_bitmask, 0xff, MAX_APIC_WORDS * sizeof(uint32_t)); + } else { + memset(deliver_bitmask, 0x00, MAX_APIC_WORDS * sizeof(uint32_t)); + set_bit(deliver_bitmask, dest); + } } else { /* XXX: cluster mode */ - for (apic_iter = first_local_apic; apic_iter != NULL; - apic_iter = apic_iter->next_apic) { - if (dest & apic_iter->log_dest) - mask |= (1 << apic_iter->id); + memset(deliver_bitmask, 0x00, MAX_APIC_WORDS * sizeof(uint32_t)); + for(i = 0; i < MAX_APICS; i++) { + apic_iter = local_apics[i]; + if (apic_iter) { + if (apic_iter->dest_mode == 0xf) { + if (dest & apic_iter->log_dest) + set_bit(deliver_bitmask, i); + } else if (apic_iter->dest_mode == 0x0) { + if ((dest & 0xf0) == (apic_iter->log_dest & 0xf0) && + (dest & apic_iter->log_dest & 0x0f)) { + set_bit(deliver_bitmask, i); + } + } + } } } - - return mask; } @@ -312,16 +382,15 @@ static void apic_init_ipi(APICState *s) { int i; - for(i = 0; i < APIC_LVT_NB; i++) - s->lvt[i] = 1 << 16; /* mask LVT */ s->tpr = 0; s->spurious_vec = 0xff; s->log_dest = 0; - s->dest_mode = 0; + s->dest_mode = 0xf; memset(s->isr, 0, sizeof(s->isr)); memset(s->tmr, 0, sizeof(s->tmr)); memset(s->irr, 0, sizeof(s->irr)); - memset(s->lvt, 0, sizeof(s->lvt)); + for(i = 0; i < APIC_LVT_NB; i++) + s->lvt[i] = 1 << 16; /* mask LVT */ s->esr = 0; memset(s->icr, 0, sizeof(s->icr)); s->divide_conf = 0; @@ -331,61 +400,62 @@ static void apic_init_ipi(APICState *s) s->next_time = 0; } +/* send a SIPI message to the CPU to start it */ +static void apic_startup(APICState *s, int vector_num) +{ + CPUState *env = s->cpu_env; + if (!(env->hflags & HF_HALTED_MASK)) + return; + env->eip = 0; + cpu_x86_load_seg_cache(env, R_CS, vector_num << 8, vector_num << 12, + 0xffff, 0); + env->hflags &= ~HF_HALTED_MASK; +} + static void apic_deliver(APICState *s, uint8_t dest, uint8_t dest_mode, uint8_t delivery_mode, uint8_t vector_num, uint8_t polarity, uint8_t trigger_mode) { - uint32_t deliver_bitmask = 0; + uint32_t deliver_bitmask[MAX_APIC_WORDS]; int dest_shorthand = (s->icr[0] >> 18) & 3; APICState *apic_iter; - switch (delivery_mode) { - case APIC_DM_LOWPRI: - /* XXX: serch for focus processor, arbitration */ - dest = s->id; + switch (dest_shorthand) { + case 0: + apic_get_delivery_bitmask(deliver_bitmask, dest, dest_mode); + break; + case 1: + memset(deliver_bitmask, 0x00, sizeof(deliver_bitmask)); + set_bit(deliver_bitmask, s->id); + break; + case 2: + memset(deliver_bitmask, 0xff, sizeof(deliver_bitmask)); + break; + case 3: + memset(deliver_bitmask, 0xff, sizeof(deliver_bitmask)); + reset_bit(deliver_bitmask, s->id); + break; + } + switch (delivery_mode) { case APIC_DM_INIT: { int trig_mode = (s->icr[0] >> 15) & 1; int level = (s->icr[0] >> 14) & 1; if (level == 0 && trig_mode == 1) { - for (apic_iter = first_local_apic; apic_iter != NULL; - apic_iter = apic_iter->next_apic) { - if (deliver_bitmask & (1 << apic_iter->id)) { - apic_iter->arb_id = apic_iter->id; - } - } + foreach_apic(apic_iter, deliver_bitmask, + apic_iter->arb_id = apic_iter->id ); return; } } break; case APIC_DM_SIPI: - for (apic_iter = first_local_apic; apic_iter != NULL; - apic_iter = apic_iter->next_apic) { - if (deliver_bitmask & (1 << apic_iter->id)) { - /* XXX: SMP support */ - /* apic_startup(apic_iter); */ - } - } + foreach_apic(apic_iter, deliver_bitmask, + apic_startup(apic_iter, vector_num) ); return; } - switch (dest_shorthand) { - case 0: - deliver_bitmask = apic_get_delivery_bitmask(dest, dest_mode); - break; - case 1: - deliver_bitmask = (1 << s->id); - break; - case 2: - deliver_bitmask = 0xffffffff; - break; - case 3: - deliver_bitmask = 0xffffffff & ~(1 << s->id); - break; - } - apic_bus_deliver(deliver_bitmask, delivery_mode, vector_num, polarity, trigger_mode); } @@ -401,24 +471,43 @@ int apic_get_interrupt(CPUState *env) return -1; if (!(s->spurious_vec & APIC_SV_ENABLE)) return -1; - + /* XXX: spurious IRQ handling */ intno = get_highest_priority_int(s->irr); if (intno < 0) return -1; - reset_bit(s->irr, intno); if (s->tpr && intno <= s->tpr) return s->spurious_vec & 0xff; + reset_bit(s->irr, intno); set_bit(s->isr, intno); apic_update_irq(s); return intno; } +int apic_accept_pic_intr(CPUState *env) +{ + APICState *s = env->apic_state; + uint32_t lvt0; + + if (!s) + return -1; + + 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))) + return 1; + + return 0; +} + static uint32_t apic_get_current_count(APICState *s) { int64_t d; uint32_t val; - d = (qemu_get_clock(vm_clock) - s->initial_count_load_time) >> + d = (qemu_get_clock(vm_clock) - s->initial_count_load_time) >> s->count_shift; if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) { /* periodic */ @@ -435,9 +524,9 @@ static uint32_t apic_get_current_count(APICState *s) static void apic_timer_update(APICState *s, int64_t current_time) { int64_t next_time, d; - + if (!(s->lvt[APIC_LVT_TIMER] & APIC_LVT_MASKED)) { - d = (current_time - s->initial_count_load_time) >> + d = (current_time - s->initial_count_load_time) >> s->count_shift; if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) { d = ((d / ((uint64_t)s->initial_count + 1)) + 1) * ((uint64_t)s->initial_count + 1); @@ -534,13 +623,13 @@ static uint32_t apic_mem_readl(void *opaque, target_phys_addr_t addr) case 0x28: val = s->esr; break; - case 0x32 ... 0x37: - val = s->lvt[index - 0x32]; - break; case 0x30: case 0x31: val = s->icr[index & 1]; break; + case 0x32 ... 0x37: + val = s->lvt[index - 0x32]; + break; case 0x38: val = s->initial_count; break; @@ -581,10 +670,15 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) case 0x02: s->id = (val >> 24); break; + case 0x03: + break; case 0x08: s->tpr = val; apic_update_irq(s); break; + case 0x09: + case 0x0a: + break; case 0x0b: /* EOI */ apic_eoi(s); break; @@ -598,6 +692,11 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) s->spurious_vec = val & 0x1ff; apic_update_irq(s); break; + case 0x10 ... 0x17: + case 0x18 ... 0x1f: + case 0x20 ... 0x27: + case 0x28: + break; case 0x30: s->icr[0] = val; apic_deliver(s, (s->icr[1] >> 24) & 0xff, (s->icr[0] >> 11) & 1, @@ -620,6 +719,8 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) s->initial_count_load_time = qemu_get_clock(vm_clock); apic_timer_update(s, s->initial_count_load_time); break; + case 0x39: + break; case 0x3e: { int v; @@ -662,6 +763,8 @@ static void apic_save(QEMUFile *f, void *opaque) qemu_put_be32s(f, &s->initial_count); qemu_put_be64s(f, &s->initial_count_load_time); qemu_put_be64s(f, &s->next_time); + + qemu_put_timer(f, s->timer); } static int apic_load(QEMUFile *f, void *opaque, int version_id) @@ -669,7 +772,7 @@ static int apic_load(QEMUFile *f, void *opaque, int version_id) APICState *s = opaque; int i; - if (version_id != 1) + if (version_id > 2) return -EINVAL; /* XXX: what if the base changes? (registered memory regions) */ @@ -696,6 +799,9 @@ static int apic_load(QEMUFile *f, void *opaque, int version_id) qemu_get_be32s(f, &s->initial_count); qemu_get_be64s(f, &s->initial_count_load_time); qemu_get_be64s(f, &s->next_time); + + if (version_id >= 2) + qemu_get_timer(f, s->timer); return 0; } @@ -703,6 +809,13 @@ 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; } static CPUReadMemoryFunc *apic_mem_read[3] = { @@ -721,33 +834,41 @@ int apic_init(CPUState *env) { APICState *s; + if (last_apic_id >= MAX_APICS) + return -1; s = qemu_mallocz(sizeof(APICState)); 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->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; + /* 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 on the global memory bus. */ - apic_io_memory = cpu_register_io_memory(0, apic_mem_read, + apic_io_memory = cpu_register_io_memory(0, apic_mem_read, apic_mem_write, NULL); cpu_register_physical_memory(s->apicbase & ~0xfff, 0x1000, apic_io_memory); } s->timer = qemu_new_timer(vm_clock, apic_timer, s); - register_savevm("apic", 0, 1, apic_save, apic_load, s); + register_savevm("apic", s->id, 2, apic_save, apic_load, s); qemu_register_reset(apic_reset, s); - s->next_apic = first_local_apic; - first_local_apic = s; - + local_apics[s->id] = s; return 0; } @@ -762,6 +883,7 @@ static void ioapic_service(IOAPICState *s) uint8_t dest; uint8_t dest_mode; uint8_t polarity; + uint32_t deliver_bitmask[MAX_APIC_WORDS]; for (i = 0; i < IOAPIC_NUM_PINS; i++) { mask = 1 << i; @@ -779,8 +901,10 @@ static void ioapic_service(IOAPICState *s) vector = pic_read_irq(isa_pic); else vector = entry & 0xff; - apic_bus_deliver(apic_get_delivery_bitmask(dest, dest_mode), - delivery_mode, vector, polarity, trig_mode); + + apic_get_delivery_bitmask(deliver_bitmask, dest, dest_mode); + apic_bus_deliver(deliver_bitmask, delivery_mode, + vector, polarity, trig_mode); } } } @@ -945,12 +1069,12 @@ IOAPICState *ioapic_init(void) ioapic_reset(s); s->id = last_apic_id++; - io_memory = cpu_register_io_memory(0, ioapic_mem_read, + io_memory = cpu_register_io_memory(0, ioapic_mem_read, ioapic_mem_write, s); cpu_register_physical_memory(0xfec00000, 0x1000, io_memory); register_savevm("ioapic", 0, 1, ioapic_save, ioapic_load, s); qemu_register_reset(ioapic_reset, s); - + return s; }