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