source: mainline/kernel/arch/ia32/src/smp/apic.c@ 06e1e95

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

C99 compliant header guards (hopefully) everywhere in the kernel.
Formatting and indentation changes.
Small improvements in sparc64.

  • Property mode set to 100644
File size: 13.5 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 ia32
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.vector = ((uintptr_t) ap_boot) / 4096; /* calculate the reset vector */
309 icr.delmod = DELMOD_STARTUP;
310 icr.destmod = DESTMOD_PHYS;
311 icr.level = LEVEL_ASSERT;
312 icr.shorthand = SHORTHAND_NONE;
313 icr.trigger_mode = TRIGMOD_LEVEL;
314 l_apic[ICRlo] = icr.lo;
315 delay(200);
316 }
317 }
318
319 return apic_poll_errors();
320}
321
322/** Initialize Local APIC. */
323void l_apic_init(void)
324{
325 lvt_error_t error;
326 lvt_lint_t lint;
327 tpr_t tpr;
328 svr_t svr;
329 icr_t icr;
330 tdcr_t tdcr;
331 lvt_tm_t tm;
332 ldr_t ldr;
333 dfr_t dfr;
334 uint32_t t1, t2;
335
336 /* Initialize LVT Error register. */
337 error.value = l_apic[LVT_Err];
338 error.masked = true;
339 l_apic[LVT_Err] = error.value;
340
341 /* Initialize LVT LINT0 register. */
342 lint.value = l_apic[LVT_LINT0];
343 lint.masked = true;
344 l_apic[LVT_LINT0] = lint.value;
345
346 /* Initialize LVT LINT1 register. */
347 lint.value = l_apic[LVT_LINT1];
348 lint.masked = true;
349 l_apic[LVT_LINT1] = lint.value;
350
351 /* Task Priority Register initialization. */
352 tpr.value = l_apic[TPR];
353 tpr.pri_sc = 0;
354 tpr.pri = 0;
355 l_apic[TPR] = tpr.value;
356
357 /* Spurious-Interrupt Vector Register initialization. */
358 svr.value = l_apic[SVR];
359 svr.vector = VECTOR_APIC_SPUR;
360 svr.lapic_enabled = true;
361 svr.focus_checking = true;
362 l_apic[SVR] = svr.value;
363
364 if (CPU->arch.family >= 6)
365 enable_l_apic_in_msr();
366
367 /* Interrupt Command Register initialization. */
368 icr.lo = l_apic[ICRlo];
369 icr.delmod = DELMOD_INIT;
370 icr.destmod = DESTMOD_PHYS;
371 icr.level = LEVEL_DEASSERT;
372 icr.shorthand = SHORTHAND_ALL_INCL;
373 icr.trigger_mode = TRIGMOD_LEVEL;
374 l_apic[ICRlo] = icr.lo;
375
376 /* Timer Divide Configuration Register initialization. */
377 tdcr.value = l_apic[TDCR];
378 tdcr.div_value = DIVIDE_1;
379 l_apic[TDCR] = tdcr.value;
380
381 /* Program local timer. */
382 tm.value = l_apic[LVT_Tm];
383 tm.vector = VECTOR_CLK;
384 tm.mode = TIMER_PERIODIC;
385 tm.masked = false;
386 l_apic[LVT_Tm] = tm.value;
387
388 /*
389 * Measure and configure the timer to generate timer
390 * interrupt with period 1s/HZ seconds.
391 */
392 t1 = l_apic[CCRT];
393 l_apic[ICRT] = 0xffffffff;
394
395 while (l_apic[CCRT] == t1)
396 ;
397
398 t1 = l_apic[CCRT];
399 delay(1000000/HZ);
400 t2 = l_apic[CCRT];
401
402 l_apic[ICRT] = t1-t2;
403
404 /* Program Logical Destination Register. */
405 ldr.value = l_apic[LDR];
406 if (CPU->id < sizeof(CPU->id)*8) /* size in bits */
407 ldr.id = (1<<CPU->id);
408 l_apic[LDR] = ldr.value;
409
410 /* Program Destination Format Register for Flat mode. */
411 dfr.value = l_apic[DFR];
412 dfr.model = MODEL_FLAT;
413 l_apic[DFR] = dfr.value;
414}
415
416/** Local APIC End of Interrupt. */
417void l_apic_eoi(void)
418{
419 l_apic[EOI] = 0;
420}
421
422/** Dump content of Local APIC registers. */
423void l_apic_debug(void)
424{
425#ifdef LAPIC_VERBOSE
426 lvt_tm_t tm;
427 lvt_lint_t lint;
428 lvt_error_t error;
429
430 printf("LVT on cpu%d, LAPIC ID: %d\n", CPU->id, l_apic_id());
431
432 tm.value = l_apic[LVT_Tm];
433 printf("LVT Tm: vector=%hhd, %s, %s, %s\n", tm.vector, delivs_str[tm.delivs], mask_str[tm.masked], tm_mode_str[tm.mode]);
434 lint.value = l_apic[LVT_LINT0];
435 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]);
436 lint.value = l_apic[LVT_LINT1];
437 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]);
438 error.value = l_apic[LVT_Err];
439 printf("LVT Err: vector=%hhd, %s, %s\n", error.vector, delivs_str[error.delivs], mask_str[error.masked]);
440#endif
441}
442
443/** Local APIC Timer Interrupt.
444 *
445 * @param n Interrupt vector number.
446 * @param istate Interrupted state.
447 */
448void l_apic_timer_interrupt(int n, istate_t *istate)
449{
450 l_apic_eoi();
451 clock();
452}
453
454/** Get Local APIC ID.
455 *
456 * @return Local APIC ID.
457 */
458uint8_t l_apic_id(void)
459{
460 l_apic_id_t idreg;
461
462 idreg.value = l_apic[L_APIC_ID];
463 return idreg.apic_id;
464}
465
466/** Read from IO APIC register.
467 *
468 * @param address IO APIC register address.
469 *
470 * @return Content of the addressed IO APIC register.
471 */
472uint32_t io_apic_read(uint8_t address)
473{
474 io_regsel_t regsel;
475
476 regsel.value = io_apic[IOREGSEL];
477 regsel.reg_addr = address;
478 io_apic[IOREGSEL] = regsel.value;
479 return io_apic[IOWIN];
480}
481
482/** Write to IO APIC register.
483 *
484 * @param address IO APIC register address.
485 * @param x Content to be written to the addressed IO APIC register.
486 */
487void io_apic_write(uint8_t address, uint32_t x)
488{
489 io_regsel_t regsel;
490
491 regsel.value = io_apic[IOREGSEL];
492 regsel.reg_addr = address;
493 io_apic[IOREGSEL] = regsel.value;
494 io_apic[IOWIN] = x;
495}
496
497/** Change some attributes of one item in I/O Redirection Table.
498 *
499 * @param pin IO APIC pin number.
500 * @param dest Interrupt destination address.
501 * @param v Interrupt vector to trigger.
502 * @param flags Flags.
503 */
504void io_apic_change_ioredtbl(int pin, int dest, uint8_t v, int flags)
505{
506 io_redirection_reg_t reg;
507 int dlvr = DELMOD_FIXED;
508
509 if (flags & LOPRI)
510 dlvr = DELMOD_LOWPRI;
511
512 reg.lo = io_apic_read(IOREDTBL + pin*2);
513 reg.hi = io_apic_read(IOREDTBL + pin*2 + 1);
514
515 reg.dest = dest;
516 reg.destmod = DESTMOD_LOGIC;
517 reg.trigger_mode = TRIGMOD_EDGE;
518 reg.intpol = POLARITY_HIGH;
519 reg.delmod = dlvr;
520 reg.intvec = v;
521
522 io_apic_write(IOREDTBL + pin*2, reg.lo);
523 io_apic_write(IOREDTBL + pin*2 + 1, reg.hi);
524}
525
526/** Mask IRQs in IO APIC.
527 *
528 * @param irqmask Bitmask of IRQs to be masked (0 = do not mask, 1 = mask).
529 */
530void io_apic_disable_irqs(uint16_t irqmask)
531{
532 io_redirection_reg_t reg;
533 int i, pin;
534
535 for (i=0;i<16;i++) {
536 if (irqmask & (1<<i)) {
537 /*
538 * Mask the signal input in IO APIC if there is a
539 * mapping for the respective IRQ number.
540 */
541 pin = smp_irq_to_pin(i);
542 if (pin != -1) {
543 reg.lo = io_apic_read(IOREDTBL + pin*2);
544 reg.masked = true;
545 io_apic_write(IOREDTBL + pin*2, reg.lo);
546 }
547
548 }
549 }
550}
551
552/** Unmask IRQs in IO APIC.
553 *
554 * @param irqmask Bitmask of IRQs to be unmasked (0 = do not unmask, 1 = unmask).
555 */
556void io_apic_enable_irqs(uint16_t irqmask)
557{
558 int i, pin;
559 io_redirection_reg_t reg;
560
561 for (i=0;i<16;i++) {
562 if (irqmask & (1<<i)) {
563 /*
564 * Unmask the signal input in IO APIC if there is a
565 * mapping for the respective IRQ number.
566 */
567 pin = smp_irq_to_pin(i);
568 if (pin != -1) {
569 reg.lo = io_apic_read(IOREDTBL + pin*2);
570 reg.masked = false;
571 io_apic_write(IOREDTBL + pin*2, reg.lo);
572 }
573
574 }
575 }
576}
577
578#endif /* CONFIG_SMP */
579
580/** @}
581 */
Note: See TracBrowser for help on using the repository browser.