Fix comma mistakenly removed in last commit.
[helenos.git] / kernel / arch / xen32 / src / smp / apic.c
blob0191f0600745bf7ebc2440b0a16ce54a94ee2bfd
1 /*
2 * Copyright (C) 2001-2004 Jakub Jermar
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
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.
29 /** @addtogroup xen32
30 * @{
32 /** @file
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>
48 #ifdef CONFIG_SMP
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
63 * These variables either stay configured as initilalized, or are changed by
64 * the MP configuration code.
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.
70 volatile uint32_t *l_apic = (uint32_t *) 0xfee00000;
71 volatile uint32_t *io_apic = (uint32_t *) 0xfec00000;
73 uint32_t apic_id_mask = 0;
75 static int apic_poll_errors(void);
77 #ifdef LAPIC_VERBOSE
78 static char *delmod_str[] = {
79 "Fixed",
80 "Lowest Priority",
81 "SMI",
82 "Reserved",
83 "NMI",
84 "INIT",
85 "STARTUP",
86 "ExtInt"
89 static char *destmod_str[] = {
90 "Physical",
91 "Logical"
94 static char *trigmod_str[] = {
95 "Edge",
96 "Level"
99 static char *mask_str[] = {
100 "Unmasked",
101 "Masked"
104 static char *delivs_str[] = {
105 "Idle",
106 "Send Pending"
109 static char *tm_mode_str[] = {
110 "One-shot",
111 "Periodic"
114 static char *intpol_str[] = {
115 "Polarity High",
116 "Polarity Low"
118 #endif /* LAPIC_VERBOSE */
121 static void apic_spurious(int n, istate_t *istate);
122 static void l_apic_timer_interrupt(int n, istate_t *istate);
124 /** Initialize APIC on BSP. */
125 void apic_init(void)
127 io_apic_id_t idreg;
128 int i;
130 exc_register(VECTOR_APIC_SPUR, "apic_spurious", (iroutine) apic_spurious);
132 enable_irqs_function = io_apic_enable_irqs;
133 disable_irqs_function = io_apic_disable_irqs;
134 eoi_function = l_apic_eoi;
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.
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;
146 if ((pin = smp_irq_to_pin(i)) != -1) {
147 io_apic_change_ioredtbl(pin, DEST_ALL, IVT_IRQBASE+i, LOPRI);
152 * Ensure that io_apic has unique ID.
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;
166 * Configure the BSP's lapic.
168 l_apic_init();
170 l_apic_debug();
173 /** APIC spurious interrupt handler.
175 * @param n Interrupt vector.
176 * @param istate Interrupted state.
178 void apic_spurious(int n, istate_t *istate)
180 #ifdef CONFIG_DEBUG
181 printf("cpu%d: APIC spurious interrupt\n", CPU->id);
182 #endif
185 /** Poll for APIC errors.
187 * Examine Error Status Register and report all errors found.
189 * @return 0 on error, 1 on success.
191 int apic_poll_errors(void)
193 esr_t esr;
195 esr.value = l_apic[ESR];
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");
212 return !esr.err_bitmap;
215 /** Send all CPUs excluding CPU IPI vector.
217 * @param vector Interrupt vector to be sent.
219 * @return 0 on failure, 1 on success.
221 int l_apic_broadcast_custom_ipi(uint8_t vector)
223 icr_t icr;
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;
233 l_apic[ICRlo] = icr.lo;
235 icr.lo = l_apic[ICRlo];
236 if (icr.delivs == DELIVS_PENDING) {
237 #ifdef CONFIG_DEBUG
238 printf("IPI is pending.\n");
239 #endif
242 return apic_poll_errors();
245 /** Universal Start-up Algorithm for bringing up the AP processors.
247 * @param apicid APIC ID of the processor to be brought up.
249 * @return 0 on failure, 1 on success.
251 int l_apic_send_init_ipi(uint8_t apicid)
253 icr_t icr;
254 int i;
257 * Read the ICR register in and zero all non-reserved fields.
259 icr.lo = l_apic[ICRlo];
260 icr.hi = l_apic[ICRhi];
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;
270 l_apic[ICRhi] = icr.hi;
271 l_apic[ICRlo] = icr.lo;
274 * According to MP Specification, 20us should be enough to
275 * deliver the IPI.
277 delay(20);
279 if (!apic_poll_errors())
280 return 0;
282 icr.lo = l_apic[ICRlo];
283 if (icr.delivs == DELIVS_PENDING) {
284 #ifdef CONFIG_DEBUG
285 printf("IPI is pending.\n");
286 #endif
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;
298 * Wait 10ms as MP Specification specifies.
300 delay(10000);
302 if (!is_82489DX_apic(l_apic[LAVR])) {
304 * If this is not 82489DX-based l_apic we must send two STARTUP IPI's.
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);
318 return apic_poll_errors();
321 /** Initialize Local APIC. */
322 void l_apic_init(void)
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;
335 /* Initialize LVT Error register. */
336 error.value = l_apic[LVT_Err];
337 error.masked = true;
338 l_apic[LVT_Err] = error.value;
340 /* Initialize LVT LINT0 register. */
341 lint.value = l_apic[LVT_LINT0];
342 lint.masked = true;
343 l_apic[LVT_LINT0] = lint.value;
345 /* Initialize LVT LINT1 register. */
346 lint.value = l_apic[LVT_LINT1];
347 lint.masked = true;
348 l_apic[LVT_LINT1] = lint.value;
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;
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;
363 if (CPU->arch.family >= 6)
364 enable_l_apic_in_msr();
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;
375 /* Timer Divide Configuration Register initialization. */
376 tdcr.value = l_apic[TDCR];
377 tdcr.div_value = DIVIDE_1;
378 l_apic[TDCR] = tdcr.value;
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;
388 * Measure and configure the timer to generate timer
389 * interrupt with period 1s/HZ seconds.
391 t1 = l_apic[CCRT];
392 l_apic[ICRT] = 0xffffffff;
394 while (l_apic[CCRT] == t1)
397 t1 = l_apic[CCRT];
398 delay(1000000/HZ);
399 t2 = l_apic[CCRT];
401 l_apic[ICRT] = t1-t2;
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;
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;
415 /** Local APIC End of Interrupt. */
416 void l_apic_eoi(void)
418 l_apic[EOI] = 0;
421 /** Dump content of Local APIC registers. */
422 void l_apic_debug(void)
424 #ifdef LAPIC_VERBOSE
425 lvt_tm_t tm;
426 lvt_lint_t lint;
427 lvt_error_t error;
429 printf("LVT on cpu%d, LAPIC ID: %d\n", CPU->id, l_apic_id());
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
442 /** Local APIC Timer Interrupt.
444 * @param n Interrupt vector number.
445 * @param istate Interrupted state.
447 void l_apic_timer_interrupt(int n, istate_t *istate)
449 l_apic_eoi();
450 clock();
453 /** Get Local APIC ID.
455 * @return Local APIC ID.
457 uint8_t l_apic_id(void)
459 l_apic_id_t idreg;
461 idreg.value = l_apic[L_APIC_ID];
462 return idreg.apic_id;
465 /** Read from IO APIC register.
467 * @param address IO APIC register address.
469 * @return Content of the addressed IO APIC register.
471 uint32_t io_apic_read(uint8_t address)
473 io_regsel_t regsel;
475 regsel.value = io_apic[IOREGSEL];
476 regsel.reg_addr = address;
477 io_apic[IOREGSEL] = regsel.value;
478 return io_apic[IOWIN];
481 /** Write to IO APIC register.
483 * @param address IO APIC register address.
484 * @param x Content to be written to the addressed IO APIC register.
486 void io_apic_write(uint8_t address, uint32_t x)
488 io_regsel_t regsel;
490 regsel.value = io_apic[IOREGSEL];
491 regsel.reg_addr = address;
492 io_apic[IOREGSEL] = regsel.value;
493 io_apic[IOWIN] = x;
496 /** Change some attributes of one item in I/O Redirection Table.
498 * @param pin IO APIC pin number.
499 * @param dest Interrupt destination address.
500 * @param v Interrupt vector to trigger.
501 * @param flags Flags.
503 void io_apic_change_ioredtbl(int pin, int dest, uint8_t v, int flags)
505 io_redirection_reg_t reg;
506 int dlvr = DELMOD_FIXED;
508 if (flags & LOPRI)
509 dlvr = DELMOD_LOWPRI;
511 reg.lo = io_apic_read(IOREDTBL + pin*2);
512 reg.hi = io_apic_read(IOREDTBL + pin*2 + 1);
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;
521 io_apic_write(IOREDTBL + pin*2, reg.lo);
522 io_apic_write(IOREDTBL + pin*2 + 1, reg.hi);
525 /** Mask IRQs in IO APIC.
527 * @param irqmask Bitmask of IRQs to be masked (0 = do not mask, 1 = mask).
529 void io_apic_disable_irqs(uint16_t irqmask)
531 io_redirection_reg_t reg;
532 int i, pin;
534 for (i=0;i<16;i++) {
535 if (irqmask & (1<<i)) {
537 * Mask the signal input in IO APIC if there is a
538 * mapping for the respective IRQ number.
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);
551 /** Unmask IRQs in IO APIC.
553 * @param irqmask Bitmask of IRQs to be unmasked (0 = do not unmask, 1 = unmask).
555 void io_apic_enable_irqs(uint16_t irqmask)
557 int i, pin;
558 io_redirection_reg_t reg;
560 for (i=0;i<16;i++) {
561 if (irqmask & (1<<i)) {
563 * Unmask the signal input in IO APIC if there is a
564 * mapping for the respective IRQ number.
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);
577 #endif /* CONFIG_SMP */
579 /** @}