424 lines
9.5 KiB
C
424 lines
9.5 KiB
C
/*++
|
||
|
||
Copyright (c) 1990 Microsoft Corporation
|
||
Copyright (c) 1994 Motorola, IBM Corp.
|
||
|
||
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:
|
||
|
||
David N. Cutler 29-Apr-1993
|
||
Joe Notarangelo 30-Nov-1993
|
||
Pat Carr 16-Aug-1994
|
||
|
||
Environment:
|
||
|
||
Kernel mode only.
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
|
||
#include "ki.h"
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
|
||
#pragma alloc_text(INIT, KeStartAllProcessors)
|
||
|
||
#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 (3 * KERNEL_STACK_SIZE)
|
||
#define BLOCK2_SIZE (ROUND_UP(KPRCB) + ROUND_UP(ETHREAD) + 64)
|
||
|
||
//
|
||
// Define barrier wait static data.
|
||
//
|
||
|
||
#if !defined(NT_UP)
|
||
|
||
ULONG KiBarrierWait = 0;
|
||
|
||
#endif
|
||
|
||
#if !defined(NT_UP)
|
||
MEMORY_ALLOCATION_DESCRIPTOR KiFreePcrPagesDescriptor;
|
||
#endif
|
||
|
||
//
|
||
// Define forward referenced prototypes.
|
||
//
|
||
|
||
VOID
|
||
KiCalibratePerformanceCounter(
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
KiCalibratePerformanceCounterTarget (
|
||
IN PULONG SignalDone,
|
||
IN PVOID Count,
|
||
IN PVOID Parameter2,
|
||
IN PVOID Parameter3
|
||
);
|
||
|
||
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 MemoryBlock1;
|
||
ULONG MemoryBlock2;
|
||
ULONG Number;
|
||
ULONG PcrAddress;
|
||
ULONG PcrPage;
|
||
PKPRCB Prcb;
|
||
KPROCESSOR_STATE ProcessorState;
|
||
volatile PRESTART_BLOCK RestartBlock;
|
||
BOOLEAN Started;
|
||
PHYSICAL_ADDRESS PcrPhysicalAddress;
|
||
PMEMORY_ALLOCATION_DESCRIPTOR KiPcrPagesDescriptor = KeLoaderBlock->u.Ppc.PcrPagesDescriptor;
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
RtlZeroMemory(&ProcessorState, sizeof(KPROCESSOR_STATE));
|
||
ProcessorState.ContextFrame.Gpr3 = (ULONG)KeLoaderBlock;
|
||
ProcessorState.ContextFrame.Iar = *(PULONG)KiStartProcessor;
|
||
|
||
Number = 0;
|
||
|
||
while ((Number+1) < KeRegisteredProcessors) {
|
||
|
||
//
|
||
// 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 nonpaged pool, then stop starting processors.
|
||
//
|
||
|
||
if (Number >= KiPcrPagesDescriptor->PageCount) {
|
||
break;
|
||
}
|
||
|
||
MemoryBlock1 = (ULONG)ExAllocatePool(NonPagedPool, BLOCK1_SIZE);
|
||
if ((PVOID)MemoryBlock1 == NULL) {
|
||
break;
|
||
}
|
||
|
||
MemoryBlock2 = (ULONG)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);
|
||
|
||
//
|
||
// Set address of interrupt stack in loader parameter block.
|
||
//
|
||
|
||
KeLoaderBlock->u.Ppc.InterruptStack = MemoryBlock1 + (1 * KERNEL_STACK_SIZE);
|
||
|
||
//
|
||
// Set address of idle thread kernel stack in loader parameter block.
|
||
//
|
||
|
||
KeLoaderBlock->KernelStack = MemoryBlock1 + (2 * KERNEL_STACK_SIZE);
|
||
|
||
ProcessorState.ContextFrame.Gpr1 = (ULONG)KeLoaderBlock->KernelStack;
|
||
|
||
//
|
||
// Set address of panic stack in loader parameter block.
|
||
//
|
||
|
||
KeLoaderBlock->u.Ppc.PanicStack = MemoryBlock1 + (3 * KERNEL_STACK_SIZE);
|
||
|
||
//
|
||
// Set the page frame of the PCR page in the loader parameter block.
|
||
//
|
||
|
||
PcrPage = KiPcrPagesDescriptor->BasePage + Number;
|
||
PcrAddress = KSEG0_BASE | (PcrPage << PAGE_SHIFT);
|
||
RtlZeroMemory((PVOID)PcrAddress, PAGE_SIZE);
|
||
ProcessorState.ContextFrame.Gpr4 = PcrAddress;
|
||
KeLoaderBlock->u.Ppc.PcrPage = PcrPage;
|
||
|
||
//
|
||
// Copy the physical address of the PCR2 page from the current
|
||
// processor's PCR into the loader parameter block for the new
|
||
// processor.
|
||
//
|
||
// Note that in the PCR this is an address rather than a page
|
||
// number.
|
||
//
|
||
|
||
KeLoaderBlock->u.Ppc.PcrPage2 = PCR->PcrPage2 >> PAGE_SHIFT;
|
||
|
||
//
|
||
// Set the address of the processor block and executive thread in the
|
||
// loader parameter block.
|
||
//
|
||
|
||
KeLoaderBlock->Prcb = (MemoryBlock2 + 63) & ~63;
|
||
KeLoaderBlock->Thread = KeLoaderBlock->Prcb + ROUND_UP(KPRCB);
|
||
|
||
//
|
||
// 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) {
|
||
}
|
||
}
|
||
|
||
Number += 1;
|
||
|
||
}
|
||
|
||
//
|
||
// Allow all processor that were started to enter the idle loop and
|
||
// begin execution.
|
||
//
|
||
|
||
KiBarrierWait = 0;
|
||
|
||
if ( Number < KiPcrPagesDescriptor->PageCount ) {
|
||
if ( Number == 0 ) {
|
||
KiPcrPagesDescriptor->MemoryType = LoaderOsloaderHeap;
|
||
} else {
|
||
KiFreePcrPagesDescriptor.BasePage = KiPcrPagesDescriptor->BasePage + Number;
|
||
KiFreePcrPagesDescriptor.PageCount = KiPcrPagesDescriptor->PageCount - Number;
|
||
KiFreePcrPagesDescriptor.MemoryType = LoaderOsloaderHeap;
|
||
InsertTailList(&KeLoaderBlock->MemoryDescriptorListHead,
|
||
&KiFreePcrPagesDescriptor.ListEntry);
|
||
}
|
||
}
|
||
|
||
#endif
|
||
|
||
//
|
||
// Reset and synchronize the performance counters of all processors.
|
||
//
|
||
|
||
KiCalibratePerformanceCounter();
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
KiCalibratePerformanceCounter(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function resets and synchronizes the performance counter on all
|
||
processors in the configuration.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
LONG Count = 1;
|
||
|
||
#if !defined(NT_UP)
|
||
|
||
KIRQL OldIrql;
|
||
KAFFINITY TargetProcessors;
|
||
|
||
ASSERT(KeGetCurrentIrql() <= SYNCH_LEVEL);
|
||
|
||
//
|
||
// Raise IRQL to synchronization level to avoid a possible context switch.
|
||
//
|
||
|
||
OldIrql = KeRaiseIrqlToSynchLevel();
|
||
|
||
//
|
||
// Initialize the reset performance counter packet, compute the target
|
||
// set of processors, and send the packet to the target processors, if
|
||
// any, for execution.
|
||
//
|
||
|
||
TargetProcessors = KeActiveProcessors & PCR->NotMember;
|
||
if (TargetProcessors != 0) {
|
||
Count = (LONG)KeNumberProcessors;
|
||
KiIpiSendPacket(TargetProcessors,
|
||
KiCalibratePerformanceCounterTarget,
|
||
(PVOID)&Count,
|
||
NULL,
|
||
NULL);
|
||
}
|
||
|
||
#endif
|
||
|
||
//
|
||
// Reset the performance counter on current processor.
|
||
//
|
||
|
||
HalCalibratePerformanceCounter((volatile PLONG)&Count);
|
||
|
||
//
|
||
// Wait until all target processors have reset and synchronized their
|
||
// performance counters.
|
||
//
|
||
|
||
#if !defined(NT_UP)
|
||
|
||
if (TargetProcessors != 0) {
|
||
KiIpiStallOnPacketTargets();
|
||
}
|
||
|
||
//
|
||
// Lower IRQL to previous level.
|
||
//
|
||
|
||
KeLowerIrql(OldIrql);
|
||
|
||
#endif
|
||
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
KiCalibratePerformanceCounterTarget (
|
||
IN PULONG SignalDone,
|
||
IN PVOID Count,
|
||
IN PVOID Parameter2,
|
||
IN PVOID Parameter3
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the target function for reseting the performance counter.
|
||
|
||
Arguments:
|
||
|
||
SignalDone - Supplies a pointer to a variable that is cleared when the
|
||
requested operation has been performed.
|
||
|
||
Count - Supplies a pointer to the number of processors in the host
|
||
configuration.
|
||
|
||
Parameter2 - Parameter3 - Not used.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// Reset and synchronize the perfromance counter on the current processor
|
||
// and clear the reset performance counter address to signal the source to
|
||
// continue.
|
||
//
|
||
|
||
#if !defined(NT_UP)
|
||
|
||
HalCalibratePerformanceCounter((volatile PLONG)Count);
|
||
KiIpiSignalPacketDone(SignalDone);
|
||
|
||
#endif
|
||
|
||
return;
|
||
}
|