455 lines
12 KiB
C
455 lines
12 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1990 Microsoft Corporation
|
|||
|
Copyright (c) 1993 Digital Equipment Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
allproc.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module allocates and initializes kernel resources required
|
|||
|
to start a new processor, and passes a complete processor state
|
|||
|
structure to the HAL to obtain a new processor.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
David N. Cutler 29-Apr-1993
|
|||
|
Joe Notarangelo 30-Nov-1993
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode only.
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
|
|||
|
#include "ki.h"
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
|
|||
|
#pragma alloc_text(INIT, KeStartAllProcessors)
|
|||
|
#pragma alloc_text(INIT, KiAllProcessorsStarted)
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
#if defined(KE_MULTINODE)
|
|||
|
|
|||
|
PHALNUMAQUERYNODEAFFINITY KiQueryNodeAffinity;
|
|||
|
|
|||
|
//
|
|||
|
// 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 macro to round up to 64-byte boundary and define block sizes.
|
|||
|
//
|
|||
|
|
|||
|
#define ROUND_UP(x) ((sizeof(x) + 64) & (~64))
|
|||
|
#define BLOCK1_SIZE ((3 * KERNEL_STACK_SIZE) + PAGE_SIZE)
|
|||
|
#define BLOCK2_SIZE (ROUND_UP(KPRCB) + ROUND_UP(KNODE) + ROUND_UP(ETHREAD) + 64)
|
|||
|
|
|||
|
//
|
|||
|
// Macros to compute whether an address is physically addressable.
|
|||
|
//
|
|||
|
|
|||
|
#if defined(_AXP64_)
|
|||
|
|
|||
|
#define IS_KSEG_ADDRESS(v) \
|
|||
|
(((v) >= KSEG43_BASE) && \
|
|||
|
((v) < KSEG43_LIMIT) && \
|
|||
|
(KSEG_PFN(v) < ((KSEG2_BASE - KSEG0_BASE) >> PAGE_SHIFT)))
|
|||
|
|
|||
|
#define KSEG_PFN(v) ((ULONG)(((v) - KSEG43_BASE) >> PAGE_SHIFT))
|
|||
|
#define KSEG0_ADDRESS(v) (KSEG0_BASE | ((v) - KSEG43_BASE))
|
|||
|
|
|||
|
#else
|
|||
|
|
|||
|
#define IS_KSEG_ADDRESS(v) (((v) >= KSEG0_BASE) && ((v) < KSEG2_BASE))
|
|||
|
#define KSEG_PFN(v) ((ULONG)(((v) - KSEG0_BASE) >> PAGE_SHIFT))
|
|||
|
#define KSEG0_ADDRESS(v) (v)
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// Define forward referenced prototypes.
|
|||
|
//
|
|||
|
|
|||
|
VOID
|
|||
|
KiStartProcessor (
|
|||
|
IN PLOADER_PARAMETER_BLOCK Loaderblock
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
KeStartAllProcessors(
|
|||
|
VOID
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function is called during phase 1 initialization 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 Number;
|
|||
|
ULONG PcrPage;
|
|||
|
PKPRCB Prcb;
|
|||
|
KPROCESSOR_STATE ProcessorState;
|
|||
|
struct _RESTART_BLOCK *RestartBlock;
|
|||
|
BOOLEAN Started;
|
|||
|
LOGICAL SpecialPoolState;
|
|||
|
UCHAR NodeNumber = 0;
|
|||
|
|
|||
|
#if defined(KE_MULTINODE)
|
|||
|
|
|||
|
KAFFINITY NewProcessorAffinity;
|
|||
|
PKNODE Node;
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
//
|
|||
|
// If this is a NUMA system, find out the number of nodes and the
|
|||
|
// processors which belong to each node.
|
|||
|
//
|
|||
|
|
|||
|
if (KeNumberNodes > 1) {
|
|||
|
for (NodeNumber = 0; NodeNumber < KeNumberNodes; NodeNumber++) {
|
|||
|
|
|||
|
Node = KeNodeBlock[NodeNumber];
|
|||
|
|
|||
|
//
|
|||
|
// Ask HAL which processors belong to this node and
|
|||
|
// what Node Color to use for page coloring.
|
|||
|
//
|
|||
|
|
|||
|
Status = KiQueryNodeAffinity(NodeNumber,
|
|||
|
&Node->ProcessorMask);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
DbgPrint(
|
|||
|
"KE/HAL: NUMA Hal failed to return info for Node %i.\n",
|
|||
|
NodeNumber);
|
|||
|
DbgPrint("KE/HAL: Reverting to non NUMA configuration.\n");
|
|||
|
ASSERT(NT_SUCCESS(Status));
|
|||
|
KeNumberNodes = 1;
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// In the unlikely event that processor 0 is not
|
|||
|
// on node 0, now would be the perfect time to
|
|||
|
// fix it.
|
|||
|
//
|
|||
|
|
|||
|
if (Node->ProcessorMask & 1) {
|
|||
|
KeGetCurrentPrcb()->ParentNode = Node;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#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;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// 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.
|
|||
|
//
|
|||
|
|
|||
|
RtlZeroMemory(&ProcessorState, sizeof(KPROCESSOR_STATE));
|
|||
|
ProcessorState.ContextFrame.IntA0 = (ULONGLONG)(LONG_PTR)KeLoaderBlock;
|
|||
|
ProcessorState.ContextFrame.Fir = (ULONGLONG)(LONG_PTR)KiStartProcessor;
|
|||
|
Number = 1;
|
|||
|
while (Number < KeRegisteredProcessors) {
|
|||
|
|
|||
|
#if defined(KE_MULTINODE)
|
|||
|
|
|||
|
NewProcessorAffinity = 1 << Number;
|
|||
|
|
|||
|
for (NodeNumber = 0; NodeNumber < KeNumberNodes; NodeNumber++) {
|
|||
|
Node = KeNodeBlock[NodeNumber];
|
|||
|
if (Node->ProcessorMask & NewProcessorAffinity) {
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (NodeNumber == KeNumberNodes) {
|
|||
|
|
|||
|
//
|
|||
|
// This should only happen when we're about to ask
|
|||
|
// for one processor more than is in the system. We
|
|||
|
// could bail here but we have always depended on
|
|||
|
// the HAL to tell us we're done. Set up as if
|
|||
|
// on Node 0 so MM and friends won't be referencing
|
|||
|
// uninitialized structures.
|
|||
|
//
|
|||
|
|
|||
|
NodeNumber = 0;
|
|||
|
Node = KeNodeBlock[0];
|
|||
|
}
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// Allocate a DPC stack, an idle thread kernel stack, a panic
|
|||
|
// stack, a PCR page, a processor block, and an executive thread
|
|||
|
// object. If the allocation fails or the allocation cannot be
|
|||
|
// made from unmapped nonpaged pool, then stop starting processors.
|
|||
|
//
|
|||
|
// Disable any special pooling that the user may have set in the
|
|||
|
// registry as the next couple of allocations must come from KSEG0.
|
|||
|
//
|
|||
|
|
|||
|
SpecialPoolState = MmSetSpecialPool(FALSE);
|
|||
|
MemoryBlock1 = (ULONG_PTR)ExAllocatePool(NonPagedPool, BLOCK1_SIZE);
|
|||
|
if (IS_KSEG_ADDRESS(MemoryBlock1) == FALSE) {
|
|||
|
MmSetSpecialPool(SpecialPoolState);
|
|||
|
if ((PVOID)MemoryBlock1 != NULL) {
|
|||
|
ExFreePool((PVOID)MemoryBlock1);
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
MemoryBlock2 = (ULONG_PTR)ExAllocatePool(NonPagedPool, BLOCK2_SIZE);
|
|||
|
if (IS_KSEG_ADDRESS(MemoryBlock2) == FALSE) {
|
|||
|
MmSetSpecialPool(SpecialPoolState);
|
|||
|
ExFreePool((PVOID)MemoryBlock1);
|
|||
|
if ((PVOID)MemoryBlock2 != NULL) {
|
|||
|
ExFreePool((PVOID)MemoryBlock2);
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
MmSetSpecialPool(SpecialPoolState);
|
|||
|
|
|||
|
//
|
|||
|
// Zero both blocks of allocated memory.
|
|||
|
//
|
|||
|
|
|||
|
RtlZeroMemory((PVOID)MemoryBlock1, BLOCK1_SIZE);
|
|||
|
RtlZeroMemory((PVOID)MemoryBlock2, BLOCK2_SIZE);
|
|||
|
|
|||
|
//
|
|||
|
// Set address of interrupt stack in loader parameter block.
|
|||
|
//
|
|||
|
|
|||
|
KeLoaderBlock->u.Alpha.PanicStack =
|
|||
|
KSEG0_ADDRESS(MemoryBlock1 + (1 * KERNEL_STACK_SIZE));
|
|||
|
|
|||
|
//
|
|||
|
// Set address of idle thread kernel stack in loader parameter block.
|
|||
|
//
|
|||
|
|
|||
|
KeLoaderBlock->KernelStack =
|
|||
|
KSEG0_ADDRESS(MemoryBlock1 + (2 * KERNEL_STACK_SIZE));
|
|||
|
|
|||
|
ProcessorState.ContextFrame.IntSp =
|
|||
|
(ULONGLONG)(LONG_PTR)KeLoaderBlock->KernelStack;
|
|||
|
|
|||
|
//
|
|||
|
// Set address of panic stack in loader parameter block.
|
|||
|
//
|
|||
|
|
|||
|
KeLoaderBlock->u.Alpha.DpcStack =
|
|||
|
KSEG0_ADDRESS(MemoryBlock1 + (3 * KERNEL_STACK_SIZE));
|
|||
|
|
|||
|
//
|
|||
|
// Set the page frame of the PCR page in the loader parameter block.
|
|||
|
//
|
|||
|
|
|||
|
PcrPage = KSEG_PFN(MemoryBlock1 + (3 * KERNEL_STACK_SIZE));
|
|||
|
KeLoaderBlock->u.Alpha.PcrPage = PcrPage;
|
|||
|
|
|||
|
//
|
|||
|
// Set the address of the processor block and executive thread in the
|
|||
|
// loader parameter block.
|
|||
|
//
|
|||
|
|
|||
|
KeLoaderBlock->Prcb = KSEG0_ADDRESS((MemoryBlock2 + 63) & ~63);
|
|||
|
KeLoaderBlock->Thread = KeLoaderBlock->Prcb +
|
|||
|
ROUND_UP(KPRCB) +
|
|||
|
ROUND_UP(KNODE);
|
|||
|
|
|||
|
#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)(KeLoaderBlock->Prcb + ROUND_UP(KPRCB));
|
|||
|
*Node = KiNodeInit[NodeNumber];
|
|||
|
KeNodeBlock[NodeNumber] = Node;
|
|||
|
}
|
|||
|
|
|||
|
((PKPRCB)KeLoaderBlock->Prcb)->ParentNode = Node;
|
|||
|
|
|||
|
#else
|
|||
|
|
|||
|
((PKPRCB)KeLoaderBlock->Prcb)->ParentNode = KeNodeBlock[0];
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// 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 == FALSE) {
|
|||
|
ExFreePool((PVOID)MemoryBlock1);
|
|||
|
ExFreePool((PVOID)MemoryBlock2);
|
|||
|
break;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Wait until boot is finished on the target processor before
|
|||
|
// starting the next processor. Booting is considered to be
|
|||
|
// finished when a processor completes its initialization and
|
|||
|
// drops into the idle loop.
|
|||
|
//
|
|||
|
|
|||
|
Prcb = (PKPRCB)(KeLoaderBlock->Prcb);
|
|||
|
RestartBlock = Prcb->RestartBlock;
|
|||
|
while (RestartBlock->BootStatus.BootFinished == 0) {
|
|||
|
KiMb();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Number += 1;
|
|||
|
}
|
|||
|
//
|
|||
|
//
|
|||
|
// All processors have been stated.
|
|||
|
//
|
|||
|
|
|||
|
KiAllProcessorsStarted();
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// Reset and synchronize the performance counters of all processors, by
|
|||
|
// applying a null adjustment to the interrupt time
|
|||
|
//
|
|||
|
|
|||
|
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
|