xref: /reactos/hal/halx86/mp/apic.c (revision 34593d93)
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