]>
Commit | Line | Data |
---|---|---|
574bbf7b FB |
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 | ||
9230e66e FB |
103 | void cpu_set_apic_tpr(CPUX86State *env, uint8_t val) |
104 | { | |
105 | APICState *s = env->apic_state; | |
106 | s->tpr = (val & 0x0f) << 4; | |
107 | } | |
108 | ||
109 | uint8_t cpu_get_apic_tpr(CPUX86State *env) | |
110 | { | |
111 | APICState *s = env->apic_state; | |
112 | return s->tpr >> 4; | |
113 | } | |
114 | ||
574bbf7b FB |
115 | /* return -1 if no bit is set */ |
116 | static int get_highest_priority_int(uint32_t *tab) | |
117 | { | |
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; | |
125 | } | |
126 | ||
127 | static inline void set_bit(uint32_t *tab, int index) | |
128 | { | |
129 | int i, mask; | |
130 | i = index >> 5; | |
131 | mask = 1 << (index & 0x1f); | |
132 | tab[i] |= mask; | |
133 | } | |
134 | ||
135 | static inline void reset_bit(uint32_t *tab, int index) | |
136 | { | |
137 | int i, mask; | |
138 | i = index >> 5; | |
139 | mask = 1 << (index & 0x1f); | |
140 | tab[i] &= ~mask; | |
141 | } | |
142 | ||
143 | static int apic_get_ppr(APICState *s) | |
144 | { | |
145 | int tpr, isrv, ppr; | |
146 | ||
147 | tpr = (s->tpr >> 4); | |
148 | isrv = get_highest_priority_int(s->isr); | |
149 | if (isrv < 0) | |
150 | isrv = 0; | |
151 | isrv >>= 4; | |
152 | if (tpr >= isrv) | |
153 | ppr = s->tpr; | |
154 | else | |
155 | ppr = isrv << 4; | |
156 | return ppr; | |
157 | } | |
158 | ||
159 | /* signal the CPU if an irq is pending */ | |
160 | static void apic_update_irq(APICState *s) | |
161 | { | |
162 | int irrv, isrv; | |
163 | irrv = get_highest_priority_int(s->irr); | |
164 | if (irrv < 0) | |
165 | 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) | |
169 | return; | |
170 | cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD); | |
171 | } | |
172 | ||
173 | static void apic_set_irq(APICState *s, int vector_num, int trigger_mode) | |
174 | { | |
175 | set_bit(s->irr, vector_num); | |
176 | if (trigger_mode) | |
177 | set_bit(s->tmr, vector_num); | |
178 | else | |
179 | reset_bit(s->tmr, vector_num); | |
180 | apic_update_irq(s); | |
181 | } | |
182 | ||
183 | static void apic_eoi(APICState *s) | |
184 | { | |
185 | int isrv; | |
186 | isrv = get_highest_priority_int(s->isr); | |
187 | if (isrv < 0) | |
188 | return; | |
189 | reset_bit(s->isr, isrv); | |
190 | apic_update_irq(s); | |
191 | } | |
192 | ||
193 | int apic_get_interrupt(CPUState *env) | |
194 | { | |
195 | APICState *s = env->apic_state; | |
196 | int intno; | |
197 | ||
198 | /* if the APIC is installed or enabled, we let the 8259 handle the | |
199 | IRQs */ | |
200 | if (!s) | |
201 | return -1; | |
202 | if (!(s->spurious_vec & APIC_SV_ENABLE)) | |
203 | return -1; | |
204 | ||
205 | /* XXX: spurious IRQ handling */ | |
206 | intno = get_highest_priority_int(s->irr); | |
207 | if (intno < 0) | |
208 | return -1; | |
209 | reset_bit(s->irr, intno); | |
210 | set_bit(s->isr, intno); | |
211 | apic_update_irq(s); | |
212 | return intno; | |
213 | } | |
214 | ||
215 | static uint32_t apic_get_current_count(APICState *s) | |
216 | { | |
217 | int64_t d; | |
218 | uint32_t val; | |
219 | d = (qemu_get_clock(vm_clock) - s->initial_count_load_time) >> | |
220 | s->count_shift; | |
221 | if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) { | |
222 | /* periodic */ | |
223 | val = s->initial_count - (d % (s->initial_count + 1)); | |
224 | } else { | |
225 | if (d >= s->initial_count) | |
226 | val = 0; | |
227 | else | |
228 | val = s->initial_count - d; | |
229 | } | |
230 | return val; | |
231 | } | |
232 | ||
233 | static void apic_timer_update(APICState *s, int64_t current_time) | |
234 | { | |
235 | int64_t next_time, d; | |
236 | ||
237 | if (!(s->lvt[APIC_LVT_TIMER] & APIC_LVT_MASKED)) { | |
238 | d = (current_time - s->initial_count_load_time) >> | |
239 | s->count_shift; | |
240 | if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) { | |
241 | d = ((d / (s->initial_count + 1)) + 1) * (s->initial_count + 1); | |
242 | } else { | |
243 | if (d >= s->initial_count) | |
244 | goto no_timer; | |
245 | d = s->initial_count + 1; | |
246 | } | |
247 | next_time = s->initial_count_load_time + (d << s->count_shift); | |
248 | qemu_mod_timer(s->timer, next_time); | |
249 | s->next_time = next_time; | |
250 | } else { | |
251 | no_timer: | |
252 | qemu_del_timer(s->timer); | |
253 | } | |
254 | } | |
255 | ||
256 | static void apic_timer(void *opaque) | |
257 | { | |
258 | APICState *s = opaque; | |
259 | ||
260 | if (!(s->lvt[APIC_LVT_TIMER] & APIC_LVT_MASKED)) { | |
261 | apic_set_irq(s, s->lvt[APIC_LVT_TIMER] & 0xff, APIC_TRIGGER_EDGE); | |
262 | } | |
263 | apic_timer_update(s, s->next_time); | |
264 | } | |
265 | ||
266 | static uint32_t apic_mem_readb(void *opaque, target_phys_addr_t addr) | |
267 | { | |
268 | return 0; | |
269 | } | |
270 | ||
271 | static uint32_t apic_mem_readw(void *opaque, target_phys_addr_t addr) | |
272 | { | |
273 | return 0; | |
274 | } | |
275 | ||
276 | static void apic_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) | |
277 | { | |
278 | } | |
279 | ||
280 | static void apic_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val) | |
281 | { | |
282 | } | |
283 | ||
284 | static uint32_t apic_mem_readl(void *opaque, target_phys_addr_t addr) | |
285 | { | |
286 | CPUState *env; | |
287 | APICState *s; | |
288 | uint32_t val; | |
289 | int index; | |
290 | ||
291 | env = cpu_single_env; | |
292 | if (!env) | |
293 | return 0; | |
294 | s = env->apic_state; | |
295 | ||
296 | index = (addr >> 4) & 0xff; | |
297 | switch(index) { | |
298 | case 0x02: /* id */ | |
299 | val = s->id << 24; | |
300 | break; | |
301 | case 0x03: /* version */ | |
302 | val = 0x11 | ((APIC_LVT_NB - 1) << 16); /* version 0x11 */ | |
303 | break; | |
304 | case 0x08: | |
305 | val = s->tpr; | |
306 | break; | |
307 | case 0x0a: | |
308 | /* ppr */ | |
309 | val = apic_get_ppr(s); | |
310 | break; | |
311 | case 0x0f: | |
312 | val = s->spurious_vec; | |
313 | break; | |
314 | case 0x10 ... 0x17: | |
315 | val = s->isr[index & 7]; | |
316 | break; | |
317 | case 0x18 ... 0x1f: | |
318 | val = s->tmr[index & 7]; | |
319 | break; | |
320 | case 0x20 ... 0x27: | |
321 | val = s->irr[index & 7]; | |
322 | break; | |
323 | case 0x28: | |
324 | val = s->esr; | |
325 | break; | |
326 | case 0x32 ... 0x37: | |
327 | val = s->lvt[index - 0x32]; | |
328 | break; | |
329 | case 0x30: | |
330 | case 0x31: | |
331 | val = s->icr[index & 1]; | |
332 | break; | |
333 | case 0x38: | |
334 | val = s->initial_count; | |
335 | break; | |
336 | case 0x39: | |
337 | val = apic_get_current_count(s); | |
338 | break; | |
339 | case 0x3e: | |
340 | val = s->divide_conf; | |
341 | break; | |
342 | default: | |
343 | s->esr |= ESR_ILLEGAL_ADDRESS; | |
344 | val = 0; | |
345 | break; | |
346 | } | |
347 | #ifdef DEBUG_APIC | |
348 | printf("APIC read: %08x = %08x\n", (uint32_t)addr, val); | |
349 | #endif | |
350 | return val; | |
351 | } | |
352 | ||
353 | static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) | |
354 | { | |
355 | CPUState *env; | |
356 | APICState *s; | |
357 | int index; | |
358 | ||
359 | env = cpu_single_env; | |
360 | if (!env) | |
361 | return; | |
362 | s = env->apic_state; | |
363 | ||
364 | #ifdef DEBUG_APIC | |
365 | printf("APIC write: %08x = %08x\n", (uint32_t)addr, val); | |
366 | #endif | |
367 | ||
368 | index = (addr >> 4) & 0xff; | |
369 | switch(index) { | |
370 | case 0x02: | |
371 | s->id = (val >> 24); | |
372 | break; | |
373 | case 0x08: | |
374 | s->tpr = val; | |
375 | break; | |
376 | case 0x0b: /* EOI */ | |
377 | apic_eoi(s); | |
378 | break; | |
379 | case 0x0f: | |
380 | s->spurious_vec = val & 0x1ff; | |
381 | break; | |
382 | case 0x30: | |
383 | case 0x31: | |
384 | s->icr[index & 1] = val; | |
385 | break; | |
386 | case 0x32 ... 0x37: | |
387 | { | |
388 | int n = index - 0x32; | |
389 | s->lvt[n] = val; | |
390 | if (n == APIC_LVT_TIMER) | |
391 | apic_timer_update(s, qemu_get_clock(vm_clock)); | |
392 | } | |
393 | break; | |
394 | case 0x38: | |
395 | s->initial_count = val; | |
396 | s->initial_count_load_time = qemu_get_clock(vm_clock); | |
397 | apic_timer_update(s, s->initial_count_load_time); | |
398 | break; | |
399 | case 0x3e: | |
400 | { | |
401 | int v; | |
402 | s->divide_conf = val & 0xb; | |
403 | v = (s->divide_conf & 3) | ((s->divide_conf >> 1) & 4); | |
404 | s->count_shift = (v + 1) & 7; | |
405 | } | |
406 | break; | |
407 | default: | |
408 | s->esr |= ESR_ILLEGAL_ADDRESS; | |
409 | break; | |
410 | } | |
411 | } | |
412 | ||
413 | ||
414 | ||
415 | static CPUReadMemoryFunc *apic_mem_read[3] = { | |
416 | apic_mem_readb, | |
417 | apic_mem_readw, | |
418 | apic_mem_readl, | |
419 | }; | |
420 | ||
421 | static CPUWriteMemoryFunc *apic_mem_write[3] = { | |
422 | apic_mem_writeb, | |
423 | apic_mem_writew, | |
424 | apic_mem_writel, | |
425 | }; | |
426 | ||
427 | int apic_init(CPUState *env) | |
428 | { | |
429 | APICState *s; | |
430 | int i; | |
431 | ||
432 | s = malloc(sizeof(APICState)); | |
433 | if (!s) | |
434 | return -1; | |
435 | memset(s, 0, sizeof(*s)); | |
436 | env->apic_state = s; | |
437 | s->cpu_env = env; | |
438 | 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; | |
443 | ||
444 | if (apic_io_memory == 0) { | |
445 | /* NOTE: the APIC is directly connected to the CPU - it is not | |
446 | on the global memory bus. */ | |
447 | apic_io_memory = cpu_register_io_memory(0, apic_mem_read, | |
448 | apic_mem_write, NULL); | |
449 | cpu_register_physical_memory(s->apicbase & ~0xfff, 0x1000, apic_io_memory); | |
450 | } | |
451 | s->timer = qemu_new_timer(vm_clock, apic_timer, s); | |
452 | return 0; | |
453 | } |