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