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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since acc7ce4 was acc7ce4, checked in by Martin Decky <martin@…>, 14 years ago

uspace interrupt controller drivers for i8259 and APIC (non-functional yet)
convert NE2000 driver to use these drivers (not enabling the IRQ in kernel), this solves the "spurious interrupt" issue
(however, on SMP machines this renders the driver unusable for now since the APIC driver does not do anything yet)

  • Property mode set to 100644
File size: 14.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 <typedefs.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#include <ddi/irq.h>
48#include <ddi/device.h>
49
50#ifdef CONFIG_SMP
51
52/*
53 * Advanced Programmable Interrupt Controller for SMP systems.
54 * Tested on:
55 * Bochs 2.0.2 - Bochs 2.2.6 with 2-8 CPUs
56 * Simics 2.0.28 - Simics 2.2.19 2-15 CPUs
57 * VMware Workstation 5.5 with 2 CPUs
58 * QEMU 0.8.0 with 2-15 CPUs
59 * ASUS P/I-P65UP5 + ASUS C-P55T2D REV. 1.41 with 2x 200Mhz Pentium CPUs
60 * ASUS PCH-DL with 2x 3000Mhz Pentium 4 Xeon (HT) CPUs
61 * MSI K7D Master-L with 2x 2100MHz Athlon MP CPUs
62 *
63 */
64
65/*
66 * These variables either stay configured as initilalized, or are changed by
67 * the MP configuration code.
68 *
69 * Pay special attention to the volatile keyword. Without it, gcc -O2 would
70 * optimize the code too much and accesses to l_apic and io_apic, that must
71 * always be 32-bit, would use byte oriented instructions.
72 *
73 */
74volatile uint32_t *l_apic = (uint32_t *) UINT32_C(0xfee00000);
75volatile uint32_t *io_apic = (uint32_t *) UINT32_C(0xfec00000);
76
77uint32_t apic_id_mask = 0;
78uint8_t bsp_l_apic = 0;
79
80static irq_t l_apic_timer_irq;
81
82static int apic_poll_errors(void);
83
84#ifdef LAPIC_VERBOSE
85static const char *delmod_str[] = {
86 "Fixed",
87 "Lowest Priority",
88 "SMI",
89 "Reserved",
90 "NMI",
91 "INIT",
92 "STARTUP",
93 "ExtInt"
94};
95
96static const char *destmod_str[] = {
97 "Physical",
98 "Logical"
99};
100
101static const char *trigmod_str[] = {
102 "Edge",
103 "Level"
104};
105
106static const char *mask_str[] = {
107 "Unmasked",
108 "Masked"
109};
110
111static const char *delivs_str[] = {
112 "Idle",
113 "Send Pending"
114};
115
116static const char *tm_mode_str[] = {
117 "One-shot",
118 "Periodic"
119};
120
121static const char *intpol_str[] = {
122 "Polarity High",
123 "Polarity Low"
124};
125#endif /* LAPIC_VERBOSE */
126
127/** APIC spurious interrupt handler.
128 *
129 * @param n Interrupt vector.
130 * @param istate Interrupted state.
131 *
132 */
133static void apic_spurious(unsigned int n __attribute__((unused)),
134 istate_t *istate __attribute__((unused)))
135{
136#ifdef CONFIG_DEBUG
137 printf("cpu%u: APIC spurious interrupt\n", CPU->id);
138#endif
139}
140
141static irq_ownership_t l_apic_timer_claim(irq_t *irq)
142{
143 return IRQ_ACCEPT;
144}
145
146static void l_apic_timer_irq_handler(irq_t *irq)
147{
148 /*
149 * Holding a spinlock could prevent clock() from preempting
150 * the current thread. In this case, we don't need to hold the
151 * irq->lock so we just unlock it and then lock it again.
152 */
153 irq_spinlock_unlock(&irq->lock, false);
154 clock();
155 irq_spinlock_lock(&irq->lock, false);
156}
157
158/** Get Local APIC ID.
159 *
160 * @return Local APIC ID.
161 *
162 */
163static uint8_t l_apic_id(void)
164{
165 l_apic_id_t idreg;
166
167 idreg.value = l_apic[L_APIC_ID];
168 return idreg.apic_id;
169}
170
171/** Initialize APIC on BSP. */
172void apic_init(void)
173{
174 exc_register(VECTOR_APIC_SPUR, "apic_spurious", false,
175 (iroutine_t) apic_spurious);
176
177 enable_irqs_function = io_apic_enable_irqs;
178 disable_irqs_function = io_apic_disable_irqs;
179 eoi_function = l_apic_eoi;
180 irqs_info = "apic";
181
182 /*
183 * Configure interrupt routing.
184 * IRQ 0 remains masked as the time signal is generated by l_apic's themselves.
185 * Other interrupts will be forwarded to the lowest priority CPU.
186 */
187 io_apic_disable_irqs(0xffffU);
188
189 irq_initialize(&l_apic_timer_irq);
190 l_apic_timer_irq.preack = true;
191 l_apic_timer_irq.devno = device_assign_devno();
192 l_apic_timer_irq.inr = IRQ_CLK;
193 l_apic_timer_irq.claim = l_apic_timer_claim;
194 l_apic_timer_irq.handler = l_apic_timer_irq_handler;
195 irq_register(&l_apic_timer_irq);
196
197 uint8_t i;
198 for (i = 0; i < IRQ_COUNT; i++) {
199 int pin;
200
201 if ((pin = smp_irq_to_pin(i)) != -1)
202 io_apic_change_ioredtbl((uint8_t) pin, DEST_ALL, (uint8_t) (IVT_IRQBASE + i), LOPRI);
203 }
204
205 /*
206 * Ensure that io_apic has unique ID.
207 */
208 io_apic_id_t idreg;
209
210 idreg.value = io_apic_read(IOAPICID);
211 if ((1 << idreg.apic_id) & apic_id_mask) { /* See if IO APIC ID is used already */
212 for (i = 0; i < APIC_ID_COUNT; i++) {
213 if (!((1 << i) & apic_id_mask)) {
214 idreg.apic_id = i;
215 io_apic_write(IOAPICID, idreg.value);
216 break;
217 }
218 }
219 }
220
221 /*
222 * Configure the BSP's lapic.
223 */
224 l_apic_init();
225 l_apic_debug();
226
227 bsp_l_apic = l_apic_id();
228}
229
230/** Poll for APIC errors.
231 *
232 * Examine Error Status Register and report all errors found.
233 *
234 * @return 0 on error, 1 on success.
235 *
236 */
237int apic_poll_errors(void)
238{
239 esr_t esr;
240
241 esr.value = l_apic[ESR];
242
243 if (esr.send_checksum_error)
244 printf("Send Checksum Error\n");
245 if (esr.receive_checksum_error)
246 printf("Receive Checksum Error\n");
247 if (esr.send_accept_error)
248 printf("Send Accept Error\n");
249 if (esr.receive_accept_error)
250 printf("Receive Accept Error\n");
251 if (esr.send_illegal_vector)
252 printf("Send Illegal Vector\n");
253 if (esr.received_illegal_vector)
254 printf("Received Illegal Vector\n");
255 if (esr.illegal_register_address)
256 printf("Illegal Register Address\n");
257
258 return !esr.err_bitmap;
259}
260
261/** Send all CPUs excluding CPU IPI vector.
262 *
263 * @param vector Interrupt vector to be sent.
264 *
265 * @return 0 on failure, 1 on success.
266 *
267 */
268int l_apic_broadcast_custom_ipi(uint8_t vector)
269{
270 icr_t icr;
271
272 icr.lo = l_apic[ICRlo];
273 icr.delmod = DELMOD_FIXED;
274 icr.destmod = DESTMOD_LOGIC;
275 icr.level = LEVEL_ASSERT;
276 icr.shorthand = SHORTHAND_ALL_EXCL;
277 icr.trigger_mode = TRIGMOD_LEVEL;
278 icr.vector = vector;
279
280 l_apic[ICRlo] = icr.lo;
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 return apic_poll_errors();
290}
291
292/** Universal Start-up Algorithm for bringing up the AP processors.
293 *
294 * @param apicid APIC ID of the processor to be brought up.
295 *
296 * @return 0 on failure, 1 on success.
297 *
298 */
299int l_apic_send_init_ipi(uint8_t apicid)
300{
301 /*
302 * Read the ICR register in and zero all non-reserved fields.
303 */
304 icr_t icr;
305
306 icr.lo = l_apic[ICRlo];
307 icr.hi = l_apic[ICRhi];
308
309 icr.delmod = DELMOD_INIT;
310 icr.destmod = DESTMOD_PHYS;
311 icr.level = LEVEL_ASSERT;
312 icr.trigger_mode = TRIGMOD_LEVEL;
313 icr.shorthand = SHORTHAND_NONE;
314 icr.vector = 0;
315 icr.dest = apicid;
316
317 l_apic[ICRhi] = icr.hi;
318 l_apic[ICRlo] = icr.lo;
319
320 /*
321 * According to MP Specification, 20us should be enough to
322 * deliver the IPI.
323 */
324 delay(20);
325
326 if (!apic_poll_errors())
327 return 0;
328
329 icr.lo = l_apic[ICRlo];
330 if (icr.delivs == DELIVS_PENDING) {
331#ifdef CONFIG_DEBUG
332 printf("IPI is pending.\n");
333#endif
334 }
335
336 icr.delmod = DELMOD_INIT;
337 icr.destmod = DESTMOD_PHYS;
338 icr.level = LEVEL_DEASSERT;
339 icr.shorthand = SHORTHAND_NONE;
340 icr.trigger_mode = TRIGMOD_LEVEL;
341 icr.vector = 0;
342 l_apic[ICRlo] = icr.lo;
343
344 /*
345 * Wait 10ms as MP Specification specifies.
346 */
347 delay(10000);
348
349 if (!is_82489DX_apic(l_apic[LAVR])) {
350 /*
351 * If this is not 82489DX-based l_apic we must send two STARTUP IPI's.
352 */
353 unsigned int i;
354 for (i = 0; i < 2; i++) {
355 icr.lo = l_apic[ICRlo];
356 icr.vector = (uint8_t) (((uintptr_t) ap_boot) >> 12); /* calculate the reset vector */
357 icr.delmod = DELMOD_STARTUP;
358 icr.destmod = DESTMOD_PHYS;
359 icr.level = LEVEL_ASSERT;
360 icr.shorthand = SHORTHAND_NONE;
361 icr.trigger_mode = TRIGMOD_LEVEL;
362 l_apic[ICRlo] = icr.lo;
363 delay(200);
364 }
365 }
366
367 return apic_poll_errors();
368}
369
370/** Initialize Local APIC. */
371void l_apic_init(void)
372{
373 /* Initialize LVT Error register. */
374 lvt_error_t error;
375
376 error.value = l_apic[LVT_Err];
377 error.masked = true;
378 l_apic[LVT_Err] = error.value;
379
380 /* Initialize LVT LINT0 register. */
381 lvt_lint_t lint;
382
383 lint.value = l_apic[LVT_LINT0];
384 lint.masked = true;
385 l_apic[LVT_LINT0] = lint.value;
386
387 /* Initialize LVT LINT1 register. */
388 lint.value = l_apic[LVT_LINT1];
389 lint.masked = true;
390 l_apic[LVT_LINT1] = lint.value;
391
392 /* Task Priority Register initialization. */
393 tpr_t tpr;
394
395 tpr.value = l_apic[TPR];
396 tpr.pri_sc = 0;
397 tpr.pri = 0;
398 l_apic[TPR] = tpr.value;
399
400 /* Spurious-Interrupt Vector Register initialization. */
401 svr_t svr;
402
403 svr.value = l_apic[SVR];
404 svr.vector = VECTOR_APIC_SPUR;
405 svr.lapic_enabled = true;
406 svr.focus_checking = true;
407 l_apic[SVR] = svr.value;
408
409 if (CPU->arch.family >= 6)
410 enable_l_apic_in_msr();
411
412 /* Interrupt Command Register initialization. */
413 icr_t icr;
414
415 icr.lo = l_apic[ICRlo];
416 icr.delmod = DELMOD_INIT;
417 icr.destmod = DESTMOD_PHYS;
418 icr.level = LEVEL_DEASSERT;
419 icr.shorthand = SHORTHAND_ALL_INCL;
420 icr.trigger_mode = TRIGMOD_LEVEL;
421 l_apic[ICRlo] = icr.lo;
422
423 /* Timer Divide Configuration Register initialization. */
424 tdcr_t tdcr;
425
426 tdcr.value = l_apic[TDCR];
427 tdcr.div_value = DIVIDE_1;
428 l_apic[TDCR] = tdcr.value;
429
430 /* Program local timer. */
431 lvt_tm_t tm;
432
433 tm.value = l_apic[LVT_Tm];
434 tm.vector = VECTOR_CLK;
435 tm.mode = TIMER_PERIODIC;
436 tm.masked = false;
437 l_apic[LVT_Tm] = tm.value;
438
439 /*
440 * Measure and configure the timer to generate timer
441 * interrupt with period 1s/HZ seconds.
442 */
443 uint32_t t1 = l_apic[CCRT];
444 l_apic[ICRT] = 0xffffffff;
445
446 while (l_apic[CCRT] == t1);
447
448 t1 = l_apic[CCRT];
449 delay(1000000 / HZ);
450 uint32_t t2 = l_apic[CCRT];
451
452 l_apic[ICRT] = t1 - t2;
453
454 /* Program Logical Destination Register. */
455 ASSERT(CPU->id < 8);
456 ldr_t ldr;
457
458 ldr.value = l_apic[LDR];
459 ldr.id = (uint8_t) (1 << CPU->id);
460 l_apic[LDR] = ldr.value;
461
462 /* Program Destination Format Register for Flat mode. */
463 dfr_t dfr;
464
465 dfr.value = l_apic[DFR];
466 dfr.model = MODEL_FLAT;
467 l_apic[DFR] = dfr.value;
468}
469
470/** Local APIC End of Interrupt. */
471void l_apic_eoi(void)
472{
473 l_apic[EOI] = 0;
474}
475
476/** Dump content of Local APIC registers. */
477void l_apic_debug(void)
478{
479#ifdef LAPIC_VERBOSE
480 printf("LVT on cpu%u, LAPIC ID: %" PRIu8 "\n",
481 CPU->id, l_apic_id());
482
483 lvt_tm_t tm;
484 tm.value = l_apic[LVT_Tm];
485 printf("LVT Tm: vector=%" PRIu8 ", %s, %s, %s\n",
486 tm.vector, delivs_str[tm.delivs], mask_str[tm.masked],
487 tm_mode_str[tm.mode]);
488
489 lvt_lint_t lint;
490 lint.value = l_apic[LVT_LINT0];
491 printf("LVT LINT0: vector=%" PRIu8 ", %s, %s, %s, irr=%u, %s, %s\n",
492 tm.vector, delmod_str[lint.delmod], delivs_str[lint.delivs],
493 intpol_str[lint.intpol], lint.irr, trigmod_str[lint.trigger_mode],
494 mask_str[lint.masked]);
495
496 lint.value = l_apic[LVT_LINT1];
497 printf("LVT LINT1: vector=%" PRIu8 ", %s, %s, %s, irr=%u, %s, %s\n",
498 tm.vector, delmod_str[lint.delmod], delivs_str[lint.delivs],
499 intpol_str[lint.intpol], lint.irr, trigmod_str[lint.trigger_mode],
500 mask_str[lint.masked]);
501
502 lvt_error_t error;
503 error.value = l_apic[LVT_Err];
504 printf("LVT Err: vector=%" PRIu8 ", %s, %s\n", error.vector,
505 delivs_str[error.delivs], mask_str[error.masked]);
506#endif
507}
508
509/** Read from IO APIC register.
510 *
511 * @param address IO APIC register address.
512 *
513 * @return Content of the addressed IO APIC register.
514 *
515 */
516uint32_t io_apic_read(uint8_t address)
517{
518 io_regsel_t regsel;
519
520 regsel.value = io_apic[IOREGSEL];
521 regsel.reg_addr = address;
522 io_apic[IOREGSEL] = regsel.value;
523 return io_apic[IOWIN];
524}
525
526/** Write to IO APIC register.
527 *
528 * @param address IO APIC register address.
529 * @param val Content to be written to the addressed IO APIC register.
530 *
531 */
532void io_apic_write(uint8_t address, uint32_t val)
533{
534 io_regsel_t regsel;
535
536 regsel.value = io_apic[IOREGSEL];
537 regsel.reg_addr = address;
538 io_apic[IOREGSEL] = regsel.value;
539 io_apic[IOWIN] = val;
540}
541
542/** Change some attributes of one item in I/O Redirection Table.
543 *
544 * @param pin IO APIC pin number.
545 * @param dest Interrupt destination address.
546 * @param vec Interrupt vector to trigger.
547 * @param flags Flags.
548 *
549 */
550void io_apic_change_ioredtbl(uint8_t pin, uint8_t dest, uint8_t vec,
551 unsigned int flags)
552{
553 unsigned int dlvr;
554
555 if (flags & LOPRI)
556 dlvr = DELMOD_LOWPRI;
557 else
558 dlvr = DELMOD_FIXED;
559
560 io_redirection_reg_t reg;
561 reg.lo = io_apic_read((uint8_t) (IOREDTBL + pin * 2));
562 reg.hi = io_apic_read((uint8_t) (IOREDTBL + pin * 2 + 1));
563
564 reg.dest = dest;
565 reg.destmod = DESTMOD_LOGIC;
566 reg.trigger_mode = TRIGMOD_EDGE;
567 reg.intpol = POLARITY_HIGH;
568 reg.delmod = dlvr;
569 reg.intvec = vec;
570
571 io_apic_write((uint8_t) (IOREDTBL + pin * 2), reg.lo);
572 io_apic_write((uint8_t) (IOREDTBL + pin * 2 + 1), reg.hi);
573}
574
575/** Mask IRQs in IO APIC.
576 *
577 * @param irqmask Bitmask of IRQs to be masked (0 = do not mask, 1 = mask).
578 *
579 */
580void io_apic_disable_irqs(uint16_t irqmask)
581{
582 unsigned int i;
583 for (i = 0; i < 16; i++) {
584 if (irqmask & (1 << i)) {
585 /*
586 * Mask the signal input in IO APIC if there is a
587 * mapping for the respective IRQ number.
588 */
589 int pin = smp_irq_to_pin(i);
590 if (pin != -1) {
591 io_redirection_reg_t reg;
592
593 reg.lo = io_apic_read((uint8_t) (IOREDTBL + pin * 2));
594 reg.masked = true;
595 io_apic_write((uint8_t) (IOREDTBL + pin * 2), reg.lo);
596 }
597
598 }
599 }
600}
601
602/** Unmask IRQs in IO APIC.
603 *
604 * @param irqmask Bitmask of IRQs to be unmasked (0 = do not unmask, 1 = unmask).
605 *
606 */
607void io_apic_enable_irqs(uint16_t irqmask)
608{
609 unsigned int i;
610 for (i = 0; i < 16; i++) {
611 if (irqmask & (1 << i)) {
612 /*
613 * Unmask the signal input in IO APIC if there is a
614 * mapping for the respective IRQ number.
615 */
616 int pin = smp_irq_to_pin(i);
617 if (pin != -1) {
618 io_redirection_reg_t reg;
619
620 reg.lo = io_apic_read((uint8_t) (IOREDTBL + pin * 2));
621 reg.masked = false;
622 io_apic_write((uint8_t) (IOREDTBL + pin * 2), reg.lo);
623 }
624
625 }
626 }
627}
628
629#endif /* CONFIG_SMP */
630
631/** @}
632 */
Note: See TracBrowser for help on using the repository browser.