Sync to trunk head (r42241)
[reactos.git] / reactos / hal / halx86 / mp / apic.c
index e1ae197..651500a 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ReactOS kernel
- *  Copyright (C) 2004 ReactOS Team
+ *  Copyright (C) 2004, 2005 ReactOS Team
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  * PROGRAMMER:  
  */
 
-#include <ddk/ntddk.h>
-#include <internal/i386/ps.h>
+/* INCLUDE ***********************************************************************/
 
 #include <hal.h>
-#include <halirq.h>
-#include <mps.h>
-#include <apic.h>
-
+#include <halfuncs.h> /* Not in PCH because only used for MP HAL */
+#include <rtlfuncs.h> /* Not in PCH because only used for MP HAL */
 #define NDEBUG
-#include <internal/debug.h>
+#include <debug.h>
 
-BOOLEAN VerifyLocalAPIC(VOID);
-VOID APICCalibrateTimer(ULONG CPU);
+/* GLOBALS ***********************************************************************/
 
-extern VOID MpsTimerInterrupt(VOID);
-extern VOID MpsErrorInterrupt(VOID);
-extern VOID MpsSpuriousInterrupt(VOID);
-extern VOID MpsIpiInterrupt(VOID);
+ULONG CPUCount;                                        /* Total number of CPUs */
+ULONG BootCPU;                                 /* Bootstrap processor */
+ULONG OnlineCPUs;                              /* Bitmask of online CPUs */
+CPU_INFO CPUMap[MAX_CPU];                      /* Map of all CPUs in the system */
+
+#ifdef CONFIG_SMP
+PULONG BIOSBase;                               /* Virtual address of BIOS data segment */
+PULONG CommonBase;                             /* Virtual address of common area */
+#endif
+
+PULONG APICBase = (PULONG)APIC_DEFAULT_BASE;   /* Virtual address of local APIC */
 
-extern ULONG APICMode;         /* APIC mode at startup */
-extern PULONG BIOSBase;         /* Virtual address of BIOS data segment */
-extern PULONG CommonBase;       /* Virtual address of common area */
-extern ULONG BootCPU;           /* Bootstrap processor */
-extern ULONG OnlineCPUs;        /* Bitmask of online CPUs */
+ULONG APICMode;                                        /* APIC mode at startup */
+
+/* For debugging */
+ULONG lastregr[MAX_CPU];
+ULONG lastvalr[MAX_CPU];
+ULONG lastregw[MAX_CPU];
+ULONG lastvalw[MAX_CPU];
+
+#ifdef CONFIG_SMP
+#include <pshpack1.h>
+typedef struct _COMMON_AREA_INFO
+{
+   ULONG Stack;                    /* Location of AP stack */
+   ULONG PageDirectory;            /* Page directory for an AP */
+   ULONG NtProcessStartup;  /* Kernel entry point for an AP */
+   ULONG PaeModeEnabled;    /* PAE mode is enabled */
+   ULONG Debug[16];        /* For debugging */
+} COMMON_AREA_INFO, *PCOMMON_AREA_INFO;
+#include <poppack.h>
+#endif
 
-extern CHAR *APstart, *APend;
-extern VOID (*APflush)(VOID);
+CHAR *APstart, *APend;
 
-#define CMOS_READ(address) ({ \
+#define BIOS_AREA      0x0
+#define COMMON_AREA    0x2000
+
+#define HZ             (100)
+#define APIC_DIVISOR   (16)
+
+#define CMOS_READ(address) { \
    WRITE_PORT_UCHAR((PUCHAR)0x70, address)); \
    READ_PORT_UCHAR((PUCHAR)0x71)); \
-})
+}
 
-#define CMOS_WRITE(address, value) ({ \
+#define CMOS_WRITE(address, value) { \
    WRITE_PORT_UCHAR((PUCHAR)0x70, address); \
    WRITE_PORT_UCHAR((PUCHAR)0x71, value); \
-})
-
-#define BIOS_AREA    0x0
-#define COMMON_AREA  0x2000
-
+}
 
-extern CPU_INFO CPUMap[MAX_CPU];               /* Map of all CPUs in the system */
+extern ULONG_PTR KernelBase;
 
-PULONG APICBase = (PULONG)APIC_DEFAULT_BASE;   /* Virtual address of local APIC */
+/* FUNCTIONS *********************************************************************/
 
-/* For debugging */
-ULONG lastregr[MAX_CPU];
-ULONG lastvalr[MAX_CPU];
-ULONG lastregw[MAX_CPU];
-ULONG lastvalw[MAX_CPU];
+extern ULONG Read8254Timer(VOID);
+extern VOID WaitFor8254Wraparound(VOID);
+extern VOID MpsTimerInterrupt(VOID);
+extern VOID MpsErrorInterrupt(VOID);
+extern VOID MpsSpuriousInterrupt(VOID);
+extern VOID MpsIpiInterrupt(VOID);
 
 ULONG APICGetMaxLVT(VOID)
 {
@@ -152,7 +172,7 @@ VOID APICClear(VOID)
 }
 
 /* Enable symetric I/O mode ie. connect the BSP's local APIC to INT and NMI lines */
-VOID EnableSMPMode(VOID)
+VOID EnableApicMode(VOID)
 {
    /*
     * Do not trust the local APIC being empty at bootup.
@@ -164,7 +184,7 @@ VOID EnableSMPMode(VOID)
 }
 
 /* Disable symetric I/O mode ie. go to PIC mode */
-inline VOID DisableSMPMode(VOID)
+__inline VOID DisableSMPMode(VOID)
 {
    /*
     * Put the board back into PIC mode (has an effect
@@ -203,129 +223,6 @@ VOID APICDisable(VOID)
   APICWrite(APIC_SIVR, tmp);
 }
 
-VOID HaliInitBSP(VOID)
-{
-   PUSHORT ps;
-   static BOOLEAN BSPInitialized = FALSE;
-
-   /* Only initialize the BSP once */
-   if (BSPInitialized)
-   {
-      KEBUGCHECK(0);
-      return;
-   }
-
-   BSPInitialized = TRUE;
-
-   DPRINT("APIC is mapped at 0x%X\n", APICBase);
-
-   if (VerifyLocalAPIC()) 
-   {
-      DPRINT("APIC found\n");
-   } 
-   else 
-   {
-      DPRINT("No APIC found\n");
-      KEBUGCHECK(0);
-   }
-
-   if (APICMode == amPIC) 
-   {
-      EnableSMPMode();
-   }
-
-   APICSetup();
-
-   /* BIOS data segment */
-   BIOSBase = (PULONG)BIOS_AREA;
-   
-   /* Area for communicating with the APs */
-   CommonBase = (PULONG)COMMON_AREA;
-   /* Copy bootstrap code to common area */
-   memcpy((PVOID)((ULONG)CommonBase + PAGE_SIZE),
-         &APstart,
-         (ULONG)&APend - (ULONG)&APstart + 1);
-
-   /* Set shutdown code */
-   CMOS_WRITE(0xF, 0xA);
-
-   /* Set warm reset vector */
-   ps = (PUSHORT)((ULONG)BIOSBase + 0x467);
-   *ps = (COMMON_AREA + PAGE_SIZE) & 0xF;
-   ps = (PUSHORT)((ULONG)BIOSBase + 0x469);
-   *ps = (COMMON_AREA + PAGE_SIZE) >> 4;
-
-   /* Calibrate APIC timer */
-   APICCalibrateTimer(BootCPU);
-}
-
-inline ULONG _APICRead(ULONG Offset)
-{
-   PULONG p;
-
-   p = (PULONG)((ULONG)APICBase + Offset);
-   return *p;
-}
-
-#if 0
-inline VOID APICWrite(ULONG Offset,
-                     ULONG Value)
-{
-   PULONG p;
-
-   p = (PULONG)((ULONG)APICBase + Offset);
-
-   *p = Value;
-}
-#else
-inline VOID APICWrite(ULONG Offset,
-                     ULONG Value)
-{
-   PULONG p;
-   ULONG CPU = (_APICRead(APIC_ID) & APIC_ID_MASK) >> 24;
-
-   lastregw[CPU] = Offset;
-   lastvalw[CPU] = Value;
-
-   p = (PULONG)((ULONG)APICBase + Offset);
-
-   *p = Value;
-}
-#endif
-
-
-#if 0
-inline ULONG APICRead(ULONG Offset)
-{
-   PULONG p;
-
-   p = (PULONG)((ULONG)APICBase + Offset);
-   return *p;
-}
-#else
-inline ULONG APICRead(ULONG Offset)
-{
-   PULONG p;
-   ULONG CPU = (_APICRead(APIC_ID) & APIC_ID_MASK) >> 24;
-
-   lastregr[CPU] = Offset;
-   lastvalr[CPU] = 0;
-
-   p = (PULONG)((ULONG)APICBase + Offset);
-
-   lastvalr[CPU] = *p;
-   return lastvalr[CPU];
-}
-#endif
-
-inline VOID APICSendEOI(VOID)
-{
-  // Send the EOI
-  APICWrite(APIC_EOI, 0);
-}
-
 static VOID APICDumpBit(ULONG base)
 {
        ULONG v, i, j;
@@ -353,8 +250,7 @@ VOID APICDump(VOID)
 {
   ULONG v, ver, maxlvt;
   ULONG r1, r2, w1, w2;
-  ULONG CPU = ThisCPU();;
-
+  ULONG CPU = ThisCPU();
 
 
   r1 = lastregr[CPU];
@@ -454,8 +350,8 @@ VOID APICDump(VOID)
 
 BOOLEAN VerifyLocalAPIC(VOID)
 {
-   UINT reg0, reg1;
-   CHECKPOINT1;
+   SIZE_T reg0, reg1;
+   ULONG l = 0, h = 0;
    /* The version register is read-only in a real APIC */
    reg0 = APICRead(APIC_VER);
    DPRINT1("Getting VERSION: %x\n", reg0);
@@ -502,16 +398,29 @@ BOOLEAN VerifyLocalAPIC(VOID)
       return FALSE;
    }
 
+   Ke386Rdmsr(0x1b /*MSR_IA32_APICBASE*/, l, h);
+
+   if (!(l & /*MSR_IA32_APICBASE_ENABLE*/(1<<11))) 
+   {
+      DPRINT1("Local APIC disabled by BIOS -- reenabling.\n");
+      l &= ~/*MSR_IA32_APICBASE_BASE*/(1<<11);
+      l |= /*MSR_IA32_APICBASE_ENABLE | APIC_DEFAULT_PHYS_BASE*/(1<<11)|0xfee00000;
+      Ke386Wrmsr(0x1b/*MSR_IA32_APICBASE*/, l, h);
+   }
+
+    
+
    return TRUE;
 }
 
+#ifdef CONFIG_SMP
 VOID APICSendIPI(ULONG Target, ULONG Mode)
 {
-   ULONG tmp, i, flags;
+   ULONG tmp, i, flags = 0;
 
    /* save flags and disable interrupts */
-   Ki386SaveFlags(flags);
-   Ki386DisableInterrupts();
+   Ke386SaveFlags(flags);
+   _disable();
 
    /* Wait up to 100ms for the APIC to become ready */
    for (i = 0; i < 10000; i++) 
@@ -566,8 +475,9 @@ VOID APICSendIPI(ULONG Target, ULONG Mode)
    {
       DPRINT1("CPU(%d) Current IPI was not delivered after 100ms.\n", ThisCPU());
    }
-   Ki386RestoreFlags(flags);
+   Ke386RestoreFlags(flags);
 }
+#endif
 
 VOID APICSetup(VOID)
 {
@@ -651,7 +561,6 @@ VOID APICSetup(VOID)
   }
   APICWrite(APIC_LINT0, tmp);
 
-
   /*
    * Only the BSP should see the LINT1 NMI signal, obviously.
    */
@@ -697,7 +606,7 @@ VOID APICSetup(VOID)
      DPRINT("ESR value after enabling vector: 0x%X\n", tmp);
   }
 }
-
+#ifdef CONFIG_SMP
 VOID APICSyncArbIDs(VOID)
 {
    ULONG i, tmp;
@@ -720,6 +629,7 @@ VOID APICSyncArbIDs(VOID)
    DPRINT("Synchronizing Arb IDs.\n");
    APICWrite(APIC_ICR0, APIC_ICR0_DESTS_ALL | APIC_ICR0_LEVEL | APIC_DM_INIT);
 }
+#endif
 
 VOID MpsErrorHandler(VOID)
 {
@@ -755,7 +665,7 @@ VOID MpsSpuriousHandler(VOID)
 {
   ULONG tmp;
 
-  DPRINT1("Spurious interrupt on CPU(%d)\n", ThisCPU());
+  DPRINT("Spurious interrupt on CPU(%d)\n", ThisCPU());
   
   tmp = APICRead(APIC_ISR + ((SPURIOUS_VECTOR & ~0x1f) >> 1));
   if (tmp & (1 << (SPURIOUS_VECTOR & 0x1f)))
@@ -769,14 +679,15 @@ VOID MpsSpuriousHandler(VOID)
 #endif
 }
 
+#ifdef CONFIG_SMP
 VOID MpsIpiHandler(VOID)
 {
    KIRQL oldIrql;
 
-   HalBeginSystemInterrupt(IPI_VECTOR, 
-                           VECTOR2IRQL(IPI_VECTOR)
+   HalBeginSystemInterrupt(IPI_LEVEL,
+                           IPI_VECTOR
                           &oldIrql);
-   Ki386EnableInterrupts();
+   _enable();
 #if 0
    DbgPrint("(%s:%d) MpsIpiHandler on CPU%d, current irql is %d\n", 
             __FILE__,__LINE__, KeGetCurrentProcessorNumber(), KeGetCurrentIrql());
@@ -788,29 +699,34 @@ VOID MpsIpiHandler(VOID)
    DbgPrint("(%s:%d) MpsIpiHandler on CPU%d done\n", __FILE__,__LINE__, KeGetCurrentProcessorNumber());
 #endif
 
-   Ki386DisableInterrupts();
+   _disable();
    HalEndSystemInterrupt(oldIrql, 0);
 }
+#endif
 
 VOID
 MpsIRQTrapFrameToTrapFrame(PKIRQ_TRAPFRAME IrqTrapFrame,
                           PKTRAP_FRAME TrapFrame)
 {
-   TrapFrame->Gs     = (USHORT)IrqTrapFrame->Gs;
-   TrapFrame->Fs     = (USHORT)IrqTrapFrame->Fs;
-   TrapFrame->Es     = (USHORT)IrqTrapFrame->Es;
-   TrapFrame->Ds     = (USHORT)IrqTrapFrame->Ds;
+#ifdef _M_AMD64
+    UNIMPLEMENTED;
+#else
+   TrapFrame->SegGs     = (USHORT)IrqTrapFrame->Gs;
+   TrapFrame->SegFs     = (USHORT)IrqTrapFrame->Fs;
+   TrapFrame->SegEs     = (USHORT)IrqTrapFrame->Es;
+   TrapFrame->SegDs     = (USHORT)IrqTrapFrame->Ds;
    TrapFrame->Eax    = IrqTrapFrame->Eax;
    TrapFrame->Ecx    = IrqTrapFrame->Ecx;
    TrapFrame->Edx    = IrqTrapFrame->Edx;
    TrapFrame->Ebx    = IrqTrapFrame->Ebx;
-   TrapFrame->Esp    = IrqTrapFrame->Esp;
+   TrapFrame->HardwareEsp    = IrqTrapFrame->Esp;
    TrapFrame->Ebp    = IrqTrapFrame->Ebp;
    TrapFrame->Esi    = IrqTrapFrame->Esi;
    TrapFrame->Edi    = IrqTrapFrame->Edi;
    TrapFrame->Eip    = IrqTrapFrame->Eip;
-   TrapFrame->Cs     = IrqTrapFrame->Cs;
-   TrapFrame->Eflags = IrqTrapFrame->Eflags;
+   TrapFrame->SegCs     = IrqTrapFrame->Cs;
+   TrapFrame->EFlags = IrqTrapFrame->Eflags;
+#endif
 }
 
 VOID
@@ -823,31 +739,374 @@ MpsTimerHandler(ULONG Vector, PKIRQ_TRAPFRAME Trapframe)
    static ULONG Count[MAX_CPU] = {0,};
 #endif
    HalBeginSystemInterrupt(LOCAL_TIMER_VECTOR, 
-                           VECTOR2IRQL(LOCAL_TIMER_VECTOR)
+                           PROFILE_LEVEL
                           &oldIrql);
-   Ki386EnableInterrupts();
+   _enable();
 
 #if 0
    CPU = ThisCPU();
    if ((Count[CPU] % 100) == 0)
    {
-     DbgPrint("(%s:%d) MpsTimerHandler on CPU%d, irql = %d, epi = %x, KPCR = %x\n", __FILE__, __LINE__, CPU, oldIrql,Trapframe->Eip, KeGetCurrentKPCR());
+     DbgPrint("(%s:%d) MpsTimerHandler on CPU%d, irql = %d, epi = %x, KPCR = %x\n", __FILE__, __LINE__, CPU, oldIrql,Trapframe->Eip, KeGetPcr());
    }
    Count[CPU]++;
 #endif
 
+   /* FIXME: SMP is totally broken */
    MpsIRQTrapFrameToTrapFrame(Trapframe, &KernelTrapFrame);
    if (KeGetCurrentProcessorNumber() == 0)
    {
-      KeUpdateSystemTime(&KernelTrapFrame, oldIrql);
+      //KeUpdateSystemTime(&KernelTrapFrame, oldIrql);
    }
    else
    {
-      KeUpdateRunTime(&KernelTrapFrame, oldIrql);
+      //KeUpdateRunTime(&KernelTrapFrame, oldIrql);
    }
 
-   Ki386DisableInterrupts();
+   _disable();
    HalEndSystemInterrupt (oldIrql, 0);
 }
 
+VOID APICSetupLVTT(ULONG ClockTicks)
+{
+   ULONG tmp;
+
+   tmp = GET_APIC_VERSION(APICRead(APIC_VER));
+   if (!APIC_INTEGRATED(tmp))
+   {
+      tmp = SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV) | APIC_LVT_PERIODIC | LOCAL_TIMER_VECTOR;
+   }
+   else
+   {
+      /* Periodic timer */
+      tmp = APIC_LVT_PERIODIC | LOCAL_TIMER_VECTOR;
+   }
+   APICWrite(APIC_LVTT, tmp);
+
+   tmp = APICRead(APIC_TDCR);
+   tmp &= ~(APIC_TDCR_1 | APIC_TIMER_BASE_DIV);
+   tmp |= APIC_TDCR_16;
+   APICWrite(APIC_TDCR, tmp);
+   APICWrite(APIC_ICRT, ClockTicks / APIC_DIVISOR);
+}
+
+VOID 
+APICCalibrateTimer(ULONG CPU)
+{
+   ULARGE_INTEGER t1, t2;
+   LONG tt1, tt2;
+   BOOLEAN TSCPresent;
+
+   DPRINT("Calibrating APIC timer for CPU %d\n", CPU);
+
+   APICSetupLVTT(1000000000);
+
+   TSCPresent = KeGetCurrentPrcb()->FeatureBits & KF_RDTSC ? TRUE : FALSE;
+
+   /*
+    * The timer chip counts down to zero. Let's wait
+    * for a wraparound to start exact measurement:
+    * (the current tick might have been already half done)
+    */
+   //WaitFor8254Wraparound();
+
+   /*
+    * We wrapped around just now. Let's start
+    */
+   if (TSCPresent)
+   {
+      t1.QuadPart = (LONGLONG)__rdtsc();
+   }
+   tt1 = APICRead(APIC_CCRT);
+
+   //WaitFor8254Wraparound();
+
+
+   tt2 = APICRead(APIC_CCRT);
+   if (TSCPresent)
+   {
+      t2.QuadPart = (LONGLONG)__rdtsc();
+      CPUMap[CPU].CoreSpeed = (HZ * (t2.QuadPart - t1.QuadPart));
+      DPRINT("CPU clock speed is %ld.%04ld MHz.\n",
+            CPUMap[CPU].CoreSpeed/1000000,
+            CPUMap[CPU].CoreSpeed%1000000);
+      KeGetCurrentPrcb()->MHz = CPUMap[CPU].CoreSpeed/1000000;
+   }
+
+   CPUMap[CPU].BusSpeed = (HZ * (long)(tt1 - tt2) * APIC_DIVISOR);
+
+   /* Setup timer for normal operation */
+// APICSetupLVTT((CPUMap[CPU].BusSpeed / 1000000) * 100);    // 100ns
+   APICSetupLVTT((CPUMap[CPU].BusSpeed / 1000000) * 10000);  // 10ms
+// APICSetupLVTT((CPUMap[CPU].BusSpeed / 1000000) * 100000); // 100ms
+
+   DPRINT("Host bus clock speed is %ld.%04ld MHz.\n",
+         CPUMap[CPU].BusSpeed/1000000,
+         CPUMap[CPU].BusSpeed%1000000);
+}
+
+VOID 
+SetInterruptGate(ULONG index, ULONG_PTR address)
+{
+#ifdef _M_AMD64
+  KIDTENTRY64 *idt;
+
+  idt = &KeGetPcr()->IdtBase[index];
+
+  idt->OffsetLow = address & 0xffff;
+  idt->Selector = KGDT_64_R0_CODE;
+  idt->IstIndex = 0;
+  idt->Reserved0 = 0;
+  idt->Type = 0x0e;
+  idt->Dpl = 0;
+  idt->Present = 1;
+  idt->OffsetMiddle = (address >> 16) & 0xffff;
+  idt->OffsetHigh = address >> 32;
+  idt->Reserved1 = 0;
+  idt->Alignment = 0;
+#else
+  KIDTENTRY *idt;
+  KIDT_ACCESS Access;
+
+  /* Set the IDT Access Bits */
+  Access.Reserved = 0;
+  Access.Present = 1;
+  Access.Dpl = 0; /* Kernel-Mode */
+  Access.SystemSegmentFlag = 0;
+  Access.SegmentType = I386_INTERRUPT_GATE;
+  
+  idt = (KIDTENTRY*)((ULONG)KeGetPcr()->IDT + index * sizeof(KIDTENTRY));
+  idt->Offset = address & 0xffff;
+  idt->Selector = KGDT_R0_CODE;
+  idt->Access = Access.Value;
+  idt->ExtendedOffset = address >> 16;
+#endif
+}
+
+VOID HaliInitBSP(VOID)
+{
+#ifdef CONFIG_SMP
+   PUSHORT ps;
+#endif
+
+   static BOOLEAN BSPInitialized = FALSE;
+
+   /* Only initialize the BSP once */
+   if (BSPInitialized)
+   {
+      ASSERT(FALSE);
+      return;
+   }
+
+   BSPInitialized = TRUE;
+
+   /* Setup interrupt handlers */
+   SetInterruptGate(LOCAL_TIMER_VECTOR, (ULONG_PTR)MpsTimerInterrupt);
+   SetInterruptGate(ERROR_VECTOR, (ULONG_PTR)MpsErrorInterrupt);
+   SetInterruptGate(SPURIOUS_VECTOR, (ULONG_PTR)MpsSpuriousInterrupt);
+#ifdef CONFIG_SMP
+   SetInterruptGate(IPI_VECTOR, (ULONG_PTR)MpsIpiInterrupt);
+#endif
+   DPRINT1("APIC is mapped at 0x%p\n", (PVOID)APICBase);
+
+   if (VerifyLocalAPIC()) 
+   {
+      DPRINT("APIC found\n");
+   } 
+   else 
+   {
+      DPRINT1("No APIC found\n");
+      ASSERT(FALSE);
+   }
+
+   if (APICMode == amPIC) 
+   {
+      EnableApicMode();
+   }
+
+   APICSetup();
+
+#ifdef CONFIG_SMP
+   /* BIOS data segment */
+   BIOSBase = (PULONG)BIOS_AREA;
+   
+   /* Area for communicating with the APs */
+   CommonBase = (PULONG)COMMON_AREA;
+   /* Copy bootstrap code to common area */
+   memcpy((PVOID)((ULONG_PTR)CommonBase + PAGE_SIZE),
+         &APstart,
+         (ULONG_PTR)&APend - (ULONG_PTR)&APstart + 1);
+
+   /* Set shutdown code */
+   CMOS_WRITE(0xF, 0xA);
+
+   /* Set warm reset vector */
+   ps = (PUSHORT)((ULONG_PTR)BIOSBase + 0x467);
+   *ps = (COMMON_AREA + PAGE_SIZE) & 0xF;
+   ps = (PUSHORT)((ULONG_PTR)BIOSBase + 0x469);
+   *ps = (COMMON_AREA + PAGE_SIZE) >> 4;
+#endif
+
+   /* Calibrate APIC timer */
+   APICCalibrateTimer(BootCPU);
+}
+
+#ifdef CONFIG_SMP
+VOID
+HaliStartApplicationProcessor(ULONG Cpu, ULONG Stack)
+{
+   ULONG tmp, maxlvt;
+   PCOMMON_AREA_INFO Common;
+   ULONG StartupCount;
+   ULONG i, j;
+   ULONG DeliveryStatus = 0;
+   ULONG AcceptStatus = 0;
+
+   if (Cpu >= MAX_CPU ||
+       Cpu >= CPUCount ||
+       OnlineCPUs & (1 << Cpu))
+   {
+     ASSERT(FALSE);
+   }
+   DPRINT1("Attempting to boot CPU %d\n", Cpu);
+
+   /* Send INIT IPI */
+
+   APICSendIPI(Cpu, APIC_DM_INIT|APIC_ICR0_LEVEL_ASSERT);
+   KeStallExecutionProcessor(200);
+
+   /* Deassert INIT */
+
+   APICSendIPI(Cpu, APIC_DM_INIT|APIC_ICR0_LEVEL_DEASSERT);
+
+   if (APIC_INTEGRATED(CPUMap[Cpu].APICVersion)) 
+   {
+      /* Clear APIC errors */
+      APICWrite(APIC_ESR, 0);
+      tmp = (APICRead(APIC_ESR) & APIC_ESR_MASK);
+   }
+
+   Common = (PCOMMON_AREA_INFO)CommonBase;
+
+   /* Write the location of the AP stack */
+   Common->Stack = (ULONG)Stack;
+   /* Write the page directory page */
+   Common->PageDirectory = __readcr3();
+   /* Write the kernel entry point */
+   Common->NtProcessStartup = (ULONG_PTR)RtlImageNtHeader((PVOID)KernelBase)->OptionalHeader.AddressOfEntryPoint + KernelBase;
+   /* Write the state of the mae mode */
+   Common->PaeModeEnabled = __readcr4() & CR4_PAE ? 1 : 0;
+
+   DPRINT1("%x %x %x %x\n", Common->Stack, Common->PageDirectory, Common->NtProcessStartup, Common->PaeModeEnabled);
+
+   DPRINT("Cpu %d got stack at 0x%X\n", Cpu, Common->Stack);
+#if 0
+   for (j = 0; j < 16; j++) 
+   {
+      Common->Debug[j] = 0;
+   }
+#endif
+
+   maxlvt = APICGetMaxLVT();
+
+   /* Is this a local APIC or an 82489DX? */
+   StartupCount = (APIC_INTEGRATED(CPUMap[Cpu].APICVersion)) ? 2 : 0;
+
+   for (i = 1; i <= StartupCount; i++)
+   {
+      /* It's a local APIC, so send STARTUP IPI */
+      DPRINT("Sending startup signal %d\n", i);
+      /* Clear errors */
+      APICWrite(APIC_ESR, 0);
+      APICRead(APIC_ESR);
+
+      APICSendIPI(Cpu, APIC_DM_STARTUP | ((COMMON_AREA + PAGE_SIZE) >> 12)|APIC_ICR0_LEVEL_DEASSERT);
+
+      /* Wait up to 10ms for IPI to be delivered */
+      j = 0;
+      do 
+      {
+         KeStallExecutionProcessor(10);
+
+         /* Check Delivery Status */
+         DeliveryStatus = APICRead(APIC_ICR0) & APIC_ICR0_DS;
+
+         j++;
+      } while ((DeliveryStatus) && (j < 1000));
+
+      KeStallExecutionProcessor(200);
+
+      /*
+       * Due to the Pentium erratum 3AP.
+       */
+      if (maxlvt > 3) 
+      {
+        APICRead(APIC_SIVR);
+        APICWrite(APIC_ESR, 0);
+      }
+
+      AcceptStatus = APICRead(APIC_ESR) & APIC_ESR_MASK;
+
+      if (DeliveryStatus || AcceptStatus) 
+      {
+         break;
+      }
+   }
+
+   if (DeliveryStatus) 
+   {
+      DPRINT("STARTUP IPI for CPU %d was never delivered.\n", Cpu);
+   }
+
+   if (AcceptStatus) 
+   {
+      DPRINT("STARTUP IPI for CPU %d was never accepted.\n", Cpu);
+   }
+
+   if (!(DeliveryStatus || AcceptStatus)) 
+   {
+
+      /* Wait no more than 5 seconds for processor to boot */
+      DPRINT("Waiting for 5 seconds for CPU %d to boot\n", Cpu);
+
+      /* Wait no more than 5 seconds */
+      for (j = 0; j < 50000; j++) 
+      {
+         if (CPUMap[Cpu].Flags & CPU_ENABLED)
+        {
+            break;
+        }
+         KeStallExecutionProcessor(100);
+      }
+   }
+
+   if (CPUMap[Cpu].Flags & CPU_ENABLED) 
+   {
+      DbgPrint("CPU %d is now running\n", Cpu);
+   } 
+   else 
+   {
+      DbgPrint("Initialization of CPU %d failed\n", Cpu);
+   }
+
+#if 0
+   DPRINT("Debug bytes are:\n");
+
+   for (j = 0; j < 4; j++) 
+   {
+      DPRINT("0x%08X 0x%08X 0x%08X 0x%08X.\n",
+             Common->Debug[j*4+0],
+             Common->Debug[j*4+1],
+             Common->Debug[j*4+2],
+             Common->Debug[j*4+3]);
+   }
+
+#endif
+}
+
+#endif
+
 /* EOF */