496 lines
11 KiB
C
496 lines
11 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"
|
|
|
|
//
|
|
// 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;
|
|
} 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_RESERVED 0xF8
|
|
|
|
#pragma warning(disable:4035) // re-enable below
|
|
|
|
// notes:
|
|
// Kix86FxSave(NpxFame) - performs an FxSave to the address specificied
|
|
// - no other action occurs
|
|
__inline
|
|
KIRQL
|
|
Kix86FxSave(
|
|
PULONG NpxFrame
|
|
)
|
|
{
|
|
_asm {
|
|
mov eax, NpxFrame
|
|
;fxsave [eax]
|
|
_emit 0fh
|
|
_emit 0aeh
|
|
_emit 0
|
|
}
|
|
}
|
|
|
|
//
|
|
// Load Katmai New Instruction Technology Control/Status
|
|
//
|
|
__inline
|
|
KIRQL
|
|
Kix86LdMXCsr(
|
|
PULONG MXCsr
|
|
)
|
|
{
|
|
_asm {
|
|
mov eax, MXCsr
|
|
;LDMXCSR [eax]
|
|
_emit 0fh
|
|
_emit 0aeh
|
|
_emit 10h
|
|
}
|
|
}
|
|
|
|
//
|
|
// Store Katmai New Instruction Technology Control/Status
|
|
//
|
|
__inline
|
|
KIRQL
|
|
Kix86StMXCsr(
|
|
PULONG MXCsr
|
|
)
|
|
{
|
|
_asm {
|
|
mov eax, MXCsr
|
|
;STMXCSR [eax]
|
|
_emit 0fh
|
|
_emit 0aeh
|
|
_emit 18h
|
|
}
|
|
}
|
|
#pragma warning(default:4035)
|
|
|
|
|
|
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;
|
|
|
|
//
|
|
// 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 (
|
|
sizeof (FX_SAVE_AREA),
|
|
' XPN'
|
|
);
|
|
|
|
if (!FloatSave->u.Context) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
FloatSave->Flags |= FLOAT_SAVE_FREE_CONTEXT_HEAP;
|
|
|
|
} 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->StackBase) -
|
|
sizeof(FX_SAVE_AREA)));
|
|
|
|
Kix86FxSave((PULONG)NpxFrame);
|
|
|
|
Prcb->NpxThread->NpxState = NPX_STATE_NOT_LOADED;
|
|
}
|
|
|
|
Prcb->NpxThread = Thread;
|
|
}
|
|
|
|
NpxFrame = (PFX_SAVE_AREA)(((ULONG)(Thread->StackBase) -
|
|
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) {
|
|
|
|
Kix86FxSave ((PULONG)(FloatSave->u.Context));
|
|
|
|
FloatSave->u.Context->FloatSave.Cr0NpxState = NpxFrame->FloatSave.Cr0NpxState;
|
|
|
|
} else {
|
|
|
|
RtlCopyMemory (FloatSave->u.Context, NpxFrame, sizeof(FX_SAVE_AREA));
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Save only the non-volatile state
|
|
//
|
|
|
|
if (Thread->NpxState == NPX_STATE_LOADED) {
|
|
|
|
_asm {
|
|
mov eax, FloatSave
|
|
fnstcw [eax] FLOAT_SAVE.u.Fcw.ControlWord
|
|
}
|
|
|
|
Kix86StMXCsr(&FloatSave->u.Fcw.MXCsr);
|
|
|
|
} else {
|
|
//
|
|
// Save the control word from the npx frame.
|
|
//
|
|
|
|
FloatSave->u.Fcw.ControlWord = (USHORT) NpxFrame->FloatSave.ControlWord;
|
|
FloatSave->u.Fcw.MXCsr = NpxFrame->FloatSave.MXCsr;
|
|
}
|
|
|
|
//
|
|
// Save Cr0NpxState, but clear CR0_TS as there's not non-volatile
|
|
// pending fp exceptions
|
|
//
|
|
|
|
FloatSave->Cr0NpxState = NpxFrame->FloatSave.Cr0NpxState & ~CR0_TS;
|
|
}
|
|
|
|
//
|
|
// The previous state is saved. Set an initial default
|
|
// FP state for the caller
|
|
//
|
|
|
|
NpxFrame->FloatSave.Cr0NpxState = 0;
|
|
Thread->NpxState = NPX_STATE_LOADED;
|
|
Thread->NpxIrql = Irql;
|
|
ControlWord = 0x27f; // 64bit mode
|
|
MXCsr = 0x1f80;
|
|
|
|
_asm {
|
|
fninit
|
|
fldcw ControlWord
|
|
}
|
|
|
|
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;
|
|
|
|
FloatSave = (PFLOAT_SAVE) PublicFloatSave;
|
|
Thread = FloatSave->Thread;
|
|
|
|
NpxFrame = (PFX_SAVE_AREA)(((ULONG)(Thread->StackBase) -
|
|
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
|
|
}
|
|
|
|
Kix86LdMXCsr(&FloatSave->u.Fcw.MXCsr);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Fp state not loaded. Restore control word in npx frame
|
|
//
|
|
|
|
NpxFrame->FloatSave.ControlWord = FloatSave->u.Fcw.ControlWord;
|
|
NpxFrame->FloatSave.StatusWord = 0;
|
|
NpxFrame->FloatSave.TagWord = 0;
|
|
NpxFrame->FloatSave.MXCsr = FloatSave->u.Fcw.MXCsr;
|
|
}
|
|
|
|
NpxFrame->FloatSave.Cr0NpxState = FloatSave->Cr0NpxState;
|
|
}
|
|
|
|
//
|
|
// Restore NpxIrql and Cr0
|
|
//
|
|
|
|
Thread->NpxIrql = FloatSave->PreviousNpxIrql;
|
|
Cr0State = Thread->NpxState | NpxFrame->FloatSave.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) {
|
|
ExFreePool (FloatSave->u.Context);
|
|
}
|
|
|
|
FloatSave->Flags = 0;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|