650 lines
15 KiB
C
650 lines
15 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1990 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
initkr.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains the code to initialize the kernel data structures
|
|||
|
and to initialize the idle thread, its process, and the processor control
|
|||
|
block.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
David N. Cutler (davec) 11-Apr-1990
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode only.
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
Joe Notarangelo 21-April-1992
|
|||
|
very minor changes for ALPHA
|
|||
|
- system time to 64bit integer
|
|||
|
- some data moved out of pcr
|
|||
|
--*/
|
|||
|
|
|||
|
#include "ki.h"
|
|||
|
|
|||
|
//
|
|||
|
// Define forward referenced prototypes.
|
|||
|
//
|
|||
|
|
|||
|
ULONG
|
|||
|
KiGetFeatureBits (
|
|||
|
VOID
|
|||
|
);
|
|||
|
|
|||
|
__inline
|
|||
|
ULONG
|
|||
|
amask(
|
|||
|
IN ULONG Feature
|
|||
|
)
|
|||
|
|
|||
|
{
|
|||
|
return(__asm("amask %0, v0",Feature));
|
|||
|
}
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(INIT, KiInitializeKernel)
|
|||
|
#endif
|
|||
|
|
|||
|
VOID
|
|||
|
KiInitializeKernel (
|
|||
|
IN PKPROCESS Process,
|
|||
|
IN PKTHREAD Thread,
|
|||
|
IN PVOID IdleStack,
|
|||
|
IN PKPRCB Prcb,
|
|||
|
IN CCHAR Number,
|
|||
|
IN PLOADER_PARAMETER_BLOCK LoaderBlock
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function gains control after the system has been bootstrapped and
|
|||
|
before the system has been initialized. Its function is to initialize
|
|||
|
the kernel data structures, initialize the idle thread and process objects,
|
|||
|
initialize the processor control block, call the executive initialization
|
|||
|
routine, and then return to the system startup routine. This routine is
|
|||
|
also called to initialize the processor specific structures when a new
|
|||
|
processor is brought on line.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Process - Supplies a pointer to a control object of type process for
|
|||
|
the specified processor.
|
|||
|
|
|||
|
Thread - Supplies a pointer to a dispatcher object of type thread for
|
|||
|
the specified processor.
|
|||
|
|
|||
|
IdleStack - Supplies a pointer the base of the real kernel stack for
|
|||
|
idle thread on the specified processor.
|
|||
|
|
|||
|
Prcb - Supplies a pointer to a processor control block for the specified
|
|||
|
processor.
|
|||
|
|
|||
|
Number - Supplies the number of the processor that is being
|
|||
|
initialized.
|
|||
|
|
|||
|
LoaderBlock - Supplies a pointer to the loader parameter block.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
UCHAR DataByte;
|
|||
|
ULONG DataLong;
|
|||
|
LONG Index;
|
|||
|
KIRQL OldIrql;
|
|||
|
PKPCR Pcr = PCR;
|
|||
|
struct _RESTART_BLOCK *RestartBlock;
|
|||
|
|
|||
|
//
|
|||
|
// Save the address of the loader parameter block.
|
|||
|
//
|
|||
|
|
|||
|
KeLoaderBlock = LoaderBlock;
|
|||
|
|
|||
|
//
|
|||
|
// Set the appropriate member in the active processors set.
|
|||
|
//
|
|||
|
|
|||
|
SetMember(Number, KeActiveProcessors);
|
|||
|
|
|||
|
//
|
|||
|
// Set the number of processors based on the maximum of the current
|
|||
|
// number of processors and the current processor number.
|
|||
|
//
|
|||
|
|
|||
|
if ((Number + 1) > KeNumberProcessors) {
|
|||
|
KeNumberProcessors = Number + 1;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the maximum address space number to the minimum of all maximum
|
|||
|
// address space numbers passed via the loader block.
|
|||
|
//
|
|||
|
|
|||
|
if (Number == 0) {
|
|||
|
KiMaximumAsn = LoaderBlock->u.Alpha.MaximumAddressSpaceNumber;
|
|||
|
|
|||
|
} else if (KiMaximumAsn > LoaderBlock->u.Alpha.MaximumAddressSpaceNumber) {
|
|||
|
KiMaximumAsn = LoaderBlock->u.Alpha.MaximumAddressSpaceNumber;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the passive release, APC, and DPC interrupt vectors.
|
|||
|
//
|
|||
|
|
|||
|
Pcr->InterruptRoutine[0] = KiPassiveRelease;
|
|||
|
Pcr->InterruptRoutine[APC_LEVEL] = KiApcInterrupt;
|
|||
|
Pcr->InterruptRoutine[DISPATCH_LEVEL] = KiDispatchInterrupt;
|
|||
|
Pcr->ReservedVectors =
|
|||
|
(1 << PASSIVE_LEVEL) | (1 << APC_LEVEL) | (1 << DISPATCH_LEVEL);
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the processor id fields in the PCR.
|
|||
|
//
|
|||
|
|
|||
|
Pcr->Number = Number;
|
|||
|
Pcr->SetMember = 1 << Number;
|
|||
|
Pcr->NotMember = ~Pcr->SetMember;
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the processor block.
|
|||
|
//
|
|||
|
|
|||
|
Prcb->MinorVersion = PRCB_MINOR_VERSION;
|
|||
|
Prcb->MajorVersion = PRCB_MAJOR_VERSION;
|
|||
|
Prcb->BuildType = 0;
|
|||
|
|
|||
|
#if DBG
|
|||
|
|
|||
|
Prcb->BuildType |= PRCB_BUILD_DEBUG;
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
#ifdef NT_UP
|
|||
|
|
|||
|
Prcb->BuildType |= PRCB_BUILD_UNIPROCESSOR;
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
Prcb->CurrentThread = Thread;
|
|||
|
Prcb->NextThread = (PKTHREAD)NULL;
|
|||
|
Prcb->IdleThread = Thread;
|
|||
|
Prcb->Number = Number;
|
|||
|
Prcb->SetMember = 1 << Number;
|
|||
|
|
|||
|
KeInitializeDpc(&Prcb->QuantumEndDpc,
|
|||
|
(PKDEFERRED_ROUTINE)KiQuantumEnd,
|
|||
|
NIL);
|
|||
|
|
|||
|
//
|
|||
|
// initialize the per processor lock queue entry for implemented locks.
|
|||
|
//
|
|||
|
|
|||
|
KiInitQueuedSpinLocks(Prcb, Number);
|
|||
|
|
|||
|
//
|
|||
|
// Set address of PCR in PRCB.
|
|||
|
//
|
|||
|
|
|||
|
Prcb->Pcr = Pcr;
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the interprocessor communication packet.
|
|||
|
//
|
|||
|
|
|||
|
#if !defined(NT_UP)
|
|||
|
|
|||
|
Prcb->TargetSet = 0;
|
|||
|
Prcb->WorkerRoutine = NULL;
|
|||
|
Prcb->RequestSummary = 0;
|
|||
|
Prcb->IpiFrozen = 0;
|
|||
|
|
|||
|
#if NT_INST
|
|||
|
|
|||
|
Prcb->IpiCounts = &KiIpiCounts[Number];
|
|||
|
|
|||
|
#endif //NT_INST
|
|||
|
|
|||
|
#endif //NT_UP
|
|||
|
|
|||
|
Prcb->MaximumDpcQueueDepth = KiMaximumDpcQueueDepth;
|
|||
|
Prcb->MinimumDpcRate = KiMinimumDpcRate;
|
|||
|
Prcb->AdjustDpcThreshold = KiAdjustDpcThreshold;
|
|||
|
|
|||
|
//
|
|||
|
// Initialize DPC listhead and lock.
|
|||
|
//
|
|||
|
|
|||
|
InitializeListHead(&Prcb->DpcListHead);
|
|||
|
KeInitializeSpinLock(&Prcb->DpcLock);
|
|||
|
|
|||
|
//
|
|||
|
// Set address of processor block.
|
|||
|
//
|
|||
|
|
|||
|
KiProcessorBlock[Number] = Prcb;
|
|||
|
|
|||
|
//
|
|||
|
// Set address of process object in thread object.
|
|||
|
//
|
|||
|
|
|||
|
Thread->ApcState.Process = Process;
|
|||
|
|
|||
|
//
|
|||
|
// Set the appropriate member in the active processors set.
|
|||
|
//
|
|||
|
|
|||
|
SetMember( Number, KeActiveProcessors );
|
|||
|
|
|||
|
//
|
|||
|
// Set the number of processors based on the maximum of the current
|
|||
|
// number of processors and the current processor number.
|
|||
|
//
|
|||
|
|
|||
|
if( (Number+1) > KeNumberProcessors ){
|
|||
|
KeNumberProcessors = Number + 1;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Initialize processors PowerState
|
|||
|
//
|
|||
|
|
|||
|
PoInitializePrcb (Prcb);
|
|||
|
|
|||
|
//
|
|||
|
// Set global processor architecture, level and revision. The
|
|||
|
// latter two are the least common denominator on an MP system.
|
|||
|
//
|
|||
|
|
|||
|
#ifdef _AXP64_
|
|||
|
KeProcessorArchitecture = PROCESSOR_ARCHITECTURE_ALPHA64;
|
|||
|
#else
|
|||
|
KeProcessorArchitecture = PROCESSOR_ARCHITECTURE_ALPHA;
|
|||
|
#endif
|
|||
|
|
|||
|
if ((KeProcessorLevel == 0) ||
|
|||
|
(KeProcessorLevel > (USHORT)Pcr->ProcessorType)) {
|
|||
|
KeProcessorLevel = (USHORT)Pcr->ProcessorType;
|
|||
|
}
|
|||
|
|
|||
|
if ((KeProcessorRevision == 0) ||
|
|||
|
(KeProcessorRevision > (USHORT)Pcr->ProcessorRevision)) {
|
|||
|
KeProcessorRevision = (USHORT)Pcr->ProcessorRevision;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Initialize all interrupt vectors to transfer control to the unexpected
|
|||
|
// interrupt routine.
|
|||
|
//
|
|||
|
// N.B. This interrupt object is never actually "connected" to an interrupt
|
|||
|
// vector via KeConnectInterrupt. It is initialized and then connected
|
|||
|
// by simply storing the address of the dispatch code in the interrupt
|
|||
|
// vector.
|
|||
|
//
|
|||
|
|
|||
|
if (Number == 0) {
|
|||
|
|
|||
|
//
|
|||
|
// Set default node. Used in non-multinode systems and in
|
|||
|
// multinode systems until the node topology is available.
|
|||
|
//
|
|||
|
|
|||
|
extern KNODE KiNode0;
|
|||
|
|
|||
|
KeNodeBlock[0] = &KiNode0;
|
|||
|
|
|||
|
#if defined(KE_MULTINODE)
|
|||
|
|
|||
|
for (Index = 1; Index < MAXIMUM_CCNUMA_NODES; Index++) {
|
|||
|
|
|||
|
extern KNODE KiNodeInit[];
|
|||
|
|
|||
|
//
|
|||
|
// Set temporary node.
|
|||
|
//
|
|||
|
|
|||
|
KeNodeBlock[Index] = &KiNodeInit[Index];
|
|||
|
}
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
Prcb->ParentNode = KeNodeBlock[0];
|
|||
|
KeNodeBlock[0]->ProcessorMask = Prcb->SetMember;
|
|||
|
|
|||
|
KeFeatureBits = KiGetFeatureBits();
|
|||
|
|
|||
|
//
|
|||
|
// Initial the address of the interrupt dispatch routine.
|
|||
|
//
|
|||
|
|
|||
|
KxUnexpectedInterrupt.DispatchAddress = KiUnexpectedInterrupt;
|
|||
|
|
|||
|
//
|
|||
|
// Copy the interrupt dispatch code template into the interrupt object
|
|||
|
// and flush the dcache on all processors that the current thread can
|
|||
|
// run on to ensure that the code is actually in memory.
|
|||
|
//
|
|||
|
|
|||
|
for (Index = 0; Index < DISPATCH_LENGTH; Index += 1) {
|
|||
|
KxUnexpectedInterrupt.DispatchCode[Index] = KiInterruptTemplate[Index];
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Sweep the instruction cache on the current processor.
|
|||
|
//
|
|||
|
|
|||
|
KiImb();
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Mask off feature bits that are not supported on all processors.
|
|||
|
//
|
|||
|
|
|||
|
KeFeatureBits &= KiGetFeatureBits();
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Update processor features
|
|||
|
//
|
|||
|
|
|||
|
SharedUserData->ProcessorFeatures[PF_ALPHA_BYTE_INSTRUCTIONS] =
|
|||
|
(KeFeatureBits & KF_BYTE) ? TRUE : FALSE;
|
|||
|
|
|||
|
for (Index = DISPATCH_LEVEL+1; Index < MAXIMUM_VECTOR; Index += 1) {
|
|||
|
Pcr->InterruptRoutine[Index] = (PKINTERRUPT_ROUTINE)(&KxUnexpectedInterrupt.DispatchCode);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Raise IRQL to APC level.
|
|||
|
//
|
|||
|
|
|||
|
KeRaiseIrql(APC_LEVEL, &OldIrql);
|
|||
|
|
|||
|
//
|
|||
|
// If the initial processor is being initialized, then initialize the
|
|||
|
// per system data structures.
|
|||
|
//
|
|||
|
|
|||
|
if (Number == 0) {
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the address of the restart block for the boot master.
|
|||
|
//
|
|||
|
|
|||
|
Prcb->RestartBlock = SYSTEM_BLOCK->RestartBlock;
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the kernel debugger if enabled by the load options.
|
|||
|
//
|
|||
|
|
|||
|
if (KdInitSystem(0, LoaderBlock, FALSE) == FALSE) {
|
|||
|
KeBugCheck(PHASE0_INITIALIZATION_FAILED);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Initialize processor block array.
|
|||
|
//
|
|||
|
|
|||
|
for (Index = 1; Index < MAXIMUM_PROCESSORS; Index += 1) {
|
|||
|
KiProcessorBlock[Index] = (PKPRCB)NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Initialize default DMA coherency value for Alpha.
|
|||
|
//
|
|||
|
|
|||
|
KiDmaIoCoherency = DMA_READ_DCACHE_INVALIDATE | DMA_WRITE_DCACHE_SNOOP;
|
|||
|
|
|||
|
//
|
|||
|
// Perform architecture independent initialization.
|
|||
|
//
|
|||
|
|
|||
|
KiInitSystem();
|
|||
|
|
|||
|
//
|
|||
|
// Initialize idle thread process object and then set:
|
|||
|
//
|
|||
|
// 1. all the quantum values to the maximum possible.
|
|||
|
// 2. the process in the balance set.
|
|||
|
// 3. the active processor mask to the specified processor.
|
|||
|
//
|
|||
|
|
|||
|
KeInitializeProcess(Process,
|
|||
|
(KPRIORITY)0,
|
|||
|
(KAFFINITY)(0xffffffff),
|
|||
|
(PULONG_PTR)PDE_SELFMAP,
|
|||
|
FALSE);
|
|||
|
|
|||
|
Process->ThreadQuantum = MAXCHAR;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Initialize idle thread object and then set:
|
|||
|
//
|
|||
|
// 1. the initial kernel stack to the specified idle stack.
|
|||
|
// 2. the next processor number to the specified processor.
|
|||
|
// 3. the thread priority to the highest possible value.
|
|||
|
// 4. the state of the thread to running.
|
|||
|
// 5. the thread affinity to the specified processor.
|
|||
|
// 6. the specified processor member in the process active processors
|
|||
|
// set.
|
|||
|
//
|
|||
|
|
|||
|
KeInitializeThread(Thread,
|
|||
|
(PVOID)((ULONG_PTR)IdleStack - PAGE_SIZE),
|
|||
|
(PKSYSTEM_ROUTINE)NULL,
|
|||
|
(PKSTART_ROUTINE)NULL,
|
|||
|
(PVOID)NULL,
|
|||
|
(PCONTEXT)NULL,
|
|||
|
(PVOID)NULL,
|
|||
|
Process);
|
|||
|
|
|||
|
Thread->InitialStack = IdleStack;
|
|||
|
Thread->StackBase = IdleStack;
|
|||
|
Thread->StackLimit = (PVOID)((ULONG_PTR)IdleStack - KERNEL_STACK_SIZE);
|
|||
|
Thread->NextProcessor = Number;
|
|||
|
Thread->Priority = HIGH_PRIORITY;
|
|||
|
Thread->State = Running;
|
|||
|
Thread->Affinity = (KAFFINITY)(1 << Number);
|
|||
|
Thread->WaitIrql = DISPATCH_LEVEL;
|
|||
|
|
|||
|
//
|
|||
|
// If the current processor is the boot master then set the appropriate
|
|||
|
// bit in the active summary of the idle process.
|
|||
|
//
|
|||
|
|
|||
|
if (Number == 0) {
|
|||
|
SetMember(Number, Process->ActiveProcessors);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// call the executive initialization routine.
|
|||
|
//
|
|||
|
|
|||
|
try {
|
|||
|
ExpInitializeExecutive(Number, LoaderBlock);
|
|||
|
|
|||
|
} except(KeBugCheckEx(PHASE0_EXCEPTION,
|
|||
|
(ULONG)GetExceptionCode(),
|
|||
|
(ULONG_PTR)GetExceptionInformation(),
|
|||
|
0,0), EXCEPTION_EXECUTE_HANDLER) {
|
|||
|
; // should never get here
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the initial processor is being initialized, then compute the
|
|||
|
// timer table reciprocal value and reset the PRCB values for
|
|||
|
// the controllable DPC behavior in order to reflect any registry
|
|||
|
// overrides.
|
|||
|
//
|
|||
|
|
|||
|
if (Number == 0) {
|
|||
|
KiTimeIncrementReciprocal = KiComputeReciprocal((LONG)KeMaximumIncrement,
|
|||
|
&KiTimeIncrementShiftCount);
|
|||
|
|
|||
|
Prcb->MaximumDpcQueueDepth = KiMaximumDpcQueueDepth;
|
|||
|
Prcb->MinimumDpcRate = KiMinimumDpcRate;
|
|||
|
Prcb->AdjustDpcThreshold = KiAdjustDpcThreshold;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Try to enable automatic PAL code fixups on this processor.
|
|||
|
// This must be done after the configuration values are read
|
|||
|
// out of the registry in ExpInitializeExecutive.
|
|||
|
//
|
|||
|
|
|||
|
#if !defined(_AXP64_)
|
|||
|
|
|||
|
//
|
|||
|
// Don't let the PAL fix up alignment exceptions for axp64, so that we can catch
|
|||
|
// and fix them as they occur.
|
|||
|
//
|
|||
|
|
|||
|
KiDisableAlignmentExceptions();
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
//
|
|||
|
// Raise IRQL to dispatch level and set the priority of the idle thread
|
|||
|
// to zero. This will have the effect of immediately causing the phase
|
|||
|
// one initialization thread to get scheduled for execution. The idle
|
|||
|
// thread priority is then set to the lowest realtime priority.
|
|||
|
//
|
|||
|
|
|||
|
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
|
|||
|
KeSetPriorityThread(Thread, (KPRIORITY)0);
|
|||
|
Thread->Priority = LOW_REALTIME_PRIORITY;
|
|||
|
|
|||
|
//
|
|||
|
// Raise IRQL to the highest level.
|
|||
|
//
|
|||
|
|
|||
|
KeRaiseIrql(HIGH_LEVEL, &OldIrql);
|
|||
|
|
|||
|
//
|
|||
|
// If a restart block exists for the current processor then set boot
|
|||
|
// completed.
|
|||
|
//
|
|||
|
|
|||
|
#if !defined(NT_UP)
|
|||
|
|
|||
|
RestartBlock = Prcb->RestartBlock;
|
|||
|
if (RestartBlock != NULL) {
|
|||
|
RestartBlock->BootStatus.BootFinished = 1;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the current processor is a secondary processor and a thread has
|
|||
|
// not been selected for execution, then set the appropriate bit in the
|
|||
|
// idle summary.
|
|||
|
//
|
|||
|
|
|||
|
if ((Number != 0) && (Prcb->NextThread == NULL)) {
|
|||
|
SetMember(Number, KiIdleSummary);
|
|||
|
}
|
|||
|
|
|||
|
#endif //NT_UP
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
ULONG
|
|||
|
KiGetFeatureBits(
|
|||
|
VOID
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Return the NT feature bits supported by this processors
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
ULONG Features = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Check for byte instructions.
|
|||
|
//
|
|||
|
|
|||
|
if (amask(1) == 0) {
|
|||
|
Features |= KF_BYTE;
|
|||
|
}
|
|||
|
|
|||
|
return Features;
|
|||
|
}
|
|||
|
|
|||
|
#if defined(_AXP64_)
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
KiStartHalThread (
|
|||
|
IN PKTHREAD Thread,
|
|||
|
IN PVOID Stack,
|
|||
|
IN PKSTART_ROUTINE StartRoutine,
|
|||
|
IN ULONG_PTR Process
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function is called to initialize and start a thread from the
|
|||
|
HAL to emulate BIOS calls.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Thread - Supplies a pointer to a dispatcher object of type thread.
|
|||
|
|
|||
|
Stack - Supplies a pointer the base of the real kernel stack for
|
|||
|
thread.
|
|||
|
|
|||
|
StartRoutine - Supplies the address of the start routine.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the specified thread object.
|
|||
|
//
|
|||
|
|
|||
|
KeInitializeThread(Thread,
|
|||
|
Stack,
|
|||
|
(PKSYSTEM_ROUTINE)StartRoutine,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
(PKPROCESS)Process);
|
|||
|
|
|||
|
//
|
|||
|
// Set thread priority.
|
|||
|
//
|
|||
|
|
|||
|
Thread->Priority = (KPRIORITY) NORMAL_BASE_PRIORITY - 1;
|
|||
|
KeReadyThread(Thread);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
#endif
|