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