2020-09-30 17:17:25 +02:00

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;
}