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

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

Introduce PIC operations indirection mechanism

Some architectures switch from one interrupt controller implementation
to another during runtime. By providing a cleaner indirection mechanism,
it is possible e.g. for the ia32 IRQ 7 handler to distinguish i8259
spurious interrupts from actual IRQ 7 device interrupts, even when the
i8259 interrupt controller is no longer active.

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