3254 lines
82 KiB
C
3254 lines
82 KiB
C
/*++
|
||
|
||
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 "fastsys.inc"
|
||
|
||
#define TRAP332_GATE 0xEF00
|
||
|
||
VOID
|
||
KiSetProcessorType(
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
KiSetCR0Bits(
|
||
VOID
|
||
);
|
||
|
||
BOOLEAN
|
||
KiIsNpxPresent(
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
KiI386PentiumLockErrataFixup (
|
||
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
|
||
);
|
||
|
||
VOID
|
||
KiGetCacheInformation(
|
||
VOID
|
||
);
|
||
|
||
ULONG
|
||
KiGetCpuVendor(
|
||
VOID
|
||
);
|
||
|
||
ULONG
|
||
KiGetFeatureBits (
|
||
VOID
|
||
);
|
||
|
||
NTSTATUS
|
||
KiMoveRegTree(
|
||
HANDLE Source,
|
||
HANDLE Dest
|
||
);
|
||
|
||
VOID
|
||
Ki386EnableDE (
|
||
IN volatile PLONG Number
|
||
);
|
||
|
||
VOID
|
||
Ki386EnableFxsr (
|
||
IN volatile PLONG Number
|
||
);
|
||
|
||
|
||
VOID
|
||
Ki386EnableXMMIExceptions (
|
||
IN volatile PLONG Number
|
||
);
|
||
|
||
|
||
VOID
|
||
Ki386EnableGlobalPage (
|
||
IN volatile PLONG Number
|
||
);
|
||
|
||
VOID
|
||
Ki386UseSynchronousTbFlush (
|
||
IN volatile PLONG Number
|
||
);
|
||
|
||
BOOLEAN
|
||
KiInitMachineDependent (
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
KiInitializeMTRR (
|
||
IN BOOLEAN LastProcessor
|
||
);
|
||
|
||
VOID
|
||
KiInitializePAT (
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
KiAmdK6InitializeMTRR(
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
KiRestoreFastSyscallReturnState(
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
KiLogMcaErrors (
|
||
VOID
|
||
);
|
||
|
||
#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,KiGetCacheInformation)
|
||
#pragma alloc_text(INIT,KiGetCpuVendor)
|
||
#pragma alloc_text(INIT,KiMoveRegTree)
|
||
#pragma alloc_text(INIT,KiInitMachineDependent)
|
||
#pragma alloc_text(INIT,KiI386PentiumLockErrataFixup)
|
||
#pragma alloc_text(INIT,KiLogMcaErrors)
|
||
#endif
|
||
|
||
BOOLEAN KiI386PentiumLockErrataPresent = FALSE;
|
||
BOOLEAN KiIgnoreUnexpectedTrap07 = FALSE;
|
||
|
||
ULONG KiFastSystemCallDisable;
|
||
|
||
extern PVOID Ki387RoundModeTable;
|
||
extern PVOID Ki386IopmSaveArea;
|
||
extern ULONG KeI386ForceNpxEmulation;
|
||
extern WCHAR CmDisabledFloatingPointProcessor[];
|
||
extern UCHAR CmpCyrixID[];
|
||
extern UCHAR CmpIntelID[];
|
||
extern UCHAR CmpAmdID[];
|
||
extern UCHAR CmpTransmetaID[];
|
||
extern UCHAR CmpCentaurID[];
|
||
extern BOOLEAN KiFastSystemCallIsIA32;
|
||
|
||
#ifndef NT_UP
|
||
extern PVOID ScPatchFxb;
|
||
extern PVOID ScPatchFxe;
|
||
#endif
|
||
|
||
typedef enum {
|
||
CPU_NONE,
|
||
CPU_INTEL,
|
||
CPU_AMD,
|
||
CPU_CYRIX,
|
||
CPU_TRANSMETA,
|
||
CPU_CENTAUR,
|
||
CPU_UNKNOWN
|
||
} CPU_VENDORS;
|
||
|
||
|
||
//
|
||
// If this processor does XMMI, take advantage of it. Default is
|
||
// no XMMI.
|
||
//
|
||
|
||
BOOLEAN KeI386XMMIPresent;
|
||
|
||
//
|
||
// x86 statically provides the idle process and idle thread for
|
||
// processor 0.
|
||
//
|
||
|
||
EPROCESS KiIdleProcess;
|
||
ETHREAD KiIdleThread0;
|
||
|
||
//
|
||
// Define prototypes and static initialization for the fast zero
|
||
// page routines.
|
||
//
|
||
|
||
VOID
|
||
FASTCALL
|
||
KiZeroPage (
|
||
PVOID PageBase
|
||
);
|
||
|
||
VOID
|
||
FASTCALL
|
||
KiXMMIZeroPage (
|
||
PVOID PageBase
|
||
);
|
||
|
||
VOID
|
||
FASTCALL
|
||
KiXMMIZeroPageNoSave (
|
||
PVOID PageBase
|
||
);
|
||
|
||
KE_ZERO_PAGE_ROUTINE KeZeroPage = KiZeroPage;
|
||
KE_ZERO_PAGE_ROUTINE KeZeroPageFromIdleThread = KiZeroPage;
|
||
|
||
//
|
||
// Line size of the d-cache closest to the processor. Used by machine
|
||
// dependent prefetch routines. Default to 32.
|
||
//
|
||
|
||
ULONG KePrefetchNTAGranularity = 32;
|
||
|
||
VOID
|
||
FASTCALL
|
||
RtlPrefetchMemoryNonTemporal(
|
||
PVOID Memory,
|
||
SIZE_T Length
|
||
);
|
||
|
||
|
||
//
|
||
// The following spinlock is for compatiblity with 486 systems that don't
|
||
// have a cmpxchg8b instruction and therefore need to synchronize using a
|
||
// spinlock. NOTE: This spinlock should be initialized on x86 systems.
|
||
//
|
||
|
||
ULONG Ki486CompatibilityLock;
|
||
|
||
//
|
||
// 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.
|
||
|
||
--*/
|
||
|
||
{
|
||
LONG Index;
|
||
ULONG DirectoryTableBase[2];
|
||
KIRQL OldIrql;
|
||
PKPCR Pcr;
|
||
BOOLEAN NpxFlag;
|
||
BOOLEAN FxsrPresent;
|
||
BOOLEAN XMMIPresent;
|
||
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;
|
||
|
||
//
|
||
// Initialize processor's PowerState
|
||
//
|
||
|
||
PoInitializePrcb (Prcb);
|
||
|
||
//
|
||
// Check for unsupported processor revision
|
||
//
|
||
|
||
if (Prcb->CpuType == 3) {
|
||
KeBugCheckEx(UNSUPPORTED_PROCESSOR,0x386,0,0,0);
|
||
}
|
||
|
||
//
|
||
// Get the processor FeatureBits for this processor.
|
||
//
|
||
|
||
FeatureBits = KiGetFeatureBits();
|
||
Prcb->FeatureBits = FeatureBits;
|
||
|
||
//
|
||
// Do one time initialization of the ProcesorControlSpace in the PRCB
|
||
// so local kernel debugger can get things like the GDT
|
||
//
|
||
|
||
KiSaveProcessorControlState(&Prcb->ProcessorState);
|
||
|
||
//
|
||
// Get processor Cache Size information.
|
||
//
|
||
|
||
KiGetCacheInformation();
|
||
|
||
//
|
||
// initialize the per processor lock queue entry for implemented locks.
|
||
//
|
||
|
||
KiInitQueuedSpinLocks(Prcb, Number);
|
||
|
||
//
|
||
// If the initial processor is being initialized, then initialize the
|
||
// per system data structures.
|
||
//
|
||
|
||
if (Number == 0) {
|
||
|
||
//
|
||
// Set default node. Used in non-multinode systems and in
|
||
// multinode systems until the node topology is available.
|
||
//
|
||
|
||
KeNodeBlock[0] = &KiNode0;
|
||
|
||
#if defined(KE_MULTINODE)
|
||
|
||
for (Index = 1; Index < MAXIMUM_CCNUMA_NODES; Index++) {
|
||
|
||
//
|
||
// Set temporary node.
|
||
//
|
||
|
||
KeNodeBlock[Index] = &KiNodeInit[Index];
|
||
}
|
||
|
||
#endif
|
||
|
||
Prcb->ParentNode = KeNodeBlock[0];
|
||
KeNodeBlock[0]->ProcessorMask = Prcb->SetMember;
|
||
|
||
//
|
||
// 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 = FeatureBits;
|
||
|
||
KeI386FxsrPresent = ((KeFeatureBits & KF_FXSR) ? TRUE:FALSE);
|
||
|
||
KeI386XMMIPresent = ((KeFeatureBits & KF_XMMI) ? TRUE:FALSE);
|
||
|
||
//
|
||
// As of Whistler, cmpxchg8b is a required instruction.
|
||
//
|
||
|
||
if ((KeFeatureBits & KF_CMPXCHG8B) == 0) {
|
||
|
||
ULONG Vendor[3];
|
||
|
||
//
|
||
// Argument 1:
|
||
// bits 31-24: Unique value for missing feature.
|
||
// bits 23-0 : Family/Model/Stepping (this could compress).
|
||
// Arguments 2 thru 4:
|
||
// Vendor Id string.
|
||
//
|
||
|
||
RtlCopyMemory(Vendor, Prcb->VendorString, sizeof(Vendor));
|
||
KeBugCheckEx(UNSUPPORTED_PROCESSOR,
|
||
(1 << 24 ) // rev this for other required features
|
||
| (Prcb->CpuType << 16) | Prcb->CpuStep,
|
||
Vendor[0],
|
||
Vendor[1],
|
||
Vendor[2]
|
||
);
|
||
}
|
||
|
||
//
|
||
// Lower IRQL to APC level.
|
||
//
|
||
|
||
KeLowerIrql(APC_LEVEL);
|
||
|
||
|
||
//
|
||
// Initialize kernel internal spinlocks
|
||
//
|
||
|
||
KeInitializeSpinLock(&KiFreezeExecutionLock);
|
||
|
||
//
|
||
// Initialize 486 compatibility lock
|
||
//
|
||
|
||
KeInitializeSpinLock(&Ki486CompatibilityLock);
|
||
|
||
#if !defined(NT_UP)
|
||
|
||
//
|
||
// Set this processor as the master (ie first found) processor
|
||
// in this SMT set (whether or not it is actually SMT).
|
||
//
|
||
|
||
Prcb->MultiThreadSetMaster = Prcb;
|
||
|
||
//
|
||
// During Text Mode setup, it is possible the system is
|
||
// running with an MP kernel and a UP HAL. On X86 systems,
|
||
// spinlocks are implemented in both the kernel and the HAL
|
||
// with the verisons that alter IRQL in the HAL. If the
|
||
// HAL is UP, it will not actually acquire/release locks
|
||
// while the MP kernel will which will cause the system to
|
||
// hang (or crash). As this can only occur during text
|
||
// mode setup, we will detect the situation and disable
|
||
// the kernel only versions of queued spinlocks if the HAL
|
||
// is UP (and the kernel MP).
|
||
//
|
||
// We need to patch 3 routines, two of them are void and
|
||
// the other returns a boolean (must be true (and ZF must be
|
||
// clear) in a UP case).
|
||
//
|
||
// Determine if the HAL us UP by acquiring the dispatcher
|
||
// lock and examining it to see if the HAL actually did
|
||
// anything to it.
|
||
//
|
||
|
||
OldIrql = KfAcquireSpinLock(&Ki486CompatibilityLock);
|
||
if (Ki486CompatibilityLock == 0) {
|
||
|
||
//
|
||
// KfAcquireSpinLock is in the HAL and it did not
|
||
// change the value of the lock. This is a UP HAL.
|
||
//
|
||
|
||
extern UCHAR KeTryToAcquireQueuedSpinLockAtRaisedIrqlUP;
|
||
PUCHAR PatchTarget, PatchSource;
|
||
UCHAR Byte;
|
||
|
||
#define RET 0xc3
|
||
|
||
*(PUCHAR)(KeAcquireQueuedSpinLockAtDpcLevel) = RET;
|
||
*(PUCHAR)(KeReleaseQueuedSpinLockFromDpcLevel) = RET;
|
||
|
||
//
|
||
// Copy the UP version of KeTryToAcquireQueuedSpinLockAtRaisedIrql
|
||
// over the top of the MP versin.
|
||
//
|
||
|
||
PatchSource = &(KeTryToAcquireQueuedSpinLockAtRaisedIrqlUP);
|
||
PatchTarget = (PUCHAR)(KeTryToAcquireQueuedSpinLockAtRaisedIrql);
|
||
|
||
do {
|
||
Byte = *PatchSource++;
|
||
*PatchTarget++ = Byte;
|
||
} while (Byte != RET);
|
||
|
||
#undef RET
|
||
}
|
||
KeReleaseSpinLock(&Ki486CompatibilityLock, OldIrql);
|
||
|
||
#endif
|
||
|
||
//
|
||
// 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;
|
||
|
||
#if !defined(NT_UP)
|
||
|
||
} else {
|
||
|
||
//
|
||
// Adjust global cpu setting to represent lowest of all processors
|
||
//
|
||
|
||
FxsrPresent = ((FeatureBits & KF_FXSR) ? TRUE:FALSE);
|
||
if (FxsrPresent != KeI386FxsrPresent) {
|
||
//
|
||
// FXSR support must be available on all processors or on none
|
||
//
|
||
KeBugCheckEx (MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED, KF_FXSR, 0, 0, 0);
|
||
}
|
||
|
||
XMMIPresent = ((FeatureBits & KF_XMMI) ? TRUE:FALSE);
|
||
if (XMMIPresent != KeI386XMMIPresent) {
|
||
//
|
||
// XMMI support must be available on all processors or on none
|
||
//
|
||
KeBugCheckEx (MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED, KF_XMMI, 0, 0, 0);
|
||
}
|
||
|
||
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);
|
||
}
|
||
|
||
if ((KeFeatureBits & KF_PAT) && !(FeatureBits & KF_PAT)) {
|
||
//
|
||
// PAT must be available on all processors, if on boot processor
|
||
//
|
||
|
||
KeBugCheckEx (MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED, KF_PAT, 0, 0, 0);
|
||
}
|
||
|
||
if ((KeFeatureBits & KF_MTRR) && !(FeatureBits & KF_MTRR)) {
|
||
//
|
||
// MTRR must be available on all processors, if on boot processor
|
||
//
|
||
|
||
KeBugCheckEx (MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED, KF_MTRR, 0, 0, 0);
|
||
}
|
||
|
||
if ((KeFeatureBits & KF_FAST_SYSCALL) != (FeatureBits & KF_FAST_SYSCALL)) {
|
||
//
|
||
// If this feature is not available on all processors
|
||
// don't use it at all.
|
||
//
|
||
|
||
KiFastSystemCallDisable = 1;
|
||
}
|
||
|
||
if ((KeFeatureBits & KF_XMMI64) != (FeatureBits & KF_XMMI64)) {
|
||
|
||
//
|
||
// If not all processors support Streaming SIMD Extensions
|
||
// 64bit FP don't use it at all.
|
||
//
|
||
|
||
KeFeatureBits &= ~KF_XMMI64;
|
||
}
|
||
|
||
//
|
||
// 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);
|
||
|
||
#endif
|
||
|
||
}
|
||
|
||
//
|
||
// 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;
|
||
|
||
SharedUserData->ProcessorFeatures[PF_XMMI_INSTRUCTIONS_AVAILABLE] =
|
||
((KeFeatureBits & KF_FXSR) && (KeFeatureBits & KF_XMMI)) ? TRUE : FALSE;
|
||
|
||
SharedUserData->ProcessorFeatures[PF_XMMI64_INSTRUCTIONS_AVAILABLE] =
|
||
((KeFeatureBits & KF_FXSR) && (KeFeatureBits & KF_XMMI64)) ? TRUE : FALSE;
|
||
|
||
SharedUserData->ProcessorFeatures[PF_3DNOW_INSTRUCTIONS_AVAILABLE] =
|
||
(KeFeatureBits & KF_3DNOW) ? TRUE : FALSE;
|
||
|
||
SharedUserData->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE] =
|
||
(KeFeatureBits & KF_RDTSC) ? 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)((ULONG)IdleStack),
|
||
(PKSYSTEM_ROUTINE)NULL, (PKSTART_ROUTINE)NULL,
|
||
(PVOID)NULL, (PCONTEXT)NULL, (PVOID)NULL, Process);
|
||
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;
|
||
|
||
//
|
||
// 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;
|
||
}
|
||
|
||
if (Number == 0) {
|
||
|
||
//
|
||
// Processor 0's DPC stack was temporarily allocated on
|
||
// the Double Fault Stack, switch to a proper kernel
|
||
// stack now.
|
||
//
|
||
|
||
PVOID DpcStack;
|
||
|
||
DpcStack = MmCreateKernelStack(FALSE, 0);
|
||
|
||
if (DpcStack == NULL) {
|
||
KeBugCheckEx(NO_PAGES_AVAILABLE, 1, 0, 0, 0);
|
||
}
|
||
Prcb->DpcStack = DpcStack;
|
||
|
||
//
|
||
// Allocate 8k IOPM bit map saved area to allow BiosCall swap
|
||
// bit maps.
|
||
//
|
||
|
||
Ki386IopmSaveArea = ExAllocatePoolWithTag(PagedPool,
|
||
PAGE_SIZE * 2,
|
||
' eK');
|
||
if (Ki386IopmSaveArea == NULL) {
|
||
KeBugCheckEx(NO_PAGES_AVAILABLE, 2, PAGE_SIZE * 2, 0, 0);
|
||
}
|
||
}
|
||
|
||
//
|
||
// 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
|
||
//
|
||
|
||
KeAcquireQueuedSpinLockAtDpcLevel(KeQueuedSpinLockContext(LockQueueDispatcherLock));
|
||
if (Prcb->NextThread == (PKTHREAD)NULL) {
|
||
SetMember(Number, KiIdleSummary);
|
||
}
|
||
|
||
KeReleaseQueuedSpinLockFromDpcLevel(KeQueuedSpinLockContext(LockQueueDispatcherLock));
|
||
|
||
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,
|
||
IN PVOID DpcStack
|
||
)
|
||
|
||
/*++
|
||
|
||
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 whose 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
|
||
|
||
#if defined (_X86PAE_)
|
||
if (Processor == 0) {
|
||
//
|
||
// PAE feature must be initialized prior to the first HAL call.
|
||
//
|
||
|
||
SharedUserData->ProcessorFeatures[PF_PAE_ENABLED] = TRUE;
|
||
}
|
||
#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;
|
||
Pcr->PrcbData.DpcStack = DpcStack;
|
||
|
||
//
|
||
// Initially, set this processor as only member of SMT set.
|
||
//
|
||
|
||
Pcr->PrcbData.MultiThreadProcessorSet = Pcr->PrcbData.SetMember;
|
||
|
||
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 disables 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 initialize 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 fields (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 disables 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;
|
||
}
|
||
// dpmi requires special case for int 2, 1b, 1c, 23, 24
|
||
p[0] = 4;
|
||
p[3] = 0x18;
|
||
p[4] = 0x18;
|
||
}
|
||
|
||
//
|
||
// Initialize the map for IO_ACCESS_MAP_NONE
|
||
//
|
||
p = (PUCHAR)(Tss->IntDirectionMap);
|
||
for (j = 0; j < INT_DIRECTION_MAP_SIZE; j++) {
|
||
p[j] = 0;
|
||
}
|
||
|
||
// dpmi requires special case for int 2, 1b, 1c, 23, 24
|
||
p[0] = 4;
|
||
p[3] = 0x18;
|
||
p[4] = 0x18;
|
||
|
||
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
|
||
KiGetCpuVendor(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
(Try to) Determine the manufacturer of this processor based on
|
||
data returned by the CPUID instruction (if present).
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
One of the members of the enumeration CPU_VENDORS (defined above).
|
||
|
||
--*/
|
||
{
|
||
PKPRCB Prcb;
|
||
ULONG Junk;
|
||
ULONG Buffer[4];
|
||
|
||
Prcb = KeGetCurrentPrcb();
|
||
Prcb->VendorString[0] = 0;
|
||
|
||
if (!Prcb->CpuID) {
|
||
return CPU_NONE;
|
||
}
|
||
|
||
CPUID(0, &Junk, Buffer+0, Buffer+2, Buffer+1);
|
||
Buffer[3] = 0;
|
||
|
||
//
|
||
// Copy vendor string to Prcb for debugging (ensure it's NULL
|
||
// terminated).
|
||
//
|
||
|
||
RtlCopyMemory(
|
||
Prcb->VendorString,
|
||
Buffer,
|
||
sizeof(Prcb->VendorString) - 1
|
||
);
|
||
|
||
Prcb->VendorString[sizeof(Prcb->VendorString) - 1] = '\0';
|
||
|
||
if (strcmp((PVOID)Buffer, CmpIntelID) == 0) {
|
||
return CPU_INTEL;
|
||
} else if (strcmp((PVOID)Buffer, CmpAmdID) == 0) {
|
||
return CPU_AMD;
|
||
} else if (strcmp((PVOID)Buffer, CmpCyrixID) == 0) {
|
||
return CPU_CYRIX;
|
||
} else if (strcmp((PVOID)Buffer, CmpTransmetaID) == 0) {
|
||
return CPU_TRANSMETA;
|
||
} else if (strcmp((PVOID)Buffer, CmpCentaurID) == 0) {
|
||
return CPU_CENTAUR;
|
||
}
|
||
return CPU_UNKNOWN;
|
||
}
|
||
|
||
ULONG
|
||
KiGetFeatureBits (
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Examine the processor specific feature bits to determine the
|
||
Windows 2000 supported features supported by this processor.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
Returns a Windows 2000 normalized set of processor features.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG Junk;
|
||
ULONG Temp;
|
||
ULONG ProcessorFeatures;
|
||
ULONG NtBits;
|
||
ULONG ExtendedProcessorFeatures;
|
||
ULONG ProcessorSignature;
|
||
ULONG CpuVendor;
|
||
PKPRCB Prcb;
|
||
BOOLEAN ExtendedCPUIDSupport = TRUE;
|
||
|
||
Prcb = KeGetCurrentPrcb();
|
||
|
||
NtBits = KF_WORKING_PTE;
|
||
|
||
//
|
||
// Determine the processor type
|
||
//
|
||
|
||
CpuVendor = KiGetCpuVendor();
|
||
|
||
//
|
||
// If this processor does not support the CPUID instruction,
|
||
// don't try to use it.
|
||
//
|
||
|
||
if (CpuVendor == CPU_NONE) {
|
||
return NtBits;
|
||
}
|
||
|
||
//
|
||
// Determine which NT compatible features are present
|
||
//
|
||
|
||
CPUID (1, &ProcessorSignature, &Temp, &Junk, &ProcessorFeatures);
|
||
|
||
//
|
||
// CPUID(1) now returns information in EBX. On the grounds that
|
||
// the low functions are supposed to be standard, we record the
|
||
// information regardless of processor vendor even though it may
|
||
// be 0 or undefined on older implementations.
|
||
//
|
||
|
||
Prcb->InitialApicId = (UCHAR)(Temp >> 24);
|
||
|
||
//
|
||
// AMD specific stuff
|
||
//
|
||
|
||
if (CpuVendor == CPU_AMD) {
|
||
|
||
//
|
||
// Check for K5 and above.
|
||
//
|
||
|
||
if ((ProcessorSignature & 0x0F00) >= 0x0500) {
|
||
|
||
if ((ProcessorSignature & 0x0F00) == 0x0500) {
|
||
|
||
switch (ProcessorSignature & 0x00F0) {
|
||
|
||
case 0x0010: // K5 Model 1
|
||
|
||
//
|
||
// for K5 Model 1 stepping 0 or 1 don't set global page
|
||
//
|
||
|
||
if ((ProcessorSignature & 0x000F) > 0x03) {
|
||
|
||
//
|
||
// K5 Model 1 stepping 2 or greater
|
||
//
|
||
|
||
break;
|
||
}
|
||
|
||
//
|
||
// K5 Model 1 stepping 0 or 1, FALL THRU.
|
||
//
|
||
|
||
case 0x0000: // K5 Model 0
|
||
|
||
//
|
||
// for K5 Model 0 or model unknown don't set global page
|
||
//
|
||
|
||
ProcessorFeatures &= ~0x2000;
|
||
break;
|
||
|
||
case 0x0080: // K6 Model 8 (K6-2)
|
||
|
||
//
|
||
// All steppings >= 8 support MTRRs.
|
||
//
|
||
|
||
if ((ProcessorSignature & 0x000F) >= 0x8) {
|
||
NtBits |= KF_AMDK6MTRR;
|
||
}
|
||
break;
|
||
|
||
case 0x0090: // K6 Model 9 (K6-3)
|
||
|
||
NtBits |= KF_AMDK6MTRR;
|
||
break;
|
||
|
||
default: // anything else, nothing to do.
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Less than family 5, don't set GLOBAL PAGE, LARGE
|
||
// PAGE or CMOV. (greater than family 5 will have the
|
||
// bits set correctly).
|
||
//
|
||
|
||
ProcessorFeatures &= ~(0x08 | 0x2000 | 0x8000);
|
||
|
||
//
|
||
// We don't know what this processor returns if we
|
||
// probe for extended CPUID support.
|
||
//
|
||
|
||
ExtendedCPUIDSupport = FALSE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Intel specific stuff
|
||
//
|
||
|
||
if (CpuVendor == CPU_INTEL) {
|
||
if (Prcb->CpuType >= 6) {
|
||
WRMSR (0x8B, 0);
|
||
CPUID (1, &Junk, &Junk, &Junk, &ProcessorFeatures);
|
||
Prcb->UpdateSignature.QuadPart = RDMSR (0x8B);
|
||
}
|
||
|
||
else if (Prcb->CpuType == 5) {
|
||
KiI386PentiumLockErrataPresent = TRUE;
|
||
}
|
||
|
||
if ( ((ProcessorSignature & 0x0FF0) == 0x0610 &&
|
||
(ProcessorSignature & 0x000F) <= 0x9) ||
|
||
|
||
((ProcessorSignature & 0x0FF0) == 0x0630 &&
|
||
(ProcessorSignature & 0x000F) <= 0x4)) {
|
||
|
||
NtBits &= ~KF_WORKING_PTE;
|
||
}
|
||
|
||
//
|
||
// Don't support prior attempts at implementing syscall/sysexit
|
||
// instructions.
|
||
//
|
||
|
||
if ((Prcb->CpuType < 6) ||
|
||
((Prcb->CpuType == 6) && (Prcb->CpuStep < 0x0303))) {
|
||
|
||
ProcessorFeatures &= ~KI_FAST_SYSCALL_SUPPORTED;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Cyrix specific stuff
|
||
//
|
||
|
||
if (CpuVendor == CPU_CYRIX) {
|
||
|
||
//
|
||
// Workaround bug 324467 which is caused by INTR being
|
||
// held high too long during an FP instruction and causing
|
||
// random Trap07 with no exception bits.
|
||
//
|
||
|
||
extern BOOLEAN KiIgnoreUnexpectedTrap07;
|
||
|
||
KiIgnoreUnexpectedTrap07 = TRUE;
|
||
|
||
//
|
||
// Workaround CMPXCHG bug to Cyrix processors where
|
||
// Family = 6, Model = 0, Stepping <= 1. Note that
|
||
// Prcb->CpuStep contains both model and stepping.
|
||
//
|
||
// Disable Locking in one of processor specific registers
|
||
// (accessible via i/o space index/data pair).
|
||
//
|
||
|
||
if ((Prcb->CpuType == 6) &&
|
||
(Prcb->CpuStep <= 1)) {
|
||
|
||
#define CRC_NDX (PUCHAR)0x22
|
||
#define CRC_DAT (CRC_NDX + 1)
|
||
#define CCR1 0xc1
|
||
|
||
UCHAR ValueCCR1;
|
||
|
||
//
|
||
// Get current setting.
|
||
//
|
||
|
||
WRITE_PORT_UCHAR(CRC_NDX, CCR1);
|
||
|
||
ValueCCR1 = READ_PORT_UCHAR(CRC_DAT);
|
||
|
||
//
|
||
// Set the NO_LOCK bit and write it back.
|
||
//
|
||
|
||
ValueCCR1 |= 0x10;
|
||
|
||
WRITE_PORT_UCHAR(CRC_NDX, CCR1);
|
||
WRITE_PORT_UCHAR(CRC_DAT, ValueCCR1);
|
||
|
||
#undef CCR1
|
||
#undef CRC_DAT
|
||
#undef CRC_NDX
|
||
}
|
||
}
|
||
|
||
//
|
||
// Check the standard CPUID feature bits.
|
||
//
|
||
// The following bits are known to work on Intel, AMD and Cyrix.
|
||
// We hope (and assume) the clone makers will follow suit.
|
||
//
|
||
|
||
if (ProcessorFeatures & 0x00000002) {
|
||
NtBits |= KF_V86_VIS | KF_CR4;
|
||
}
|
||
|
||
if (ProcessorFeatures & 0x00000008) {
|
||
NtBits |= KF_LARGE_PAGE | KF_CR4;
|
||
}
|
||
|
||
if (ProcessorFeatures & 0x00000010) {
|
||
NtBits |= KF_RDTSC;
|
||
}
|
||
|
||
//
|
||
// N.B. CMPXCHG8B MUST be done in a generic manner or clone processors
|
||
// will not be able to boot if they set this feature bit.
|
||
//
|
||
// This was incorrect in NT4 and resulted processor vendors claiming
|
||
// not to support cmpxchg8b even if they did. Whistler requires
|
||
// cmpxchg8b, work around this problems for the cases we know about.
|
||
//
|
||
// Because cmpxchg8b is a requirement for whistler, winnt32 needs to
|
||
// be modified if new processors are added to the following list.
|
||
// Also, setupldr. Both executables were modified so as to warn
|
||
// the user rather than installing an unbootable system.
|
||
//
|
||
|
||
if ((ProcessorFeatures & 0x00000100) == 0) {
|
||
|
||
ULONGLONG MsrValue;
|
||
|
||
if ((CpuVendor == CPU_TRANSMETA) &&
|
||
(Prcb->CpuType >= 5) &&
|
||
(Prcb->CpuStep >= 0x402)) {
|
||
|
||
//
|
||
// Transmeta processors have a cpuid feature bit 'mask' in
|
||
// msr 80860004. Unmask the cmpxchg8b bit.
|
||
//
|
||
|
||
MsrValue = RDMSR(0x80860004);
|
||
MsrValue |= 0x100;
|
||
WRMSR(0x80860004, MsrValue);
|
||
|
||
ProcessorFeatures |= 0x100;
|
||
|
||
} else if ((CpuVendor == CPU_CENTAUR) &&
|
||
(Prcb->CpuType >= 5)) {
|
||
|
||
//
|
||
// Centaur/IDT processors turn on the cmpxchg8b
|
||
// feature bit by setting bit 1 in MSR 107.
|
||
//
|
||
|
||
ULONG CentaurFeatureControlMSR = 0x107;
|
||
|
||
if (Prcb->CpuType >= 6) {
|
||
|
||
//
|
||
// Centaur processors (Cyrix III) turn on the cmpxchg8b
|
||
// feature bit by setting bit 1 in MSR 1107.
|
||
//
|
||
|
||
CentaurFeatureControlMSR = 0x1107;
|
||
}
|
||
|
||
MsrValue = RDMSR(CentaurFeatureControlMSR);
|
||
MsrValue |= 2;
|
||
WRMSR(CentaurFeatureControlMSR, MsrValue);
|
||
|
||
ProcessorFeatures |= 0x100;
|
||
}
|
||
}
|
||
|
||
if (ProcessorFeatures & 0x00000100) {
|
||
NtBits |= KF_CMPXCHG8B;
|
||
}
|
||
|
||
if (ProcessorFeatures & KI_FAST_SYSCALL_SUPPORTED) {
|
||
NtBits |= KF_FAST_SYSCALL;
|
||
KiFastSystemCallIsIA32 = TRUE;
|
||
}
|
||
|
||
if (ProcessorFeatures & 0x00001000) {
|
||
NtBits |= KF_MTRR;
|
||
}
|
||
|
||
if (ProcessorFeatures & 0x00002000) {
|
||
NtBits |= KF_GLOBAL_PAGE | KF_CR4;
|
||
}
|
||
|
||
if (ProcessorFeatures & 0x00008000) {
|
||
NtBits |= KF_CMOV;
|
||
}
|
||
|
||
if (ProcessorFeatures & 0x00010000) {
|
||
NtBits |= KF_PAT;
|
||
}
|
||
|
||
if (ProcessorFeatures & 0x00200000) {
|
||
NtBits |= KF_DTS;
|
||
}
|
||
|
||
if (ProcessorFeatures & 0x00800000) {
|
||
NtBits |= KF_MMX;
|
||
}
|
||
|
||
if (ProcessorFeatures & 0x01000000) {
|
||
NtBits |= KF_FXSR;
|
||
}
|
||
|
||
if (ProcessorFeatures & 0x02000000) {
|
||
NtBits |= KF_XMMI;
|
||
}
|
||
|
||
if (ProcessorFeatures & 0x04000000) {
|
||
NtBits |= KF_XMMI64;
|
||
}
|
||
|
||
if (ProcessorFeatures & 0x10000000) {
|
||
NtBits |= KF_SMT;
|
||
Prcb->LogicalProcessorsPerPhysicalProcessor = (UCHAR)(Temp >> 16);
|
||
} else {
|
||
Prcb->LogicalProcessorsPerPhysicalProcessor = 1;
|
||
}
|
||
|
||
//
|
||
// Check extended functions. First, check for existance,
|
||
// then check extended function 0x80000001 (Extended Processor
|
||
// Features) if present.
|
||
//
|
||
// Note: Intel guarantees that no processor that doesn't support
|
||
// extended CPUID functions will ever return a value with the
|
||
// most significant bit set. Microsoft asks all CPU vendors
|
||
// to make the same guarantee.
|
||
//
|
||
|
||
if (ExtendedCPUIDSupport != FALSE) {
|
||
|
||
CPUID(0x80000000, &Temp, &Junk, &Junk, &Junk);
|
||
|
||
//
|
||
// Sanity check the result, assuming there are no more
|
||
// than 256 extended feature functions (should be valid
|
||
// for a little while).
|
||
//
|
||
|
||
if ((Temp & 0xffffff00) == 0x80000000) {
|
||
|
||
//
|
||
// Check extended processor features. These, by definition,
|
||
// can vary on a processor by processor basis.
|
||
//
|
||
|
||
if (Temp >= 0x80000001) {
|
||
|
||
CPUID(0x80000001, &Temp, &Junk, &Junk, &ExtendedProcessorFeatures);
|
||
|
||
//
|
||
// With these, we can only do what we're told.
|
||
//
|
||
|
||
switch (CpuVendor) {
|
||
case CPU_AMD:
|
||
|
||
if (ExtendedProcessorFeatures & 0x80000000) {
|
||
NtBits |= KF_3DNOW;
|
||
}
|
||
|
||
#if 0
|
||
|
||
//
|
||
// There is a security hole with this implementation
|
||
// of fast system call such that it is possible to
|
||
// end up in the trap01 handler running on the user
|
||
// stack (ie not kernel stack). Unfortunately this
|
||
// prohibits use of this instruction pair.
|
||
//
|
||
|
||
if (ExtendedProcessorFeatures & 0x00000800) {
|
||
|
||
//
|
||
// This processor supports AMD's implementation
|
||
// of SYSENTER/SYSEXIT (SYSCALL/SYSRET). Use this
|
||
// unless it also supports the IA32 version.
|
||
//
|
||
|
||
if ((NtBits & KF_FAST_SYSCALL) == 0) {
|
||
NtBits |= KF_FAST_SYSCALL;
|
||
}
|
||
}
|
||
|
||
#endif
|
||
|
||
#if 0
|
||
|
||
if (ExtendedProcessorFeatures & 0x00100000) {
|
||
Temp = (ULONG)RDMSR(0xc0000080);
|
||
Temp |= 0x800;
|
||
WRMSR(0xc0000080, (ULONGLONG)Temp);
|
||
KeErrorMask = 0x9;
|
||
MmPaeErrMask = 0x8;
|
||
MmPaeMask = 0x8000000000000000UI64;
|
||
}
|
||
#endif
|
||
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return NtBits;
|
||
}
|
||
|
||
VOID
|
||
KiGetCacheInformation(
|
||
VOID
|
||
)
|
||
{
|
||
#define CPUID_REG_COUNT 4
|
||
ULONG CpuidData[CPUID_REG_COUNT];
|
||
ULONG Line = 64;
|
||
ULONG Size = 0;
|
||
ULONG AdjustedSize = 0;
|
||
UCHAR Assoc = 0;
|
||
ULONG CpuVendor;
|
||
PKPCR Pcr;
|
||
|
||
//
|
||
// Set default.
|
||
//
|
||
|
||
Pcr = KeGetPcr();
|
||
|
||
Pcr->SecondLevelCacheSize = 0;
|
||
|
||
//
|
||
// Determine the processor manufacturer
|
||
//
|
||
|
||
CpuVendor = KiGetCpuVendor();
|
||
|
||
if (CpuVendor == CPU_NONE) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Obtain Cache size information for those processors on which
|
||
// we know how.
|
||
//
|
||
|
||
switch (CpuVendor) {
|
||
case CPU_INTEL:
|
||
|
||
CPUID(0, CpuidData, CpuidData+1, CpuidData+2, CpuidData+3);
|
||
|
||
//
|
||
// Check this processor supports CPUID function 2 which is the
|
||
// one that returns cache size info.
|
||
//
|
||
|
||
if (CpuidData[0] >= 2) {
|
||
|
||
//
|
||
// The above returns a series of bytes. (In EAX, EBX, ECX
|
||
// and EDX). The least significant byte (of EAX) gives the
|
||
// number of times CPUID(2 ...) should be issued to return
|
||
// the complete set of data. The bytes are self describing
|
||
// data.
|
||
//
|
||
// In particular, the bytes describing the L2 cache size
|
||
// will be in the following set (and meaning)
|
||
//
|
||
// 0x40 0 bytes
|
||
// 0x41 128K bytes
|
||
// 0x42 256K bytes
|
||
// 0x43 512K bytes
|
||
// 0x44 1024K bytes
|
||
// 0x45 2048K bytes
|
||
// 0x46 4096K bytes
|
||
//
|
||
// I am extrapolating the above as anything in the range
|
||
// 0x41 thru 0x4f can be computed as
|
||
//
|
||
// 128KB << (descriptor - 0x41)
|
||
//
|
||
// The Intel folks say keep it to a reasonable upper bound,
|
||
// eg 49.
|
||
//
|
||
// N.B. the range 0x80 .. 0x86 indicates the same cache
|
||
// sizes but 8 way associative.
|
||
//
|
||
// Also, the most significant bit of each register indicates
|
||
// whether not the register contains valid information.
|
||
// 0 == Valid, 1 == InValid.
|
||
//
|
||
|
||
ULONG CpuidIterations;
|
||
ULONG i;
|
||
ULONG CpuidReg;
|
||
|
||
BOOLEAN FirstPass = TRUE;
|
||
|
||
do {
|
||
CPUID(2, CpuidData, CpuidData+1, CpuidData+2, CpuidData+3);
|
||
|
||
if (FirstPass) {
|
||
|
||
//
|
||
// Get the iteration count from the first byte
|
||
// of the returned data then replace that byte
|
||
// with 0 (a null descriptor).
|
||
//
|
||
|
||
CpuidIterations = CpuidData[0] & 0xff;
|
||
CpuidData[0] &= 0xffffff00;
|
||
|
||
FirstPass = FALSE;
|
||
}
|
||
|
||
for (i = 0; i < CPUID_REG_COUNT; i++) {
|
||
|
||
CpuidReg = CpuidData[i];
|
||
|
||
if (CpuidReg & 0x80000000) {
|
||
|
||
//
|
||
// Register doesn't contain valid data,
|
||
// skip it.
|
||
//
|
||
|
||
continue;
|
||
}
|
||
|
||
while (CpuidReg) {
|
||
|
||
//
|
||
// Get LS Byte from this DWORD and remove the
|
||
// byte.
|
||
//
|
||
|
||
UCHAR Descriptor = (UCHAR)(CpuidReg & 0xff);
|
||
CpuidReg >>= 8;
|
||
|
||
if (Descriptor == 0) {
|
||
|
||
//
|
||
// NULL descriptor
|
||
//
|
||
|
||
continue;
|
||
}
|
||
|
||
if (((Descriptor > 0x40) && (Descriptor <= 0x47)) ||
|
||
((Descriptor > 0x78) && (Descriptor <= 0x7c)) ||
|
||
((Descriptor > 0x80) && (Descriptor <= 0x87))) {
|
||
|
||
//
|
||
// L2 descriptor.
|
||
//
|
||
// To date, for all the descriptors we know
|
||
// about those above 0x78 are 8 way and those
|
||
// below are 4 way.
|
||
//
|
||
|
||
Assoc = Descriptor >= 0x79 ? 8 : 4;
|
||
|
||
if (((Descriptor & 0xf8) == 0x78) &&
|
||
(Line < 128)) {
|
||
Line = 128;
|
||
}
|
||
Descriptor &= 0x07;
|
||
|
||
//
|
||
// Assert the descriptor is in the range we
|
||
// officially know about. If this hits on
|
||
// a checked build, check with Intel about
|
||
// the interpretation.
|
||
//
|
||
|
||
ASSERT(Descriptor <= 0x6);
|
||
|
||
Size = 0x10000 << Descriptor;
|
||
if ((Size / Assoc) > AdjustedSize) {
|
||
AdjustedSize = Size / Assoc;
|
||
Pcr->SecondLevelCacheSize = Size;
|
||
Pcr->SecondLevelCacheAssociativity = Assoc;
|
||
}
|
||
|
||
} else if ((Descriptor > 0x21) && (Descriptor <= 0x29)) {
|
||
if (Line < 128) {
|
||
Line = 128;
|
||
}
|
||
Assoc = 8;
|
||
switch (Descriptor) {
|
||
case 0x22:
|
||
Size = 512 * 1024;
|
||
Assoc = 4;
|
||
break;
|
||
case 0x23:
|
||
Size = 1024 * 1024;
|
||
break;
|
||
case 0x25:
|
||
Size = 2048 * 1024;
|
||
break;
|
||
case 0x29:
|
||
Size = 4096 * 1024;
|
||
break;
|
||
default:
|
||
Size = 0;
|
||
break;
|
||
}
|
||
if ((Size / Assoc) > AdjustedSize) {
|
||
AdjustedSize = Size / Assoc;
|
||
Pcr->SecondLevelCacheSize = Size;
|
||
Pcr->SecondLevelCacheAssociativity = Assoc;
|
||
}
|
||
} else if ((Descriptor > 0x65) && (Descriptor < 0x69)) {
|
||
|
||
//
|
||
// L1 Descriptor with line size of 64 bytes.
|
||
// N.B. These are the only ones we know about,
|
||
// we will get it wrong on future processors
|
||
// unless a less brain dead way of determining
|
||
// line size is provided.
|
||
//
|
||
|
||
KePrefetchNTAGranularity = 64;
|
||
}
|
||
|
||
//
|
||
// else if (do other descriptors)
|
||
//
|
||
|
||
} // while more bytes in this register
|
||
|
||
} // for each register
|
||
|
||
//
|
||
// Note: Always run thru all iterations indicated by
|
||
// the first to ensure a subsequent call won't start
|
||
// part way thru.
|
||
//
|
||
|
||
} while (--CpuidIterations);
|
||
}
|
||
break;
|
||
case CPU_AMD:
|
||
|
||
//
|
||
// Get L1 Cache Data.
|
||
//
|
||
|
||
CPUID(0x80000000, CpuidData, CpuidData+1, CpuidData+2, CpuidData+3);
|
||
if (CpuidData[0] < 0x80000005) {
|
||
|
||
//
|
||
// This processor doesn't support L1 cache details.
|
||
//
|
||
|
||
break;
|
||
}
|
||
CPUID(0x80000005, CpuidData, CpuidData+1, CpuidData+2, CpuidData+3);
|
||
KePrefetchNTAGranularity = CpuidData[2] & 0xff;
|
||
|
||
//
|
||
// Get L2 data.
|
||
//
|
||
|
||
CPUID(0x80000000, CpuidData, CpuidData+1, CpuidData+2, CpuidData+3);
|
||
if (CpuidData[0] < 0x80000006) {
|
||
|
||
//
|
||
// This processor doesn't support L2 cache details.
|
||
//
|
||
|
||
break;
|
||
}
|
||
CPUID(0x80000006, CpuidData, CpuidData+1, CpuidData+2, CpuidData+3);
|
||
Line = CpuidData[2] & 0xff;
|
||
switch ((CpuidData[2] >> 12) & 0xf) {
|
||
case 0x2: Assoc = 2; break;
|
||
case 0x4: Assoc = 4; break;
|
||
case 0x6: Assoc = 8; break;
|
||
case 0x8: Assoc = 16; break;
|
||
|
||
//
|
||
// ff is really fully associative, just represent as 16 way.
|
||
//
|
||
case 0xff: Assoc = 16; break;
|
||
default: Assoc = 1; break;
|
||
}
|
||
Size = (CpuidData[2] >> 16) << 10;
|
||
if ((Pcr->PrcbData.CpuType == 0x6) &&
|
||
(Pcr->PrcbData.CpuStep == 0x300)) {
|
||
|
||
//
|
||
// Model 6,3,0 uses a different algorithm to report cache
|
||
// size.
|
||
//
|
||
|
||
Size = 64 * 1024;
|
||
}
|
||
|
||
Pcr->SecondLevelCacheAssociativity = Assoc;
|
||
Pcr->SecondLevelCacheSize = Size;
|
||
break;
|
||
}
|
||
|
||
if (Line > KeLargestCacheLine) {
|
||
KeLargestCacheLine = Line;
|
||
}
|
||
|
||
#undef CPUID_REG_COUNT
|
||
}
|
||
|
||
#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;
|
||
PUCHAR PatchLocation;
|
||
|
||
//
|
||
// 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,
|
||
&Ki386EnableCurrentLargePage,
|
||
&Ki386EnableCurrentLargePageEnd )) {
|
||
|
||
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)
|
||
);
|
||
}
|
||
|
||
#if !defined(NT_UP)
|
||
|
||
//
|
||
// If some processor doesn't have proper MP PTE implementation,
|
||
// then use a synchronous TB shoot down handler
|
||
//
|
||
|
||
if (!(KeFeatureBits & KF_WORKING_PTE)) {
|
||
NumberProcessors = KeNumberProcessors;
|
||
KiIpiGenericCall (
|
||
(PKIPI_BROADCAST_WORKER) Ki386UseSynchronousTbFlush,
|
||
(ULONG)(&NumberProcessors)
|
||
);
|
||
}
|
||
|
||
#endif
|
||
|
||
//
|
||
// If PAT or MTRR supported but the HAL indicates it shouldn't
|
||
// be used (eg on a Shared Memory Cluster), drop the feature.
|
||
//
|
||
|
||
if (KeFeatureBits & (KF_PAT | KF_MTRR)) {
|
||
|
||
NTSTATUS Status;
|
||
BOOLEAN UseFrameBufferCaching;
|
||
ULONG Size;
|
||
|
||
Status = HalQuerySystemInformation(
|
||
HalFrameBufferCachingInformation,
|
||
sizeof(UseFrameBufferCaching),
|
||
&UseFrameBufferCaching,
|
||
&Size
|
||
);
|
||
|
||
if (NT_SUCCESS(Status) &&
|
||
(UseFrameBufferCaching == FALSE)) {
|
||
|
||
//
|
||
// Hal says don't use.
|
||
//
|
||
|
||
KeFeatureBits &= ~(KF_PAT | KF_MTRR);
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// If PAT is supported then initialize it.
|
||
//
|
||
|
||
if (KeFeatureBits & KF_PAT) {
|
||
KiInitializePAT();
|
||
}
|
||
|
||
|
||
//
|
||
// Check to see if the floating point emulator should be used.
|
||
//
|
||
|
||
SharedUserData->ProcessorFeatures[PF_FLOATING_POINT_PRECISION_ERRATA] =
|
||
FALSE;
|
||
|
||
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 (KeI386NpxPresent) {
|
||
|
||
//
|
||
// A coprocessor is present, check to see if the precision
|
||
// errata exists.
|
||
//
|
||
|
||
double Dividend, Divisor;
|
||
BOOLEAN 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:
|
||
}
|
||
if (PrecisionErrata) {
|
||
KeI386NpxPresent = FALSE;
|
||
SharedUserData->ProcessorFeatures[PF_FLOATING_POINT_PRECISION_ERRATA] = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
break;
|
||
|
||
default:
|
||
|
||
//
|
||
// Unknown setting - use the emulator
|
||
//
|
||
|
||
KeI386NpxPresent = FALSE;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Setup processor features, and install emulator if needed
|
||
//
|
||
|
||
SharedUserData->ProcessorFeatures[PF_FLOATING_POINT_EMULATED] =
|
||
!KeI386NpxPresent;
|
||
|
||
if (!KeI386NpxPresent) {
|
||
|
||
//
|
||
// MMx, fast save/restore, streaming SIMD not available when
|
||
// emulator is used. (Nor FP errata).
|
||
//
|
||
|
||
KeFeatureBits &= ~(KF_MMX | KF_FXSR | KF_XMMI | KF_XMMI64);
|
||
KeI386XMMIPresent = FALSE;
|
||
KeI386FxsrPresent = FALSE;
|
||
|
||
SharedUserData->ProcessorFeatures[PF_MMX_INSTRUCTIONS_AVAILABLE] =
|
||
SharedUserData->ProcessorFeatures[PF_XMMI_INSTRUCTIONS_AVAILABLE] =
|
||
SharedUserData->ProcessorFeatures[PF_3DNOW_INSTRUCTIONS_AVAILABLE] =
|
||
SharedUserData->ProcessorFeatures[PF_XMMI64_INSTRUCTIONS_AVAILABLE] =
|
||
SharedUserData->ProcessorFeatures[PF_FLOATING_POINT_PRECISION_ERRATA] =
|
||
FALSE;
|
||
}
|
||
|
||
//
|
||
// If CR4 exists, enable DE extensions for IO breakpoints
|
||
//
|
||
|
||
if (KeFeatureBits & KF_CR4) {
|
||
NumberProcessors = KeNumberProcessors;
|
||
|
||
KiIpiGenericCall (
|
||
(PKIPI_BROADCAST_WORKER) Ki386EnableDE,
|
||
(ULONG)(&NumberProcessors)
|
||
);
|
||
}
|
||
|
||
//
|
||
// If FXSR feature is supported, set OSFXSR (bit 9) in CR4
|
||
//
|
||
|
||
if (KeFeatureBits & KF_FXSR) {
|
||
NumberProcessors = KeNumberProcessors;
|
||
|
||
KiIpiGenericCall (
|
||
(PKIPI_BROADCAST_WORKER) Ki386EnableFxsr,
|
||
(ULONG)(&NumberProcessors)
|
||
);
|
||
|
||
|
||
//
|
||
// If XMMI feature is supported,
|
||
// a. Hook int 19 handler
|
||
// b. Set OSXMMEXCPT (bit 10) in CR4
|
||
// c. Enable use of fast XMMI based zero page routines.
|
||
// d. Remove return instruction at start of prefetch routine.
|
||
//
|
||
|
||
if (KeFeatureBits & KF_XMMI) {
|
||
KiIpiGenericCall (
|
||
(PKIPI_BROADCAST_WORKER) Ki386EnableXMMIExceptions,
|
||
(ULONG)(&NumberProcessors)
|
||
);
|
||
|
||
KeZeroPage = KiXMMIZeroPage;
|
||
KeZeroPageFromIdleThread = KiXMMIZeroPageNoSave;
|
||
|
||
*(PUCHAR)&RtlPrefetchMemoryNonTemporal = 0x90;
|
||
}
|
||
|
||
|
||
} else {
|
||
#ifndef NT_UP
|
||
//
|
||
// Patch the fxsave instruction in SwapContext to use
|
||
// "fnsave {dd, 31}, fwait {9b}"
|
||
//
|
||
ASSERT( ((ULONG)&ScPatchFxe-(ULONG)&ScPatchFxb) >= 3);
|
||
|
||
PatchLocation = (PUCHAR)&ScPatchFxb;
|
||
|
||
*PatchLocation++ = 0xdd;
|
||
*PatchLocation++ = 0x31;
|
||
*PatchLocation++ = 0x9b;
|
||
|
||
while (PatchLocation < (PUCHAR)&ScPatchFxe) {
|
||
//
|
||
// Put nop's in the remaining bytes
|
||
//
|
||
*PatchLocation++ = 0x90;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
//
|
||
// If the system (ie all processors) supports fast system
|
||
// call/return, initialize the machine specific registers
|
||
// required to support it.
|
||
//
|
||
|
||
KiRestoreFastSyscallReturnState();
|
||
|
||
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 within a MHz, done
|
||
//
|
||
|
||
if (Index) {
|
||
if (pSamp->MHz == pSamp[-1].MHz ||
|
||
pSamp->MHz == pSamp[-1].MHz + 1 ||
|
||
pSamp->MHz == pSamp[-1].MHz - 1) {
|
||
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 MTRRs are supported and PAT not supported, initialize MTRRs
|
||
// per processor
|
||
//
|
||
|
||
if (KeFeatureBits & KF_MTRR) {
|
||
KiInitializeMTRR ( (BOOLEAN) (ActiveProcessors ? FALSE : TRUE));
|
||
}
|
||
|
||
//
|
||
// If the processor is a AMD K6 with MTRR support then
|
||
// perform processor specific initialization.
|
||
//
|
||
|
||
if (KeFeatureBits & KF_AMDK6MTRR) {
|
||
KiAmdK6InitializeMTRR();
|
||
}
|
||
|
||
//
|
||
// Apply Pentium workaround if needed
|
||
//
|
||
|
||
if (KiI386PentiumLockErrataPresent) {
|
||
KiI386PentiumLockErrataFixup ();
|
||
}
|
||
|
||
//
|
||
// If this processor supports fast floating save/restore,
|
||
// determine the MXCSR mask value that should be used.
|
||
//
|
||
|
||
if (KeFeatureBits & KF_FXSR) {
|
||
|
||
//
|
||
// Get base of NPX save area.
|
||
//
|
||
//
|
||
|
||
PFX_SAVE_AREA NpxFrame;
|
||
KFLOATING_SAVE Save;
|
||
ULONG MXCsrMask = 0xFFBF;
|
||
|
||
NpxFrame = (PFX_SAVE_AREA)
|
||
(((ULONG)(KeGetCurrentThread()->InitialStack) -
|
||
sizeof(FX_SAVE_AREA)));
|
||
|
||
NpxFrame->U.FxArea.MXCsrMask = 0;
|
||
Kix86FxSave(NpxFrame);
|
||
|
||
//
|
||
// If the processor supplied a mask value, use
|
||
// that, otherwise set the default value.
|
||
//
|
||
|
||
if (NpxFrame->U.FxArea.MXCsrMask != 0) {
|
||
MXCsrMask = NpxFrame->U.FxArea.MXCsrMask;
|
||
}
|
||
|
||
//
|
||
// All processors must use the same (most restrictive)
|
||
// value.
|
||
//
|
||
|
||
if (KiMXCsrMask == 0) {
|
||
KiMXCsrMask = MXCsrMask;
|
||
} else if (KiMXCsrMask != MXCsrMask) {
|
||
KeBugCheckEx(MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED,
|
||
KF_FXSR,
|
||
KiMXCsrMask,
|
||
MXCsrMask,
|
||
0);
|
||
}
|
||
|
||
KiMXCsrMask &= MXCsrMask;
|
||
}
|
||
}
|
||
}
|
||
|
||
KeRevertToUserAffinityThread();
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
VOID
|
||
KeOptimizeProcessorControlState (
|
||
VOID
|
||
)
|
||
{
|
||
Ke386ConfigureCyrixProcessor ();
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
KeSetup80387OrEmulate (
|
||
IN PVOID *R3EmulatorTable
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called by PS initialization after loading NTDLL.
|
||
|
||
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 NTDLL, 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;
|
||
|
||
if (!KeI386NpxPresent) {
|
||
|
||
//
|
||
// 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);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Set affinity back to the original value.
|
||
//
|
||
|
||
KeRevertToUserAffinityThread();
|
||
|
||
//
|
||
// 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);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
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 deleted by the
|
||
// call to KiMoveRegTree).
|
||
//
|
||
}
|
||
|
||
//
|
||
// Remove source node
|
||
//
|
||
|
||
return NtDeleteKey (Source);
|
||
}
|
||
|
||
VOID
|
||
KiI386PentiumLockErrataFixup (
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called once on every processor when
|
||
KiI386PentiumLockErrataPresent is TRUE.
|
||
|
||
This routine replaces the local IDT with an IDT that has the first 7 IDT
|
||
entries on their own page and returns the first page to the caller to
|
||
be marked as read-only. This causes the processor to trap-0e fault when
|
||
the errata occurs. Special code in the trap-0e handler detects the
|
||
problem and performs the proper fixup.
|
||
|
||
Arguments:
|
||
|
||
FixupPage - Returns a virtual address of a page to be marked read-only
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
KDESCRIPTOR IdtDescriptor;
|
||
ULONG OrginalBase;
|
||
PUCHAR NewBase, BasePage;
|
||
BOOLEAN Enable;
|
||
BOOLEAN Status;
|
||
|
||
|
||
#define IDT_SKIP (7 * sizeof (KIDTENTRY))
|
||
|
||
//
|
||
// Allocate memory for a new copy of the processor's IDT
|
||
//
|
||
|
||
BasePage = MmAllocateIndependentPages (2*PAGE_SIZE, 0);
|
||
|
||
//
|
||
// The IDT base is such that the first 7 entries are on the
|
||
// first (read-only) page, and the remaining entries are on the
|
||
// second (read-write) page
|
||
//
|
||
|
||
NewBase = BasePage + PAGE_SIZE - IDT_SKIP;
|
||
|
||
//
|
||
// Disable interrupts on this processor while updating the IDT base
|
||
//
|
||
|
||
Enable = KeDisableInterrupts();
|
||
|
||
//
|
||
// Copy Old IDT to new IDT
|
||
//
|
||
|
||
_asm {
|
||
sidt IdtDescriptor.Limit
|
||
}
|
||
|
||
RtlCopyMemory ((PVOID) NewBase,
|
||
(PVOID) IdtDescriptor.Base,
|
||
IdtDescriptor.Limit + 1
|
||
);
|
||
|
||
IdtDescriptor.Base = (ULONG) NewBase;
|
||
|
||
//
|
||
// Set the new IDT
|
||
//
|
||
|
||
_asm {
|
||
lidt IdtDescriptor.Limit
|
||
}
|
||
|
||
//
|
||
// Update the PCR
|
||
//
|
||
|
||
KeGetPcr()->IDT = (PKIDTENTRY) NewBase;
|
||
|
||
//
|
||
// Restore interrupts
|
||
//
|
||
|
||
KeEnableInterrupts(Enable);
|
||
|
||
//
|
||
// Mark the first page which contains IDT entries 0-6 as read-only
|
||
//
|
||
|
||
Status = MmSetPageProtection (BasePage, PAGE_SIZE, PAGE_READONLY);
|
||
ASSERT (Status);
|
||
}
|
||
|
||
VOID
|
||
KiLogMcaErrors (
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
Routine Description:
|
||
This routine is called to check for remembered machine checks.
|
||
If a machine check crashed the system, this is the last chance
|
||
to save information about it, at the time the system reboots.
|
||
|
||
Arguments:
|
||
none
|
||
|
||
Return Value:
|
||
none
|
||
--*/
|
||
|
||
{
|
||
PIO_ERROR_LOG_PACKET ErrLog;
|
||
MCA_EXCEPTION Exception;
|
||
PKPRCB Prcb;
|
||
PWSTR PDest;
|
||
PWSTR PSrc;
|
||
WCHAR PString[20];
|
||
NTSTATUS Status;
|
||
ULONG ToDo;
|
||
ULONG Length;
|
||
PUCHAR PData;
|
||
ULONG RegSet;
|
||
KAFFINITY ActiveProcessors, CurrentAffinity;
|
||
HAL_MCA_INTERFACE Mca;
|
||
UCHAR BankNumber;
|
||
|
||
//
|
||
// Current Pentium IIIs don't support the retention of machine
|
||
// check information across a warm reset. This should've meant
|
||
// that this function wouldn't generate any eventlog entries on
|
||
// those processors. However, this code was logging non-fatal
|
||
// MCAs created by BIOS initialization resulting in spurious
|
||
// eventlog entries that did not indicate a reliability problem.
|
||
//
|
||
// Now this code is only run on Intel processors that are
|
||
// Pentium 4-derived or later i.e. where the MCA warm reset retention
|
||
// functionality is definitely implemented.
|
||
//
|
||
|
||
Prcb = KeGetCurrentPrcb();
|
||
if ((strcmp((PCHAR) Prcb->VendorString, CmpIntelID) == 0) &&
|
||
(KeProcessorLevel < 0xF)) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Obtain the HAL interface to the Machine Check Architecture
|
||
// register set.
|
||
//
|
||
|
||
Status = HalQuerySystemInformation(HalQueryMcaInterface,
|
||
sizeof(Mca),
|
||
&Mca,
|
||
&Length);
|
||
|
||
if (Status != STATUS_SUCCESS) {
|
||
return;
|
||
}
|
||
ASSERT(Length == sizeof(Mca));
|
||
|
||
//
|
||
// Clear the exception data and indicate to the HAL that we
|
||
// support extended MCA data. (VersionNumber = 2).
|
||
//
|
||
|
||
RtlZeroMemory(&Exception, sizeof(Exception));
|
||
Exception.VersionNumber = 2;
|
||
|
||
//
|
||
// Obtain exclusive access to the MCA registers.
|
||
//
|
||
|
||
Mca.Lock();
|
||
|
||
//
|
||
// Check for remembered machine check errors on each processor
|
||
// in the system.
|
||
//
|
||
|
||
ActiveProcessors = KeActiveProcessors;
|
||
|
||
for (CurrentAffinity = 1; ActiveProcessors; CurrentAffinity <<= 1) {
|
||
|
||
if ((CurrentAffinity & ActiveProcessors) == 0) {
|
||
continue;
|
||
}
|
||
ActiveProcessors ^= CurrentAffinity;
|
||
|
||
KeSetSystemAffinityThread(CurrentAffinity);
|
||
|
||
//
|
||
// Look for a machine check on this processor.
|
||
//
|
||
|
||
for (BankNumber = 0; TRUE; BankNumber++) {
|
||
|
||
Status = Mca.ReadRegister(BankNumber, &Exception);
|
||
|
||
if (Status == STATUS_UNSUCCESSFUL) {
|
||
|
||
//
|
||
// No error this bank, examine next.
|
||
//
|
||
|
||
continue;
|
||
}
|
||
|
||
if (Status != STATUS_SUCCESS) {
|
||
|
||
//
|
||
// No such register, done looking on this processor.
|
||
//
|
||
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Have a machine check, try to report it.
|
||
//
|
||
|
||
ErrLog = IoAllocateGenericErrorLogEntry(ERROR_LOG_MAXIMUM_SIZE);
|
||
if (!ErrLog) {
|
||
|
||
//
|
||
// Unable to allocate an error log entry? Give up.
|
||
//
|
||
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Fill it in and write it out.
|
||
//
|
||
// "Application popup: %1 %2"
|
||
//
|
||
|
||
ErrLog->DumpDataSize = (USHORT)MCA_EXCEPTION_V1_SIZE;
|
||
ErrLog->NumberOfStrings = 0;
|
||
ErrLog->StringOffset =
|
||
FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData)
|
||
+ MCA_EXCEPTION_V1_SIZE;
|
||
ErrLog->StringOffset =
|
||
(ErrLog->StringOffset + sizeof(PWSTR)-1)
|
||
/ sizeof(PWSTR);
|
||
ErrLog->StringOffset *= sizeof(PWSTR);
|
||
ErrLog->ErrorCode = STATUS_LOG_HARD_ERROR;
|
||
ErrLog->UniqueErrorValue = Exception.u.Mca.BankNumber;
|
||
ErrLog->FinalStatus = STATUS_LOG_HARD_ERROR;
|
||
|
||
RtlCopyMemory(ErrLog->DumpData, &Exception, MCA_EXCEPTION_V1_SIZE);
|
||
|
||
PDest = (PWSTR)((PCHAR)ErrLog+ErrLog->StringOffset);
|
||
PSrc = L"Machine Check:";
|
||
RtlCopyMemory(PDest, PSrc, (wcslen(PSrc)+1) * sizeof(WCHAR));
|
||
PDest += wcslen(PSrc)+1;
|
||
ErrLog->NumberOfStrings++;
|
||
|
||
PSrc = L"Banks";
|
||
RtlCopyMemory(PDest, PSrc, (wcslen(PSrc)+1) * sizeof(WCHAR));
|
||
PDest += wcslen(PSrc)+1;
|
||
ErrLog->NumberOfStrings++;
|
||
|
||
IoWriteErrorLogEntry(ErrLog);
|
||
|
||
//
|
||
// There are Exception.ExtCnt entries in Exception.ExtReg,
|
||
// plus Exception.ExtCnt itself.
|
||
//
|
||
|
||
ToDo = (Exception.ExtCnt+1) * sizeof(Exception.ExtReg[0]);
|
||
PData = ((PUCHAR)&Exception)+MCA_EXCEPTION_V1_SIZE;
|
||
RegSet = 0;
|
||
while (ToDo > 0) {
|
||
|
||
//
|
||
// Have additional machine check data, try to report it.
|
||
//
|
||
|
||
if (ToDo > 8 * sizeof(ULONGLONG)) {
|
||
Length = 8 * sizeof(ULONGLONG);
|
||
} else {
|
||
Length = ToDo;
|
||
}
|
||
ErrLog = IoAllocateGenericErrorLogEntry(ERROR_LOG_MAXIMUM_SIZE);
|
||
if (!ErrLog) {
|
||
break; // Inner loop only
|
||
}
|
||
|
||
//
|
||
// Fill it in and write it out.
|
||
//
|
||
// "Application popup: %1 %2"
|
||
//
|
||
|
||
ErrLog->DumpDataSize = (USHORT)Length;
|
||
ErrLog->NumberOfStrings = 0;
|
||
ErrLog->StringOffset =
|
||
FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData)
|
||
+ (USHORT)Length;
|
||
ErrLog->StringOffset =
|
||
(ErrLog->StringOffset + sizeof(PWSTR)-1)
|
||
/ sizeof(PWSTR);
|
||
ErrLog->StringOffset *= sizeof(PWSTR);
|
||
ErrLog->ErrorCode = STATUS_LOG_HARD_ERROR;
|
||
ErrLog->UniqueErrorValue = RegSet;
|
||
ErrLog->FinalStatus = STATUS_LOG_HARD_ERROR;
|
||
|
||
RtlCopyMemory(ErrLog->DumpData, PData, Length);
|
||
|
||
PDest = (PWSTR)((PCHAR)ErrLog+ErrLog->StringOffset);
|
||
PSrc = L"Machine Check: Regs";
|
||
RtlCopyMemory(PDest, PSrc, (wcslen(PSrc)+1) * sizeof(WCHAR));
|
||
PDest += wcslen(PSrc)+1;
|
||
ErrLog->NumberOfStrings++;
|
||
|
||
PSrc = _itow(RegSet, PString, 10);
|
||
RtlCopyMemory(PDest, PSrc, (wcslen(PSrc)+1) * sizeof(WCHAR));
|
||
PDest += wcslen(PSrc)+1;
|
||
ErrLog->NumberOfStrings++;
|
||
|
||
IoWriteErrorLogEntry(ErrLog);
|
||
PData += Length;
|
||
ToDo -= Length;
|
||
RegSet+= 8;
|
||
}
|
||
|
||
//
|
||
// Reset for next iteration.
|
||
//
|
||
|
||
RtlZeroMemory(&Exception, sizeof(Exception));
|
||
Exception.VersionNumber = 2;
|
||
}
|
||
}
|
||
KeRevertToUserAffinityThread();
|
||
Mca.Unlock();
|
||
}
|