Add a nice little KIDT_ACCESS structure for setting IDT entries in a pretty way
[reactos.git] / reactos / hal / halx86 / mp / apic.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 2004, 2005 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id$
20 *
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * FILE: hal/halx86/apic.c
24 * PURPOSE:
25 * PROGRAMMER:
26 */
27
28 /* INCLUDE ***********************************************************************/
29
30 #include <hal.h>
31 #define NDEBUG
32 #include <debug.h>
33 #include <internal/ntoskrnl.h>
34
35 /* GLOBALS ***********************************************************************/
36
37 ULONG CPUCount; /* Total number of CPUs */
38 ULONG BootCPU; /* Bootstrap processor */
39 ULONG OnlineCPUs; /* Bitmask of online CPUs */
40 CPU_INFO CPUMap[MAX_CPU]; /* Map of all CPUs in the system */
41
42 #ifdef CONFIG_SMP
43 PULONG BIOSBase; /* Virtual address of BIOS data segment */
44 PULONG CommonBase; /* Virtual address of common area */
45 #endif
46
47 PULONG APICBase = (PULONG)APIC_DEFAULT_BASE; /* Virtual address of local APIC */
48
49 ULONG APICMode; /* APIC mode at startup */
50
51 /* For debugging */
52 ULONG lastregr[MAX_CPU];
53 ULONG lastvalr[MAX_CPU];
54 ULONG lastregw[MAX_CPU];
55 ULONG lastvalw[MAX_CPU];
56
57 #ifdef CONFIG_SMP
58 typedef struct __attribute__((packed)) _COMMON_AREA_INFO
59 {
60 ULONG Stack; /* Location of AP stack */
61 ULONG PageDirectory; /* Page directory for an AP */
62 ULONG NtProcessStartup; /* Kernel entry point for an AP */
63 ULONG PaeModeEnabled; /* PAE mode is enabled */
64 ULONG Debug[16]; /* For debugging */
65 } COMMON_AREA_INFO, *PCOMMON_AREA_INFO;
66 #endif
67
68 CHAR *APstart, *APend;
69
70 #define BIOS_AREA 0x0
71 #define COMMON_AREA 0x2000
72
73 #define HZ (100)
74 #define APIC_DIVISOR (16)
75
76 #define CMOS_READ(address) ({ \
77 WRITE_PORT_UCHAR((PUCHAR)0x70, address)); \
78 READ_PORT_UCHAR((PUCHAR)0x71)); \
79 })
80
81 #define CMOS_WRITE(address, value) ({ \
82 WRITE_PORT_UCHAR((PUCHAR)0x70, address); \
83 WRITE_PORT_UCHAR((PUCHAR)0x71, value); \
84 })
85
86 /* FUNCTIONS *********************************************************************/
87
88 extern ULONG Read8254Timer(VOID);
89 extern VOID WaitFor8254Wraparound(VOID);
90 extern VOID MpsTimerInterrupt(VOID);
91 extern VOID MpsErrorInterrupt(VOID);
92 extern VOID MpsSpuriousInterrupt(VOID);
93 extern VOID MpsIpiInterrupt(VOID);
94
95 ULONG APICGetMaxLVT(VOID)
96 {
97 ULONG tmp, ver, maxlvt;
98
99 tmp = APICRead(APIC_VER);
100 ver = GET_APIC_VERSION(tmp);
101 /* 82489DXs do not report # of LVT entries. */
102 maxlvt = APIC_INTEGRATED(ver) ? GET_APIC_MAXLVT(tmp) : 2;
103
104 return maxlvt;
105 }
106
107 VOID APICClear(VOID)
108 {
109 ULONG tmp, maxlvt;
110
111 maxlvt = APICGetMaxLVT();
112
113 /*
114 * Careful: we have to set masks only first to deassert
115 * any level-triggered sources.
116 */
117
118 if (maxlvt >= 3)
119 {
120 tmp = ERROR_VECTOR;
121 APICWrite(APIC_LVT3, tmp | APIC_LVT3_MASKED);
122 }
123
124 tmp = APICRead(APIC_LVTT);
125 APICWrite(APIC_LVTT, tmp | APIC_LVT_MASKED);
126
127 tmp = APICRead(APIC_LINT0);
128 APICWrite(APIC_LINT0, tmp | APIC_LVT_MASKED);
129
130 tmp = APICRead(APIC_LINT1);
131 APICWrite(APIC_LINT1, tmp | APIC_LVT_MASKED);
132
133 if (maxlvt >= 4)
134 {
135 tmp = APICRead(APIC_LVTPC);
136 APICWrite(APIC_LVTPC, tmp | APIC_LVT_MASKED);
137 }
138 #if 0
139 if (maxlvt >= 5)
140 {
141 tmp = APICRead(APIC_LVTTHMR);
142 APICWrite(APIC_LVTTHMR, tmp | APIC_LVT_MASKED);
143 }
144 #endif
145 /*
146 * Clean APIC state for other OSs:
147 */
148 APICWrite(APIC_LVTT, APIC_LVT_MASKED);
149 APICWrite(APIC_LINT0, APIC_LVT_MASKED);
150 APICWrite(APIC_LINT1, APIC_LVT_MASKED);
151
152 if (maxlvt >= 3)
153 {
154 APICWrite(APIC_LVT3, APIC_LVT3_MASKED);
155 }
156
157 if (maxlvt >= 4)
158 {
159 APICWrite(APIC_LVTPC, APIC_LVT_MASKED);
160 }
161 #if 0
162 if (maxlvt >= 5)
163 {
164 APICWrite(APIC_LVTTHMR, APIC_LVT_MASKED);
165 }
166 #endif
167 }
168
169 /* Enable symetric I/O mode ie. connect the BSP's local APIC to INT and NMI lines */
170 VOID EnableApicMode(VOID)
171 {
172 /*
173 * Do not trust the local APIC being empty at bootup.
174 */
175 APICClear();
176
177 WRITE_PORT_UCHAR((PUCHAR)0x22, 0x70);
178 WRITE_PORT_UCHAR((PUCHAR)0x23, 0x01);
179 }
180
181 /* Disable symetric I/O mode ie. go to PIC mode */
182 inline VOID DisableSMPMode(VOID)
183 {
184 /*
185 * Put the board back into PIC mode (has an effect
186 * only on certain older boards). Note that APIC
187 * interrupts, including IPIs, won't work beyond
188 * this point! The only exception are INIT IPIs.
189 */
190 WRITE_PORT_UCHAR((PUCHAR)0x22, 0x70);
191 WRITE_PORT_UCHAR((PUCHAR)0x23, 0x00);
192 }
193
194 VOID DumpESR(VOID)
195 {
196 ULONG tmp;
197
198 if (APICGetMaxLVT() > 3)
199 {
200 APICWrite(APIC_ESR, 0);
201 }
202 tmp = APICRead(APIC_ESR);
203 DbgPrint("ESR %08x\n", tmp);
204 }
205
206
207 VOID APICDisable(VOID)
208 {
209 ULONG tmp;
210
211 APICClear();
212
213 /*
214 * Disable APIC (implies clearing of registers for 82489DX!).
215 */
216 tmp = APICRead(APIC_SIVR);
217 tmp &= ~APIC_SIVR_ENABLE;
218 APICWrite(APIC_SIVR, tmp);
219 }
220
221
222 inline ULONG _APICRead(ULONG Offset)
223 {
224 PULONG p;
225
226 p = (PULONG)((ULONG)APICBase + Offset);
227 return *p;
228 }
229
230 #if 0
231 inline VOID APICWrite(ULONG Offset,
232 ULONG Value)
233 {
234 PULONG p;
235
236 p = (PULONG)((ULONG)APICBase + Offset);
237
238 *p = Value;
239 }
240 #else
241 inline VOID APICWrite(ULONG Offset,
242 ULONG Value)
243 {
244 PULONG p;
245 ULONG CPU = (_APICRead(APIC_ID) & APIC_ID_MASK) >> 24;
246
247 lastregw[CPU] = Offset;
248 lastvalw[CPU] = Value;
249
250 p = (PULONG)((ULONG)APICBase + Offset);
251
252 *p = Value;
253 }
254 #endif
255
256
257 #if 0
258 inline ULONG APICRead(ULONG Offset)
259 {
260 PULONG p;
261
262 p = (PULONG)((ULONG)APICBase + Offset);
263 return *p;
264 }
265 #else
266 inline ULONG APICRead(ULONG Offset)
267 {
268 PULONG p;
269 ULONG CPU = (_APICRead(APIC_ID) & APIC_ID_MASK) >> 24;
270
271 lastregr[CPU] = Offset;
272 lastvalr[CPU] = 0;
273
274 p = (PULONG)((ULONG)APICBase + Offset);
275
276 lastvalr[CPU] = *p;
277 return lastvalr[CPU];
278 }
279 #endif
280
281 inline VOID APICSendEOI(VOID)
282 {
283 // Send the EOI
284 APICWrite(APIC_EOI, 0);
285 }
286
287 static VOID APICDumpBit(ULONG base)
288 {
289 ULONG v, i, j;
290
291 DbgPrint("0123456789abcdef0123456789abcdef\n");
292 for (i = 0; i < 8; i++)
293 {
294 v = APICRead(base + i*0x10);
295 for (j = 0; j < 32; j++)
296 {
297 if (v & (1<<j))
298 DbgPrint("1");
299 else
300 DbgPrint("0");
301 }
302 DbgPrint("\n");
303 }
304 }
305
306
307 VOID APICDump(VOID)
308 /*
309 * Dump the contents of the local APIC registers
310 */
311 {
312 ULONG v, ver, maxlvt;
313 ULONG r1, r2, w1, w2;
314 ULONG CPU = ThisCPU();;
315
316
317
318 r1 = lastregr[CPU];
319 r2 = lastvalr[CPU];
320 w1 = lastregw[CPU];
321 w2 = lastvalw[CPU];
322
323 DbgPrint("\nPrinting local APIC contents on CPU(%d):\n", ThisCPU());
324 v = APICRead(APIC_ID);
325 DbgPrint("... ID : %08x (%01x) ", v, GET_APIC_ID(v));
326 v = APICRead(APIC_VER);
327 DbgPrint("... VERSION: %08x\n", v);
328 ver = GET_APIC_VERSION(v);
329 maxlvt = APICGetMaxLVT();
330
331 v = APICRead(APIC_TPR);
332 DbgPrint("... TPR : %08x (%02x)", v, v & ~0);
333
334 if (APIC_INTEGRATED(ver))
335 {
336 /* !82489DX */
337 v = APICRead(APIC_APR);
338 DbgPrint("... APR : %08x (%02x)\n", v, v & ~0);
339 v = APICRead(APIC_PPR);
340 DbgPrint("... PPR : %08x\n", v);
341 }
342
343 v = APICRead(APIC_EOI);
344 DbgPrint("... EOI : %08x ! ", v);
345 v = APICRead(APIC_LDR);
346 DbgPrint("... LDR : %08x\n", v);
347 v = APICRead(APIC_DFR);
348 DbgPrint("... DFR : %08x ! ", v);
349 v = APICRead(APIC_SIVR);
350 DbgPrint("... SIVR : %08x\n", v);
351
352 if (0)
353 {
354 DbgPrint("... ISR field:\n");
355 APICDumpBit(APIC_ISR);
356 DbgPrint("... TMR field:\n");
357 APICDumpBit(APIC_TMR);
358 DbgPrint("... IRR field:\n");
359 APICDumpBit(APIC_IRR);
360 }
361
362 if (APIC_INTEGRATED(ver))
363 {
364 /* !82489DX */
365 if (maxlvt > 3)
366 {
367 /* Due to the Pentium erratum 3AP. */
368 APICWrite(APIC_ESR, 0);
369 }
370 v = APICRead(APIC_ESR);
371 DbgPrint("... ESR : %08x\n", v);
372 }
373
374 v = APICRead(APIC_ICR0);
375 DbgPrint("... ICR0 : %08x ! ", v);
376 v = APICRead(APIC_ICR1);
377 DbgPrint("... ICR1 : %08x ! ", v);
378
379 v = APICRead(APIC_LVTT);
380 DbgPrint("... LVTT : %08x\n", v);
381
382 if (maxlvt > 3)
383 {
384 /* PC is LVT#4. */
385 v = APICRead(APIC_LVTPC);
386 DbgPrint("... LVTPC : %08x ! ", v);
387 }
388 v = APICRead(APIC_LINT0);
389 DbgPrint("... LINT0 : %08x ! ", v);
390 v = APICRead(APIC_LINT1);
391 DbgPrint("... LINT1 : %08x\n", v);
392
393 if (maxlvt > 2)
394 {
395 v = APICRead(APIC_LVT3);
396 DbgPrint("... LVT3 : %08x\n", v);
397 }
398
399 v = APICRead(APIC_ICRT);
400 DbgPrint("... ICRT : %08x ! ", v);
401 v = APICRead(APIC_CCRT);
402 DbgPrint("... CCCT : %08x ! ", v);
403 v = APICRead(APIC_TDCR);
404 DbgPrint("... TDCR : %08x\n", v);
405 DbgPrint("\n");
406 DbgPrint("Last register read (offset): 0x%08X\n", r1);
407 DbgPrint("Last register read (value): 0x%08X\n", r2);
408 DbgPrint("Last register written (offset): 0x%08X\n", w1);
409 DbgPrint("Last register written (value): 0x%08X\n", w2);
410 DbgPrint("\n");
411 }
412
413 BOOLEAN VerifyLocalAPIC(VOID)
414 {
415 UINT reg0, reg1;
416 /* The version register is read-only in a real APIC */
417 reg0 = APICRead(APIC_VER);
418 DPRINT1("Getting VERSION: %x\n", reg0);
419 APICWrite(APIC_VER, reg0 ^ APIC_VER_MASK);
420 reg1 = APICRead(APIC_VER);
421 DPRINT1("Getting VERSION: %x\n", reg1);
422
423 /*
424 * The two version reads above should print the same
425 * numbers. If the second one is different, then we
426 * poke at a non-APIC.
427 */
428
429 if (reg1 != reg0)
430 {
431 return FALSE;
432 }
433
434 /*
435 * Check if the version looks reasonably.
436 */
437 reg1 = GET_APIC_VERSION(reg0);
438 if (reg1 == 0x00 || reg1 == 0xff)
439 {
440 return FALSE;
441 }
442 reg1 = APICGetMaxLVT();
443 if (reg1 < 0x02 || reg1 == 0xff)
444 {
445 return FALSE;
446 }
447
448 /*
449 * The ID register is read/write in a real APIC.
450 */
451 reg0 = APICRead(APIC_ID);
452 DPRINT1("Getting ID: %x\n", reg0);
453 APICWrite(APIC_ID, reg0 ^ APIC_ID_MASK);
454 reg1 = APICRead(APIC_ID);
455 DPRINT1("Getting ID: %x\n", reg1);
456 APICWrite(APIC_ID, reg0);
457 if (reg1 != (reg0 ^ APIC_ID_MASK))
458 {
459 return FALSE;
460 }
461
462 ULONG l, h;
463 Ki386Rdmsr(0x1b /*MSR_IA32_APICBASE*/, l, h);
464
465 if (!(l & /*MSR_IA32_APICBASE_ENABLE*/(1<<11)))
466 {
467 DPRINT1("Local APIC disabled by BIOS -- reenabling.\n");
468 l &= ~/*MSR_IA32_APICBASE_BASE*/(1<<11);
469 l |= /*MSR_IA32_APICBASE_ENABLE | APIC_DEFAULT_PHYS_BASE*/(1<<11)|0xfee00000;
470 Ki386Wrmsr(0x1b/*MSR_IA32_APICBASE*/, l, h);
471 }
472
473
474
475 return TRUE;
476 }
477
478 #ifdef CONFIG_SMP
479 VOID APICSendIPI(ULONG Target, ULONG Mode)
480 {
481 ULONG tmp, i, flags;
482
483 /* save flags and disable interrupts */
484 Ki386SaveFlags(flags);
485 Ki386DisableInterrupts();
486
487 /* Wait up to 100ms for the APIC to become ready */
488 for (i = 0; i < 10000; i++)
489 {
490 tmp = APICRead(APIC_ICR0);
491 /* Check Delivery Status */
492 if ((tmp & APIC_ICR0_DS) == 0)
493 break;
494 KeStallExecutionProcessor(10);
495 }
496
497 if (i == 10000)
498 {
499 DPRINT1("CPU(%d) Previous IPI was not delivered after 100ms.\n", ThisCPU());
500 }
501
502 /* Setup the APIC to deliver the IPI */
503 DPRINT("%08x %x\n", SET_APIC_DEST_FIELD(Target), Target);
504 APICWrite(APIC_ICR1, SET_APIC_DEST_FIELD(Target));
505
506 if (Target == APIC_TARGET_SELF)
507 {
508 Mode |= APIC_ICR0_DESTS_SELF;
509 }
510 else if (Target == APIC_TARGET_ALL)
511 {
512 Mode |= APIC_ICR0_DESTS_ALL;
513 }
514 else if (Target == APIC_TARGET_ALL_BUT_SELF)
515 {
516 Mode |= APIC_ICR0_DESTS_ALL_BUT_SELF;
517 }
518 else
519 {
520 Mode |= APIC_ICR0_DESTS_FIELD;
521 }
522
523 /* Now, fire off the IPI */
524 APICWrite(APIC_ICR0, Mode);
525
526 /* Wait up to 100ms for the APIC to become ready */
527 for (i = 0; i < 10000; i++)
528 {
529 tmp = APICRead(APIC_ICR0);
530 /* Check Delivery Status */
531 if ((tmp & APIC_ICR0_DS) == 0)
532 break;
533 KeStallExecutionProcessor(10);
534 }
535
536 if (i == 10000)
537 {
538 DPRINT1("CPU(%d) Current IPI was not delivered after 100ms.\n", ThisCPU());
539 }
540 Ki386RestoreFlags(flags);
541 }
542 #endif
543
544 VOID APICSetup(VOID)
545 {
546 ULONG CPU, tmp;
547
548 CPU = ThisCPU();
549
550 // APICDump();
551
552 DPRINT1("CPU%d:\n", CPU);
553 DPRINT1(" Physical APIC id: %d\n", GET_APIC_ID(APICRead(APIC_ID)));
554 DPRINT1(" Logical APIC id: %d\n", GET_APIC_LOGICAL_ID(APICRead(APIC_LDR)));
555 DPRINT1("%08x %08x %08x\n", APICRead(APIC_ID), APICRead(APIC_LDR), APICRead(APIC_DFR));
556
557 /*
558 * Intel recommends to set DFR, LDR and TPR before enabling
559 * an APIC. See e.g. "AP-388 82489DX User's Manual" (Intel
560 * document number 292116). So here it goes...
561 */
562
563 /*
564 * Put the APIC into flat delivery mode.
565 * Must be "all ones" explicitly for 82489DX.
566 */
567 APICWrite(APIC_DFR, 0xFFFFFFFF);
568
569 /*
570 * Set up the logical destination ID.
571 */
572 tmp = APICRead(APIC_LDR);
573 tmp &= ~APIC_LDR_MASK;
574 /*
575 * FIXME:
576 * This works only up to 8 CPU's
577 */
578 tmp |= (1 << (KeGetCurrentProcessorNumber() + 24));
579 APICWrite(APIC_LDR, tmp);
580
581
582 DPRINT1("CPU%d:\n", CPU);
583 DPRINT1(" Physical APIC id: %d\n", GET_APIC_ID(APICRead(APIC_ID)));
584 DPRINT1(" Logical APIC id: %d\n", GET_APIC_LOGICAL_ID(APICRead(APIC_LDR)));
585 DPRINT1("%08x %08x %08x\n", APICRead(APIC_ID), APICRead(APIC_LDR), APICRead(APIC_DFR));
586 DPRINT1("%d\n", CPUMap[CPU].APICId);
587
588 /* Accept only higher interrupts */
589 APICWrite(APIC_TPR, 0xef);
590
591 /* Enable local APIC */
592 tmp = APICRead(APIC_SIVR);
593 tmp &= ~0xff;
594 tmp |= APIC_SIVR_ENABLE;
595
596 #if 0
597 tmp &= ~APIC_SIVR_FOCUS;
598 #else
599 tmp |= APIC_SIVR_FOCUS;
600 #endif
601
602 /* Set spurious interrupt vector */
603 tmp |= SPURIOUS_VECTOR;
604 APICWrite(APIC_SIVR, tmp);
605
606 /*
607 * Set up LVT0, LVT1:
608 *
609 * set up through-local-APIC on the BP's LINT0. This is not
610 * strictly necessery in pure symmetric-IO mode, but sometimes
611 * we delegate interrupts to the 8259A.
612 */
613 tmp = APICRead(APIC_LINT0) & APIC_LVT_MASKED;
614 if (CPU == BootCPU && (APICMode == amPIC || !tmp))
615 {
616 tmp = APIC_DM_EXTINT;
617 DPRINT1("enabled ExtINT on CPU#%d\n", CPU);
618 }
619 else
620 {
621 tmp = APIC_DM_EXTINT | APIC_LVT_MASKED;
622 DPRINT1("masked ExtINT on CPU#%d\n", CPU);
623 }
624 APICWrite(APIC_LINT0, tmp);
625
626 /*
627 * Only the BSP should see the LINT1 NMI signal, obviously.
628 */
629 if (CPU == BootCPU)
630 {
631 tmp = APIC_DM_NMI;
632 }
633 else
634 {
635 tmp = APIC_DM_NMI | APIC_LVT_MASKED;
636 }
637 if (!APIC_INTEGRATED(CPUMap[CPU].APICVersion))
638 {
639 /* 82489DX */
640 tmp |= APIC_LVT_LEVEL_TRIGGER;
641 }
642 APICWrite(APIC_LINT1, tmp);
643
644 if (APIC_INTEGRATED(CPUMap[CPU].APICVersion))
645 {
646 /* !82489DX */
647 if (APICGetMaxLVT() > 3)
648 {
649 /* Due to the Pentium erratum 3AP */
650 APICWrite(APIC_ESR, 0);
651 }
652
653 tmp = APICRead(APIC_ESR);
654 DPRINT("ESR value before enabling vector: 0x%X\n", tmp);
655
656 /* Enable sending errors */
657 tmp = ERROR_VECTOR;
658 APICWrite(APIC_LVT3, tmp);
659
660 /*
661 * Spec says clear errors after enabling vector
662 */
663 if (APICGetMaxLVT() > 3)
664 {
665 APICWrite(APIC_ESR, 0);
666 }
667 tmp = APICRead(APIC_ESR);
668 DPRINT("ESR value after enabling vector: 0x%X\n", tmp);
669 }
670 }
671 #ifdef CONFIG_SMP
672 VOID APICSyncArbIDs(VOID)
673 {
674 ULONG i, tmp;
675
676 /* Wait up to 100ms for the APIC to become ready */
677 for (i = 0; i < 10000; i++)
678 {
679 tmp = APICRead(APIC_ICR0);
680 /* Check Delivery Status */
681 if ((tmp & APIC_ICR0_DS) == 0)
682 break;
683 KeStallExecutionProcessor(10);
684 }
685
686 if (i == 10000)
687 {
688 DPRINT("CPU(%d) APIC busy for 100ms.\n", ThisCPU());
689 }
690
691 DPRINT("Synchronizing Arb IDs.\n");
692 APICWrite(APIC_ICR0, APIC_ICR0_DESTS_ALL | APIC_ICR0_LEVEL | APIC_DM_INIT);
693 }
694 #endif
695
696 VOID MpsErrorHandler(VOID)
697 {
698 ULONG tmp1, tmp2;
699
700 APICDump();
701
702 tmp1 = APICRead(APIC_ESR);
703 APICWrite(APIC_ESR, 0);
704 tmp2 = APICRead(APIC_ESR);
705 DPRINT1("APIC error on CPU(%d) ESR(%x)(%x)\n", ThisCPU(), tmp1, tmp2);
706
707 /*
708 * Acknowledge the interrupt
709 */
710 APICSendEOI();
711
712 /* Here is what the APIC error bits mean:
713 * 0: Send CS error
714 * 1: Receive CS error
715 * 2: Send accept error
716 * 3: Receive accept error
717 * 4: Reserved
718 * 5: Send illegal vector
719 * 6: Received illegal vector
720 * 7: Illegal register address
721 */
722 DPRINT1("APIC error on CPU(%d) ESR(%x)(%x)\n", ThisCPU(), tmp1, tmp2);
723 for (;;);
724 }
725
726 VOID MpsSpuriousHandler(VOID)
727 {
728 ULONG tmp;
729
730 DPRINT("Spurious interrupt on CPU(%d)\n", ThisCPU());
731
732 tmp = APICRead(APIC_ISR + ((SPURIOUS_VECTOR & ~0x1f) >> 1));
733 if (tmp & (1 << (SPURIOUS_VECTOR & 0x1f)))
734 {
735 APICSendEOI();
736 return;
737 }
738 #if 0
739 /* No need to send EOI here */
740 APICDump();
741 #endif
742 }
743
744 #ifdef CONFIG_SMP
745 VOID MpsIpiHandler(VOID)
746 {
747 KIRQL oldIrql;
748
749 HalBeginSystemInterrupt(IPI_VECTOR,
750 IPI_LEVEL,
751 &oldIrql);
752 Ki386EnableInterrupts();
753 #if 0
754 DbgPrint("(%s:%d) MpsIpiHandler on CPU%d, current irql is %d\n",
755 __FILE__,__LINE__, KeGetCurrentProcessorNumber(), KeGetCurrentIrql());
756 #endif
757
758 KiIpiServiceRoutine(NULL, NULL);
759
760 #if 0
761 DbgPrint("(%s:%d) MpsIpiHandler on CPU%d done\n", __FILE__,__LINE__, KeGetCurrentProcessorNumber());
762 #endif
763
764 Ki386DisableInterrupts();
765 HalEndSystemInterrupt(oldIrql, 0);
766 }
767 #endif
768
769 VOID
770 MpsIRQTrapFrameToTrapFrame(PKIRQ_TRAPFRAME IrqTrapFrame,
771 PKTRAP_FRAME TrapFrame)
772 {
773 TrapFrame->Gs = (USHORT)IrqTrapFrame->Gs;
774 TrapFrame->Fs = (USHORT)IrqTrapFrame->Fs;
775 TrapFrame->Es = (USHORT)IrqTrapFrame->Es;
776 TrapFrame->Ds = (USHORT)IrqTrapFrame->Ds;
777 TrapFrame->Eax = IrqTrapFrame->Eax;
778 TrapFrame->Ecx = IrqTrapFrame->Ecx;
779 TrapFrame->Edx = IrqTrapFrame->Edx;
780 TrapFrame->Ebx = IrqTrapFrame->Ebx;
781 TrapFrame->Esp = IrqTrapFrame->Esp;
782 TrapFrame->Ebp = IrqTrapFrame->Ebp;
783 TrapFrame->Esi = IrqTrapFrame->Esi;
784 TrapFrame->Edi = IrqTrapFrame->Edi;
785 TrapFrame->Eip = IrqTrapFrame->Eip;
786 TrapFrame->Cs = IrqTrapFrame->Cs;
787 TrapFrame->Eflags = IrqTrapFrame->Eflags;
788 }
789
790 VOID
791 MpsTimerHandler(ULONG Vector, PKIRQ_TRAPFRAME Trapframe)
792 {
793 KIRQL oldIrql;
794 KTRAP_FRAME KernelTrapFrame;
795 #if 0
796 ULONG CPU;
797 static ULONG Count[MAX_CPU] = {0,};
798 #endif
799 HalBeginSystemInterrupt(LOCAL_TIMER_VECTOR,
800 PROFILE_LEVEL,
801 &oldIrql);
802 Ki386EnableInterrupts();
803
804 #if 0
805 CPU = ThisCPU();
806 if ((Count[CPU] % 100) == 0)
807 {
808 DbgPrint("(%s:%d) MpsTimerHandler on CPU%d, irql = %d, epi = %x, KPCR = %x\n", __FILE__, __LINE__, CPU, oldIrql,Trapframe->Eip, KeGetCurrentKPCR());
809 }
810 Count[CPU]++;
811 #endif
812
813 MpsIRQTrapFrameToTrapFrame(Trapframe, &KernelTrapFrame);
814 if (KeGetCurrentProcessorNumber() == 0)
815 {
816 KeUpdateSystemTime(&KernelTrapFrame, oldIrql);
817 }
818 else
819 {
820 KeUpdateRunTime(&KernelTrapFrame, oldIrql);
821 }
822
823 Ki386DisableInterrupts();
824 HalEndSystemInterrupt (oldIrql, 0);
825 }
826
827 VOID APICSetupLVTT(ULONG ClockTicks)
828 {
829 ULONG tmp;
830
831 tmp = GET_APIC_VERSION(APICRead(APIC_VER));
832 if (!APIC_INTEGRATED(tmp))
833 {
834 tmp = SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV) | APIC_LVT_PERIODIC | LOCAL_TIMER_VECTOR;;
835 }
836 else
837 {
838 /* Periodic timer */
839 tmp = APIC_LVT_PERIODIC | LOCAL_TIMER_VECTOR;;
840 }
841 APICWrite(APIC_LVTT, tmp);
842
843 tmp = APICRead(APIC_TDCR);
844 tmp &= ~(APIC_TDCR_1 | APIC_TIMER_BASE_DIV);
845 tmp |= APIC_TDCR_16;
846 APICWrite(APIC_TDCR, tmp);
847 APICWrite(APIC_ICRT, ClockTicks / APIC_DIVISOR);
848 }
849
850 VOID
851 APICCalibrateTimer(ULONG CPU)
852 {
853 ULARGE_INTEGER t1, t2;
854 LONG tt1, tt2;
855 BOOLEAN TSCPresent;
856
857 DPRINT("Calibrating APIC timer for CPU %d\n", CPU);
858
859 APICSetupLVTT(1000000000);
860
861 TSCPresent = ((PKIPCR)KeGetCurrentKPCR())->PrcbData.FeatureBits & X86_FEATURE_TSC ? TRUE : FALSE;
862
863 /*
864 * The timer chip counts down to zero. Let's wait
865 * for a wraparound to start exact measurement:
866 * (the current tick might have been already half done)
867 */
868 WaitFor8254Wraparound();
869
870 /*
871 * We wrapped around just now. Let's start
872 */
873 if (TSCPresent)
874 {
875 Ki386RdTSC(t1);
876 }
877 tt1 = APICRead(APIC_CCRT);
878
879 WaitFor8254Wraparound();
880
881
882 tt2 = APICRead(APIC_CCRT);
883 if (TSCPresent)
884 {
885 Ki386RdTSC(t2);
886 CPUMap[CPU].CoreSpeed = (HZ * (t2.QuadPart - t1.QuadPart));
887 DPRINT("CPU clock speed is %ld.%04ld MHz.\n",
888 CPUMap[CPU].CoreSpeed/1000000,
889 CPUMap[CPU].CoreSpeed%1000000);
890 ((PKIPCR)KeGetCurrentKPCR())->PrcbData.MHz = CPUMap[CPU].CoreSpeed/1000000;
891 }
892
893 CPUMap[CPU].BusSpeed = (HZ * (long)(tt1 - tt2) * APIC_DIVISOR);
894
895 /* Setup timer for normal operation */
896 // APICSetupLVTT((CPUMap[CPU].BusSpeed / 1000000) * 100); // 100ns
897 APICSetupLVTT((CPUMap[CPU].BusSpeed / 1000000) * 10000); // 10ms
898 // APICSetupLVTT((CPUMap[CPU].BusSpeed / 1000000) * 100000); // 100ms
899
900 DPRINT("Host bus clock speed is %ld.%04ld MHz.\n",
901 CPUMap[CPU].BusSpeed/1000000,
902 CPUMap[CPU].BusSpeed%1000000);
903 }
904
905 VOID
906 SetInterruptGate(ULONG index, ULONG address)
907 {
908 KIDTENTRY *idt;
909 KIDT_ACCESS Access;
910
911 /* Set the IDT Access Bits */
912 Access.Reserved = 0;
913 Access.Present = 1;
914 Access.Dpl = 0; /* Kernel-Mode */
915 Access.SystemSegmentFlag = 0;
916 Access.SegmentType = I386_INTERRUPT_GATE;
917
918 idt = (KIDTENTRY*)((ULONG)KeGetCurrentKPCR()->IDT + index * sizeof(KIDTENTRY));
919 idt->Offset = address & 0xffff;
920 idt->Selector = KERNEL_CS;
921 idt->Access = Access.Value;
922 idt->ExtendedOffset = address >> 16;
923 }
924
925 VOID HaliInitBSP(VOID)
926 {
927 #ifdef CONFIG_SMP
928 PUSHORT ps;
929 #endif
930
931 static BOOLEAN BSPInitialized = FALSE;
932
933 /* Only initialize the BSP once */
934 if (BSPInitialized)
935 {
936 KEBUGCHECK(0);
937 return;
938 }
939
940 BSPInitialized = TRUE;
941
942 /* Setup interrupt handlers */
943 SetInterruptGate(LOCAL_TIMER_VECTOR, (ULONG)MpsTimerInterrupt);
944 SetInterruptGate(ERROR_VECTOR, (ULONG)MpsErrorInterrupt);
945 SetInterruptGate(SPURIOUS_VECTOR, (ULONG)MpsSpuriousInterrupt);
946 #ifdef CONFIG_SMP
947 SetInterruptGate(IPI_VECTOR, (ULONG)MpsIpiInterrupt);
948 #endif
949 DPRINT("APIC is mapped at 0x%X\n", APICBase);
950
951 if (VerifyLocalAPIC())
952 {
953 DPRINT("APIC found\n");
954 }
955 else
956 {
957 DPRINT("No APIC found\n");
958 KEBUGCHECK(0);
959 }
960
961 if (APICMode == amPIC)
962 {
963 EnableApicMode();
964 }
965
966 APICSetup();
967
968 #ifdef CONFIG_SMP
969 /* BIOS data segment */
970 BIOSBase = (PULONG)BIOS_AREA;
971
972 /* Area for communicating with the APs */
973 CommonBase = (PULONG)COMMON_AREA;
974
975 /* Copy bootstrap code to common area */
976 memcpy((PVOID)((ULONG)CommonBase + PAGE_SIZE),
977 &APstart,
978 (ULONG)&APend - (ULONG)&APstart + 1);
979
980 /* Set shutdown code */
981 CMOS_WRITE(0xF, 0xA);
982
983 /* Set warm reset vector */
984 ps = (PUSHORT)((ULONG)BIOSBase + 0x467);
985 *ps = (COMMON_AREA + PAGE_SIZE) & 0xF;
986
987 ps = (PUSHORT)((ULONG)BIOSBase + 0x469);
988 *ps = (COMMON_AREA + PAGE_SIZE) >> 4;
989 #endif
990
991 /* Calibrate APIC timer */
992 APICCalibrateTimer(BootCPU);
993 }
994
995 #ifdef CONFIG_SMP
996 VOID
997 HaliStartApplicationProcessor(ULONG Cpu, ULONG Stack)
998 {
999 ULONG tmp, maxlvt;
1000 PCOMMON_AREA_INFO Common;
1001 ULONG StartupCount;
1002 ULONG i, j;
1003 ULONG DeliveryStatus = 0;
1004 ULONG AcceptStatus = 0;
1005
1006 if (Cpu >= MAX_CPU ||
1007 Cpu >= CPUCount ||
1008 OnlineCPUs & (1 << Cpu))
1009 {
1010 KEBUGCHECK(0);
1011 }
1012 DPRINT1("Attempting to boot CPU %d\n", Cpu);
1013
1014 /* Send INIT IPI */
1015
1016 APICSendIPI(Cpu, APIC_DM_INIT|APIC_ICR0_LEVEL_ASSERT);
1017
1018 KeStallExecutionProcessor(200);
1019
1020 /* Deassert INIT */
1021
1022 APICSendIPI(Cpu, APIC_DM_INIT|APIC_ICR0_LEVEL_DEASSERT);
1023
1024 if (APIC_INTEGRATED(CPUMap[Cpu].APICVersion))
1025 {
1026 /* Clear APIC errors */
1027 APICWrite(APIC_ESR, 0);
1028 tmp = (APICRead(APIC_ESR) & APIC_ESR_MASK);
1029 }
1030
1031 Common = (PCOMMON_AREA_INFO)CommonBase;
1032
1033 /* Write the location of the AP stack */
1034 Common->Stack = (ULONG)Stack;
1035 /* Write the page directory page */
1036 Ke386GetPageTableDirectory(Common->PageDirectory);
1037 /* Write the kernel entry point */
1038 Common->NtProcessStartup = (ULONG_PTR)RtlImageNtHeader(MmSystemRangeStart)->OptionalHeader.AddressOfEntryPoint + (ULONG_PTR)MmSystemRangeStart;
1039 /* Write the state of the mae mode */
1040 Common->PaeModeEnabled = Ke386GetCr4() & X86_CR4_PAE ? 1 : 0;
1041
1042 DPRINT1("%x %x %x %x\n", Common->Stack, Common->PageDirectory, Common->NtProcessStartup, Common->PaeModeEnabled);
1043
1044 DPRINT("Cpu %d got stack at 0x%X\n", Cpu, Common->Stack);
1045 #if 0
1046 for (j = 0; j < 16; j++)
1047 {
1048 Common->Debug[j] = 0;
1049 }
1050 #endif
1051
1052 maxlvt = APICGetMaxLVT();
1053
1054 /* Is this a local APIC or an 82489DX? */
1055 StartupCount = (APIC_INTEGRATED(CPUMap[Cpu].APICVersion)) ? 2 : 0;
1056
1057 for (i = 1; i <= StartupCount; i++)
1058 {
1059 /* It's a local APIC, so send STARTUP IPI */
1060 DPRINT("Sending startup signal %d\n", i);
1061 /* Clear errors */
1062 APICWrite(APIC_ESR, 0);
1063 APICRead(APIC_ESR);
1064
1065 APICSendIPI(Cpu, APIC_DM_STARTUP | ((COMMON_AREA + PAGE_SIZE) >> 12)|APIC_ICR0_LEVEL_DEASSERT);
1066
1067 /* Wait up to 10ms for IPI to be delivered */
1068 j = 0;
1069 do
1070 {
1071 KeStallExecutionProcessor(10);
1072
1073 /* Check Delivery Status */
1074 DeliveryStatus = APICRead(APIC_ICR0) & APIC_ICR0_DS;
1075
1076 j++;
1077 } while ((DeliveryStatus) && (j < 1000));
1078
1079 KeStallExecutionProcessor(200);
1080
1081 /*
1082 * Due to the Pentium erratum 3AP.
1083 */
1084 if (maxlvt > 3)
1085 {
1086 APICRead(APIC_SIVR);
1087 APICWrite(APIC_ESR, 0);
1088 }
1089
1090 AcceptStatus = APICRead(APIC_ESR) & APIC_ESR_MASK;
1091
1092 if (DeliveryStatus || AcceptStatus)
1093 {
1094 break;
1095 }
1096 }
1097
1098 if (DeliveryStatus)
1099 {
1100 DPRINT("STARTUP IPI for CPU %d was never delivered.\n", Cpu);
1101 }
1102
1103 if (AcceptStatus)
1104 {
1105 DPRINT("STARTUP IPI for CPU %d was never accepted.\n", Cpu);
1106 }
1107
1108 if (!(DeliveryStatus || AcceptStatus))
1109 {
1110
1111 /* Wait no more than 5 seconds for processor to boot */
1112 DPRINT("Waiting for 5 seconds for CPU %d to boot\n", Cpu);
1113
1114 /* Wait no more than 5 seconds */
1115 for (j = 0; j < 50000; j++)
1116 {
1117 if (CPUMap[Cpu].Flags & CPU_ENABLED)
1118 {
1119 break;
1120 }
1121 KeStallExecutionProcessor(100);
1122 }
1123 }
1124
1125 if (CPUMap[Cpu].Flags & CPU_ENABLED)
1126 {
1127 DbgPrint("CPU %d is now running\n", Cpu);
1128 }
1129 else
1130 {
1131 DbgPrint("Initialization of CPU %d failed\n", Cpu);
1132 }
1133
1134 #if 0
1135 DPRINT("Debug bytes are:\n");
1136
1137 for (j = 0; j < 4; j++)
1138 {
1139 DPRINT("0x%08X 0x%08X 0x%08X 0x%08X.\n",
1140 Common->Debug[j*4+0],
1141 Common->Debug[j*4+1],
1142 Common->Debug[j*4+2],
1143 Common->Debug[j*4+3]);
1144 }
1145
1146 #endif
1147 }
1148
1149 #endif
1150
1151 /* EOF */