You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1417 lines
34 KiB
1417 lines
34 KiB
/*++
|
|
|
|
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"
|
|
#include "pool.h"
|
|
|
|
//
|
|
// KiSMTProcessorsPresent is used to indicate whether or not any SMT
|
|
// processors have been started. This is used to determine whether to
|
|
// check for processor licensing before or after starting the
|
|
// processor and to trigger additional work after processor startup if
|
|
// SMT processors are present.
|
|
//
|
|
|
|
BOOLEAN KiSMTProcessorsPresent;
|
|
|
|
//
|
|
// KiUnlicensedProcessorPresent is used so that the processor feature
|
|
// enable code is aware that there are logical processors present that
|
|
// have a state dependency on the processor features that were enabled
|
|
// when it was put into a holding state because we couldn't license
|
|
// the processor.
|
|
//
|
|
|
|
BOOLEAN KiUnlicensedProcessorPresent;
|
|
|
|
|
|
#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
|
|
);
|
|
|
|
VOID
|
|
KiSetHaltedNmiandDoubleFaultHandler(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
KiDummyNmiHandler (
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
KiDummyDoubleFaultHandler(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
KiClearBusyBitInTssDescriptor(
|
|
IN ULONG DescriptorOffset
|
|
);
|
|
|
|
VOID
|
|
KiHaltThisProcessor(
|
|
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];
|
|
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma data_seg()
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#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;
|
|
PUCHAR pThreadObject;
|
|
PULONG pTopOfStack;
|
|
ULONG NewProcessorNumber;
|
|
BOOLEAN NewProcessor;
|
|
PKTSS pTSS;
|
|
SIZE_T ProcessorDataSize;
|
|
UCHAR NodeNumber = 0;
|
|
PVOID PerProcessorAllocation;
|
|
PUCHAR Base;
|
|
ULONG IdtOffset;
|
|
ULONG GdtOffset;
|
|
BOOLEAN NewLicense;
|
|
PKPRCB NewPrcb;
|
|
|
|
#if defined(KE_MULTINODE)
|
|
|
|
USHORT ProcessorId;
|
|
PKNODE Node;
|
|
NTSTATUS Status;
|
|
|
|
#endif
|
|
|
|
//
|
|
// Do not start additional processors if the RELOCATEPHYSICAL loader
|
|
// switch has been specified.
|
|
//
|
|
|
|
if (KeLoaderBlock->LoadOptions != NULL) {
|
|
if (strstr(KeLoaderBlock->LoadOptions, "RELOCATEPHYSICAL") != NULL) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the boot processor has PII spec A27 errata (also present in
|
|
// early Pentium Pro chips), then use only one processor to avoid
|
|
// unpredictable eflags corruption.
|
|
//
|
|
// Note this only affects some (but not all) chips @ 333Mhz and below.
|
|
//
|
|
|
|
if (!(KeFeatureBits & KF_WORKING_PTE)) {
|
|
return;
|
|
}
|
|
|
|
#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)) {
|
|
|
|
//
|
|
// Adjust the data structures to reflect that P0 is not on Node 0.
|
|
//
|
|
|
|
if (NodeNumber != 0) {
|
|
|
|
ASSERT(KeNodeBlock[0] == &KiNode0);
|
|
KeNodeBlock[0]->ProcessorMask &= ~1;
|
|
KiNodeInit[0] = *KeNodeBlock[0];
|
|
KeNodeBlock[0] = &KiNodeInit[0];
|
|
|
|
KiNode0 = *KeNodeBlock[NodeNumber];
|
|
KeNodeBlock[NodeNumber] = &KiNode0;
|
|
KeNodeBlock[NodeNumber]->ProcessorMask |= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
#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 (!KiSMTProcessorsPresent) {
|
|
|
|
//
|
|
// If some of 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;
|
|
}
|
|
}
|
|
|
|
RetryStartProcessor:
|
|
|
|
#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;
|
|
}
|
|
|
|
//
|
|
// Allocate a pool tag table for the new processor.
|
|
//
|
|
|
|
if (ExCreatePoolTagTable (NewProcessorNumber, NodeNumber) == NULL) {
|
|
MmFreeIndependentPages ( PerProcessorAllocation, ProcessorDataSize);
|
|
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;
|
|
|
|
|
|
//
|
|
// 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;
|
|
ExDeletePoolTagTable (NewProcessorNumber);
|
|
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;
|
|
UCHAR LogicalProcessors;
|
|
|
|
//
|
|
// Retrieve logical processor count.
|
|
//
|
|
|
|
LogicalProcessors = (UCHAR) (KiProcessorStartData[1] >> 16);
|
|
|
|
//
|
|
// If this physical processor supports greater than 1
|
|
// logical processors (threads), then it supports SMT
|
|
// and should only be charged a license if it
|
|
// represents a new physical processor.
|
|
//
|
|
|
|
if (LogicalProcessors > 1) {
|
|
|
|
//
|
|
// Round number of logical processors per physical processor
|
|
// up to a power of two then subtract 1 to get the logical
|
|
// processor apic mask.
|
|
//
|
|
|
|
ApicMask = LogicalProcessors + LogicalProcessors - 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();
|
|
|
|
//
|
|
// Free resources not being used by processor we
|
|
// weren't able to license.
|
|
//
|
|
|
|
KiProcessorBlock[NewProcessorNumber] = NULL;
|
|
MmDeleteKernelStack ( pDpcStack, FALSE);
|
|
ExDeletePoolTagTable (NewProcessorNumber);
|
|
|
|
//
|
|
// The new processor has been put into a HLT loop with
|
|
// interrupts disabled and handlers for NMI/double
|
|
// faults. Because this processor is dependent on
|
|
// page table state as it exists now, avoid turning on
|
|
// large page support later.
|
|
//
|
|
|
|
KiUnlicensedProcessorPresent = TRUE;
|
|
|
|
//
|
|
// Ask the HAL to start the next processor but without
|
|
// advancing the processor number.
|
|
//
|
|
|
|
|
|
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 started.
|
|
//
|
|
|
|
KiAllProcessorsStarted();
|
|
|
|
//
|
|
// Reset and synchronize the performance counters of all processors, by
|
|
// applying a null adjustment to the interrupt time.
|
|
//
|
|
|
|
KeAdjustInterruptTime (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;
|
|
ULONG Limit;
|
|
|
|
_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) {
|
|
Limit = (Descriptor.Limit << PAGE_SHIFT) - 1;
|
|
Descriptor.Limit = (USHORT) Limit;
|
|
ASSERT (Descriptor.Limit == Limit);
|
|
}
|
|
|
|
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;
|
|
|
|
if (!KiSMTProcessorsPresent) {
|
|
|
|
//
|
|
// 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->LogicalProcessorsPerPhysicalProcessor == 1)) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// 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).
|
|
//
|
|
|
|
ApicId = Prcb->InitialApicId;
|
|
|
|
//
|
|
// Round number of logical processors up to a power of 2
|
|
// then subtract one to get the logical processor apic mask.
|
|
//
|
|
|
|
ASSERT(Prcb->LogicalProcessorsPerPhysicalProcessor);
|
|
ApicMask = Prcb->LogicalProcessorsPerPhysicalProcessor;
|
|
|
|
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->LogicalProcessorsPerPhysicalProcessor == 1)) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Does this processor have the same ID as the one
|
|
// we're looking for?
|
|
//
|
|
|
|
if ((BuddyPrcb->InitialApicId & ApicMask) != ApicId) {
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
#if defined(KE_MULTINODE)
|
|
ULONG i;
|
|
#endif
|
|
|
|
//
|
|
// 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];
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the node number.
|
|
//
|
|
|
|
KeNodeBlock[i]->NodeNumber = (UCHAR)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;
|
|
KiSetHaltedNmiandDoubleFaultHandler();
|
|
_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;
|
|
}
|
|
|
|
VOID
|
|
KiSetHaltedNmiandDoubleFaultHandler(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
This routine is a called before the application processor that is not
|
|
going to be started is put into halt. It is used to hook a dummy Nmi and
|
|
double fault handler.
|
|
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
--*/
|
|
{
|
|
PKPCR Pcr;
|
|
PKGDTENTRY GdtPtr;
|
|
ULONG TssAddr;
|
|
|
|
Pcr = KeGetPcr();
|
|
|
|
GdtPtr = (PKGDTENTRY)&(Pcr->GDT[Pcr->IDT[IDT_NMI_VECTOR].Selector >> 3]);
|
|
TssAddr = (((GdtPtr->HighWord.Bits.BaseHi << 8) +
|
|
GdtPtr->HighWord.Bits.BaseMid) << 16) + GdtPtr->BaseLow;
|
|
((PKTSS)TssAddr)->Eip = (ULONG)KiDummyNmiHandler;
|
|
|
|
|
|
GdtPtr = (PKGDTENTRY)&(Pcr->GDT[Pcr->IDT[IDT_DFH_VECTOR].Selector >> 3]);
|
|
TssAddr = (((GdtPtr->HighWord.Bits.BaseHi << 8) +
|
|
GdtPtr->HighWord.Bits.BaseMid) << 16) + GdtPtr->BaseLow;
|
|
((PKTSS)TssAddr)->Eip = (ULONG)KiDummyDoubleFaultHandler;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
KiDummyNmiHandler (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the dummy handler that is executed by the processor
|
|
that is not started. We are just being paranoid about clearing
|
|
the busy bit for the NMI and Double Fault Handler TSS.
|
|
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Does not return
|
|
--*/
|
|
{
|
|
KiClearBusyBitInTssDescriptor(NMI_TSS_DESC_OFFSET);
|
|
KiHaltThisProcessor();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
KiDummyDoubleFaultHandler(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the dummy handler that is executed by the processor
|
|
that is not started. We are just being paranoid about clearing
|
|
the busy bit for the NMI and Double Fault Handler TSS.
|
|
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Does not return
|
|
--*/
|
|
{
|
|
KiClearBusyBitInTssDescriptor(DF_TSS_DESC_OFFSET);
|
|
KiHaltThisProcessor();
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
KiClearBusyBitInTssDescriptor(
|
|
IN ULONG DescriptorOffset
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called to clear busy bit in descriptor from the NMI and double
|
|
Fault Handlers
|
|
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
--*/
|
|
{
|
|
PKPCR Pcr;
|
|
PKGDTENTRY GdtPtr;
|
|
Pcr = KeGetPcr();
|
|
GdtPtr =(PKGDTENTRY)(Pcr->GDT);
|
|
GdtPtr =(PKGDTENTRY)((ULONG)GdtPtr + DescriptorOffset);
|
|
GdtPtr->HighWord.Bytes.Flags1 = 0x89; // 32bit. dpl=0. present, TSS32, not busy
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
KiHaltThisProcessor(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
After Clearing the busy bit (just being paranoid here) we halt
|
|
this processor.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
--*/
|
|
{
|
|
|
|
while(1) {
|
|
_asm {
|
|
hlt
|
|
}
|
|
}
|
|
}
|
|
#endif // !NT_UP
|
|
|