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 | +} | ... | ... |