windows-nt/Source/XPSP1/NT/base/ntos/ke/i386/allproc.c

1158 lines
28 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
allproc.c
Abstract:
This module allocates and initializes kernel resources required
to start a new processor, and passes a complete process_state
structre to the hal to obtain a new processor. This is done
for every processor.
Author:
Ken Reneris (kenr) 22-Jan-92
Environment:
Kernel mode only.
Phase 1 of bootup
Revision History:
--*/
#include "ki.h"
#ifdef NT_UP
VOID
KeStartAllProcessors (
VOID
)
{
// UP Build - this function is a nop
}
#else
static VOID
KiCloneDescriptor (
IN PKDESCRIPTOR pSrcDescriptorInfo,
IN PKDESCRIPTOR pDestDescriptorInfo,
IN PVOID Base
);
static VOID
KiCloneSelector (
IN ULONG SrcSelector,
IN PKGDTENTRY pNGDT,
IN PKDESCRIPTOR pDestDescriptor,
IN PVOID Base
);
VOID
KiAdjustSimultaneousMultiThreadingCharacteristics(
VOID
);
VOID
KiProcessorStart(
VOID
);
BOOLEAN
KiStartWaitAcknowledge(
VOID
);
#if defined(KE_MULTINODE)
NTSTATUS
KiNotNumaQueryProcessorNode(
IN ULONG ProcessorNumber,
OUT PUSHORT Identifier,
OUT PUCHAR Node
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, KiNotNumaQueryProcessorNode)
#endif
#endif
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,KeStartAllProcessors)
#pragma alloc_text(INIT,KiCloneDescriptor)
#pragma alloc_text(INIT,KiCloneSelector)
#pragma alloc_text(INIT,KiAllProcessorsStarted)
#pragma alloc_text(INIT,KiAdjustSimultaneousMultiThreadingCharacteristics)
#pragma alloc_text(INIT,KiStartWaitAcknowledge)
#endif
enum {
KcStartContinue,
KcStartWait,
KcStartGetId,
KcStartDoNotStart,
KcStartCommandError = 0xff
} KiProcessorStartControl = KcStartContinue;
ULONG KiProcessorStartData[4];
ULONG KiBarrierWait = 0;
//
// KeNumprocSpecified is set to the number of processors specified with
// /NUMPROC in OSLOADOPTIONS. This will bypass the license increase for
// logical processors limiting the total number of processors to the number
// specified.
//
ULONG KeNumprocSpecified;
#if defined(KE_MULTINODE)
PHALNUMAQUERYPROCESSORNODE KiQueryProcessorNode = KiNotNumaQueryProcessorNode;
//
// Statically preallocate enough KNODE structures to allow MM
// to allocate pages by node during system initialization. As
// processors are brought online, real KNODE structures are
// allocated in the appropriate memory for the node.
//
// This statically allocated set will be deallocated once the
// system is initialized.
//
#ifdef ALLOC_DATA_PRAGMA
#pragma data_seg("INITDATA")
#endif
KNODE KiNodeInit[MAXIMUM_CCNUMA_NODES];
#endif
#define REJECT_PROCESSOR_LIMIT 64
#define ROUNDUP16(x) (((x)+15) & ~15)
VOID
KeStartAllProcessors (
VOID
)
/*++
Routine Description:
Called by p0 during phase 1 of bootup. This function implements
the x86 specific code to contact the hal for each system processor.
Arguments:
Return Value:
All available processors are sent to KiSystemStartup.
--*/
{
KPROCESSOR_STATE ProcessorState;
KDESCRIPTOR Descriptor;
KDESCRIPTOR TSSDesc, DFTSSDesc, NMITSSDesc, PCRDesc;
PKGDTENTRY pGDT;
PVOID pStack;
PVOID pDpcStack;
ULONG DFStack;
PUCHAR pThreadObject;
PULONG pTopOfStack;
ULONG NewProcessorNumber;
BOOLEAN NewProcessor;
PKPROCESS Process;
PKTHREAD Thread;
PKTSS pTSS;
PLIST_ENTRY NextEntry;
LONG NumberProcessors;
SIZE_T ProcessorDataSize;
UCHAR NodeNumber = 0;
USHORT ProcessorId;
PVOID PerProcessorAllocation;
PUCHAR Base;
ULONG IdtOffset;
ULONG GdtOffset;
NTSTATUS Status;
PKNODE Node;
BOOLEAN NewLicense;
UCHAR RejectedProcessorCount = 0;
PKPRCB NewPrcb;
#if defined(KE_MULTINODE)
//
// In the unlikely event that processor 0 is not on node
// 0, fix it.
//
if (KeNumberNodes > 1) {
Status = KiQueryProcessorNode(0,
&ProcessorId,
&NodeNumber);
if (NT_SUCCESS(Status)) {
//
// This should never fail.
//
if (NodeNumber != 0) {
KeNodeBlock[0]->ProcessorMask &= ~1;
KeNodeBlock[NodeNumber]->ProcessorMask |= 1;
KeGetCurrentPrcb()->ParentNode = KeNodeBlock[NodeNumber];
}
}
}
#endif
//
// Calculate the size of the per processor data. This includes
// PCR (+PRCB)
// TSS
// Idle Thread Object
// NMI TSS
// Double Fault TSS
// Double Fault Stack
// GDT
// IDT
//
// If this is a multinode system, the KNODE structure is allocated
// as well. It isn't very big so we waste a few bytes for
// processors that aren't the first in a node.
//
// A DPC and Idle stack are also allocated but these are done
// seperately.
//
ProcessorDataSize = ROUNDUP16(sizeof(KPCR)) +
ROUNDUP16(sizeof(KTSS)) +
ROUNDUP16(sizeof(ETHREAD)) +
ROUNDUP16(FIELD_OFFSET(KTSS, IoMaps)) +
ROUNDUP16(FIELD_OFFSET(KTSS, IoMaps)) +
ROUNDUP16(DOUBLE_FAULT_STACK_SIZE);
#if defined(KE_MULTINODE)
ProcessorDataSize += ROUNDUP16(sizeof(KNODE));
#endif
//
// Add sizeof GDT
//
GdtOffset = ProcessorDataSize;
_asm {
sgdt Descriptor.Limit
}
ProcessorDataSize += Descriptor.Limit + 1;
//
// Add sizeof IDT
//
IdtOffset = ProcessorDataSize;
_asm {
sidt Descriptor.Limit
}
ProcessorDataSize += Descriptor.Limit + 1;
//
// If the registered number of processors is greater than the maximum
// number of processors supported, then only allow the maximum number
// of supported processors.
//
if (KeRegisteredProcessors > MAXIMUM_PROCESSORS) {
KeRegisteredProcessors = MAXIMUM_PROCESSORS;
}
//
// Set barrier that will prevent any other processor from entering the
// idle loop until all processors have been started.
//
KiBarrierWait = 1;
//
// Loop asking the HAL for the next processor. Stop when the
// HAL says there aren't any more.
//
for (NewProcessorNumber = 1;
NewProcessorNumber < MAXIMUM_PROCESSORS;
NewProcessorNumber++) {
if ((KeFeatureBits & KF_SMT) == 0) {
//
// If all the processors in the system support Simultaneous
// Multi-Threading we allow the additional logical processors
// in a set to run under the same license as the first logical
// processor in a set.
//
// Otherwise, do not attempt to start more processors than
// there are licenses for. (This is because as of Whistler
// Beta2 we are having problems with systems that send SMIs
// to processors that are not in "wait for SIPI" state. The
// code to scan for additional logical processors causes
// processors not licensed to be in a halted state).
//
// PeterJ 03/02/01.
//
if (NewProcessorNumber >= KeRegisteredProcessors) {
break;
}
}
#if defined(KE_MULTINODE)
Status = KiQueryProcessorNode(NewProcessorNumber,
&ProcessorId,
&NodeNumber);
if (!NT_SUCCESS(Status)) {
//
// No such processor, advance to next.
//
continue;
}
Node = KeNodeBlock[NodeNumber];
#endif
//
// Allocate memory for the new processor specific data. If
// the allocation fails, stop starting processors.
//
PerProcessorAllocation =
MmAllocateIndependentPages (ProcessorDataSize, NodeNumber);
if (PerProcessorAllocation == NULL) {
break;
}
Base = (PUCHAR)PerProcessorAllocation;
//
// Build up a processor state for new processor
//
RtlZeroMemory ((PVOID) &ProcessorState, sizeof ProcessorState);
//
// Give the new processor its own GDT
//
_asm {
sgdt Descriptor.Limit
}
KiCloneDescriptor (&Descriptor,
&ProcessorState.SpecialRegisters.Gdtr,
Base + GdtOffset);
pGDT = (PKGDTENTRY) ProcessorState.SpecialRegisters.Gdtr.Base;
//
// Give new processor its own IDT
//
_asm {
sidt Descriptor.Limit
}
KiCloneDescriptor (&Descriptor,
&ProcessorState.SpecialRegisters.Idtr,
Base + IdtOffset);
//
// Give new processor its own TSS and PCR
//
KiCloneSelector (KGDT_R0_PCR, pGDT, &PCRDesc, Base);
RtlZeroMemory (Base, ROUNDUP16(sizeof(KPCR)));
Base += ROUNDUP16(sizeof(KPCR));
KiCloneSelector (KGDT_TSS, pGDT, &TSSDesc, Base);
Base += ROUNDUP16(sizeof(KTSS));
//
// Idle Thread thread object.
//
pThreadObject = Base;
RtlZeroMemory(Base, sizeof(ETHREAD));
Base += ROUNDUP16(sizeof(ETHREAD));
//
// NMI TSS and double-fault TSS & stack.
//
KiCloneSelector (KGDT_DF_TSS, pGDT, &DFTSSDesc, Base);
Base += ROUNDUP16(FIELD_OFFSET(KTSS, IoMaps));
KiCloneSelector (KGDT_NMI_TSS, pGDT, &NMITSSDesc, Base);
Base += ROUNDUP16(FIELD_OFFSET(KTSS, IoMaps));
Base += DOUBLE_FAULT_STACK_SIZE;
pTSS = (PKTSS)DFTSSDesc.Base;
pTSS->Esp0 = (ULONG)Base;
pTSS->Esp = (ULONG)Base;
pTSS = (PKTSS)NMITSSDesc.Base;
pTSS->Esp0 = (ULONG)Base;
pTSS->Esp = (ULONG)Base;
//
// Set other SpecialRegisters in processor state
//
_asm {
mov eax, cr0
and eax, NOT (CR0_AM or CR0_WP)
mov ProcessorState.SpecialRegisters.Cr0, eax
mov eax, cr3
mov ProcessorState.SpecialRegisters.Cr3, eax
pushfd
pop eax
mov ProcessorState.ContextFrame.EFlags, eax
and ProcessorState.ContextFrame.EFlags, NOT EFLAGS_INTERRUPT_MASK
}
ProcessorState.SpecialRegisters.Tr = KGDT_TSS;
pGDT[KGDT_TSS>>3].HighWord.Bytes.Flags1 = 0x89;
#if defined(_X86PAE_)
ProcessorState.SpecialRegisters.Cr4 = CR4_PAE;
#endif
//
// Allocate a DPC stack, idle thread stack and ThreadObject for
// the new processor.
//
pStack = MmCreateKernelStack (FALSE, NodeNumber);
pDpcStack = MmCreateKernelStack (FALSE, NodeNumber);
//
// Setup context
// Push variables onto new stack
//
pTopOfStack = (PULONG) pStack;
pTopOfStack[-1] = (ULONG) KeLoaderBlock;
ProcessorState.ContextFrame.Esp = (ULONG) (pTopOfStack-2);
ProcessorState.ContextFrame.Eip = (ULONG) KiSystemStartup;
ProcessorState.ContextFrame.SegCs = KGDT_R0_CODE;
ProcessorState.ContextFrame.SegDs = KGDT_R3_DATA;
ProcessorState.ContextFrame.SegEs = KGDT_R3_DATA;
ProcessorState.ContextFrame.SegFs = KGDT_R0_PCR;
ProcessorState.ContextFrame.SegSs = KGDT_R0_DATA;
//
// Initialize new processor's PCR & Prcb
//
KiInitializePcr (
(ULONG) NewProcessorNumber,
(PKPCR) PCRDesc.Base,
(PKIDTENTRY) ProcessorState.SpecialRegisters.Idtr.Base,
(PKGDTENTRY) ProcessorState.SpecialRegisters.Gdtr.Base,
(PKTSS) TSSDesc.Base,
(PKTHREAD) pThreadObject,
(PVOID) pDpcStack
);
NewPrcb = ((PKPCR)(PCRDesc.Base))->Prcb;
//
// Assume new processor will be the first processor in its
// SMT set. (Right choice for non SMT processors, adjusted
// later if not correct).
//
NewPrcb->MultiThreadSetMaster = NewPrcb;
#if defined(KE_MULTINODE)
//
// If this is the first processor on this node, use the
// space allocated for KNODE as the KNODE.
//
if (KeNodeBlock[NodeNumber] == &KiNodeInit[NodeNumber]) {
Node = (PKNODE)Base;
*Node = KiNodeInit[NodeNumber];
KeNodeBlock[NodeNumber] = Node;
}
Base += ROUNDUP16(sizeof(KNODE));
NewPrcb->ParentNode = Node;
#else
NewPrcb->ParentNode = KeNodeBlock[0];
#endif
ASSERT(((PUCHAR)PerProcessorAllocation + GdtOffset) == Base);
//
// Adjust LoaderBlock so it has the next processors state
//
KeLoaderBlock->KernelStack = (ULONG) pTopOfStack;
KeLoaderBlock->Thread = (ULONG) pThreadObject;
KeLoaderBlock->Prcb = (ULONG) NewPrcb;
RetryStartProcessor:
//
// Get CPUID(1) info from the starting processor.
//
KiProcessorStartData[0] = 1;
KiProcessorStartControl = KcStartGetId;
//
// Contact hal to start new processor
//
NewProcessor = HalStartNextProcessor (KeLoaderBlock, &ProcessorState);
if (!NewProcessor) {
//
// There wasn't another processor, so free resources and
// break
//
KiProcessorBlock[NewProcessorNumber] = NULL;
MmFreeIndependentPages ( PerProcessorAllocation, ProcessorDataSize);
MmDeleteKernelStack ( pStack, FALSE);
MmDeleteKernelStack ( pDpcStack, FALSE);
break;
}
//
// Wait for the new processor to fill in the CPUID data requested.
//
NewLicense = TRUE;
if (KiStartWaitAcknowledge() == TRUE) {
if (KiProcessorStartData[3] & 0x10000000) {
//
// This processor might support SMT, in which case, if this
// is not the first logical processor in an SMT set, it should
// not be charged a license. If it is the first in a set,
// and the total number of sets exceeds the number of licensed
// processors, this processor should not be allowed to start.
//
ULONG ApicMask;
ULONG ApicId;
ULONG i;
PKPRCB SmtCheckPrcb;
//
// Round number of logical processors per physical processor
// up to a power of two then subrtact 1 to get the logical
// processor apic mask.
//
ApicMask = (KiProcessorStartData[1] >> 16) & 0xff;
ApicMask = ApicMask + ApicMask - 1;
KeFindFirstSetLeftMember(ApicMask, &ApicMask);
ApicMask = ~((1 << ApicMask) - 1);
ApicId = (KiProcessorStartData[1] >> 24) & ApicMask;
//
// Check to see if any started processor is in the same
// set.
//
for (i = 0; i < NewProcessorNumber; i++) {
SmtCheckPrcb = KiProcessorBlock[i];
if (SmtCheckPrcb) {
if ((SmtCheckPrcb->InitialApicId & ApicMask) == ApicId) {
NewLicense = FALSE;
NewPrcb->MultiThreadSetMaster = SmtCheckPrcb;
break;
}
}
}
}
}
if ((NewLicense == FALSE) &&
((KeNumprocSpecified == 0) ||
(KeRegisteredProcessors < KeNumprocSpecified))) {
//
// This processor is a logical processor in the same SMT
// set as another logical processor. Don't charge a
// license for it.
//
KeRegisteredProcessors++;
} else {
//
// The new processor is the first or only logical processor
// in a physical processor. If the number of physical
// processors exceeds the license, don't start this processor.
//
if ((ULONG)KeNumberProcessors >= KeRegisteredProcessors) {
KiProcessorStartControl = KcStartDoNotStart;
KiStartWaitAcknowledge();
if (++RejectedProcessorCount > REJECT_PROCESSOR_LIMIT) {
//
// The HAL must not be advancing to the next
// processor. Give up.
//
// Note: This test is due to the introduction
// of this feature of asking the HAL for a new
// processor (using the same processor number
// as the just rejected processor). If the
// HAL does not support advancing under this
// condition we would loop here forever.
//
break;
}
//
// The new processor has now returned to the state it
// was in before the HAL initialized it. Have the
// HAL start the next processor using the same resource
// set.
//
goto RetryStartProcessor;
}
}
KiProcessorStartControl = KcStartContinue;
#if defined(KE_MULTINODE)
Node->ProcessorMask |= 1 << NewProcessorNumber;
#endif
//
// Wait for processor to initialize in kernel, then loop for another
//
while (*((volatile ULONG *) &KeLoaderBlock->Prcb) != 0) {
KeYieldProcessor();
}
}
//
// All processors have been stated.
//
KiAllProcessorsStarted();
//
// Reset and synchronize the performance counters of all processors, by
// applying a null adjustment to the interrupt time
//
KiAdjustInterruptTime (0);
//
// Allow all processors that were started to enter the idle loop and
// begin execution.
//
KiBarrierWait = 0;
}
static VOID
KiCloneSelector (
IN ULONG SrcSelector,
IN PKGDTENTRY pNGDT,
IN PKDESCRIPTOR pDestDescriptor,
IN PVOID Base
)
/*++
Routine Description:
Makes a copy of the current selector's data, and updates the new
gdt's linear address to point to the new copy.
Arguments:
SrcSelector - Selector value to clone
pNGDT - New gdt table which is being built
DescDescriptor - descriptor structure to fill in with resulting memory
Base - Base memory for the new descriptor.
Return Value:
None.
--*/
{
KDESCRIPTOR Descriptor;
PKGDTENTRY pGDT;
ULONG CurrentBase;
ULONG NewBase;
_asm {
sgdt fword ptr [Descriptor.Limit] ; Get GDT's addr
}
pGDT = (PKGDTENTRY) Descriptor.Base;
pGDT += SrcSelector >> 3;
pNGDT += SrcSelector >> 3;
CurrentBase = pGDT->BaseLow | (pGDT->HighWord.Bits.BaseMid << 16) |
(pGDT->HighWord.Bits.BaseHi << 24);
Descriptor.Base = CurrentBase;
Descriptor.Limit = pGDT->LimitLow;
if (pGDT->HighWord.Bits.Granularity & GRAN_PAGE)
Descriptor.Limit = (Descriptor.Limit << PAGE_SHIFT) -1;
KiCloneDescriptor (&Descriptor, pDestDescriptor, Base);
NewBase = pDestDescriptor->Base;
pNGDT->BaseLow = (USHORT) NewBase & 0xffff;
pNGDT->HighWord.Bits.BaseMid = (UCHAR) (NewBase >> 16) & 0xff;
pNGDT->HighWord.Bits.BaseHi = (UCHAR) (NewBase >> 24) & 0xff;
}
static VOID
KiCloneDescriptor (
IN PKDESCRIPTOR pSrcDescriptor,
IN PKDESCRIPTOR pDestDescriptor,
IN PVOID Base
)
/*++
Routine Description:
Makes a copy of the specified descriptor, and supplies a return
descriptor for the new copy
Arguments:
pSrcDescriptor - descriptor to clone
pDescDescriptor - the cloned descriptor
Base - Base memory for the new descriptor.
Return Value:
None.
--*/
{
ULONG Size;
Size = pSrcDescriptor->Limit + 1;
pDestDescriptor->Limit = (USHORT) Size -1;
pDestDescriptor->Base = (ULONG) Base;
RtlCopyMemory(Base, (PVOID)pSrcDescriptor->Base, Size);
}
VOID
KiAdjustSimultaneousMultiThreadingCharacteristics(
VOID
)
/*++
Routine Description:
This routine is called (possibly while the dispatcher lock is held)
after processors are added to or removed from the system. It runs
thru the PRCBs for each processor in the system and adjusts scheduling
data.
Arguments:
None.
Return Value:
None.
--*/
{
ULONG ProcessorNumber;
ULONG BuddyNumber;
KAFFINITY ProcessorSet;
PKPRCB Prcb;
PKPRCB BuddyPrcb;
ULONG ApicMask;
ULONG ApicId;
ULONG NextIndependent;
if ((KeFeatureBits & KF_SMT) == 0) {
//
// Nobody doing SMT, nothing to do.
//
return;
}
for (ProcessorNumber = 0;
ProcessorNumber < (ULONG)KeNumberProcessors;
ProcessorNumber++) {
Prcb = KiProcessorBlock[ProcessorNumber];
//
// Skip processors which are not present or which do not
// support Simultaneous Multi Threading.
//
if ((Prcb == NULL) || ((Prcb->FeatureBits & KF_SMT) == 0)) {
continue;
}
NextIndependent = 0;
//
// Find all processors with the same physical processor APIC ID.
// The APIC ID for the physical processor is the upper portion
// of the APIC ID, the number of bits in the lower portion is
// log 2 (number logical processors per physical rounded up to
// a power of 2).
//
ASSERT(Prcb->LogicalProcessorsPerPhysicalProcessor);
ApicId = Prcb->InitialApicId;
ApicMask = Prcb->LogicalProcessorsPerPhysicalProcessor;
if (ApicMask == 0) {
//
// Sanity fails: This processor isn't right.
//
continue;
}
//
// Round number of logical processors up to a power of 2
// then subtract one to get the logical processor apic mask.
//
ApicMask = ApicMask + ApicMask - 1;
KeFindFirstSetLeftMember(ApicMask, &ApicMask);
ApicMask = ~((1 << ApicMask) - 1);
ApicId &= ApicMask;
ProcessorSet = 1 << Prcb->Number;
//
// Examine each remaining processor to see if it is part of
// the same set.
//
for (BuddyNumber = ProcessorNumber + 1;
BuddyNumber < (ULONG)KeNumberProcessors;
BuddyNumber++) {
BuddyPrcb = KiProcessorBlock[BuddyNumber];
//
// Skip not present, not SMT.
//
if ((BuddyPrcb == NULL) ||
((BuddyPrcb->FeatureBits & KF_SMT) == 0)) {
continue;
}
//
// Does this processor have the same ID as the one
// we're looking for?
//
if ((BuddyPrcb->InitialApicId & ApicMask) != ApicId) {
//
// No, if this is the first occurance on another
// APIC Id (relative to the processor we're examining)
// then remember this processor as it's the next
// independent processor.
//
if (!NextIndependent) {
NextIndependent = BuddyNumber;
}
continue;
}
//
// Match.
//
ASSERT(Prcb->LogicalProcessorsPerPhysicalProcessor ==
BuddyPrcb->LogicalProcessorsPerPhysicalProcessor);
ProcessorSet |= 1 << BuddyPrcb->Number;
BuddyPrcb->MultiThreadProcessorSet |= ProcessorSet;
}
Prcb->MultiThreadProcessorSet |= ProcessorSet;
}
}
VOID
KiAllProcessorsStarted(
VOID
)
/*++
Routine Description:
This routine is called once all processors in the system
have been started.
Arguments:
None.
Return Value:
None.
--*/
{
ULONG i;
//
// If the system contains Simultaneous Multi Threaded processors,
// adjust grouping information now that each processor is started.
//
KiAdjustSimultaneousMultiThreadingCharacteristics();
#if defined(KE_MULTINODE)
//
// Make sure there are no references to the temporary nodes
// used during initialization.
//
for (i = 0; i < KeNumberNodes; i++) {
if (KeNodeBlock[i] == &KiNodeInit[i]) {
//
// No processor started on this node so no new node
// structure has been allocated. This is possible
// if the node contains only memory or IO busses. At
// this time we need to allocate a permanent node
// structure for the node.
//
KeNodeBlock[i] = ExAllocatePoolWithTag(NonPagedPool,
sizeof(KNODE),
' eK');
if (KeNodeBlock[i]) {
*KeNodeBlock[i] = KiNodeInit[i];
}
}
}
for (i = KeNumberNodes; i < MAXIMUM_CCNUMA_NODES; i++) {
KeNodeBlock[i] = NULL;
}
#endif
if (KeNumberNodes == 1) {
//
// For Non NUMA machines, Node 0 gets all processors.
//
KeNodeBlock[0]->ProcessorMask = KeActiveProcessors;
}
}
#if defined(KE_MULTINODE)
NTSTATUS
KiNotNumaQueryProcessorNode(
IN ULONG ProcessorNumber,
OUT PUSHORT Identifier,
OUT PUCHAR Node
)
/*++
Routine Description:
This routine is a stub used on non NUMA systems to provide a
consistent method of determining the NUMA configuration rather
than checking for the presense of multiple nodes inline.
Arguments:
ProcessorNumber supplies the system logical processor number.
Identifier supplies the address of a variable to receive
the unique identifier for this processor.
NodeNumber supplies the address of a variable to receive
the number of the node this processor resides on.
Return Value:
Returns success.
--*/
{
*Identifier = (USHORT)ProcessorNumber;
*Node = 0;
return STATUS_SUCCESS;
}
#endif
VOID
KiProcessorStart(
VOID
)
/*++
Routine Description:
This routine is a called when a processor begins execution.
It is used to pass processor characteristic information to
the boot processor and to control the starting or non-starting
of this processor.
Arguments:
None.
Return Value:
None.
--*/
{
while (TRUE) {
switch (KiProcessorStartControl) {
case KcStartContinue:
return;
case KcStartWait:
KeYieldProcessor();
break;
case KcStartGetId:
CPUID(KiProcessorStartData[0],
&KiProcessorStartData[0],
&KiProcessorStartData[1],
&KiProcessorStartData[2],
&KiProcessorStartData[3]);
KiProcessorStartControl = KcStartWait;
break;
case KcStartDoNotStart:
//
// The boot processor has determined that this processor
// should NOT be started.
//
// Acknowledge the command so the boot processor will
// continue, disable interrupts (should already be
// the case here) and HALT the processor.
//
KiProcessorStartControl = KcStartWait;
_disable();
while(1) {
_asm { hlt };
}
default:
//
// Not much we can do with unknown commands.
//
KiProcessorStartControl = KcStartCommandError;
break;
}
}
}
BOOLEAN
KiStartWaitAcknowledge(
VOID
)
{
while (KiProcessorStartControl != KcStartWait) {
if (KiProcessorStartControl == KcStartCommandError) {
return FALSE;
}
KeYieldProcessor();
}
return TRUE;
}
#endif // !NT_UP