1158 lines
28 KiB
C
1158 lines
28 KiB
C
/*++
|
||
|
||
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
|
||
|