source: mainline/kernel/arch/ia32xen/src/smp/apic.c@ df4ed85

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since df4ed85 was df4ed85, checked in by Jakub Jermar <jakub@…>, 19 years ago

© versus ©

  • Property mode set to 100644
File size: 13.4 KB
Line 
1/*
2 * Copyright (c) 2001-2004 Jakub Jermar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup ia32xen
30 * @{
31 */
32/** @file
33 */
34
35#include <arch/types.h>
36#include <arch/smp/apic.h>
37#include <arch/smp/ap.h>
38#include <arch/smp/mps.h>
39#include <arch/boot/boot.h>
40#include <mm/page.h>
41#include <time/delay.h>
42#include <interrupt.h>
43#include <arch/interrupt.h>
44#include <print.h>
45#include <arch/asm.h>
46#include <arch.h>
47
48#ifdef CONFIG_SMP
49
50/*
51 * Advanced Programmable Interrupt Controller for SMP systems.
52 * Tested on:
53 * Bochs 2.0.2 - Bochs 2.2.6 with 2-8 CPUs
54 * Simics 2.0.28 - Simics 2.2.19 2-15 CPUs
55 * VMware Workstation 5.5 with 2 CPUs
56 * QEMU 0.8.0 with 2-15 CPUs
57 * ASUS P/I-P65UP5 + ASUS C-P55T2D REV. 1.41 with 2x 200Mhz Pentium CPUs
58 * ASUS PCH-DL with 2x 3000Mhz Pentium 4 Xeon (HT) CPUs
59 * MSI K7D Master-L with 2x 2100MHz Athlon MP CPUs
60 */
61
62/*
63 * These variables either stay configured as initilalized, or are changed by
64 * the MP configuration code.
65 *
66 * Pay special attention to the volatile keyword. Without it, gcc -O2 would
67 * optimize the code too much and accesses to l_apic and io_apic, that must
68 * always be 32-bit, would use byte oriented instructions.
69 */
70volatile uint32_t *l_apic = (uint32_t *) 0xfee00000;
71volatile uint32_t *io_apic = (uint32_t *) 0xfec00000;
72
73uint32_t apic_id_mask = 0;
74
75static int apic_poll_errors(void);
76
77#ifdef LAPIC_VERBOSE
78static char *delmod_str[] = {
79 "Fixed",
80 "Lowest Priority",
81 "SMI",
82 "Reserved",
83 "NMI",
84 "INIT",
85 "STARTUP",
86 "ExtInt"
87};
88
89static char *destmod_str[] = {
90 "Physical",
91 "Logical"
92};
93
94static char *trigmod_str[] = {
95 "Edge",
96 "Level"
97};
98
99static char *mask_str[] = {
100 "Unmasked",
101 "Masked"
102};
103
104static char *delivs_str[] = {
105 "Idle",
106 "Send Pending"
107};
108
109static char *tm_mode_str[] = {
110 "One-shot",
111 "Periodic"
112};
113
114static char *intpol_str[] = {
115 "Polarity High",
116 "Polarity Low"
117};
118#endif /* LAPIC_VERBOSE */
119
120
121static void apic_spurious(int n, istate_t *istate);
122static void l_apic_timer_interrupt(int n, istate_t *istate);
123
124/** Initialize APIC on BSP. */
125void apic_init(void)
126{
127 io_apic_id_t idreg;
128 int i;
129
130 exc_register(VECTOR_APIC_SPUR, "apic_spurious", (iroutine) apic_spurious);
131
132 enable_irqs_function = io_apic_enable_irqs;
133 disable_irqs_function = io_apic_disable_irqs;
134 eoi_function = l_apic_eoi;
135
136 /*
137 * Configure interrupt routing.
138 * IRQ 0 remains masked as the time signal is generated by l_apic's themselves.
139 * Other interrupts will be forwarded to the lowest priority CPU.
140 */
141 io_apic_disable_irqs(0xffff);
142 exc_register(VECTOR_CLK, "l_apic_timer", (iroutine) l_apic_timer_interrupt);
143 for (i = 0; i < IRQ_COUNT; i++) {
144 int pin;
145
146 if ((pin = smp_irq_to_pin(i)) != -1) {
147 io_apic_change_ioredtbl(pin, DEST_ALL, IVT_IRQBASE+i, LOPRI);
148 }
149 }
150
151 /*
152 * Ensure that io_apic has unique ID.
153 */
154 idreg.value = io_apic_read(IOAPICID);
155 if ((1<<idreg.apic_id) & apic_id_mask) { /* see if IO APIC ID is used already */
156 for (i = 0; i < APIC_ID_COUNT; i++) {
157 if (!((1<<i) & apic_id_mask)) {
158 idreg.apic_id = i;
159 io_apic_write(IOAPICID, idreg.value);
160 break;
161 }
162 }
163 }
164
165 /*
166 * Configure the BSP's lapic.
167 */
168 l_apic_init();
169
170 l_apic_debug();
171}
172
173/** APIC spurious interrupt handler.
174 *
175 * @param n Interrupt vector.
176 * @param istate Interrupted state.
177 */
178void apic_spurious(int n, istate_t *istate)
179{
180#ifdef CONFIG_DEBUG
181 printf("cpu%d: APIC spurious interrupt\n", CPU->id);
182#endif
183}
184
185/** Poll for APIC errors.
186 *
187 * Examine Error Status Register and report all errors found.
188 *
189 * @return 0 on error, 1 on success.
190 */
191int apic_poll_errors(void)
192{
193 esr_t esr;
194
195 esr.value = l_apic[ESR];
196
197 if (esr.send_checksum_error)
198 printf("Send Checksum Error\n");
199 if (esr.receive_checksum_error)
200 printf("Receive Checksum Error\n");
201 if (esr.send_accept_error)
202 printf("Send Accept Error\n");
203 if (esr.receive_accept_error)
204 printf("Receive Accept Error\n");
205 if (esr.send_illegal_vector)
206 printf("Send Illegal Vector\n");
207 if (esr.received_illegal_vector)
208 printf("Received Illegal Vector\n");
209 if (esr.illegal_register_address)
210 printf("Illegal Register Address\n");
211
212 return !esr.err_bitmap;
213}
214
215/** Send all CPUs excluding CPU IPI vector.
216 *
217 * @param vector Interrupt vector to be sent.
218 *
219 * @return 0 on failure, 1 on success.
220 */
221int l_apic_broadcast_custom_ipi(uint8_t vector)
222{
223 icr_t icr;
224
225 icr.lo = l_apic[ICRlo];
226 icr.delmod = DELMOD_FIXED;
227 icr.destmod = DESTMOD_LOGIC;
228 icr.level = LEVEL_ASSERT;
229 icr.shorthand = SHORTHAND_ALL_EXCL;
230 icr.trigger_mode = TRIGMOD_LEVEL;
231 icr.vector = vector;
232
233 l_apic[ICRlo] = icr.lo;
234
235 icr.lo = l_apic[ICRlo];
236 if (icr.delivs == DELIVS_PENDING) {
237#ifdef CONFIG_DEBUG
238 printf("IPI is pending.\n");
239#endif
240 }
241
242 return apic_poll_errors();
243}
244
245/** Universal Start-up Algorithm for bringing up the AP processors.
246 *
247 * @param apicid APIC ID of the processor to be brought up.
248 *
249 * @return 0 on failure, 1 on success.
250 */
251int l_apic_send_init_ipi(uint8_t apicid)
252{
253 icr_t icr;
254 int i;
255
256 /*
257 * Read the ICR register in and zero all non-reserved fields.
258 */
259 icr.lo = l_apic[ICRlo];
260 icr.hi = l_apic[ICRhi];
261
262 icr.delmod = DELMOD_INIT;
263 icr.destmod = DESTMOD_PHYS;
264 icr.level = LEVEL_ASSERT;
265 icr.trigger_mode = TRIGMOD_LEVEL;
266 icr.shorthand = SHORTHAND_NONE;
267 icr.vector = 0;
268 icr.dest = apicid;
269
270 l_apic[ICRhi] = icr.hi;
271 l_apic[ICRlo] = icr.lo;
272
273 /*
274 * According to MP Specification, 20us should be enough to
275 * deliver the IPI.
276 */
277 delay(20);
278
279 if (!apic_poll_errors())
280 return 0;
281
282 icr.lo = l_apic[ICRlo];
283 if (icr.delivs == DELIVS_PENDING) {
284#ifdef CONFIG_DEBUG
285 printf("IPI is pending.\n");
286#endif
287 }
288
289 icr.delmod = DELMOD_INIT;
290 icr.destmod = DESTMOD_PHYS;
291 icr.level = LEVEL_DEASSERT;
292 icr.shorthand = SHORTHAND_NONE;
293 icr.trigger_mode = TRIGMOD_LEVEL;
294 icr.vector = 0;
295 l_apic[ICRlo] = icr.lo;
296
297 /*
298 * Wait 10ms as MP Specification specifies.
299 */
300 delay(10000);
301
302 if (!is_82489DX_apic(l_apic[LAVR])) {
303 /*
304 * If this is not 82489DX-based l_apic we must send two STARTUP IPI's.
305 */
306 for (i = 0; i<2; i++) {
307 icr.lo = l_apic[ICRlo];
308 icr.delmod = DELMOD_STARTUP;
309 icr.destmod = DESTMOD_PHYS;
310 icr.level = LEVEL_ASSERT;
311 icr.shorthand = SHORTHAND_NONE;
312 icr.trigger_mode = TRIGMOD_LEVEL;
313 l_apic[ICRlo] = icr.lo;
314 delay(200);
315 }
316 }
317
318 return apic_poll_errors();
319}
320
321/** Initialize Local APIC. */
322void l_apic_init(void)
323{
324 lvt_error_t error;
325 lvt_lint_t lint;
326 tpr_t tpr;
327 svr_t svr;
328 icr_t icr;
329 tdcr_t tdcr;
330 lvt_tm_t tm;
331 ldr_t ldr;
332 dfr_t dfr;
333 uint32_t t1, t2;
334
335 /* Initialize LVT Error register. */
336 error.value = l_apic[LVT_Err];
337 error.masked = true;
338 l_apic[LVT_Err] = error.value;
339
340 /* Initialize LVT LINT0 register. */
341 lint.value = l_apic[LVT_LINT0];
342 lint.masked = true;
343 l_apic[LVT_LINT0] = lint.value;
344
345 /* Initialize LVT LINT1 register. */
346 lint.value = l_apic[LVT_LINT1];
347 lint.masked = true;
348 l_apic[LVT_LINT1] = lint.value;
349
350 /* Task Priority Register initialization. */
351 tpr.value = l_apic[TPR];
352 tpr.pri_sc = 0;
353 tpr.pri = 0;
354 l_apic[TPR] = tpr.value;
355
356 /* Spurious-Interrupt Vector Register initialization. */
357 svr.value = l_apic[SVR];
358 svr.vector = VECTOR_APIC_SPUR;
359 svr.lapic_enabled = true;
360 svr.focus_checking = true;
361 l_apic[SVR] = svr.value;
362
363 if (CPU->arch.family >= 6)
364 enable_l_apic_in_msr();
365
366 /* Interrupt Command Register initialization. */
367 icr.lo = l_apic[ICRlo];
368 icr.delmod = DELMOD_INIT;
369 icr.destmod = DESTMOD_PHYS;
370 icr.level = LEVEL_DEASSERT;
371 icr.shorthand = SHORTHAND_ALL_INCL;
372 icr.trigger_mode = TRIGMOD_LEVEL;
373 l_apic[ICRlo] = icr.lo;
374
375 /* Timer Divide Configuration Register initialization. */
376 tdcr.value = l_apic[TDCR];
377 tdcr.div_value = DIVIDE_1;
378 l_apic[TDCR] = tdcr.value;
379
380 /* Program local timer. */
381 tm.value = l_apic[LVT_Tm];
382 tm.vector = VECTOR_CLK;
383 tm.mode = TIMER_PERIODIC;
384 tm.masked = false;
385 l_apic[LVT_Tm] = tm.value;
386
387 /*
388 * Measure and configure the timer to generate timer
389 * interrupt with period 1s/HZ seconds.
390 */
391 t1 = l_apic[CCRT];
392 l_apic[ICRT] = 0xffffffff;
393
394 while (l_apic[CCRT] == t1)
395 ;
396
397 t1 = l_apic[CCRT];
398 delay(1000000/HZ);
399 t2 = l_apic[CCRT];
400
401 l_apic[ICRT] = t1-t2;
402
403 /* Program Logical Destination Register. */
404 ldr.value = l_apic[LDR];
405 if (CPU->id < sizeof(CPU->id)*8) /* size in bits */
406 ldr.id = (1<<CPU->id);
407 l_apic[LDR] = ldr.value;
408
409 /* Program Destination Format Register for Flat mode. */
410 dfr.value = l_apic[DFR];
411 dfr.model = MODEL_FLAT;
412 l_apic[DFR] = dfr.value;
413}
414
415/** Local APIC End of Interrupt. */
416void l_apic_eoi(void)
417{
418 l_apic[EOI] = 0;
419}
420
421/** Dump content of Local APIC registers. */
422void l_apic_debug(void)
423{
424#ifdef LAPIC_VERBOSE
425 lvt_tm_t tm;
426 lvt_lint_t lint;
427 lvt_error_t error;
428
429 printf("LVT on cpu%d, LAPIC ID: %d\n", CPU->id, l_apic_id());
430
431 tm.value = l_apic[LVT_Tm];
432 printf("LVT Tm: vector=%hhd, %s, %s, %s\n", tm.vector, delivs_str[tm.delivs], mask_str[tm.masked], tm_mode_str[tm.mode]);
433 lint.value = l_apic[LVT_LINT0];
434 printf("LVT LINT0: vector=%hhd, %s, %s, %s, irr=%d, %s, %s\n", tm.vector, delmod_str[lint.delmod], delivs_str[lint.delivs], intpol_str[lint.intpol], lint.irr, trigmod_str[lint.trigger_mode], mask_str[lint.masked]);
435 lint.value = l_apic[LVT_LINT1];
436 printf("LVT LINT1: vector=%hhd, %s, %s, %s, irr=%d, %s, %s\n", tm.vector, delmod_str[lint.delmod], delivs_str[lint.delivs], intpol_str[lint.intpol], lint.irr, trigmod_str[lint.trigger_mode], mask_str[lint.masked]);
437 error.value = l_apic[LVT_Err];
438 printf("LVT Err: vector=%hhd, %s, %s\n", error.vector, delivs_str[error.delivs], mask_str[error.masked]);
439#endif
440}
441
442/** Local APIC Timer Interrupt.
443 *
444 * @param n Interrupt vector number.
445 * @param istate Interrupted state.
446 */
447void l_apic_timer_interrupt(int n, istate_t *istate)
448{
449 l_apic_eoi();
450 clock();
451}
452
453/** Get Local APIC ID.
454 *
455 * @return Local APIC ID.
456 */
457uint8_t l_apic_id(void)
458{
459 l_apic_id_t idreg;
460
461 idreg.value = l_apic[L_APIC_ID];
462 return idreg.apic_id;
463}
464
465/** Read from IO APIC register.
466 *
467 * @param address IO APIC register address.
468 *
469 * @return Content of the addressed IO APIC register.
470 */
471uint32_t io_apic_read(uint8_t address)
472{
473 io_regsel_t regsel;
474
475 regsel.value = io_apic[IOREGSEL];
476 regsel.reg_addr = address;
477 io_apic[IOREGSEL] = regsel.value;
478 return io_apic[IOWIN];
479}
480
481/** Write to IO APIC register.
482 *
483 * @param address IO APIC register address.
484 * @param x Content to be written to the addressed IO APIC register.
485 */
486void io_apic_write(uint8_t address, uint32_t x)
487{
488 io_regsel_t regsel;
489
490 regsel.value = io_apic[IOREGSEL];
491 regsel.reg_addr = address;
492 io_apic[IOREGSEL] = regsel.value;
493 io_apic[IOWIN] = x;
494}
495
496/** Change some attributes of one item in I/O Redirection Table.
497 *
498 * @param pin IO APIC pin number.
499 * @param dest Interrupt destination address.
500 * @param v Interrupt vector to trigger.
501 * @param flags Flags.
502 */
503void io_apic_change_ioredtbl(int pin, int dest, uint8_t v, int flags)
504{
505 io_redirection_reg_t reg;
506 int dlvr = DELMOD_FIXED;
507
508 if (flags & LOPRI)
509 dlvr = DELMOD_LOWPRI;
510
511 reg.lo = io_apic_read(IOREDTBL + pin*2);
512 reg.hi = io_apic_read(IOREDTBL + pin*2 + 1);
513
514 reg.dest = dest;
515 reg.destmod = DESTMOD_LOGIC;
516 reg.trigger_mode = TRIGMOD_EDGE;
517 reg.intpol = POLARITY_HIGH;
518 reg.delmod = dlvr;
519 reg.intvec = v;
520
521 io_apic_write(IOREDTBL + pin*2, reg.lo);
522 io_apic_write(IOREDTBL + pin*2 + 1, reg.hi);
523}
524
525/** Mask IRQs in IO APIC.
526 *
527 * @param irqmask Bitmask of IRQs to be masked (0 = do not mask, 1 = mask).
528 */
529void io_apic_disable_irqs(uint16_t irqmask)
530{
531 io_redirection_reg_t reg;
532 int i, pin;
533
534 for (i=0;i<16;i++) {
535 if (irqmask & (1<<i)) {
536 /*
537 * Mask the signal input in IO APIC if there is a
538 * mapping for the respective IRQ number.
539 */
540 pin = smp_irq_to_pin(i);
541 if (pin != -1) {
542 reg.lo = io_apic_read(IOREDTBL + pin*2);
543 reg.masked = true;
544 io_apic_write(IOREDTBL + pin*2, reg.lo);
545 }
546
547 }
548 }
549}
550
551/** Unmask IRQs in IO APIC.
552 *
553 * @param irqmask Bitmask of IRQs to be unmasked (0 = do not unmask, 1 = unmask).
554 */
555void io_apic_enable_irqs(uint16_t irqmask)
556{
557 int i, pin;
558 io_redirection_reg_t reg;
559
560 for (i=0;i<16;i++) {
561 if (irqmask & (1<<i)) {
562 /*
563 * Unmask the signal input in IO APIC if there is a
564 * mapping for the respective IRQ number.
565 */
566 pin = smp_irq_to_pin(i);
567 if (pin != -1) {
568 reg.lo = io_apic_read(IOREDTBL + pin*2);
569 reg.masked = false;
570 io_apic_write(IOREDTBL + pin*2, reg.lo);
571 }
572
573 }
574 }
575}
576
577#endif /* CONFIG_SMP */
578
579/** @}
580 */
Note: See TracBrowser for help on using the repository browser.