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