mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1581 lines
37 KiB
1581 lines
37 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
kernlini.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.
|
|
|
|
For the i386, it also contains code to initialize the PCR.
|
|
|
|
Author:
|
|
|
|
David N. Cutler (davec) 21-Apr-1989
|
|
|
|
Environment:
|
|
|
|
Kernel mode only.
|
|
|
|
Revision History:
|
|
|
|
24-Jan-1990 shielin
|
|
|
|
Changed for NT386
|
|
|
|
20-Mar-1990 bryanwi
|
|
|
|
Added KiInitializePcr
|
|
|
|
--*/
|
|
|
|
#include "ki.h"
|
|
#include "ki386.h"
|
|
|
|
#define TRAP332_GATE 0xEF00
|
|
|
|
VOID
|
|
KiSetProcessorType(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
KiSetCR0Bits(
|
|
VOID
|
|
);
|
|
|
|
BOOLEAN
|
|
KiIsNpxPresent(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
KiInitializeDblFaultTSS(
|
|
IN PKTSS Tss,
|
|
IN ULONG Stack,
|
|
IN PKGDTENTRY TssDescriptor
|
|
);
|
|
|
|
VOID
|
|
KiInitializeTSS2 (
|
|
IN PKTSS Tss,
|
|
IN PKGDTENTRY TssDescriptor
|
|
);
|
|
|
|
VOID
|
|
KiSwapIDT (
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
KeSetup80387OrEmulate (
|
|
IN PVOID *R3EmulatorTable
|
|
);
|
|
|
|
ULONG
|
|
KiGetFeatureBits (
|
|
VOID
|
|
);
|
|
|
|
NTSTATUS
|
|
KiMoveRegTree(
|
|
HANDLE Source,
|
|
HANDLE Dest
|
|
);
|
|
|
|
VOID
|
|
Ki386EnableGlobalPage (
|
|
IN volatile PLONG Number
|
|
);
|
|
|
|
BOOLEAN
|
|
KiInitMachineDependent (
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
KiInitializeMTRR (
|
|
IN BOOLEAN LastProcessor
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT,KiInitializeKernel)
|
|
#pragma alloc_text(INIT,KiInitializePcr)
|
|
#pragma alloc_text(INIT,KiInitializeDblFaultTSS)
|
|
#pragma alloc_text(INIT,KiInitializeTSS2)
|
|
#pragma alloc_text(INIT,KiSwapIDT)
|
|
#pragma alloc_text(INIT,KeSetup80387OrEmulate)
|
|
#pragma alloc_text(INIT,KiGetFeatureBits)
|
|
#pragma alloc_text(INIT,KiMoveRegTree)
|
|
#pragma alloc_text(INIT,KiInitMachineDependent)
|
|
#endif
|
|
|
|
|
|
#if 0
|
|
PVOID KiTrap08;
|
|
#endif
|
|
|
|
extern PVOID Ki387RoundModeTable;
|
|
extern PVOID Ki386IopmSaveArea;
|
|
extern ULONG KeI386ForceNpxEmulation;
|
|
extern WCHAR CmDisabledFloatingPointProcessor[];
|
|
extern UCHAR CmpCyrixID[];
|
|
|
|
#define CPU_NONE 0
|
|
#define CPU_INTEL 1
|
|
#define CPU_AMD 2
|
|
#define CPU_CYRIX 3
|
|
|
|
|
|
|
|
|
|
//
|
|
// Profile vars
|
|
//
|
|
|
|
extern KIDTENTRY IDT[];
|
|
|
|
VOID
|
|
KiInitializeKernel (
|
|
IN PKPROCESS Process,
|
|
IN PKTHREAD Thread,
|
|
IN PVOID IdleStack,
|
|
IN PKPRCB Prcb,
|
|
IN CCHAR Number,
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
#define INITIAL_KERNEL_STACK_SIZE (((sizeof(FLOATING_SAVE_AREA)+KTRAP_FRAME_LENGTH+KTRAP_FRAME_ROUND) & ~KTRAP_FRAME_ROUND)/sizeof(ULONG))+1
|
|
|
|
ULONG KernelStack[INITIAL_KERNEL_STACK_SIZE];
|
|
LONG Index;
|
|
ULONG DirectoryTableBase[2];
|
|
KIRQL OldIrql;
|
|
PKPCR Pcr;
|
|
BOOLEAN NpxFlag;
|
|
ULONG FeatureBits;
|
|
|
|
KiSetProcessorType();
|
|
KiSetCR0Bits();
|
|
NpxFlag = KiIsNpxPresent();
|
|
|
|
Pcr = KeGetPcr();
|
|
|
|
//
|
|
// Initialize DPC listhead and lock.
|
|
//
|
|
|
|
InitializeListHead(&Prcb->DpcListHead);
|
|
KeInitializeSpinLock(&Prcb->DpcLock);
|
|
Prcb->DpcRoutineActive = 0;
|
|
Prcb->DpcQueueDepth = 0;
|
|
Prcb->MaximumDpcQueueDepth = KiMaximumDpcQueueDepth;
|
|
Prcb->MinimumDpcRate = KiMinimumDpcRate;
|
|
Prcb->AdjustDpcThreshold = KiAdjustDpcThreshold;
|
|
|
|
//
|
|
// Check for unsupported processor revision
|
|
//
|
|
|
|
if (Prcb->CpuType == 3) {
|
|
KeBugCheckEx(UNSUPPORTED_PROCESSOR,0x386,0,0,0);
|
|
}
|
|
|
|
//
|
|
// If the initial processor is being initialized, then initialize the
|
|
// per system data structures.
|
|
//
|
|
|
|
if (Number == 0) {
|
|
|
|
//
|
|
// Initial setting for global Cpu & Stepping levels
|
|
//
|
|
|
|
KeI386NpxPresent = NpxFlag;
|
|
KeI386CpuType = Prcb->CpuType;
|
|
KeI386CpuStep = Prcb->CpuStep;
|
|
|
|
KeProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL;
|
|
KeProcessorLevel = (USHORT)Prcb->CpuType;
|
|
if (Prcb->CpuID == 0) {
|
|
KeProcessorRevision = 0xFF00 |
|
|
(((Prcb->CpuStep >> 4) + 0xa0 ) & 0x0F0) |
|
|
(Prcb->CpuStep & 0xf);
|
|
} else {
|
|
KeProcessorRevision = Prcb->CpuStep;
|
|
}
|
|
|
|
KeFeatureBits = KiGetFeatureBits();
|
|
|
|
//
|
|
// If cmpxchg8b was available at boot, verify its still available
|
|
//
|
|
|
|
if ((KiBootFeatureBits & KF_CMPXCHG8B) && !(KeFeatureBits & KF_CMPXCHG8B)) {
|
|
KeBugCheckEx (MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED, KF_CMPXCHG8B, 0, 0, 0);
|
|
}
|
|
|
|
//
|
|
// Lower IRQL to APC level.
|
|
//
|
|
|
|
KeLowerIrql(APC_LEVEL);
|
|
|
|
|
|
//
|
|
// Initialize kernel internal spinlocks
|
|
//
|
|
|
|
KeInitializeSpinLock(&KiContextSwapLock);
|
|
KeInitializeSpinLock(&KiDispatcherLock);
|
|
KeInitializeSpinLock(&KiFreezeExecutionLock);
|
|
|
|
|
|
//
|
|
// Performance 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 process.
|
|
//
|
|
|
|
DirectoryTableBase[0] = 0;
|
|
DirectoryTableBase[1] = 0;
|
|
KeInitializeProcess(Process,
|
|
(KPRIORITY)0,
|
|
(KAFFINITY)(0xffffffff),
|
|
&DirectoryTableBase[0],
|
|
FALSE);
|
|
|
|
Process->ThreadQuantum = MAXCHAR;
|
|
|
|
} else {
|
|
|
|
FeatureBits = KiGetFeatureBits();
|
|
|
|
//
|
|
// Adjust global cpu setting to represent lowest of all processors
|
|
//
|
|
|
|
if (NpxFlag != KeI386NpxPresent) {
|
|
//
|
|
// NPX support must be available on all processors or on none
|
|
//
|
|
|
|
KeBugCheckEx (MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED, 0x387, 0, 0, 0);
|
|
}
|
|
|
|
if ((ULONG)(Prcb->CpuType) != KeI386CpuType) {
|
|
|
|
if ((ULONG)(Prcb->CpuType) < KeI386CpuType) {
|
|
|
|
//
|
|
// What is the lowest CPU type
|
|
//
|
|
|
|
KeI386CpuType = (ULONG)Prcb->CpuType;
|
|
KeProcessorLevel = (USHORT)Prcb->CpuType;
|
|
}
|
|
}
|
|
|
|
if ((KiBootFeatureBits & KF_CMPXCHG8B) && !(FeatureBits & KF_CMPXCHG8B)) {
|
|
//
|
|
// cmpxchg8b must be available on all processors, if installed at boot
|
|
//
|
|
|
|
KeBugCheckEx (MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED, KF_CMPXCHG8B, 0, 0, 0);
|
|
}
|
|
|
|
if ((KeFeatureBits & KF_GLOBAL_PAGE) && !(FeatureBits & KF_GLOBAL_PAGE)) {
|
|
//
|
|
// Global page support must be available on all processors, if on boot processor
|
|
//
|
|
|
|
KeBugCheckEx (MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED, KF_GLOBAL_PAGE, 0, 0, 0);
|
|
}
|
|
|
|
//
|
|
// Use lowest stepping value
|
|
//
|
|
|
|
if (Prcb->CpuStep < KeI386CpuStep) {
|
|
KeI386CpuStep = Prcb->CpuStep;
|
|
if (Prcb->CpuID == 0) {
|
|
KeProcessorRevision = 0xFF00 |
|
|
((Prcb->CpuStep >> 8) + 'A') |
|
|
(Prcb->CpuStep & 0xf);
|
|
} else {
|
|
KeProcessorRevision = Prcb->CpuStep;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Use subset of all NT feature bits available on each processor
|
|
//
|
|
|
|
KeFeatureBits &= FeatureBits;
|
|
|
|
//
|
|
// Lower IRQL to DISPATCH level.
|
|
//
|
|
|
|
KeLowerIrql(DISPATCH_LEVEL);
|
|
|
|
}
|
|
|
|
//
|
|
// Update processor features
|
|
//
|
|
|
|
SharedUserData->ProcessorFeatures[PF_MMX_INSTRUCTIONS_AVAILABLE] =
|
|
(KeFeatureBits & KF_MMX) ? TRUE : FALSE;
|
|
|
|
SharedUserData->ProcessorFeatures[PF_COMPARE_EXCHANGE_DOUBLE] =
|
|
(KeFeatureBits & KF_CMPXCHG8B) ? TRUE : FALSE;
|
|
|
|
//
|
|
// 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)&KernelStack[INITIAL_KERNEL_STACK_SIZE],
|
|
(PKSYSTEM_ROUTINE)NULL, (PKSTART_ROUTINE)NULL,
|
|
(PVOID)NULL, (PCONTEXT)NULL, (PVOID)NULL, Process);
|
|
Thread->InitialStack = (PVOID)(((ULONG)IdleStack) &0xfffffff0);
|
|
Thread->StackBase = Thread->InitialStack;
|
|
Thread->StackLimit = (PVOID)((ULONG)Thread->InitialStack - KERNEL_STACK_SIZE);
|
|
Thread->NextProcessor = Number;
|
|
Thread->Priority = HIGH_PRIORITY;
|
|
Thread->State = Running;
|
|
Thread->Affinity = (KAFFINITY)(1<<Number);
|
|
Thread->WaitIrql = DISPATCH_LEVEL;
|
|
SetMember(Number, Process->ActiveProcessors);
|
|
|
|
//
|
|
// Initialize the processor block. (Note that some fields have been
|
|
// initialized at KiInitializePcr().
|
|
//
|
|
|
|
Prcb->CurrentThread = Thread;
|
|
Prcb->NextThread = (PKTHREAD)NULL;
|
|
Prcb->IdleThread = Thread;
|
|
Pcr->NtTib.StackBase = Thread->InitialStack;
|
|
|
|
//
|
|
// The following operations need to be done atomically. So we
|
|
// grab the DispatcherDatabase.
|
|
//
|
|
|
|
KiAcquireSpinLock(&KiDispatcherLock);
|
|
|
|
//
|
|
// Release DispatcherDatabase
|
|
//
|
|
|
|
KiReleaseSpinLock(&KiDispatcherLock);
|
|
|
|
//
|
|
// call the executive initialization routine.
|
|
//
|
|
|
|
try {
|
|
ExpInitializeExecutive(Number, LoaderBlock);
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
KeBugCheck (PHASE0_EXCEPTION);
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
//
|
|
// Allocate 8k IOPM bit map saved area to allow BiosCall swap
|
|
// bit maps.
|
|
//
|
|
|
|
if (Number == 0) {
|
|
Ki386IopmSaveArea = ExAllocatePool(PagedPool, PAGE_SIZE * 2);
|
|
if (Ki386IopmSaveArea == NULL) {
|
|
KeBugCheck(NO_PAGES_AVAILABLE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the priority of the specified idle thread to zero, set appropriate
|
|
// member in KiIdleSummary and return to the system start up routine.
|
|
//
|
|
|
|
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
|
|
KeSetPriorityThread(Thread, (KPRIORITY)0);
|
|
|
|
//
|
|
// if a thread has not been selected to run on the current processors,
|
|
// check to see if there are any ready threads; otherwise add this
|
|
// processors to the IdleSummary
|
|
//
|
|
|
|
KiAcquireSpinLock(&KiDispatcherLock);
|
|
if (Prcb->NextThread == (PKTHREAD)NULL) {
|
|
SetMember(Number, KiIdleSummary);
|
|
}
|
|
KiReleaseSpinLock(&KiDispatcherLock);
|
|
|
|
KeRaiseIrql(HIGH_LEVEL, &OldIrql);
|
|
|
|
//
|
|
// This processor has initialized
|
|
//
|
|
|
|
LoaderBlock->Prcb = (ULONG)NULL;
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
KiInitializePcr (
|
|
IN ULONG Processor,
|
|
IN PKPCR Pcr,
|
|
IN PKIDTENTRY Idt,
|
|
IN PKGDTENTRY Gdt,
|
|
IN PKTSS Tss,
|
|
IN PKTHREAD Thread
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called to initialize the PCR for a processor. It
|
|
simply stuffs values into the PCR. (The PCR is not inited statically
|
|
because the number varies with the number of processors.)
|
|
|
|
Note that each processor has its own IDT, GDT, and TSS as well as PCR!
|
|
|
|
Arguments:
|
|
|
|
Processor - Processor whoes Pcr to initialize.
|
|
|
|
Pcr - Linear address of PCR.
|
|
|
|
Idt - Linear address of i386 IDT.
|
|
|
|
Gdt - Linear address of i386 GDT.
|
|
|
|
Tss - Linear address (NOT SELECTOR!) of the i386 TSS.
|
|
|
|
Thread - Dummy thread object to use very early on.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
// set version values
|
|
|
|
Pcr->MajorVersion = PCR_MAJOR_VERSION;
|
|
Pcr->MinorVersion = PCR_MINOR_VERSION;
|
|
|
|
Pcr->PrcbData.MajorVersion = PRCB_MAJOR_VERSION;
|
|
Pcr->PrcbData.MinorVersion = PRCB_MINOR_VERSION;
|
|
|
|
Pcr->PrcbData.BuildType = 0;
|
|
|
|
#if DBG
|
|
Pcr->PrcbData.BuildType |= PRCB_BUILD_DEBUG;
|
|
#endif
|
|
|
|
#ifdef NT_UP
|
|
Pcr->PrcbData.BuildType |= PRCB_BUILD_UNIPROCESSOR;
|
|
#endif
|
|
|
|
// Basic addressing fields
|
|
|
|
Pcr->SelfPcr = Pcr;
|
|
Pcr->Prcb = &(Pcr->PrcbData);
|
|
|
|
// Thread control fields
|
|
|
|
Pcr->NtTib.ExceptionList = EXCEPTION_CHAIN_END;
|
|
Pcr->NtTib.StackBase = 0;
|
|
Pcr->NtTib.StackLimit = 0;
|
|
Pcr->NtTib.Self = 0;
|
|
|
|
Pcr->PrcbData.CurrentThread = Thread;
|
|
|
|
//
|
|
// Init Prcb.Number and ProcessorBlock such that Ipi will work
|
|
// as early as possible.
|
|
//
|
|
|
|
Pcr->PrcbData.Number = (UCHAR)Processor;
|
|
Pcr->PrcbData.SetMember = 1 << Processor;
|
|
KiProcessorBlock[Processor] = Pcr->Prcb;
|
|
|
|
Pcr->Irql = 0;
|
|
|
|
// Machine structure addresses
|
|
|
|
Pcr->GDT = Gdt;
|
|
Pcr->IDT = Idt;
|
|
Pcr->TSS = Tss;
|
|
|
|
return;
|
|
}
|
|
|
|
#if 0
|
|
VOID
|
|
KiInitializeDblFaultTSS(
|
|
IN PKTSS Tss,
|
|
IN ULONG Stack,
|
|
IN PKGDTENTRY TssDescriptor
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called to initialize the double-fault TSS for a
|
|
processor. It will set the static fields of the TSS to point to
|
|
the double-fault handler and the appropriate double-fault stack.
|
|
|
|
Note that the IOPM for the double-fault TSS grants access to all
|
|
ports. This is so the standard HAL's V86-mode callback to reset
|
|
the display to text mode will work.
|
|
|
|
Arguments:
|
|
|
|
Tss - Supplies a pointer to the double-fault TSS
|
|
|
|
Stack - Supplies a pointer to the double-fault stack.
|
|
|
|
TssDescriptor - Linear address of the descriptor for the TSS.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PUCHAR p;
|
|
ULONG i;
|
|
ULONG j;
|
|
|
|
//
|
|
// Set limit for TSS
|
|
//
|
|
|
|
if (TssDescriptor != NULL) {
|
|
TssDescriptor->LimitLow = sizeof(KTSS) - 1;
|
|
TssDescriptor->HighWord.Bits.LimitHi = 0;
|
|
}
|
|
|
|
//
|
|
// Initialize IOPMs
|
|
//
|
|
|
|
for (i = 0; i < IOPM_COUNT; i++) {
|
|
p = (PUCHAR)(Tss->IoMaps[i]);
|
|
|
|
for (j = 0; j < PIOPM_SIZE; j++) {
|
|
p[j] = 0;
|
|
}
|
|
}
|
|
|
|
// Set IO Map base address to indicate no IO map present.
|
|
|
|
// N.B. -1 does not seem to be a valid value for the map base. If this
|
|
// value is used, byte immediate in's and out's will actually go
|
|
// the hardware when executed in V86 mode.
|
|
|
|
Tss->IoMapBase = KiComputeIopmOffset(IO_ACCESS_MAP_NONE);
|
|
|
|
// Set flags to 0, which in particular dispables traps on task switches.
|
|
|
|
Tss->Flags = 0;
|
|
|
|
|
|
// Set LDT and Ss0 to constants used by NT.
|
|
|
|
Tss->LDT = 0;
|
|
Tss->Ss0 = KGDT_R0_DATA;
|
|
Tss->Esp0 = Stack;
|
|
Tss->Eip = (ULONG)KiTrap08;
|
|
Tss->Cs = KGDT_R0_CODE || RPL_MASK;
|
|
Tss->Ds = KGDT_R0_DATA;
|
|
Tss->Es = KGDT_R0_DATA;
|
|
Tss->Fs = KGDT_R0_DATA;
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
#endif
|
|
|
|
|
|
VOID
|
|
KiInitializeTSS (
|
|
IN PKTSS Tss
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called to intialize the TSS for a processor.
|
|
It will set the static fields of the TSS. (ie Those fields that
|
|
the part reads, and for which NT uses constant values.)
|
|
|
|
The dynamic fiels (Esp0 and CR3) are set in the context swap
|
|
code.
|
|
|
|
Arguments:
|
|
|
|
Tss - Linear address of the Task State Segment.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
// Set IO Map base address to indicate no IO map present.
|
|
|
|
// N.B. -1 does not seem to be a valid value for the map base. If this
|
|
// value is used, byte immediate in's and out's will actually go
|
|
// the hardware when executed in V86 mode.
|
|
|
|
Tss->IoMapBase = KiComputeIopmOffset(IO_ACCESS_MAP_NONE);
|
|
|
|
// Set flags to 0, which in particular dispables traps on task switches.
|
|
|
|
Tss->Flags = 0;
|
|
|
|
|
|
// Set LDT and Ss0 to constants used by NT.
|
|
|
|
Tss->LDT = 0;
|
|
Tss->Ss0 = KGDT_R0_DATA;
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
KiInitializeTSS2 (
|
|
IN PKTSS Tss,
|
|
IN PKGDTENTRY TssDescriptor
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Do part of TSS init we do only once.
|
|
|
|
Arguments:
|
|
|
|
Tss - Linear address of the Task State Segment.
|
|
|
|
TssDescriptor - Linear address of the descriptor for the TSS.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PUCHAR p;
|
|
ULONG i;
|
|
ULONG j;
|
|
|
|
//
|
|
// Set limit for TSS
|
|
//
|
|
|
|
if (TssDescriptor != NULL) {
|
|
TssDescriptor->LimitLow = sizeof(KTSS) - 1;
|
|
TssDescriptor->HighWord.Bits.LimitHi = 0;
|
|
}
|
|
|
|
//
|
|
// Initialize IOPMs
|
|
//
|
|
|
|
for (i = 0; i < IOPM_COUNT; i++) {
|
|
p = (PUCHAR)(Tss->IoMaps[i].IoMap);
|
|
|
|
for (j = 0; j < PIOPM_SIZE; j++) {
|
|
p[j] = (UCHAR)-1;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Initialize Software Interrupt Direction Maps
|
|
//
|
|
|
|
for (i = 0; i < IOPM_COUNT; i++) {
|
|
p = (PUCHAR)(Tss->IoMaps[i].DirectionMap);
|
|
for (j = 0; j < INT_DIRECTION_MAP_SIZE; j++) {
|
|
p[j] = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Initialize the map for IO_ACCESS_MAP_NONE
|
|
//
|
|
p = (PUCHAR)(Tss->IntDirectionMap);
|
|
for (j = 0; j < INT_DIRECTION_MAP_SIZE; j++) {
|
|
p[j] = 0;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
KiSwapIDT (
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called to edit the IDT. It swaps words of the address
|
|
and access fields around into the format the part actually needs.
|
|
This allows for easy static init of the IDT.
|
|
|
|
Note that this procedure edits the current IDT.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
LONG Index;
|
|
USHORT Temp;
|
|
|
|
//
|
|
// Rearrange the entries of IDT to match i386 interrupt gate structure
|
|
//
|
|
|
|
for (Index = 0; Index <= MAXIMUM_IDTVECTOR; Index += 1) {
|
|
Temp = IDT[Index].Selector;
|
|
IDT[Index].Selector = IDT[Index].ExtendedOffset;
|
|
IDT[Index].ExtendedOffset = Temp;
|
|
}
|
|
}
|
|
|
|
ULONG
|
|
KiGetFeatureBits ()
|
|
/*++
|
|
|
|
Return the NT feature bits supported by this processors
|
|
|
|
--*/
|
|
{
|
|
UCHAR Buffer[50];
|
|
ULONG Junk, ProcessorFeatures, NtBits;
|
|
ULONG CpuVendor;
|
|
PKPRCB Prcb;
|
|
|
|
NtBits = 0;
|
|
|
|
Prcb = KeGetCurrentPrcb();
|
|
Prcb->VendorString[0] = 0;
|
|
|
|
if (!Prcb->CpuID) {
|
|
return NtBits;
|
|
}
|
|
|
|
//
|
|
// Determine the processor type
|
|
//
|
|
|
|
CPUID (0, &Junk, (PULONG) Buffer+0, (PULONG) Buffer+2, (PULONG) Buffer+1);
|
|
Buffer[12] = 0;
|
|
|
|
//
|
|
// Copy vendor string to Prcb for debugging
|
|
//
|
|
|
|
strcpy (Prcb->VendorString, Buffer);
|
|
|
|
//
|
|
// Determine OEM type
|
|
//
|
|
|
|
CpuVendor = CPU_NONE;
|
|
if (strcmp (Buffer, "GenuineIntel") == 0) {
|
|
CpuVendor = CPU_INTEL;
|
|
} else if (strcmp (Buffer, "AuthenticAMD") == 0) {
|
|
CpuVendor = CPU_AMD;
|
|
} else if (strcmp (Buffer, CmpCyrixID) == 0) {
|
|
CpuVendor = CPU_CYRIX;
|
|
}
|
|
|
|
//
|
|
// Determine which NT compatible features are present
|
|
//
|
|
|
|
CPUID (1, &Junk, &Junk, &Junk, &ProcessorFeatures);
|
|
|
|
if (CpuVendor == CPU_INTEL || CpuVendor == CPU_AMD || CpuVendor == CPU_CYRIX) {
|
|
if (ProcessorFeatures & 0x100) {
|
|
NtBits |= KF_CMPXCHG8B;
|
|
}
|
|
|
|
if (ProcessorFeatures & 0x10) {
|
|
NtBits |= KF_RDTSC;
|
|
}
|
|
|
|
if (ProcessorFeatures & 0x02) {
|
|
NtBits |= KF_V86_VIS | KF_CR4;
|
|
}
|
|
|
|
if (ProcessorFeatures & 0x00800000) {
|
|
NtBits |= KF_MMX;
|
|
}
|
|
}
|
|
|
|
|
|
if (CpuVendor == CPU_INTEL || CpuVendor == CPU_CYRIX) {
|
|
|
|
if (ProcessorFeatures & 0x08) {
|
|
NtBits |= KF_LARGE_PAGE | KF_CR4;
|
|
}
|
|
|
|
if (ProcessorFeatures & 0x2000) {
|
|
NtBits |= KF_GLOBAL_PAGE | KF_CR4;
|
|
}
|
|
|
|
if (ProcessorFeatures & 0x8000) {
|
|
NtBits |= KF_CMOV;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Intel specific stuff
|
|
//
|
|
|
|
if (CpuVendor == CPU_INTEL) {
|
|
if (ProcessorFeatures & 0x1000) {
|
|
NtBits |= KF_MTRR;
|
|
}
|
|
|
|
if (Prcb->CpuType == 6) {
|
|
WRMSR (0x8B, 0);
|
|
CPUID (1, &Junk, &Junk, &Junk, &ProcessorFeatures);
|
|
Prcb->UpdateSignature.QuadPart = RDMSR (0x8B);
|
|
}
|
|
}
|
|
|
|
return NtBits;
|
|
}
|
|
|
|
#define MAX_ATTEMPTS 10
|
|
|
|
BOOLEAN
|
|
KiInitMachineDependent (
|
|
VOID
|
|
)
|
|
{
|
|
KAFFINITY ActiveProcessors, CurrentAffinity;
|
|
ULONG NumberProcessors;
|
|
IDENTITY_MAP IdentityMap;
|
|
ULONG Index;
|
|
ULONG Average;
|
|
ULONG Junk;
|
|
struct {
|
|
LARGE_INTEGER PerfStart;
|
|
LARGE_INTEGER PerfEnd;
|
|
LONGLONG PerfDelta;
|
|
LARGE_INTEGER PerfFreq;
|
|
LONGLONG TSCStart;
|
|
LONGLONG TSCEnd;
|
|
LONGLONG TSCDelta;
|
|
ULONG MHz;
|
|
} Samples[MAX_ATTEMPTS], *pSamp;
|
|
|
|
//
|
|
// If PDE large page is supported, enable it.
|
|
//
|
|
// We enable large pages before global pages to make TLB invalidation
|
|
// easier while turning on large pages.
|
|
//
|
|
|
|
if (KeFeatureBits & KF_LARGE_PAGE) {
|
|
if (Ki386CreateIdentityMap(&IdentityMap)) {
|
|
|
|
KiIpiGenericCall (
|
|
(PKIPI_BROADCAST_WORKER) Ki386EnableTargetLargePage,
|
|
(ULONG)(&IdentityMap)
|
|
);
|
|
}
|
|
|
|
//
|
|
// Always call Ki386ClearIdentityMap() to free any memory allocated
|
|
//
|
|
|
|
Ki386ClearIdentityMap(&IdentityMap);
|
|
}
|
|
|
|
//
|
|
// If PDE/PTE global page is supported, enable it
|
|
//
|
|
|
|
if (KeFeatureBits & KF_GLOBAL_PAGE) {
|
|
NumberProcessors = KeNumberProcessors;
|
|
KiIpiGenericCall (
|
|
(PKIPI_BROADCAST_WORKER) Ki386EnableGlobalPage,
|
|
(ULONG)(&NumberProcessors)
|
|
);
|
|
}
|
|
|
|
ActiveProcessors = KeActiveProcessors;
|
|
for (CurrentAffinity=1; ActiveProcessors; CurrentAffinity <<= 1) {
|
|
|
|
if (ActiveProcessors & CurrentAffinity) {
|
|
|
|
//
|
|
// Switch to that processor, and remove it from the
|
|
// remaining set of processors
|
|
//
|
|
|
|
ActiveProcessors &= ~CurrentAffinity;
|
|
KeSetSystemAffinityThread(CurrentAffinity);
|
|
|
|
//
|
|
// Determine the MHz for the processor
|
|
//
|
|
|
|
KeGetCurrentPrcb()->MHz = 0;
|
|
|
|
if (KeFeatureBits & KF_RDTSC) {
|
|
|
|
Index = 0;
|
|
pSamp = Samples;
|
|
|
|
for (; ;) {
|
|
|
|
//
|
|
// Collect a new sample
|
|
// Delay the thread a "long" amount and time it with
|
|
// a time source and RDTSC.
|
|
//
|
|
|
|
CPUID (0, &Junk, &Junk, &Junk, &Junk);
|
|
pSamp->PerfStart = KeQueryPerformanceCounter (NULL);
|
|
pSamp->TSCStart = RDTSC();
|
|
pSamp->PerfFreq.QuadPart = -50000;
|
|
|
|
KeDelayExecutionThread (KernelMode, FALSE, &pSamp->PerfFreq);
|
|
|
|
CPUID (0, &Junk, &Junk, &Junk, &Junk);
|
|
pSamp->PerfEnd = KeQueryPerformanceCounter (&pSamp->PerfFreq);
|
|
pSamp->TSCEnd = RDTSC();
|
|
|
|
//
|
|
// Calculate processors MHz
|
|
//
|
|
|
|
pSamp->PerfDelta = pSamp->PerfEnd.QuadPart - pSamp->PerfStart.QuadPart;
|
|
pSamp->TSCDelta = pSamp->TSCEnd - pSamp->TSCStart;
|
|
|
|
pSamp->MHz = (ULONG) ((pSamp->TSCDelta * pSamp->PerfFreq.QuadPart + 500000L) /
|
|
(pSamp->PerfDelta * 1000000L));
|
|
|
|
|
|
//
|
|
// If last 2 samples matched, done
|
|
//
|
|
|
|
if (Index && pSamp->MHz == pSamp[-1].MHz) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Advance to next sample
|
|
//
|
|
|
|
pSamp += 1;
|
|
Index += 1;
|
|
|
|
//
|
|
// If too many samples, then something is wrong
|
|
//
|
|
|
|
if (Index >= MAX_ATTEMPTS) {
|
|
|
|
#if DBG
|
|
//
|
|
// Temp breakpoint to see where this is failing
|
|
// and why
|
|
//
|
|
|
|
DbgBreakPoint();
|
|
#endif
|
|
|
|
Average = 0;
|
|
for (Index = 0; Index < MAX_ATTEMPTS; Index++) {
|
|
Average += Samples[Index].MHz;
|
|
}
|
|
pSamp[-1].MHz = Average / MAX_ATTEMPTS;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
KeGetCurrentPrcb()->MHz = (USHORT) pSamp[-1].MHz;
|
|
}
|
|
|
|
//
|
|
// If MTRR is supported, initialize per processor
|
|
//
|
|
|
|
if (KeFeatureBits & KF_MTRR) {
|
|
KiInitializeMTRR ( (BOOLEAN) (ActiveProcessors ? FALSE : TRUE));
|
|
}
|
|
}
|
|
}
|
|
|
|
KeRevertToUserAffinityThread();
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
KeOptimizeProcessorControlState (
|
|
VOID
|
|
)
|
|
{
|
|
Ke386ConfigureCyrixProcessor ();
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
KeSetup80387OrEmulate (
|
|
IN PVOID *R3EmulatorTable
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by PS initialization after loading UDLL.
|
|
|
|
If this is a 386 system without 387s (all processors must be
|
|
symmetrical) then this function will set the trap 07 vector on all
|
|
processors to point to the address passed in (which should be the
|
|
entry point of the 80387 emulator in UDLL, NPXNPHandler).
|
|
|
|
Arguments:
|
|
|
|
HandlerAddress - Supplies the address of the trap07 handler.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PKINTERRUPT_ROUTINE HandlerAddress;
|
|
KAFFINITY ActiveProcessors, CurrentAffinity;
|
|
KIRQL OldIrql;
|
|
ULONG disposition;
|
|
HANDLE SystemHandle, SourceHandle, DestHandle;
|
|
NTSTATUS Status;
|
|
UNICODE_STRING unicodeString;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
double Dividend, Divisor;
|
|
BOOLEAN PrecisionErrata;
|
|
|
|
if (KeI386NpxPresent) {
|
|
|
|
//
|
|
// A coprocessor is present - check to see if the precision errata exists
|
|
//
|
|
|
|
PrecisionErrata = FALSE;
|
|
|
|
ActiveProcessors = KeActiveProcessors;
|
|
for (CurrentAffinity = 1; ActiveProcessors; CurrentAffinity <<= 1) {
|
|
|
|
if (ActiveProcessors & CurrentAffinity) {
|
|
ActiveProcessors &= ~CurrentAffinity;
|
|
|
|
//
|
|
// Run calculation on each processor.
|
|
//
|
|
|
|
KeSetSystemAffinityThread(CurrentAffinity);
|
|
_asm {
|
|
|
|
;
|
|
; This is going to destroy the state in the coprocesssor,
|
|
; but we know that there's no state currently in it.
|
|
;
|
|
|
|
cli
|
|
mov eax, cr0
|
|
mov ecx, eax ; hold original cr0 value
|
|
and eax, not (CR0_TS+CR0_MP+CR0_EM)
|
|
mov cr0, eax
|
|
|
|
fninit ; to known state
|
|
}
|
|
|
|
Dividend = 4195835.0;
|
|
Divisor = 3145727.0;
|
|
|
|
_asm {
|
|
fld Dividend
|
|
fdiv Divisor ; test known faulty divison
|
|
fmul Divisor ; Multiple quotient by divisor
|
|
fcomp Dividend ; Compare product and dividend
|
|
fstsw ax ; Move float conditions to ax
|
|
sahf ; move to eflags
|
|
|
|
mov cr0, ecx ; restore cr0
|
|
sti
|
|
|
|
jc short em10
|
|
jz short em20
|
|
em10: mov PrecisionErrata, TRUE
|
|
em20:
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Check to see if the emulator should be used anyway
|
|
//
|
|
|
|
switch (KeI386ForceNpxEmulation) {
|
|
case 0:
|
|
//
|
|
// Use the emulator based on the value in KeI386NpxPresent
|
|
//
|
|
|
|
break;
|
|
|
|
case 1:
|
|
//
|
|
// Only use the emulator if any processor has the known
|
|
// Pentium floating point division problem.
|
|
//
|
|
|
|
if (PrecisionErrata) {
|
|
KeI386NpxPresent = FALSE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
|
|
//
|
|
// Unkown setting - use the emulator
|
|
//
|
|
|
|
KeI386NpxPresent = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Setup processor features, and install emulator if needed
|
|
//
|
|
|
|
SharedUserData->ProcessorFeatures[PF_FLOATING_POINT_EMULATED] = KeI386NpxPresent;
|
|
SharedUserData->ProcessorFeatures[PF_FLOATING_POINT_PRECISION_ERRATA] = PrecisionErrata;
|
|
|
|
if (!KeI386NpxPresent) {
|
|
|
|
//
|
|
// MMx not available when emulator is used
|
|
//
|
|
|
|
KeFeatureBits &= ~KF_MMX;
|
|
SharedUserData->ProcessorFeatures[PF_MMX_INSTRUCTIONS_AVAILABLE] = FALSE;
|
|
|
|
//
|
|
// Errata not present when using emulator
|
|
//
|
|
|
|
SharedUserData->ProcessorFeatures[PF_FLOATING_POINT_PRECISION_ERRATA] = FALSE;
|
|
|
|
//
|
|
// Use the user mode floating point emulator
|
|
//
|
|
|
|
HandlerAddress = (PKINTERRUPT_ROUTINE) ((PULONG) R3EmulatorTable)[0];
|
|
Ki387RoundModeTable = (PVOID) ((PULONG) R3EmulatorTable)[1];
|
|
|
|
ActiveProcessors = KeActiveProcessors;
|
|
for (CurrentAffinity = 1; ActiveProcessors; CurrentAffinity <<= 1) {
|
|
|
|
if (ActiveProcessors & CurrentAffinity) {
|
|
ActiveProcessors &= ~CurrentAffinity;
|
|
|
|
//
|
|
// Run this code on each processor.
|
|
//
|
|
|
|
KeSetSystemAffinityThread(CurrentAffinity);
|
|
|
|
//
|
|
// Raise IRQL and lock dispatcher database.
|
|
//
|
|
|
|
KiLockDispatcherDatabase(&OldIrql);
|
|
|
|
//
|
|
// Make the trap 07 IDT entry point at the passed-in handler
|
|
//
|
|
|
|
KiSetHandlerAddressToIDT(I386_80387_NP_VECTOR, HandlerAddress);
|
|
KeGetPcr()->IDT[I386_80387_NP_VECTOR].Selector = KGDT_R3_CODE;
|
|
KeGetPcr()->IDT[I386_80387_NP_VECTOR].Access = TRAP332_GATE;
|
|
|
|
|
|
//
|
|
// Unlock dispatcher database and lower IRQL to its previous value.
|
|
//
|
|
|
|
KiUnlockDispatcherDatabase(OldIrql);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Move any entries from ..\System\FloatingPointProcessor to
|
|
// ..\System\DisabledFloatingPointProcessor.
|
|
//
|
|
|
|
//
|
|
// Open system tree
|
|
//
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&CmRegistryMachineHardwareDescriptionSystemName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
Status = ZwOpenKey( &SystemHandle,
|
|
KEY_ALL_ACCESS,
|
|
&ObjectAttributes
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Open FloatingPointProcessor key
|
|
//
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&CmTypeName[FloatingPointProcessor],
|
|
OBJ_CASE_INSENSITIVE,
|
|
SystemHandle,
|
|
NULL
|
|
);
|
|
|
|
Status = ZwOpenKey ( &SourceHandle,
|
|
KEY_ALL_ACCESS,
|
|
&ObjectAttributes
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Create DisabledFloatingPointProcessor key
|
|
//
|
|
|
|
RtlInitUnicodeString (
|
|
&unicodeString,
|
|
CmDisabledFloatingPointProcessor
|
|
);
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&unicodeString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
SystemHandle,
|
|
NULL
|
|
);
|
|
|
|
Status = ZwCreateKey( &DestHandle,
|
|
KEY_ALL_ACCESS,
|
|
&ObjectAttributes,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_VOLATILE,
|
|
&disposition
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Move it
|
|
//
|
|
|
|
KiMoveRegTree (SourceHandle, DestHandle);
|
|
ZwClose (DestHandle);
|
|
}
|
|
ZwClose (SourceHandle);
|
|
}
|
|
ZwClose (SystemHandle);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set affinity back to the original value.
|
|
//
|
|
|
|
KeRevertToUserAffinityThread();
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
KiMoveRegTree(
|
|
HANDLE Source,
|
|
HANDLE Dest
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PKEY_BASIC_INFORMATION KeyInformation;
|
|
PKEY_VALUE_FULL_INFORMATION KeyValue;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE SourceChild;
|
|
HANDLE DestChild;
|
|
ULONG ResultLength;
|
|
UCHAR buffer[1024]; // hmm....
|
|
UNICODE_STRING ValueName;
|
|
UNICODE_STRING KeyName;
|
|
|
|
|
|
KeyValue = (PKEY_VALUE_FULL_INFORMATION)buffer;
|
|
|
|
//
|
|
// Move values from source node to dest node
|
|
//
|
|
|
|
for (; ;) {
|
|
//
|
|
// Get first value
|
|
//
|
|
|
|
Status = ZwEnumerateValueKey(Source,
|
|
0,
|
|
KeyValueFullInformation,
|
|
buffer,
|
|
sizeof (buffer),
|
|
&ResultLength);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Write value to dest node
|
|
//
|
|
|
|
ValueName.Buffer = KeyValue->Name;
|
|
ValueName.Length = (USHORT) KeyValue->NameLength;
|
|
ZwSetValueKey( Dest,
|
|
&ValueName,
|
|
KeyValue->TitleIndex,
|
|
KeyValue->Type,
|
|
buffer+KeyValue->DataOffset,
|
|
KeyValue->DataLength
|
|
);
|
|
|
|
//
|
|
// Delete value and get first value again
|
|
//
|
|
|
|
Status = ZwDeleteValueKey (Source, &ValueName);
|
|
if (!NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Enumerate node's children and apply ourselves to each one
|
|
//
|
|
|
|
KeyInformation = (PKEY_BASIC_INFORMATION)buffer;
|
|
for (; ;) {
|
|
|
|
//
|
|
// Open node's first key
|
|
//
|
|
|
|
Status = ZwEnumerateKey(
|
|
Source,
|
|
0,
|
|
KeyBasicInformation,
|
|
KeyInformation,
|
|
sizeof (buffer),
|
|
&ResultLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
KeyName.Buffer = KeyInformation->Name;
|
|
KeyName.Length = (USHORT) KeyInformation->NameLength;
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&KeyName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
Source,
|
|
NULL
|
|
);
|
|
|
|
Status = ZwOpenKey(
|
|
&SourceChild,
|
|
KEY_ALL_ACCESS,
|
|
&ObjectAttributes
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Create key in dest tree
|
|
//
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&KeyName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
Dest,
|
|
NULL
|
|
);
|
|
|
|
Status = ZwCreateKey(
|
|
&DestChild,
|
|
KEY_ALL_ACCESS,
|
|
&ObjectAttributes,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_VOLATILE,
|
|
NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Move subtree
|
|
//
|
|
|
|
Status = KiMoveRegTree(SourceChild, DestChild);
|
|
|
|
ZwClose(DestChild);
|
|
ZwClose(SourceChild);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Loop and get first key. (old first key was delete by the
|
|
// call to KiMoveRegTree).
|
|
//
|
|
}
|
|
|
|
//
|
|
// Remove source node
|
|
//
|
|
|
|
return NtDeleteKey (Source);
|
|
}
|