]>
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 | ||
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 | } |