📄 apic.c
字号:
/* * APIC support * * Copyright (c) 2004-2005 Fabrice Bellard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include "vl.h"//#define DEBUG_APIC//#define DEBUG_IOAPIC/* APIC Local Vector Table */#define APIC_LVT_TIMER 0#define APIC_LVT_THERMAL 1#define APIC_LVT_PERFORM 2#define APIC_LVT_LINT0 3#define APIC_LVT_LINT1 4#define APIC_LVT_ERROR 5#define APIC_LVT_NB 6/* APIC delivery modes */#define APIC_DM_FIXED 0#define APIC_DM_LOWPRI 1#define APIC_DM_SMI 2#define APIC_DM_NMI 4#define APIC_DM_INIT 5#define APIC_DM_SIPI 6#define APIC_DM_EXTINT 7/* APIC destination mode */#define APIC_DESTMODE_FLAT 0xf#define APIC_DESTMODE_CLUSTER 1#define APIC_TRIGGER_EDGE 0#define APIC_TRIGGER_LEVEL 1#define APIC_LVT_TIMER_PERIODIC (1<<17)#define APIC_LVT_MASKED (1<<16)#define APIC_LVT_LEVEL_TRIGGER (1<<15)#define APIC_LVT_REMOTE_IRR (1<<14)#define APIC_INPUT_POLARITY (1<<13)#define APIC_SEND_PENDING (1<<12)#define IOAPIC_NUM_PINS 0x18#define ESR_ILLEGAL_ADDRESS (1 << 7)#define APIC_SV_ENABLE (1 << 8)#define MAX_APICS 255#define MAX_APIC_WORDS 8typedef struct APICState { CPUState *cpu_env; uint32_t apicbase; uint8_t id; uint8_t arb_id; uint8_t tpr; uint32_t spurious_vec; uint8_t log_dest; uint8_t dest_mode; uint32_t isr[8]; /* in service register */ uint32_t tmr[8]; /* trigger mode register */ uint32_t irr[8]; /* interrupt request register */ uint32_t lvt[APIC_LVT_NB]; uint32_t esr; /* error register */ uint32_t icr[2]; uint32_t divide_conf; int count_shift; uint32_t initial_count; int64_t initial_count_load_time, next_time; QEMUTimer *timer;} APICState;struct IOAPICState { uint8_t id; uint8_t ioregsel; uint32_t irr; uint64_t ioredtbl[IOAPIC_NUM_PINS];};static int apic_io_memory;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);/* 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){ APICState *apic_iter; 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: break; case APIC_DM_SMI: case APIC_DM_NMI: break; case APIC_DM_INIT: /* normal INIT IPI sent to processors */ foreach_apic(apic_iter, deliver_bitmask, apic_init_ipi(apic_iter) ); return; case APIC_DM_EXTINT: /* handled in I/O APIC code */ break; default: return; } 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: %016" PRIx64 "\n", val);#endif 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)) { s->apicbase &= ~MSR_IA32_APICBASE_ENABLE; env->cpuid_features &= ~CPUID_APIC; s->spurious_vec &= ~APIC_SV_ENABLE; }}uint64_t cpu_get_apic_base(CPUState *env){ APICState *s = env->apic_state;#ifdef DEBUG_APIC printf("cpu_get_apic_base: %016" PRIx64 "\n", (uint64_t)s->apicbase);#endif return s->apicbase;}void cpu_set_apic_tpr(CPUX86State *env, uint8_t val){ APICState *s = env->apic_state; s->tpr = (val & 0x0f) << 4; apic_update_irq(s);}uint8_t cpu_get_apic_tpr(CPUX86State *env){ APICState *s = env->apic_state; return s->tpr >> 4;}/* return -1 if no bit is set */static int get_highest_priority_int(uint32_t *tab){ int i; for(i = 7; i >= 0; i--) { if (tab[i] != 0) { return i * 32 + fls_bit(tab[i]); } } return -1;}static int apic_get_ppr(APICState *s){ int tpr, isrv, ppr; tpr = (s->tpr >> 4); isrv = get_highest_priority_int(s->isr); if (isrv < 0) isrv = 0; isrv >>= 4; if (tpr >= isrv) ppr = s->tpr; else ppr = isrv << 4; return ppr;}static int apic_get_arb_pri(APICState *s){ /* XXX: arbitration */ return 0;}/* signal the CPU if an irq is pending */static void apic_update_irq(APICState *s){ int irrv, ppr; if (!(s->spurious_vec & APIC_SV_ENABLE)) return; irrv = get_highest_priority_int(s->irr); if (irrv < 0) return; ppr = apic_get_ppr(s); if (ppr && (irrv & 0xf0) <= (ppr & 0xf0)) return; cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD);}static void apic_set_irq(APICState *s, int vector_num, int trigger_mode){ set_bit(s->irr, vector_num); if (trigger_mode) set_bit(s->tmr, vector_num); else reset_bit(s->tmr, vector_num); apic_update_irq(s);}static void apic_eoi(APICState *s){ int isrv; isrv = get_highest_priority_int(s->isr); if (isrv < 0) return; reset_bit(s->isr, isrv); /* XXX: send the EOI packet to the APIC bus to allow the I/O APIC to set the remote IRR bit for level triggered interrupts. */ apic_update_irq(s);}static void apic_get_delivery_bitmask(uint32_t *deliver_bitmask, uint8_t dest, uint8_t dest_mode){ APICState *apic_iter; int i; if (dest_mode == 0) { 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 */ 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); } } } } }}static void apic_init_ipi(APICState *s){ int i; s->tpr = 0; s->spurious_vec = 0xff; s->log_dest = 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)); 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; s->count_shift = 0; s->initial_count = 0; s->initial_count_load_time = 0; 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[MAX_APIC_WORDS]; int dest_shorthand = (s->icr[0] >> 18) & 3; APICState *apic_iter; 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) { foreach_apic(apic_iter, deliver_bitmask, apic_iter->arb_id = apic_iter->id ); return; } } break; case APIC_DM_SIPI: foreach_apic(apic_iter, deliver_bitmask, apic_startup(apic_iter, vector_num) ); return; } apic_bus_deliver(deliver_bitmask, delivery_mode, vector_num, polarity, trigger_mode);}int apic_get_interrupt(CPUState *env){ APICState *s = env->apic_state; int intno; /* if the APIC is installed or enabled, we let the 8259 handle the IRQs */ if (!s) 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; 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;}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) >> s->count_shift; if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) { /* periodic */ val = s->initial_count - (d % ((uint64_t)s->initial_count + 1)); } else { if (d >= s->initial_count) val = 0; else val = s->initial_count - d; } return val;}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) >> 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); } else { if (d >= s->initial_count) goto no_timer; d = (uint64_t)s->initial_count + 1; } next_time = s->initial_count_load_time + (d << s->count_shift); qemu_mod_timer(s->timer, next_time); s->next_time = next_time; } else { no_timer: qemu_del_timer(s->timer);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -