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