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

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

as kernel little brother drivers are not needed anymore, the device numbers do not have to be correlated between kernel and uspace in any way
introduce new syscall sys_device_assign_devno() for generating system-wide unique device numbers for uspace

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