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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a35b458 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 15.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 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
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 *) L_APIC_BASE;
75volatile uint32_t *io_apic = (uint32_t *) IO_APIC_BASE;
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 log(LF_ARCH, LVL_DEBUG, "cpu%u: APIC spurious interrupt", 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.inr = IRQ_CLK;
192 l_apic_timer_irq.claim = l_apic_timer_claim;
193 l_apic_timer_irq.handler = l_apic_timer_irq_handler;
194 irq_register(&l_apic_timer_irq);
195
196 uint8_t i;
197 for (i = 0; i < IRQ_COUNT; i++) {
198 int pin;
199
200 if ((pin = smp_irq_to_pin(i)) != -1)
201 io_apic_change_ioredtbl((uint8_t) pin, DEST_ALL, (uint8_t) (IVT_IRQBASE + i), LOPRI);
202 }
203
204 /*
205 * Ensure that io_apic has unique ID.
206 */
207 io_apic_id_t idreg;
208
209 idreg.value = io_apic_read(IOAPICID);
210 if ((1 << idreg.apic_id) & apic_id_mask) { /* See if IO APIC ID is used already */
211 for (i = 0; i < APIC_ID_COUNT; i++) {
212 if (!((1 << i) & apic_id_mask)) {
213 idreg.apic_id = i;
214 io_apic_write(IOAPICID, idreg.value);
215 break;
216 }
217 }
218 }
219
220 /*
221 * Configure the BSP's lapic.
222 */
223 l_apic_init();
224 l_apic_debug();
225
226 bsp_l_apic = l_apic_id();
227}
228
229/** Poll for APIC errors.
230 *
231 * Examine Error Status Register and report all errors found.
232 *
233 * @return 0 on error, 1 on success.
234 *
235 */
236int apic_poll_errors(void)
237{
238 esr_t esr;
239
240 esr.value = l_apic[ESR];
241
242 if (esr.err_bitmap) {
243 log_begin(LF_ARCH, LVL_ERROR);
244 log_printf("APIC errors detected:");
245 if (esr.send_checksum_error)
246 log_printf("\nSend Checksum Error");
247 if (esr.receive_checksum_error)
248 log_printf("\nReceive Checksum Error");
249 if (esr.send_accept_error)
250 log_printf("\nSend Accept Error");
251 if (esr.receive_accept_error)
252 log_printf("\nReceive Accept Error");
253 if (esr.send_illegal_vector)
254 log_printf("\nSend Illegal Vector");
255 if (esr.received_illegal_vector)
256 log_printf("\nReceived Illegal Vector");
257 if (esr.illegal_register_address)
258 log_printf("\nIllegal Register Address");
259 log_end();
260 }
261
262 return !esr.err_bitmap;
263}
264
265/* Waits for the destination cpu to accept the previous ipi. */
266static void l_apic_wait_for_delivery(void)
267{
268 icr_t icr;
269
270 do {
271 icr.lo = l_apic[ICRlo];
272 } while (icr.delivs != DELIVS_IDLE);
273}
274
275/** Send one CPU an IPI vector.
276 *
277 * @param apicid Physical APIC ID of the destination CPU.
278 * @param vector Interrupt vector to be sent.
279 *
280 * @return 0 on failure, 1 on success.
281 */
282int l_apic_send_custom_ipi(uint8_t apicid, uint8_t vector)
283{
284 icr_t icr;
285
286 /* Wait for a destination cpu to accept our previous ipi. */
287 l_apic_wait_for_delivery();
288
289 icr.lo = l_apic[ICRlo];
290 icr.hi = l_apic[ICRhi];
291
292 icr.delmod = DELMOD_FIXED;
293 icr.destmod = DESTMOD_PHYS;
294 icr.level = LEVEL_ASSERT;
295 icr.shorthand = SHORTHAND_NONE;
296 icr.trigger_mode = TRIGMOD_LEVEL;
297 icr.vector = vector;
298 icr.dest = apicid;
299
300 /* Send the IPI by writing to l_apic[ICRlo]. */
301 l_apic[ICRhi] = icr.hi;
302 l_apic[ICRlo] = icr.lo;
303
304 return apic_poll_errors();
305}
306
307/** Send all CPUs excluding CPU IPI vector.
308 *
309 * @param vector Interrupt vector to be sent.
310 *
311 * @return 0 on failure, 1 on success.
312 *
313 */
314int l_apic_broadcast_custom_ipi(uint8_t vector)
315{
316 icr_t icr;
317
318 /* Wait for a destination cpu to accept our previous ipi. */
319 l_apic_wait_for_delivery();
320
321 icr.lo = l_apic[ICRlo];
322 icr.delmod = DELMOD_FIXED;
323 icr.destmod = DESTMOD_LOGIC;
324 icr.level = LEVEL_ASSERT;
325 icr.shorthand = SHORTHAND_ALL_EXCL;
326 icr.trigger_mode = TRIGMOD_LEVEL;
327 icr.vector = vector;
328
329 l_apic[ICRlo] = icr.lo;
330
331 return apic_poll_errors();
332}
333
334/** Universal Start-up Algorithm for bringing up the AP processors.
335 *
336 * @param apicid APIC ID of the processor to be brought up.
337 *
338 * @return 0 on failure, 1 on success.
339 *
340 */
341int l_apic_send_init_ipi(uint8_t apicid)
342{
343 /*
344 * Read the ICR register in and zero all non-reserved fields.
345 */
346 icr_t icr;
347
348 icr.lo = l_apic[ICRlo];
349 icr.hi = l_apic[ICRhi];
350
351 icr.delmod = DELMOD_INIT;
352 icr.destmod = DESTMOD_PHYS;
353 icr.level = LEVEL_ASSERT;
354 icr.trigger_mode = TRIGMOD_LEVEL;
355 icr.shorthand = SHORTHAND_NONE;
356 icr.vector = 0;
357 icr.dest = apicid;
358
359 l_apic[ICRhi] = icr.hi;
360 l_apic[ICRlo] = icr.lo;
361
362 /*
363 * According to MP Specification, 20us should be enough to
364 * deliver the IPI.
365 */
366 delay(20);
367
368 if (!apic_poll_errors())
369 return 0;
370
371 l_apic_wait_for_delivery();
372
373 icr.lo = l_apic[ICRlo];
374 icr.delmod = DELMOD_INIT;
375 icr.destmod = DESTMOD_PHYS;
376 icr.level = LEVEL_DEASSERT;
377 icr.shorthand = SHORTHAND_NONE;
378 icr.trigger_mode = TRIGMOD_LEVEL;
379 icr.vector = 0;
380 l_apic[ICRlo] = icr.lo;
381
382 /*
383 * Wait 10ms as MP Specification specifies.
384 */
385 delay(10000);
386
387 if (!is_82489DX_apic(l_apic[LAVR])) {
388 /*
389 * If this is not 82489DX-based l_apic we must send two STARTUP IPI's.
390 */
391 unsigned int i;
392 for (i = 0; i < 2; i++) {
393 icr.lo = l_apic[ICRlo];
394 icr.vector = (uint8_t) (((uintptr_t) ap_boot) >> 12); /* calculate the reset vector */
395 icr.delmod = DELMOD_STARTUP;
396 icr.destmod = DESTMOD_PHYS;
397 icr.level = LEVEL_ASSERT;
398 icr.shorthand = SHORTHAND_NONE;
399 icr.trigger_mode = TRIGMOD_LEVEL;
400 l_apic[ICRlo] = icr.lo;
401 delay(200);
402 }
403 }
404
405 return apic_poll_errors();
406}
407
408/** Initialize Local APIC. */
409void l_apic_init(void)
410{
411 /* Initialize LVT Error register. */
412 lvt_error_t error;
413
414 error.value = l_apic[LVT_Err];
415 error.masked = true;
416 l_apic[LVT_Err] = error.value;
417
418 /* Initialize LVT LINT0 register. */
419 lvt_lint_t lint;
420
421 lint.value = l_apic[LVT_LINT0];
422 lint.masked = true;
423 l_apic[LVT_LINT0] = lint.value;
424
425 /* Initialize LVT LINT1 register. */
426 lint.value = l_apic[LVT_LINT1];
427 lint.masked = true;
428 l_apic[LVT_LINT1] = lint.value;
429
430 /* Task Priority Register initialization. */
431 tpr_t tpr;
432
433 tpr.value = l_apic[TPR];
434 tpr.pri_sc = 0;
435 tpr.pri = 0;
436 l_apic[TPR] = tpr.value;
437
438 /* Spurious-Interrupt Vector Register initialization. */
439 svr_t svr;
440
441 svr.value = l_apic[SVR];
442 svr.vector = VECTOR_APIC_SPUR;
443 svr.lapic_enabled = true;
444 svr.focus_checking = true;
445 l_apic[SVR] = svr.value;
446
447 if (CPU->arch.family >= 6)
448 enable_l_apic_in_msr();
449
450 /* Interrupt Command Register initialization. */
451 icr_t icr;
452
453 icr.lo = l_apic[ICRlo];
454 icr.delmod = DELMOD_INIT;
455 icr.destmod = DESTMOD_PHYS;
456 icr.level = LEVEL_DEASSERT;
457 icr.shorthand = SHORTHAND_ALL_INCL;
458 icr.trigger_mode = TRIGMOD_LEVEL;
459 l_apic[ICRlo] = icr.lo;
460
461 /* Timer Divide Configuration Register initialization. */
462 tdcr_t tdcr;
463
464 tdcr.value = l_apic[TDCR];
465 tdcr.div_value = DIVIDE_1;
466 l_apic[TDCR] = tdcr.value;
467
468 /* Program local timer. */
469 lvt_tm_t tm;
470
471 tm.value = l_apic[LVT_Tm];
472 tm.vector = VECTOR_CLK;
473 tm.mode = TIMER_PERIODIC;
474 tm.masked = false;
475 l_apic[LVT_Tm] = tm.value;
476
477 /*
478 * Measure and configure the timer to generate timer
479 * interrupt with period 1s/HZ seconds.
480 */
481 uint32_t t1 = l_apic[CCRT];
482 l_apic[ICRT] = 0xffffffff;
483
484 while (l_apic[CCRT] == t1);
485
486 t1 = l_apic[CCRT];
487 delay(1000000 / HZ);
488 uint32_t t2 = l_apic[CCRT];
489
490 l_apic[ICRT] = t1 - t2;
491
492 /* Program Logical Destination Register. */
493 assert(CPU->id < 8);
494 ldr_t ldr;
495
496 ldr.value = l_apic[LDR];
497 ldr.id = (uint8_t) (1 << CPU->id);
498 l_apic[LDR] = ldr.value;
499
500 /* Program Destination Format Register for Flat mode. */
501 dfr_t dfr;
502
503 dfr.value = l_apic[DFR];
504 dfr.model = MODEL_FLAT;
505 l_apic[DFR] = dfr.value;
506}
507
508/** Local APIC End of Interrupt. */
509void l_apic_eoi(void)
510{
511 l_apic[EOI] = 0;
512}
513
514/** Dump content of Local APIC registers. */
515void l_apic_debug(void)
516{
517#ifdef LAPIC_VERBOSE
518 log_begin(LF_ARCH, LVL_DEBUG);
519 log_printf("LVT on cpu%u, LAPIC ID: %" PRIu8 "\n",
520 CPU->id, l_apic_id());
521
522 lvt_tm_t tm;
523 tm.value = l_apic[LVT_Tm];
524 log_printf("LVT Tm: vector=%" PRIu8 ", %s, %s, %s\n",
525 tm.vector, delivs_str[tm.delivs], mask_str[tm.masked],
526 tm_mode_str[tm.mode]);
527
528 lvt_lint_t lint;
529 lint.value = l_apic[LVT_LINT0];
530 log_printf("LVT LINT0: vector=%" PRIu8 ", %s, %s, %s, irr=%u, %s, %s\n",
531 tm.vector, delmod_str[lint.delmod], delivs_str[lint.delivs],
532 intpol_str[lint.intpol], lint.irr, trigmod_str[lint.trigger_mode],
533 mask_str[lint.masked]);
534
535 lint.value = l_apic[LVT_LINT1];
536 log_printf("LVT LINT1: vector=%" PRIu8 ", %s, %s, %s, irr=%u, %s, %s\n",
537 tm.vector, delmod_str[lint.delmod], delivs_str[lint.delivs],
538 intpol_str[lint.intpol], lint.irr, trigmod_str[lint.trigger_mode],
539 mask_str[lint.masked]);
540
541 lvt_error_t error;
542 error.value = l_apic[LVT_Err];
543 log_printf("LVT Err: vector=%" PRIu8 ", %s, %s\n", error.vector,
544 delivs_str[error.delivs], mask_str[error.masked]);
545 log_end();
546#endif
547}
548
549/** Read from IO APIC register.
550 *
551 * @param address IO APIC register address.
552 *
553 * @return Content of the addressed IO APIC register.
554 *
555 */
556uint32_t io_apic_read(uint8_t address)
557{
558 io_regsel_t regsel;
559
560 regsel.value = io_apic[IOREGSEL];
561 regsel.reg_addr = address;
562 io_apic[IOREGSEL] = regsel.value;
563 return io_apic[IOWIN];
564}
565
566/** Write to IO APIC register.
567 *
568 * @param address IO APIC register address.
569 * @param val Content to be written to the addressed IO APIC register.
570 *
571 */
572void io_apic_write(uint8_t address, uint32_t val)
573{
574 io_regsel_t regsel;
575
576 regsel.value = io_apic[IOREGSEL];
577 regsel.reg_addr = address;
578 io_apic[IOREGSEL] = regsel.value;
579 io_apic[IOWIN] = val;
580}
581
582/** Change some attributes of one item in I/O Redirection Table.
583 *
584 * @param pin IO APIC pin number.
585 * @param dest Interrupt destination address.
586 * @param vec Interrupt vector to trigger.
587 * @param flags Flags.
588 *
589 */
590void io_apic_change_ioredtbl(uint8_t pin, uint8_t dest, uint8_t vec,
591 unsigned int flags)
592{
593 unsigned int dlvr;
594
595 if (flags & LOPRI)
596 dlvr = DELMOD_LOWPRI;
597 else
598 dlvr = DELMOD_FIXED;
599
600 io_redirection_reg_t reg;
601 reg.lo = io_apic_read((uint8_t) (IOREDTBL + pin * 2));
602 reg.hi = io_apic_read((uint8_t) (IOREDTBL + pin * 2 + 1));
603
604 reg.dest = dest;
605 reg.destmod = DESTMOD_LOGIC;
606 reg.trigger_mode = TRIGMOD_EDGE;
607 reg.intpol = POLARITY_HIGH;
608 reg.delmod = dlvr;
609 reg.intvec = vec;
610
611 io_apic_write((uint8_t) (IOREDTBL + pin * 2), reg.lo);
612 io_apic_write((uint8_t) (IOREDTBL + pin * 2 + 1), reg.hi);
613}
614
615/** Mask IRQs in IO APIC.
616 *
617 * @param irqmask Bitmask of IRQs to be masked (0 = do not mask, 1 = mask).
618 *
619 */
620void io_apic_disable_irqs(uint16_t irqmask)
621{
622 unsigned int i;
623 for (i = 0; i < 16; i++) {
624 if (irqmask & (1 << i)) {
625 /*
626 * Mask the signal input in IO APIC if there is a
627 * mapping for the respective IRQ number.
628 */
629 int pin = smp_irq_to_pin(i);
630 if (pin != -1) {
631 io_redirection_reg_t reg;
632
633 reg.lo = io_apic_read((uint8_t) (IOREDTBL + pin * 2));
634 reg.masked = true;
635 io_apic_write((uint8_t) (IOREDTBL + pin * 2), reg.lo);
636 }
637
638 }
639 }
640}
641
642/** Unmask IRQs in IO APIC.
643 *
644 * @param irqmask Bitmask of IRQs to be unmasked (0 = do not unmask, 1 = unmask).
645 *
646 */
647void io_apic_enable_irqs(uint16_t irqmask)
648{
649 unsigned int i;
650 for (i = 0; i < 16; i++) {
651 if (irqmask & (1 << i)) {
652 /*
653 * Unmask the signal input in IO APIC if there is a
654 * mapping for the respective IRQ number.
655 */
656 int pin = smp_irq_to_pin(i);
657 if (pin != -1) {
658 io_redirection_reg_t reg;
659
660 reg.lo = io_apic_read((uint8_t) (IOREDTBL + pin * 2));
661 reg.masked = false;
662 io_apic_write((uint8_t) (IOREDTBL + pin * 2), reg.lo);
663 }
664
665 }
666 }
667}
668
669#endif /* CONFIG_SMP */
670
671/** @}
672 */
Note: See TracBrowser for help on using the repository browser.