1643 lines
36 KiB
C
1643 lines
36 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
misc.c
|
||
|
||
Abstract:
|
||
|
||
This module implements machine dependent miscellaneous kernel functions.
|
||
|
||
Author:
|
||
|
||
Ken Reneris 7-5-95
|
||
|
||
Environment:
|
||
|
||
Kernel mode only.
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "ki.h"
|
||
#include "fastsys.inc"
|
||
|
||
extern BOOLEAN KeI386FxsrPresent;
|
||
extern BOOLEAN KeI386XMMIPresent;
|
||
extern UCHAR KiSystemCallExitBranch[];
|
||
extern UCHAR KiFastCallEntry[];
|
||
extern UCHAR KiDefaultSystemCall[];
|
||
extern UCHAR KiSystemCallExit[];
|
||
extern UCHAR KiSystemCallExit2[];
|
||
extern UCHAR KiSystemCallExit3[];
|
||
extern UCHAR KiFastSystemCallIa32[];
|
||
extern UCHAR KiFastSystemCallAmdK6[];
|
||
extern ULONG_PTR KiSystemCallExitAdjust;
|
||
extern ULONG KiFastSystemCallDisable;
|
||
PVOID KiFastSystemCallCode = KiDefaultSystemCall;
|
||
|
||
ULONG_PTR KiSystemCallExitAdjust;
|
||
UCHAR KiSystemCallExitAdjusted;
|
||
BOOLEAN KiFastSystemCallIsIA32;
|
||
BOOLEAN KiFastCallCopyDoneOnce = FALSE;
|
||
|
||
VOID
|
||
KeRestoreMtrr (
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
KeRestorePAT(
|
||
VOID
|
||
);
|
||
//
|
||
//
|
||
// Internal format of the floating_save structure which is passed
|
||
//
|
||
typedef struct _CONTROL_WORD {
|
||
USHORT ControlWord;
|
||
ULONG MXCsr;
|
||
} CONTROL_WORD, *PCONTROL_WORD;
|
||
|
||
typedef struct {
|
||
UCHAR Flags;
|
||
KIRQL Irql;
|
||
KIRQL PreviousNpxIrql;
|
||
UCHAR Spare[2];
|
||
|
||
union {
|
||
CONTROL_WORD Fcw;
|
||
PFX_SAVE_AREA Context;
|
||
ULONG_PTR ContextAddressAsULONG;
|
||
} u;
|
||
ULONG Cr0NpxState;
|
||
|
||
PKTHREAD Thread; // debug
|
||
|
||
} FLOAT_SAVE, *PFLOAT_SAVE;
|
||
|
||
|
||
#define FLOAT_SAVE_COMPLETE_CONTEXT 0x01
|
||
#define FLOAT_SAVE_FREE_CONTEXT_HEAP 0x02
|
||
#define FLOAT_SAVE_VALID 0x04
|
||
#define FLOAT_SAVE_ALIGN_ADJUSTED 0x08
|
||
#define FLOAT_SAVE_RESERVED 0xF0
|
||
|
||
//
|
||
// Allocate Pool returns a pointer which is 8 byte aligned. The
|
||
// floating point save area needs to be 16 byte aligned. When
|
||
// allocating the save area we add the difference and adjust if
|
||
// needed.
|
||
//
|
||
|
||
#define ALIGN_ADJUST 8
|
||
|
||
|
||
NTSTATUS
|
||
KeSaveFloatingPointState (
|
||
OUT PKFLOATING_SAVE PublicFloatSave
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine saves the thread's current non-volatile NPX state,
|
||
and sets a new initial floating point state for the caller.
|
||
|
||
Arguments:
|
||
|
||
FloatSave - receives the current non-volatile npx state for the thread
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
{
|
||
PKTHREAD Thread;
|
||
PFX_SAVE_AREA NpxFrame;
|
||
KIRQL Irql;
|
||
USHORT ControlWord;
|
||
ULONG MXCsr;
|
||
PKPRCB Prcb;
|
||
PFLOAT_SAVE FloatSave;
|
||
|
||
//
|
||
// If the system is using floating point emulation, then
|
||
// return an error
|
||
//
|
||
|
||
if (!KeI386NpxPresent) {
|
||
return STATUS_ILLEGAL_FLOAT_CONTEXT;
|
||
}
|
||
|
||
//
|
||
// Get the current irql and thread
|
||
//
|
||
|
||
FloatSave = (PFLOAT_SAVE) PublicFloatSave;
|
||
|
||
Irql = KeGetCurrentIrql();
|
||
Thread = KeGetCurrentThread();
|
||
|
||
ASSERT (Thread->NpxIrql <= Irql);
|
||
|
||
FloatSave->Flags = 0;
|
||
FloatSave->Irql = Irql;
|
||
FloatSave->PreviousNpxIrql = Thread->NpxIrql;
|
||
FloatSave->Thread = Thread;
|
||
|
||
//
|
||
// If the irql has changed we need to save the complete floating
|
||
// state context as the prior level has been interrupted.
|
||
//
|
||
|
||
if (Thread->NpxIrql != Irql) {
|
||
|
||
//
|
||
// If this is apc level we don't have anyplace to hold this
|
||
// context, allocate some heap.
|
||
//
|
||
|
||
if (Irql == APC_LEVEL) {
|
||
FloatSave->u.Context = ExAllocatePoolWithTag (
|
||
NonPagedPool,
|
||
sizeof (FX_SAVE_AREA) + ALIGN_ADJUST,
|
||
' XPN'
|
||
);
|
||
|
||
if (!FloatSave->u.Context) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
FloatSave->Flags |= FLOAT_SAVE_FREE_CONTEXT_HEAP;
|
||
|
||
//
|
||
// ExAllocatePoolWithTag returns an 8 byte aligned pointer.
|
||
// The FXSAVE instruction requires 16 byte alignment. Adjust
|
||
// the base address of the save area if needed.
|
||
//
|
||
|
||
if ((FloatSave->u.ContextAddressAsULONG & ALIGN_ADJUST) != 0) {
|
||
FloatSave->u.ContextAddressAsULONG += ALIGN_ADJUST;
|
||
FloatSave->Flags |= FLOAT_SAVE_ALIGN_ADJUSTED;
|
||
}
|
||
ASSERT((FloatSave->u.ContextAddressAsULONG & 0xF) == 0);
|
||
|
||
} else {
|
||
|
||
ASSERT (Irql == DISPATCH_LEVEL);
|
||
FloatSave->u.Context = &KeGetCurrentPrcb()->NpxSaveArea;
|
||
|
||
}
|
||
|
||
FloatSave->Flags |= FLOAT_SAVE_COMPLETE_CONTEXT;
|
||
}
|
||
|
||
//
|
||
// Stop context switching and allow access to the local fp unit
|
||
//
|
||
|
||
_asm {
|
||
cli
|
||
mov eax, cr0
|
||
mov ecx, eax
|
||
and eax, not (CR0_MP|CR0_EM|CR0_TS)
|
||
cmp eax, ecx
|
||
je short sav10
|
||
|
||
mov cr0, eax
|
||
sav10:
|
||
}
|
||
|
||
Prcb = KeGetCurrentPrcb();
|
||
|
||
//
|
||
// Get ownership of npx register set for this context
|
||
//
|
||
|
||
if (Prcb->NpxThread != Thread) {
|
||
|
||
//
|
||
// If the other context is loaded in the npx registers, flush
|
||
// it to that threads save area
|
||
//
|
||
if (Prcb->NpxThread) {
|
||
|
||
NpxFrame = (PFX_SAVE_AREA)(((ULONG)(Prcb->NpxThread->InitialStack) -
|
||
sizeof(FX_SAVE_AREA)));
|
||
|
||
if (KeI386FxsrPresent) {
|
||
Kix86FxSave(NpxFrame);
|
||
} else {
|
||
Kix86FnSave(NpxFrame);
|
||
}
|
||
|
||
NpxFrame->NpxSavedCpu = 0;
|
||
Prcb->NpxThread->NpxState = NPX_STATE_NOT_LOADED;
|
||
|
||
}
|
||
|
||
Prcb->NpxThread = Thread;
|
||
}
|
||
|
||
NpxFrame = (PFX_SAVE_AREA)(((ULONG)(Thread->InitialStack) -
|
||
sizeof(FX_SAVE_AREA)));
|
||
|
||
|
||
//
|
||
// Save the previous state as required
|
||
//
|
||
|
||
if (FloatSave->Flags & FLOAT_SAVE_COMPLETE_CONTEXT) {
|
||
|
||
//
|
||
// Need to save the entire context
|
||
//
|
||
|
||
if (Thread->NpxState == NPX_STATE_LOADED) {
|
||
if (KeI386FxsrPresent) {
|
||
Kix86FxSave((FloatSave->u.Context));
|
||
} else {
|
||
Kix86FnSave((FloatSave->u.Context));
|
||
}
|
||
|
||
FloatSave->u.Context->NpxSavedCpu = 0;
|
||
FloatSave->u.Context->Cr0NpxState = NpxFrame->Cr0NpxState;
|
||
|
||
} else {
|
||
RtlCopyMemory (FloatSave->u.Context, NpxFrame, sizeof(FX_SAVE_AREA));
|
||
FloatSave->u.Context->NpxSavedCpu = 0;
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Save only the non-volatile state
|
||
//
|
||
|
||
if (Thread->NpxState == NPX_STATE_LOADED) {
|
||
|
||
_asm {
|
||
mov eax, FloatSave
|
||
fnstcw [eax] FLOAT_SAVE.u.Fcw.ControlWord
|
||
}
|
||
|
||
if ((KeI386FxsrPresent) && (KeI386XMMIPresent)) {
|
||
Kix86StMXCsr(&FloatSave->u.Fcw.MXCsr);
|
||
}
|
||
|
||
} else {
|
||
//
|
||
// Save the control word from the npx frame.
|
||
//
|
||
|
||
if (KeI386FxsrPresent) {
|
||
FloatSave->u.Fcw.ControlWord = (USHORT) NpxFrame->U.FxArea.ControlWord;
|
||
FloatSave->u.Fcw.MXCsr = NpxFrame->U.FxArea.MXCsr;
|
||
|
||
} else {
|
||
FloatSave->u.Fcw.ControlWord = (USHORT) NpxFrame->U.FnArea.ControlWord;
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Save Cr0NpxState, but clear CR0_TS as there's not non-volatile
|
||
// pending fp exceptions
|
||
//
|
||
|
||
FloatSave->Cr0NpxState = NpxFrame->Cr0NpxState & ~CR0_TS;
|
||
}
|
||
|
||
//
|
||
// The previous state is saved. Set an initial default
|
||
// FP state for the caller
|
||
//
|
||
|
||
NpxFrame->Cr0NpxState = 0;
|
||
Thread->NpxState = NPX_STATE_LOADED;
|
||
Thread->NpxIrql = Irql;
|
||
ControlWord = 0x27f; // 64bit mode
|
||
MXCsr = 0x1f80;
|
||
|
||
_asm {
|
||
fninit
|
||
fldcw ControlWord
|
||
}
|
||
|
||
if ((KeI386FxsrPresent) && (KeI386XMMIPresent)) {
|
||
Kix86LdMXCsr(&MXCsr);
|
||
}
|
||
|
||
_asm {
|
||
sti
|
||
}
|
||
|
||
FloatSave->Flags |= FLOAT_SAVE_VALID;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
KeRestoreFloatingPointState (
|
||
IN PKFLOATING_SAVE PublicFloatSave
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine retores the thread's current non-volatile NPX state,
|
||
to the passed in state.
|
||
|
||
Arguments:
|
||
|
||
FloatSave - the non-volatile npx state for the thread to restore
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
{
|
||
PKTHREAD Thread;
|
||
PFX_SAVE_AREA NpxFrame;
|
||
ULONG Cr0State;
|
||
PFLOAT_SAVE FloatSave;
|
||
|
||
ASSERT (KeI386NpxPresent);
|
||
|
||
FloatSave = (PFLOAT_SAVE) PublicFloatSave;
|
||
Thread = FloatSave->Thread;
|
||
|
||
NpxFrame = (PFX_SAVE_AREA)(((ULONG)(Thread->InitialStack) -
|
||
sizeof(FX_SAVE_AREA)));
|
||
|
||
|
||
//
|
||
// Verify float save looks like it's from the right context
|
||
//
|
||
|
||
if ((FloatSave->Flags & (FLOAT_SAVE_VALID | FLOAT_SAVE_RESERVED)) != FLOAT_SAVE_VALID) {
|
||
|
||
//
|
||
// Invalid floating point save area.
|
||
//
|
||
|
||
KeBugCheckEx(INVALID_FLOATING_POINT_STATE,
|
||
0,
|
||
FloatSave->Flags,
|
||
0,
|
||
0);
|
||
}
|
||
|
||
if (FloatSave->Irql != KeGetCurrentIrql()) {
|
||
|
||
//
|
||
// Invalid IRQL. IRQL now must be the same as when the
|
||
// context was saved. (Why? Because we save it in different
|
||
// places depending on the IRQL at that time).
|
||
//
|
||
|
||
KeBugCheckEx(INVALID_FLOATING_POINT_STATE,
|
||
1,
|
||
FloatSave->Irql,
|
||
KeGetCurrentIrql(),
|
||
0);
|
||
}
|
||
|
||
if (Thread != KeGetCurrentThread()) {
|
||
|
||
//
|
||
// Invalid Thread. The thread this floating point context
|
||
// belongs to is not the current thread (or the saved thread
|
||
// field is trash).
|
||
//
|
||
|
||
KeBugCheckEx(INVALID_FLOATING_POINT_STATE,
|
||
2,
|
||
(ULONG_PTR)Thread,
|
||
(ULONG_PTR)KeGetCurrentThread(),
|
||
0);
|
||
}
|
||
|
||
|
||
//
|
||
// Synchronize with context switches and the npx trap handlers
|
||
//
|
||
|
||
_asm {
|
||
cli
|
||
}
|
||
|
||
//
|
||
// Restore the required state
|
||
//
|
||
|
||
if (FloatSave->Flags & FLOAT_SAVE_COMPLETE_CONTEXT) {
|
||
|
||
//
|
||
// Restore the entire fp state to the threads save area
|
||
//
|
||
|
||
if (Thread->NpxState == NPX_STATE_LOADED) {
|
||
|
||
//
|
||
// This state in the fp unit is no longer needed, just disregard it
|
||
//
|
||
|
||
Thread->NpxState = NPX_STATE_NOT_LOADED;
|
||
KeGetCurrentPrcb()->NpxThread = NULL;
|
||
}
|
||
|
||
//
|
||
// Copy restored state to npx frame
|
||
//
|
||
|
||
RtlCopyMemory (NpxFrame, FloatSave->u.Context, sizeof(FX_SAVE_AREA));
|
||
|
||
} else {
|
||
|
||
//
|
||
// Restore the non-volatile state
|
||
//
|
||
|
||
if (Thread->NpxState == NPX_STATE_LOADED) {
|
||
|
||
//
|
||
// Init fp state and restore control word
|
||
//
|
||
|
||
_asm {
|
||
fninit
|
||
mov eax, FloatSave
|
||
fldcw [eax] FLOAT_SAVE.u.Fcw.ControlWord
|
||
}
|
||
|
||
|
||
if ((KeI386FxsrPresent) && (KeI386XMMIPresent)) {
|
||
Kix86LdMXCsr(&FloatSave->u.Fcw.MXCsr);
|
||
}
|
||
|
||
|
||
} else {
|
||
|
||
//
|
||
// Fp state not loaded. Restore control word in npx frame
|
||
//
|
||
|
||
if (KeI386FxsrPresent) {
|
||
NpxFrame->U.FxArea.ControlWord = FloatSave->u.Fcw.ControlWord;
|
||
NpxFrame->U.FxArea.StatusWord = 0;
|
||
NpxFrame->U.FxArea.TagWord = 0;
|
||
NpxFrame->NpxSavedCpu = 0;
|
||
NpxFrame->U.FxArea.MXCsr = FloatSave->u.Fcw.MXCsr;
|
||
|
||
} else {
|
||
NpxFrame->U.FnArea.ControlWord = FloatSave->u.Fcw.ControlWord;
|
||
NpxFrame->U.FnArea.StatusWord = 0;
|
||
NpxFrame->U.FnArea.TagWord = 0xffff;
|
||
}
|
||
|
||
}
|
||
|
||
NpxFrame->Cr0NpxState = FloatSave->Cr0NpxState;
|
||
}
|
||
|
||
//
|
||
// Restore NpxIrql and Cr0
|
||
//
|
||
|
||
Thread->NpxIrql = FloatSave->PreviousNpxIrql;
|
||
Cr0State = Thread->NpxState | NpxFrame->Cr0NpxState;
|
||
|
||
_asm {
|
||
mov eax, cr0
|
||
mov ecx, eax
|
||
and eax, not (CR0_MP|CR0_EM|CR0_TS)
|
||
or eax, Cr0State
|
||
cmp eax, ecx
|
||
je short res10
|
||
mov cr0, eax
|
||
res10:
|
||
sti
|
||
}
|
||
|
||
//
|
||
// Done
|
||
//
|
||
|
||
if ((FloatSave->Flags & FLOAT_SAVE_FREE_CONTEXT_HEAP) != 0) {
|
||
|
||
//
|
||
// If FXSAVE area was adjusted for alignment after allocation,
|
||
// undo that adjustment before freeing.
|
||
//
|
||
|
||
if ((FloatSave->Flags & FLOAT_SAVE_ALIGN_ADJUSTED) != 0) {
|
||
FloatSave->u.ContextAddressAsULONG -= ALIGN_ADJUST;
|
||
}
|
||
ExFreePool (FloatSave->u.Context);
|
||
}
|
||
|
||
FloatSave->Flags = 0;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
VOID
|
||
KiDisableFastSyscallReturn(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The fast syscall/return feature cannot be used until
|
||
certain processor specific registers have been initialized.
|
||
This routine is called when the system is switching to a
|
||
state where not all processors are powered on.
|
||
|
||
This routine adjusts the exit path for system calls to
|
||
use the iretd instruction instead of the faster sysexit
|
||
instruction, it accomplishes this by adjusting the offset
|
||
of a branch.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
if (KiSystemCallExitAdjusted) {
|
||
KiSystemCallExitBranch[1] -= KiSystemCallExitAdjusted;
|
||
KiSystemCallExitAdjusted = 0;
|
||
}
|
||
}
|
||
|
||
VOID
|
||
KiEnableFastSyscallReturn(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The fast syscall/return feature cannot be used until
|
||
certain processor specific registers have been initialized.
|
||
This routine is called once the registers are known to
|
||
have been set on all processors.
|
||
|
||
This routine adjusts the exit path for system calls to
|
||
use the appropriate sequence for the processor, it does
|
||
this by adjusting the offset of a branch.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// Adjust the second byte of the two byte branch instruction.
|
||
// It can never be otherwise, but, make sure we aren't going
|
||
// to adjust it out of range.
|
||
//
|
||
|
||
//
|
||
// The following is a workaround for the fact that in resume
|
||
// from hibernate the kernel is read only. Basically, we
|
||
// won't try to do it again, we also don't undo it when
|
||
// hibernating/suspending.
|
||
//
|
||
|
||
if ((KiSystemCallExitAdjusted == KiSystemCallExitAdjust) &&
|
||
KiFastCallCopyDoneOnce) {
|
||
|
||
//
|
||
// It's already done, don't try to do it again.
|
||
//
|
||
|
||
return;
|
||
}
|
||
|
||
if ((KiSystemCallExitAdjust + KiSystemCallExitBranch[1]) < 0x80) {
|
||
|
||
//
|
||
// It's good, undo any previous adjustment.
|
||
//
|
||
|
||
KiDisableFastSyscallReturn();
|
||
|
||
//
|
||
// Adjust the branch.
|
||
//
|
||
|
||
KiSystemCallExitAdjusted = (UCHAR)KiSystemCallExitAdjust;
|
||
KiSystemCallExitBranch[1] += KiSystemCallExitAdjusted;
|
||
|
||
//
|
||
// Copy the appropriate system entry code into user shared
|
||
// data where it can be executed from user mode.
|
||
//
|
||
|
||
RtlCopyMemory(SharedUserData->SystemCall,
|
||
KiFastSystemCallCode,
|
||
sizeof(SharedUserData->SystemCall));
|
||
KiFastCallCopyDoneOnce = TRUE;
|
||
}
|
||
}
|
||
|
||
VOID
|
||
KePrepareToLoseProcessorSpecificState(
|
||
VOID
|
||
)
|
||
{
|
||
//
|
||
// The kernel has been marked read only, adjusting
|
||
// code right now won't work. Fortunately, we
|
||
// don't actually need to do this as the SYSEXIT
|
||
// instruction doesn't depend on the SYSENTER MSRs.
|
||
//
|
||
// KiDisableFastSyscallReturn();
|
||
}
|
||
|
||
VOID
|
||
KiLoadFastSyscallMachineSpecificRegisters(
|
||
IN volatile PLONG Void
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Load MSRs used to support Fast Syscall/return. This routine is
|
||
run on all processors.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
if (KiFastSystemCallIsIA32) {
|
||
|
||
//
|
||
// Use Intel defined way of doing this.
|
||
//
|
||
|
||
WRMSR(MSR_SYSENTER_CS, KGDT_R0_CODE);
|
||
WRMSR(MSR_SYSENTER_EIP, (ULONGLONG)(ULONG)KiFastCallEntry);
|
||
WRMSR(MSR_SYSENTER_ESP, 0);
|
||
|
||
#if 0
|
||
|
||
} else {
|
||
|
||
//
|
||
// Use the non-Intel way. (Note: Now that Intel has also
|
||
// defined a way, most new processors do it that way).
|
||
//
|
||
|
||
LARGE_INTEGER Value;
|
||
|
||
Value.u.HighPart = ((KGDT_R3_CODE | 3) << 16) | KGDT_R0_CODE;
|
||
Value.u.LowPart = (ULONG)KiFastCallEntry;
|
||
WRMSR(MSR_SYSCALL_TARGET_ADDR, Value.QuadPart);
|
||
|
||
//
|
||
// Now enable the feature.
|
||
//
|
||
|
||
Value.QuadPart = RDMSR(MSR_EXT_FEATURE_ENABLE);
|
||
Value.u.LowPart |= MSR_EFER_SCE;
|
||
WRMSR(MSR_EXT_FEATURE_ENABLE, Value.QuadPart);
|
||
|
||
#endif
|
||
|
||
}
|
||
}
|
||
|
||
VOID
|
||
KiRestoreFastSyscallReturnState(
|
||
VOID
|
||
)
|
||
{
|
||
ULONG_PTR Void = 0;
|
||
|
||
if (KeFeatureBits & KF_FAST_SYSCALL) {
|
||
|
||
if (KiFastSystemCallDisable == 0) {
|
||
|
||
//
|
||
// Fast system call is enabled.
|
||
//
|
||
|
||
if (KiFastSystemCallIsIA32 == TRUE) {
|
||
KiSystemCallExitAdjust = KiSystemCallExit2 - KiSystemCallExit;
|
||
KiFastSystemCallCode = KiFastSystemCallIa32;
|
||
} else {
|
||
KiSystemCallExitAdjust = KiSystemCallExit3 - KiSystemCallExit;
|
||
KiFastSystemCallCode = KiFastSystemCallAmdK6;
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// Fast system call has been explicitly disabled or is
|
||
// not implemented on all processors in the system.
|
||
//
|
||
|
||
KeFeatureBits &= ~KF_FAST_SYSCALL;
|
||
}
|
||
}
|
||
if (KeFeatureBits & KF_FAST_SYSCALL) {
|
||
|
||
//
|
||
// On all processors, set the MSRs that support syscall/sysexit.
|
||
//
|
||
|
||
KiIpiGenericCall(
|
||
(PKIPI_BROADCAST_WORKER)KiLoadFastSyscallMachineSpecificRegisters,
|
||
Void
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// Set the appropriate code for system call into the system
|
||
// call area of the shared user data area.
|
||
//
|
||
|
||
KiEnableFastSyscallReturn();
|
||
}
|
||
|
||
VOID
|
||
KeRestoreProcessorSpecificFeatures(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Restore processor specific features. This routine is called
|
||
when processors have been restored to a powered on state to
|
||
restore those things which are not part of the processor's
|
||
"normal" context which may have been lost. For example, this
|
||
routine is called when a system is resumed from hibernate or
|
||
suspend.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
KeRestoreMtrr();
|
||
KeRestorePAT();
|
||
KiRestoreFastSyscallReturnState();
|
||
}
|
||
|
||
|
||
#if !defined(NT_UP)
|
||
|
||
VOID
|
||
FASTCALL
|
||
KiAcquireQueuedSpinLockCheckForFreeze(
|
||
IN PKSPIN_LOCK_QUEUE QueuedLock,
|
||
IN PKTRAP_FRAME TrapFrame
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to acquire a queued spin lock while at high
|
||
priority. While the lock is not available, a check is made to see
|
||
if another processor has requested this processor freeze execution.
|
||
|
||
Note: This routine must be called with current IRQL at or above
|
||
dispatch lever, or interrupts disabled.
|
||
|
||
Arguments:
|
||
|
||
QueuedLock Supplies the address of the queued spinlock.
|
||
TrapFrame Supplies the address of the trap frame to pass to
|
||
KiFreezeTargetExecution.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PKSPIN_LOCK_QUEUE Previous;
|
||
PKSPIN_LOCK Lock;
|
||
PKPRCB Prcb;
|
||
volatile ULONG_PTR * LockPointer;
|
||
|
||
LockPointer = (volatile ULONG_PTR *)&QueuedLock->Lock;
|
||
|
||
Previous = InterlockedExchangePointer(QueuedLock->Lock, QueuedLock);
|
||
|
||
if (Previous == NULL) {
|
||
|
||
//
|
||
// This processor now owns this lock.
|
||
//
|
||
|
||
*LockPointer |= LOCK_QUEUE_OWNER;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Lock is already held, update thew next pointer in the
|
||
// previous queue entry to point to this new waiter and
|
||
// wait until the lock is granted.
|
||
//
|
||
// The following loop is careful not to write ANYTHING
|
||
// while waiting unless a freeze execution has been
|
||
// requested. This includes any stack variables or
|
||
// return addresses.
|
||
//
|
||
|
||
*LockPointer |= LOCK_QUEUE_WAIT;
|
||
Previous->Next = QueuedLock;
|
||
|
||
Prcb = KeGetCurrentPrcb();
|
||
|
||
while (*LockPointer & LOCK_QUEUE_WAIT) {
|
||
if (Prcb->RequestSummary & IPI_FREEZE) {
|
||
ULONG OldSummary;
|
||
ULONG NewSummary;
|
||
ULONG Summary;
|
||
|
||
OldSummary = Prcb->RequestSummary;
|
||
NewSummary = OldSummary & ~IPI_FREEZE;
|
||
Summary = InterlockedCompareExchange((PVOID)&Prcb->RequestSummary,
|
||
NewSummary,
|
||
OldSummary);
|
||
|
||
//
|
||
// If something else edited the RequestSummary, we'll
|
||
// get it next time around (unless the IPI has been
|
||
// handled).
|
||
//
|
||
|
||
if (Summary == OldSummary) {
|
||
|
||
//
|
||
// IPI_FREEZE cleared in RequestSummary. Now
|
||
// freeze as requested.
|
||
//
|
||
|
||
KiFreezeTargetExecution(TrapFrame, NULL);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Don't be a hog.
|
||
//
|
||
|
||
KeYieldProcessor();
|
||
}
|
||
}
|
||
|
||
//
|
||
// Lock has been acquired.
|
||
//
|
||
}
|
||
|
||
#endif
|
||
|
||
|
||
/*++
|
||
|
||
QLOCK_STAT_GATHER
|
||
|
||
If this flag is defined, the queued spinlock routines are
|
||
replaced by wrappers used to gather performance characteristics
|
||
of the code acquiring the locks.
|
||
|
||
--*/
|
||
|
||
#if defined(QLOCK_STAT_GATHER)
|
||
|
||
#define QLOCK_STAT_CLEAN
|
||
#define QLOCKS_NUMBER 16
|
||
#define QLOCKS_MAX_LOG 512
|
||
|
||
ULONG
|
||
FASTCALL
|
||
KiRDTSC(
|
||
PULONGLONG Time
|
||
);
|
||
|
||
//
|
||
// The following structure is used to accumulate data about each
|
||
// acquire/release pair for a lock.
|
||
//
|
||
|
||
typedef struct {
|
||
ULONGLONG Key;
|
||
ULONGLONG Time;
|
||
ULONGLONG WaitTime;
|
||
ULONG Count;
|
||
ULONG Waiters;
|
||
ULONG Depth;
|
||
ULONG IncreasedDepth;
|
||
ULONG Clean;
|
||
} QLOCKDATA, *PQLOCKDATA;
|
||
|
||
//
|
||
// House keeping data for each lock.
|
||
//
|
||
|
||
typedef struct {
|
||
|
||
//
|
||
// The following fields are used to keep data from acquire
|
||
// to release.
|
||
//
|
||
|
||
ULONGLONG AcquireTime;
|
||
ULONGLONG WaitToAcquire;
|
||
ULONG_PTR AcquirePoint;
|
||
BOOLEAN Clean;
|
||
|
||
//
|
||
// Remaining fields accumulate global stats for this lock.
|
||
//
|
||
|
||
ULONG Count;
|
||
ULONG Pairs;
|
||
ULONG FailedTry;
|
||
UCHAR MaxDepth;
|
||
UCHAR PreviousDepth;
|
||
ULONG NoWait;
|
||
} QLOCKHOUSE, *PQLOCKHOUSE;
|
||
|
||
QLOCKDATA KiQueuedSpinLockLog[QLOCKS_NUMBER][QLOCKS_MAX_LOG];
|
||
QLOCKHOUSE KiQueuedSpinLockHouse[QLOCKS_NUMBER];
|
||
|
||
//
|
||
// Implement the lock queue mechanisms in C for when we are
|
||
// gathering performance data.
|
||
//
|
||
|
||
VOID
|
||
FASTCALL
|
||
KiAcquireQueuedLock(
|
||
IN PKSPIN_LOCK_QUEUE QueuedLock
|
||
)
|
||
{
|
||
PKSPIN_LOCK_QUEUE Previous;
|
||
PKSPIN_LOCK Lock;
|
||
volatile ULONG_PTR * LockPointer;
|
||
|
||
LockPointer = (volatile ULONG_PTR *)&QueuedLock->Lock;
|
||
|
||
Previous = InterlockedExchangePointer(QueuedLock->Lock, QueuedLock);
|
||
|
||
if (Previous == NULL) {
|
||
|
||
//
|
||
// This processor now owns this lock.
|
||
//
|
||
|
||
#if defined(QLOCK_STAT_CLEAN)
|
||
|
||
ULONG LockNumber;
|
||
|
||
LockNumber = QueuedLock - KeGetCurrentPrcb()->LockQueue;
|
||
|
||
//
|
||
// The following check allows the conversion from QueuedLock to
|
||
// lock number to work (validly) even if in stack queued spin
|
||
// locks are using this routine.
|
||
//
|
||
|
||
if (LockNumber < QLOCKS_NUMBER) {
|
||
KiQueuedSpinLockHouse[LockNumber].Clean = 1;
|
||
}
|
||
|
||
#endif
|
||
|
||
*LockPointer |= LOCK_QUEUE_OWNER;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Lock is already held, update thew next pointer in the
|
||
// previous queue entry to point to this new waiter and
|
||
// wait until the lock is granted.
|
||
//
|
||
|
||
*LockPointer |= LOCK_QUEUE_WAIT;
|
||
Previous->Next = QueuedLock;
|
||
|
||
while (*LockPointer & LOCK_QUEUE_WAIT) {
|
||
KeYieldProcessor();
|
||
}
|
||
}
|
||
|
||
//
|
||
// Lock has been acquired.
|
||
//
|
||
}
|
||
|
||
VOID
|
||
FASTCALL
|
||
KiReleaseQueuedLock(
|
||
IN PKSPIN_LOCK_QUEUE QueuedLock
|
||
)
|
||
{
|
||
PKSPIN_LOCK_QUEUE Waiter;
|
||
|
||
//
|
||
// Get the address of the actual lock and strip out the bottom
|
||
// two bits which are used for status.
|
||
//
|
||
|
||
ASSERT((((ULONG_PTR)QueuedLock->Lock) & 3) == LOCK_QUEUE_OWNER);
|
||
QueuedLock->Lock = (PKSPIN_LOCK)((ULONG_PTR)QueuedLock->Lock & ~3);
|
||
|
||
Waiter = (PKSPIN_LOCK_QUEUE)*QueuedLock->Lock;
|
||
|
||
if (Waiter == QueuedLock) {
|
||
|
||
//
|
||
// Good chance noone is queued on this lock, to be sure
|
||
// we need to do an interlocked operation on it.
|
||
// Note: This is just an optimization, there is no point
|
||
// in doing the interlocked compare exchange if someone
|
||
// else has already joined the queue.
|
||
//
|
||
|
||
Waiter = InterlockedCompareExchangePointer(QueuedLock->Lock,
|
||
NULL,
|
||
QueuedLock);
|
||
}
|
||
if (Waiter != QueuedLock) {
|
||
|
||
//
|
||
// There is another waiter. It is possible for the waiter
|
||
// to have only just performed the exchange that put its
|
||
// context in the lock and to have not yet updated the
|
||
// 'next' pointer in the previous context (which could be
|
||
// this context), so we wait for our next pointer to be
|
||
// non-null before continuing.
|
||
//
|
||
|
||
volatile PKSPIN_LOCK_QUEUE * NextQueuedLock = &QueuedLock->Next;
|
||
|
||
while ((Waiter = *NextQueuedLock) == NULL) {
|
||
KeYieldProcessor();
|
||
}
|
||
|
||
//
|
||
// Pass the lock on to the next in line.
|
||
//
|
||
|
||
*((PULONG_PTR)&Waiter->Lock) ^= (LOCK_QUEUE_WAIT | LOCK_QUEUE_OWNER);
|
||
QueuedLock->Next = NULL;
|
||
}
|
||
}
|
||
|
||
KIRQL
|
||
FASTCALL
|
||
KiQueueStatAcquireQueuedLock(
|
||
IN KSPIN_LOCK_QUEUE_NUMBER Number
|
||
)
|
||
{
|
||
KIRQL PreviousIrql;
|
||
|
||
PreviousIrql = KfRaiseIrql(DISPATCH_LEVEL);
|
||
KiAcquireQueuedLock(&KeGetCurrentPrcb()->LockQueue[Number]);
|
||
return PreviousIrql;
|
||
}
|
||
|
||
KIRQL
|
||
FASTCALL
|
||
KiQueueStatAcquireQueuedLockRTS(
|
||
IN KSPIN_LOCK_QUEUE_NUMBER Number
|
||
)
|
||
{
|
||
KIRQL PreviousIrql;
|
||
|
||
PreviousIrql = KfRaiseIrql(SYNCH_LEVEL);
|
||
KiAcquireQueuedLock(&KeGetCurrentPrcb()->LockQueue[Number]);
|
||
return PreviousIrql;
|
||
}
|
||
|
||
LOGICAL
|
||
FASTCALL
|
||
KiQueueStatTryAcquire(
|
||
IN KSPIN_LOCK_QUEUE_NUMBER Number,
|
||
IN PKIRQL OldIrql,
|
||
IN KIRQL NewIrql
|
||
)
|
||
{
|
||
KIRQL PreviousIrql;
|
||
LOGICAL Acquired = FALSE;
|
||
PKSPIN_LOCK_QUEUE Previous;
|
||
PKSPIN_LOCK_QUEUE QueuedLock;
|
||
ULONG_PTR * LockPointer;
|
||
ULONG_PTR Lock;
|
||
|
||
_disable();
|
||
|
||
QueuedLock = &KeGetCurrentPrcb()->LockQueue[Number];
|
||
LockPointer = (ULONG_PTR *)&QueuedLock->Lock;
|
||
Lock = *LockPointer;
|
||
|
||
Previous = InterlockedCompareExchangePointer(Lock, QueuedLock, NULL);
|
||
|
||
if (Previous == NULL) {
|
||
|
||
//
|
||
// This processor now owns this lock. Set the owner bit in
|
||
// the queued lock lock pointer, raise IRQL to the requested
|
||
// level, set the old IRQL in the caller provided location
|
||
// and return success.
|
||
//
|
||
|
||
Lock |= LOCK_QUEUE_OWNER;
|
||
*LockPointer = Lock;
|
||
Acquired = TRUE;
|
||
PreviousIrql = KfRaiseIrql(NewIrql);
|
||
*OldIrql = PreviousIrql;
|
||
}
|
||
|
||
_enable();
|
||
|
||
return Acquired;
|
||
}
|
||
|
||
VOID
|
||
FASTCALL
|
||
KiQueueStatReleaseQueuedLock(
|
||
IN KSPIN_LOCK_QUEUE_NUMBER Number,
|
||
IN KIRQL OldIrql
|
||
)
|
||
{
|
||
KiReleaseQueuedLock(&KeGetCurrentPrcb()->LockQueue[Number]);
|
||
KfLowerIrql(OldIrql);
|
||
}
|
||
|
||
UCHAR
|
||
FASTCALL
|
||
KiQueuedLockDepth(
|
||
IN PKSPIN_LOCK_QUEUE QueuedLock
|
||
)
|
||
{
|
||
//
|
||
// Run down the list of waiters and see how many there are.
|
||
//
|
||
|
||
ULONG Depth = 0;
|
||
ULONG_PTR LastAcquire;
|
||
ULONG Debug;
|
||
|
||
|
||
//
|
||
// Get address of last acquirer in queue (stip the status bits
|
||
// out of the address).
|
||
//
|
||
|
||
LastAcquire = (ULONG_PTR)QueuedLock->Lock;
|
||
LastAcquire &= ~3;
|
||
LastAcquire = *(PULONG_PTR)LastAcquire;
|
||
|
||
//
|
||
// Run down the list advancing QueuedLock until the end is reached.
|
||
//
|
||
|
||
while (LastAcquire != (ULONG_PTR)QueuedLock) {
|
||
Debug = 0;
|
||
|
||
//
|
||
// If the waiter is not at the end of the list and has not yet
|
||
// updated the forward pointer, wait for that update to happen.
|
||
//
|
||
|
||
if (QueuedLock->Next == NULL) {
|
||
volatile PKSPIN_LOCK_QUEUE * NextQueuedLock = &QueuedLock->Next;
|
||
|
||
while (*NextQueuedLock == NULL) {
|
||
KeYieldProcessor();
|
||
if (++Debug > 10000000) {
|
||
DbgBreakPoint();
|
||
Debug = 0;
|
||
}
|
||
}
|
||
}
|
||
Depth++;
|
||
QueuedLock = QueuedLock->Next;
|
||
}
|
||
|
||
return Depth;
|
||
}
|
||
|
||
//
|
||
// The following routines complete the queued spinlock package.
|
||
//
|
||
|
||
VOID
|
||
FASTCALL
|
||
KeAcquireInStackQueuedSpinLockAtDpcLevel(
|
||
IN PKSPIN_LOCK SpinLock,
|
||
IN PKLOCK_QUEUE_HANDLE LockHandle
|
||
)
|
||
{
|
||
LockHandle->LockQueue.Next = NULL;
|
||
LockHandle->LockQueue.Lock = SpinLock;
|
||
KiAcquireQueuedLock(&LockHandle->LockQueue);
|
||
}
|
||
|
||
VOID
|
||
FASTCALL
|
||
KeReleaseInStackQueuedSpinLockFromDpcLevel (
|
||
IN PKLOCK_QUEUE_HANDLE LockHandle
|
||
)
|
||
{
|
||
KiReleaseQueuedLock(&LockHandle->LockQueue);
|
||
}
|
||
|
||
//
|
||
// Although part of the queued spinlock package, the following
|
||
// routines need to be implemented in assembly code to gather
|
||
// lock statistics.
|
||
//
|
||
|
||
#if 0
|
||
VOID
|
||
FASTCALL
|
||
KeAcquireQueuedSpinLockAtDpcLevel(
|
||
IN PKSPIN_LOCK_QUEUE QueuedLock
|
||
)
|
||
{
|
||
KiAcquireQueuedLock(QueuedLock);
|
||
}
|
||
|
||
VOID
|
||
FASTCALL
|
||
KeReleaseQueuedSpinLockFromDpcLevel (
|
||
IN PKSPIN_LOCK_QUEUE QueuedLock
|
||
)
|
||
{
|
||
KiReleaseQueuedLock(QueuedLock);
|
||
}
|
||
|
||
#endif
|
||
|
||
VOID
|
||
FASTCALL
|
||
KiQueueStatTrySucceeded(
|
||
IN PKSPIN_LOCK_QUEUE QueuedLock,
|
||
IN ULONG_PTR CallersAddress
|
||
)
|
||
{
|
||
PKPRCB Prcb;
|
||
ULONG LockNumber;
|
||
|
||
Prcb = KeGetCurrentPrcb();
|
||
LockNumber = QueuedLock - Prcb->LockQueue;
|
||
|
||
//
|
||
// Record time now.
|
||
//
|
||
|
||
KiRDTSC(&KiQueuedSpinLockHouse[LockNumber].AcquireTime);
|
||
KiQueuedSpinLockHouse[LockNumber].WaitToAcquire = 0;
|
||
KiQueuedSpinLockHouse[LockNumber].AcquirePoint = CallersAddress;
|
||
}
|
||
|
||
VOID
|
||
FASTCALL
|
||
KiQueueStatTryFailed(
|
||
IN PKSPIN_LOCK_QUEUE QueuedLock
|
||
)
|
||
{
|
||
PKPRCB Prcb;
|
||
ULONG LockNumber;
|
||
|
||
Prcb = KeGetCurrentPrcb();
|
||
LockNumber = QueuedLock - Prcb->LockQueue;
|
||
|
||
KiQueuedSpinLockHouse[LockNumber].FailedTry++;
|
||
}
|
||
|
||
VOID
|
||
FASTCALL
|
||
KiQueueStatTry(
|
||
IN PULONG Everything
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Log success or failure of a TryToAcquire.
|
||
|
||
If success, logs the same data as KiQueueStatAcquire except
|
||
the wait time is 0.
|
||
|
||
Arguments:
|
||
|
||
Argument points to an array of ULONG data.
|
||
|
||
+0 xxxxxxRR RR is result (1 = success, 0 = fail)
|
||
+4 aaaaaaaa Argument to try to acquire (ie lock number)
|
||
+8 cccccccc Caller address
|
||
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
UCHAR Success = *(PUCHAR)Everything;
|
||
ULONG LockNumber = Everything[1];
|
||
|
||
if (!Success) {
|
||
KiQueuedSpinLockHouse[LockNumber].FailedTry++;
|
||
return;
|
||
}
|
||
|
||
KiRDTSC(&KiQueuedSpinLockHouse[LockNumber].AcquireTime);
|
||
KiQueuedSpinLockHouse[LockNumber].WaitToAcquire = 0;
|
||
KiQueuedSpinLockHouse[LockNumber].AcquirePoint = Everything[2];
|
||
}
|
||
|
||
VOID
|
||
FASTCALL
|
||
KiQueueStatAcquire(
|
||
IN PULONG Everything
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called when a lock has been acquired. It's
|
||
purpose it to record wait time, acquisition time and who
|
||
acquired the lock.
|
||
|
||
Arguments:
|
||
|
||
Argument points to an array of ULONG data.
|
||
|
||
+0 aaaaaaaa LockNumber
|
||
+4 tltltltl time low = time wait to acquire began
|
||
+8 thththth time high =
|
||
+c cccccccc Caller address
|
||
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG LockNumber = Everything[0];
|
||
PQLOCKHOUSE LockHome;
|
||
|
||
//
|
||
// Make this routine work with either a lock number of lock address.
|
||
//
|
||
|
||
if (LockNumber > QLOCKS_NUMBER) {
|
||
|
||
LockNumber = ((PKSPIN_LOCK_QUEUE)Everything[0]) -
|
||
KeGetCurrentPrcb()->LockQueue;
|
||
}
|
||
|
||
LockHome = &KiQueuedSpinLockHouse[LockNumber];
|
||
LockHome->WaitToAcquire = *(PULONGLONG)&Everything[1];
|
||
LockHome->AcquirePoint = Everything[3];
|
||
KiRDTSC(&LockHome->AcquireTime);
|
||
}
|
||
|
||
VOID
|
||
FASTCALL
|
||
KiQueueStatRelease(
|
||
IN PULONG Everything
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called when a lock is released to log statistics
|
||
about the lock. This routine is called with the lock still held,
|
||
the statistics update is protected by the lock itself.
|
||
|
||
Arguments:
|
||
|
||
Argument points to an array of ULONG data.
|
||
|
||
+0 aaaaaaaa Lock number
|
||
+4 cccccccc Caller address
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PQLOCKDATA Entry;
|
||
ULONGLONG Key;
|
||
ULONGLONG Now;
|
||
UCHAR Waiters;
|
||
PQLOCKHOUSE LockHome;
|
||
ULONG LockNumber = Everything[0];
|
||
LONGLONG HoldTime;
|
||
ULONG Clean;
|
||
|
||
KiRDTSC(&Now);
|
||
|
||
//
|
||
// Make this routine work with either a lock number of lock address.
|
||
//
|
||
|
||
if (LockNumber > QLOCKS_NUMBER) {
|
||
LockNumber = ((PKSPIN_LOCK_QUEUE)Everything[0]) -
|
||
KeGetCurrentPrcb()->LockQueue;
|
||
}
|
||
|
||
LockHome = &KiQueuedSpinLockHouse[LockNumber];
|
||
|
||
//
|
||
// Make up the key for this acquire/release pair.
|
||
//
|
||
|
||
((PLARGE_INTEGER)&Key)->HighPart = LockHome->AcquirePoint;
|
||
((PLARGE_INTEGER)&Key)->LowPart = Everything[1];
|
||
|
||
//
|
||
// Get the count of processors now waiting on this lock.
|
||
//
|
||
|
||
Waiters = KiQueuedLockDepth(&KeGetCurrentPrcb()->LockQueue[LockNumber]);
|
||
if (Waiters > LockHome->MaxDepth) {
|
||
LockHome->MaxDepth = Waiters;
|
||
}
|
||
|
||
//
|
||
// Reset per acquire/release data. This is data we don't want
|
||
// lying around for the next pair if we happen to throw away this
|
||
// particular data point.
|
||
//
|
||
|
||
Clean = LockHome->Clean;
|
||
LockHome->Clean = 0;
|
||
LockHome->AcquirePoint = 0;
|
||
|
||
HoldTime = Now - LockHome->AcquireTime;
|
||
if (HoldTime < 0) {
|
||
|
||
//
|
||
// This happens when KeSetSystemTime is called.
|
||
// Drop any negative results.
|
||
//
|
||
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Update global statistics.
|
||
//
|
||
|
||
LockHome->Count++;
|
||
LockHome->NoWait += Clean;
|
||
|
||
//
|
||
// Search for a match in the log and add in the new data.
|
||
//
|
||
|
||
for (Entry = KiQueuedSpinLockLog[LockNumber]; TRUE; Entry++) {
|
||
if (Entry->Key == 0) {
|
||
|
||
//
|
||
// We have reached the end of the list of valid
|
||
// entries without finding a key match. If there's
|
||
// room, create a new entry.
|
||
//
|
||
|
||
if (LockHome->Pairs >= QLOCKS_MAX_LOG) {
|
||
|
||
//
|
||
// No room, just return.
|
||
//
|
||
|
||
return;
|
||
}
|
||
LockHome->Pairs++;
|
||
Entry->Key = Key;
|
||
}
|
||
|
||
if (Entry->Key == Key) {
|
||
|
||
//
|
||
// Found a match (or created a new pair). Update statistics
|
||
// for this acquire/release pair.
|
||
//
|
||
|
||
Entry->Time += HoldTime;
|
||
if (LockHome->WaitToAcquire) {
|
||
Entry->WaitTime += (LockHome->AcquireTime - LockHome->WaitToAcquire);
|
||
}
|
||
Entry->Count++;
|
||
Entry->Waiters += (Waiters != 0);
|
||
Entry->Depth += Waiters;
|
||
|
||
//
|
||
// There should be one less waiter now than there was
|
||
// before we acquired the lock. If not, a new waiter
|
||
// has joined the queue. This is is condition we want
|
||
// to know about as it indicates contention on this
|
||
// lock.
|
||
//
|
||
|
||
if ((Waiters) && (Waiters >= LockHome->PreviousDepth)) {
|
||
Entry->IncreasedDepth++;
|
||
}
|
||
LockHome->PreviousDepth = Waiters;
|
||
Entry->Clean += Clean;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
#endif
|
||
|
||
#ifdef _X86_
|
||
#pragma optimize("y", off) // RtlCaptureContext needs EBP to be correct
|
||
#endif
|
||
|
||
|
||
VOID
|
||
__cdecl
|
||
KeSaveStateForHibernate(
|
||
IN PKPROCESSOR_STATE ProcessorState
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Saves all processor-specific state that must be preserved
|
||
across an S4 state (hibernation).
|
||
|
||
N.B. #pragma surrounding this function is required in order
|
||
to create the frame pointer than RtlCaptureContext relies
|
||
on.
|
||
N.B. _CRTAPI1 (__cdecl) decoration is also required so that
|
||
RtlCaptureContext can compute the correct ESP.
|
||
|
||
Arguments:
|
||
|
||
ProcessorState - Supplies the KPROCESSOR_STATE where the
|
||
current CPU's state is to be saved.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
RtlCaptureContext(&ProcessorState->ContextFrame);
|
||
KiSaveProcessorControlState(ProcessorState);
|
||
}
|
||
#ifdef _X86_
|
||
#pragma optimize("", on)
|
||
#endif
|