|
|
/*++
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
|