/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    psctx.c

Abstract:

    This procedure implements Get/Set Context Thread

Author:

    Mark Lucovsky (markl) 25-May-1989

Notes:

    There IS NO NonVolatileContext stored outside of the trap
    frame on a 386, with the exception of floating point.  Hence,
    the NonVolatileContextPointers argument to Get/SetContext is
    always NULL on the 386.

Revision History:

     8-Jan-90   bryanwi

        Port to 386

--*/

#include "psp.h"

#define PSPALIGN_DOWN(address,amt) ((ULONG)(address) & ~(( amt ) - 1))

#define PSPALIGN_UP(address,amt) (PSPALIGN_DOWN( (address + (amt) - 1), (amt) ))

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,PspGetContext )
#pragma alloc_text(PAGE,PspGetSetContextSpecialApc )
#pragma alloc_text(PAGE,PspSetContext)
#endif


VOID
PspGetContext(
    IN PKTRAP_FRAME TrapFrame,
    IN PKNONVOLATILE_CONTEXT_POINTERS NonVolatileContext,
    IN OUT PCONTEXT Context
    )

/*++

Routine Description:

    This function moves the contents of the specified trap and NonVolatile
    context into the specified context record. It's primary user will
    be NtGetContextThread.

    N.B. - NonVolatileContext is IGNORED on the 386.

Arguments:

    TrapFrame - Supplies the contents of a trap frame that should be
                restored copied into the proper location in the context
                record.

    Context - Returns the threads current context.

Return Value:

    None.

--*/

{
    UNREFERENCED_PARAMETER( NonVolatileContext );

    PAGED_CODE();

    KeContextFromKframes(TrapFrame, NULL, Context);
}

VOID
PspSetContext(
    OUT PKTRAP_FRAME TrapFrame,
    OUT PKNONVOLATILE_CONTEXT_POINTERS NonVolatileContext,
    IN PCONTEXT Context,
    KPROCESSOR_MODE Mode
    )

/*++

Routine Description:

    This function moves the contents of the specified context record
    into the specified trap frame, and modifies the thread's non volatile
    context by storing through the thread's nonvolatile context pointers.

    N.B. - NonVolatileContext is IGNORED on the 386.

Arguments:

    TrapFrame - Returns selected pieces of the context record.

    Context - Supplies a context record to be copied in the trap and
              nonvolatile context.

    Mode - Supplies the mode to be used when sanitizing the psr, epsr and fsr

Return Value:

    None.

--*/

{
    UNREFERENCED_PARAMETER( NonVolatileContext );

    PAGED_CODE();

    KeContextToKframes(TrapFrame, NULL, Context, Context->ContextFlags, Mode);
}

VOID
PspGetSetContextSpecialApc(
    IN PKAPC Apc,
    IN PKNORMAL_ROUTINE *NormalRoutine,
    IN PVOID *NormalContext,
    IN PVOID *SystemArgument1,
    IN PVOID *SystemArgument2
    )

/*++

Routine Description:

    This function either captures the usermode state of the current
    thread, or sets the usermode state of the current thread. The
    operation type is determined by the value of SystemArgument1. A
    NULL value is used for get context, and a non-NULL value is used
    for set context.

Arguments:

    Apc - Supplies a pointer to the APC control object that caused entry
          into this routine.

    NormalRoutine - Supplies a pointer to a pointer to the normal routine
        function that was specifed when the APC was initialized.

    NormalContext - Supplies a pointer to a pointer to an arbitrary data
        structure that was specified when the APC was initialized.

    SystemArgument1, SystemArgument2 - Supplies a set of two pointer to two
        arguments that contain untyped data.

Return Value:

    None.

--*/

{
    PGETSETCONTEXT Ctx;
    PKTRAP_FRAME TrapFrame;
    PETHREAD Thread;

    PAGED_CODE();

    UNREFERENCED_PARAMETER( NormalRoutine );
    UNREFERENCED_PARAMETER( NormalContext );
    UNREFERENCED_PARAMETER( SystemArgument1 );
    UNREFERENCED_PARAMETER( SystemArgument2 );

    Ctx = CONTAINING_RECORD(Apc,GETSETCONTEXT,Apc);
    Thread = Apc->SystemArgument2;

    TrapFrame = 0;

    if (Ctx->Mode == KernelMode) {
	TrapFrame = Thread->Tcb.TrapFrame;
    }

    if (TrapFrame == NULL) {
	TrapFrame = (PKTRAP_FRAME)((PUCHAR)Thread->Tcb.InitialStack -
		    PSPALIGN_UP(sizeof(KTRAP_FRAME),KTRAP_FRAME_ALIGN) -
		    sizeof(FX_SAVE_AREA));
    }

    if ( Apc->SystemArgument1 ) {

        //
        // Set Context
        //

        PspSetContext(TrapFrame,NULL,&Ctx->Context,Ctx->Mode);

    } else {

        //
        // Get Context
        //

        PspGetContext(TrapFrame,NULL,&Ctx->Context);
    }

    KeSetEvent(&Ctx->OperationComplete,0,FALSE);

}
