Commit 574bbf7b0d59f7973cd7a11cb0e370a6d415dcae
1 parent
acae4681
initial APIC support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1183 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
441 additions
and
0 deletions
hw/apic.c
0 → 100644
| 1 | +/* | ||
| 2 | + * APIC support | ||
| 3 | + * | ||
| 4 | + * Copyright (c) 2004-2005 Fabrice Bellard | ||
| 5 | + * | ||
| 6 | + * This library is free software; you can redistribute it and/or | ||
| 7 | + * modify it under the terms of the GNU Lesser General Public | ||
| 8 | + * License as published by the Free Software Foundation; either | ||
| 9 | + * version 2 of the License, or (at your option) any later version. | ||
| 10 | + * | ||
| 11 | + * This library is distributed in the hope that it will be useful, | ||
| 12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 14 | + * Lesser General Public License for more details. | ||
| 15 | + * | ||
| 16 | + * You should have received a copy of the GNU Lesser General Public | ||
| 17 | + * License along with this library; if not, write to the Free Software | ||
| 18 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | + */ | ||
| 20 | +#include "vl.h" | ||
| 21 | + | ||
| 22 | +//#define DEBUG_APIC | ||
| 23 | + | ||
| 24 | +/* APIC Local Vector Table */ | ||
| 25 | +#define APIC_LVT_TIMER 0 | ||
| 26 | +#define APIC_LVT_THERMAL 1 | ||
| 27 | +#define APIC_LVT_PERFORM 2 | ||
| 28 | +#define APIC_LVT_LINT0 3 | ||
| 29 | +#define APIC_LVT_LINT1 4 | ||
| 30 | +#define APIC_LVT_ERROR 5 | ||
| 31 | +#define APIC_LVT_NB 6 | ||
| 32 | + | ||
| 33 | +/* APIC delivery modes */ | ||
| 34 | +#define APIC_DM_FIXED 0 | ||
| 35 | +#define APIC_DM_LOWPRI 1 | ||
| 36 | +#define APIC_DM_SMI 2 | ||
| 37 | +#define APIC_DM_NMI 4 | ||
| 38 | +#define APIC_DM_INIT 5 | ||
| 39 | +#define APIC_DM_SIPI 6 | ||
| 40 | +#define APIC_DM_EXTINT 7 | ||
| 41 | + | ||
| 42 | +#define APIC_TRIGGER_EDGE 0 | ||
| 43 | +#define APIC_TRIGGER_LEVEL 1 | ||
| 44 | + | ||
| 45 | +#define APIC_LVT_TIMER_PERIODIC (1<<17) | ||
| 46 | +#define APIC_LVT_MASKED (1<<16) | ||
| 47 | +#define APIC_LVT_LEVEL_TRIGGER (1<<15) | ||
| 48 | +#define APIC_LVT_REMOTE_IRR (1<<14) | ||
| 49 | +#define APIC_INPUT_POLARITY (1<<13) | ||
| 50 | +#define APIC_SEND_PENDING (1<<12) | ||
| 51 | + | ||
| 52 | +#define ESR_ILLEGAL_ADDRESS (1 << 7) | ||
| 53 | + | ||
| 54 | +#define APIC_SV_ENABLE (1 << 8) | ||
| 55 | + | ||
| 56 | +typedef struct APICState { | ||
| 57 | + CPUState *cpu_env; | ||
| 58 | + uint32_t apicbase; | ||
| 59 | + uint8_t id; | ||
| 60 | + uint8_t tpr; | ||
| 61 | + uint32_t spurious_vec; | ||
| 62 | + uint32_t isr[8]; /* in service register */ | ||
| 63 | + uint32_t tmr[8]; /* trigger mode register */ | ||
| 64 | + uint32_t irr[8]; /* interrupt request register */ | ||
| 65 | + uint32_t lvt[APIC_LVT_NB]; | ||
| 66 | + uint32_t esr; /* error register */ | ||
| 67 | + uint32_t icr[2]; | ||
| 68 | + | ||
| 69 | + uint32_t divide_conf; | ||
| 70 | + int count_shift; | ||
| 71 | + uint32_t initial_count; | ||
| 72 | + int64_t initial_count_load_time, next_time; | ||
| 73 | + QEMUTimer *timer; | ||
| 74 | +} APICState; | ||
| 75 | + | ||
| 76 | +static int apic_io_memory; | ||
| 77 | + | ||
| 78 | +void cpu_set_apic_base(CPUState *env, uint64_t val) | ||
| 79 | +{ | ||
| 80 | + APICState *s = env->apic_state; | ||
| 81 | +#ifdef DEBUG_APIC | ||
| 82 | + printf("cpu_set_apic_base: %016llx\n", val); | ||
| 83 | +#endif | ||
| 84 | + s->apicbase = (val & 0xfffff000) | | ||
| 85 | + (s->apicbase & (MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE)); | ||
| 86 | + /* if disabled, cannot be enabled again */ | ||
| 87 | + if (!(val & MSR_IA32_APICBASE_ENABLE)) { | ||
| 88 | + s->apicbase &= ~MSR_IA32_APICBASE_ENABLE; | ||
| 89 | + env->cpuid_features &= ~CPUID_APIC; | ||
| 90 | + s->spurious_vec &= ~APIC_SV_ENABLE; | ||
| 91 | + } | ||
| 92 | +} | ||
| 93 | + | ||
| 94 | +uint64_t cpu_get_apic_base(CPUState *env) | ||
| 95 | +{ | ||
| 96 | + APICState *s = env->apic_state; | ||
| 97 | +#ifdef DEBUG_APIC | ||
| 98 | + printf("cpu_get_apic_base: %016llx\n", (uint64_t)s->apicbase); | ||
| 99 | +#endif | ||
| 100 | + return s->apicbase; | ||
| 101 | +} | ||
| 102 | + | ||
| 103 | +/* return -1 if no bit is set */ | ||
| 104 | +static int get_highest_priority_int(uint32_t *tab) | ||
| 105 | +{ | ||
| 106 | + int i; | ||
| 107 | + for(i = 0;i < 8; i++) { | ||
| 108 | + if (tab[i] != 0) { | ||
| 109 | + return i * 32 + ffs(tab[i]) - 1; | ||
| 110 | + } | ||
| 111 | + } | ||
| 112 | + return -1; | ||
| 113 | +} | ||
| 114 | + | ||
| 115 | +static inline void set_bit(uint32_t *tab, int index) | ||
| 116 | +{ | ||
| 117 | + int i, mask; | ||
| 118 | + i = index >> 5; | ||
| 119 | + mask = 1 << (index & 0x1f); | ||
| 120 | + tab[i] |= mask; | ||
| 121 | +} | ||
| 122 | + | ||
| 123 | +static inline void reset_bit(uint32_t *tab, int index) | ||
| 124 | +{ | ||
| 125 | + int i, mask; | ||
| 126 | + i = index >> 5; | ||
| 127 | + mask = 1 << (index & 0x1f); | ||
| 128 | + tab[i] &= ~mask; | ||
| 129 | +} | ||
| 130 | + | ||
| 131 | +static int apic_get_ppr(APICState *s) | ||
| 132 | +{ | ||
| 133 | + int tpr, isrv, ppr; | ||
| 134 | + | ||
| 135 | + tpr = (s->tpr >> 4); | ||
| 136 | + isrv = get_highest_priority_int(s->isr); | ||
| 137 | + if (isrv < 0) | ||
| 138 | + isrv = 0; | ||
| 139 | + isrv >>= 4; | ||
| 140 | + if (tpr >= isrv) | ||
| 141 | + ppr = s->tpr; | ||
| 142 | + else | ||
| 143 | + ppr = isrv << 4; | ||
| 144 | + return ppr; | ||
| 145 | +} | ||
| 146 | + | ||
| 147 | +/* signal the CPU if an irq is pending */ | ||
| 148 | +static void apic_update_irq(APICState *s) | ||
| 149 | +{ | ||
| 150 | + int irrv, isrv; | ||
| 151 | + irrv = get_highest_priority_int(s->irr); | ||
| 152 | + if (irrv < 0) | ||
| 153 | + return; | ||
| 154 | + isrv = get_highest_priority_int(s->isr); | ||
| 155 | + /* if the pending irq has less priority, we do not make a new request */ | ||
| 156 | + if (isrv >= 0 && irrv >= isrv) | ||
| 157 | + return; | ||
| 158 | + cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD); | ||
| 159 | +} | ||
| 160 | + | ||
| 161 | +static void apic_set_irq(APICState *s, int vector_num, int trigger_mode) | ||
| 162 | +{ | ||
| 163 | + set_bit(s->irr, vector_num); | ||
| 164 | + if (trigger_mode) | ||
| 165 | + set_bit(s->tmr, vector_num); | ||
| 166 | + else | ||
| 167 | + reset_bit(s->tmr, vector_num); | ||
| 168 | + apic_update_irq(s); | ||
| 169 | +} | ||
| 170 | + | ||
| 171 | +static void apic_eoi(APICState *s) | ||
| 172 | +{ | ||
| 173 | + int isrv; | ||
| 174 | + isrv = get_highest_priority_int(s->isr); | ||
| 175 | + if (isrv < 0) | ||
| 176 | + return; | ||
| 177 | + reset_bit(s->isr, isrv); | ||
| 178 | + apic_update_irq(s); | ||
| 179 | +} | ||
| 180 | + | ||
| 181 | +int apic_get_interrupt(CPUState *env) | ||
| 182 | +{ | ||
| 183 | + APICState *s = env->apic_state; | ||
| 184 | + int intno; | ||
| 185 | + | ||
| 186 | + /* if the APIC is installed or enabled, we let the 8259 handle the | ||
| 187 | + IRQs */ | ||
| 188 | + if (!s) | ||
| 189 | + return -1; | ||
| 190 | + if (!(s->spurious_vec & APIC_SV_ENABLE)) | ||
| 191 | + return -1; | ||
| 192 | + | ||
| 193 | + /* XXX: spurious IRQ handling */ | ||
| 194 | + intno = get_highest_priority_int(s->irr); | ||
| 195 | + if (intno < 0) | ||
| 196 | + return -1; | ||
| 197 | + reset_bit(s->irr, intno); | ||
| 198 | + set_bit(s->isr, intno); | ||
| 199 | + apic_update_irq(s); | ||
| 200 | + return intno; | ||
| 201 | +} | ||
| 202 | + | ||
| 203 | +static uint32_t apic_get_current_count(APICState *s) | ||
| 204 | +{ | ||
| 205 | + int64_t d; | ||
| 206 | + uint32_t val; | ||
| 207 | + d = (qemu_get_clock(vm_clock) - s->initial_count_load_time) >> | ||
| 208 | + s->count_shift; | ||
| 209 | + if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) { | ||
| 210 | + /* periodic */ | ||
| 211 | + val = s->initial_count - (d % (s->initial_count + 1)); | ||
| 212 | + } else { | ||
| 213 | + if (d >= s->initial_count) | ||
| 214 | + val = 0; | ||
| 215 | + else | ||
| 216 | + val = s->initial_count - d; | ||
| 217 | + } | ||
| 218 | + return val; | ||
| 219 | +} | ||
| 220 | + | ||
| 221 | +static void apic_timer_update(APICState *s, int64_t current_time) | ||
| 222 | +{ | ||
| 223 | + int64_t next_time, d; | ||
| 224 | + | ||
| 225 | + if (!(s->lvt[APIC_LVT_TIMER] & APIC_LVT_MASKED)) { | ||
| 226 | + d = (current_time - s->initial_count_load_time) >> | ||
| 227 | + s->count_shift; | ||
| 228 | + if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) { | ||
| 229 | + d = ((d / (s->initial_count + 1)) + 1) * (s->initial_count + 1); | ||
| 230 | + } else { | ||
| 231 | + if (d >= s->initial_count) | ||
| 232 | + goto no_timer; | ||
| 233 | + d = s->initial_count + 1; | ||
| 234 | + } | ||
| 235 | + next_time = s->initial_count_load_time + (d << s->count_shift); | ||
| 236 | + qemu_mod_timer(s->timer, next_time); | ||
| 237 | + s->next_time = next_time; | ||
| 238 | + } else { | ||
| 239 | + no_timer: | ||
| 240 | + qemu_del_timer(s->timer); | ||
| 241 | + } | ||
| 242 | +} | ||
| 243 | + | ||
| 244 | +static void apic_timer(void *opaque) | ||
| 245 | +{ | ||
| 246 | + APICState *s = opaque; | ||
| 247 | + | ||
| 248 | + if (!(s->lvt[APIC_LVT_TIMER] & APIC_LVT_MASKED)) { | ||
| 249 | + apic_set_irq(s, s->lvt[APIC_LVT_TIMER] & 0xff, APIC_TRIGGER_EDGE); | ||
| 250 | + } | ||
| 251 | + apic_timer_update(s, s->next_time); | ||
| 252 | +} | ||
| 253 | + | ||
| 254 | +static uint32_t apic_mem_readb(void *opaque, target_phys_addr_t addr) | ||
| 255 | +{ | ||
| 256 | + return 0; | ||
| 257 | +} | ||
| 258 | + | ||
| 259 | +static uint32_t apic_mem_readw(void *opaque, target_phys_addr_t addr) | ||
| 260 | +{ | ||
| 261 | + return 0; | ||
| 262 | +} | ||
| 263 | + | ||
| 264 | +static void apic_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) | ||
| 265 | +{ | ||
| 266 | +} | ||
| 267 | + | ||
| 268 | +static void apic_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val) | ||
| 269 | +{ | ||
| 270 | +} | ||
| 271 | + | ||
| 272 | +static uint32_t apic_mem_readl(void *opaque, target_phys_addr_t addr) | ||
| 273 | +{ | ||
| 274 | + CPUState *env; | ||
| 275 | + APICState *s; | ||
| 276 | + uint32_t val; | ||
| 277 | + int index; | ||
| 278 | + | ||
| 279 | + env = cpu_single_env; | ||
| 280 | + if (!env) | ||
| 281 | + return 0; | ||
| 282 | + s = env->apic_state; | ||
| 283 | + | ||
| 284 | + index = (addr >> 4) & 0xff; | ||
| 285 | + switch(index) { | ||
| 286 | + case 0x02: /* id */ | ||
| 287 | + val = s->id << 24; | ||
| 288 | + break; | ||
| 289 | + case 0x03: /* version */ | ||
| 290 | + val = 0x11 | ((APIC_LVT_NB - 1) << 16); /* version 0x11 */ | ||
| 291 | + break; | ||
| 292 | + case 0x08: | ||
| 293 | + val = s->tpr; | ||
| 294 | + break; | ||
| 295 | + case 0x0a: | ||
| 296 | + /* ppr */ | ||
| 297 | + val = apic_get_ppr(s); | ||
| 298 | + break; | ||
| 299 | + case 0x0f: | ||
| 300 | + val = s->spurious_vec; | ||
| 301 | + break; | ||
| 302 | + case 0x10 ... 0x17: | ||
| 303 | + val = s->isr[index & 7]; | ||
| 304 | + break; | ||
| 305 | + case 0x18 ... 0x1f: | ||
| 306 | + val = s->tmr[index & 7]; | ||
| 307 | + break; | ||
| 308 | + case 0x20 ... 0x27: | ||
| 309 | + val = s->irr[index & 7]; | ||
| 310 | + break; | ||
| 311 | + case 0x28: | ||
| 312 | + val = s->esr; | ||
| 313 | + break; | ||
| 314 | + case 0x32 ... 0x37: | ||
| 315 | + val = s->lvt[index - 0x32]; | ||
| 316 | + break; | ||
| 317 | + case 0x30: | ||
| 318 | + case 0x31: | ||
| 319 | + val = s->icr[index & 1]; | ||
| 320 | + break; | ||
| 321 | + case 0x38: | ||
| 322 | + val = s->initial_count; | ||
| 323 | + break; | ||
| 324 | + case 0x39: | ||
| 325 | + val = apic_get_current_count(s); | ||
| 326 | + break; | ||
| 327 | + case 0x3e: | ||
| 328 | + val = s->divide_conf; | ||
| 329 | + break; | ||
| 330 | + default: | ||
| 331 | + s->esr |= ESR_ILLEGAL_ADDRESS; | ||
| 332 | + val = 0; | ||
| 333 | + break; | ||
| 334 | + } | ||
| 335 | +#ifdef DEBUG_APIC | ||
| 336 | + printf("APIC read: %08x = %08x\n", (uint32_t)addr, val); | ||
| 337 | +#endif | ||
| 338 | + return val; | ||
| 339 | +} | ||
| 340 | + | ||
| 341 | +static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) | ||
| 342 | +{ | ||
| 343 | + CPUState *env; | ||
| 344 | + APICState *s; | ||
| 345 | + int index; | ||
| 346 | + | ||
| 347 | + env = cpu_single_env; | ||
| 348 | + if (!env) | ||
| 349 | + return; | ||
| 350 | + s = env->apic_state; | ||
| 351 | + | ||
| 352 | +#ifdef DEBUG_APIC | ||
| 353 | + printf("APIC write: %08x = %08x\n", (uint32_t)addr, val); | ||
| 354 | +#endif | ||
| 355 | + | ||
| 356 | + index = (addr >> 4) & 0xff; | ||
| 357 | + switch(index) { | ||
| 358 | + case 0x02: | ||
| 359 | + s->id = (val >> 24); | ||
| 360 | + break; | ||
| 361 | + case 0x08: | ||
| 362 | + s->tpr = val; | ||
| 363 | + break; | ||
| 364 | + case 0x0b: /* EOI */ | ||
| 365 | + apic_eoi(s); | ||
| 366 | + break; | ||
| 367 | + case 0x0f: | ||
| 368 | + s->spurious_vec = val & 0x1ff; | ||
| 369 | + break; | ||
| 370 | + case 0x30: | ||
| 371 | + case 0x31: | ||
| 372 | + s->icr[index & 1] = val; | ||
| 373 | + break; | ||
| 374 | + case 0x32 ... 0x37: | ||
| 375 | + { | ||
| 376 | + int n = index - 0x32; | ||
| 377 | + s->lvt[n] = val; | ||
| 378 | + if (n == APIC_LVT_TIMER) | ||
| 379 | + apic_timer_update(s, qemu_get_clock(vm_clock)); | ||
| 380 | + } | ||
| 381 | + break; | ||
| 382 | + case 0x38: | ||
| 383 | + s->initial_count = val; | ||
| 384 | + s->initial_count_load_time = qemu_get_clock(vm_clock); | ||
| 385 | + apic_timer_update(s, s->initial_count_load_time); | ||
| 386 | + break; | ||
| 387 | + case 0x3e: | ||
| 388 | + { | ||
| 389 | + int v; | ||
| 390 | + s->divide_conf = val & 0xb; | ||
| 391 | + v = (s->divide_conf & 3) | ((s->divide_conf >> 1) & 4); | ||
| 392 | + s->count_shift = (v + 1) & 7; | ||
| 393 | + } | ||
| 394 | + break; | ||
| 395 | + default: | ||
| 396 | + s->esr |= ESR_ILLEGAL_ADDRESS; | ||
| 397 | + break; | ||
| 398 | + } | ||
| 399 | +} | ||
| 400 | + | ||
| 401 | + | ||
| 402 | + | ||
| 403 | +static CPUReadMemoryFunc *apic_mem_read[3] = { | ||
| 404 | + apic_mem_readb, | ||
| 405 | + apic_mem_readw, | ||
| 406 | + apic_mem_readl, | ||
| 407 | +}; | ||
| 408 | + | ||
| 409 | +static CPUWriteMemoryFunc *apic_mem_write[3] = { | ||
| 410 | + apic_mem_writeb, | ||
| 411 | + apic_mem_writew, | ||
| 412 | + apic_mem_writel, | ||
| 413 | +}; | ||
| 414 | + | ||
| 415 | +int apic_init(CPUState *env) | ||
| 416 | +{ | ||
| 417 | + APICState *s; | ||
| 418 | + int i; | ||
| 419 | + | ||
| 420 | + s = malloc(sizeof(APICState)); | ||
| 421 | + if (!s) | ||
| 422 | + return -1; | ||
| 423 | + memset(s, 0, sizeof(*s)); | ||
| 424 | + env->apic_state = s; | ||
| 425 | + s->cpu_env = env; | ||
| 426 | + s->apicbase = 0xfee00000 | | ||
| 427 | + MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE; | ||
| 428 | + for(i = 0; i < APIC_LVT_NB; i++) | ||
| 429 | + s->lvt[i] = 1 << 16; /* mask LVT */ | ||
| 430 | + s->spurious_vec = 0xff; | ||
| 431 | + | ||
| 432 | + if (apic_io_memory == 0) { | ||
| 433 | + /* NOTE: the APIC is directly connected to the CPU - it is not | ||
| 434 | + on the global memory bus. */ | ||
| 435 | + apic_io_memory = cpu_register_io_memory(0, apic_mem_read, | ||
| 436 | + apic_mem_write, NULL); | ||
| 437 | + cpu_register_physical_memory(s->apicbase & ~0xfff, 0x1000, apic_io_memory); | ||
| 438 | + } | ||
| 439 | + s->timer = qemu_new_timer(vm_clock, apic_timer, s); | ||
| 440 | + return 0; | ||
| 441 | +} |