536 lines
12 KiB
C
536 lines
12 KiB
C
/*++
|
||
|
||
Copyright (c) 1990 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
allproc.c
|
||
|
||
Abstract:
|
||
|
||
This module allocates and intializes kernel resources required
|
||
to start a new processor, and passes a complete processor state
|
||
structure to the HAL to obtain a new processor.
|
||
|
||
Author:
|
||
|
||
Bernard Lint 31-Jul-96
|
||
|
||
Environment:
|
||
|
||
Kernel mode only.
|
||
|
||
Revision History:
|
||
|
||
Based on MIPS original (David N. Cutler 29-Apr-1993)
|
||
|
||
--*/
|
||
|
||
|
||
#include "ki.h"
|
||
|
||
#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, KiAllProcessorsStarted)
|
||
|
||
#endif
|
||
|
||
//
|
||
// Define macro to round up to 64-byte boundary and define block sizes.
|
||
//
|
||
|
||
#define ROUND_UP(x) ((sizeof(x) + 63) & (~63))
|
||
#define BLOCK1_SIZE (2 * (KERNEL_BSTORE_SIZE + KERNEL_STACK_SIZE) + PAGE_SIZE)
|
||
#define BLOCK2_SIZE (ROUND_UP(KPRCB) + ROUND_UP(KNODE) + ROUND_UP(ETHREAD) + 64)
|
||
|
||
#if !defined(NT_UP)
|
||
|
||
//
|
||
// Define barrier wait static data.
|
||
//
|
||
|
||
ULONG KiBarrierWait = 0;
|
||
|
||
#endif
|
||
|
||
#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_PROCESSORS];
|
||
|
||
#endif
|
||
|
||
extern ULONG_PTR KiUserSharedDataPage;
|
||
extern ULONG_PTR KiKernelPcrPage;
|
||
|
||
//
|
||
// Define forward referenced prototypes.
|
||
//
|
||
|
||
VOID
|
||
KiCalibratePerformanceCounter(
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
KiCalibratePerformanceCounterTarget (
|
||
IN PULONG SignalDone,
|
||
IN PVOID Count,
|
||
IN PVOID Parameter2,
|
||
IN PVOID Parameter3
|
||
);
|
||
|
||
VOID
|
||
KiOSRendezvous (
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
KeStartAllProcessors(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called during phase 1 initialize on the master boot
|
||
processor to start all of the other registered processors.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
#if !defined(NT_UP)
|
||
|
||
ULONG_PTR MemoryBlock1;
|
||
ULONG_PTR MemoryBlock2;
|
||
ULONG_PTR MemoryBlock3;
|
||
ULONG_PTR PcrAddress;
|
||
ULONG Number;
|
||
ULONG Count;
|
||
PHYSICAL_ADDRESS PcrPage;
|
||
PKPRCB Prcb;
|
||
BOOLEAN Started;
|
||
KPROCESSOR_STATE ProcessorState;
|
||
UCHAR NodeNumber = 0;
|
||
USHORT ProcessorId;
|
||
SIZE_T ProcessorDataSize;
|
||
PKNODE Node;
|
||
NTSTATUS Status;
|
||
PKPCR NewPcr;
|
||
|
||
#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 &= ~1I64;
|
||
KeNodeBlock[NodeNumber]->ProcessorMask |= 1;
|
||
KeGetCurrentPrcb()->ParentNode = KeNodeBlock[NodeNumber];
|
||
}
|
||
KeGetCurrentPrcb()->ProcessorId = ProcessorId;
|
||
}
|
||
}
|
||
|
||
#endif
|
||
|
||
//
|
||
// 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;
|
||
|
||
//
|
||
// Initialize the processor state that will be used to start each of
|
||
// processors. Each processor starts in the system initialization code
|
||
// with address of the loader parameter block as an argument.
|
||
//
|
||
|
||
Number = 0;
|
||
Count = 1;
|
||
RtlZeroMemory(&ProcessorState, sizeof(KPROCESSOR_STATE));
|
||
ProcessorState.ContextFrame.StIIP = ((PPLABEL_DESCRIPTOR)KiOSRendezvous)->EntryPoint;
|
||
while (Count < KeRegisteredProcessors) {
|
||
|
||
Number++;
|
||
|
||
if (Number >= MAXIMUM_PROCESSORS) {
|
||
break;
|
||
}
|
||
|
||
#if defined(KE_MULTINODE)
|
||
|
||
Status = KiQueryProcessorNode(Number,
|
||
&ProcessorId,
|
||
&NodeNumber);
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
//
|
||
// No such processor, advance to next.
|
||
//
|
||
|
||
continue;
|
||
}
|
||
|
||
Node = KeNodeBlock[NodeNumber];
|
||
|
||
#endif
|
||
|
||
//
|
||
// Allocate a DPC stack, an idle thread kernel stack, a panic
|
||
// stack, a PCR page, a processor block, a kernel node structure
|
||
// and an executive thread object. If the allocation fails, stop
|
||
// starting processors.
|
||
//
|
||
|
||
#if 0
|
||
//PLJTMP: Need to investigate which pieces need to be in KSEG0
|
||
//and allocate the other stuff per node. plus deal with any alignment
|
||
//padding for the size below based on roundup of BLOCK1_SIZE.
|
||
ProcessorDataSize = BLOCK1_SIZE + BLOCK2_SIZE;
|
||
|
||
MemoryBlock1 = (ULONG_PTR)MmAllocateIndependentPages (ProcessorDataSize,
|
||
NodeNumber);
|
||
if ((PVOID)MemoryBlock1 == NULL) {
|
||
break;
|
||
}
|
||
|
||
MemoryBlock2 = MemoryBlock1 + BLOCK1_SIZE;
|
||
|
||
//
|
||
// Zero the allocated memory.
|
||
//
|
||
|
||
RtlZeroMemory((PVOID)MemoryBlock1, ProcessorDataSize);
|
||
#else
|
||
MemoryBlock1 = (ULONG_PTR)ExAllocatePool(NonPagedPool, BLOCK1_SIZE);
|
||
if ((PVOID)MemoryBlock1 == NULL) {
|
||
break;
|
||
}
|
||
|
||
MemoryBlock2 = (ULONG_PTR)ExAllocatePool(NonPagedPool, BLOCK2_SIZE);
|
||
if ((PVOID)MemoryBlock2 == NULL) {
|
||
ExFreePool((PVOID)MemoryBlock1);
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Zero both blocks of allocated memory.
|
||
//
|
||
|
||
RtlZeroMemory((PVOID)MemoryBlock1, BLOCK1_SIZE);
|
||
RtlZeroMemory((PVOID)MemoryBlock2, BLOCK2_SIZE);
|
||
|
||
#endif
|
||
|
||
//
|
||
// Set address of idle thread kernel stack in loader parameter block.
|
||
//
|
||
|
||
KeLoaderBlock->KernelStack = MemoryBlock1 + KERNEL_STACK_SIZE;
|
||
|
||
//
|
||
// Set address of panic stack in loader parameter block.
|
||
//
|
||
|
||
KeLoaderBlock->u.Ia64.PanicStack = MemoryBlock1 + KERNEL_BSTORE_SIZE +
|
||
(2 * KERNEL_STACK_SIZE);
|
||
|
||
//
|
||
// Set the address of the processor block and executive thread in the
|
||
// loader parameter block.
|
||
//
|
||
|
||
KeLoaderBlock->Prcb = MemoryBlock2;
|
||
KeLoaderBlock->Thread = KeLoaderBlock->Prcb + ROUND_UP(KPRCB) +
|
||
ROUND_UP(KNODE);
|
||
((PKPRCB)KeLoaderBlock->Prcb)->Number = (UCHAR)Number;
|
||
|
||
#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)(MemoryBlock1 + ROUND_UP(KPRCB));
|
||
*Node = KiNodeInit[NodeNumber];
|
||
KeNodeBlock[NodeNumber] = Node;
|
||
}
|
||
|
||
((PKPRCB)KeLoaderBlock->Prcb)->ParentNode = Node;
|
||
((PKPRCB)KeLoaderBlock->Prcb)->ProcessorId = ProcessorId;
|
||
|
||
#else
|
||
|
||
((PKPRCB)KeLoaderBlock->Prcb)->ParentNode = KeNodeBlock[0];
|
||
|
||
#endif
|
||
|
||
|
||
//
|
||
// Set the page frame of the PCR page in the loader parameter block.
|
||
//
|
||
|
||
PcrAddress = MemoryBlock1 + (2 * (KERNEL_BSTORE_SIZE + KERNEL_STACK_SIZE));
|
||
PcrPage = MmGetPhysicalAddress((PVOID)PcrAddress);
|
||
KeLoaderBlock->u.Ia64.PcrPage = PcrPage.QuadPart >> PAGE_SHIFT;
|
||
KeLoaderBlock->u.Ia64.PcrPage2 = KiUserSharedDataPage;
|
||
KiKernelPcrPage = KeLoaderBlock->u.Ia64.PcrPage;
|
||
|
||
//
|
||
// Initialize the NT page table base addresses in PCR
|
||
//
|
||
|
||
NewPcr = (PKPCR) PcrAddress;
|
||
NewPcr->PteUbase = PCR->PteUbase;
|
||
NewPcr->PteKbase = PCR->PteKbase;
|
||
NewPcr->PteSbase = PCR->PteSbase;
|
||
NewPcr->PdeUbase = PCR->PdeUbase;
|
||
NewPcr->PdeKbase = PCR->PdeKbase;
|
||
NewPcr->PdeSbase = PCR->PdeSbase;
|
||
NewPcr->PdeUtbase = PCR->PdeUtbase;
|
||
NewPcr->PdeKtbase = PCR->PdeKtbase;
|
||
NewPcr->PdeStbase = PCR->PdeStbase;
|
||
|
||
//
|
||
// Attempt to start the next processor. If attempt is successful,
|
||
// then wait for the processor to get initialized. Otherwise,
|
||
// deallocate the processor resources and terminate the loop.
|
||
//
|
||
|
||
Started = HalStartNextProcessor(KeLoaderBlock, &ProcessorState);
|
||
|
||
if (Started) {
|
||
|
||
//
|
||
// Wait for processor to initialize in kernel,
|
||
// then loop for another
|
||
//
|
||
|
||
while (*((volatile ULONG_PTR *) &KeLoaderBlock->Prcb) != 0) {
|
||
KeYieldProcessor();
|
||
}
|
||
|
||
#if defined(KE_MULTINODE)
|
||
|
||
Node->ProcessorMask |= 1I64 << Number;
|
||
|
||
#endif
|
||
|
||
} else {
|
||
|
||
#if 0
|
||
MmFreeIndependentPages((PVOID)MemoryBlock1, ProcessorDataSize);
|
||
#else
|
||
ExFreePool((PVOID)MemoryBlock1);
|
||
ExFreePool((PVOID)MemoryBlock2);
|
||
#endif
|
||
break;
|
||
|
||
}
|
||
|
||
Count += 1;
|
||
}
|
||
|
||
//
|
||
// All processors have been stated.
|
||
//
|
||
|
||
KiAllProcessorsStarted();
|
||
|
||
//
|
||
// Allow all processor that were started to enter the idle loop and
|
||
// begin execution.
|
||
//
|
||
|
||
KiBarrierWait = 0;
|
||
|
||
#endif
|
||
|
||
//
|
||
// Reset and synchronize the performance counters of all processors.
|
||
//
|
||
|
||
KiAdjustInterruptTime (0);
|
||
return;
|
||
}
|
||
|
||
#if !defined(NT_UP)
|
||
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 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;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
|
||
#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
|
||
|