- Get MP hal and Generic HAL to build too.
[reactos.git] / reactos / hal / halx86 / mp / apic.c
index f87fe83..f90fc00 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>
-
 #define NDEBUG
-#include <internal/debug.h>
+#include <debug.h>
+#include <internal/ntoskrnl.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 */
+
+ULONG APICMode;                                        /* APIC mode at startup */
+
+/* For debugging */
+ULONG lastregr[MAX_CPU];
+ULONG lastvalr[MAX_CPU];
+ULONG lastregw[MAX_CPU];
+ULONG lastvalw[MAX_CPU];
 
-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 */
+#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
+
+CHAR *APstart, *APend;
+
+#define BIOS_AREA      0x0
+#define COMMON_AREA    0x2000
 
-extern CHAR *APstart, *APend;
-extern VOID (*APflush)(VOID);
+#define HZ             (100)
+#define APIC_DIVISOR   (16)
 
-#define CMOS_READ(address) ({ \
+#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 +171,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 +183,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,65 +222,8 @@ 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)
+__inline ULONG _APICRead(ULONG Offset)
 {
    PULONG p;
 
@@ -270,7 +232,7 @@ inline ULONG _APICRead(ULONG Offset)
 }
 
 #if 0
-inline VOID APICWrite(ULONG Offset,
+__inline VOID APICWrite(ULONG Offset,
                      ULONG Value)
 {
    PULONG p;
@@ -280,7 +242,7 @@ inline VOID APICWrite(ULONG Offset,
    *p = Value;
 }
 #else
-inline VOID APICWrite(ULONG Offset,
+__inline VOID APICWrite(ULONG Offset,
                      ULONG Value)
 {
    PULONG p;
@@ -297,7 +259,7 @@ inline VOID APICWrite(ULONG Offset,
 
 
 #if 0
-inline ULONG APICRead(ULONG Offset)
+__inline ULONG APICRead(ULONG Offset)
 {
    PULONG p;
 
@@ -305,7 +267,7 @@ inline ULONG APICRead(ULONG Offset)
    return *p;
 }
 #else
-inline ULONG APICRead(ULONG Offset)
+__inline ULONG APICRead(ULONG Offset)
 {
    PULONG p;
    ULONG CPU = (_APICRead(APIC_ID) & APIC_ID_MASK) >> 24;
@@ -320,7 +282,7 @@ inline ULONG APICRead(ULONG Offset)
 }
 #endif
 
-inline VOID APICSendEOI(VOID)
+__inline VOID APICSendEOI(VOID)
 {
   // Send the EOI
   APICWrite(APIC_EOI, 0);
@@ -454,8 +416,8 @@ VOID APICDump(VOID)
 
 BOOLEAN VerifyLocalAPIC(VOID)
 {
-   UINT reg0, reg1;
-   CHECKPOINT1;
+   SIZE_T reg0, reg1;
+   ULONG l, h;
    /* The version register is read-only in a real APIC */
    reg0 = APICRead(APIC_VER);
    DPRINT1("Getting VERSION: %x\n", reg0);
@@ -502,9 +464,22 @@ BOOLEAN VerifyLocalAPIC(VOID)
       return FALSE;
    }
 
+   Ki386Rdmsr(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;
+      Ki386Wrmsr(0x1b/*MSR_IA32_APICBASE*/, l, h);
+   }
+
+    
+
    return TRUE;
 }
 
+#ifdef CONFIG_SMP
 VOID APICSendIPI(ULONG Target, ULONG Mode)
 {
    ULONG tmp, i, flags;
@@ -568,6 +543,7 @@ VOID APICSendIPI(ULONG Target, ULONG Mode)
    }
    Ki386RestoreFlags(flags);
 }
+#endif
 
 VOID APICSetup(VOID)
 {
@@ -651,7 +627,6 @@ VOID APICSetup(VOID)
   }
   APICWrite(APIC_LINT0, tmp);
 
-
   /*
    * Only the BSP should see the LINT1 NMI signal, obviously.
    */
@@ -697,7 +672,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 +695,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)
 {
@@ -769,12 +745,13 @@ VOID MpsSpuriousHandler(VOID)
 #endif
 }
 
+#ifdef CONFIG_SMP
 VOID MpsIpiHandler(VOID)
 {
    KIRQL oldIrql;
 
    HalBeginSystemInterrupt(IPI_VECTOR, 
-                           VECTOR2IRQL(IPI_VECTOR)
+                           IPI_LEVEL
                           &oldIrql);
    Ki386EnableInterrupts();
 #if 0
@@ -791,26 +768,27 @@ VOID MpsIpiHandler(VOID)
    Ki386DisableInterrupts();
    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;
+   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;
 }
 
 VOID
@@ -823,7 +801,7 @@ 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();
 
@@ -850,4 +828,328 @@ MpsTimerHandler(ULONG Vector, PKIRQ_TRAPFRAME Trapframe)
    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 = ((PKIPCR)KeGetCurrentKPCR())->PrcbData.FeatureBits & X86_FEATURE_TSC ? 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)
+   {
+      Ki386RdTSC(t1);
+   }
+   tt1 = APICRead(APIC_CCRT);
+
+   WaitFor8254Wraparound();
+
+
+   tt2 = APICRead(APIC_CCRT);
+   if (TSCPresent)
+   {
+      Ki386RdTSC(t2);
+      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);
+      ((PKIPCR)KeGetCurrentKPCR())->PrcbData.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 address)
+{
+  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)KeGetCurrentKPCR()->IDT + index * sizeof(KIDTENTRY));
+  idt->Offset = address & 0xffff;
+  idt->Selector = KGDT_R0_CODE;
+  idt->Access = Access.Value;
+  idt->ExtendedOffset = address >> 16;
+}
+
+VOID HaliInitBSP(VOID)
+{
+#ifdef CONFIG_SMP
+   PUSHORT ps;
+#endif
+
+   static BOOLEAN BSPInitialized = FALSE;
+
+   /* Only initialize the BSP once */
+   if (BSPInitialized)
+   {
+      KEBUGCHECK(0);
+      return;
+   }
+
+   BSPInitialized = TRUE;
+
+   /* Setup interrupt handlers */
+   SetInterruptGate(LOCAL_TIMER_VECTOR, (ULONG)MpsTimerInterrupt);
+   SetInterruptGate(ERROR_VECTOR, (ULONG)MpsErrorInterrupt);
+   SetInterruptGate(SPURIOUS_VECTOR, (ULONG)MpsSpuriousInterrupt);
+#ifdef CONFIG_SMP
+   SetInterruptGate(IPI_VECTOR, (ULONG)MpsIpiInterrupt);
+#endif
+   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) 
+   {
+      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)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;
+#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))
+   {
+     KEBUGCHECK(0);
+   }
+   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 */
+   Ke386GetPageTableDirectory(Common->PageDirectory);
+   /* Write the kernel entry point */
+   Common->NtProcessStartup = (ULONG_PTR)RtlImageNtHeader((PVOID)KernelBase)->OptionalHeader.AddressOfEntryPoint + KernelBase;
+   /* Write the state of the mae mode */
+   Common->PaeModeEnabled = Ke386GetCr4() & X86_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 */