/*static char *SCCSID = "@(#)kmode.c    6.2 91/12/03";*/
#define SCCSID  "@(#)kmode.c    6.2 91/12/03"
/***    KMODE.C
 *
 *      IBM/Microsoft Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1989
 *      Copyright (c) Microsoft Corporation 1987, 1989
 *
 *      All Rights Reserved
 *
 *      Title:
 *
 *      Kernel Entry/Exit Support Routines
 *
 *      Description:
 *
 *      Kernel support routines to get and change client's
 *      registers and stack.
 *
 *      Public routines:
 *
 *      KMAllocContextHook
 *      KMArmContextHook
 *      KMAllocHook
 *      KMFreeHook
 *      KMDisarmHook
 *      KMThreadTerminate
 *      KMRebuildStackFrame
 *      KMQueryClientRegister
 *      KMSetClientRegister
 *      KMPushClientStack
 *      KMPopClientStack
 *      KMQueryReturnESPLaddr
 *      KMQueryClientReturnESP
 *      KMSetClientReturnESP
 *      KMSetFlatDispatch
 *
 *  Defect  flag  Date     Author         Abstract
 *  ==============================================================================
 *  105216  83540 11/17/94 Change Team    Roll defect 83540 into r207 sparse tree.
 *                                        Change kmGetStackAddr so that the call
 *                                        gate parameters for a 16 bit DYNAMIC API
 *                                        are no longer double-word rounded.
 *
 */

#define INCL_TYPES                      // for basedef.h typedefs

#include <basemac.h>
#include <basedef.h>
#define FNO_NUM FNO_KMODE_C
#include <fnotask.h>
#include <osmac.h>
#include <ostypes.h>
#include <mi.h>
#include <vmowner.h>
#include <dossymc.h>                    // for PKS frame
#include <sigdef.h>
#include <except.h>
#include <infoseg.h>
#include <pgexport.h>
#include <vmexport.h>
#include <selxport.h>
#include <seldesc.h>
#include <tkexport.h>
#include <curdir.h>
#include <dirent.h>
#include <find.h>
#include <screen.h>
#include <sem.h>
#include <tkptda.h>
#include <error.h>
#include <kmode.h>
#include <kutil.h>
#include <string.h>

/* external variables */

extern LONG InterruptLevel;             // interrupt level
extern USHORT ausSCIReturn[];           // SCI return routines
extern FPVOID afpfnReturnSEF[];         // SEF stack frame return routines
extern FPVOID afpfnReturnPASCALSEF[];   // PASCAL SEF return routines
extern FPVOID fpfnINTKernelExit;        // kernel exit routines addresses
extern FPVOID fpfnTRAPKernelExit;
extern FPVOID fpfnVDMKernelExit;
extern FPVOID fpfnSCIKernelExit;
extern FPVOID fpfnDHKernelExit;

/* internal routine prototypes */

static VOID PASCAL kmRebuildMiscISF(ULONG, PKSF, ULONG);
static VOID PASCAL kmRebuildMiscSEF(ULONG, PKSF, ULONG);
static VOID PASCAL kmRebuildMiscPASCALSEF(ULONG, PKSF, ULONG);
static VOID PASCAL kmRebuildMiscSSF(ULONG, PKSF, ULONG);
static INT PASCAL kmGetStackAddr(PTCB, PPVOID, register INT);
static PVOID PASCAL kmQueryRegPtr(register PTCB, ULONG);
static PVOID PASCAL kmQueryRegPtrPRIP(register PTCB, register PRIP);
static INT PASCAL kmClientSSRelative(PTCB, PVOID *, ULONG);

#define kmQueryPRIP(ptcb, ulRegNum)     ((PRIP)((PBYTE) \
                                        ((ptcb)->TCB_pcriFrameType) + \
                                        (ulRegNum)))

#define kmQueryReg(ptcb,ulRegNum)       (*((PULONG) \
                                          kmQueryRegPtr(ptcb,ulRegNum)))

#define kmQueryWordReg(ptcb,ulRegNum)   (*((PUSHORT) \
                                          kmQueryRegPtr(ptcb,ulRegNum)))

#define kmRoundcbargs(cbargs)           (((cbargs+3)&~3)&~_16B_Gate) //B729310
#define kmRoundcbargs_16B_Gate(cbargs)  ((cbargs & ~_16B_Gate)/2)    //B729310

/***LD  CRI structures - stack frame type structures
 *
 *      The CRI structure defines the offset from the stack frame
 *      base an register is placed.   A CRI data structure is defined
 *      for each stack type and the address of this structure is put
 *      into TCB_pcriFrameType on entry to the kernel.
 */


CRI criISF = {                          /* Interrupt Stack Frame */
    ISF_SIZE,
    0, FIELDOFFSET(ISF,isf_eax),
    0, FIELDOFFSET(ISF,isf_ebx),
    0, FIELDOFFSET(ISF,isf_ecx),
    0, FIELDOFFSET(ISF,isf_edx),
    0, FIELDOFFSET(ISF,isf_ebp),
    0, FIELDOFFSET(ISF,isf_esi),
    0, FIELDOFFSET(ISF,isf_edi),
    KM_RIP_WORD, FIELDOFFSET(ISF,isf_ds),
    KM_RIP_WORD, FIELDOFFSET(ISF,isf_es),
    KM_RIP_WORD, FIELDOFFSET(ISF,isf_fs),
    KM_RIP_WORD, FIELDOFFSET(ISF,isf_gs),
    KM_RIP_WORD, FIELDOFFSET(ISF,isf_cs),
    0, FIELDOFFSET(ISF,isf_eip),
    0, FIELDOFFSET(ISF,isf_eflag),
    KM_RIP_WORD, FIELDOFFSET(ISF,isf_ss),
    0, FIELDOFFSET(ISF,isf_esp),
    KM_RIP_INVALID, 0,                  // no cbargs
    KM_RIP_INVALID, 0,                  // no trapnum
    KM_RIP_INVALID, 0,                  // no errcode
    kmRebuildMiscISF,
    &fpfnINTKernelExit,
};

CRI criTSF = {                          /* Trap Stack Frame */
    TSF_SIZE,
    0, FIELDOFFSET(TSF,tsf_eax),
    0, FIELDOFFSET(TSF,tsf_ebx),
    0, FIELDOFFSET(TSF,tsf_ecx),
    0, FIELDOFFSET(TSF,tsf_edx),
    0, FIELDOFFSET(TSF,tsf_ebp),
    0, FIELDOFFSET(TSF,tsf_esi),
    0, FIELDOFFSET(TSF,tsf_edi),
    KM_RIP_WORD, FIELDOFFSET(TSF,tsf_ds),
    KM_RIP_WORD, FIELDOFFSET(TSF,tsf_es),
    KM_RIP_WORD, FIELDOFFSET(TSF,tsf_fs),
    KM_RIP_WORD, FIELDOFFSET(TSF,tsf_gs),
    KM_RIP_WORD, FIELDOFFSET(TSF,tsf_cs),
    0, FIELDOFFSET(TSF,tsf_eip),
    0, FIELDOFFSET(TSF,tsf_eflag),
    KM_RIP_WORD, FIELDOFFSET(TSF,tsf_ss),
    0, FIELDOFFSET(TSF,tsf_esp),
    KM_RIP_INVALID, 0,                  // no cbargs
    0, FIELDOFFSET(TSF,tsf_trapnum),
    KM_RIP_WORD, FIELDOFFSET(TSF,tsf_errcode),
    0,
    &fpfnTRAPKernelExit,
};

CRI criVSF = {                          /* VDM Stack Frame */
    VSF_SIZE,
    0, FIELDOFFSET(VSF,vsf_eax),
    0, FIELDOFFSET(VSF,vsf_ebx),
    0, FIELDOFFSET(VSF,vsf_ecx),
    0, FIELDOFFSET(VSF,vsf_edx),
    0, FIELDOFFSET(VSF,vsf_ebp),
    0, FIELDOFFSET(VSF,vsf_esi),
    0, FIELDOFFSET(VSF,vsf_edi),
    KM_RIP_WORD, FIELDOFFSET(VSF,vsf_ds),
    KM_RIP_WORD, FIELDOFFSET(VSF,vsf_es),
    KM_RIP_WORD, FIELDOFFSET(VSF,vsf_fs),
    KM_RIP_WORD, FIELDOFFSET(VSF,vsf_gs),
    KM_RIP_WORD, FIELDOFFSET(VSF,vsf_cs),
    0, FIELDOFFSET(VSF,vsf_eip),
    0, FIELDOFFSET(VSF,vsf_eflag),
    KM_RIP_WORD, FIELDOFFSET(VSF,vsf_ss),
    KM_RIP_WORD, FIELDOFFSET(VSF,vsf_esp),
    KM_RIP_INVALID, 0,                  // no cbargs
    0, FIELDOFFSET(VSF,vsf_trapnum),
    KM_RIP_WORD, FIELDOFFSET(VSF,vsf_errcode),
    0,
    &fpfnVDMKernelExit,
};

CRI criSEF = {                          /* System Entry Stack Frame (C32) */
    SEF_SIZE,
    0, FIELDOFFSET(SEF,sef_eax),
    0, FIELDOFFSET(SEF,sef_ebx),
    0, FIELDOFFSET(SEF,sef_ecx),
    0, FIELDOFFSET(SEF,sef_edx),
    0, FIELDOFFSET(SEF,sef_ebp),
    0, FIELDOFFSET(SEF,sef_esi),
    0, FIELDOFFSET(SEF,sef_edi),
    KM_RIP_WORD, FIELDOFFSET(SEF,sef_ds),
    KM_RIP_WORD, FIELDOFFSET(SEF,sef_es),
    KM_RIP_WORD, FIELDOFFSET(SEF,sef_fs),
    KM_RIP_WORD, FIELDOFFSET(SEF,sef_gs),
    KM_RIP_WORD, FIELDOFFSET(SEF,sef_cs),
    0, FIELDOFFSET(SEF,sef_eip),
    0, FIELDOFFSET(SEF,sef_eflag),
    KM_RIP_WORD + KM_RIP_TSD_RELATIVE, FIELDOFFSET(tsd_t,TSDUserSS),
    KM_RIP_C32  + KM_RIP_TSD_RELATIVE, FIELDOFFSET(tsd_t,TSDUserESP),
    0, FIELDOFFSET(SEF,sef_cbargs),
    KM_RIP_INVALID, 0,                  // no trapnum
    KM_RIP_INVALID, 0,                  // no errcode
    kmRebuildMiscSEF,
    0,
};

CRI criPASCALSEF = {                    /* System Entry Stack Frame (PASCAL32)*/
    SEF_SIZE,
    0, FIELDOFFSET(SEF,sef_eax),
    0, FIELDOFFSET(SEF,sef_ebx),
    0, FIELDOFFSET(SEF,sef_ecx),
    0, FIELDOFFSET(SEF,sef_edx),
    0, FIELDOFFSET(SEF,sef_ebp),
    0, FIELDOFFSET(SEF,sef_esi),
    0, FIELDOFFSET(SEF,sef_edi),
    KM_RIP_WORD, FIELDOFFSET(SEF,sef_ds),
    KM_RIP_WORD, FIELDOFFSET(SEF,sef_es),
    KM_RIP_WORD, FIELDOFFSET(SEF,sef_fs),
    KM_RIP_WORD, FIELDOFFSET(SEF,sef_gs),
    KM_RIP_WORD, FIELDOFFSET(SEF,sef_cs),
    0, FIELDOFFSET(SEF,sef_eip),
    0, FIELDOFFSET(SEF,sef_eflag),
    KM_RIP_WORD + KM_RIP_TSD_RELATIVE, FIELDOFFSET(tsd_t,TSDUserSS),
    KM_RIP_TSD_RELATIVE, FIELDOFFSET(tsd_t,TSDUserESP),
    0, FIELDOFFSET(SEF,sef_cbargs),
    KM_RIP_INVALID, 0,                  // no trapnum
    KM_RIP_INVALID, 0,                  // no errcode
    kmRebuildMiscPASCALSEF,
    0,
};

CRI criSSF = {                          /* SCI Stack Frame (PASCAL16) */
    SSF_SIZE,
    0, FIELDOFFSET(SSF,ssf_eax),
    0, FIELDOFFSET(SSF,ssf_ebx),
    0, FIELDOFFSET(SSF,ssf_ecx),
    0, FIELDOFFSET(SSF,ssf_edx),
    0, FIELDOFFSET(SSF,ssf_ebp),
    0, FIELDOFFSET(SSF,ssf_esi),
    0, FIELDOFFSET(SSF,ssf_edi),
    KM_RIP_WORD, FIELDOFFSET(SSF,ssf_ds),
    KM_RIP_WORD, FIELDOFFSET(SSF,ssf_es),
    KM_RIP_WORD, FIELDOFFSET(SSF,ssf_fs),
    KM_RIP_WORD, FIELDOFFSET(SSF,ssf_gs),
    KM_RIP_WORD, FIELDOFFSET(SSF,ssf_cs),
    0, FIELDOFFSET(SSF,ssf_eip),
    0, FIELDOFFSET(SSF,ssf_eflag),
    KM_RIP_WORD + KM_RIP_TSD_RELATIVE, FIELDOFFSET(tsd_t,TSDUserSS),
    KM_RIP_TSD_RELATIVE, FIELDOFFSET(tsd_t,TSDUserESP),
    0, FIELDOFFSET(SSF,ssf_cbargs),
    KM_RIP_INVALID, 0,                  // no trapnum
    KM_RIP_INVALID, 0,                  // no errcode
    kmRebuildMiscSSF,
    &fpfnSCIKernelExit,
};

CRI criDHF = {                          /* DevHlp Stack Frame */
    DHF_SIZE,
    0, FIELDOFFSET(DHF,dhf_eax),
    0, FIELDOFFSET(DHF,dhf_ebx),
    0, FIELDOFFSET(DHF,dhf_ecx),
    0, FIELDOFFSET(DHF,dhf_edx),
    0, FIELDOFFSET(DHF,dhf_ebp),
    0, FIELDOFFSET(DHF,dhf_esi),
    0, FIELDOFFSET(DHF,dhf_edi),
    KM_RIP_WORD, FIELDOFFSET(DHF,dhf_ds),
    KM_RIP_WORD, FIELDOFFSET(DHF,dhf_es),
    KM_RIP_WORD, FIELDOFFSET(DHF,dhf_fs),
    KM_RIP_WORD, FIELDOFFSET(DHF,dhf_gs),
    KM_RIP_WORD, FIELDOFFSET(DHF,dhf_cs),
    0, FIELDOFFSET(DHF,dhf_eip),
    0, FIELDOFFSET(DHF,dhf_eflag),
    KM_RIP_WORD + KM_RIP_TSD_RELATIVE, FIELDOFFSET(tsd_t,TSDUserSS),
    KM_RIP_TSD_RELATIVE, FIELDOFFSET(tsd_t,TSDUserESP),
    KM_RIP_INVALID, 0,                  // no cbargs
    KM_RIP_INVALID, 0,                  // no trapnum
    KM_RIP_INVALID, 0,                  // no errcode
    0,
    &fpfnDHKernelExit,
};

/***LT+ aeachreg - array of register number/kmreg offsets
 *
 *      This structure is used by Query/SetAll to get the
 *      register number and offset into the KMREG buffer for
 *      each register.
 */

struct erg_s {
    USHORT erg_regnum;
    USHORT erg_kmreg;
} aerg[] = {
    KM_REG_EAX, FIELDOFFSET(KMREG, kmreg_eax),
    KM_REG_EBX, FIELDOFFSET(KMREG, kmreg_ebx),
    KM_REG_ECX, FIELDOFFSET(KMREG, kmreg_ecx),
    KM_REG_EDX, FIELDOFFSET(KMREG, kmreg_edx),
    KM_REG_EBP, FIELDOFFSET(KMREG, kmreg_ebp),
    KM_REG_ESI, FIELDOFFSET(KMREG, kmreg_esi),
    KM_REG_EDI, FIELDOFFSET(KMREG, kmreg_edi),
    KM_REG_DS, FIELDOFFSET(KMREG, kmreg_ds),
    KM_REG_ES, FIELDOFFSET(KMREG, kmreg_es),
    KM_REG_FS, FIELDOFFSET(KMREG, kmreg_fs),
    KM_REG_GS, FIELDOFFSET(KMREG, kmreg_gs),
    KM_REG_CS, FIELDOFFSET(KMREG, kmreg_cs),
    KM_REG_EIP, FIELDOFFSET(KMREG, kmreg_eip),
    KM_REG_EFLAG, FIELDOFFSET(KMREG, kmreg_eflag),
    KM_REG_SS, FIELDOFFSET(KMREG, kmreg_ss),
    KM_REG_ESP, FIELDOFFSET(KMREG, kmreg_esp),
    -1, -1                              // end of table
};

/***LD+ hookheadGlobal - Global context hook head
 *
 *      This is the head and tail of the pending global context
 *      hook blocks.  The pending context hooks are executed anytime
 *      we exit the kernel or when we go thru SchedNext (unless there
 *      is swapper activity).
 *
 *      NOTE  hookheadGlobal is initialized to point to itself so the
 *            doublely linked list code will work (see KMInitHookHead).
 */

extern HOOKHEAD hookheadGlobal;
/*end*/

/***EP  KMAllocContextHook - allocate an context hook control block
 *
 *      This function allocates a hook control block for KMArmContextHook.
 *
 *      INT PASCAL KMAllocContextHook(pfn, cbRefData, ptcb, pphook);
 *
 *      ENTRY   (pfn) = arm hook function address
 *                  FN fn(pRefData, pFrameBase)
 *                      ENTRY   (pRefData) - pointer to reference data
 *                              (pFrameBase) - stack frame base pointer
 *
 *                      EXIT    NONE
 *
 *                      USES    EAX, ECX, EDX, GS, FS, Flags
 *
 *              (cbRefData) = size of reference data block
 *              (ptcb) = if -1 (KM_GLOBAL_HOOK), allocate globally,
 *                       else, allocate for this thread.
 *              (pphook)    = pointer to put hook block pointer
 *
 *      EXIT    if success, returns NO_ERROR
 *              if failure, returns error code
 *                      ERROR_NOT_ENOUGH_MEMORY
 *                      ERROR_INTERRUPTED
 *
 *      NOTE    "Global" allocated hooks are never freed automaticly; they
 *              must be cleaned up with KMFreeHook.  "Per-thread" hooks
 *              are freed when the thread terminates.
 *
 *      CONTEXT Task
 *
 *      PSEUDO-CODE
 *
 *      call alloc hook
 */

INT PASCAL
KMAllocContextHook(pfn, cbRefData, ptcb, pphook)
PFN pfn;
ULONG cbRefData;
PTCB ptcb;
PHOOK *pphook;
{
    return(KMAllocHook(pfn, 0, 0, cbRefData, ptcb, pphook));
}

/***EP  KMArmContextHook - arm a local or global context hook
 *
 *      This service adds a handler to the list of routines called either
 *      the next time "task time" is entered (global context) or the next
 *      time a specific "task time" is reached (local context).  This
 *      is a one-shot service; the handler is removed from the list after
 *      it is executed.
 *
 *      VOID PASCAL KMArmContextHook(phook, ptcb);
 *
 *      ENTRY   (phook) = hook block pointer
 *              (ptcb)  = TCB pointer
 *                   -1 => global context
 *
 *      EXIT    returns TRUE, if already armed
 *              returns FALSE, if it wasn't armed
 *
 *      CONTEXT Task
 *              Interrupt
 *
 *      NOTE    Hook routine interface:
 *                  FNARM fnarm(pRefData, pFrameBase)
 *                      ENTRY   (pRefData) - pointer to reference data
 *                              (pFrameBase) - stack frame base pointer
 *
 *                      EXIT    NONE
 *
 *                      USES    EAX, ECX, EDX, GS, FS, Flags
 *
 *                      CONTEXT Task (global) or Task (local)
 *
 *                      NOTE    1) (pFrameBase) is only valid for local
 *                                 context hook handlers.
 *
 *              Local context hooks are serviced on the way out of the
 *              kernel and can be used as a "exit event".  Exit events
 *              are used to delay some action until a thread winds itself
 *              out of kernel freeing resources like semaphores, etc.
 *              before executing the hook handler.
 *
 *              Global context hooks are either executed immediately
 *              if already task time, the next time a thread reschedules
 *              and there is no swapping activity or on the way out of
 *              the kernel.
 *
 *              Can not arm a hook that has already been armed. Panics.
 *
 *      PSEUDO-CODE
 *
 *      save and disable interrupt state
 *      if global context hook
 *          if interrupt time
 *              arm context hook block (hookheadGlobal)
 *              set global context hook flag
 *          else // task time
 *              call handler passing handler specific data
 *      else // local context hook
 *          arm context hook block (TCB_hookheadLocal)
 *          set TCB local context hook force flag
 *      restore interrupt state
 */

BOOL PASCAL
KMArmContextHook(phook, ptcb)
PHOOK phook;
PTCB ptcb;
{
    BOOL f = FALSE;
    ULONG ulFlags;

    kmAssertP(HOOK, phook);
    ulFlags = TKDisable();
    if(ptcb == KM_GLOBAL_CONTEXT) {
        if(!(f = KMArmHook(phook, &hookheadGlobal))) {
            flGlobalActions |= KM_FF_GCTXH;
        }
    }
    else {
        if(!(f = KMArmHook(phook, (PHOOKHEAD)&ptcb->TCB_hookheadLocal))) {
            ptcb->TCBForcedActions |= TK_FF_CTXH;
        }
    }
    TKEnable(ulFlags);
    return(f);
}

/***EP  KMAllocHook - allocate an arm hook service control block
 *
 *      This function allocates a generic hook control block for
 *      KMArmContextHook and various other kernel hook services.
 *
 *      ENTRY   (pfn) = arm hook function address
 *                  FN fn(pRefData, pFrameBase)
 *                      ENTRY   (pRefData) - pointer to reference data
 *                              (pFrameBase) - stack frame base pointer
 *
 *                      EXIT    NONE
 *
 *                      USES    EAX, ECX, EDX, GS, FS, Flags
 *
 *              (iType) = hook type
 *              (cbHookData) = size of hook specific data (< 256)
 *              (cbRefData) = size of reference data block
 *              (ptcb) = if -1 (KM_GLOBAL_HOOK), allocate globally,
 *                       else, allocate for this thread.
 *              (pphook)    = pointer to put hook block pointer
 *
 *      EXIT    if success, returns NO_ERROR
 *              if failure, returns error code
 *                      ERROR_NOT_ENOUGH_MEMORY
 *                      ERROR_INTERRUPTED
 *
 *      NOTE    "Global" allocated hooks are never freed automaticly; they
 *              must be cleaned up with KMFreeHook.  "Per-thread" hooks
 *              are freed when the thread terminates.
 *
 *      CONTEXT Task
 *
 *      PSEUDO-CODE
 *
 *      phook = alloc(sizeof(HOOK) + cbHookData + cbRefData)
 *      if error
 *         return error
 *      put pfn into hook structure
 *      put reference data offset into hook structure
 *      initialize links to unarmed
 *      if ptcb is -1
 *         set owner to global owner value (-1)
 *      else
 *         add to ptcb's hook list (ptcb->TCB_phookOwnerHead)
 *      return phook
 */

INT PASCAL
KMAllocHook(pfn, iType, cbHookData, cbRefData, ptcb, pphook)
PFN pfn;
BYTE iType;
BYTE cbHookData;
ULONG cbRefData;
PTCB ptcb;
PHOOK *pphook;
{
    register PHOOK phook;
    int rc;

    // reference data must be dword aligned
    kmAssertTRUE((cbHookData & 0x3) == 0);

    // reference data plus hook structure size must be < 256
    kmAssertTRUE((cbHookData + sizeof(HOOK)) < 256);

    // allocate hook data structure from heap
    rc = VMAllocKHB(VM_HKH_PUB_RESRW, sizeof(HOOK) + cbHookData + cbRefData,
      KMHOOKOWNER, 0, pphook);
    if(rc == NO_ERROR) {
        phook = *pphook;
        kmAssertCreate(HOOK, phook);
        phook->hook_pfn = (PVOID)pfn;
        phook->hook_offRefData = cbHookData + (BYTE)sizeof(HOOK);
#ifdef  KMSTRICT
        if(cbRefData == 0) {
            phook->hook_offRefData = 0;
        }
#endif
        phook->hook_iType = iType;
        KMMarkHookUnlinked(phook);
        if(ptcb == KM_GLOBAL_HOOK) {
            phook->hook_phookOwner = KM_GLOBAL_HOOK;
        }
        else {
            phook->hook_ptcbOwner = ptcb;
            phook->hook_phookOwner = ptcb->TCB_phookOwnerHead;
            ptcb->TCB_phookOwnerHead = phook;
        }
    }
    return(rc);
}

/***EP  KMFreeHook - free an arm hook service control block.
 *
 *      This function frees a generic hook control block for
 *      KMArmContextHook and various other kernel hook services.
 *
 *      ENTRY   (phookFree) = hook block pointer to free
 *
 *      EXIT    NONE
 *
 *      CONTEXT Task
 *
 *      PSEUDO-CODE
 *
 *      remove block from per-thread owner list
 *      disarm hook
 *      free hook block
 */

VOID PASCAL
KMFreeHook(phookFree)
PHOOK phookFree;
{
    register PHOOK phookTemp;

    kmAssertP(HOOK, phookFree);
    if(phookFree->hook_phookOwner != KM_GLOBAL_HOOK) {

        phookTemp = (PHOOK) &((PTCB)(phookFree->hook_ptcbOwner))->TCB_phookOwnerHead;

        (PBYTE) phookTemp -= FIELDOFFSET(HOOK, hook_phookOwner);
        while(phookTemp != NULL) {
            if(phookTemp->hook_phookOwner == phookFree) {
                phookTemp->hook_phookOwner = phookFree->hook_phookOwner;
                break;
            }
            phookTemp = phookTemp->hook_phookOwner;
            kmAssertP(HOOK, phookTemp);
        }
    }
    KMDisarmHook(phookFree);
    kmAssertDestroy(HOOK, phookFree);
    VMFreeKHB(VM_HKH_PUB_RESRW, phookFree);
}

/***EP  KMDisarmHook - disarm a hook/remove arm hook block from arm list
 *
 *      Removes a hook control block from the arm hook service list.
 *
 *      ENTRY   (phook) = hook block pointer
 *
 *      EXIT    returns TRUE, if already unarmed
 *              returns FALSE, if armed
 *
 *      CONTEXT Task
 *              Interrupt
 *
 *      PSEUDO-CODE
 *
 *      save and disable interrupt state
 *      if unarmed
 *         restore interrupt state
 *         return TRUE
 *      else
 *         remove the hook block from the linked list
 *         restore interrupt state
 *         return FALSE
 */

BOOL PASCAL
KMDisarmHook(phook)
PHOOK phook;
{
    ULONG ulFlags;

    kmAssertP(HOOK, phook);
    ulFlags = TKDisable();
    if(!KMIsHookLinked(phook)) {
        TKEnable(ulFlags);
        return(TRUE);
    }
    KMDeleteHook(phook);
    TKEnable(ulFlags);
    return(FALSE);
}

/***EP  KMThreadTerminate - thread termination routine for arm hook block
 *
 *      This function frees all the generic hook control block
 *      for KMArmContextHook and various other kernel hook services
 *      during thread termination.
 *
 *      ENTRY   NONE
 *
 *      EXIT    NONE
 *
 *      CONTEXT Task
 *
 *      PSEUDO-CODE
 *
 *      for each entry in the TCB_phookOwnerHead list
 *          free hook block
 */

VOID PASCAL
KMThreadTerminate()
{
    register PHOOK phook, phookNext;

    phook = pTCBCur->TCB_phookOwnerHead;
    while(phook != NULL) {
        kmAssertP(HOOK, phook);
        phookNext = phook->hook_phookOwner;
        KMDisarmHook(phook);            // do KMFreeHook without the
        VMFreeKHB(VM_HKH_PUB_RESRW, phook);     //  owner list removal code
        phook = phookNext;
    }
    pTCBCur->TCB_phookOwnerHead = NULL;
}

/***EP  KMRebuildStackFrame - rebuilds the stack frame for InDos == 0 faults
 *
 *      This function rebuilds the stack frame when we fault at ring 0
 *      while InDos == 0.  This condition happens when we are exiting
 *      the kernel so the TCB_pFrameBase and TCB_pcriFrameType were set.
 *      The trap manager before calling this routine must have moved the
 *      KSF (kernel stack frame) up to make room for the original stack
 *      frame and interpreted the faulting instruction.  This routine
 *      uses the information in the CRI to rebuild the original stack
 *      frame.  If the faulting instruction is a RET n, the calling
 *      routine must pass the "n" value as the number of arguments.
 *
 *      ENTRY   (ulFaultReg) = faulting register number
 *              (pksf)       = pointer to kernel stack frame
 *              (cbargs)     = number of arguments on the stack (must be 0
 *                             for frames with no cbargs)
 *
 *      EXIT    NONE
 *
 *      CONTEXT Task
 *
 *      WARNING This code assumes that the general registers are always saved
 *              on the stack with a "pushad" instruction.
 *
 *      PSEUDO-CODE
 *
 *      get the stack offset for the faulting register from the CRI
 *      if rebuild DS
 *          move DS from the KSF to the new stack frame
 *      if rebuild ES
 *          move ES from the KSF to the new stack frame
 *      if rebuild FS
 *          move FS from the KSF to the new stack frame
 *      if rebuild GS
 *          move GS from the KSF to the new stack frame
 *      if CBARGS exists
 *          if rebuild CBARGS
 *              calculate the number of arguments on the stack
 *              set cbargs in the new stack frame
 *      if rebuild GEN
 *          move the general registers from the KSF to the new stack frame
 *      if rebuild error code
 *          move error code from KSF to new stack frame
 *      if rebuild trap number
 *          move trap number from KSF to new stack frame
 *      get stack frame exit routine address from CRI
 *      if stack frame exit routine exists
 *          change the KSF's CS:EIP to return to the exit routine
 *      if misc rebuild routine exists
 *          execute misc rebuild routine to set up things like CurrIntLevel,etc
 *      make sure the KSF's interrupts are disabled, direction flag clear.
 */

VOID PASCAL
KMRebuildStackFrame(ulFaultReg, pksf, cbargs)
ULONG ulFaultReg;
PKSF pksf;
ULONG cbargs;
{
    register USHORT off;
    register PTCB ptcb = pTCBCur;
    register PRIP prip;
    PCRI pcri;

    pcri = ptcb->TCB_pcriFrameType;
    off = (USHORT)((ulFaultReg == KM_REG_TOP) ? 0 :
      kmQueryPRIP(ptcb, ulFaultReg)->rip_offset);

    prip = kmQueryPRIP(ptcb, KM_REG_DS);
    if(off > prip->rip_offset) {
        *(PUSHORT)kmQueryRegPtrPRIP(ptcb, prip) = pksf->ksf_ds;
    }
    prip = kmQueryPRIP(ptcb, KM_REG_ES);
    if(off > prip->rip_offset) {
        *(PUSHORT)kmQueryRegPtrPRIP(ptcb, prip) = pksf->ksf_es;
    }
    prip = kmQueryPRIP(ptcb, KM_REG_FS);
    if(off > prip->rip_offset) {
        *(PUSHORT)kmQueryRegPtrPRIP(ptcb, prip) = pksf->ksf_fs;
    }
    prip = kmQueryPRIP(ptcb, KM_REG_GS);
    if(off > prip->rip_offset) {
        *(PUSHORT)kmQueryRegPtrPRIP(ptcb, prip) = pksf->ksf_gs;
    }
    prip = kmQueryPRIP(ptcb, KM_REG_CBARGS);
    if(!(prip->rip_flags & KM_RIP_INVALID)) {
        if(off > prip->rip_offset) {
            if(ulFaultReg != KM_REG_RETURN) {

                // BUGBUG: We are unable to preserve word-oddness here today.

                cbargs = (ULONG) &pTSDCur->TSDUserESP -
                  ((ULONG) kmQueryRegPtr(pTCBCur, KM_REG_CS) + sizeof(ULONG));
            }
            *(PULONG)kmQueryRegPtrPRIP(ptcb, prip) = cbargs;
        }
        else {
            cbargs = *(PULONG)kmQueryRegPtrPRIP(ptcb, prip);
        }
    }
    prip = kmQueryPRIP(ptcb, KM_REG_EDI);
    if(off > prip->rip_offset) {

        // This code assumes that the general registers are always saved
        // on the stack with a "pushad" instruction.

        memcpy(kmQueryRegPtrPRIP(ptcb, prip), &pksf->ksf_edi,
          sizeof(struct pushad_s));
    }
    prip = kmQueryPRIP(ptcb, KM_REG_EFLAG);
    if(off > prip->rip_offset) {
        *(PULONG)kmQueryRegPtrPRIP(ptcb, prip) = pksf->ksf_eflag;
    }
    prip = kmQueryPRIP(ptcb, KM_REG_ERRCODE);
    if(!(prip->rip_flags & KM_RIP_INVALID)) {
        if(off > prip->rip_offset) {
            *(PULONG)kmQueryRegPtrPRIP(ptcb, prip) = pksf->ksf_errcode;
        }
    }
    prip = kmQueryPRIP(ptcb, KM_REG_TRAPNUM);
    if(!(prip->rip_flags & KM_RIP_INVALID)) {
        if(off > prip->rip_offset) {
            *(PULONG)kmQueryRegPtrPRIP(ptcb, prip) = pksf->ksf_trapnum;
        }
    }
    if(pcri->cri_pfpfnKernelExit != NULL) {
        pksf->ksf_eip = OFFSETOF32(*pcri->cri_pfpfnKernelExit);
        pksf->ksf_cs = SEGMENTOF32(*pcri->cri_pfpfnKernelExit);
    }
    if(pcri->cri_pfnRebuild != NULL) {
        (*pcri->cri_pfnRebuild)(ulFaultReg, pksf, cbargs);
    }
    pksf->ksf_eflag &= ~(F_INTERRUPT | F_DIRECTION);
}

/***LP  kmRebuildMiscISF - rebuilds misc interrupt manager stack frame fields
 *
 *      ENTRY   (ulFaultReg) = faulting register number
 *              (pksf)       = pointer to kernel stack frame
 *              (cbargs)     = number of arguments on the stack (must be 0
 *                             for frames with no cbargs)
 *
 *      EXIT    NONE
 *
 *      CONTEXT Task
 *
 *      PSEUDO-CODE
 *
 *      set the current IRQ level field to -1 (task time)
 */

VOID PASCAL
kmRebuildMiscISF(ulFaultReg, pksf, cbargs)
ULONG ulFaultReg;
PKSF pksf;
ULONG cbargs;
{
    ((PISF)(pTCBCur->TCB_pFrameBase))->isf_CurrIntLevel = -1;
}

/***LP  kmRebuildMiscSEF - rebuilds misc 32 bit C callgate stack frame fields
 *
 *      ENTRY   (ulFaultReg) = faulting register number
 *              (pksf)       = pointer to kernel stack frame
 *              (cbargs)     = number of arguments on the stack (must be 0
 *                             for frames with no cbargs)
 *
 *      EXIT    NONE
 *
 *      CONTEXT Task
 *
 *      PSEUDO-CODE
 */

VOID PASCAL
kmRebuildMiscSEF(ulFaultReg, pksf, cbargs)
ULONG ulFaultReg;
PKSF pksf;
ULONG cbargs;
{
    pksf->ksf_eip = OFFSETOF32(afpfnReturnSEF[kmRoundcbargs(cbargs)/4]);
    pksf->ksf_cs = SEGMENTOF32(afpfnReturnSEF[kmRoundcbargs(cbargs)/4]);

    // the kernel exit routine sets user's EAX to the EAX passed
    pksf->ksf_eax = kmQueryReg(pTCBCur, KM_REG_EAX);

    // unadjust user's ESP; this is the inverse of the C-style RETF n
    pTSDCur->TSDUserESP += kmRoundcbargs(cbargs);
}

/***LP  kmRebuildMiscPASCALSEF - rebuilds misc 32 bit PASCAL callgate
 *                               stack frame fields
 *
 *      ENTRY   (ulFaultReg) = faulting register number
 *              (pksf)       = pointer to kernel stack frame
 *              (cbargs)     = number of arguments on the stack (must be 0
 *                             for frames with no cbargs)
 *
 *      EXIT    NONE
 *
 *      CONTEXT Task
 *
 *      PSEUDO-CODE
 */

VOID PASCAL
kmRebuildMiscPASCALSEF(ulFaultReg, pksf, cbargs)
ULONG ulFaultReg;
PKSF pksf;
ULONG cbargs;
{
    pksf->ksf_eip = OFFSETOF32(afpfnReturnPASCALSEF[kmRoundcbargs(cbargs)/4]);
    pksf->ksf_cs = SEGMENTOF32(afpfnReturnPASCALSEF[kmRoundcbargs(cbargs)/4]);

    // the kernel exit routine sets user's EAX to the EAX passed
    pksf->ksf_eax = kmQueryReg(pTCBCur, KM_REG_EAX);
}

/***LP  kmRebuildMiscSSF - rebuilds misc SCI stack frame fields
 *
 *      ENTRY   (ulFaultReg) = faulting register number
 *              (pksf)       = pointer to kernel stack frame
 *              (cbargs)     = number of arguments on the stack (must be 0
 *                             for frames with no cbargs)
 *
 *      EXIT    NONE
 *
 *      CONTEXT Task
 *
 *      PSEUDO-CODE
 */

VOID PASCAL
kmRebuildMiscSSF(ulFaultReg, pksf, cbargs)
ULONG ulFaultReg;
PKSF pksf;
ULONG cbargs;
{
    /* B729310
     * The SCI mechanism in API's is currently being used with 32 bit
     * call gates, exclusively. The only exception is Dynamic API's which
     * can use the SCI mechanism with a 16 bit call gate.
     * The _16B_Gate flag (MSB in cbargs) determines the method used
     * in calculating an index into ths ausSCIReturn array.
     * If set a Dynamic API is using a 16 bit call gate and the index
     * will point to a word aligned SCIReturnX routine
     * (e.g. SCIReturn0,SCIReturn2,SCIReturn4,...)
     * If clear a 32 bit call gate is being used and the index will
     * point to a dword align SCIReturnX routine
     * (e.g. SCIReturn0,SCIReturn4,SCIReturn8,...)
     *
     */
    int index = ((cbargs & _16B_Gate) ?                              //B729310
                   kmRoundcbargs_16B_Gate(cbargs) :                  //B729310
                   (kmRoundcbargs(cbargs)/2));                       //B729310
    ((PSSF)(pTCBCur->TCB_pFrameBase))->ssf_sciret =
        ausSCIReturn[index];                                         //B729310
}

/***EP  KMQueryAllClientRegisters - returns all the client registers
 *
 *      Returns all the client's registers in the pkmreg buffer.
 *
 *      ENTRY   (ptcb) = pointer to tcb
 *              (pkmreg) = pointer to kmreg buffer
 *
 *      EXIT    if success, returns NO_ERROR
 *              if failure, returns error code
 *                      ERROR_INVALID_PARAMETER
 *
 *      CONTEXT Task
 *
 *      PSEUDO-CODE
 */

INT PASCAL
KMQueryAllClientRegisters(ptcb, pkmreg)
PTCB    ptcb;
PKMREG  pkmreg;
{
    int i;

    for(i = 0; aerg[i].erg_regnum != (USHORT) -1; i++) {
        KMQueryClientRegister(ptcb, aerg[i].erg_regnum,
          (PULONG) ((PBYTE) pkmreg + aerg[i].erg_kmreg));
    }
    return(NO_ERROR);
}

/***EP  KMSetAllClientRegisters - sets all the client registers
 *
 *      Sets all the client's registers from the pkmreg buffer.
 *
 *      ENTRY   (ptcb) = pointer to tcb
 *              (pkmreg) = pointer to kmreg buffer
 *
 *      EXIT    if success, returns NO_ERROR
 *              if failure, returns error code
 *                      ERROR_INVALID_PARAMETER
 *
 *      CONTEXT Task
 *
 *      PSEUDO-CODE
 */

INT PASCAL
KMSetAllClientRegisters(ptcb, pkmreg)
PTCB    ptcb;
PKMREG  pkmreg;
{
    int i;

    for(i = 0; aerg[i].erg_regnum != (USHORT) -1; i++) {
        KMSetClientRegister(ptcb, aerg[i].erg_regnum,
          *((PULONG) ((PBYTE) pkmreg + aerg[i].erg_kmreg)));
    }
    return(NO_ERROR);
}

/***EP  KMQueryClientRegister - returns the specified client register
 *
 *      Returns the specified client register from the client's
 *      stack frame.
 *
 *      ENTRY   (ptcb) = pointer to tcb
 *              (ulRegNum) = register number
 *                              KM_REG_DS
 *                              KM_REG_ES
 *                              KM_REG_SS
 *                              KM_REG_CS
 *                              KM_REG_GS
 *                              KM_REG_FS
 *                              KM_REG_EAX
 *                              KM_REG_EBX
 *                              KM_REG_ECX
 *                              KM_REG_EDX
 *                              KM_REG_EBP
 *                              KM_REG_ESP
 *                              KM_REG_ESI
 *                              KM_REG_EDI
 *                              KM_REG_EIP
 *                              KM_REG_EFLAG
 *                              KM_REG_CBARGS
 *                              KM_REG_TRAPNUM
 *                              KM_REG_ERRCODE
 *              (pulReg) = pointer to put register
 *
 *      EXIT    if success, returns NO_ERROR
 *              if failure, returns error code
 *                      ERROR_INVALID_PARAMETER
 *
 *      CONTEXT Task
 *
 *      PSEUDO-CODE
 *
 *      get pointer client register on stack frame
 *      return the client register
 */

INT PASCAL
KMQueryClientRegister(ptcb, ulRegNum, pulReg)
PTCB ptcb;
ULONG ulRegNum;
PULONG pulReg;
{
    register PRIP prip;

    prip = kmQueryPRIP(ptcb, ulRegNum);
    if(prip->rip_flags & KM_RIP_INVALID) {
        return(ERROR_INVALID_PARAMETER);
    }
    if(prip->rip_flags & KM_RIP_WORD) {
        *pulReg = *(PUSHORT)kmQueryRegPtrPRIP(ptcb, prip);
    } else {
        *pulReg = *(PULONG)kmQueryRegPtrPRIP(ptcb, prip);
    }
    return(NO_ERROR);
}

/***EP  KMSetClientRegister - sets the specified client register
 *
 *      Changes the specified client register in the client's
 *      stack frame.
 *
 *      ENTRY   (ptcb) = pointer to tcb
 *              (ulRegNum) = register number
 *                              KM_REG_DS
 *                              KM_REG_ES
 *                              KM_REG_SS
 *                              KM_REG_CS
 *                              KM_REG_GS
 *                              KM_REG_FS
 *                              KM_REG_EAX
 *                              KM_REG_EBX
 *                              KM_REG_ECX
 *                              KM_REG_EDX
 *                              KM_REG_EBP
 *                              KM_REG_ESP
 *                              KM_REG_ESI
 *                              KM_REG_EDI
 *                              KM_REG_EIP
 *                              KM_REG_EFLAG
 *              (ulReg) = register value
 *
 *      EXIT    if success, returns NO_ERROR
 *              if failure, returns error code
 *                      ERROR_INVALID_PARAMETER
 *
 *      CONTEXT Task
 *
 *      PSEUDO-CODE
 *
 *      get pointer client register on stack frame
 *      set the client register with value passed
 */

INT PASCAL
KMSetClientRegister(ptcb, ulRegNum, ulReg)
PTCB    ptcb;
ULONG   ulRegNum;
ULONG   ulReg;
{
    register PRIP prip;

    prip = kmQueryPRIP(ptcb, ulRegNum);
    if(prip->rip_flags & (KM_RIP_INVALID | KM_RIP_INVALID_SET)) {
        return(ERROR_INVALID_PARAMETER);
    }
    if(prip->rip_flags & KM_RIP_WORD) {
        *(PUSHORT)kmQueryRegPtrPRIP(ptcb, prip) = (USHORT) ulReg;
    } else {
        *(PULONG)kmQueryRegPtrPRIP(ptcb, prip) = ulReg;
    }
    return(NO_ERROR);
}

/***EP  KMQueryCSEIPLaddr - returns the linear address of the client's CS:EIP
 *
 *      Calculates the linear address of the client's CS:EIP.
 *
 *      ENTRY   (ptcb) = pointer to tcb
 *              (pp)   = pointer to put laddr
 *
 *      EXIT    if no errors, returns NO_ERROR
 *              if error, returns error code
 *                      ERROR_PROTECTION_VIOLATION
 *
 *      CONTEXT Task
 *
 *      PSEUDO-CODE
 *
 *      get CS selector or segment
 *      if V86 mode
 *          base = seg * 16
 *      else
 *          get descriptor info
 *          if error
 *              put up gp fault popup
 *              return error
 *      return base + client's eip
 */

INT PASCAL
KMQueryCSEIPLaddr(ptcb, pp)
PTCB ptcb;
PVOID *pp;
{
    struct seldesc_s seldesc;
    SEL sel;

    /* get CS attributes, limit and base */
    sel = kmQueryWordReg(ptcb, KM_REG_CS);

    /* if a V86 mode address */
    if(kmQueryReg(ptcb, KM_REG_EFLAG) & F_VM) {
        seldesc.desc_laddr = sel << 4;
    }
    else {
        if(SELGetDescInfo(sel, 0, SSToDS(&seldesc))) {
            TRAPProcessFatalFault(EXCGP, sel);
            return(ERROR_PROTECTION_VIOLATION);
        }
    }
    *pp = (void *) (seldesc.desc_laddr + kmQueryReg(ptcb, KM_REG_EIP));
    return(NO_ERROR);
}

/***EP  KMQuerySSESPLaddr - returns the linear address of the client's SS:ESP
 *
 *      Calculates the linear address of the client's SS:ESP.
 *
 *      This is the "Real ESP", as stored in the stack frame.
 *      KMQuerySSESPLaddr does not adjust for callgates.  See
 *      KMQueryReturnESPLaddr.
 *
 *      KMQuerySSESPLaddr ignores desc_limit.
 *
 *      ENTRY   (ptcb) = pointer to tcb
 *              (pp)   = pointer to put laddr
 *
 *      EXIT    if no errors, returns NO_ERROR
 *              if error, returns error code
 *                      ERROR_PROTECTION_VIOLATION
 *
 *      CONTEXT Task
 *
 *      PSEUDO-CODE
 *
 *      get ESP register;
 *      return kmClientSSRelative(esp);
 */

INT PASCAL
KMQuerySSESPLaddr(ptcb, pp)
PTCB ptcb;
PVOID *pp;
{
    return (kmClientSSRelative(ptcb, pp, kmQueryReg(ptcb, KM_REG_ESP)));
}

/***EP  KMQueryReturnESPLaddr - Get linear address of the client's return ESP
 *
 *      KMQueryReturnESPLaddr gets the linear address of the client's
 *      ESP as if the client had just returned to user space.
 *
 *      ENTRY   (ptcb) = pointer to tcb
 *              (pp)   = pointer to put laddr
 *
 *      EXIT    if no errors, returns NO_ERROR
 *              if error, returns error code
 *                      ERROR_PROTECTION_VIOLATION
 *
 *      CONTEXT Task
 *
 *      PSEUDO-CODE
 *
 *      ulesp = KMQueryClientReturnESP
 *      return kmClientSSRelative(esp);
 */

INT PASCAL
KMQueryReturnESPLaddr(ptcb, pp)
PTCB ptcb;
PVOID *pp;
{
    ULONG ulesp;

    KMQueryClientReturnESP(ptcb, SSToDS(&ulesp));
    return (kmClientSSRelative(ptcb, pp, ulesp));
}

/***LP  kmClientSSRelative - returns the linear address of a client stack addr
 *
 *      Calculates the linear address of an offset on the client's stack,
 *      based on the B bit of SS.  kmClientSSRelative ignores desc_limit.
 *      If the SS selector is invalid, ERROR_PROTECTION_VIOLATION will occur.
 *
 *      ENTRY   (ptcb) = pointer to tcb
 *              (pp)   = pointer to put laddr
 *              (ulOff)= client stack-relative offset
 *
 *      EXIT    if no errors, returns NO_ERROR
 *              if error, returns error code
 *                      ERROR_PROTECTION_VIOLATION
 *                      Process marked to die.
 *
 *      CONTEXT Task
 *
 *      PSEUDO-CODE
 *
 *      get SS selector or segment
 *      if V86 mode
 *          base = seg * 16
 *          attr = 0
 *      else
 *          get descriptor info
 *          if error
 *              put up gp fault popup
 *              return error
 *      if BIG bit clear on stack selector
 *          mask ulOff to 16-bit
 *      return base + esp
 */

static INT PASCAL
kmClientSSRelative(ptcb, pp, ulOff)
PTCB ptcb;
PVOID *pp;
ULONG ulOff;
{
    struct seldesc_s seldesc;
    SEL sel;

    /* get SS attributes, limit and base */
    sel = kmQueryWordReg(ptcb, KM_REG_SS);

    /* if a V86 mode address */
    if(kmQueryReg(ptcb, KM_REG_EFLAG) & F_VM) {
        seldesc.desc_laddr = sel << 4;
        seldesc.desc_attr = 0;
    }
    else {
        if(SELGetDescInfo(sel, 0, SSToDS(&seldesc))) {
            TRAPProcessFatalFault(EXCGP, sel);
            return(ERROR_PROTECTION_VIOLATION);
        }
    }
    /* mask offset if B bit is clear */
    if(!(seldesc.desc_attr & D_DBIG)) {
        ulOff &= 0xffff;
    }
    *pp = (void *) (seldesc.desc_laddr + ulOff);
    return(NO_ERROR);
}

/***EP  KMQueryClientReturnESP - Get client's return ESP
 *
 *      KMQueryClientReturnESP gets the value of the client's ESP
 *      as if the client had just returned to user space.
 *
 *      KMQueryClientReturnESP does examine the Client's SS register,
 *      so the high word of ESP may be garbage if the B bit is clear
 *      in SS.  Do not simply zeroize this garbage value, but maintain it.
 *
 *      ENTRY   (ptcb) = pointer to tcb
 *              (pulReg) = pointer to put register
 *
 *      EXIT    if no errors, returns NO_ERROR
 *              if error, returns error code
 *                      ERROR_PROTECTION_VIOLATION
 *
 *      CONTEXT Task
 *
 *      PSEUDO-CODE
 *
 *      cbargs = 0
 *      get prip(ESP)
 *      if (pcbargs exists) and (KM_RIP_C32 not set in rip_flags)
 *          cbargs = *pcbargs;
 *      *pulReg = esp + cbargs;
 */

INT PASCAL
KMQueryClientReturnESP(ptcb, pulReg)
PTCB ptcb;
PULONG pulReg;
{
    ULONG cbargs = 0;
    PULONG pcbargs;
    register PRIP prip;

    prip = kmQueryPRIP(ptcb, KM_REG_ESP);

    if (((pcbargs = kmQueryRegPtr(ptcb, KM_REG_CBARGS)) != NULL) &&
        (!(prip->rip_flags & KM_RIP_C32)))
        cbargs = *pcbargs;

    *pulReg = kmQueryReg(ptcb, KM_REG_ESP) + kmRoundcbargs(cbargs);

    return(NO_ERROR);
}

/***EP  KMSetClientReturnESP - Set the Return ESP depending on stack frame
 *
 *      KMSetClientReturnESP will set up the user's stack so that
 *      the Return ESP is that specified in this call.  ulReg is the
 *      desired Return ESP value, and should not be adjusted in any
 *      way by the caller of KMSetClientReturnESP.
 *
 *      KMSetClientReturnESP does examine the Client's SS register,
 *      so an invalid stack pointer may result from this call.
 *
 *      ENTRY   (ptcb) = pointer to tcb
 *              (ulReg)= desired Return ESP value
 *
 *      EXIT    if no errors, returns NO_ERROR
 *              if error, returns error code
 *                      ERROR_PROTECTION_VIOLATION
 *
 *      CONTEXT Task
 *
 *      PSEUDO-CODE
 *
 *      cbargs = 0
 *      get prip(ESP)
 *      if (pcbargs exists) and (KM_RIP_C32 not set in rip_flags)
 *          cbargs = *pcbargs;
 *      return (KMSetClientRegister(...ESP, ulReg - kmRoundcbargs(cbargs));
 */

INT PASCAL
KMSetClientReturnESP(ptcb, ulReg)
PTCB    ptcb;
ULONG   ulReg;
{
    register PRIP prip;
    register ULONG cbargs = 0;
    register PULONG pcbargs;

    prip = kmQueryPRIP(ptcb, KM_REG_ESP);

    if (((pcbargs = kmQueryRegPtr(ptcb, KM_REG_CBARGS)) != NULL) &&
        (!(prip->rip_flags & KM_RIP_C32)))
            cbargs = *pcbargs;

    return(KMSetClientRegister(ptcb, KM_REG_ESP, ulReg-kmRoundcbargs(cbargs)));
}

/***EP  KMSetFlatDispatch - Set up a flat model kernel dispatcher's registers
 *
 *      KMSetFlatDispatch sets up the client registers to prepare to
 *      dispatch to some user flat model address. The stack will
 *      not be moved, but may be thunked to a 32 bit value.
 *
 *      KMSetFlatDispatch will maintain for the current user ring level.
 *
 *      If an error is returned, the client's registers have not changed.
 *
 *      ENTRY   (ptcb) = pointer to tcb
 *              (pfn)  = Dispatcher entry point.
 *
 *      EXIT    if no errors, returns NO_ERROR
 *              if error, returns error code
 *                      ERROR_PROTECTION_VIOLATION
 *
 *      EFFECTS:
 *          alters client registers: CS, EIP, DS, ES, FS, GS, SS, ESP
 *
 *      CONTEXT Task
 *
 *      PSEUDO-CODE
 *
 *      set segment register set to ring 3 defaults
 *      kmAssertTRUE (Client is not in V86 mode);
 *      rc = KMQueryReturnESPLaddr (ptcb, SSToDS(&ulesp));
 *      if (ERROR) return (rc);
 *      usInitialSS = Client's SS Register;
 *      if (usInitialSS is neither ring 2 nor ring 3)
 *          TRAPProcessFatalFault(EXCGP, sel);
 *          return(ERROR_PROTECTION_VIOLATION);
 *      if (usInitialSS is ring 2)
 *          convert segment register set to ring 2 equivalents
 *      KMSetClientReturnESP(ptcb, ulesp);
 *      Set the client segment registers to calculated values
 *      Set the client's EIP register to pfn
 *      return (NO_ERROR)
 */

INT PASCAL
KMSetFlatDispatch(ptcb, pfn)
PTCB    ptcb;
PFN     pfn;
{
    ULONG   rc;
    ULONG   ulesp;
    PRIP    prip;
    SEL     selCS = R3CS;
    SEL     selDS = R3DS;
    SEL     selSSInitial;

    kmAssertTRUE(!(kmQueryReg(ptcb, KM_REG_EFLAG) & F_VM));

    if (NO_ERROR != (rc= KMQueryReturnESPLaddr(ptcb, (PVOID *)SSToDS(&ulesp))))
        return (rc);

    selSSInitial = kmQueryWordReg(ptcb, KM_REG_SS);

    /* If the SS is neither ring 2 nor ring 3, the stack frame is toast! */

    if ((selSSInitial & RPL_MASK) < RPL_RING2) {
        TRAPProcessFatalFault(EXCGP, selSSInitial);
        return(ERROR_PROTECTION_VIOLATION);
    }

    /* Maintain current ring level. */

    if ((selSSInitial & RPL_MASK) == RPL_RING2) {
        selCS = R2CS;
        selDS = R2DS;
    }

    /* Now set all the required client registers. */

    KMSetClientReturnESP(ptcb, ulesp);

    prip = kmQueryPRIP(ptcb, KM_REG_CS);
    *(PUSHORT)kmQueryRegPtrPRIP(ptcb, prip) = selCS;

    prip = kmQueryPRIP(ptcb, KM_REG_DS);
    *(PUSHORT)kmQueryRegPtrPRIP(ptcb, prip) = selDS;

    prip = kmQueryPRIP(ptcb, KM_REG_ES);
    *(PUSHORT)kmQueryRegPtrPRIP(ptcb, prip) = selDS;

    prip = kmQueryPRIP(ptcb, KM_REG_FS);
    *(PUSHORT)kmQueryRegPtrPRIP(ptcb, prip) = R3TIB;

    prip = kmQueryPRIP(ptcb, KM_REG_GS);
    *(PUSHORT)kmQueryRegPtrPRIP(ptcb, prip) = 0;

    prip = kmQueryPRIP(ptcb, KM_REG_SS);
    *(PUSHORT)kmQueryRegPtrPRIP(ptcb, prip) = selDS;

    prip = kmQueryPRIP(ptcb, KM_REG_EIP);
    *(PULONG)kmQueryRegPtrPRIP(ptcb, prip) = (ULONG) pfn;

    return (NO_ERROR);
}

/***EP  KMPushClientStack - pushes memory on client's stack
 *
 *      Adds the specified number of bytes from the buffer
 *      passed to the client's stack.
 *
 *      ENTRY   (ptcb) = tcb of thread to push
 *              (cb)   = number of bytes
 *              (pbuf) = pointer to buffer
 *                  if NULL, just update ESP
 *              (flags)= TKSuBuff Flags
 *                  if TK_UM_FATAL, on error mark process for termination
 *                  otherwise, return error code only
 *
 *      EXIT    if no errors, returns NO_ERROR
 *              if error, returns error code
 *                      ERROR_PROTECTION_VIOLATION
 *
 *      CONTEXT Task
 *
 *      NOTE    On a call gate type stack frame, this function
 *              overwrites the call gate arguments with the data
 *              pass in this call.  The call gate arguments are
 *              logically moved to the top of stack, but they are
 *              not copied so the argument data is lost.
 *
 *              +------------+            +------------+
 *              |            |            |            |
 *              +------------+            +------------+
 *              |   cbargs   |            |    pbuf    |
 *              +------------+ <- SS:ESP  +------------+
 *              |            |            |   cbargs   |
 *              +------------+            +------------+ <- SS:ESP
 *              |            |            |            |
 *              ~            ~            ~            ~
 *              +------------+ 0          +------------+ 0
 *                  BEFORE                     AFTER
 *
 *      PSEUDO-CODE
 *
 *      get the linear address of the client stack
 *      call TKSuBuff to copy the bytes pushed
 */

INT PASCAL
KMPushClientStack(ptcb, cb, pbuf, flags)
PTCB    ptcb;
ULONG   cb;
PVOID   pbuf;
ULONG   flags;
{
    PVOID p;
    INT ret;

    if((ret = kmGetStackAddr(ptcb, (PPVOID) SSToDS(&p), -cb))) {
        return(ret);
    }
    if(pbuf != NULL) {
        return(TKSuBuff(p, pbuf, cb, flags & TK_UM_FATAL));
    }
    return(NO_ERROR);
}

/***EP  KMPopClientStack - pops memory off of client's stack
 *
 *      Copies and removes the specified number of bytes from
 *      the client's stack to the return buffer.
 *
 *      ENTRY   (ptcb) = tcb of thread to pop
 *              (cb)   = number of bytes
 *              (pbuf) = pointer to buffer to put bytes
 *                  if NULL, just update ESP
 *              (flags)= TKFuBuff Flags
 *                  if TK_UM_FATAL, on error mark process for termination
 *                  otherwise, return error code only
 *
 *      EXIT    if no errors, returns NO_ERROR
 *              if error, returns error code
 *                      ERROR_PROTECTION_VIOLATION
 *
 *      CONTEXT Task
 *
 *      NOTE    This function does not return the arguments for
 *              a call gate type of stack frame.  The call gate
 *              arguments are ignored and the data on stack after
 *              them is returned.
 *
 *      PSEUDO-CODE
 *
 *      get the linear address of the client stack
 *      call TKFuBuff to copy the bytes popped to kernel buffer
 */

INT PASCAL
KMPopClientStack(ptcb, cb, pbuf, flags)
PTCB    ptcb;
register ULONG cb;
PVOID   pbuf;
ULONG   flags;
{
    PVOID p;
    INT ret;

    if((ret = kmGetStackAddr(ptcb, (PPVOID) SSToDS(&p), cb))) {
        return(ret);
    }
    if(pbuf != NULL) {
        return(TKFuBuff(pbuf, (PBYTE) p - cb, cb, flags & TK_UM_FATAL));
    }
    return(NO_ERROR);
}

/***LP  kmGetStackAddr - returns the client's linear stack address
 *
 *      Calculates and returns the linear address of the client's stack.
 *
 *      kmGetStackAddr pays close attention to SP wrapping and limit
 *      violations.
 *
 *      ENTRY   (ptcb) = pointer to tcb
 *              (pp)   = pointer to put client stack address
 *              (cb)   = number of bytes to adjust stack
 *                      0 = do nothing to ESP
 *                      > 0 = pop stack (added to ESP)
 *                      < 0 = push stack (subtract from ESP)
 *
 *      EXIT    if no errors, returns NO_ERROR
 *              if error, returns error code
 *                      ERROR_PROTECTION_VIOLATION
 *
 *      CONTEXT Task
 *
 *      PSEUDO-CODE
 *
 *      validate not a V86 mode stack frame
 *      get SS information (attr, limit and base)
 *      get word or dword ESP based on SS's BIG bit
 *      correct for call gate arguments
 *      check for stack under or overflow
 *      adjust ESP by cb value
 *      return SS:ESP's linear address
 */

static INT PASCAL
kmGetStackAddr(ptcb, pp, cb)
PTCB    ptcb;
PPVOID  pp;
register INT cb;
{
    struct seldesc_s seldesc;
    register ULONG ulesp;
    register ULONG cbargs = 0;
    PULONG pulesp;
    PULONG pcbargs;
    SEL sel;
    PRIP prip;

    kmAssertTRUE(!(kmQueryReg(ptcb, KM_REG_EFLAG) & F_VM));

    /* get SS attributes, limit and base */
    sel = kmQueryWordReg(ptcb, KM_REG_SS);
    if(SELGetDescInfo(sel, 0, SSToDS(&seldesc))) {
        TRAPProcessFatalFault(EXCGP, sel);
        return(ERROR_PROTECTION_VIOLATION);
    }
    kmAssertTRUE(!(seldesc.desc_acc & D_EXPDN));

    /* get pointer to ESP */
    pulesp = kmQueryRegPtr(ptcb, KM_REG_ESP);

    /* get ESP */
    if(seldesc.desc_attr & D_DBIG) {
        ulesp = *pulesp;
    }
    else {
        ulesp = *((PUSHORT) pulesp);
    }

    /* If this a PASCAL call gate stack frame, the call gate parameters
     * are logically moved to the top of stack so the same RETF n can
     * be used to exit the kernel. For C32 stack frames, the RETF n still
     * exists, but cbargs is subtracted from ESP before the return so that
     * the ring3 paramters stay in place.  Anyway, cbargs is 0 for C32.
     */

    prip = kmQueryPRIP(ptcb, KM_REG_ESP);
    if(((pcbargs = kmQueryRegPtr(ptcb, KM_REG_CBARGS)) != NULL) &&
        (!(prip->rip_flags & KM_RIP_C32))) {
//83540        cbargs = kmRoundcbargs(*pcbargs);
        if  (!(*pcbargs & _16B_Gate))  {            //83540 Do NOT round if
           cbargs = kmRoundcbargs(*pcbargs);        //83540 returning from
        } else {                                    //83540 16B DYNAMIC API
           cbargs = (*pcbargs) & (~_16B_Gate);      //83540     =======
        }                                           //83540
    }

    /* if poping stack (positive) */
    if(cb > 0) {

        /* verify not underflowing stack limit */
        if(cb + ulesp + cbargs > seldesc.desc_cb) {
             TRAPProcessFatalFault(EXCSSONP, 0);
             return(ERROR_PROTECTION_VIOLATION);
        }
    }
    /* else pushing stack (negative) */
    else {

        /* verify not wrapping around 0 */
        if(-cb > ulesp + cbargs) {
             TRAPProcessFatalFault(EXCSSONP, 0);
             return(ERROR_PROTECTION_VIOLATION);
        }
    }
    ulesp += cb;                                // adjust ESP
    *pulesp = ulesp;                            // set new value
    *pp = (void *) (seldesc.desc_laddr + ulesp + cbargs);
                                                // return base address
    return(NO_ERROR);
}

/***LP  kmQueryRegPtr - returns a pointer the specified client register
 *
 *      Returns a pointer the specified client register on the
 *      kernel stack frame.
 *
 *      ENTRY   (ptcb)     = tcb of the thread to query
 *              (ulRegNum) = register number
 *                              KM_REG_EAX
 *                              KM_REG_EBX
 *                              KM_REG_ECX
 *                              KM_REG_EDX
 *                              KM_REG_EBP
 *                              KM_REG_ESP
 *                              KM_REG_ESI
 *                              KM_REG_EDI
 *                              KM_REG_EIP
 *                              KM_REG_DS
 *                              KM_REG_ES
 *                              KM_REG_SS
 *                              KM_REG_CS
 *                              KM_REG_GS
 *                              KM_REG_FS
 *                              KM_REG_EFLAG
 *                              KM_REG_CBARGS
 *                              KM_REG_TRAPNUM
 *                              KM_REG_ERRCODE
 *
 *      EXIT    if valid ulRegNum,
 *                      p = pointer to client register
 *              if invalid ulRegNum,
 *                      p = NULL
 *
 *      CONTEXT Task
 *
 *      PSEUDO-CODE
 *
 *      validate ulRegNum
 *      get RIP pointer for the stack frame type
 *      calculate pointer to register from RIP entry
 */

static PVOID PASCAL
kmQueryRegPtr(ptcb, ulRegNum)
register PTCB ptcb;
ULONG ulRegNum;
{
    register PRIP prip;

#ifdef KMSTRICT
    if(ulRegNum > KM_REG_MAX) {
        panic("kmQueryRegPtr: ulRegNum too large");
    }
#endif
    prip = kmQueryPRIP(ptcb, ulRegNum);
    if(prip->rip_flags & KM_RIP_INVALID) {
        return(NULL);
    }
    return(kmQueryRegPtrPRIP(ptcb, prip));
}

/***LP  kmQueryRegPtrPRIP - returns a pointer the specified client register
 *
 *      Returns a pointer the client register on the kernel stack frame
 *      specified by the RIP.
 *
 *      ENTRY   (ptcb) = tcb of the thread to query
 *              (prip) = pointer to RIP
 *
 *      EXIT    p = pointer to client register
 *
 *      CONTEXT Task
 *
 *      PSEUDO-CODE
 *
 *      calculate pointer to register from RIP entry
 */

static PVOID PASCAL
kmQueryRegPtrPRIP(ptcb, prip)
register PTCB ptcb;
register PRIP prip;
{
    if(prip->rip_flags & KM_RIP_TSD_RELATIVE) {
        return((PBYTE) ptcb->TCBpTSD + prip->rip_offset);
    }
    else {
        return((PBYTE) ptcb->TCB_pFrameBase + prip->rip_offset);
    }
}
