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