Commit d592d3033d7bc41db6159e9387591a216e3c46e0
1 parent
1ff5c1a6
IOAPIC support (initial patch by Filip Navara)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1520 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
4 changed files
with
547 additions
and
29 deletions
hw/apic.c
| ... | ... | @@ -20,6 +20,7 @@ |
| 20 | 20 | #include "vl.h" |
| 21 | 21 | |
| 22 | 22 | //#define DEBUG_APIC |
| 23 | +//#define DEBUG_IOAPIC | |
| 23 | 24 | |
| 24 | 25 | /* APIC Local Vector Table */ |
| 25 | 26 | #define APIC_LVT_TIMER 0 |
| ... | ... | @@ -39,6 +40,10 @@ |
| 39 | 40 | #define APIC_DM_SIPI 6 |
| 40 | 41 | #define APIC_DM_EXTINT 7 |
| 41 | 42 | |
| 43 | +/* APIC destination mode */ | |
| 44 | +#define APIC_DESTMODE_FLAT 0xf | |
| 45 | +#define APIC_DESTMODE_CLUSTER 1 | |
| 46 | + | |
| 42 | 47 | #define APIC_TRIGGER_EDGE 0 |
| 43 | 48 | #define APIC_TRIGGER_LEVEL 1 |
| 44 | 49 | |
| ... | ... | @@ -49,6 +54,8 @@ |
| 49 | 54 | #define APIC_INPUT_POLARITY (1<<13) |
| 50 | 55 | #define APIC_SEND_PENDING (1<<12) |
| 51 | 56 | |
| 57 | +#define IOAPIC_NUM_PINS 0x18 | |
| 58 | + | |
| 52 | 59 | #define ESR_ILLEGAL_ADDRESS (1 << 7) |
| 53 | 60 | |
| 54 | 61 | #define APIC_SV_ENABLE (1 << 8) |
| ... | ... | @@ -57,8 +64,11 @@ typedef struct APICState { |
| 57 | 64 | CPUState *cpu_env; |
| 58 | 65 | uint32_t apicbase; |
| 59 | 66 | uint8_t id; |
| 67 | + uint8_t arb_id; | |
| 60 | 68 | uint8_t tpr; |
| 61 | 69 | uint32_t spurious_vec; |
| 70 | + uint8_t log_dest; | |
| 71 | + uint8_t dest_mode; | |
| 62 | 72 | uint32_t isr[8]; /* in service register */ |
| 63 | 73 | uint32_t tmr[8]; /* trigger mode register */ |
| 64 | 74 | uint32_t irr[8]; /* interrupt request register */ |
| ... | ... | @@ -71,9 +81,65 @@ typedef struct APICState { |
| 71 | 81 | uint32_t initial_count; |
| 72 | 82 | int64_t initial_count_load_time, next_time; |
| 73 | 83 | QEMUTimer *timer; |
| 84 | + | |
| 85 | + struct APICState *next_apic; | |
| 74 | 86 | } APICState; |
| 75 | 87 | |
| 88 | +struct IOAPICState { | |
| 89 | + uint8_t id; | |
| 90 | + uint8_t ioregsel; | |
| 91 | + | |
| 92 | + uint32_t irr; | |
| 93 | + uint64_t ioredtbl[IOAPIC_NUM_PINS]; | |
| 94 | +}; | |
| 95 | + | |
| 76 | 96 | static int apic_io_memory; |
| 97 | +static APICState *first_local_apic = NULL; | |
| 98 | +static int last_apic_id = 0; | |
| 99 | +static IOAPICState *ioapic_state; | |
| 100 | + | |
| 101 | +static void apic_init_ipi(APICState *s); | |
| 102 | +static void apic_set_irq(APICState *s, int vector_num, int trigger_mode); | |
| 103 | +static void apic_update_irq(APICState *s); | |
| 104 | + | |
| 105 | +static void apic_bus_deliver(uint32_t deliver_bitmask, uint8_t delivery_mode, | |
| 106 | + uint8_t vector_num, uint8_t polarity, | |
| 107 | + uint8_t trigger_mode) | |
| 108 | +{ | |
| 109 | + APICState *apic_iter; | |
| 110 | + | |
| 111 | + switch (delivery_mode) { | |
| 112 | + case APIC_DM_LOWPRI: | |
| 113 | + case APIC_DM_FIXED: | |
| 114 | + /* XXX: arbitration */ | |
| 115 | + break; | |
| 116 | + | |
| 117 | + case APIC_DM_SMI: | |
| 118 | + case APIC_DM_NMI: | |
| 119 | + break; | |
| 120 | + | |
| 121 | + case APIC_DM_INIT: | |
| 122 | + /* normal INIT IPI sent to processors */ | |
| 123 | + for (apic_iter = first_local_apic; apic_iter != NULL; | |
| 124 | + apic_iter = apic_iter->next_apic) { | |
| 125 | + apic_init_ipi(apic_iter); | |
| 126 | + } | |
| 127 | + return; | |
| 128 | + | |
| 129 | + case APIC_DM_EXTINT: | |
| 130 | + /* XXX: implement */ | |
| 131 | + break; | |
| 132 | + | |
| 133 | + default: | |
| 134 | + return; | |
| 135 | + } | |
| 136 | + | |
| 137 | + for (apic_iter = first_local_apic; apic_iter != NULL; | |
| 138 | + apic_iter = apic_iter->next_apic) { | |
| 139 | + if (deliver_bitmask & (1 << apic_iter->id)) | |
| 140 | + apic_set_irq(apic_iter, vector_num, trigger_mode); | |
| 141 | + } | |
| 142 | +} | |
| 77 | 143 | |
| 78 | 144 | void cpu_set_apic_base(CPUState *env, uint64_t val) |
| 79 | 145 | { |
| ... | ... | @@ -104,6 +170,7 @@ void cpu_set_apic_tpr(CPUX86State *env, uint8_t val) |
| 104 | 170 | { |
| 105 | 171 | APICState *s = env->apic_state; |
| 106 | 172 | s->tpr = (val & 0x0f) << 4; |
| 173 | + apic_update_irq(s); | |
| 107 | 174 | } |
| 108 | 175 | |
| 109 | 176 | uint8_t cpu_get_apic_tpr(CPUX86State *env) |
| ... | ... | @@ -112,16 +179,24 @@ uint8_t cpu_get_apic_tpr(CPUX86State *env) |
| 112 | 179 | return s->tpr >> 4; |
| 113 | 180 | } |
| 114 | 181 | |
| 115 | -/* return -1 if no bit is set */ | |
| 116 | -static int get_highest_priority_int(uint32_t *tab) | |
| 182 | +int fls_bit(int value) | |
| 117 | 183 | { |
| 118 | - int i; | |
| 119 | - for(i = 0;i < 8; i++) { | |
| 120 | - if (tab[i] != 0) { | |
| 121 | - return i * 32 + ffs(tab[i]) - 1; | |
| 122 | - } | |
| 123 | - } | |
| 124 | - return -1; | |
| 184 | + unsigned int ret = 0; | |
| 185 | + | |
| 186 | +#ifdef HOST_I386 | |
| 187 | + __asm__ __volatile__ ("bsr %1, %0\n" : "+r" (ret) : "rm" (value)); | |
| 188 | + return ret; | |
| 189 | +#else | |
| 190 | + if (value > 0xffff) | |
| 191 | + value >>= 16, ret = 16; | |
| 192 | + if (value > 0xff) | |
| 193 | + value >>= 8, ret += 8; | |
| 194 | + if (value > 0xf) | |
| 195 | + value >>= 4, ret += 4; | |
| 196 | + if (value > 0x3) | |
| 197 | + value >>= 2, ret += 2; | |
| 198 | + return ret + (value >> 1); | |
| 199 | +#endif | |
| 125 | 200 | } |
| 126 | 201 | |
| 127 | 202 | static inline void set_bit(uint32_t *tab, int index) |
| ... | ... | @@ -140,6 +215,18 @@ static inline void reset_bit(uint32_t *tab, int index) |
| 140 | 215 | tab[i] &= ~mask; |
| 141 | 216 | } |
| 142 | 217 | |
| 218 | +/* return -1 if no bit is set */ | |
| 219 | +static int get_highest_priority_int(uint32_t *tab) | |
| 220 | +{ | |
| 221 | + int i; | |
| 222 | + for(i = 7; i >= 0; i--) { | |
| 223 | + if (tab[i] != 0) { | |
| 224 | + return i * 32 + fls_bit(tab[i]); | |
| 225 | + } | |
| 226 | + } | |
| 227 | + return -1; | |
| 228 | +} | |
| 229 | + | |
| 143 | 230 | static int apic_get_ppr(APICState *s) |
| 144 | 231 | { |
| 145 | 232 | int tpr, isrv, ppr; |
| ... | ... | @@ -156,16 +243,23 @@ static int apic_get_ppr(APICState *s) |
| 156 | 243 | return ppr; |
| 157 | 244 | } |
| 158 | 245 | |
| 246 | +static int apic_get_arb_pri(APICState *s) | |
| 247 | +{ | |
| 248 | + /* XXX: arbitration */ | |
| 249 | + return 0; | |
| 250 | +} | |
| 251 | + | |
| 159 | 252 | /* signal the CPU if an irq is pending */ |
| 160 | 253 | static void apic_update_irq(APICState *s) |
| 161 | 254 | { |
| 162 | - int irrv, isrv; | |
| 255 | + int irrv, ppr; | |
| 256 | + if (!(s->spurious_vec & APIC_SV_ENABLE)) | |
| 257 | + return; | |
| 163 | 258 | irrv = get_highest_priority_int(s->irr); |
| 164 | 259 | if (irrv < 0) |
| 165 | 260 | return; |
| 166 | - isrv = get_highest_priority_int(s->isr); | |
| 167 | - /* if the pending irq has less priority, we do not make a new request */ | |
| 168 | - if (isrv >= 0 && irrv >= isrv) | |
| 261 | + ppr = apic_get_ppr(s); | |
| 262 | + if (ppr && (irrv & 0xf0) <= (ppr & 0xf0)) | |
| 169 | 263 | return; |
| 170 | 264 | cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD); |
| 171 | 265 | } |
| ... | ... | @@ -187,9 +281,116 @@ static void apic_eoi(APICState *s) |
| 187 | 281 | if (isrv < 0) |
| 188 | 282 | return; |
| 189 | 283 | reset_bit(s->isr, isrv); |
| 284 | + /* XXX: send the EOI packet to the APIC bus to allow the I/O APIC to | |
| 285 | + set the remote IRR bit for level triggered interrupts. */ | |
| 190 | 286 | apic_update_irq(s); |
| 191 | 287 | } |
| 192 | 288 | |
| 289 | +static uint32_t apic_get_delivery_bitmask(uint8_t dest, uint8_t dest_mode) | |
| 290 | +{ | |
| 291 | + uint32_t mask = 0; | |
| 292 | + APICState *apic_iter; | |
| 293 | + | |
| 294 | + if (dest_mode == 0) { | |
| 295 | + if (dest == 0xff) | |
| 296 | + mask = 0xff; | |
| 297 | + else | |
| 298 | + mask = 1 << dest; | |
| 299 | + } else { | |
| 300 | + /* XXX: cluster mode */ | |
| 301 | + for (apic_iter = first_local_apic; apic_iter != NULL; | |
| 302 | + apic_iter = apic_iter->next_apic) { | |
| 303 | + if (dest & apic_iter->log_dest) | |
| 304 | + mask |= (1 << apic_iter->id); | |
| 305 | + } | |
| 306 | + } | |
| 307 | + | |
| 308 | + return mask; | |
| 309 | +} | |
| 310 | + | |
| 311 | + | |
| 312 | +static void apic_init_ipi(APICState *s) | |
| 313 | +{ | |
| 314 | + int i; | |
| 315 | + | |
| 316 | + for(i = 0; i < APIC_LVT_NB; i++) | |
| 317 | + s->lvt[i] = 1 << 16; /* mask LVT */ | |
| 318 | + s->tpr = 0; | |
| 319 | + s->spurious_vec = 0xff; | |
| 320 | + s->log_dest = 0; | |
| 321 | + s->dest_mode = 0; | |
| 322 | + memset(s->isr, 0, sizeof(s->isr)); | |
| 323 | + memset(s->tmr, 0, sizeof(s->tmr)); | |
| 324 | + memset(s->irr, 0, sizeof(s->irr)); | |
| 325 | + memset(s->lvt, 0, sizeof(s->lvt)); | |
| 326 | + s->esr = 0; | |
| 327 | + memset(s->icr, 0, sizeof(s->icr)); | |
| 328 | + s->divide_conf = 0; | |
| 329 | + s->count_shift = 0; | |
| 330 | + s->initial_count = 0; | |
| 331 | + s->initial_count_load_time = 0; | |
| 332 | + s->next_time = 0; | |
| 333 | +} | |
| 334 | + | |
| 335 | +static void apic_deliver(APICState *s, uint8_t dest, uint8_t dest_mode, | |
| 336 | + uint8_t delivery_mode, uint8_t vector_num, | |
| 337 | + uint8_t polarity, uint8_t trigger_mode) | |
| 338 | +{ | |
| 339 | + uint32_t deliver_bitmask = 0; | |
| 340 | + int dest_shorthand = (s->icr[0] >> 18) & 3; | |
| 341 | + APICState *apic_iter; | |
| 342 | + | |
| 343 | + switch (delivery_mode) { | |
| 344 | + case APIC_DM_LOWPRI: | |
| 345 | + /* XXX: serch for focus processor, arbitration */ | |
| 346 | + dest = s->id; | |
| 347 | + | |
| 348 | + case APIC_DM_INIT: | |
| 349 | + { | |
| 350 | + int trig_mode = (s->icr[0] >> 15) & 1; | |
| 351 | + int level = (s->icr[0] >> 14) & 1; | |
| 352 | + if (level == 0 && trig_mode == 1) { | |
| 353 | + for (apic_iter = first_local_apic; apic_iter != NULL; | |
| 354 | + apic_iter = apic_iter->next_apic) { | |
| 355 | + if (deliver_bitmask & (1 << apic_iter->id)) { | |
| 356 | + apic_iter->arb_id = apic_iter->id; | |
| 357 | + } | |
| 358 | + } | |
| 359 | + return; | |
| 360 | + } | |
| 361 | + } | |
| 362 | + break; | |
| 363 | + | |
| 364 | + case APIC_DM_SIPI: | |
| 365 | + for (apic_iter = first_local_apic; apic_iter != NULL; | |
| 366 | + apic_iter = apic_iter->next_apic) { | |
| 367 | + if (deliver_bitmask & (1 << apic_iter->id)) { | |
| 368 | + /* XXX: SMP support */ | |
| 369 | + /* apic_startup(apic_iter); */ | |
| 370 | + } | |
| 371 | + } | |
| 372 | + return; | |
| 373 | + } | |
| 374 | + | |
| 375 | + switch (dest_shorthand) { | |
| 376 | + case 0: | |
| 377 | + deliver_bitmask = apic_get_delivery_bitmask(dest, dest_mode); | |
| 378 | + break; | |
| 379 | + case 1: | |
| 380 | + deliver_bitmask = (1 << s->id); | |
| 381 | + break; | |
| 382 | + case 2: | |
| 383 | + deliver_bitmask = 0xffffffff; | |
| 384 | + break; | |
| 385 | + case 3: | |
| 386 | + deliver_bitmask = 0xffffffff & ~(1 << s->id); | |
| 387 | + break; | |
| 388 | + } | |
| 389 | + | |
| 390 | + apic_bus_deliver(deliver_bitmask, delivery_mode, vector_num, polarity, | |
| 391 | + trigger_mode); | |
| 392 | +} | |
| 393 | + | |
| 193 | 394 | int apic_get_interrupt(CPUState *env) |
| 194 | 395 | { |
| 195 | 396 | APICState *s = env->apic_state; |
| ... | ... | @@ -207,6 +408,8 @@ int apic_get_interrupt(CPUState *env) |
| 207 | 408 | if (intno < 0) |
| 208 | 409 | return -1; |
| 209 | 410 | reset_bit(s->irr, intno); |
| 411 | + if (s->tpr && intno <= s->tpr) | |
| 412 | + return s->spurious_vec & 0xff; | |
| 210 | 413 | set_bit(s->isr, intno); |
| 211 | 414 | apic_update_irq(s); |
| 212 | 415 | return intno; |
| ... | ... | @@ -220,7 +423,7 @@ static uint32_t apic_get_current_count(APICState *s) |
| 220 | 423 | s->count_shift; |
| 221 | 424 | if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) { |
| 222 | 425 | /* periodic */ |
| 223 | - val = s->initial_count - (d % (s->initial_count + 1)); | |
| 426 | + val = s->initial_count - (d % ((uint64_t)s->initial_count + 1)); | |
| 224 | 427 | } else { |
| 225 | 428 | if (d >= s->initial_count) |
| 226 | 429 | val = 0; |
| ... | ... | @@ -238,11 +441,11 @@ static void apic_timer_update(APICState *s, int64_t current_time) |
| 238 | 441 | d = (current_time - s->initial_count_load_time) >> |
| 239 | 442 | s->count_shift; |
| 240 | 443 | if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) { |
| 241 | - d = ((d / (s->initial_count + 1)) + 1) * (s->initial_count + 1); | |
| 444 | + d = ((d / ((uint64_t)s->initial_count + 1)) + 1) * ((uint64_t)s->initial_count + 1); | |
| 242 | 445 | } else { |
| 243 | 446 | if (d >= s->initial_count) |
| 244 | 447 | goto no_timer; |
| 245 | - d = s->initial_count + 1; | |
| 448 | + d = (uint64_t)s->initial_count + 1; | |
| 246 | 449 | } |
| 247 | 450 | next_time = s->initial_count_load_time + (d << s->count_shift); |
| 248 | 451 | qemu_mod_timer(s->timer, next_time); |
| ... | ... | @@ -304,10 +507,19 @@ static uint32_t apic_mem_readl(void *opaque, target_phys_addr_t addr) |
| 304 | 507 | case 0x08: |
| 305 | 508 | val = s->tpr; |
| 306 | 509 | break; |
| 510 | + case 0x09: | |
| 511 | + val = apic_get_arb_pri(s); | |
| 512 | + break; | |
| 307 | 513 | case 0x0a: |
| 308 | 514 | /* ppr */ |
| 309 | 515 | val = apic_get_ppr(s); |
| 310 | 516 | break; |
| 517 | + case 0x0d: | |
| 518 | + val = s->log_dest << 24; | |
| 519 | + break; | |
| 520 | + case 0x0e: | |
| 521 | + val = s->dest_mode << 28; | |
| 522 | + break; | |
| 311 | 523 | case 0x0f: |
| 312 | 524 | val = s->spurious_vec; |
| 313 | 525 | break; |
| ... | ... | @@ -372,16 +584,29 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) |
| 372 | 584 | break; |
| 373 | 585 | case 0x08: |
| 374 | 586 | s->tpr = val; |
| 587 | + apic_update_irq(s); | |
| 375 | 588 | break; |
| 376 | 589 | case 0x0b: /* EOI */ |
| 377 | 590 | apic_eoi(s); |
| 378 | 591 | break; |
| 592 | + case 0x0d: | |
| 593 | + s->log_dest = val >> 24; | |
| 594 | + break; | |
| 595 | + case 0x0e: | |
| 596 | + s->dest_mode = val >> 28; | |
| 597 | + break; | |
| 379 | 598 | case 0x0f: |
| 380 | 599 | s->spurious_vec = val & 0x1ff; |
| 600 | + apic_update_irq(s); | |
| 381 | 601 | break; |
| 382 | 602 | case 0x30: |
| 603 | + s->icr[0] = val; | |
| 604 | + apic_deliver(s, (s->icr[1] >> 24) & 0xff, (s->icr[0] >> 11) & 1, | |
| 605 | + (s->icr[0] >> 8) & 7, (s->icr[0] & 0xff), | |
| 606 | + (s->icr[0] >> 14) & 1, (s->icr[0] >> 15) & 1); | |
| 607 | + break; | |
| 383 | 608 | case 0x31: |
| 384 | - s->icr[index & 1] = val; | |
| 609 | + s->icr[1] = val; | |
| 385 | 610 | break; |
| 386 | 611 | case 0x32 ... 0x37: |
| 387 | 612 | { |
| ... | ... | @@ -410,7 +635,76 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) |
| 410 | 635 | } |
| 411 | 636 | } |
| 412 | 637 | |
| 638 | +static void apic_save(QEMUFile *f, void *opaque) | |
| 639 | +{ | |
| 640 | + APICState *s = opaque; | |
| 641 | + int i; | |
| 642 | + | |
| 643 | + qemu_put_be32s(f, &s->apicbase); | |
| 644 | + qemu_put_8s(f, &s->id); | |
| 645 | + qemu_put_8s(f, &s->arb_id); | |
| 646 | + qemu_put_8s(f, &s->tpr); | |
| 647 | + qemu_put_be32s(f, &s->spurious_vec); | |
| 648 | + qemu_put_8s(f, &s->log_dest); | |
| 649 | + qemu_put_8s(f, &s->dest_mode); | |
| 650 | + for (i = 0; i < 8; i++) { | |
| 651 | + qemu_put_be32s(f, &s->isr[i]); | |
| 652 | + qemu_put_be32s(f, &s->tmr[i]); | |
| 653 | + qemu_put_be32s(f, &s->irr[i]); | |
| 654 | + } | |
| 655 | + for (i = 0; i < APIC_LVT_NB; i++) { | |
| 656 | + qemu_put_be32s(f, &s->lvt[i]); | |
| 657 | + } | |
| 658 | + qemu_put_be32s(f, &s->esr); | |
| 659 | + qemu_put_be32s(f, &s->icr[0]); | |
| 660 | + qemu_put_be32s(f, &s->icr[1]); | |
| 661 | + qemu_put_be32s(f, &s->divide_conf); | |
| 662 | + qemu_put_be32s(f, &s->count_shift); | |
| 663 | + qemu_put_be32s(f, &s->initial_count); | |
| 664 | + qemu_put_be64s(f, &s->initial_count_load_time); | |
| 665 | + qemu_put_be64s(f, &s->next_time); | |
| 666 | +} | |
| 667 | + | |
| 668 | +static int apic_load(QEMUFile *f, void *opaque, int version_id) | |
| 669 | +{ | |
| 670 | + APICState *s = opaque; | |
| 671 | + int i; | |
| 672 | + | |
| 673 | + if (version_id != 1) | |
| 674 | + return -EINVAL; | |
| 675 | + | |
| 676 | + /* XXX: what if the base changes? (registered memory regions) */ | |
| 677 | + qemu_get_be32s(f, &s->apicbase); | |
| 678 | + qemu_get_8s(f, &s->id); | |
| 679 | + qemu_get_8s(f, &s->arb_id); | |
| 680 | + qemu_get_8s(f, &s->tpr); | |
| 681 | + qemu_get_be32s(f, &s->spurious_vec); | |
| 682 | + qemu_get_8s(f, &s->log_dest); | |
| 683 | + qemu_get_8s(f, &s->dest_mode); | |
| 684 | + for (i = 0; i < 8; i++) { | |
| 685 | + qemu_get_be32s(f, &s->isr[i]); | |
| 686 | + qemu_get_be32s(f, &s->tmr[i]); | |
| 687 | + qemu_get_be32s(f, &s->irr[i]); | |
| 688 | + } | |
| 689 | + for (i = 0; i < APIC_LVT_NB; i++) { | |
| 690 | + qemu_get_be32s(f, &s->lvt[i]); | |
| 691 | + } | |
| 692 | + qemu_get_be32s(f, &s->esr); | |
| 693 | + qemu_get_be32s(f, &s->icr[0]); | |
| 694 | + qemu_get_be32s(f, &s->icr[1]); | |
| 695 | + qemu_get_be32s(f, &s->divide_conf); | |
| 696 | + qemu_get_be32s(f, &s->count_shift); | |
| 697 | + qemu_get_be32s(f, &s->initial_count); | |
| 698 | + qemu_get_be64s(f, &s->initial_count_load_time); | |
| 699 | + qemu_get_be64s(f, &s->next_time); | |
| 700 | + return 0; | |
| 701 | +} | |
| 413 | 702 | |
| 703 | +static void apic_reset(void *opaque) | |
| 704 | +{ | |
| 705 | + APICState *s = opaque; | |
| 706 | + apic_init_ipi(s); | |
| 707 | +} | |
| 414 | 708 | |
| 415 | 709 | static CPUReadMemoryFunc *apic_mem_read[3] = { |
| 416 | 710 | apic_mem_readb, |
| ... | ... | @@ -427,27 +721,228 @@ static CPUWriteMemoryFunc *apic_mem_write[3] = { |
| 427 | 721 | int apic_init(CPUState *env) |
| 428 | 722 | { |
| 429 | 723 | APICState *s; |
| 430 | - int i; | |
| 431 | 724 | |
| 432 | - s = malloc(sizeof(APICState)); | |
| 725 | + s = qemu_mallocz(sizeof(APICState)); | |
| 433 | 726 | if (!s) |
| 434 | 727 | return -1; |
| 435 | - memset(s, 0, sizeof(*s)); | |
| 436 | 728 | env->apic_state = s; |
| 729 | + apic_init_ipi(s); | |
| 730 | + s->id = last_apic_id++; | |
| 437 | 731 | s->cpu_env = env; |
| 438 | 732 | s->apicbase = 0xfee00000 | |
| 439 | - MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE; | |
| 440 | - for(i = 0; i < APIC_LVT_NB; i++) | |
| 441 | - s->lvt[i] = 1 << 16; /* mask LVT */ | |
| 442 | - s->spurious_vec = 0xff; | |
| 733 | + (s->id ? 0 : MSR_IA32_APICBASE_BSP) | MSR_IA32_APICBASE_ENABLE; | |
| 443 | 734 | |
| 735 | + /* XXX: mapping more APICs at the same memory location */ | |
| 444 | 736 | if (apic_io_memory == 0) { |
| 445 | 737 | /* NOTE: the APIC is directly connected to the CPU - it is not |
| 446 | 738 | on the global memory bus. */ |
| 447 | 739 | apic_io_memory = cpu_register_io_memory(0, apic_mem_read, |
| 448 | 740 | apic_mem_write, NULL); |
| 449 | - cpu_register_physical_memory(s->apicbase & ~0xfff, 0x1000, apic_io_memory); | |
| 741 | + cpu_register_physical_memory(s->apicbase & ~0xfff, 0x1000, | |
| 742 | + apic_io_memory); | |
| 450 | 743 | } |
| 451 | 744 | s->timer = qemu_new_timer(vm_clock, apic_timer, s); |
| 745 | + | |
| 746 | + register_savevm("apic", 0, 1, apic_save, apic_load, s); | |
| 747 | + qemu_register_reset(apic_reset, s); | |
| 748 | + | |
| 749 | + s->next_apic = first_local_apic; | |
| 750 | + first_local_apic = s; | |
| 751 | + | |
| 752 | + return 0; | |
| 753 | +} | |
| 754 | + | |
| 755 | +static void ioapic_service(IOAPICState *s) | |
| 756 | +{ | |
| 757 | + uint8_t vector; | |
| 758 | + uint32_t mask; | |
| 759 | + uint64_t entry; | |
| 760 | + uint8_t dest; | |
| 761 | + uint8_t dest_mode; | |
| 762 | + | |
| 763 | + for (vector = 0; vector < IOAPIC_NUM_PINS; vector++) { | |
| 764 | + mask = 1 << vector; | |
| 765 | + if (s->irr & mask) { | |
| 766 | + entry = s->ioredtbl[vector]; | |
| 767 | + if (!(entry & APIC_LVT_MASKED)) { | |
| 768 | + if (!((entry >> 15) & 1)) | |
| 769 | + s->irr &= ~mask; | |
| 770 | + dest = entry >> 56; | |
| 771 | + dest_mode = (entry >> 11) & 1; | |
| 772 | + apic_bus_deliver(apic_get_delivery_bitmask(dest, dest_mode), | |
| 773 | + (entry >> 8) & 7, entry & 0xff, | |
| 774 | + (entry >> 13) & 1, (entry >> 15) & 1); | |
| 775 | + } | |
| 776 | + } | |
| 777 | + } | |
| 778 | +} | |
| 779 | + | |
| 780 | +void ioapic_set_irq(void *opaque, int vector, int level) | |
| 781 | +{ | |
| 782 | + IOAPICState *s = opaque; | |
| 783 | + | |
| 784 | + if (vector >= 0 && vector < IOAPIC_NUM_PINS) { | |
| 785 | + uint32_t mask = 1 << vector; | |
| 786 | + uint64_t entry = s->ioredtbl[vector]; | |
| 787 | + | |
| 788 | + if ((entry >> 15) & 1) { | |
| 789 | + /* level triggered */ | |
| 790 | + if (level) { | |
| 791 | + s->irr |= mask; | |
| 792 | + ioapic_service(s); | |
| 793 | + } else { | |
| 794 | + s->irr &= ~mask; | |
| 795 | + } | |
| 796 | + } else { | |
| 797 | + /* edge triggered */ | |
| 798 | + if (level) { | |
| 799 | + s->irr |= mask; | |
| 800 | + ioapic_service(s); | |
| 801 | + } | |
| 802 | + } | |
| 803 | + } | |
| 804 | +} | |
| 805 | + | |
| 806 | +static uint32_t ioapic_mem_readl(void *opaque, target_phys_addr_t addr) | |
| 807 | +{ | |
| 808 | + IOAPICState *s = opaque; | |
| 809 | + int index; | |
| 810 | + uint32_t val = 0; | |
| 811 | + | |
| 812 | + addr &= 0xff; | |
| 813 | + if (addr == 0x00) { | |
| 814 | + val = s->ioregsel; | |
| 815 | + } else if (addr == 0x10) { | |
| 816 | + switch (s->ioregsel) { | |
| 817 | + case 0x00: | |
| 818 | + val = s->id << 24; | |
| 819 | + break; | |
| 820 | + case 0x01: | |
| 821 | + val = 0x11 | ((IOAPIC_NUM_PINS - 1) << 16); /* version 0x11 */ | |
| 822 | + break; | |
| 823 | + case 0x02: | |
| 824 | + val = 0; | |
| 825 | + break; | |
| 826 | + default: | |
| 827 | + index = (s->ioregsel - 0x10) >> 1; | |
| 828 | + if (index >= 0 && index < IOAPIC_NUM_PINS) { | |
| 829 | + if (s->ioregsel & 1) | |
| 830 | + val = s->ioredtbl[index] >> 32; | |
| 831 | + else | |
| 832 | + val = s->ioredtbl[index] & 0xffffffff; | |
| 833 | + } | |
| 834 | + } | |
| 835 | +#ifdef DEBUG_IOAPIC | |
| 836 | + printf("I/O APIC read: %08x = %08x\n", s->ioregsel, val); | |
| 837 | +#endif | |
| 838 | + } | |
| 839 | + return val; | |
| 840 | +} | |
| 841 | + | |
| 842 | +static void ioapic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) | |
| 843 | +{ | |
| 844 | + IOAPICState *s = opaque; | |
| 845 | + int index; | |
| 846 | + | |
| 847 | + addr &= 0xff; | |
| 848 | + if (addr == 0x00) { | |
| 849 | + s->ioregsel = val; | |
| 850 | + return; | |
| 851 | + } else if (addr == 0x10) { | |
| 852 | +#ifdef DEBUG_IOAPIC | |
| 853 | + printf("I/O APIC write: %08x = %08x\n", s->ioregsel, val); | |
| 854 | +#endif | |
| 855 | + switch (s->ioregsel) { | |
| 856 | + case 0x00: | |
| 857 | + s->id = (val >> 24) & 0xff; | |
| 858 | + return; | |
| 859 | + case 0x01: | |
| 860 | + case 0x02: | |
| 861 | + return; | |
| 862 | + default: | |
| 863 | + index = (s->ioregsel - 0x10) >> 1; | |
| 864 | + if (index >= 0 && index < IOAPIC_NUM_PINS) { | |
| 865 | + if (s->ioregsel & 1) { | |
| 866 | + s->ioredtbl[index] &= 0xffffffff; | |
| 867 | + s->ioredtbl[index] |= (uint64_t)val << 32; | |
| 868 | + } else { | |
| 869 | + s->ioredtbl[index] &= ~0xffffffffULL; | |
| 870 | + s->ioredtbl[index] |= val; | |
| 871 | + } | |
| 872 | + ioapic_service(s); | |
| 873 | + } | |
| 874 | + } | |
| 875 | + } | |
| 876 | +} | |
| 877 | + | |
| 878 | +static void ioapic_save(QEMUFile *f, void *opaque) | |
| 879 | +{ | |
| 880 | + IOAPICState *s = opaque; | |
| 881 | + int i; | |
| 882 | + | |
| 883 | + qemu_put_8s(f, &s->id); | |
| 884 | + qemu_put_8s(f, &s->ioregsel); | |
| 885 | + for (i = 0; i < IOAPIC_NUM_PINS; i++) { | |
| 886 | + qemu_put_be64s(f, &s->ioredtbl[i]); | |
| 887 | + } | |
| 888 | +} | |
| 889 | + | |
| 890 | +static int ioapic_load(QEMUFile *f, void *opaque, int version_id) | |
| 891 | +{ | |
| 892 | + IOAPICState *s = opaque; | |
| 893 | + int i; | |
| 894 | + | |
| 895 | + if (version_id != 1) | |
| 896 | + return -EINVAL; | |
| 897 | + | |
| 898 | + qemu_get_8s(f, &s->id); | |
| 899 | + qemu_get_8s(f, &s->ioregsel); | |
| 900 | + for (i = 0; i < IOAPIC_NUM_PINS; i++) { | |
| 901 | + qemu_get_be64s(f, &s->ioredtbl[i]); | |
| 902 | + } | |
| 452 | 903 | return 0; |
| 453 | 904 | } |
| 905 | + | |
| 906 | +static void ioapic_reset(void *opaque) | |
| 907 | +{ | |
| 908 | + IOAPICState *s = opaque; | |
| 909 | + int i; | |
| 910 | + | |
| 911 | + memset(s, 0, sizeof(*s)); | |
| 912 | + for(i = 0; i < IOAPIC_NUM_PINS; i++) | |
| 913 | + s->ioredtbl[i] = 1 << 16; /* mask LVT */ | |
| 914 | +} | |
| 915 | + | |
| 916 | +static CPUReadMemoryFunc *ioapic_mem_read[3] = { | |
| 917 | + ioapic_mem_readl, | |
| 918 | + ioapic_mem_readl, | |
| 919 | + ioapic_mem_readl, | |
| 920 | +}; | |
| 921 | + | |
| 922 | +static CPUWriteMemoryFunc *ioapic_mem_write[3] = { | |
| 923 | + ioapic_mem_writel, | |
| 924 | + ioapic_mem_writel, | |
| 925 | + ioapic_mem_writel, | |
| 926 | +}; | |
| 927 | + | |
| 928 | +IOAPICState *ioapic_init(void) | |
| 929 | +{ | |
| 930 | + IOAPICState *s; | |
| 931 | + int io_memory; | |
| 932 | + | |
| 933 | + s = malloc(sizeof(IOAPICState)); | |
| 934 | + if (!s) | |
| 935 | + return NULL; | |
| 936 | + ioapic_state = s; | |
| 937 | + ioapic_reset(s); | |
| 938 | + s->id = last_apic_id++; | |
| 939 | + | |
| 940 | + io_memory = cpu_register_io_memory(0, ioapic_mem_read, | |
| 941 | + ioapic_mem_write, s); | |
| 942 | + cpu_register_physical_memory(0xfec00000, 0x1000, io_memory); | |
| 943 | + | |
| 944 | + register_savevm("ioapic", 0, 1, ioapic_save, ioapic_load, s); | |
| 945 | + qemu_register_reset(ioapic_reset, s); | |
| 946 | + | |
| 947 | + return s; | |
| 948 | +} | ... | ... |
hw/i8259.c
| ... | ... | @@ -55,6 +55,9 @@ struct PicState2 { |
| 55 | 55 | PicState pics[2]; |
| 56 | 56 | IRQRequestFunc *irq_request; |
| 57 | 57 | void *irq_request_opaque; |
| 58 | + /* IOAPIC callback support */ | |
| 59 | + SetIRQFunc *alt_irq_func; | |
| 60 | + void *alt_irq_opaque; | |
| 58 | 61 | }; |
| 59 | 62 | |
| 60 | 63 | #if defined(DEBUG_PIC) || defined (DEBUG_IRQ_COUNT) |
| ... | ... | @@ -186,6 +189,9 @@ void pic_set_irq_new(void *opaque, int irq, int level) |
| 186 | 189 | } |
| 187 | 190 | #endif |
| 188 | 191 | pic_set_irq1(&s->pics[irq >> 3], irq & 7, level); |
| 192 | + /* used for IOAPIC irqs */ | |
| 193 | + if (s->alt_irq_func) | |
| 194 | + s->alt_irq_func(s->alt_irq_opaque, irq, level); | |
| 189 | 195 | pic_update_irq(s); |
| 190 | 196 | } |
| 191 | 197 | |
| ... | ... | @@ -546,3 +552,10 @@ PicState2 *pic_init(IRQRequestFunc *irq_request, void *irq_request_opaque) |
| 546 | 552 | s->pics[1].pics_state = s; |
| 547 | 553 | return s; |
| 548 | 554 | } |
| 555 | + | |
| 556 | +void pic_set_alt_irq_func(PicState2 *s, SetIRQFunc *alt_irq_func, | |
| 557 | + void *alt_irq_opaque) | |
| 558 | +{ | |
| 559 | + s->alt_irq_func = alt_irq_func; | |
| 560 | + s->alt_irq_opaque = alt_irq_opaque; | |
| 561 | +} | ... | ... |
hw/pc.c
| ... | ... | @@ -41,6 +41,7 @@ int dummy_refresh_clock; |
| 41 | 41 | static fdctrl_t *floppy_controller; |
| 42 | 42 | static RTCState *rtc_state; |
| 43 | 43 | static PITState *pit; |
| 44 | +static IOAPICState *ioapic; | |
| 44 | 45 | |
| 45 | 46 | static void ioport80_write(void *opaque, uint32_t addr, uint32_t data) |
| 46 | 47 | { |
| ... | ... | @@ -70,7 +71,6 @@ int cpu_get_pic_interrupt(CPUState *env) |
| 70 | 71 | { |
| 71 | 72 | int intno; |
| 72 | 73 | |
| 73 | -#ifdef TARGET_X86_64 | |
| 74 | 74 | intno = apic_get_interrupt(env); |
| 75 | 75 | if (intno >= 0) { |
| 76 | 76 | /* set irq request if a PIC irq is still pending */ |
| ... | ... | @@ -78,7 +78,6 @@ int cpu_get_pic_interrupt(CPUState *env) |
| 78 | 78 | pic_update_irq(isa_pic); |
| 79 | 79 | return intno; |
| 80 | 80 | } |
| 81 | -#endif | |
| 82 | 81 | /* read the irq from the PIC */ |
| 83 | 82 | intno = pic_read_irq(isa_pic); |
| 84 | 83 | return intno; |
| ... | ... | @@ -417,7 +416,7 @@ static void pc_init1(int ram_size, int vga_ram_size, int boot_device, |
| 417 | 416 | unsigned long bios_offset, vga_bios_offset; |
| 418 | 417 | int bios_size, isa_bios_size; |
| 419 | 418 | PCIBus *pci_bus; |
| 420 | - | |
| 419 | + | |
| 421 | 420 | linux_boot = (kernel_filename != NULL); |
| 422 | 421 | |
| 423 | 422 | /* allocate RAM */ |
| ... | ... | @@ -557,10 +556,15 @@ static void pc_init1(int ram_size, int vga_ram_size, int boot_device, |
| 557 | 556 | register_ioport_read(0x92, 1, 1, ioport92_read, NULL); |
| 558 | 557 | register_ioport_write(0x92, 1, 1, ioport92_write, NULL); |
| 559 | 558 | |
| 560 | - if (pci_enabled) | |
| 559 | + if (pci_enabled) { | |
| 561 | 560 | apic_init(cpu_single_env); |
| 561 | + ioapic = ioapic_init(); | |
| 562 | + } | |
| 562 | 563 | isa_pic = pic_init(pic_irq_request, cpu_single_env); |
| 563 | 564 | pit = pit_init(0x40, 0); |
| 565 | + if (pci_enabled) { | |
| 566 | + pic_set_alt_irq_func(isa_pic, ioapic_set_irq, ioapic); | |
| 567 | + } | |
| 564 | 568 | |
| 565 | 569 | for(i = 0; i < MAX_SERIAL_PORTS; i++) { |
| 566 | 570 | if (serial_hds[i]) { | ... | ... |
vl.h
| ... | ... | @@ -695,6 +695,8 @@ extern PicState2 *isa_pic; |
| 695 | 695 | void pic_set_irq(int irq, int level); |
| 696 | 696 | void pic_set_irq_new(void *opaque, int irq, int level); |
| 697 | 697 | PicState2 *pic_init(IRQRequestFunc *irq_request, void *irq_request_opaque); |
| 698 | +void pic_set_alt_irq_func(PicState2 *s, SetIRQFunc *alt_irq_func, | |
| 699 | + void *alt_irq_opaque); | |
| 698 | 700 | int pic_read_irq(PicState2 *s); |
| 699 | 701 | void pic_update_irq(PicState2 *s); |
| 700 | 702 | uint32_t pic_intack_read(PicState2 *s); |
| ... | ... | @@ -702,8 +704,12 @@ void pic_info(void); |
| 702 | 704 | void irq_info(void); |
| 703 | 705 | |
| 704 | 706 | /* APIC */ |
| 707 | +typedef struct IOAPICState IOAPICState; | |
| 708 | + | |
| 705 | 709 | int apic_init(CPUState *env); |
| 706 | 710 | int apic_get_interrupt(CPUState *env); |
| 711 | +IOAPICState *ioapic_init(void); | |
| 712 | +void ioapic_set_irq(void *opaque, int vector, int level); | |
| 707 | 713 | |
| 708 | 714 | /* i8254.c */ |
| 709 | 715 | ... | ... |